diff --git a/.gitignore b/.gitignore index 5bf633a8b..9216f23a1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ user_path_config-deprecated.txt /.coverage* /auth.json .DS_Store +/.venv diff --git a/api/controllers/__init__.py b/api/controllers/__init__.py new file mode 100644 index 000000000..ecdf489b2 --- /dev/null +++ b/api/controllers/__init__.py @@ -0,0 +1,23 @@ +import os +import importlib +from flask import Blueprint +from flask_restx import Namespace + + +def register_blueprints(app, api): + """Register all Blueprints to the Flask app automatically.""" + controllers_dir = os.path.dirname(__file__) + for filename in os.listdir(controllers_dir): + if filename.endswith('_controller.py') and filename != '__init__.py': + module_name = filename[:-3] # Remove ".py" + module = importlib.import_module( + f'.{module_name}', package=__package__) + for attribute_name in dir(module): + attribute = getattr(module, attribute_name) + if isinstance(attribute, Namespace): + api.add_namespace(attribute) + + if isinstance(attribute, Blueprint): + app.register_blueprint( + attribute) + print(f"Registered blueprint: {attribute_name}") \ No newline at end of file diff --git a/api/controllers/gpu_usage_controller.py b/api/controllers/gpu_usage_controller.py new file mode 100644 index 000000000..8d5ba63bb --- /dev/null +++ b/api/controllers/gpu_usage_controller.py @@ -0,0 +1,105 @@ +from flask_restx import Api, Resource, fields, Namespace +from flask import jsonify, request, make_response, Blueprint +import psutil +import GPUtil +import time + +# Create a Blueprint for the gpu_usage controller +gpu_usage_bp = Blueprint('gpu_usage', __name__) +gpu_usage_api = Api(gpu_usage_bp, version='1.0', title='gpu_usage API', + description='API for managing gpu_usage') + +# Define a namespace for gpu_usage +gpu_usage_ns = Namespace('gpu_usage', description='gpu usage operations') + +# Define the model for a gpu +gpu_model = gpu_usage_ns.model('gpu_usage', { + 'id': fields.Integer(required=True, description='The unique identifier of the gpu'), + 'description': fields.String(required=True, description='Description of the gpu'), + 'status': fields.String(description='Status of the gpu') +}) + +# Cache for system usage data +cache = { + 'timestamp': 0, + 'data': { + 'cpu': 0, + 'ram': 0, + 'gpu': 0, + 'vram': 0, + 'hdd': 0, + 'temp': 0 + } +} +CACHE_DURATION = 1 # Cache duration in seconds + + +@gpu_usage_ns.route('/') +class GPUInfo(Resource): + def get_cache(self, current_time): + # Get CPU utilization + cpu_percent = psutil.cpu_percent(interval=0) + + # Get Memory utilization + mem = psutil.virtual_memory() + mem_percent = mem.percent + + # Get GPU utilization (considering only the first GPU) + gpus = GPUtil.getGPUs() + gpu_percent = gpus[0].load * 100 if gpus else 0 + + # Get VRAM usage (considering only the first GPU) + vram_usage = 0 + if gpus: + used = gpus[0].memoryUsed + total = gpus[0].memoryTotal + vram_usage = (used / total) * 100 + + # Get HDD usage (assuming usage of the primary disk) + hdd = psutil.disk_usage('/') + hdd_percent = hdd.percent + + # Get temperature (if available) + temperature = gpus[0].temperature + + # Update the cache + cache['data'] = { + 'cpu': cpu_percent, + 'ram': mem_percent, + 'gpu': gpu_percent, + 'vram': vram_usage, # Convert bytes to MB + 'hdd': hdd_percent, + 'temp': temperature # Add temperature + } + cache['timestamp'] = current_time + return cache + + def get(self): + if request.method == "OPTIONS": # CORS preflight + return _build_cors_preflight_response() + + current_time = time.time() + + # Check if the cache is still valid + if current_time - cache['timestamp'] < CACHE_DURATION: + return _corsify_actual_response(jsonify(cache['data'])) + + try: + self.get_cache(current_time) + + return _corsify_actual_response(jsonify(cache['data'])) + except Exception as e: + return _corsify_actual_response(jsonify({'error': str(e)})) + + +def _build_cors_preflight_response(): + response = make_response() + response.headers.add("Access-Control-Allow-Origin", "*") + response.headers.add("Access-Control-Allow-Headers", "*") + response.headers.add("Access-Control-Allow-Methods", "*") + return response + + +def _corsify_actual_response(response): + response.headers.add("Access-Control-Allow-Origin", "*") + return response diff --git a/api/controllers/jobs_controller.py b/api/controllers/jobs_controller.py new file mode 100644 index 000000000..c8313855a --- /dev/null +++ b/api/controllers/jobs_controller.py @@ -0,0 +1,110 @@ +import pickle +import os +from flask import Blueprint, request, jsonify, make_response +from flask_restx import Api, Resource, fields, Namespace + +# Create a Blueprint for the jobs controller +jobs_bp = Blueprint('jobs', __name__) +jobs_api = Api(jobs_bp, version='1.0', title='Jobs API', + description='API for managing jobs') + +# Define a namespace for jobs +jobs_ns = Namespace('jobs', description='Job operations') + +# Define the model for a job +job_model = jobs_ns.model('Job', { + 'id': fields.Integer(required=True, description='The unique identifier of the job'), + 'description': fields.String(required=True, description='Description of the job'), + 'status': fields.String(description='Status of the job') +}) + + +# File to persist data +DATA_FILE = 'jobs.pkl' + + +def load_jobs(): + if os.path.exists(DATA_FILE): + with open(DATA_FILE, 'rb') as file: + return pickle.load(file) + else: + # Create an empty file if it doesn't exist + with open(DATA_FILE, 'wb') as file: + pickle.dump({}, file) + return {} + + +def save_jobs(jobs): + with open(DATA_FILE, 'wb') as file: + pickle.dump(jobs, file) + + +# Load initial data +jobs_store = load_jobs() + + +@jobs_ns.route('/') +class JobList(Resource): + def get(self): + """List all jobs""" + jobs_store = load_jobs() + return _corsify_actual_response(jsonify(list(jobs_store.values()))) + + @jobs_ns.expect(job_model) + def post(self): + """Create a new job""" + if request.method == "OPTIONS": # CORS preflight + return _build_cors_preflight_response() + + job = request.json + job_id = job['id'] + if job_id in jobs_store: + return {'message': 'Job already exists'}, 400 + jobs_store[job_id] = job + save_jobs(jobs_store) # Save to file + return _corsify_actual_response(jsonify(job)) + + +@jobs_ns.route('/') +class JobItem(Resource): + def get(self, job_id): + """Get a job by ID""" + job = jobs_store.get(job_id) + if job is None: + return {'message': 'Job not found'}, 404 + return _corsify_actual_response(jsonify(job)) + + @jobs_ns.expect(job_model) + def put(self, job_id): + """Update a job by ID""" + if request.method == "OPTIONS": # CORS preflight + return _build_cors_preflight_response() + job = request.json + if job_id not in jobs_store: + return {'message': 'Job not found'}, 404 + jobs_store[job_id] = job + save_jobs(jobs_store) # Save to file + return _corsify_actual_response(jsonify(job)) + + def delete(self, job_id): + """Delete a job by ID""" + if request.method == "OPTIONS": # CORS preflight + return _build_cors_preflight_response() + if job_id not in jobs_store: + return {'message': 'Job not found'}, 404 + del jobs_store[job_id] + save_jobs(jobs_store) # Save to file + return _corsify_actual_response(jsonify({'message': 'Job deleted'})) + + +def _build_cors_preflight_response(): + response = make_response() + response.headers.add("Access-Control-Allow-Origin", "*") + response.headers.add("Access-Control-Allow-Headers", "*") + response.headers.add("Access-Control-Allow-Methods", "*") + return response + + +def _corsify_actual_response(response): + response.headers.add("Access-Control-Allow-Origin", "*") + return response diff --git a/api/controllers/settings_controller.py b/api/controllers/settings_controller.py new file mode 100644 index 000000000..575843cea --- /dev/null +++ b/api/controllers/settings_controller.py @@ -0,0 +1,71 @@ +import json +import os +from flask import Blueprint, jsonify, request +from flask_restx import Api, Resource, fields, Namespace + +# Create a Blueprint for the settings controller +settings_bp = Blueprint('settings', __name__) +settings_api = Api(settings_bp, version='1.0', title='Settings API', + description='API for managing settings') + +# Define a namespace for settings +settings_ns = Namespace('settings', description='Settings operations') + +# Define the model for settings +settings_model = settings_ns.model('Setting', { + 'key': fields.String(required=True, description='The key of the setting'), + 'value': fields.String(required=True, description='The value of the setting') +}) + +# File to persist settings data +SETTINGS_FILE = 'settings.json' + + +def load_settings(): + if os.path.exists(SETTINGS_FILE): + with open(SETTINGS_FILE, 'r') as file: + return json.load(file) + return {} + + +def save_settings(settings): + with open(SETTINGS_FILE, 'w') as file: + json.dump(settings, file, indent=4) + + +# Load initial data +settings_store = load_settings() + + +@settings_ns.route('/') +class SettingsList(Resource): + def get(self): + """List all settings""" + return jsonify({'settings': list(settings_store.values())}) + + @settings_ns.expect(settings_model) + def post(self): + """Create or update a setting""" + setting = request.json + key = setting['key'] + settings_store[key] = setting + save_settings(settings_store) # Save to file + return jsonify(setting) + + +@settings_ns.route('/') +class SettingItem(Resource): + def get(self, key): + """Get a setting by key""" + setting = settings_store.get(key) + if setting is None: + return {'message': 'Setting not found'}, 404 + return jsonify(setting) + + def delete(self, key): + """Delete a setting by key""" + if key not in settings_store: + return {'message': 'Setting not found'}, 404 + del settings_store[key] + save_settings(settings_store) # Save to file + return {'message': 'Setting deleted'} diff --git a/api/dependency_installer.py b/api/dependency_installer.py new file mode 100644 index 000000000..1637d13e0 --- /dev/null +++ b/api/dependency_installer.py @@ -0,0 +1,214 @@ +import subprocess +import sys +import os +import shutil +import zipfile +import importlib +import urllib.request +import re + + +re_requirement = re.compile(r"\s*([-\w]+)\s*(?:==\s*([-+.\w]+))?\s*") + +python = sys.executable +default_command_live = (os.environ.get('LAUNCH_LIVE_OUTPUT') == "1") +index_url = os.environ.get('INDEX_URL', "") + +modules_path = os.path.dirname(os.path.realpath(__file__)) +script_path = os.path.dirname(modules_path) + + +def detect_python_version(): + version = sys.version_info + version_str = f"{version.major}.{version.minor}" + is_embedded = hasattr(sys, '_base_executable') or ( + sys.base_prefix != sys.prefix and not hasattr(sys, 'real_prefix')) + return version_str, is_embedded + + +def check_tkinter_installed(): + version_str, is_embedded = detect_python_version() + print(f"Detected Python version: {version_str}") + print(f"Is Embedded Python: {is_embedded}") + try: + import tkinter + tkinter_installed = True + except ImportError: + tkinter_installed = False + if not tkinter_installed or (is_embedded and not tkinter_installed): + install_tkinter(version_str) + + +def check_GPUtil_installed(): + + try: + import GPUtil + import psutil + return True + except ImportError: + import_GPUtil() + return False + + +def check_flask_installed(): + + try: + import flask + import flask_restx + import flask_cors + + import flask_socketio + return True + except ImportError: + import_flask() + return False + + +def download_and_unzip_tkinter(): + url = "https://github.com/ChrisColeTech/tkinter-standalone/releases/download/1.0.0/tkinter-standalone.zip" + zip_path = "tkinter-standalone.zip" + print(f"Downloading {url}...") + urllib.request.urlretrieve(url, zip_path) + + print("Unzipping tkinter-standalone.zip...") + with zipfile.ZipFile(zip_path, 'r') as zip_ref: + zip_ref.extractall("tkinter-standalone") + + os.remove(zip_path) + print("Download and extraction complete.") + + +def copy_tkinter_files(version_str): + src_folder = os.path.join("tkinter-standalone", + version_str, "python_embedded") + number_only = version_str.replace(".", "") + python_zip = f"python{number_only}" + python_zip_path = os.path.join(src_folder, f"{python_zip}.zip") + with zipfile.ZipFile(python_zip_path, 'r') as zip_ref: + zip_ref.extractall(os.path.join(src_folder, python_zip)) + + if not os.path.exists(src_folder): + print(f"Error: No tkinter files for Python {version_str}") + return + + # Define paths + python_dir = os.path.dirname(sys.executable) + pth_filename = f"{python_zip}._pth" + pth_path_src = os.path.join(src_folder, pth_filename) + pth_path_dest = os.path.join(python_dir, pth_filename) + + # Copy the .pth file from python_dir to src_folder + if os.path.exists(pth_path_dest): + shutil.copy(pth_path_dest, pth_path_src) + else: + print(f"Error: {pth_filename} not found in {python_dir}") + return + + # Modify the .pth file + with open(pth_path_src, 'a') as pth_file: + pth_file.write(f'\n./{python_zip}\n') + pth_file.write('./Scripts\n') + pth_file.write('./DLLs\n') + + print(f"Copying tkinter files from {src_folder} to {python_dir}...") + shutil.copytree(src_folder, python_dir, dirs_exist_ok=True) + + print("Tkinter files copied successfully.") + shutil.rmtree("tkinter-standalone", ignore_errors=True) + + +def install_tkinter(version_str): + download_and_unzip_tkinter() + copy_tkinter_files(version_str) + import_tkinter() + + +def import_tkinter(): + try: + tkinter = importlib.import_module("tkinter") + print("tkinter module loaded successfully.") + return tkinter + except ModuleNotFoundError as e: + print(f"Module not found: {e}") + except ImportError as e: + print("Failed to import Tkinter after installation.") + print(f"An error occurred: {e}") + except Exception as e: + print(f"An error occurred: {e}") + + return None + + +def import_GPUtil(): + run_pip(f"install GPUtil psutil", desc="GPU Utility for NVIDIA GPUs") + + try: + GPUtil = importlib.import_module( + "GPUtil") + psutil = importlib.import_module( + "psutil") + return GPUtil + except ImportError: + print("Failed to import GPUtil after installation.") + return None + + +def import_flask(): + run_pip(f"install flask flask-restx flask-cors flask_socketio", desc="Flask Rest API") + + try: + flask = importlib.import_module("flask") + restx = importlib.import_module("flask-restx") + flask_socketio = importlib.import_module("flask_socketio") + return restx + except ImportError: + print("Failed to import flask after installation.") + return None + + +def run(command, desc=None, errdesc=None, custom_env=None, live: bool = default_command_live) -> str: + if desc is not None: + print(desc) + + run_kwargs = { + "args": command, + "shell": True, + "env": os.environ if custom_env is None else custom_env, + "encoding": 'utf8', + "errors": 'ignore', + } + + if not live: + run_kwargs["stdout"] = run_kwargs["stderr"] = subprocess.PIPE + + result = subprocess.run(**run_kwargs) + + if result.returncode != 0: + error_bits = [ + f"{errdesc or 'Error running command'}.", + f"Command: {command}", + f"Error code: {result.returncode}", + ] + if result.stdout: + error_bits.append(f"stdout: {result.stdout}") + if result.stderr: + error_bits.append(f"stderr: {result.stderr}") + raise RuntimeError("\n".join(error_bits)) + + return (result.stdout or "") + + +def run_pip(command, desc=None, live=default_command_live): + try: + index_url_line = f' --index-url {index_url}' if index_url != '' else '' + return run(f'"{python}" -m pip {command} --prefer-binary{index_url_line}', desc=f"Installing {desc}", + errdesc=f"Couldn't install {desc}", live=live) + except Exception as e: + print(e) + print(f'CMD Failed {desc}: {command}') + return None + + +check_tkinter_installed() +check_GPUtil_installed() +check_flask_installed() diff --git a/api/gradio_helper.py b/api/gradio_helper.py new file mode 100644 index 000000000..bc0410df9 --- /dev/null +++ b/api/gradio_helper.py @@ -0,0 +1,18 @@ + +import gradio as gr +import os +from .http_server import * + +def addResourceMonitor(): + ceq = None + with gr.Row(): + ceq = gr.HTML(load_page('templates/perf-monitor/index.html')) + + return ceq + +def load_page(filename): + """Load an HTML file as a string and return it""" + file_path = os.path.join("web", filename) + with open(file_path, 'r') as file: + content = file.read() + return content diff --git a/api/http_server.py b/api/http_server.py new file mode 100644 index 000000000..c68298808 --- /dev/null +++ b/api/http_server.py @@ -0,0 +1,116 @@ +from .dependency_installer import * +from flask import Flask, send_from_directory, render_template +from flask_socketio import SocketIO, emit +from flask_restx import Api +import logging +import time +from .controllers import register_blueprints +import psutil +import GPUtil +import threading + +# Initialize Flask app +title = f"Resource Monitor" +app = Flask(title, static_folder='web/assets', template_folder='web/templates') +app.config['CORS_HEADERS'] = 'Content-Type' + +# Initialize Flask-RESTx API +api = Api(app, version='1.0', title=title, description='API for system resource monitoring') + +# Register blueprints (API endpoints) +register_blueprints(app, api) + +# Initialize SocketIO with the Flask app +socketio = SocketIO(app, cors_allowed_origins="*") + +# Cache for system usage data +cache = { + 'timestamp': 0, + 'data': { + 'cpu': 0, + 'ram': 0, + 'gpu': 0, + 'vram': 0, + 'hdd': 0, + 'temp': 0 + } +} +CACHE_DURATION = 1 # Cache duration in seconds + +# Suppress the Flask development server warning +log = logging.getLogger('werkzeug') +log.setLevel(logging.ERROR) # Set level to ERROR to suppress warnings + +def get_cache(current_time): + # Get CPU utilization + cpu_percent = psutil.cpu_percent(interval=0) + + # Get Memory utilization + mem = psutil.virtual_memory() + mem_percent = mem.percent + + # Get GPU utilization (considering only the first GPU) + gpus = GPUtil.getGPUs() + gpu_percent = gpus[0].load * 100 if gpus else 0 + + # Get VRAM usage (considering only the first GPU) + vram_usage = 0 + if gpus: + used = gpus[0].memoryUsed + total = gpus[0].memoryTotal + vram_usage = (used / total) * 100 + + # Get HDD usage (assuming usage of the primary disk) + hdd = psutil.disk_usage('/') + hdd_percent = hdd.percent + + # Get temperature (if available) + temperature = gpus[0].temperature if gpus else 0 + + # Update the cache + cache['data'] = { + 'cpu': cpu_percent, + 'ram': mem_percent, + 'gpu': gpu_percent, + 'vram': vram_usage, # Convert bytes to MB + 'hdd': hdd_percent, + 'temp': temperature # Add temperature + } + cache['timestamp'] = current_time + +@app.route('/') +def home(): + return render_template('index.html') + +@app.route('/') +def serve_static(filename): + return send_from_directory('web', filename) + +@socketio.on('connect') +def handle_connect(): + # Emit initial data + current_time = time.time() + get_cache(current_time) + emit('data_update', cache['data']) + +@socketio.on('disconnect') +def handle_disconnect(): + pass + +def background_thread(): + while True: + current_time = time.time() + get_cache(current_time) + socketio.emit('data_update', cache['data']) + time.sleep(.5) + +def run_app(): + time.sleep(1) # Sleep for a short while to let the server start + # Start the background thread for emitting data + socketio.start_background_task(target=background_thread) + # Run the Flask app with SocketIO + socketio.run(app, port=5000) + +# Start Flask app in a separate thread +thread = threading.Thread(target=run_app) +thread.start() \ No newline at end of file diff --git a/modules/async_worker.py b/modules/async_worker.py index a0b96a542..8e4bf1585 100644 --- a/modules/async_worker.py +++ b/modules/async_worker.py @@ -3,7 +3,8 @@ from extras.inpaint_mask import generate_mask_from_image, SAMOptions from modules.patch import PatchSettings, patch_settings, patch_all import modules.config - +from PIL import Image +import modules.gradio_hijack as grh patch_all() @@ -95,8 +96,10 @@ def __init__(self, args): self.inpaint_advanced_masking_checkbox = args.pop() self.invert_mask_checkbox = args.pop() self.inpaint_erode_or_dilate = args.pop() - self.save_final_enhanced_image_only = args.pop() if not args_manager.args.disable_image_log else False - self.save_metadata_to_images = args.pop() if not args_manager.args.disable_metadata else False + self.save_final_enhanced_image_only = args.pop( + ) if not args_manager.args.disable_image_log else False + self.save_metadata_to_images = args.pop( + ) if not args_manager.args.disable_metadata else False self.metadata_scheme = MetadataScheme( args.pop()) if not args_manager.args.disable_metadata else MetadataScheme.FOOOCUS @@ -154,11 +157,24 @@ def __init__(self, args): enhance_inpaint_erode_or_dilate, enhance_mask_invert ]) - self.should_enhance = self.enhance_checkbox and (self.enhance_uov_method != disabled.casefold() or len(self.enhance_ctrls) > 0) + self.should_enhance = self.enhance_checkbox and ( + self.enhance_uov_method != disabled.casefold() or len(self.enhance_ctrls) > 0) self.images_to_enhance_count = 0 self.enhance_stats = {} + self.bulk_enhance_enabled = args.pop() + self.bulk_enhance_data_type = args.pop() + self.bulk_enhance_file_explorer = args.pop() + self.bulk_enhance_input_path = args.pop() + self.current_task_id = 0 + self.current_progress = None + self.preparation_steps = None + self.all_steps = None + self.total_count = None + self.current_async_task = None + async_tasks = [] +# Define global variables class EarlyReturnException(BaseException): @@ -222,7 +238,8 @@ def yield_result(async_task, imgs, progressbar_index, black_out_nsfw, censor=Tru imgs = [imgs] if censor and (modules.config.default_black_out_nsfw or black_out_nsfw): - progressbar(async_task, progressbar_index, 'Checking for NSFW content ...') + progressbar(async_task, progressbar_index, + 'Checking for NSFW content ...') imgs = default_censor(imgs) async_task.results = async_task.results + imgs @@ -313,12 +330,16 @@ def process_task(all_steps, async_task, callback, controlnet_canny_path, control del positive_cond, negative_cond # Save memory if inpaint_worker.current_task is not None: imgs = [inpaint_worker.current_task.post_process(x) for x in imgs] - current_progress = int(base_progress + (100 - preparation_steps) / float(all_steps) * steps) + current_progress = int( + base_progress + (100 - preparation_steps) / float(all_steps) * steps) if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: - progressbar(async_task, current_progress, 'Checking for NSFW content ...') + progressbar(async_task, current_progress, + 'Checking for NSFW content ...') imgs = default_censor(imgs) - progressbar(async_task, current_progress, f'Saving image {current_task_id + 1}/{total_count} to system ...') - img_paths = save_and_log(async_task, height, imgs, task, use_expansion, width, loras, persist_image) + progressbar(async_task, current_progress, + f'Saving image {current_task_id + 1}/{total_count} to system ...') + img_paths = save_and_log( + async_task, height, imgs, task, use_expansion, width, loras, persist_image) yield_result(async_task, img_paths, current_progress, async_task.black_out_nsfw, False, do_not_show_finished_images=not show_intermediate_results or async_task.disable_intermediate_results) @@ -338,11 +359,14 @@ def save_and_log(async_task, height, imgs, task, use_expansion, width, loras, pe img_paths = [] for x in imgs: d = [('Prompt', 'prompt', task['log_positive_prompt']), - ('Negative Prompt', 'negative_prompt', task['log_negative_prompt']), - ('Fooocus V2 Expansion', 'prompt_expansion', task['expansion']), + ('Negative Prompt', 'negative_prompt', + task['log_negative_prompt']), + ('Fooocus V2 Expansion', + 'prompt_expansion', task['expansion']), ('Styles', 'styles', str(task['styles'] if not use_expansion else [fooocus_expansion] + task['styles'])), - ('Performance', 'performance', async_task.performance_selection.value), + ('Performance', 'performance', + async_task.performance_selection.value), ('Steps', 'steps', async_task.steps), ('Resolution', 'resolution', str((width, height))), ('Guidance Scale', 'guidance_scale', async_task.cfg_scale), @@ -357,9 +381,11 @@ def save_and_log(async_task, height, imgs, task, use_expansion, width, loras, pe if async_task.refiner_model_name != 'None': if async_task.overwrite_switch > 0: - d.append(('Overwrite Switch', 'overwrite_switch', async_task.overwrite_switch)) + d.append(('Overwrite Switch', 'overwrite_switch', + async_task.overwrite_switch)) if async_task.refiner_swap_method != flags.refiner_swap_method: - d.append(('Refiner Swap Method', 'refiner_swap_method', async_task.refiner_swap_method)) + d.append( + ('Refiner Swap Method', 'refiner_swap_method', async_task.refiner_swap_method)) if modules.patch.patch_settings[pid].adaptive_cfg != modules.config.default_cfg_tsnr: d.append( ('CFG Mimicking from TSNR', 'adaptive_cfg', modules.patch.patch_settings[pid].adaptive_cfg)) @@ -377,11 +403,13 @@ def save_and_log(async_task, height, imgs, task, use_expansion, width, loras, pe for li, (n, w) in enumerate(loras): if n != 'None': - d.append((f'LoRA {li + 1}', f'lora_combined_{li + 1}', f'{n} : {w}')) + d.append( + (f'LoRA {li + 1}', f'lora_combined_{li + 1}', f'{n} : {w}')) metadata_parser = None if async_task.save_metadata_to_images: - metadata_parser = modules.meta_parser.get_metadata_parser(async_task.metadata_scheme) + metadata_parser = modules.meta_parser.get_metadata_parser( + async_task.metadata_scheme) metadata_parser.set_data(task['log_positive_prompt'], task['positive'], task['log_negative_prompt'], task['negative'], async_task.steps, async_task.base_model_name, async_task.refiner_model_name, @@ -389,7 +417,8 @@ def save_and_log(async_task, height, imgs, task, use_expansion, width, loras, pe d.append(('Metadata Scheme', 'metadata_scheme', async_task.metadata_scheme.value if async_task.save_metadata_to_images else async_task.save_metadata_to_images)) d.append(('Version', 'version', 'Fooocus v' + fooocus_version.version)) - img_paths.append(log(x, d, metadata_parser, async_task.output_format, task, persist_image)) + img_paths.append( + log(x, d, metadata_parser, async_task.output_format, task, persist_image)) return img_paths @@ -405,7 +434,8 @@ def apply_control_nets(async_task, height, ip_adapter_face_path, ip_adapter_path cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) for task in async_task.cn_tasks[flags.cn_cpds]: cn_img, cn_stop, cn_weight = task cn_img = resize_image(HWC3(cn_img), width=width, height=height) @@ -416,7 +446,8 @@ def apply_control_nets(async_task, height, ip_adapter_face_path, ip_adapter_path cn_img = HWC3(cn_img) task[0] = core.numpy_to_pytorch(cn_img) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) for task in async_task.cn_tasks[flags.cn_ip]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -424,9 +455,11 @@ def apply_control_nets(async_task, height, ip_adapter_face_path, ip_adapter_path # https://github.com/tencent-ailab/IP-Adapter/blob/d580c50a291566bbf9fc7ac0f760506607297e6d/README.md?plain=1#L75 cn_img = resize_image(cn_img, width=224, height=224, resize_mode=0) - task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_path) + task[0] = ip_adapter.preprocess( + cn_img, ip_adapter_path=ip_adapter_path) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) for task in async_task.cn_tasks[flags.cn_ip_face]: cn_img, cn_stop, cn_weight = task cn_img = HWC3(cn_img) @@ -437,12 +470,16 @@ def apply_control_nets(async_task, height, ip_adapter_face_path, ip_adapter_path # https://github.com/tencent-ailab/IP-Adapter/blob/d580c50a291566bbf9fc7ac0f760506607297e6d/README.md?plain=1#L75 cn_img = resize_image(cn_img, width=224, height=224, resize_mode=0) - task[0] = ip_adapter.preprocess(cn_img, ip_adapter_path=ip_adapter_face_path) + task[0] = ip_adapter.preprocess( + cn_img, ip_adapter_path=ip_adapter_face_path) if async_task.debugging_cn_preprocessor: - yield_result(async_task, cn_img, current_progress, async_task.black_out_nsfw, do_not_show_finished_images=True) - all_ip_tasks = async_task.cn_tasks[flags.cn_ip] + async_task.cn_tasks[flags.cn_ip_face] + yield_result(async_task, cn_img, current_progress, + async_task.black_out_nsfw, do_not_show_finished_images=True) + all_ip_tasks = async_task.cn_tasks[flags.cn_ip] + \ + async_task.cn_tasks[flags.cn_ip_face] if len(all_ip_tasks) > 0: - pipeline.final_unet = ip_adapter.patch_model(pipeline.final_unet, all_ip_tasks) + pipeline.final_unet = ip_adapter.patch_model( + pipeline.final_unet, all_ip_tasks) def apply_vary(async_task, uov_method, denoising_strength, uov_input_image, switch, current_progress, advance_progress=False): if 'subtle' in uov_method: @@ -469,7 +506,8 @@ def apply_vary(async_task, uov_method, denoising_strength, uov_input_image, swit denoise=denoising_strength, refiner_swap_method=async_task.refiner_swap_method ) - initial_latent = core.encode_vae(vae=candidate_vae, pixels=initial_pixels) + initial_latent = core.encode_vae( + vae=candidate_vae, pixels=initial_pixels) B, C, H, W = initial_latent['samples'].shape width = W * 8 height = H * 8 @@ -481,7 +519,8 @@ def apply_inpaint(async_task, initial_latent, inpaint_head_model_path, inpaint_i inpaint_disable_initial_latent, current_progress, skip_apply_outpaint=False, advance_progress=False): if not skip_apply_outpaint: - inpaint_image, inpaint_mask = apply_outpaint(async_task, inpaint_image, inpaint_mask) + inpaint_image, inpaint_mask = apply_outpaint( + async_task, inpaint_image, inpaint_mask) inpaint_worker.current_task = inpaint_worker.InpaintWorker( image=inpaint_image, @@ -497,9 +536,12 @@ def apply_inpaint(async_task, initial_latent, inpaint_head_model_path, inpaint_i if advance_progress: current_progress += 1 progressbar(async_task, current_progress, 'VAE Inpaint encoding ...') - inpaint_pixel_fill = core.numpy_to_pytorch(inpaint_worker.current_task.interested_fill) - inpaint_pixel_image = core.numpy_to_pytorch(inpaint_worker.current_task.interested_image) - inpaint_pixel_mask = core.numpy_to_pytorch(inpaint_worker.current_task.interested_mask) + inpaint_pixel_fill = core.numpy_to_pytorch( + inpaint_worker.current_task.interested_fill) + inpaint_pixel_image = core.numpy_to_pytorch( + inpaint_worker.current_task.interested_image) + inpaint_pixel_mask = core.numpy_to_pytorch( + inpaint_worker.current_task.interested_mask) candidate_vae, candidate_vae_swap = pipeline.get_candidate_vae( steps=async_task.steps, switch=switch, @@ -538,7 +580,8 @@ def apply_inpaint(async_task, initial_latent, inpaint_head_model_path, inpaint_i B, C, H, W = latent_fill.shape height, width = H * 8, W * 8 final_height, final_width = inpaint_worker.current_task.image.shape[:2] - print(f'Final resolution is {str((final_width, final_height))}, latent is {str((width, height))}.') + print( + f'Final resolution is {str((final_width, final_height))}, latent is {str((width, height))}.') return denoising_strength, initial_latent, width, height, current_progress @@ -546,21 +589,25 @@ def apply_outpaint(async_task, inpaint_image, inpaint_mask): if len(async_task.outpaint_selections) > 0: H, W, C = inpaint_image.shape if 'top' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[int(H * 0.3), 0], [0, 0], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[int(H * 0.3), 0], [0, 0], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[int(H * 0.3), 0], [0, 0]], mode='constant', constant_values=255) if 'bottom' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[0, int(H * 0.3)], [0, 0], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[0, int(H * 0.3)], [0, 0], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[0, int(H * 0.3)], [0, 0]], mode='constant', constant_values=255) H, W, C = inpaint_image.shape if 'left' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[0, 0], [int(W * 0.3), 0], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[0, 0], [int(W * 0.3), 0], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[0, 0], [int(W * 0.3), 0]], mode='constant', constant_values=255) if 'right' in async_task.outpaint_selections: - inpaint_image = np.pad(inpaint_image, [[0, 0], [0, int(W * 0.3)], [0, 0]], mode='edge') + inpaint_image = np.pad( + inpaint_image, [[0, 0], [0, int(W * 0.3)], [0, 0]], mode='edge') inpaint_mask = np.pad(inpaint_mask, [[0, 0], [0, int(W * 0.3)]], mode='constant', constant_values=255) @@ -574,7 +621,8 @@ def apply_upscale(async_task, uov_input_image, uov_method, switch, current_progr H, W, C = uov_input_image.shape if advance_progress: current_progress += 1 - progressbar(async_task, current_progress, f'Upscaling image from {str((W, H))} ...') + progressbar(async_task, current_progress, + f'Upscaling image from {str((W, H))} ...') uov_input_image = perform_upscale(uov_input_image) print(f'Image upscaled.') if '1.5x' in uov_method: @@ -589,7 +637,8 @@ def apply_upscale(async_task, uov_input_image, uov_method, switch, current_progr uov_input_image = set_image_shape_ceil(uov_input_image, 1024) shape_ceil = 1024 else: - uov_input_image = resample_image(uov_input_image, width=W * f, height=H * f) + uov_input_image = resample_image( + uov_input_image, width=W * f, height=H * f) image_is_super_large = shape_ceil > 2800 if 'fast' in uov_method: direct_return = True @@ -640,15 +689,18 @@ def apply_overrides(async_task, steps, height, width): def process_prompt(async_task, prompt, negative_prompt, base_model_additional_loras, image_number, disable_seed_increment, use_expansion, use_style, use_synthetic_refiner, current_progress, advance_progress=False): - prompts = remove_empty_str([safe_str(p) for p in prompt.splitlines()], default='') - negative_prompts = remove_empty_str([safe_str(p) for p in negative_prompt.splitlines()], default='') + prompts = remove_empty_str([safe_str(p) + for p in prompt.splitlines()], default='') + negative_prompts = remove_empty_str( + [safe_str(p) for p in negative_prompt.splitlines()], default='') prompt = prompts[0] negative_prompt = negative_prompts[0] if prompt == '': # disable expansion when empty since it is not meaningful and influences image prompt use_expansion = False extra_positive_prompts = prompts[1:] if len(prompts) > 1 else [] - extra_negative_prompts = negative_prompts[1:] if len(negative_prompts) > 1 else [] + extra_negative_prompts = negative_prompts[1:] if len( + negative_prompts) > 1 else [] if advance_progress: current_progress += 1 progressbar(async_task, current_progress, 'Loading models ...') @@ -671,12 +723,16 @@ def process_prompt(async_task, prompt, negative_prompt, base_model_additional_lo if disable_seed_increment: task_seed = async_task.seed % (constants.MAX_SEED + 1) else: - task_seed = (async_task.seed + i) % (constants.MAX_SEED + 1) # randint is inclusive, % is not + # randint is inclusive, % is not + task_seed = (async_task.seed + i) % (constants.MAX_SEED + 1) - task_rng = random.Random(task_seed) # may bind to inpaint noise in the future - task_prompt = apply_wildcards(prompt, task_rng, i, async_task.read_wildcards_in_order) + # may bind to inpaint noise in the future + task_rng = random.Random(task_seed) + task_prompt = apply_wildcards( + prompt, task_rng, i, async_task.read_wildcards_in_order) task_prompt = apply_arrays(task_prompt, i) - task_negative_prompt = apply_wildcards(negative_prompt, task_rng, i, async_task.read_wildcards_in_order) + task_negative_prompt = apply_wildcards( + negative_prompt, task_rng, i, async_task.read_wildcards_in_order) task_extra_positive_prompts = [apply_wildcards(pmt, task_rng, i, async_task.read_wildcards_in_order) for pmt in extra_positive_prompts] @@ -695,24 +751,29 @@ def process_prompt(async_task, prompt, negative_prompt, base_model_additional_lo if s == random_style_name: s = get_random_style(task_rng) task_styles[j] = s - p, n, style_has_placeholder = apply_style(s, positive=task_prompt) + p, n, style_has_placeholder = apply_style( + s, positive=task_prompt) if style_has_placeholder: placeholder_replaced = True positive_basic_workloads = positive_basic_workloads + p negative_basic_workloads = negative_basic_workloads + n if not placeholder_replaced: - positive_basic_workloads = [task_prompt] + positive_basic_workloads + positive_basic_workloads = [ + task_prompt] + positive_basic_workloads else: positive_basic_workloads.append(task_prompt) - negative_basic_workloads.append(task_negative_prompt) # Always use independent workload for negative. + # Always use independent workload for negative. + negative_basic_workloads.append(task_negative_prompt) positive_basic_workloads = positive_basic_workloads + task_extra_positive_prompts negative_basic_workloads = negative_basic_workloads + task_extra_negative_prompts - positive_basic_workloads = remove_empty_str(positive_basic_workloads, default=task_prompt) - negative_basic_workloads = remove_empty_str(negative_basic_workloads, default=task_negative_prompt) + positive_basic_workloads = remove_empty_str( + positive_basic_workloads, default=task_prompt) + negative_basic_workloads = remove_empty_str( + negative_basic_workloads, default=task_negative_prompt) tasks.append(dict( task_seed=task_seed, @@ -725,8 +786,10 @@ def process_prompt(async_task, prompt, negative_prompt, base_model_additional_lo uc=None, positive_top_k=len(positive_basic_workloads), negative_top_k=len(negative_basic_workloads), - log_positive_prompt='\n'.join([task_prompt] + task_extra_positive_prompts), - log_negative_prompt='\n'.join([task_negative_prompt] + task_extra_negative_prompts), + log_positive_prompt='\n'.join( + [task_prompt] + task_extra_positive_prompts), + log_negative_prompt='\n'.join( + [task_negative_prompt] + task_extra_negative_prompts), styles=task_styles )) if use_expansion: @@ -734,24 +797,31 @@ def process_prompt(async_task, prompt, negative_prompt, base_model_additional_lo current_progress += 1 for i, t in enumerate(tasks): - progressbar(async_task, current_progress, f'Preparing Fooocus text #{i + 1} ...') - expansion = pipeline.final_expansion(t['task_prompt'], t['task_seed']) + progressbar(async_task, current_progress, + f'Preparing Fooocus text #{i + 1} ...') + expansion = pipeline.final_expansion( + t['task_prompt'], t['task_seed']) print(f'[Prompt Expansion] {expansion}') t['expansion'] = expansion - t['positive'] = copy.deepcopy(t['positive']) + [expansion] # Deep copy. + # Deep copy. + t['positive'] = copy.deepcopy(t['positive']) + [expansion] if advance_progress: current_progress += 1 for i, t in enumerate(tasks): - progressbar(async_task, current_progress, f'Encoding positive #{i + 1} ...') - t['c'] = pipeline.clip_encode(texts=t['positive'], pool_top_k=t['positive_top_k']) + progressbar(async_task, current_progress, + f'Encoding positive #{i + 1} ...') + t['c'] = pipeline.clip_encode( + texts=t['positive'], pool_top_k=t['positive_top_k']) if advance_progress: current_progress += 1 for i, t in enumerate(tasks): if abs(float(async_task.cfg_scale) - 1.0) < 1e-4: t['uc'] = pipeline.clone_cond(t['c']) else: - progressbar(async_task, current_progress, f'Encoding negative #{i + 1} ...') - t['uc'] = pipeline.clip_encode(texts=t['negative'], pool_top_k=t['negative_top_k']) + progressbar(async_task, current_progress, + f'Encoding negative #{i + 1} ...') + t['uc'] = pipeline.clip_encode( + texts=t['negative'], pool_top_k=t['negative_top_k']) return tasks, use_expansion, loras, current_progress def apply_freeu(async_task): @@ -776,16 +846,20 @@ def patch_samplers(async_task): if async_task.scheduler_name in ['lcm', 'tcd']: final_scheduler_name = 'sgm_uniform' if pipeline.final_unet is not None: - pipeline.final_unet = patch_discrete(pipeline.final_unet, async_task.scheduler_name) + pipeline.final_unet = patch_discrete( + pipeline.final_unet, async_task.scheduler_name) if pipeline.final_refiner_unet is not None: - pipeline.final_refiner_unet = patch_discrete(pipeline.final_refiner_unet, async_task.scheduler_name) + pipeline.final_refiner_unet = patch_discrete( + pipeline.final_refiner_unet, async_task.scheduler_name) elif async_task.scheduler_name == 'edm_playground_v2.5': final_scheduler_name = 'karras' if pipeline.final_unet is not None: - pipeline.final_unet = patch_edm(pipeline.final_unet, async_task.scheduler_name) + pipeline.final_unet = patch_edm( + pipeline.final_unet, async_task.scheduler_name) if pipeline.final_refiner_unet is not None: - pipeline.final_refiner_unet = patch_edm(pipeline.final_refiner_unet, async_task.scheduler_name) + pipeline.final_refiner_unet = patch_edm( + pipeline.final_refiner_unet, async_task.scheduler_name) return final_scheduler_name @@ -793,8 +867,10 @@ def set_hyper_sd_defaults(async_task, current_progress, advance_progress=False): print('Enter Hyper-SD mode.') if advance_progress: current_progress += 1 - progressbar(async_task, current_progress, 'Downloading Hyper-SD components ...') - async_task.performance_loras += [(modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)] + progressbar(async_task, current_progress, + 'Downloading Hyper-SD components ...') + async_task.performance_loras += [ + (modules.config.downloading_sdxl_hyper_sd_lora(), 0.8)] if async_task.refiner_model_name != 'None': print(f'Refiner disabled in Hyper-SD mode.') async_task.refiner_model_name = 'None' @@ -814,7 +890,8 @@ def set_lightning_defaults(async_task, current_progress, advance_progress=False) if advance_progress: current_progress += 1 progressbar(async_task, 1, 'Downloading Lightning components ...') - async_task.performance_loras += [(modules.config.downloading_sdxl_lightning_lora(), 1.0)] + async_task.performance_loras += [ + (modules.config.downloading_sdxl_lightning_lora(), 1.0)] if async_task.refiner_model_name != 'None': print(f'Refiner disabled in Lightning mode.') async_task.refiner_model_name = 'None' @@ -834,7 +911,8 @@ def set_lcm_defaults(async_task, current_progress, advance_progress=False): if advance_progress: current_progress += 1 progressbar(async_task, 1, 'Downloading LCM components ...') - async_task.performance_loras += [(modules.config.downloading_sdxl_lcm_lora(), 1.0)] + async_task.performance_loras += [ + (modules.config.downloading_sdxl_lcm_lora(), 1.0)] if async_task.refiner_model_name != 'None': print(f'Refiner disabled in LCM mode.') async_task.refiner_model_name = 'None' @@ -878,13 +956,16 @@ def apply_image_input(async_task, base_model_additional_loras, clip_vision_path, H, W, C = inpaint_image.shape async_task.inpaint_mask_image_upload = resample_image(async_task.inpaint_mask_image_upload, width=W, height=H) - async_task.inpaint_mask_image_upload = np.mean(async_task.inpaint_mask_image_upload, axis=2) + async_task.inpaint_mask_image_upload = np.mean( + async_task.inpaint_mask_image_upload, axis=2) async_task.inpaint_mask_image_upload = (async_task.inpaint_mask_image_upload > 127).astype( np.uint8) * 255 - inpaint_mask = np.maximum(inpaint_mask, async_task.inpaint_mask_image_upload) + inpaint_mask = np.maximum( + inpaint_mask, async_task.inpaint_mask_image_upload) if int(async_task.inpaint_erode_or_dilate) != 0: - inpaint_mask = erode_or_dilate(inpaint_mask, async_task.inpaint_erode_or_dilate) + inpaint_mask = erode_or_dilate( + inpaint_mask, async_task.inpaint_erode_or_dilate) if async_task.invert_mask_checkbox: inpaint_mask = 255 - inpaint_mask @@ -898,8 +979,10 @@ def apply_image_input(async_task, base_model_additional_loras, clip_vision_path, progressbar(async_task, 1, 'Downloading inpainter ...') inpaint_head_model_path, inpaint_patch_model_path = modules.config.downloading_inpaint_models( async_task.inpaint_engine) - base_model_additional_loras += [(inpaint_patch_model_path, 1.0)] - print(f'[Inpaint] Current inpaint model is {inpaint_patch_model_path}') + base_model_additional_loras += [ + (inpaint_patch_model_path, 1.0)] + print( + f'[Inpaint] Current inpaint model is {inpaint_patch_model_path}') if async_task.refiner_model_name == 'None': use_synthetic_refiner = True async_task.refiner_switch = 0.8 @@ -922,14 +1005,25 @@ def apply_image_input(async_task, base_model_additional_loras, clip_vision_path, if len(async_task.cn_tasks[flags.cn_cpds]) > 0: controlnet_cpds_path = modules.config.downloading_controlnet_cpds() if len(async_task.cn_tasks[flags.cn_ip]) > 0: - clip_vision_path, ip_negative_path, ip_adapter_path = modules.config.downloading_ip_adapters('ip') + clip_vision_path, ip_negative_path, ip_adapter_path = modules.config.downloading_ip_adapters( + 'ip') if len(async_task.cn_tasks[flags.cn_ip_face]) > 0: clip_vision_path, ip_negative_path, ip_adapter_face_path = modules.config.downloading_ip_adapters( 'face') if async_task.current_tab == 'enhance' and async_task.enhance_input_image is not None: goals.append('enhance') skip_prompt_processing = True - async_task.enhance_input_image = HWC3(async_task.enhance_input_image) + async_task.enhance_input_image = HWC3( + async_task.enhance_input_image) + + if async_task.bulk_enhance_enabled and async_task.bulk_enhance_data_type == 'Files' and len(async_task.bulk_enhance_file_explorer) > 0: + goals.append('bulk_enhance_files') + skip_prompt_processing = True + + if async_task.bulk_enhance_enabled and async_task.bulk_enhance_data_type == 'Folder' and async_task.bulk_enhance_input_path: + goals.append('bulk_enhance_folder') + skip_prompt_processing = True + return base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, inpaint_head_model_path, inpaint_image, inpaint_mask, ip_adapter_face_path, ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner def prepare_upscale(async_task, goals, uov_input_image, uov_method, performance, steps, current_progress, @@ -947,7 +1041,8 @@ def prepare_upscale(async_task, goals, uov_input_image, uov_method, performance, if advance_progress: current_progress += 1 - progressbar(async_task, current_progress, 'Downloading upscale models ...') + progressbar(async_task, current_progress, + 'Downloading upscale models ...') modules.config.downloading_upscale_model() return uov_input_image, skip_prompt_processing, steps @@ -970,11 +1065,13 @@ def process_enhance(all_steps, async_task, callback, controlnet_canny_path, cont use_synthetic_refiner, width, show_intermediate_results=True, persist_image=True): base_model_additional_loras = [] inpaint_head_model_path = None - inpaint_parameterized = inpaint_engine != 'None' # inpaint_engine = None, improve detail + # inpaint_engine = None, improve detail + inpaint_parameterized = inpaint_engine != 'None' initial_latent = None prompt = prepare_enhance_prompt(prompt, async_task.prompt) - negative_prompt = prepare_enhance_prompt(negative_prompt, async_task.negative_prompt) + negative_prompt = prepare_enhance_prompt( + negative_prompt, async_task.negative_prompt) if 'vary' in goals: img, denoising_strength, initial_latent, width, height, current_progress = apply_vary( @@ -985,21 +1082,27 @@ def process_enhance(all_steps, async_task, callback, controlnet_canny_path, cont if direct_return: d = [('Upscale (Fast)', 'upscale_fast', '2x')] if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: - progressbar(async_task, current_progress, 'Checking for NSFW content ...') + progressbar(async_task, current_progress, + 'Checking for NSFW content ...') img = default_censor(img) - progressbar(async_task, current_progress, f'Saving image {current_task_id + 1}/{total_count} to system ...') - uov_image_path = log(img, d, output_format=async_task.output_format, persist_image=persist_image) + progressbar(async_task, current_progress, + f'Saving image {current_task_id + 1}/{total_count} to system ...') + uov_image_path = log( + img, d, output_format=async_task.output_format, persist_image=persist_image) yield_result(async_task, uov_image_path, current_progress, async_task.black_out_nsfw, False, do_not_show_finished_images=not show_intermediate_results or async_task.disable_intermediate_results) return current_progress, img, prompt, negative_prompt if 'inpaint' in goals and inpaint_parameterized: - progressbar(async_task, current_progress, 'Downloading inpainter ...') + progressbar(async_task, current_progress, + 'Downloading inpainter ...') inpaint_head_model_path, inpaint_patch_model_path = modules.config.downloading_inpaint_models( inpaint_engine) if inpaint_patch_model_path not in base_model_additional_loras: - base_model_additional_loras += [(inpaint_patch_model_path, 1.0)] - progressbar(async_task, current_progress, 'Preparing enhance prompts ...') + base_model_additional_loras += [ + (inpaint_patch_model_path, 1.0)] + progressbar(async_task, current_progress, + 'Preparing enhance prompts ...') # positive and negative conditioning aren't available here anymore, process prompt again tasks_enhance, use_expansion, loras, current_progress = process_prompt( async_task, prompt, negative_prompt, base_model_additional_loras, 1, True, @@ -1028,6 +1131,20 @@ def process_enhance(all_steps, async_task, callback, controlnet_canny_path, cont del task_enhance['c'], task_enhance['uc'] # Save memory return current_progress, imgs[0], prompt, negative_prompt + def print_user_skipped(async_task): + print('User skipped') + async_task.last_stop = False + progressbar(async_task, 0, + 'Image skipped') + print( + "Image skipped ...") + + def print_user_stopped(async_task): + print('User stopped') + + print( + "Processing was interrupted by the user. Please try again.") + def enhance_upscale(all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, current_task_id, denoising_strength, done_steps_inpainting, done_steps_upscaling, enhance_steps, prompt, negative_prompt, final_scheduler_name, height, img, preparation_steps, switch, tiled, @@ -1035,7 +1152,8 @@ def enhance_upscale(all_steps, async_task, base_progress, callback, controlnet_c # reset inpaint worker to prevent tensor size issues and not mix upscale and inpainting inpaint_worker.current_task = None - current_progress = int(base_progress + (100 - preparation_steps) / float(all_steps) * (done_steps_upscaling + done_steps_inpainting)) + current_progress = int(base_progress + (100 - preparation_steps) / + float(all_steps) * (done_steps_upscaling + done_steps_inpainting)) goals_enhance = [] img, skip_prompt_processing, steps = prepare_upscale( async_task, goals_enhance, img, async_task.enhance_uov_method, async_task.performance_selection, @@ -1053,26 +1171,276 @@ def enhance_upscale(all_steps, async_task, base_progress, callback, controlnet_c except ldm_patched.modules.model_management.InterruptProcessingException: if async_task.last_stop == 'skip': - print('User skipped') - async_task.last_stop = False + print_user_skipped(async_task) # also skip all enhance steps for this image, but add the steps to the progress bar if async_task.enhance_uov_processing_order == flags.enhancement_uov_before: - done_steps_inpainting += len(async_task.enhance_ctrls) * enhance_steps + done_steps_inpainting += len( + async_task.enhance_ctrls) * enhance_steps exception_result = 'continue' else: - print('User stopped') exception_result = 'break' + print_user_stopped(async_task) finally: done_steps_upscaling += steps return current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result + def enhance_images( + imgs, + async_task, + current_task_id, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + current_task_number, + persist_image=True + ): + + index = current_task_id + progressbar(async_task, current_progress, + 'Processing enhance ...') + + active_enhance_tabs = len(async_task.enhance_ctrls) + should_process_enhance_uov = async_task.enhance_uov_method != flags.disabled.casefold() + enhance_uov_before = False + enhance_uov_after = False + if should_process_enhance_uov: + active_enhance_tabs += 1 + enhance_uov_before = async_task.enhance_uov_processing_order == flags.enhancement_uov_before + enhance_uov_after = async_task.enhance_uov_processing_order == flags.enhancement_uov_after + total_count = 1 * active_enhance_tabs + async_task.images_to_enhance_count = 1 + + base_progress = current_progress + current_task_id = -1 + done_steps_upscaling = 0 + done_steps_inpainting = 0 + enhance_steps, _, _, _ = apply_overrides( + async_task, async_task.original_steps, height, width) + exception_result = None + # Ensure the dictionary is initialized + if not hasattr(async_task, 'enhance_stats'): + async_task.enhance_stats = {} + + # Initialize the key if it's missing + async_task.enhance_stats.setdefault(index, 0) + # Initialize the key -1 with a default value of 0 if it's missing + async_task.enhance_stats.setdefault(-1, 0) + for img in imgs: + # Update enhance stats for each image + enhancement_image_start_time = time.perf_counter() + last_enhance_prompt = async_task.prompt + last_enhance_negative_prompt = async_task.negative_prompt + + # Include the enhancement process here for each image + # This logic should be similar to what's in the loop enhancing images_to_enhance + + if enhance_uov_before: + # Enhance the image using the enhance_upscale function + current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result = enhance_upscale( + all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, + current_task_id, denoising_strength, done_steps_inpainting, done_steps_upscaling, enhance_steps, + async_task.prompt, async_task.negative_prompt, final_scheduler_name, height, img, preparation_steps, + switch, tiled, total_count, use_expansion, use_style, use_synthetic_refiner, width, persist_image) + + if exception_result == 'continue': + continue + elif exception_result == 'break': + break + + # Continue with other enhancements as needed + # This involves the code for inpainting, masking, etc. + # For example, if you have another enhancement function like process_enhance, it should go here. + # inpaint for all other tabs + for enhance_mask_dino_prompt_text, enhance_prompt, enhance_negative_prompt, enhance_mask_model, enhance_mask_cloth_category, enhance_mask_sam_model, enhance_mask_text_threshold, enhance_mask_box_threshold, enhance_mask_sam_max_detections, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, enhance_inpaint_strength, enhance_inpaint_respective_field, enhance_inpaint_erode_or_dilate, enhance_mask_invert in async_task.enhance_ctrls: + current_task_id += 1 + current_progress = int(base_progress + (100 - preparation_steps) / float( + all_steps) * (done_steps_upscaling + done_steps_inpainting)) + progressbar(async_task, current_progress, + f'Preparing enhancement {current_task_number}/{total_count} ...') + enhancement_task_start_time = time.perf_counter() + is_last_enhance_for_image = ( + current_task_number) % active_enhance_tabs == 0 and not enhance_uov_after + persist_image = not async_task.save_final_enhanced_image_only or is_last_enhance_for_image + + extras = {} + if enhance_mask_model == 'sam': + print( + f'[Enhance] Searching for "{enhance_mask_dino_prompt_text}"') + elif enhance_mask_model == 'u2net_cloth_seg': + extras['cloth_category'] = enhance_mask_cloth_category + + mask, dino_detection_count, sam_detection_count, sam_detection_on_mask_count = generate_mask_from_image( + img, mask_model=enhance_mask_model, extras=extras, sam_options=SAMOptions( + dino_prompt=enhance_mask_dino_prompt_text, + dino_box_threshold=enhance_mask_box_threshold, + dino_text_threshold=enhance_mask_text_threshold, + dino_erode_or_dilate=async_task.dino_erode_or_dilate, + dino_debug=async_task.debugging_dino, + max_detections=enhance_mask_sam_max_detections, + model_type=enhance_mask_sam_model, + )) + if len(mask.shape) == 3: + mask = mask[:, :, 0] + + if int(enhance_inpaint_erode_or_dilate) != 0: + mask = erode_or_dilate( + mask, enhance_inpaint_erode_or_dilate) + + if enhance_mask_invert: + mask = 255 - mask + + if async_task.debugging_enhance_masks_checkbox: + async_task.yields.append( + ['preview', (current_progress, 'Loading ...', mask)]) + yield_result(async_task, mask, current_progress, async_task.black_out_nsfw, False, + async_task.disable_intermediate_results) + async_task.enhance_stats[index] += 1 + + print( + f'[Enhance] {dino_detection_count} boxes detected') + print( + f'[Enhance] {sam_detection_count} segments detected in boxes') + print( + f'[Enhance] {sam_detection_on_mask_count} segments applied to mask') + + if enhance_mask_model == 'sam' and (dino_detection_count == 0 or not async_task.debugging_dino and sam_detection_on_mask_count == 0): + print( + f'[Enhance] No "{enhance_mask_dino_prompt_text}" detected, skipping') + continue + + goals_enhance = ['inpaint'] + + current_progress, img, enhance_prompt_processed, enhance_negative_prompt_processed = process_enhance( + all_steps, async_task, callback, controlnet_canny_path, controlnet_cpds_path, + current_progress, current_task_id, denoising_strength, enhance_inpaint_disable_initial_latent, + enhance_inpaint_engine, enhance_inpaint_respective_field, enhance_inpaint_strength, + enhance_prompt, enhance_negative_prompt, final_scheduler_name, goals_enhance, height, img, mask, + preparation_steps, enhance_steps, switch, tiled, total_count, use_expansion, use_style, + use_synthetic_refiner, width, persist_image=persist_image) + async_task.enhance_stats[index] += 1 + + if (should_process_enhance_uov and async_task.enhance_uov_processing_order == flags.enhancement_uov_after + and async_task.enhance_uov_prompt_type == flags.enhancement_uov_prompt_type_last_filled): + if enhance_prompt_processed != '': + last_enhance_prompt = enhance_prompt_processed + if enhance_negative_prompt_processed != '': + last_enhance_negative_prompt = enhance_negative_prompt_processed + + done_steps_inpainting += enhance_steps + + enhancement_task_time = time.perf_counter() - enhancement_task_start_time + print( + f'Enhancement time: {enhancement_task_time:.2f} seconds') + + if exception_result == 'break': + break + + if enhance_uov_after: + current_task_id += 1 + # last step in enhance, always save + persist_image = True + current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result = enhance_upscale( + all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, + current_task_id, denoising_strength, done_steps_inpainting, done_steps_upscaling, enhance_steps, + last_enhance_prompt, last_enhance_negative_prompt, final_scheduler_name, height, img, + preparation_steps, switch, tiled, total_count, use_expansion, use_style, use_synthetic_refiner, + width, persist_image) + async_task.enhance_stats[index] += 1 + + if exception_result == 'continue': + continue + elif exception_result == 'break': + break + + enhancement_image_time = time.perf_counter() - enhancement_image_start_time + print( + f'Enhancement image time: {enhancement_image_time:.2f} seconds') + async_task.enhance_stats[-1] += 1 + + def loop_image_files(files, bulk_enhance_callback, + async_task, + current_progress, + all_steps, + height, + width, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner + ): + for index, file_name in enumerate(files): + try: + async_task.current_task_id = index + + # Build full path to the file + image = grh.Image(type='numpy')._format_image(Image.open( + file_name)) + image = HWC3(image) + + # Add the image to the list + images_to_enhance = [image] + + _, height, width = image.shape # Unpack the shape into C, H, W + + yield_result(async_task, image, current_progress, async_task.black_out_nsfw, False, + async_task.disable_intermediate_results) + enhance_images( + images_to_enhance, + async_task, + 0, + current_progress, + all_steps, + height, + width, + bulk_enhance_callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + 0 + ) + except ldm_patched.modules.model_management.InterruptProcessingException: + if async_task.last_stop == 'skip': + print_user_skipped(async_task) + continue + else: + print_user_stopped(async_task) + stop_processing( + async_task, async_task.processing_start_time) + break + @torch.no_grad() @torch.inference_mode() def handler(async_task: AsyncTask): preparation_start_time = time.perf_counter() async_task.processing = True - async_task.outpaint_selections = [o.lower() for o in async_task.outpaint_selections] + async_task.outpaint_selections = [ + o.lower() for o in async_task.outpaint_selections] base_model_additional_loras = [] async_task.uov_method = async_task.uov_method.casefold() async_task.enhance_uov_method = async_task.enhance_uov_method.casefold() @@ -1091,16 +1459,20 @@ def handler(async_task: AsyncTask): current_progress = 0 if async_task.performance_selection == Performance.EXTREME_SPEED: - set_lcm_defaults(async_task, current_progress, advance_progress=True) + set_lcm_defaults(async_task, current_progress, + advance_progress=True) elif async_task.performance_selection == Performance.LIGHTNING: - set_lightning_defaults(async_task, current_progress, advance_progress=True) + set_lightning_defaults( + async_task, current_progress, advance_progress=True) elif async_task.performance_selection == Performance.HYPER_SD: - set_hyper_sd_defaults(async_task, current_progress, advance_progress=True) + set_hyper_sd_defaults( + async_task, current_progress, advance_progress=True) print(f'[Parameters] Adaptive CFG = {async_task.adaptive_cfg}') print(f'[Parameters] CLIP Skip = {async_task.clip_skip}') print(f'[Parameters] Sharpness = {async_task.sharpness}') - print(f'[Parameters] ControlNet Softness = {async_task.controlnet_softness}') + print( + f'[Parameters] ControlNet Softness = {async_task.controlnet_softness}') print(f'[Parameters] ADM Scale = ' f'{async_task.adm_scaler_positive} : ' f'{async_task.adm_scaler_negative} : ' @@ -1115,7 +1487,8 @@ def handler(async_task: AsyncTask): denoising_strength = 1.0 tiled = False - width, height = async_task.aspect_ratios_selection.replace('×', ' ').split(' ')[:2] + width, height = async_task.aspect_ratios_selection.replace('×', ' ').split(' ')[ + :2] width, height = int(width), int(height) skip_prompt_processing = False @@ -1135,8 +1508,8 @@ def handler(async_task: AsyncTask): goals = [] tasks = [] current_progress = 1 - - if async_task.input_image_checkbox: + input_image = async_task.input_image_checkbox or async_task.bulk_enhance_enabled + if input_image: base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, inpaint_head_model_path, inpaint_image, inpaint_mask, ip_adapter_face_path, ip_adapter_path, ip_negative_path, skip_prompt_processing, use_synthetic_refiner = apply_image_input( async_task, base_model_additional_loras, clip_vision_path, controlnet_canny_path, controlnet_cpds_path, goals, inpaint_head_model_path, inpaint_image, inpaint_mask, inpaint_parameterized, ip_adapter_face_path, @@ -1144,13 +1517,18 @@ def handler(async_task: AsyncTask): # Load or unload CNs progressbar(async_task, current_progress, 'Loading control models ...') - pipeline.refresh_controlnets([controlnet_canny_path, controlnet_cpds_path]) - ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_path) - ip_adapter.load_ip_adapter(clip_vision_path, ip_negative_path, ip_adapter_face_path) - - async_task.steps, switch, width, height = apply_overrides(async_task, async_task.steps, height, width) - - print(f'[Parameters] Sampler = {async_task.sampler_name} - {async_task.scheduler_name}') + pipeline.refresh_controlnets( + [controlnet_canny_path, controlnet_cpds_path]) + ip_adapter.load_ip_adapter( + clip_vision_path, ip_negative_path, ip_adapter_path) + ip_adapter.load_ip_adapter( + clip_vision_path, ip_negative_path, ip_adapter_face_path) + + async_task.steps, switch, width, height = apply_overrides( + async_task, async_task.steps, height, width) + + print( + f'[Parameters] Sampler = {async_task.sampler_name} - {async_task.scheduler_name}') print(f'[Parameters] Steps = {async_task.steps} - {switch}') progressbar(async_task, current_progress, 'Initializing ...') @@ -1158,15 +1536,16 @@ def handler(async_task: AsyncTask): loras = async_task.loras if not skip_prompt_processing: tasks, use_expansion, loras, current_progress = process_prompt(async_task, async_task.prompt, async_task.negative_prompt, - base_model_additional_loras, async_task.image_number, - async_task.disable_seed_increment, use_expansion, use_style, - use_synthetic_refiner, current_progress, advance_progress=True) + base_model_additional_loras, async_task.image_number, + async_task.disable_seed_increment, use_expansion, use_style, + use_synthetic_refiner, current_progress, advance_progress=True) if len(goals) > 0: current_progress += 1 progressbar(async_task, current_progress, 'Image processing ...') - should_enhance = async_task.enhance_checkbox and (async_task.enhance_uov_method != flags.disabled.casefold() or len(async_task.enhance_ctrls) > 0) + should_enhance = async_task.enhance_checkbox and ( + async_task.enhance_uov_method != flags.disabled.casefold() or len(async_task.enhance_ctrls) > 0) if 'vary' in goals: async_task.uov_input_image, denoising_strength, initial_latent, width, height, current_progress = apply_vary( @@ -1180,10 +1559,13 @@ def handler(async_task: AsyncTask): if direct_return: d = [('Upscale (Fast)', 'upscale_fast', '2x')] if modules.config.default_black_out_nsfw or async_task.black_out_nsfw: - progressbar(async_task, 100, 'Checking for NSFW content ...') - async_task.uov_input_image = default_censor(async_task.uov_input_image) + progressbar(async_task, 100, + 'Checking for NSFW content ...') + async_task.uov_input_image = default_censor( + async_task.uov_input_image) progressbar(async_task, 100, 'Saving image to system ...') - uov_input_image_path = log(async_task.uov_input_image, d, output_format=async_task.output_format) + uov_input_image_path = log( + async_task.uov_input_image, d, output_format=async_task.output_format) yield_result(async_task, uov_input_image_path, 100, async_task.black_out_nsfw, False, do_not_show_finished_images=True) return @@ -1206,7 +1588,8 @@ def handler(async_task: AsyncTask): return if 'cn' in goals: - apply_control_nets(async_task, height, ip_adapter_face_path, ip_adapter_path, width, current_progress) + apply_control_nets(async_task, height, ip_adapter_face_path, + ip_adapter_path, width, current_progress) if async_task.debugging_cn_preprocessor: return @@ -1214,34 +1597,32 @@ def handler(async_task: AsyncTask): apply_freeu(async_task) # async_task.steps can have value of uov steps here when upscale has been applied - steps, _, _, _ = apply_overrides(async_task, async_task.steps, height, width) - - images_to_enhance = [] - if 'enhance' in goals: - async_task.image_number = 1 - images_to_enhance += [async_task.enhance_input_image] - height, width, _ = async_task.enhance_input_image.shape - # input image already provided, processing is skipped - steps = 0 - yield_result(async_task, async_task.enhance_input_image, current_progress, async_task.black_out_nsfw, False, - async_task.disable_intermediate_results) + steps, _, _, _ = apply_overrides( + async_task, async_task.steps, height, width) all_steps = steps * async_task.image_number - - if async_task.enhance_checkbox and async_task.enhance_uov_method != flags.disabled.casefold(): + image_enhance = async_task.enhance_checkbox or async_task.bulk_enhance_enabled + if image_enhance and async_task.enhance_uov_method != flags.disabled.casefold(): enhance_upscale_steps = async_task.performance_selection.steps() if 'upscale' in async_task.enhance_uov_method: if 'fast' in async_task.enhance_uov_method: enhance_upscale_steps = 0 else: enhance_upscale_steps = async_task.performance_selection.steps_uov() - enhance_upscale_steps, _, _, _ = apply_overrides(async_task, enhance_upscale_steps, height, width) + enhance_upscale_steps, _, _, _ = apply_overrides( + async_task, enhance_upscale_steps, height, width) enhance_upscale_steps_total = async_task.image_number * enhance_upscale_steps all_steps += enhance_upscale_steps_total - if async_task.enhance_checkbox and len(async_task.enhance_ctrls) != 0: - enhance_steps, _, _, _ = apply_overrides(async_task, async_task.original_steps, height, width) - all_steps += async_task.image_number * len(async_task.enhance_ctrls) * enhance_steps + if image_enhance and len(async_task.enhance_ctrls) != 0: + enhance_steps, _, _, _ = apply_overrides( + async_task, async_task.original_steps, height, width) + all_steps += async_task.image_number * \ + len(async_task.enhance_ctrls) * enhance_steps + + if image_enhance and len(async_task.enhance_ctrls) == 0 and async_task.enhance_uov_method == flags.disabled.casefold(): + print( + f"Warning - Enhancements will be skipped.\nNo Enhancements were selected. \n Please check the input values. \n\n") all_steps = max(all_steps, 1) @@ -1260,17 +1641,129 @@ def handler(async_task: AsyncTask): final_scheduler_name = patch_samplers(async_task) print(f'Using {final_scheduler_name} scheduler.') - async_task.yields.append(['preview', (current_progress, 'Moving model to GPU ...', None)]) + async_task.yields.append( + ['preview', (current_progress, 'Moving model to GPU ...', None)]) processing_start_time = time.perf_counter() + async_task.processing_start_time = time.perf_counter() preparation_steps = current_progress total_count = async_task.image_number + async_task.current_task_id = 0 + + + def bulk_enhance_callback(step, x0, x, total_steps, y): + if step == 0: + async_task.callback_steps = 0 + + # Calculate callback steps + async_task.callback_steps += (100 - + preparation_steps) / float(all_steps) + + # Append to yields + async_task.yields.append([ + 'preview', ( + int(current_progress + async_task.callback_steps), + f'Sampling step {step + 1}/{total_steps}, image {async_task.current_task_id + 1}/{total_count} ...', + y + ) + ]) + + images_to_enhance = [] + if 'enhance' in goals: + try: + async_task.image_number = 1 + images_to_enhance = [async_task.enhance_input_image] + height, width, _ = async_task.enhance_input_image.shape + # input image already provided, processing is skipped + steps = 0 + yield_result(async_task, async_task.enhance_input_image, current_progress, async_task.black_out_nsfw, False, + async_task.disable_intermediate_results) + enhance_images( + images_to_enhance, + async_task, + 0, + current_progress, + all_steps, + height, + width, + bulk_enhance_callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + 0 + ) + except ldm_patched.modules.model_management.InterruptProcessingException: + if async_task.last_stop == 'skip': + print_user_skipped(async_task) + else: + print_user_stopped(async_task) + + if 'bulk_enhance_files' in goals: + files = [] + + for file in async_task.bulk_enhance_file_explorer: + files.append(file.orig_name) + total_count = len(files) + loop_image_files(files, bulk_enhance_callback, + async_task, + current_progress, + all_steps, + height, + width, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner + ) + + if 'bulk_enhance_folder' in goals: + valid_extensions = (".jpg", ".jpeg", ".png", + ".bmp", ".tiff", ".webp") + files = [] + + for root, dirs, files_in_dir in os.walk(async_task.bulk_enhance_input_path): + for file_name in files_in_dir: + full_file_path = os.path.join(root, file_name) + if file_name.lower().endswith(valid_extensions): + files.append(full_file_path) + total_count = len(files) + loop_image_files(files, bulk_enhance_callback, + async_task, + current_progress, + all_steps, + height, + width, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner + ) def callback(step, x0, x, total_steps, y): if step == 0: async_task.callback_steps = 0 - async_task.callback_steps += (100 - preparation_steps) / float(all_steps) + async_task.callback_steps += (100 - + preparation_steps) / float(all_steps) async_task.yields.append(['preview', ( int(current_progress + async_task.callback_steps), f'Sampling step {step + 1}/{total_steps}, image {current_task_id + 1}/{total_count} ...', y)]) @@ -1279,187 +1772,96 @@ def callback(step, x0, x, total_steps, y): persist_image = not async_task.should_enhance or not async_task.save_final_enhanced_image_only for current_task_id, task in enumerate(tasks): - progressbar(async_task, current_progress, f'Preparing task {current_task_id + 1}/{async_task.image_number} ...') + progressbar(async_task, current_progress, + f'Preparing task {current_task_id + 1}/{async_task.image_number} ...') + current_task_number = current_task_id + 1 + setup(async_task) + patch_samplers(async_task) execution_start_time = time.perf_counter() - try: imgs, img_paths, current_progress = process_task(all_steps, async_task, callback, controlnet_canny_path, controlnet_cpds_path, current_task_id, denoising_strength, final_scheduler_name, goals, - initial_latent, async_task.steps, switch, task['c'], + initial_latent, async_task.steps, switch, task[ + 'c'], task['uc'], task, loras, tiled, use_expansion, width, height, current_progress, preparation_steps, async_task.image_number, show_intermediate_results, persist_image) - current_progress = int(preparation_steps + (100 - preparation_steps) / float(all_steps) * async_task.steps * (current_task_id + 1)) - images_to_enhance += imgs + current_progress = int(preparation_steps + (100 - preparation_steps) / float( + all_steps) * async_task.steps * (current_task_id + 1)) + + if not async_task.should_enhance: + print(f'[Enhance] Skipping, preconditions aren\'t met') + continue + + images_to_enhance = imgs + enhance_images( + images_to_enhance, + async_task, + current_task_id, + current_progress, + all_steps, + height, + width, + callback, + controlnet_canny_path, + controlnet_cpds_path, + denoising_strength, + final_scheduler_name, + preparation_steps, + switch, + tiled, + use_expansion, + use_style, + use_synthetic_refiner, + current_task_number + ) except ldm_patched.modules.model_management.InterruptProcessingException: if async_task.last_stop == 'skip': - print('User skipped') - async_task.last_stop = False + print_user_skipped(async_task) continue else: - print('User stopped') + print_user_stopped(async_task) break del task['c'], task['uc'] # Save memory execution_time = time.perf_counter() - execution_start_time print(f'Generating and saving time: {execution_time:.2f} seconds') - if not async_task.should_enhance: - print(f'[Enhance] Skipping, preconditions aren\'t met') - stop_processing(async_task, processing_start_time) - return - - progressbar(async_task, current_progress, 'Processing enhance ...') - - active_enhance_tabs = len(async_task.enhance_ctrls) - should_process_enhance_uov = async_task.enhance_uov_method != flags.disabled.casefold() - enhance_uov_before = False - enhance_uov_after = False - if should_process_enhance_uov: - active_enhance_tabs += 1 - enhance_uov_before = async_task.enhance_uov_processing_order == flags.enhancement_uov_before - enhance_uov_after = async_task.enhance_uov_processing_order == flags.enhancement_uov_after - total_count = len(images_to_enhance) * active_enhance_tabs - async_task.images_to_enhance_count = len(images_to_enhance) - - base_progress = current_progress - current_task_id = -1 - done_steps_upscaling = 0 - done_steps_inpainting = 0 - enhance_steps, _, _, _ = apply_overrides(async_task, async_task.original_steps, height, width) - exception_result = None - for index, img in enumerate(images_to_enhance): - async_task.enhance_stats[index] = 0 - enhancement_image_start_time = time.perf_counter() - - last_enhance_prompt = async_task.prompt - last_enhance_negative_prompt = async_task.negative_prompt - - if enhance_uov_before: - current_task_id += 1 - persist_image = not async_task.save_final_enhanced_image_only or active_enhance_tabs == 0 - current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result = enhance_upscale( - all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, - current_task_id, denoising_strength, done_steps_inpainting, done_steps_upscaling, enhance_steps, - async_task.prompt, async_task.negative_prompt, final_scheduler_name, height, img, preparation_steps, - switch, tiled, total_count, use_expansion, use_style, use_synthetic_refiner, width, persist_image) - async_task.enhance_stats[index] += 1 - - if exception_result == 'continue': - continue - elif exception_result == 'break': - break - - # inpaint for all other tabs - for enhance_mask_dino_prompt_text, enhance_prompt, enhance_negative_prompt, enhance_mask_model, enhance_mask_cloth_category, enhance_mask_sam_model, enhance_mask_text_threshold, enhance_mask_box_threshold, enhance_mask_sam_max_detections, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, enhance_inpaint_strength, enhance_inpaint_respective_field, enhance_inpaint_erode_or_dilate, enhance_mask_invert in async_task.enhance_ctrls: - current_task_id += 1 - current_progress = int(base_progress + (100 - preparation_steps) / float(all_steps) * (done_steps_upscaling + done_steps_inpainting)) - progressbar(async_task, current_progress, f'Preparing enhancement {current_task_id + 1}/{total_count} ...') - enhancement_task_start_time = time.perf_counter() - is_last_enhance_for_image = (current_task_id + 1) % active_enhance_tabs == 0 and not enhance_uov_after - persist_image = not async_task.save_final_enhanced_image_only or is_last_enhance_for_image - - extras = {} - if enhance_mask_model == 'sam': - print(f'[Enhance] Searching for "{enhance_mask_dino_prompt_text}"') - elif enhance_mask_model == 'u2net_cloth_seg': - extras['cloth_category'] = enhance_mask_cloth_category - - mask, dino_detection_count, sam_detection_count, sam_detection_on_mask_count = generate_mask_from_image( - img, mask_model=enhance_mask_model, extras=extras, sam_options=SAMOptions( - dino_prompt=enhance_mask_dino_prompt_text, - dino_box_threshold=enhance_mask_box_threshold, - dino_text_threshold=enhance_mask_text_threshold, - dino_erode_or_dilate=async_task.dino_erode_or_dilate, - dino_debug=async_task.debugging_dino, - max_detections=enhance_mask_sam_max_detections, - model_type=enhance_mask_sam_model, - )) - if len(mask.shape) == 3: - mask = mask[:, :, 0] - - if int(enhance_inpaint_erode_or_dilate) != 0: - mask = erode_or_dilate(mask, enhance_inpaint_erode_or_dilate) - - if enhance_mask_invert: - mask = 255 - mask - - if async_task.debugging_enhance_masks_checkbox: - async_task.yields.append(['preview', (current_progress, 'Loading ...', mask)]) - yield_result(async_task, mask, current_progress, async_task.black_out_nsfw, False, - async_task.disable_intermediate_results) - async_task.enhance_stats[index] += 1 - - print(f'[Enhance] {dino_detection_count} boxes detected') - print(f'[Enhance] {sam_detection_count} segments detected in boxes') - print(f'[Enhance] {sam_detection_on_mask_count} segments applied to mask') - - if enhance_mask_model == 'sam' and (dino_detection_count == 0 or not async_task.debugging_dino and sam_detection_on_mask_count == 0): - print(f'[Enhance] No "{enhance_mask_dino_prompt_text}" detected, skipping') - continue - - goals_enhance = ['inpaint'] - - try: - current_progress, img, enhance_prompt_processed, enhance_negative_prompt_processed = process_enhance( - all_steps, async_task, callback, controlnet_canny_path, controlnet_cpds_path, - current_progress, current_task_id, denoising_strength, enhance_inpaint_disable_initial_latent, - enhance_inpaint_engine, enhance_inpaint_respective_field, enhance_inpaint_strength, - enhance_prompt, enhance_negative_prompt, final_scheduler_name, goals_enhance, height, img, mask, - preparation_steps, enhance_steps, switch, tiled, total_count, use_expansion, use_style, - use_synthetic_refiner, width, persist_image=persist_image) - async_task.enhance_stats[index] += 1 - - if (should_process_enhance_uov and async_task.enhance_uov_processing_order == flags.enhancement_uov_after - and async_task.enhance_uov_prompt_type == flags.enhancement_uov_prompt_type_last_filled): - if enhance_prompt_processed != '': - last_enhance_prompt = enhance_prompt_processed - if enhance_negative_prompt_processed != '': - last_enhance_negative_prompt = enhance_negative_prompt_processed - - except ldm_patched.modules.model_management.InterruptProcessingException: - if async_task.last_stop == 'skip': - print('User skipped') - async_task.last_stop = False - continue - else: - print('User stopped') - exception_result = 'break' - break - finally: - done_steps_inpainting += enhance_steps - - enhancement_task_time = time.perf_counter() - enhancement_task_start_time - print(f'Enhancement time: {enhancement_task_time:.2f} seconds') - - if exception_result == 'break': - break - - if enhance_uov_after: - current_task_id += 1 - # last step in enhance, always save - persist_image = True - current_task_id, done_steps_inpainting, done_steps_upscaling, img, exception_result = enhance_upscale( - all_steps, async_task, base_progress, callback, controlnet_canny_path, controlnet_cpds_path, - current_task_id, denoising_strength, done_steps_inpainting, done_steps_upscaling, enhance_steps, - last_enhance_prompt, last_enhance_negative_prompt, final_scheduler_name, height, img, - preparation_steps, switch, tiled, total_count, use_expansion, use_style, use_synthetic_refiner, - width, persist_image) - async_task.enhance_stats[index] += 1 - - if exception_result == 'continue': - continue - elif exception_result == 'break': - break + stop_processing(async_task, processing_start_time) + return - enhancement_image_time = time.perf_counter() - enhancement_image_start_time - print(f'Enhancement image time: {enhancement_image_time:.2f} seconds') + def setup(async_task: AsyncTask): + base_model_additional_loras = [] + current_progress = 0 + use_synthetic_refiner = False + if fooocus_expansion in async_task.style_selections: + use_expansion = True + async_task.style_selections.remove(fooocus_expansion) + else: + use_expansion = False + controlnet_canny_path = None + controlnet_cpds_path = None + clip_vision_path, ip_negative_path, ip_adapter_path, ip_adapter_face_path = None, None, None, None + use_style = len(async_task.style_selections) > 0 + # Load or unload CNs + progressbar(async_task, current_progress, 'Loading control models ...') + pipeline.refresh_controlnets( + [controlnet_canny_path, controlnet_cpds_path]) + ip_adapter.load_ip_adapter( + clip_vision_path, ip_negative_path, ip_adapter_path) + ip_adapter.load_ip_adapter( + clip_vision_path, ip_negative_path, ip_adapter_face_path) + inpaint_worker.current_task = None - stop_processing(async_task, processing_start_time) + process_prompt(async_task, async_task.prompt, async_task.negative_prompt, + base_model_additional_loras, 1, + async_task.disable_seed_increment, use_expansion, use_style, + use_synthetic_refiner, current_progress, advance_progress=True) + apply_patch_settings(async_task) return while True: diff --git a/modules/bulk_enhance_helpers.py b/modules/bulk_enhance_helpers.py new file mode 100644 index 000000000..19b7f61b0 --- /dev/null +++ b/modules/bulk_enhance_helpers.py @@ -0,0 +1,140 @@ +import gradio as gr +import os +import modules.config +import modules.html +import modules.meta_parser + +from tkinter import Tk, filedialog + + +def process_directories(directory_paths): + if not directory_paths: + return "No directories selected." + + results = [] + for directory in directory_paths: + # List files in the directory + files = os.listdir(directory) + results.append(f"Contents of {directory}:\n" + "\n".join(files)) + + return "\n\n".join(results) + + +def update_visibility(x): + # Add more updates for other components + return [gr.update(visible=x), gr.update(visible=x)] + + +def list_to_string(filenames): + # Join the filenames list into a comma-separated string + file_list = ', '.join(filenames) + return file_list + + +def on_browse(data_type): + root = Tk() + root.attributes("-topmost", True) + root.withdraw() + if data_type == "Files": + filenames = filedialog.askopenfilenames() + if len(filenames) > 0: + root.destroy() + file_list = list_to_string(filenames) + return file_list + else: + filename = "Files not seleceted" + root.destroy() + return None + + elif data_type == "Folder": + filename = filedialog.askdirectory() + if filename: + if os.path.isdir(filename): + root.destroy() + return str(filename) + else: + root.destroy() + return str(filename) + else: + filename = "Folder not seleceted" + root.destroy() + return None + + +def on_file_change(files, data_type): + if files and data_type == "Files": + return gr.update(visible=True), gr.update(), gr.update(value=True) + + # If no files are selected, hide file explorer and clear input_path + if not files and data_type == "Files": + return gr.update(visible=False), gr.update(value=""), gr.update(value=False) + + if data_type == "Folder": + return gr.update(visible=False), gr.update(), gr.update(value=True) + + return gr.update(visible=False), gr.update(), gr.update(value=False) + + +def on_input_change(input, file_explorer): + if input: + # Verify with normalised version of path + input_path = os.path.normpath(os.path.realpath(input)) + + if os.path.isdir(os.path.realpath(input_path)): + # Return an empty list if input_path is a directory + return None, gr.update(visible=True), gr.update(value=True) + else: + # Return an empty list if input_path is empty + return None, gr.update(visible=False), gr.update(value=False) + + # Initialize a dictionary to track unique file names and their paths + unique_file_paths = {} + + # Process the input_path string + if input_path: + # Clean up the input path string and split it into a list of file paths + file_paths_list = input_path.strip("()").replace("'", "").split(", ") + # Extract file names and ensure uniqueness + for path in file_paths_list: + file_name = os.path.basename(path) + unique_file_paths[file_name] = path + + # Process file_explorer items if provided + if file_explorer: + # Extract 'orig_name' from each file_explorer object and ensure uniqueness + for item in file_explorer: + sanitized_path = item.orig_name + file_name = os.path.basename(sanitized_path) + # Store the path, replacing any existing path with the same file name + unique_file_paths[file_name] = sanitized_path + + # Convert the dictionary values back to a list of unique file paths + if len(unique_file_paths.values()) > 0: + return list(unique_file_paths.values()), gr.update(visible=False), gr.update(value=True) + else: + return None, gr.update(visible=False), gr.update(value=False) + + +def on_click_clear(): + return None, None, gr.update(visible=False), gr.update(visible=False) + +# Function to set prompts based on the selected type + + +def update_prompts(selected_type): + # Ensure selected_type is a valid key and exists in the dictionary + if selected_type in modules.config.default_enhance_prompts: + positive_prompt = modules.config.default_enhance_prompts[selected_type]['positive'] + negative_prompt = modules.config.default_enhance_prompts[selected_type]['negative'] + return positive_prompt, negative_prompt + else: + # Returning default or empty values + return "Default positive prompt", "Default negative prompt" + + +def on_selection_change(selected_type): + # Get prompts based on selected_type + positive_prompt, negative_prompt = update_prompts(selected_type[0]) + + # Return the prompts + return positive_prompt, negative_prompt diff --git a/modules/config.py b/modules/config.py index 8609b4154..020371253 100644 --- a/modules/config.py +++ b/modules/config.py @@ -7,7 +7,6 @@ import tempfile import modules.flags import modules.sdxl_styles - from modules.model_loader import load_file_from_url from modules.extra_utils import makedirs_with_log, get_files_from_folder, try_eval_env_var from modules.flags import OutputFormat, Performance, MetadataScheme @@ -21,9 +20,11 @@ def get_config_path(key, default_value): else: return os.path.abspath(default_value) + wildcards_max_bfs_depth = 64 config_path = get_config_path('config_path', "./config.txt") -config_example_path = get_config_path('config_example_path', "config_modification_tutorial.txt") +config_example_path = get_config_path( + 'config_example_path', "config_modification_tutorial.txt") config_dict = {} always_save_keys = [] visited_keys = [] @@ -41,9 +42,11 @@ def get_config_path(key, default_value): config_dict.update(json.load(json_file)) always_save_keys = list(config_dict.keys()) except Exception as e: - print(f'Failed to load config file "{config_path}" . The reason is: {str(e)}') + print( + f'Failed to load config file "{config_path}" . The reason is: {str(e)}') print('Please make sure that:') - print(f'1. The file "{config_path}" is a valid text file, and you have access to read it.') + print( + f'1. The file "{config_path}" is a valid text file, and you have access to read it.') print('2. Use "\\\\" instead of "\\" when describing paths.') print('3. There is no "," before the last "}".') print('4. All key/value formats are correct.') @@ -56,7 +59,8 @@ def try_load_deprecated_user_path_config(): return try: - deprecated_config_dict = json.load(open('user_path_config.txt', "r", encoding="utf-8")) + deprecated_config_dict = json.load( + open('user_path_config.txt', "r", encoding="utf-8")) def replace_config(old_key, new_key): if old_key in deprecated_config_dict: @@ -75,7 +79,8 @@ def replace_config(old_key, new_key): replace_config('temp_outputs_path', 'path_outputs') if deprecated_config_dict.get("default_model", None) == 'juggernautXL_version6Rundiffusion.safetensors': - os.replace('user_path_config.txt', 'user_path_config-deprecated.txt') + os.replace('user_path_config.txt', + 'user_path_config-deprecated.txt') print('Config updated successfully in silence. ' 'A backup of previous config is written to "user_path_config-deprecated.txt".') return @@ -86,7 +91,8 @@ def replace_config(old_key, new_key): print('Loading using deprecated old models and deprecated old configs.') return else: - os.replace('user_path_config.txt', 'user_path_config-deprecated.txt') + os.replace('user_path_config.txt', + 'user_path_config-deprecated.txt') print('Config updated successfully by user. ' 'A backup of previous config is written to "user_path_config-deprecated.txt".') return @@ -98,6 +104,7 @@ def replace_config(old_key, new_key): try_load_deprecated_user_path_config() + def get_presets(): preset_folder = 'presets' presets = ['initial'] @@ -107,10 +114,12 @@ def get_presets(): return presets + [f[:f.index(".json")] for f in os.listdir(preset_folder) if f.endswith('.json')] + def update_presets(): global available_presets available_presets = get_presets() + def try_get_preset_content(preset): if isinstance(preset, str): preset_path = os.path.abspath(f'./presets/{preset}.json') @@ -127,18 +136,22 @@ def try_get_preset_content(preset): print(e) return {} + available_presets = get_presets() preset = args_manager.args.preset config_dict.update(try_get_preset_content(preset)) + def get_path_output() -> str: """ Checking output path argument and overriding default path. """ global config_dict - path_output = get_dir_or_set_default('path_outputs', '../outputs/', make_directory=True) + path_output = get_dir_or_set_default( + 'path_outputs', '../outputs/', make_directory=True) if args_manager.args.output_path: - print(f'Overriding config value path_outputs with {args_manager.args.output_path}') + print( + f'Overriding config value path_outputs with {args_manager.args.output_path}') config_dict['path_outputs'] = path_output = args_manager.args.output_path return path_output @@ -172,15 +185,18 @@ def get_dir_or_set_default(key, default_value, as_array=False, make_directory=Fa return v if v is not None: - print(f'Failed to load config key: {json.dumps({key:v})} is invalid or does not exist; will use {json.dumps({key:default_value})} instead.') + print( + f'Failed to load config key: {json.dumps({key:v})} is invalid or does not exist; will use {json.dumps({key:default_value})} instead.') if isinstance(default_value, list): dp = [] for path in default_value: - abs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), path)) + abs_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), path)) dp.append(abs_path) os.makedirs(abs_path, exist_ok=True) else: - dp = os.path.abspath(os.path.join(os.path.dirname(__file__), default_value)) + dp = os.path.abspath(os.path.join( + os.path.dirname(__file__), default_value)) os.makedirs(dp, exist_ok=True) if as_array: dp = [dp] @@ -188,18 +204,26 @@ def get_dir_or_set_default(key, default_value, as_array=False, make_directory=Fa return dp -paths_checkpoints = get_dir_or_set_default('path_checkpoints', ['../models/checkpoints/'], True) +paths_checkpoints = get_dir_or_set_default( + 'path_checkpoints', ['../models/checkpoints/'], True) paths_loras = get_dir_or_set_default('path_loras', ['../models/loras/'], True) -path_embeddings = get_dir_or_set_default('path_embeddings', '../models/embeddings/') -path_vae_approx = get_dir_or_set_default('path_vae_approx', '../models/vae_approx/') +path_embeddings = get_dir_or_set_default( + 'path_embeddings', '../models/embeddings/') +path_vae_approx = get_dir_or_set_default( + 'path_vae_approx', '../models/vae_approx/') path_vae = get_dir_or_set_default('path_vae', '../models/vae/') -path_upscale_models = get_dir_or_set_default('path_upscale_models', '../models/upscale_models/') +path_upscale_models = get_dir_or_set_default( + 'path_upscale_models', '../models/upscale_models/') path_inpaint = get_dir_or_set_default('path_inpaint', '../models/inpaint/') -path_controlnet = get_dir_or_set_default('path_controlnet', '../models/controlnet/') -path_clip_vision = get_dir_or_set_default('path_clip_vision', '../models/clip_vision/') -path_fooocus_expansion = get_dir_or_set_default('path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion') +path_controlnet = get_dir_or_set_default( + 'path_controlnet', '../models/controlnet/') +path_clip_vision = get_dir_or_set_default( + 'path_clip_vision', '../models/clip_vision/') +path_fooocus_expansion = get_dir_or_set_default( + 'path_fooocus_expansion', '../models/prompt_expansion/fooocus_expansion') path_wildcards = get_dir_or_set_default('path_wildcards', '../wildcards/') -path_safety_checker = get_dir_or_set_default('path_safety_checker', '../models/safety_checker/') +path_safety_checker = get_dir_or_set_default( + 'path_safety_checker', '../models/safety_checker/') path_sam = get_dir_or_set_default('path_sam', '../models/sam/') path_outputs = get_path_output() @@ -209,7 +233,7 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_ if key not in visited_keys: visited_keys.append(key) - + v = os.getenv(key) if v is not None: v = try_eval_env_var(v, expected_type) @@ -228,7 +252,8 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_ return v else: if v is not None: - print(f'Failed to load config key: {json.dumps({key:v})} is invalid; will use {json.dumps({key:default_value})} instead.') + print( + f'Failed to load config key: {json.dumps({key:v})} is invalid; will use {json.dumps({key:default_value})} instead.') config_dict[key] = default_value return default_value @@ -274,7 +299,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: previous_default_models = get_config_item_or_set_default( key='previous_default_models', default_value=[], - validator=lambda x: isinstance(x, list) and all(isinstance(k, str) for k in x), + validator=lambda x: isinstance(x, list) and all( + isinstance(k, str) for k in x), expected_type=list ) default_refiner_model_name = default_refiner = get_config_item_or_set_default( @@ -331,15 +357,18 @@ def init_temp_path(path: str | None, default_path: str) -> str: ] ], validator=lambda x: isinstance(x, list) and all( - len(y) == 3 and isinstance(y[0], bool) and isinstance(y[1], str) and isinstance(y[2], numbers.Number) + len(y) == 3 and isinstance(y[0], bool) and isinstance( + y[1], str) and isinstance(y[2], numbers.Number) or len(y) == 2 and isinstance(y[0], str) and isinstance(y[1], numbers.Number) for y in x), expected_type=list ) -default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else (True, y[0], y[1]) for y in default_loras] +default_loras = [(y[0], y[1], y[2]) if len(y) == 3 else ( + True, y[0], y[1]) for y in default_loras] default_max_lora_number = get_config_item_or_set_default( key='default_max_lora_number', - default_value=len(default_loras) if isinstance(default_loras, list) and len(default_loras) > 0 else 5, + default_value=len(default_loras) if isinstance( + default_loras, list) and len(default_loras) > 0 else 5, validator=lambda x: isinstance(x, int) and x >= 1, expected_type=int ) @@ -380,7 +409,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: "Fooocus Enhance", "Fooocus Sharp" ], - validator=lambda x: isinstance(x, list) and all(y in modules.sdxl_styles.legal_style_names for y in x), + validator=lambda x: isinstance(x, list) and all( + y in modules.sdxl_styles.legal_style_names for y in x), expected_type=list ) default_prompt_negative = get_config_item_or_set_default( @@ -448,37 +478,43 @@ def init_temp_path(path: str | None, default_path: str) -> str: default_image_number = get_config_item_or_set_default( key='default_image_number', default_value=2, - validator=lambda x: isinstance(x, int) and 1 <= x <= default_max_image_number, + validator=lambda x: isinstance( + x, int) and 1 <= x <= default_max_image_number, expected_type=int ) checkpoint_downloads = get_config_item_or_set_default( key='checkpoint_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) lora_downloads = get_config_item_or_set_default( key='lora_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) embeddings_downloads = get_config_item_or_set_default( key='embeddings_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) vae_downloads = get_config_item_or_set_default( key='vae_downloads', default_value={}, - validator=lambda x: isinstance(x, dict) and all(isinstance(k, str) and isinstance(v, str) for k, v in x.items()), + validator=lambda x: isinstance(x, dict) and all( + isinstance(k, str) and isinstance(v, str) for k, v in x.items()), expected_type=dict ) available_aspect_ratios = get_config_item_or_set_default( key='available_aspect_ratios', default_value=modules.flags.sdxl_aspect_ratios, - validator=lambda x: isinstance(x, list) and all('*' in v for v in x) and len(x) > 1, + validator=lambda x: isinstance(x, list) and all( + '*' in v for v in x) and len(x) > 1, expected_type=list ) default_aspect_ratio = get_config_item_or_set_default( @@ -521,7 +557,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: default_ip_images[image_count] = get_config_item_or_set_default( key=f'default_ip_image_{image_count}', default_value='None', - validator=lambda x: x == 'None' or isinstance(x, str) and os.path.exists(x), + validator=lambda x: x == 'None' or isinstance( + x, str) and os.path.exists(x), expected_type=str ) @@ -571,7 +608,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: default_clip_skip = get_config_item_or_set_default( key='default_clip_skip', default_value=2, - validator=lambda x: isinstance(x, int) and 1 <= x <= modules.flags.clip_skip_max, + validator=lambda x: isinstance( + x, int) and 1 <= x <= modules.flags.clip_skip_max, expected_type=int ) default_overwrite_step = get_config_item_or_set_default( @@ -596,7 +634,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: default_value=[ 'highly detailed face', 'detailed girl face', 'detailed man face', 'detailed hand', 'beautiful eyes' ], - validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x), + validator=lambda x: isinstance(x, list) and all( + isinstance(v, str) for v in x), expected_type=list ) example_enhance_detection_prompts = get_config_item_or_set_default( @@ -604,9 +643,38 @@ def init_temp_path(path: str | None, default_path: str) -> str: default_value=[ 'face', 'eye', 'mouth', 'hair', 'hand', 'body' ], - validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x), + validator=lambda x: isinstance(x, list) and all( + isinstance(v, str) for v in x), expected_type=list ) + +default_enhance_prompts = { + 'face': { + 'positive': "Enhance the face to ensure clear and detailed features. The face should have a well-defined structure with smooth skin, natural contours, and a balanced complexion. Make sure the expression is natural and engaging.", + 'negative': "Avoid any blurriness or distortions in the face. Do not include uneven skin tones, unnatural facial expressions, or any missing facial features. Ensure there are no artifacts or unnatural smoothing that might distort the face's natural appearance." + }, + 'eye': { + 'positive': "Enhance the eyes to be clear, sharp, and vividly detailed. The eyes should have natural reflections and a realistic appearance. Ensure the irises and pupils are distinct, and there are no shadows or blurs affecting the eyes.", + 'negative': "Exclude any blurring, distortions, or unnatural reflections in the eyes. Avoid asymmetrical or misaligned eyes, and ensure there are no unnatural colors or artifacts that could detract from a realistic appearance." + }, + 'mouth': { + 'positive': "Enhance the mouth to appear natural and symmetrical. The lips should be smooth and well-defined, with no abnormalities. Ensure the mouth reflects a realistic expression and that teeth are visible only if naturally exposed.", + 'negative': "Avoid any distortions, asymmetry, or unnatural shapes in the mouth. Do not include missing or extra teeth, and ensure there are no anomalies or artifacts affecting the mouth's appearance." + }, + 'hair': { + 'positive': "Enhance the hair to look full, natural, and well-styled. The texture should be realistic, with clear individual strands or locks and natural shine. Ensure the color and style match the intended look without any unnatural effects.", + 'negative': "Exclude any unnatural textures, blurs, or artifacts in the hair. Avoid colors that look artificial or inconsistent, and ensure there are no missing or irregular sections of hair that could disrupt the natural appearance." + }, + 'hand': { + 'positive': "Enhance the hands to ensure all fingers are clearly visible and well-defined. The hands should have realistic textures and proportions, with no missing or distorted fingers. The overall appearance should be natural and proportional.", + 'negative': "Avoid any distortions or missing fingers in the hands. Do not include unnatural shapes or proportions, and ensure there are no anomalies or artifacts that affect the realistic appearance of the hands." + }, + 'body': { + 'positive': "Enhance the body to ensure a complete and natural appearance with all limbs properly defined. The body should reflect realistic proportions and posture, with no missing or distorted body parts. Ensure the overall shape and anatomy are natural and well-balanced.", + 'negative': "Exclude any missing limbs, distortions, or unrealistic body shapes. Avoid anomalies in body posture or proportions, and ensure there are no artifacts or inconsistencies that could affect the natural appearance of the body." + } +} + default_enhance_tabs = get_config_item_or_set_default( key='default_enhance_tabs', default_value=3, @@ -658,7 +726,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: default_metadata_scheme = get_config_item_or_set_default( key='default_metadata_scheme', default_value=MetadataScheme.FOOOCUS.value, - validator=lambda x: x in [y[1] for y in modules.flags.metadata_scheme if y[1] == x], + validator=lambda x: x in [y[1] + for y in modules.flags.metadata_scheme if y[1] == x], expected_type=str ) metadata_created_by = get_config_item_or_set_default( @@ -669,7 +738,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: ) example_inpaint_prompts = [[x] for x in example_inpaint_prompts] -example_enhance_detection_prompts = [[x] for x in example_enhance_detection_prompts] +example_enhance_detection_prompts = [[x] + for x in example_enhance_detection_prompts] default_invert_mask_checkbox = get_config_item_or_set_default( key='default_invert_mask_checkbox', @@ -719,7 +789,9 @@ def init_temp_path(path: str | None, default_path: str) -> str: expected_type=list ) -config_dict["default_loras"] = default_loras = default_loras[:default_max_lora_number] + [[True, 'None', 1.0] for _ in range(default_max_lora_number - len(default_loras))] +config_dict["default_loras"] = default_loras = default_loras[:default_max_lora_number] + \ + [[True, 'None', 1.0] + for _ in range(default_max_lora_number - len(default_loras))] # mapping config to meta parameter possible_preset_keys = { @@ -759,7 +831,8 @@ def init_temp_path(path: str | None, default_path: str) -> str: if REWRITE_PRESET and isinstance(args_manager.args.preset, str): save_path = 'presets/' + args_manager.args.preset + '.json' with open(save_path, "w", encoding="utf-8") as json_file: - json.dump({k: config_dict[k] for k in possible_preset_keys}, json_file, indent=4) + json.dump({k: config_dict[k] + for k in possible_preset_keys}, json_file, indent=4) print(f'Preset saved to {save_path}. Exiting ...') exit(0) @@ -772,13 +845,15 @@ def add_ratio(x): default_aspect_ratio = add_ratio(default_aspect_ratio) -available_aspect_ratios_labels = [add_ratio(x) for x in available_aspect_ratios] +available_aspect_ratios_labels = [ + add_ratio(x) for x in available_aspect_ratios] # Only write config in the first launch. if not os.path.exists(config_path): with open(config_path, "w", encoding="utf-8") as json_file: - json.dump({k: config_dict[k] for k in always_save_keys}, json_file, indent=4) + json.dump({k: config_dict[k] + for k in always_save_keys}, json_file, indent=4) # Always write tutorials. @@ -799,7 +874,8 @@ def add_ratio(x): def get_model_filenames(folder_paths, extensions=None, name_filter=None): if extensions is None: - extensions = ['.pth', '.ckpt', '.bin', '.safetensors', '.fooocus.patch'] + extensions = ['.pth', '.ckpt', '.bin', + '.safetensors', '.fooocus.patch'] files = [] if not isinstance(folder_paths, list): @@ -913,14 +989,16 @@ def downloading_ip_adapters(v): model_dir=path_clip_vision, file_name='clip_vision_vit_h.safetensors' ) - results += [os.path.join(path_clip_vision, 'clip_vision_vit_h.safetensors')] + results += [os.path.join(path_clip_vision, + 'clip_vision_vit_h.safetensors')] load_file_from_url( url='https://huggingface.co/lllyasviel/misc/resolve/main/fooocus_ip_negative.safetensors', model_dir=path_controlnet, file_name='fooocus_ip_negative.safetensors' ) - results += [os.path.join(path_controlnet, 'fooocus_ip_negative.safetensors')] + results += [os.path.join(path_controlnet, + 'fooocus_ip_negative.safetensors')] if v == 'ip': load_file_from_url( @@ -928,7 +1006,8 @@ def downloading_ip_adapters(v): model_dir=path_controlnet, file_name='ip-adapter-plus_sdxl_vit-h.bin' ) - results += [os.path.join(path_controlnet, 'ip-adapter-plus_sdxl_vit-h.bin')] + results += [os.path.join(path_controlnet, + 'ip-adapter-plus_sdxl_vit-h.bin')] if v == 'face': load_file_from_url( @@ -936,7 +1015,8 @@ def downloading_ip_adapters(v): model_dir=path_controlnet, file_name='ip-adapter-plus-face_sdxl_vit-h.bin' ) - results += [os.path.join(path_controlnet, 'ip-adapter-plus-face_sdxl_vit-h.bin')] + results += [os.path.join(path_controlnet, + 'ip-adapter-plus-face_sdxl_vit-h.bin')] return results @@ -949,6 +1029,7 @@ def downloading_upscale_model(): ) return os.path.join(path_upscale_models, 'fooocus_upscaler_s409985e5.bin') + def downloading_safety_checker_model(): load_file_from_url( url='https://huggingface.co/mashb1t/misc/resolve/main/stable-diffusion-safety-checker.bin', diff --git a/modules/flags.py b/modules/flags.py index 05c29a232..a7425800f 100644 --- a/modules/flags.py +++ b/modules/flags.py @@ -8,17 +8,21 @@ upscale_2 = 'Upscale (2x)' upscale_fast = 'Upscale (Fast 2x)' -uov_list = [disabled, subtle_variation, strong_variation, upscale_15, upscale_2, upscale_fast] +uov_list = [disabled, subtle_variation, strong_variation, + upscale_15, upscale_2, upscale_fast] enhancement_uov_before = "Before First Enhancement" enhancement_uov_after = "After Last Enhancement" -enhancement_uov_processing_order = [enhancement_uov_before, enhancement_uov_after] +enhancement_uov_processing_order = [ + enhancement_uov_before, enhancement_uov_after] enhancement_uov_prompt_type_original = 'Original Prompts' enhancement_uov_prompt_type_last_filled = 'Last Filled Enhancement Prompts' -enhancement_uov_prompt_types = [enhancement_uov_prompt_type_original, enhancement_uov_prompt_type_last_filled] +enhancement_uov_prompt_types = [ + enhancement_uov_prompt_type_original, enhancement_uov_prompt_type_last_filled] -CIVITAI_NO_KARRAS = ["euler", "euler_ancestral", "heun", "dpm_fast", "dpm_adaptive", "ddim", "uni_pc"] +CIVITAI_NO_KARRAS = ["euler", "euler_ancestral", "heun", + "dpm_fast", "dpm_adaptive", "ddim", "uni_pc"] # fooocus: a1111 (Civitai) KSAMPLER = { @@ -55,7 +59,8 @@ KSAMPLER_NAMES = list(KSAMPLER.keys()) -SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "lcm", "turbo", "align_your_steps", "tcd", "edm_playground_v2.5"] +SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", + "ddim_uniform", "lcm", "turbo", "align_your_steps", "tcd", "edm_playground_v2.5"] SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys()) sampler_list = SAMPLER_NAMES @@ -68,7 +73,8 @@ refiner_swap_method = 'joint' default_input_image_tab = 'uov_tab' -input_image_tab_ids = ['uov_tab', 'ip_tab', 'inpaint_tab', 'describe_tab', 'enhance_tab', 'metadata_tab'] +input_image_tab_ids = ['uov_tab', 'ip_tab', 'inpaint_tab', + 'describe_tab', 'enhance_tab', 'metadata_tab'] cn_ip = "ImagePrompt" cn_ip_face = "FaceSwap" @@ -84,7 +90,8 @@ output_formats = ['png', 'jpeg', 'webp'] -inpaint_mask_models = ['u2net', 'u2netp', 'u2net_human_seg', 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam'] +inpaint_mask_models = ['u2net', 'u2netp', 'u2net_human_seg', + 'u2net_cloth_seg', 'silueta', 'isnet-general-use', 'isnet-anime', 'sam'] inpaint_mask_cloth_category = ['full', 'upper', 'lower'] inpaint_mask_sam_model = ['vit_b', 'vit_l', 'vit_h'] @@ -92,14 +99,15 @@ inpaint_option_default = 'Inpaint or Outpaint (default)' inpaint_option_detail = 'Improve Detail (face, hand, eyes, etc.)' inpaint_option_modify = 'Modify Content (add objects, change background, etc.)' -inpaint_options = [inpaint_option_default, inpaint_option_detail, inpaint_option_modify] +inpaint_options = [inpaint_option_default, + inpaint_option_detail, inpaint_option_modify] describe_type_photo = 'Photograph' describe_type_anime = 'Art/Anime' describe_types = [describe_type_photo, describe_type_anime] sdxl_aspect_ratios = [ - '704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152', + '512*512', '704*704', '704*1408', '704*1344', '768*1344', '768*1280', '832*1216', '832*1152', '896*1152', '896*1088', '960*1088', '960*1024', '1024*1024', '1024*960', '1088*960', '1088*896', '1152*896', '1152*832', '1216*832', '1280*768', '1344*768', '1344*704', '1408*704', '1472*704', '1536*640', '1600*640', diff --git a/web/assets/css/material-icon.css b/web/assets/css/material-icon.css new file mode 100644 index 000000000..1ccc10030 --- /dev/null +++ b/web/assets/css/material-icon.css @@ -0,0 +1,23 @@ +/* fallback */ +@font-face { + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url('material.woff2') format('woff2'); +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -webkit-font-feature-settings: 'liga'; + -webkit-font-smoothing: antialiased; +} diff --git a/web/assets/css/material.woff2 b/web/assets/css/material.woff2 new file mode 100644 index 000000000..5492a6e75 Binary files /dev/null and b/web/assets/css/material.woff2 differ diff --git a/web/assets/css/styles.css b/web/assets/css/styles.css new file mode 100644 index 000000000..6e9afcc5b --- /dev/null +++ b/web/assets/css/styles.css @@ -0,0 +1,398 @@ +#chart-button { + position: sticky; + transition: background-color 0.3s ease, width 0.8s ease, height 0.8s ease, transform 0.5s ease; + background-color: #00000096 !important; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2) !important; + color: white !important; + border-radius: 10px !important; + z-index: 9998; + -webkit-app-region: drag; /* Make this container draggable */ + width: 100% !important; + height: 100% !important; + transform: scale(0); + -webkit-user-select: none; /* For Chrome, Safari, and Opera */ + -moz-user-select: none; /* For Firefox */ + -ms-user-select: none; /* For Internet Explorer and Edge */ + user-select: none; /* Standard syntax */ +} + +#chart-button-container { + top: 0px; + left: 0px; + height: 0% !important; + width: 0% !important; + transform: scale(0); + position: fixed; + z-index: 9991; + will-change: transform; + text-align: center; + transition: width 0.8s ease, height 0.8s ease, transform 0.3s ease; +} + +#chart-button-container.active { + transform: scale(1); +} + +#mydivheader { + padding: 10px; + cursor: move; + z-index: 10; + background-color: #2196f3; + color: #fff; +} + +body { + transition: all 0.3s ease; +} +#chart-button.small { + transform: scale(0.83); +} + +#chart-button.medium { + transform: scale(0.93); +} + +#chart-button.large { + transform: scale(0.96); +} + +#chart-button.bottom-right { + margin: 0px; +} + +#chart-button.bottom-left { + margin: 0px; +} + +#chart-button.bottom-center { + margin: 0px; +} + +#chart-button.top-right { + margin: 0px; +} + +#chart-button.top-left { + margin: 0px; +} + +#chart-button.top-center { + margin: 0px; +} + +#chart-button.left-center { + margin: 0px; +} + +#chart-button.right-center { + margin: 0px; +} + +#chart-button.center { + margin: 0px; +} +#chart-button.bottom-right.active { + margin: 0px; +} +#chart-button.bottom-left.active { + margin: 0px; +} +#chart-button.bottom-center.active { + margin: 0px; +} +#chart-button.top-right.active { + margin: 0px; +} +#chart-button.top-left.active { + margin: 0px; +} +#chart-button.top-center.active { + margin: 0px; +} +#chart-button.left-center.active { + margin: 0px; +} +#chart-button.right-center.active { + margin: 0px; +} +#chart-button.center.active { + margin: 0px; +} +.chart-row { + padding: 5px 5px 0px 5px !important; + text-align: right !important; + display: flex !important; + justify-content: space-evenly !important; + z-index: 9999 !important; + position: relative !important; + align-items: center; + margin-bottom: -10px; +} + +.chart-row.no-drag { + -webkit-app-region: no-drag; /* Make these elements non-draggable */ +} + +.chart-col { + flex: auto !important; +} + +.left-col a { + width: 15px !important; + cursor: pointer !important; + border-radius: 4px !important; + display: inline-block !important; + text-align: center !important; + color: #fff !important; + text-decoration: none !important; +} + +.left-col a:hover { + background-color: #fff !important; + color: #000 !important; +} + +#chart-container.bar.small { + padding: 5px !important; +} + +#chart-container.bar.medium { + padding: 5px !important; +} + +#chart-container.bar.large { + padding: 5px !important; +} + +#chart-container.line.small { + padding: 5px !important; +} + +#chart-container.line.medium { + padding: 5px !important; +} + +#chart-container.line.large { + padding: 5px !important; +} + +i { + color: #fff !important; /* Adjust color */ + cursor: pointer !important; /* Change cursor to pointer on hover */ + -webkit-app-region: no-drag; /* Make these elements non-draggable */ +} + +a, +button { + -webkit-app-region: no-drag; /* Make these elements non-draggable */ +} + +.toggle-resources-button:hover { + color: rgb(101, 101, 101) !important; /* Change color on hover */ +} + +.drag { + -webkit-app-region: drag; /* Make this container draggable */ +} + +.no-drag { + -webkit-app-region: no-drag; /* Make these elements non-draggable */ +} + +#settingsMenu { + display: grid !important; /* Show the menu */ + position: absolute !important; + transform: scale(0) translateX(-100%) translateY(-200%) !important; /* Center alignment */ + background: #000000 !important; + border: 0px solid #ddd !important; + border-radius: 6px !important; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important; + opacity: 0 !important; + transition: opacity 0.5s ease, transform 0.3s ease !important; + text-align: center; + -webkit-app-region: no-drag; /* Make these elements non-draggable */ +} + +#settingsMenu.show { + min-width: 110px; + opacity: 1 !important; + z-index: 1000 !important; + transform: scale(0.8) translateY(-30px) translateX(-10px) !important; /* Animate to visible state */ +} + +.position-menu { + display: grid !important; + grid-template-columns: repeat(3, 1fr) !important; + grid-template-rows: repeat(3, 1fr) !important; + grid-gap: 0px !important; +} + +.position-btn > i { + color: #fff !important; +} + +.position-btn { + color: #fff !important; + padding: 6px !important; + margin: 0px !important; + font-size: 12px !important; + cursor: pointer !important; + border: 0px solid #ccc !important; + background-color: transparent !important; + border-radius: 4px !important; + text-align: center !important; +} + +.position-btn:hover { + background-color: #fff !important; +} +.position-btn:hover > i { + color: #000 !important; +} + +.material-icons { + font-size: 14px !important; +} + +.left-col { + text-align: left !important; + margin-right: 10px; +} + +.left-col.text { + margin-bottom: 5px !important; +} +.settings-row { + display: inline-block; + text-align: center !important; + margin: 5px !important; +} + +.settings-col { + text-align: center; + font-size: 10px; +} +.settings-hr { + width: 0px; + margin-top: 0px !important; + margin-bottom: 4px !important; + transition: width 1s ease; +} + +.settings-hr.show { + width: 100%; +} +#custom-legend { + display: flex !important; + text-align: center; +} + +.custom-legend-item { + margin-bottom: 5px !important; + font-size: 14px !important; + flex: auto !important; + align-items: center; +} + +.custom-legend-color { + display: inline-block !important; + width: 15px !important; + height: 15px !important; + margin-right: 10px !important; + vertical-align: middle !important; +} + +.custom-legend-text { + display: inline !important; + vertical-align: middle !important; +} + +#show_resource_monitor { + cursor: pointer !important; + z-index: 9991; + margin: 5px; + display: flex; + font-size: 12px; + align-items: center; +} + +.resource-monitor-icon { + margin: 5px; + height: 14px; +} + +/* Example CSS transition for smooth resizing */ +.window { + transition: width 0.3s ease, height 0.3s ease; +} + +#show_resource_monitor_link { + cursor: pointer !important; + position: fixed; + top: 20px; + left: 10px; + z-index: 9991; + margin: 5px; + display: flex; + font-size: 12px; + align-items: center; +} + +.resource-monitor-icon { + margin: 5px; + height: 14px; +} +#item-table { + width: -webkit-fill-available; + margin-left: 5px; + margin-right: 5px; + margin-bottom: 5px; + border-collapse: separate; /* Ensures that border-radius works */ + border-spacing: 0; /* Removes spacing between cells */ + border: 0px solid #ddd; /* Adds border to the table */ + border-radius: 8px; /* Adjust the value for desired corner radius */ + overflow: hidden; /* Ensures rounded corners are visible */ +} + +#item-table th, +#item-table td { + padding: 10px; + border: 1px solid #ddd; + text-align: left; + line-height: 10px; +} + +#item-table thead { + background-color: #333; + color: #fff; +} + +#item-table tbody tr:nth-child(even) { + background-color: rgba(255, 255, 255, 0.1); +} + +#item-table tbody tr:nth-child(odd) { + background-color: transparent; +} + +#item-table tbody td { + color: #fff; +} + +#progress-bar-container { + width: -webkit-fill-available; + + height: 20px; /* Height of the progress bar */ + background-color: #e0e0e0; /* Light gray background */ + border-radius: 10px; /* Rounded corners for the progress bar container */ + margin: 0px 5px; /* Space between the progress bar and the table */ + display: none; +} + +#progress-bar { + height: 100%; + width: 0%; /* Start with 0% width */ + background-color: #76c7c0; /* Fill color of the progress bar */ + border-radius: 10px; /* Rounded corners for the progress bar */ + transition: width 0.5s ease; /* Smooth transition effect */ +} diff --git a/web/assets/img/clearfix.png b/web/assets/img/clearfix.png new file mode 100644 index 000000000..39e215a9a Binary files /dev/null and b/web/assets/img/clearfix.png differ diff --git a/web/assets/img/monitor.svg b/web/assets/img/monitor.svg new file mode 100644 index 000000000..0edd2e16d --- /dev/null +++ b/web/assets/img/monitor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/assets/js/chart-settings.js b/web/assets/js/chart-settings.js new file mode 100644 index 000000000..79c12d4b5 --- /dev/null +++ b/web/assets/js/chart-settings.js @@ -0,0 +1,563 @@ +// VARS AND CONST +const hostname = 'localhost' // Gets the host without port +const baseUrl = `http://${hostname}:5000` // Append the port 5000 +const apiUrl = `${baseUrl}/gpu_usage/` + +const colorPalette = [ + 'rgb(240, 193, 90, 0.2)', + 'rgb(240, 142, 219, 0.2)', + 'rgb(24, 90, 219, 0.2)', + 'rgb(127, 161, 195, 0.2)', + 'rgb(128, 239, 145, 0.2)', + 'rgb(245, 245, 245, 0.2)', + 'rgb(240, 142, 219, 0.2)', + 'rgb(159, 238, 209, 0.2)', +] + +const borderColors = [ + 'rgb(240, 193, 90)', + 'rgb(240, 142, 219)', + 'rgb(24, 90, 219)', + 'rgb(127, 161, 195)', + 'rgb(128, 239, 145)', + 'rgb(245, 245, 245)', + 'rgb(240, 142, 219)', + 'rgb(159, 238, 209)', +] + +let currentChart = null // Track the current chart instance +const MAX_DATA_POINTS = 50 // Number of data points to keep + +// Custom plugin to draw fixed labels in the middle of the chart area +const fixedLabelPlugin = { + id: 'fixedLabelPlugin', + afterDatasetsDraw(chart) { + const { ctx, scales, data } = chart + ctx.save() + + const centerX = scales.x.left + scales.x.width / 2 + const labelPositions = [] + data.datasets[0].data.forEach((value, index) => { + const yPos = chart.getDatasetMeta(0).data[index].y + + // Store yPos for positioning labels + labelPositions.push({ + x: centerX, + y: yPos, + value: `${+value.toFixed(2)}` + `${index == 5 ? '\u00B0' : '%'}`, + }) + }) + const size = localStorage.getItem('chart-size') ?? 'small' + let fontSize = 10 // Default font size + + switch (size) { + case 'small': + fontSize = '7px' + break + case 'medium': + fontSize = '16px' + break + default: + fontSize = '18px' + break + } + + ctx.font = fontSize + ctx.fillStyle = '#FFFFFF' + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + + labelPositions.forEach((label) => { + ctx.fillText(label.value, label.x, label.y) + }) + + ctx.restore() + }, +} + +// Initialize the bar chart +window.initializeBarChart = function () { + localStorage.setItem('active-chart', 'bar') + var table = document.getElementById('item-table') + table.style.display = 'none' + const chartContainer = document.getElementById('chart-container') + const existingCanvas = document.getElementById('usage-chart') + const chartWrapper = document.getElementById('chart-wrapper') + if (existingCanvas) { + chartContainer.removeChild(existingCanvas) + } + const size = localStorage.getItem('chart-size') ?? 'small' + let fontSize = 10 // Default font size + + switch (size) { + case 'small': + fontSize = '7px' + break + case 'medium': + fontSize = '16px' + break + default: + fontSize = '18px' + break + } + + // Create a new canvas element + const newCanvas = document.createElement('canvas') + newCanvas.id = 'usage-chart' + newCanvas.classList.add('bar') // Add the class directly to the canvas element + chartContainer.appendChild(newCanvas) + + const ctx = newCanvas.getContext('2d') + $(chartWrapper).hide() + + currentChart = new Chart(ctx, { + type: 'bar', + data: { + labels: ['CPU', 'RAM', 'GPU', 'VRAM', 'HDD', 'TEMP'], + datasets: [ + { + label: 'Usage', + data: [0, 0, 0, 0, 0], + barPercentage: 0.8, // Adjust space occupied by bars + categoryPercentage: 1, // Adjust space between bars + backgroundColor: function (context) { + const value = context.dataset.data[context.dataIndex] + return value > 90 ? '#D9534F' : colorPalette[context.dataIndex] + }, + borderColor: function (context) { + const value = context.dataset.data[context.dataIndex] + return value > 90 ? '#D9534F' : borderColors[context.dataIndex] + }, + borderWidth: 1.5, + }, + ], + }, + options: { + indexAxis: 'y', // Horizontal bars + scales: { + x: { + grid: { + display: false, // Hide all grid lines + }, + border: { + display: false, // Hide all grid lines + }, + beginAtZero: true, + max: 100, + ticks: { + color: '#ffffff', + font: { + size: fontSize, + weight: 600, + }, + align: 'center', + callback: function (value, index, ticks) { + return value + '%' + }, + }, + }, + y: { + grid: { + display: false, + }, + border: { + color: '#ffffff30', + width: 1, // Width of the axis border + }, + ticks: { + color: '#FFFFFF', + crossAlign: 'far', + font: { + weight: 600, + size: fontSize, + }, + // Specify the maximum number of ticks to show + maxTicksLimit: 10, + // Control the step size between ticks + stepSize: 1, + // Optional: Set font size and other style properties + }, + }, + }, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: false, + }, + }, + responsive: true, + maintainAspectRatio: false, + }, + plugins: [fixedLabelPlugin], // Register the custom plugins + }) + + currentChart.options.animation = true + const legendContainer = document.getElementById('custom-legend') + legendContainer.innerHTML = '' + + document.getElementById('settingsMenu').classList.remove('show') // Hide the menu + + document.querySelectorAll('canvas').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) + + window.addEventListener('resize', () => { + currentChart.resize() + }) + $(chartWrapper).fadeIn(300) + if (size == 'large') { + $(table).fadeIn(800) + } +} + +// Initialize the line chart +window.initializeLineChart = function () { + localStorage.setItem('active-chart', 'line') + var table = document.getElementById('item-table') + table.style.display = 'none' + const size = localStorage.getItem('chart-size') ?? 'medium' + const existingCanvas = document.getElementById('usage-chart') + const chartContainer = document.getElementById('chart-container') + const chartWrapper = document.getElementById('chart-wrapper') + if (existingCanvas) { + chartContainer.removeChild(existingCanvas) + } + + // Create a new canvas element + const newCanvas = document.createElement('canvas') + newCanvas.id = 'usage-chart' + newCanvas.classList.add('line') // Add the class directly to the canvas element + chartContainer.appendChild(newCanvas) + $(chartWrapper).hide() + + const ctx = newCanvas.getContext('2d') + + currentChart = new Chart(ctx, { + type: 'line', + data: { + labels: [], + datasets: [ + { + label: 'CPU', + data: [], + borderColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return borderColors[datasetIndex % borderColors.length] + }, + borderWidth: 1.5, + backgroundColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return colorPalette[datasetIndex % borderColors.length] + }, + fill: false, + tension: 0.1, + }, + { + label: 'RAM', + data: [], + borderColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return borderColors[datasetIndex % borderColors.length] + }, + borderWidth: 1.5, + backgroundColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return colorPalette[datasetIndex % borderColors.length] + }, + fill: false, + tension: 0.1, + }, + { + label: 'GPU', + data: [], + borderColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return borderColors[datasetIndex % borderColors.length] + }, + borderWidth: 1.5, + backgroundColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return colorPalette[datasetIndex % borderColors.length] + }, + fill: false, + tension: 0.1, + }, + { + label: 'VRAM', + data: [], + borderColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return borderColors[datasetIndex % borderColors.length] + }, + borderWidth: 1.5, + backgroundColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return colorPalette[datasetIndex % borderColors.length] + }, + fill: false, + tension: 0.1, + }, + { + label: 'HDD', + data: [], + borderColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return borderColors[datasetIndex % borderColors.length] + }, + borderWidth: 1.5, + backgroundColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return colorPalette[datasetIndex % borderColors.length] + }, + fill: false, + tension: 0.1, + }, + { + label: 'TEMP', + data: [], + borderColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return borderColors[datasetIndex % borderColors.length] + }, + borderWidth: 1.5, + backgroundColor: function (context) { + const dataset = context.dataset + const datasetIndex = context.datasetIndex + const shouldUseRed = dataset.data.some((value) => value > 90) + + if (shouldUseRed) { + return '#D9534F' // Return red color if any value exceeds 90 + } + return colorPalette[datasetIndex % borderColors.length] + }, + fill: false, + tension: 0.1, + }, + ], + }, + options: { + animation: { + enabled: false, + tension: { + duration: 1000, + easing: 'linear', + from: 1, + to: 0, + loop: true, + }, + }, + elements: { + point: { + radius: 0, + }, + }, + scales: { + x: { + ticks: { + display: false, + }, + }, + y: { + beginAtZero: true, + max: 100, + ticks: { + color: '#FFFFFF', + crossAlign: 'far', + padding: 0, + font: { + weight: 600, + size: 7, + }, + callback: function (value, index, ticks) { + return value + '%' + }, + }, + }, + }, + responsive: true, + plugins: { + legend: { + display: true, + labels: { + generateLabels: false, + }, + }, + title: { + display: false, + }, + }, + }, + }) + + currentChart.options.animation = false + generateCustomLegend() + document.getElementById('settingsMenu').classList.remove('show') // Hide the menu + + window.addEventListener('resize', () => { + currentChart.resize() + }) + $(chartWrapper).fadeIn(300) + if (size == 'large') { + $(table).fadeIn(800) + } +} + +function generateCustomLegend() { + const legendContainer = document.getElementById('custom-legend') + legendContainer.innerHTML = '' + + currentChart.data.datasets.forEach((dataset, index) => { + const legendItem = document.createElement('div') + legendItem.className = 'custom-legend-item' + + // Create text element + const legendText = document.createElement('span') + legendText.className = 'custom-legend-text' + legendText.textContent = dataset.label + const shouldUseRed = dataset.data.some((value) => value > 90) + legendText.style.color = shouldUseRed ? '#D9534F' : `${borderColors[index]}` + + legendText.style.fontWeight = shouldUseRed ? '800' : `600` + const size = localStorage.getItem('chart-size') ?? 'small' + switch (size) { + case 'small': + legendText.style.fontSize = '7px' + break + case 'medium': + legendText.style.fontSize = '16px' + break + default: + legendText.style.fontSize = '18px' + break + } + + legendItem.appendChild(legendText) + legendContainer.appendChild(legendItem) + }) +} + +setTimeout(() => { + const socket = io('http://localhost:5000') + + // Function to update the chart with new data + function updateChart(data) { + const timestamp = new Date().toLocaleTimeString() + + if (currentChart) { + if (currentChart.config.type === 'bar') { + // Update data for bar chart + currentChart.data.datasets[0].data = [ + data.cpu, + data.ram, + data.gpu, + data.vram, + data.hdd, + data.temp, + ] + } else if (currentChart.config.type === 'line') { + // Update data for line chart + currentChart.data.labels.push(timestamp) + currentChart.data.datasets[0].data.push(data.cpu) + currentChart.data.datasets[1].data.push(data.ram) + currentChart.data.datasets[2].data.push(data.gpu) + currentChart.data.datasets[3].data.push(data.vram) + currentChart.data.datasets[4].data.push(data.hdd) + currentChart.data.datasets[5].data.push(data.temp) + + // Prune old data if the number of points exceeds the limit + const MAX_DATA_POINTS = 50 // Adjust as needed + if (currentChart.data.labels.length > MAX_DATA_POINTS) { + currentChart.data.labels.shift() // Remove the oldest label + currentChart.data.datasets.forEach((dataset) => dataset.data.shift()) // Remove the oldest data points + } + generateCustomLegend() + } + // Update the chart with new data + currentChart.update() + } + } + + // Handle data updates from the WebSocket server + socket.on('data_update', function (data) { + updateChart(data) + }) + + // Optional: Handle WebSocket connection errors + socket.on('connect_error', function (error) { + console.error('Connection error:', error) + }) + + // Optional: Handle WebSocket disconnection + socket.on('disconnect', function () { + console.log('WebSocket disconnected') + }) +}, 4000) diff --git a/web/assets/js/chart.js b/web/assets/js/chart.js new file mode 100644 index 000000000..6c0082bb0 --- /dev/null +++ b/web/assets/js/chart.js @@ -0,0 +1,12536 @@ +/** + * Skipped minification because the original files appears to be already minified. + * Original file: /npm/chart.js@4.4.4/dist/chart.umd.js + * + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files + */ +/*! + * Chart.js v4.4.4 + * https://www.chartjs.org + * (c) 2024 Chart.js Contributors + * Released under the MIT License + */ +!(function (t, e) { + "object" == typeof exports && "undefined" != typeof module + ? (module.exports = e()) + : "function" == typeof define && define.amd + ? define(e) + : ((t = "undefined" != typeof globalThis ? globalThis : t || self).Chart = + e()); +})(this, function () { + "use strict"; + var t = Object.freeze({ + __proto__: null, + get Colors() { + return Go; + }, + get Decimation() { + return Qo; + }, + get Filler() { + return ma; + }, + get Legend() { + return ya; + }, + get SubTitle() { + return ka; + }, + get Title() { + return Ma; + }, + get Tooltip() { + return Ba; + }, + }); + function e() {} + const i = (() => { + let t = 0; + return () => t++; + })(); + function s(t) { + return null == t; + } + function n(t) { + if (Array.isArray && Array.isArray(t)) return !0; + const e = Object.prototype.toString.call(t); + return "[object" === e.slice(0, 7) && "Array]" === e.slice(-6); + } + function o(t) { + return ( + null !== t && "[object Object]" === Object.prototype.toString.call(t) + ); + } + function a(t) { + return ("number" == typeof t || t instanceof Number) && isFinite(+t); + } + function r(t, e) { + return a(t) ? t : e; + } + function l(t, e) { + return void 0 === t ? e : t; + } + const h = (t, e) => + "string" == typeof t && t.endsWith("%") ? parseFloat(t) / 100 : +t / e, + c = (t, e) => + "string" == typeof t && t.endsWith("%") ? (parseFloat(t) / 100) * e : +t; + function d(t, e, i) { + if (t && "function" == typeof t.call) return t.apply(i, e); + } + function u(t, e, i, s) { + let a, r, l; + if (n(t)) + if (((r = t.length), s)) for (a = r - 1; a >= 0; a--) e.call(i, t[a], a); + else for (a = 0; a < r; a++) e.call(i, t[a], a); + else if (o(t)) + for (l = Object.keys(t), r = l.length, a = 0; a < r; a++) + e.call(i, t[l[a]], l[a]); + } + function f(t, e) { + let i, s, n, o; + if (!t || !e || t.length !== e.length) return !1; + for (i = 0, s = t.length; i < s; ++i) + if ( + ((n = t[i]), + (o = e[i]), + n.datasetIndex !== o.datasetIndex || n.index !== o.index) + ) + return !1; + return !0; + } + function g(t) { + if (n(t)) return t.map(g); + if (o(t)) { + const e = Object.create(null), + i = Object.keys(t), + s = i.length; + let n = 0; + for (; n < s; ++n) e[i[n]] = g(t[i[n]]); + return e; + } + return t; + } + function p(t) { + return -1 === ["__proto__", "prototype", "constructor"].indexOf(t); + } + function m(t, e, i, s) { + if (!p(t)) return; + const n = e[t], + a = i[t]; + o(n) && o(a) ? x(n, a, s) : (e[t] = g(a)); + } + function x(t, e, i) { + const s = n(e) ? e : [e], + a = s.length; + if (!o(t)) return t; + const r = (i = i || {}).merger || m; + let l; + for (let e = 0; e < a; ++e) { + if (((l = s[e]), !o(l))) continue; + const n = Object.keys(l); + for (let e = 0, s = n.length; e < s; ++e) r(n[e], t, l, i); + } + return t; + } + function b(t, e) { + return x(t, e, { merger: _ }); + } + function _(t, e, i) { + if (!p(t)) return; + const s = e[t], + n = i[t]; + o(s) && o(n) + ? b(s, n) + : Object.prototype.hasOwnProperty.call(e, t) || (e[t] = g(n)); + } + const y = { "": (t) => t, x: (t) => t.x, y: (t) => t.y }; + function v(t) { + const e = t.split("."), + i = []; + let s = ""; + for (const t of e) + (s += t), + s.endsWith("\\") ? (s = s.slice(0, -1) + ".") : (i.push(s), (s = "")); + return i; + } + function M(t, e) { + const i = + y[e] || + (y[e] = (function (t) { + const e = v(t); + return (t) => { + for (const i of e) { + if ("" === i) break; + t = t && t[i]; + } + return t; + }; + })(e)); + return i(t); + } + function w(t) { + return t.charAt(0).toUpperCase() + t.slice(1); + } + const k = (t) => void 0 !== t, + S = (t) => "function" == typeof t, + P = (t, e) => { + if (t.size !== e.size) return !1; + for (const i of t) if (!e.has(i)) return !1; + return !0; + }; + function D(t) { + return ( + "mouseup" === t.type || "click" === t.type || "contextmenu" === t.type + ); + } + const C = Math.PI, + O = 2 * C, + A = O + C, + T = Number.POSITIVE_INFINITY, + L = C / 180, + E = C / 2, + R = C / 4, + I = (2 * C) / 3, + z = Math.log10, + F = Math.sign; + function V(t, e, i) { + return Math.abs(t - e) < i; + } + function B(t) { + const e = Math.round(t); + t = V(t, e, t / 1e3) ? e : t; + const i = Math.pow(10, Math.floor(z(t))), + s = t / i; + return (s <= 1 ? 1 : s <= 2 ? 2 : s <= 5 ? 5 : 10) * i; + } + function W(t) { + const e = [], + i = Math.sqrt(t); + let s; + for (s = 1; s < i; s++) t % s == 0 && (e.push(s), e.push(t / s)); + return i === (0 | i) && e.push(i), e.sort((t, e) => t - e).pop(), e; + } + function N(t) { + return !isNaN(parseFloat(t)) && isFinite(t); + } + function H(t, e) { + const i = Math.round(t); + return i - e <= t && i + e >= t; + } + function j(t, e, i) { + let s, n, o; + for (s = 0, n = t.length; s < n; s++) + (o = t[s][i]), + isNaN(o) || + ((e.min = Math.min(e.min, o)), (e.max = Math.max(e.max, o))); + } + function $(t) { + return t * (C / 180); + } + function Y(t) { + return t * (180 / C); + } + function U(t) { + if (!a(t)) return; + let e = 1, + i = 0; + for (; Math.round(t * e) / e !== t; ) (e *= 10), i++; + return i; + } + function X(t, e) { + const i = e.x - t.x, + s = e.y - t.y, + n = Math.sqrt(i * i + s * s); + let o = Math.atan2(s, i); + return o < -0.5 * C && (o += O), { angle: o, distance: n }; + } + function q(t, e) { + return Math.sqrt(Math.pow(e.x - t.x, 2) + Math.pow(e.y - t.y, 2)); + } + function K(t, e) { + return ((t - e + A) % O) - C; + } + function G(t) { + return ((t % O) + O) % O; + } + function Z(t, e, i, s) { + const n = G(t), + o = G(e), + a = G(i), + r = G(o - n), + l = G(a - n), + h = G(n - o), + c = G(n - a); + return n === o || n === a || (s && o === a) || (r > l && h < c); + } + function J(t, e, i) { + return Math.max(e, Math.min(i, t)); + } + function Q(t) { + return J(t, -32768, 32767); + } + function tt(t, e, i, s = 1e-6) { + return t >= Math.min(e, i) - s && t <= Math.max(e, i) + s; + } + function et(t, e, i) { + i = i || ((i) => t[i] < e); + let s, + n = t.length - 1, + o = 0; + for (; n - o > 1; ) (s = (o + n) >> 1), i(s) ? (o = s) : (n = s); + return { lo: o, hi: n }; + } + const it = (t, e, i, s) => + et( + t, + i, + s + ? (s) => { + const n = t[s][e]; + return n < i || (n === i && t[s + 1][e] === i); + } + : (s) => t[s][e] < i + ), + st = (t, e, i) => et(t, i, (s) => t[s][e] >= i); + function nt(t, e, i) { + let s = 0, + n = t.length; + for (; s < n && t[s] < e; ) s++; + for (; n > s && t[n - 1] > i; ) n--; + return s > 0 || n < t.length ? t.slice(s, n) : t; + } + const ot = ["push", "pop", "shift", "splice", "unshift"]; + function at(t, e) { + t._chartjs + ? t._chartjs.listeners.push(e) + : (Object.defineProperty(t, "_chartjs", { + configurable: !0, + enumerable: !1, + value: { listeners: [e] }, + }), + ot.forEach((e) => { + const i = "_onData" + w(e), + s = t[e]; + Object.defineProperty(t, e, { + configurable: !0, + enumerable: !1, + value(...e) { + const n = s.apply(this, e); + return ( + t._chartjs.listeners.forEach((t) => { + "function" == typeof t[i] && t[i](...e); + }), + n + ); + }, + }); + })); + } + function rt(t, e) { + const i = t._chartjs; + if (!i) return; + const s = i.listeners, + n = s.indexOf(e); + -1 !== n && s.splice(n, 1), + s.length > 0 || + (ot.forEach((e) => { + delete t[e]; + }), + delete t._chartjs); + } + function lt(t) { + const e = new Set(t); + return e.size === t.length ? t : Array.from(e); + } + const ht = + "undefined" == typeof window + ? function (t) { + return t(); + } + : window.requestAnimationFrame; + function ct(t, e) { + let i = [], + s = !1; + return function (...n) { + (i = n), + s || + ((s = !0), + ht.call(window, () => { + (s = !1), t.apply(e, i); + })); + }; + } + function dt(t, e) { + let i; + return function (...s) { + return ( + e ? (clearTimeout(i), (i = setTimeout(t, e, s))) : t.apply(this, s), e + ); + }; + } + const ut = (t) => ("start" === t ? "left" : "end" === t ? "right" : "center"), + ft = (t, e, i) => ("start" === t ? e : "end" === t ? i : (e + i) / 2), + gt = (t, e, i, s) => + t === (s ? "left" : "right") ? i : "center" === t ? (e + i) / 2 : e; + function pt(t, e, i) { + const s = e.length; + let n = 0, + o = s; + if (t._sorted) { + const { iScale: a, _parsed: r } = t, + l = a.axis, + { min: h, max: c, minDefined: d, maxDefined: u } = a.getUserBounds(); + d && + (n = J( + Math.min(it(r, l, h).lo, i ? s : it(e, l, a.getPixelForValue(h)).lo), + 0, + s - 1 + )), + (o = u + ? J( + Math.max( + it(r, a.axis, c, !0).hi + 1, + i ? 0 : it(e, l, a.getPixelForValue(c), !0).hi + 1 + ), + n, + s + ) - n + : s - n); + } + return { start: n, count: o }; + } + function mt(t) { + const { xScale: e, yScale: i, _scaleRanges: s } = t, + n = { xmin: e.min, xmax: e.max, ymin: i.min, ymax: i.max }; + if (!s) return (t._scaleRanges = n), !0; + const o = + s.xmin !== e.min || + s.xmax !== e.max || + s.ymin !== i.min || + s.ymax !== i.max; + return Object.assign(s, n), o; + } + class xt { + constructor() { + (this._request = null), + (this._charts = new Map()), + (this._running = !1), + (this._lastDate = void 0); + } + _notify(t, e, i, s) { + const n = e.listeners[s], + o = e.duration; + n.forEach((s) => + s({ + chart: t, + initial: e.initial, + numSteps: o, + currentStep: Math.min(i - e.start, o), + }) + ); + } + _refresh() { + this._request || + ((this._running = !0), + (this._request = ht.call(window, () => { + this._update(), + (this._request = null), + this._running && this._refresh(); + }))); + } + _update(t = Date.now()) { + let e = 0; + this._charts.forEach((i, s) => { + if (!i.running || !i.items.length) return; + const n = i.items; + let o, + a = n.length - 1, + r = !1; + for (; a >= 0; --a) + (o = n[a]), + o._active + ? (o._total > i.duration && (i.duration = o._total), + o.tick(t), + (r = !0)) + : ((n[a] = n[n.length - 1]), n.pop()); + r && (s.draw(), this._notify(s, i, t, "progress")), + n.length || + ((i.running = !1), + this._notify(s, i, t, "complete"), + (i.initial = !1)), + (e += n.length); + }), + (this._lastDate = t), + 0 === e && (this._running = !1); + } + _getAnims(t) { + const e = this._charts; + let i = e.get(t); + return ( + i || + ((i = { + running: !1, + initial: !0, + items: [], + listeners: { complete: [], progress: [] }, + }), + e.set(t, i)), + i + ); + } + listen(t, e, i) { + this._getAnims(t).listeners[e].push(i); + } + add(t, e) { + e && e.length && this._getAnims(t).items.push(...e); + } + has(t) { + return this._getAnims(t).items.length > 0; + } + start(t) { + const e = this._charts.get(t); + e && + ((e.running = !0), + (e.start = Date.now()), + (e.duration = e.items.reduce((t, e) => Math.max(t, e._duration), 0)), + this._refresh()); + } + running(t) { + if (!this._running) return !1; + const e = this._charts.get(t); + return !!(e && e.running && e.items.length); + } + stop(t) { + const e = this._charts.get(t); + if (!e || !e.items.length) return; + const i = e.items; + let s = i.length - 1; + for (; s >= 0; --s) i[s].cancel(); + (e.items = []), this._notify(t, e, Date.now(), "complete"); + } + remove(t) { + return this._charts.delete(t); + } + } + var bt = new xt(); + /*! + * @kurkle/color v0.3.2 + * https://github.com/kurkle/color#readme + * (c) 2023 Jukka Kurkela + * Released under the MIT License + */ function _t(t) { + return (t + 0.5) | 0; + } + const yt = (t, e, i) => Math.max(Math.min(t, i), e); + function vt(t) { + return yt(_t(2.55 * t), 0, 255); + } + function Mt(t) { + return yt(_t(255 * t), 0, 255); + } + function wt(t) { + return yt(_t(t / 2.55) / 100, 0, 1); + } + function kt(t) { + return yt(_t(100 * t), 0, 100); + } + const St = { + 0: 0, + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + A: 10, + B: 11, + C: 12, + D: 13, + E: 14, + F: 15, + a: 10, + b: 11, + c: 12, + d: 13, + e: 14, + f: 15, + }, + Pt = [..."0123456789ABCDEF"], + Dt = (t) => Pt[15 & t], + Ct = (t) => Pt[(240 & t) >> 4] + Pt[15 & t], + Ot = (t) => (240 & t) >> 4 == (15 & t); + function At(t) { + var e = ((t) => Ot(t.r) && Ot(t.g) && Ot(t.b) && Ot(t.a))(t) ? Dt : Ct; + return t + ? "#" + + e(t.r) + + e(t.g) + + e(t.b) + + ((t, e) => (t < 255 ? e(t) : ""))(t.a, e) + : void 0; + } + const Tt = + /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/; + function Lt(t, e, i) { + const s = e * Math.min(i, 1 - i), + n = (e, n = (e + t / 30) % 12) => + i - s * Math.max(Math.min(n - 3, 9 - n, 1), -1); + return [n(0), n(8), n(4)]; + } + function Et(t, e, i) { + const s = (s, n = (s + t / 60) % 6) => + i - i * e * Math.max(Math.min(n, 4 - n, 1), 0); + return [s(5), s(3), s(1)]; + } + function Rt(t, e, i) { + const s = Lt(t, 1, 0.5); + let n; + for ( + e + i > 1 && ((n = 1 / (e + i)), (e *= n), (i *= n)), n = 0; + n < 3; + n++ + ) + (s[n] *= 1 - e - i), (s[n] += e); + return s; + } + function It(t) { + const e = t.r / 255, + i = t.g / 255, + s = t.b / 255, + n = Math.max(e, i, s), + o = Math.min(e, i, s), + a = (n + o) / 2; + let r, l, h; + return ( + n !== o && + ((h = n - o), + (l = a > 0.5 ? h / (2 - n - o) : h / (n + o)), + (r = (function (t, e, i, s, n) { + return t === n + ? (e - i) / s + (e < i ? 6 : 0) + : e === n + ? (i - t) / s + 2 + : (t - e) / s + 4; + })(e, i, s, h, n)), + (r = 60 * r + 0.5)), + [0 | r, l || 0, a] + ); + } + function zt(t, e, i, s) { + return (Array.isArray(e) ? t(e[0], e[1], e[2]) : t(e, i, s)).map(Mt); + } + function Ft(t, e, i) { + return zt(Lt, t, e, i); + } + function Vt(t) { + return ((t % 360) + 360) % 360; + } + function Bt(t) { + const e = Tt.exec(t); + let i, + s = 255; + if (!e) return; + e[5] !== i && (s = e[6] ? vt(+e[5]) : Mt(+e[5])); + const n = Vt(+e[2]), + o = +e[3] / 100, + a = +e[4] / 100; + return ( + (i = + "hwb" === e[1] + ? (function (t, e, i) { + return zt(Rt, t, e, i); + })(n, o, a) + : "hsv" === e[1] + ? (function (t, e, i) { + return zt(Et, t, e, i); + })(n, o, a) + : Ft(n, o, a)), + { r: i[0], g: i[1], b: i[2], a: s } + ); + } + const Wt = { + x: "dark", + Z: "light", + Y: "re", + X: "blu", + W: "gr", + V: "medium", + U: "slate", + A: "ee", + T: "ol", + S: "or", + B: "ra", + C: "lateg", + D: "ights", + R: "in", + Q: "turquois", + E: "hi", + P: "ro", + O: "al", + N: "le", + M: "de", + L: "yello", + F: "en", + K: "ch", + G: "arks", + H: "ea", + I: "ightg", + J: "wh", + }, + Nt = { + OiceXe: "f0f8ff", + antiquewEte: "faebd7", + aqua: "ffff", + aquamarRe: "7fffd4", + azuY: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "0", + blanKedOmond: "ffebcd", + Xe: "ff", + XeviTet: "8a2be2", + bPwn: "a52a2a", + burlywood: "deb887", + caMtXe: "5f9ea0", + KartYuse: "7fff00", + KocTate: "d2691e", + cSO: "ff7f50", + cSnflowerXe: "6495ed", + cSnsilk: "fff8dc", + crimson: "dc143c", + cyan: "ffff", + xXe: "8b", + xcyan: "8b8b", + xgTMnPd: "b8860b", + xWay: "a9a9a9", + xgYF: "6400", + xgYy: "a9a9a9", + xkhaki: "bdb76b", + xmagFta: "8b008b", + xTivegYF: "556b2f", + xSange: "ff8c00", + xScEd: "9932cc", + xYd: "8b0000", + xsOmon: "e9967a", + xsHgYF: "8fbc8f", + xUXe: "483d8b", + xUWay: "2f4f4f", + xUgYy: "2f4f4f", + xQe: "ced1", + xviTet: "9400d3", + dAppRk: "ff1493", + dApskyXe: "bfff", + dimWay: "696969", + dimgYy: "696969", + dodgerXe: "1e90ff", + fiYbrick: "b22222", + flSOwEte: "fffaf0", + foYstWAn: "228b22", + fuKsia: "ff00ff", + gaRsbSo: "dcdcdc", + ghostwEte: "f8f8ff", + gTd: "ffd700", + gTMnPd: "daa520", + Way: "808080", + gYF: "8000", + gYFLw: "adff2f", + gYy: "808080", + honeyMw: "f0fff0", + hotpRk: "ff69b4", + RdianYd: "cd5c5c", + Rdigo: "4b0082", + ivSy: "fffff0", + khaki: "f0e68c", + lavFMr: "e6e6fa", + lavFMrXsh: "fff0f5", + lawngYF: "7cfc00", + NmoncEffon: "fffacd", + ZXe: "add8e6", + ZcSO: "f08080", + Zcyan: "e0ffff", + ZgTMnPdLw: "fafad2", + ZWay: "d3d3d3", + ZgYF: "90ee90", + ZgYy: "d3d3d3", + ZpRk: "ffb6c1", + ZsOmon: "ffa07a", + ZsHgYF: "20b2aa", + ZskyXe: "87cefa", + ZUWay: "778899", + ZUgYy: "778899", + ZstAlXe: "b0c4de", + ZLw: "ffffe0", + lime: "ff00", + limegYF: "32cd32", + lRF: "faf0e6", + magFta: "ff00ff", + maPon: "800000", + VaquamarRe: "66cdaa", + VXe: "cd", + VScEd: "ba55d3", + VpurpN: "9370db", + VsHgYF: "3cb371", + VUXe: "7b68ee", + VsprRggYF: "fa9a", + VQe: "48d1cc", + VviTetYd: "c71585", + midnightXe: "191970", + mRtcYam: "f5fffa", + mistyPse: "ffe4e1", + moccasR: "ffe4b5", + navajowEte: "ffdead", + navy: "80", + Tdlace: "fdf5e6", + Tive: "808000", + TivedBb: "6b8e23", + Sange: "ffa500", + SangeYd: "ff4500", + ScEd: "da70d6", + pOegTMnPd: "eee8aa", + pOegYF: "98fb98", + pOeQe: "afeeee", + pOeviTetYd: "db7093", + papayawEp: "ffefd5", + pHKpuff: "ffdab9", + peru: "cd853f", + pRk: "ffc0cb", + plum: "dda0dd", + powMrXe: "b0e0e6", + purpN: "800080", + YbeccapurpN: "663399", + Yd: "ff0000", + Psybrown: "bc8f8f", + PyOXe: "4169e1", + saddNbPwn: "8b4513", + sOmon: "fa8072", + sandybPwn: "f4a460", + sHgYF: "2e8b57", + sHshell: "fff5ee", + siFna: "a0522d", + silver: "c0c0c0", + skyXe: "87ceeb", + UXe: "6a5acd", + UWay: "708090", + UgYy: "708090", + snow: "fffafa", + sprRggYF: "ff7f", + stAlXe: "4682b4", + tan: "d2b48c", + teO: "8080", + tEstN: "d8bfd8", + tomato: "ff6347", + Qe: "40e0d0", + viTet: "ee82ee", + JHt: "f5deb3", + wEte: "ffffff", + wEtesmoke: "f5f5f5", + Lw: "ffff00", + LwgYF: "9acd32", + }; + let Ht; + function jt(t) { + Ht || + ((Ht = (function () { + const t = {}, + e = Object.keys(Nt), + i = Object.keys(Wt); + let s, n, o, a, r; + for (s = 0; s < e.length; s++) { + for (a = r = e[s], n = 0; n < i.length; n++) + (o = i[n]), (r = r.replace(o, Wt[o])); + (o = parseInt(Nt[a], 16)), + (t[r] = [(o >> 16) & 255, (o >> 8) & 255, 255 & o]); + } + return t; + })()), + (Ht.transparent = [0, 0, 0, 0])); + const e = Ht[t.toLowerCase()]; + return e && { r: e[0], g: e[1], b: e[2], a: 4 === e.length ? e[3] : 255 }; + } + const $t = + /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/; + const Yt = (t) => + t <= 0.0031308 ? 12.92 * t : 1.055 * Math.pow(t, 1 / 2.4) - 0.055, + Ut = (t) => (t <= 0.04045 ? t / 12.92 : Math.pow((t + 0.055) / 1.055, 2.4)); + function Xt(t, e, i) { + if (t) { + let s = It(t); + (s[e] = Math.max(0, Math.min(s[e] + s[e] * i, 0 === e ? 360 : 1))), + (s = Ft(s)), + (t.r = s[0]), + (t.g = s[1]), + (t.b = s[2]); + } + } + function qt(t, e) { + return t ? Object.assign(e || {}, t) : t; + } + function Kt(t) { + var e = { r: 0, g: 0, b: 0, a: 255 }; + return ( + Array.isArray(t) + ? t.length >= 3 && + ((e = { r: t[0], g: t[1], b: t[2], a: 255 }), + t.length > 3 && (e.a = Mt(t[3]))) + : ((e = qt(t, { r: 0, g: 0, b: 0, a: 1 })).a = Mt(e.a)), + e + ); + } + function Gt(t) { + return "r" === t.charAt(0) + ? (function (t) { + const e = $t.exec(t); + let i, + s, + n, + o = 255; + if (e) { + if (e[7] !== i) { + const t = +e[7]; + o = e[8] ? vt(t) : yt(255 * t, 0, 255); + } + return ( + (i = +e[1]), + (s = +e[3]), + (n = +e[5]), + (i = 255 & (e[2] ? vt(i) : yt(i, 0, 255))), + (s = 255 & (e[4] ? vt(s) : yt(s, 0, 255))), + (n = 255 & (e[6] ? vt(n) : yt(n, 0, 255))), + { r: i, g: s, b: n, a: o } + ); + } + })(t) + : Bt(t); + } + class Zt { + constructor(t) { + if (t instanceof Zt) return t; + const e = typeof t; + let i; + var s, n, o; + "object" === e + ? (i = Kt(t)) + : "string" === e && + ((o = (s = t).length), + "#" === s[0] && + (4 === o || 5 === o + ? (n = { + r: 255 & (17 * St[s[1]]), + g: 255 & (17 * St[s[2]]), + b: 255 & (17 * St[s[3]]), + a: 5 === o ? 17 * St[s[4]] : 255, + }) + : (7 !== o && 9 !== o) || + (n = { + r: (St[s[1]] << 4) | St[s[2]], + g: (St[s[3]] << 4) | St[s[4]], + b: (St[s[5]] << 4) | St[s[6]], + a: 9 === o ? (St[s[7]] << 4) | St[s[8]] : 255, + })), + (i = n || jt(t) || Gt(t))), + (this._rgb = i), + (this._valid = !!i); + } + get valid() { + return this._valid; + } + get rgb() { + var t = qt(this._rgb); + return t && (t.a = wt(t.a)), t; + } + set rgb(t) { + this._rgb = Kt(t); + } + rgbString() { + return this._valid + ? (t = this._rgb) && + (t.a < 255 + ? `rgba(${t.r}, ${t.g}, ${t.b}, ${wt(t.a)})` + : `rgb(${t.r}, ${t.g}, ${t.b})`) + : void 0; + var t; + } + hexString() { + return this._valid ? At(this._rgb) : void 0; + } + hslString() { + return this._valid + ? (function (t) { + if (!t) return; + const e = It(t), + i = e[0], + s = kt(e[1]), + n = kt(e[2]); + return t.a < 255 + ? `hsla(${i}, ${s}%, ${n}%, ${wt(t.a)})` + : `hsl(${i}, ${s}%, ${n}%)`; + })(this._rgb) + : void 0; + } + mix(t, e) { + if (t) { + const i = this.rgb, + s = t.rgb; + let n; + const o = e === n ? 0.5 : e, + a = 2 * o - 1, + r = i.a - s.a, + l = ((a * r == -1 ? a : (a + r) / (1 + a * r)) + 1) / 2; + (n = 1 - l), + (i.r = 255 & (l * i.r + n * s.r + 0.5)), + (i.g = 255 & (l * i.g + n * s.g + 0.5)), + (i.b = 255 & (l * i.b + n * s.b + 0.5)), + (i.a = o * i.a + (1 - o) * s.a), + (this.rgb = i); + } + return this; + } + interpolate(t, e) { + return ( + t && + (this._rgb = (function (t, e, i) { + const s = Ut(wt(t.r)), + n = Ut(wt(t.g)), + o = Ut(wt(t.b)); + return { + r: Mt(Yt(s + i * (Ut(wt(e.r)) - s))), + g: Mt(Yt(n + i * (Ut(wt(e.g)) - n))), + b: Mt(Yt(o + i * (Ut(wt(e.b)) - o))), + a: t.a + i * (e.a - t.a), + }; + })(this._rgb, t._rgb, e)), + this + ); + } + clone() { + return new Zt(this.rgb); + } + alpha(t) { + return (this._rgb.a = Mt(t)), this; + } + clearer(t) { + return (this._rgb.a *= 1 - t), this; + } + greyscale() { + const t = this._rgb, + e = _t(0.3 * t.r + 0.59 * t.g + 0.11 * t.b); + return (t.r = t.g = t.b = e), this; + } + opaquer(t) { + return (this._rgb.a *= 1 + t), this; + } + negate() { + const t = this._rgb; + return (t.r = 255 - t.r), (t.g = 255 - t.g), (t.b = 255 - t.b), this; + } + lighten(t) { + return Xt(this._rgb, 2, t), this; + } + darken(t) { + return Xt(this._rgb, 2, -t), this; + } + saturate(t) { + return Xt(this._rgb, 1, t), this; + } + desaturate(t) { + return Xt(this._rgb, 1, -t), this; + } + rotate(t) { + return ( + (function (t, e) { + var i = It(t); + (i[0] = Vt(i[0] + e)), + (i = Ft(i)), + (t.r = i[0]), + (t.g = i[1]), + (t.b = i[2]); + })(this._rgb, t), + this + ); + } + } + function Jt(t) { + if (t && "object" == typeof t) { + const e = t.toString(); + return "[object CanvasPattern]" === e || "[object CanvasGradient]" === e; + } + return !1; + } + function Qt(t) { + return Jt(t) ? t : new Zt(t); + } + function te(t) { + return Jt(t) ? t : new Zt(t).saturate(0.5).darken(0.1).hexString(); + } + const ee = ["x", "y", "borderWidth", "radius", "tension"], + ie = ["color", "borderColor", "backgroundColor"]; + const se = new Map(); + function ne(t, e, i) { + return (function (t, e) { + e = e || {}; + const i = t + JSON.stringify(e); + let s = se.get(i); + return s || ((s = new Intl.NumberFormat(t, e)), se.set(i, s)), s; + })(e, i).format(t); + } + const oe = { + values: (t) => (n(t) ? t : "" + t), + numeric(t, e, i) { + if (0 === t) return "0"; + const s = this.chart.options.locale; + let n, + o = t; + if (i.length > 1) { + const e = Math.max( + Math.abs(i[0].value), + Math.abs(i[i.length - 1].value) + ); + (e < 1e-4 || e > 1e15) && (n = "scientific"), + (o = (function (t, e) { + let i = + e.length > 3 ? e[2].value - e[1].value : e[1].value - e[0].value; + Math.abs(i) >= 1 && t !== Math.floor(t) && (i = t - Math.floor(t)); + return i; + })(t, i)); + } + const a = z(Math.abs(o)), + r = isNaN(a) ? 1 : Math.max(Math.min(-1 * Math.floor(a), 20), 0), + l = { notation: n, minimumFractionDigits: r, maximumFractionDigits: r }; + return Object.assign(l, this.options.ticks.format), ne(t, s, l); + }, + logarithmic(t, e, i) { + if (0 === t) return "0"; + const s = i[e].significand || t / Math.pow(10, Math.floor(z(t))); + return [1, 2, 3, 5, 10, 15].includes(s) || e > 0.8 * i.length + ? oe.numeric.call(this, t, e, i) + : ""; + }, + }; + var ae = { formatters: oe }; + const re = Object.create(null), + le = Object.create(null); + function he(t, e) { + if (!e) return t; + const i = e.split("."); + for (let e = 0, s = i.length; e < s; ++e) { + const s = i[e]; + t = t[s] || (t[s] = Object.create(null)); + } + return t; + } + function ce(t, e, i) { + return "string" == typeof e ? x(he(t, e), i) : x(he(t, ""), e); + } + class de { + constructor(t, e) { + (this.animation = void 0), + (this.backgroundColor = "rgba(0,0,0,0.1)"), + (this.borderColor = "rgba(0,0,0,0.1)"), + (this.color = "#666"), + (this.datasets = {}), + (this.devicePixelRatio = (t) => t.chart.platform.getDevicePixelRatio()), + (this.elements = {}), + (this.events = [ + "mousemove", + "mouseout", + "click", + "touchstart", + "touchmove", + ]), + (this.font = { + family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif", + size: 12, + style: "normal", + lineHeight: 1.2, + weight: null, + }), + (this.hover = {}), + (this.hoverBackgroundColor = (t, e) => te(e.backgroundColor)), + (this.hoverBorderColor = (t, e) => te(e.borderColor)), + (this.hoverColor = (t, e) => te(e.color)), + (this.indexAxis = "x"), + (this.interaction = { + mode: "nearest", + intersect: !0, + includeInvisible: !1, + }), + (this.maintainAspectRatio = !0), + (this.onHover = null), + (this.onClick = null), + (this.parsing = !0), + (this.plugins = {}), + (this.responsive = !0), + (this.scale = void 0), + (this.scales = {}), + (this.showLine = !0), + (this.drawActiveElementsOnTop = !0), + this.describe(t), + this.apply(e); + } + set(t, e) { + return ce(this, t, e); + } + get(t) { + return he(this, t); + } + describe(t, e) { + return ce(le, t, e); + } + override(t, e) { + return ce(re, t, e); + } + route(t, e, i, s) { + const n = he(this, t), + a = he(this, i), + r = "_" + e; + Object.defineProperties(n, { + [r]: { value: n[e], writable: !0 }, + [e]: { + enumerable: !0, + get() { + const t = this[r], + e = a[s]; + return o(t) ? Object.assign({}, e, t) : l(t, e); + }, + set(t) { + this[r] = t; + }, + }, + }); + } + apply(t) { + t.forEach((t) => t(this)); + } + } + var ue = new de( + { + _scriptable: (t) => !t.startsWith("on"), + _indexable: (t) => "events" !== t, + hover: { _fallback: "interaction" }, + interaction: { _scriptable: !1, _indexable: !1 }, + }, + [ + function (t) { + t.set("animation", { + delay: void 0, + duration: 1e3, + easing: "easeOutQuart", + fn: void 0, + from: void 0, + loop: void 0, + to: void 0, + type: void 0, + }), + t.describe("animation", { + _fallback: !1, + _indexable: !1, + _scriptable: (t) => + "onProgress" !== t && "onComplete" !== t && "fn" !== t, + }), + t.set("animations", { + colors: { type: "color", properties: ie }, + numbers: { type: "number", properties: ee }, + }), + t.describe("animations", { _fallback: "animation" }), + t.set("transitions", { + active: { animation: { duration: 400 } }, + resize: { animation: { duration: 0 } }, + show: { + animations: { + colors: { from: "transparent" }, + visible: { type: "boolean", duration: 0 }, + }, + }, + hide: { + animations: { + colors: { to: "transparent" }, + visible: { + type: "boolean", + easing: "linear", + fn: (t) => 0 | t, + }, + }, + }, + }); + }, + function (t) { + t.set("layout", { + autoPadding: !0, + padding: { top: 0, right: 0, bottom: 0, left: 0 }, + }); + }, + function (t) { + t.set("scale", { + display: !0, + offset: !1, + reverse: !1, + beginAtZero: !1, + bounds: "ticks", + clip: !0, + grace: 0, + grid: { + display: !0, + lineWidth: 1, + drawOnChartArea: !0, + drawTicks: !0, + tickLength: 8, + tickWidth: (t, e) => e.lineWidth, + tickColor: (t, e) => e.color, + offset: !1, + }, + border: { display: !0, dash: [], dashOffset: 0, width: 1 }, + title: { display: !1, text: "", padding: { top: 4, bottom: 4 } }, + ticks: { + minRotation: 0, + maxRotation: 50, + mirror: !1, + textStrokeWidth: 0, + textStrokeColor: "", + padding: 3, + display: !0, + autoSkip: !0, + autoSkipPadding: 3, + labelOffset: 0, + callback: ae.formatters.values, + minor: {}, + major: {}, + align: "center", + crossAlign: "near", + showLabelBackdrop: !1, + backdropColor: "rgba(255, 255, 255, 0.75)", + backdropPadding: 2, + }, + }), + t.route("scale.ticks", "color", "", "color"), + t.route("scale.grid", "color", "", "borderColor"), + t.route("scale.border", "color", "", "borderColor"), + t.route("scale.title", "color", "", "color"), + t.describe("scale", { + _fallback: !1, + _scriptable: (t) => + !t.startsWith("before") && + !t.startsWith("after") && + "callback" !== t && + "parser" !== t, + _indexable: (t) => + "borderDash" !== t && "tickBorderDash" !== t && "dash" !== t, + }), + t.describe("scales", { _fallback: "scale" }), + t.describe("scale.ticks", { + _scriptable: (t) => "backdropPadding" !== t && "callback" !== t, + _indexable: (t) => "backdropPadding" !== t, + }); + }, + ] + ); + function fe() { + return "undefined" != typeof window && "undefined" != typeof document; + } + function ge(t) { + let e = t.parentNode; + return e && "[object ShadowRoot]" === e.toString() && (e = e.host), e; + } + function pe(t, e, i) { + let s; + return ( + "string" == typeof t + ? ((s = parseInt(t, 10)), + -1 !== t.indexOf("%") && (s = (s / 100) * e.parentNode[i])) + : (s = t), + s + ); + } + const me = (t) => t.ownerDocument.defaultView.getComputedStyle(t, null); + function xe(t, e) { + return me(t).getPropertyValue(e); + } + const be = ["top", "right", "bottom", "left"]; + function _e(t, e, i) { + const s = {}; + i = i ? "-" + i : ""; + for (let n = 0; n < 4; n++) { + const o = be[n]; + s[o] = parseFloat(t[e + "-" + o + i]) || 0; + } + return (s.width = s.left + s.right), (s.height = s.top + s.bottom), s; + } + const ye = (t, e, i) => (t > 0 || e > 0) && (!i || !i.shadowRoot); + function ve(t, e) { + if ("native" in t) return t; + const { canvas: i, currentDevicePixelRatio: s } = e, + n = me(i), + o = "border-box" === n.boxSizing, + a = _e(n, "padding"), + r = _e(n, "border", "width"), + { + x: l, + y: h, + box: c, + } = (function (t, e) { + const i = t.touches, + s = i && i.length ? i[0] : t, + { offsetX: n, offsetY: o } = s; + let a, + r, + l = !1; + if (ye(n, o, t.target)) (a = n), (r = o); + else { + const t = e.getBoundingClientRect(); + (a = s.clientX - t.left), (r = s.clientY - t.top), (l = !0); + } + return { x: a, y: r, box: l }; + })(t, i), + d = a.left + (c && r.left), + u = a.top + (c && r.top); + let { width: f, height: g } = e; + return ( + o && ((f -= a.width + r.width), (g -= a.height + r.height)), + { + x: Math.round((((l - d) / f) * i.width) / s), + y: Math.round((((h - u) / g) * i.height) / s), + } + ); + } + const Me = (t) => Math.round(10 * t) / 10; + function we(t, e, i, s) { + const n = me(t), + o = _e(n, "margin"), + a = pe(n.maxWidth, t, "clientWidth") || T, + r = pe(n.maxHeight, t, "clientHeight") || T, + l = (function (t, e, i) { + let s, n; + if (void 0 === e || void 0 === i) { + const o = t && ge(t); + if (o) { + const t = o.getBoundingClientRect(), + a = me(o), + r = _e(a, "border", "width"), + l = _e(a, "padding"); + (e = t.width - l.width - r.width), + (i = t.height - l.height - r.height), + (s = pe(a.maxWidth, o, "clientWidth")), + (n = pe(a.maxHeight, o, "clientHeight")); + } else (e = t.clientWidth), (i = t.clientHeight); + } + return { width: e, height: i, maxWidth: s || T, maxHeight: n || T }; + })(t, e, i); + let { width: h, height: c } = l; + if ("content-box" === n.boxSizing) { + const t = _e(n, "border", "width"), + e = _e(n, "padding"); + (h -= e.width + t.width), (c -= e.height + t.height); + } + (h = Math.max(0, h - o.width)), + (c = Math.max(0, s ? h / s : c - o.height)), + (h = Me(Math.min(h, a, l.maxWidth))), + (c = Me(Math.min(c, r, l.maxHeight))), + h && !c && (c = Me(h / 2)); + return ( + (void 0 !== e || void 0 !== i) && + s && + l.height && + c > l.height && + ((c = l.height), (h = Me(Math.floor(c * s)))), + { width: h, height: c } + ); + } + function ke(t, e, i) { + const s = e || 1, + n = Math.floor(t.height * s), + o = Math.floor(t.width * s); + (t.height = Math.floor(t.height)), (t.width = Math.floor(t.width)); + const a = t.canvas; + return ( + a.style && + (i || (!a.style.height && !a.style.width)) && + ((a.style.height = `${t.height}px`), (a.style.width = `${t.width}px`)), + (t.currentDevicePixelRatio !== s || a.height !== n || a.width !== o) && + ((t.currentDevicePixelRatio = s), + (a.height = n), + (a.width = o), + t.ctx.setTransform(s, 0, 0, s, 0, 0), + !0) + ); + } + const Se = (function () { + let t = !1; + try { + const e = { + get passive() { + return (t = !0), !1; + }, + }; + fe() && + (window.addEventListener("test", null, e), + window.removeEventListener("test", null, e)); + } catch (t) {} + return t; + })(); + function Pe(t, e) { + const i = xe(t, e), + s = i && i.match(/^(\d+)(\.\d+)?px$/); + return s ? +s[1] : void 0; + } + function De(t) { + return !t || s(t.size) || s(t.family) + ? null + : (t.style ? t.style + " " : "") + + (t.weight ? t.weight + " " : "") + + t.size + + "px " + + t.family; + } + function Ce(t, e, i, s, n) { + let o = e[n]; + return ( + o || ((o = e[n] = t.measureText(n).width), i.push(n)), o > s && (s = o), s + ); + } + function Oe(t, e, i, s) { + let o = ((s = s || {}).data = s.data || {}), + a = (s.garbageCollect = s.garbageCollect || []); + s.font !== e && + ((o = s.data = {}), (a = s.garbageCollect = []), (s.font = e)), + t.save(), + (t.font = e); + let r = 0; + const l = i.length; + let h, c, d, u, f; + for (h = 0; h < l; h++) + if (((u = i[h]), null == u || n(u))) { + if (n(u)) + for (c = 0, d = u.length; c < d; c++) + (f = u[c]), null == f || n(f) || (r = Ce(t, o, a, r, f)); + } else r = Ce(t, o, a, r, u); + t.restore(); + const g = a.length / 2; + if (g > i.length) { + for (h = 0; h < g; h++) delete o[a[h]]; + a.splice(0, g); + } + return r; + } + function Ae(t, e, i) { + const s = t.currentDevicePixelRatio, + n = 0 !== i ? Math.max(i / 2, 0.5) : 0; + return Math.round((e - n) * s) / s + n; + } + function Te(t, e) { + (e || t) && + ((e = e || t.getContext("2d")).save(), + e.resetTransform(), + e.clearRect(0, 0, t.width, t.height), + e.restore()); + } + function Le(t, e, i, s) { + Ee(t, e, i, s, null); + } + function Ee(t, e, i, s, n) { + let o, a, r, l, h, c, d, u; + const f = e.pointStyle, + g = e.rotation, + p = e.radius; + let m = (g || 0) * L; + if ( + f && + "object" == typeof f && + ((o = f.toString()), + "[object HTMLImageElement]" === o || "[object HTMLCanvasElement]" === o) + ) + return ( + t.save(), + t.translate(i, s), + t.rotate(m), + t.drawImage(f, -f.width / 2, -f.height / 2, f.width, f.height), + void t.restore() + ); + if (!(isNaN(p) || p <= 0)) { + switch ((t.beginPath(), f)) { + default: + n ? t.ellipse(i, s, n / 2, p, 0, 0, O) : t.arc(i, s, p, 0, O), + t.closePath(); + break; + case "triangle": + (c = n ? n / 2 : p), + t.moveTo(i + Math.sin(m) * c, s - Math.cos(m) * p), + (m += I), + t.lineTo(i + Math.sin(m) * c, s - Math.cos(m) * p), + (m += I), + t.lineTo(i + Math.sin(m) * c, s - Math.cos(m) * p), + t.closePath(); + break; + case "rectRounded": + (h = 0.516 * p), + (l = p - h), + (a = Math.cos(m + R) * l), + (d = Math.cos(m + R) * (n ? n / 2 - h : l)), + (r = Math.sin(m + R) * l), + (u = Math.sin(m + R) * (n ? n / 2 - h : l)), + t.arc(i - d, s - r, h, m - C, m - E), + t.arc(i + u, s - a, h, m - E, m), + t.arc(i + d, s + r, h, m, m + E), + t.arc(i - u, s + a, h, m + E, m + C), + t.closePath(); + break; + case "rect": + if (!g) { + (l = Math.SQRT1_2 * p), + (c = n ? n / 2 : l), + t.rect(i - c, s - l, 2 * c, 2 * l); + break; + } + m += R; + case "rectRot": + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + u, s - a), + t.lineTo(i + d, s + r), + t.lineTo(i - u, s + a), + t.closePath(); + break; + case "crossRot": + m += R; + case "cross": + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + d, s + r), + t.moveTo(i + u, s - a), + t.lineTo(i - u, s + a); + break; + case "star": + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + d, s + r), + t.moveTo(i + u, s - a), + t.lineTo(i - u, s + a), + (m += R), + (d = Math.cos(m) * (n ? n / 2 : p)), + (a = Math.cos(m) * p), + (r = Math.sin(m) * p), + (u = Math.sin(m) * (n ? n / 2 : p)), + t.moveTo(i - d, s - r), + t.lineTo(i + d, s + r), + t.moveTo(i + u, s - a), + t.lineTo(i - u, s + a); + break; + case "line": + (a = n ? n / 2 : Math.cos(m) * p), + (r = Math.sin(m) * p), + t.moveTo(i - a, s - r), + t.lineTo(i + a, s + r); + break; + case "dash": + t.moveTo(i, s), + t.lineTo(i + Math.cos(m) * (n ? n / 2 : p), s + Math.sin(m) * p); + break; + case !1: + t.closePath(); + } + t.fill(), e.borderWidth > 0 && t.stroke(); + } + } + function Re(t, e, i) { + return ( + (i = i || 0.5), + !e || + (t && + t.x > e.left - i && + t.x < e.right + i && + t.y > e.top - i && + t.y < e.bottom + i) + ); + } + function Ie(t, e) { + t.save(), + t.beginPath(), + t.rect(e.left, e.top, e.right - e.left, e.bottom - e.top), + t.clip(); + } + function ze(t) { + t.restore(); + } + function Fe(t, e, i, s, n) { + if (!e) return t.lineTo(i.x, i.y); + if ("middle" === n) { + const s = (e.x + i.x) / 2; + t.lineTo(s, e.y), t.lineTo(s, i.y); + } else ("after" === n) != !!s ? t.lineTo(e.x, i.y) : t.lineTo(i.x, e.y); + t.lineTo(i.x, i.y); + } + function Ve(t, e, i, s) { + if (!e) return t.lineTo(i.x, i.y); + t.bezierCurveTo( + s ? e.cp1x : e.cp2x, + s ? e.cp1y : e.cp2y, + s ? i.cp2x : i.cp1x, + s ? i.cp2y : i.cp1y, + i.x, + i.y + ); + } + function Be(t, e, i, s, n) { + if (n.strikethrough || n.underline) { + const o = t.measureText(s), + a = e - o.actualBoundingBoxLeft, + r = e + o.actualBoundingBoxRight, + l = i - o.actualBoundingBoxAscent, + h = i + o.actualBoundingBoxDescent, + c = n.strikethrough ? (l + h) / 2 : h; + (t.strokeStyle = t.fillStyle), + t.beginPath(), + (t.lineWidth = n.decorationWidth || 2), + t.moveTo(a, c), + t.lineTo(r, c), + t.stroke(); + } + } + function We(t, e) { + const i = t.fillStyle; + (t.fillStyle = e.color), + t.fillRect(e.left, e.top, e.width, e.height), + (t.fillStyle = i); + } + function Ne(t, e, i, o, a, r = {}) { + const l = n(e) ? e : [e], + h = r.strokeWidth > 0 && "" !== r.strokeColor; + let c, d; + for ( + t.save(), + t.font = a.string, + (function (t, e) { + e.translation && t.translate(e.translation[0], e.translation[1]), + s(e.rotation) || t.rotate(e.rotation), + e.color && (t.fillStyle = e.color), + e.textAlign && (t.textAlign = e.textAlign), + e.textBaseline && (t.textBaseline = e.textBaseline); + })(t, r), + c = 0; + c < l.length; + ++c + ) + (d = l[c]), + r.backdrop && We(t, r.backdrop), + h && + (r.strokeColor && (t.strokeStyle = r.strokeColor), + s(r.strokeWidth) || (t.lineWidth = r.strokeWidth), + t.strokeText(d, i, o, r.maxWidth)), + t.fillText(d, i, o, r.maxWidth), + Be(t, i, o, d, r), + (o += Number(a.lineHeight)); + t.restore(); + } + function He(t, e) { + const { x: i, y: s, w: n, h: o, radius: a } = e; + t.arc(i + a.topLeft, s + a.topLeft, a.topLeft, 1.5 * C, C, !0), + t.lineTo(i, s + o - a.bottomLeft), + t.arc(i + a.bottomLeft, s + o - a.bottomLeft, a.bottomLeft, C, E, !0), + t.lineTo(i + n - a.bottomRight, s + o), + t.arc( + i + n - a.bottomRight, + s + o - a.bottomRight, + a.bottomRight, + E, + 0, + !0 + ), + t.lineTo(i + n, s + a.topRight), + t.arc(i + n - a.topRight, s + a.topRight, a.topRight, 0, -E, !0), + t.lineTo(i + a.topLeft, s); + } + function je(t, e = [""], i, s, n = () => t[0]) { + const o = i || t; + void 0 === s && (s = ti("_fallback", t)); + const a = { + [Symbol.toStringTag]: "Object", + _cacheable: !0, + _scopes: t, + _rootScopes: o, + _fallback: s, + _getTarget: n, + override: (i) => je([i, ...t], e, o, s), + }; + return new Proxy(a, { + deleteProperty: (e, i) => ( + delete e[i], delete e._keys, delete t[0][i], !0 + ), + get: (i, s) => + qe(i, s, () => + (function (t, e, i, s) { + let n; + for (const o of e) + if (((n = ti(Ue(o, t), i)), void 0 !== n)) + return Xe(t, n) ? Je(i, s, t, n) : n; + })(s, e, t, i) + ), + getOwnPropertyDescriptor: (t, e) => + Reflect.getOwnPropertyDescriptor(t._scopes[0], e), + getPrototypeOf: () => Reflect.getPrototypeOf(t[0]), + has: (t, e) => ei(t).includes(e), + ownKeys: (t) => ei(t), + set(t, e, i) { + const s = t._storage || (t._storage = n()); + return (t[e] = s[e] = i), delete t._keys, !0; + }, + }); + } + function $e(t, e, i, s) { + const a = { + _cacheable: !1, + _proxy: t, + _context: e, + _subProxy: i, + _stack: new Set(), + _descriptors: Ye(t, s), + setContext: (e) => $e(t, e, i, s), + override: (n) => $e(t.override(n), e, i, s), + }; + return new Proxy(a, { + deleteProperty: (e, i) => (delete e[i], delete t[i], !0), + get: (t, e, i) => + qe(t, e, () => + (function (t, e, i) { + const { _proxy: s, _context: a, _subProxy: r, _descriptors: l } = t; + let h = s[e]; + S(h) && + l.isScriptable(e) && + (h = (function (t, e, i, s) { + const { _proxy: n, _context: o, _subProxy: a, _stack: r } = i; + if (r.has(t)) + throw new Error( + "Recursion detected: " + Array.from(r).join("->") + "->" + t + ); + r.add(t); + let l = e(o, a || s); + r.delete(t), Xe(t, l) && (l = Je(n._scopes, n, t, l)); + return l; + })(e, h, t, i)); + n(h) && + h.length && + (h = (function (t, e, i, s) { + const { + _proxy: n, + _context: a, + _subProxy: r, + _descriptors: l, + } = i; + if (void 0 !== a.index && s(t)) return e[a.index % e.length]; + if (o(e[0])) { + const i = e, + s = n._scopes.filter((t) => t !== i); + e = []; + for (const o of i) { + const i = Je(s, n, t, o); + e.push($e(i, a, r && r[t], l)); + } + } + return e; + })(e, h, t, l.isIndexable)); + Xe(e, h) && (h = $e(h, a, r && r[e], l)); + return h; + })(t, e, i) + ), + getOwnPropertyDescriptor: (e, i) => + e._descriptors.allKeys + ? Reflect.has(t, i) + ? { enumerable: !0, configurable: !0 } + : void 0 + : Reflect.getOwnPropertyDescriptor(t, i), + getPrototypeOf: () => Reflect.getPrototypeOf(t), + has: (e, i) => Reflect.has(t, i), + ownKeys: () => Reflect.ownKeys(t), + set: (e, i, s) => ((t[i] = s), delete e[i], !0), + }); + } + function Ye(t, e = { scriptable: !0, indexable: !0 }) { + const { + _scriptable: i = e.scriptable, + _indexable: s = e.indexable, + _allKeys: n = e.allKeys, + } = t; + return { + allKeys: n, + scriptable: i, + indexable: s, + isScriptable: S(i) ? i : () => i, + isIndexable: S(s) ? s : () => s, + }; + } + const Ue = (t, e) => (t ? t + w(e) : e), + Xe = (t, e) => + o(e) && + "adapters" !== t && + (null === Object.getPrototypeOf(e) || e.constructor === Object); + function qe(t, e, i) { + if (Object.prototype.hasOwnProperty.call(t, e) || "constructor" === e) + return t[e]; + const s = i(); + return (t[e] = s), s; + } + function Ke(t, e, i) { + return S(t) ? t(e, i) : t; + } + const Ge = (t, e) => (!0 === t ? e : "string" == typeof t ? M(e, t) : void 0); + function Ze(t, e, i, s, n) { + for (const o of e) { + const e = Ge(i, o); + if (e) { + t.add(e); + const o = Ke(e._fallback, i, n); + if (void 0 !== o && o !== i && o !== s) return o; + } else if (!1 === e && void 0 !== s && i !== s) return null; + } + return !1; + } + function Je(t, e, i, s) { + const a = e._rootScopes, + r = Ke(e._fallback, i, s), + l = [...t, ...a], + h = new Set(); + h.add(s); + let c = Qe(h, l, i, r || i, s); + return ( + null !== c && + (void 0 === r || r === i || ((c = Qe(h, l, r, c, s)), null !== c)) && + je(Array.from(h), [""], a, r, () => + (function (t, e, i) { + const s = t._getTarget(); + e in s || (s[e] = {}); + const a = s[e]; + if (n(a) && o(i)) return i; + return a || {}; + })(e, i, s) + ) + ); + } + function Qe(t, e, i, s, n) { + for (; i; ) i = Ze(t, e, i, s, n); + return i; + } + function ti(t, e) { + for (const i of e) { + if (!i) continue; + const e = i[t]; + if (void 0 !== e) return e; + } + } + function ei(t) { + let e = t._keys; + return ( + e || + (e = t._keys = + (function (t) { + const e = new Set(); + for (const i of t) + for (const t of Object.keys(i).filter((t) => !t.startsWith("_"))) + e.add(t); + return Array.from(e); + })(t._scopes)), + e + ); + } + function ii(t, e, i, s) { + const { iScale: n } = t, + { key: o = "r" } = this._parsing, + a = new Array(s); + let r, l, h, c; + for (r = 0, l = s; r < l; ++r) + (h = r + i), (c = e[h]), (a[r] = { r: n.parse(M(c, o), h) }); + return a; + } + const si = Number.EPSILON || 1e-14, + ni = (t, e) => e < t.length && !t[e].skip && t[e], + oi = (t) => ("x" === t ? "y" : "x"); + function ai(t, e, i, s) { + const n = t.skip ? e : t, + o = e, + a = i.skip ? e : i, + r = q(o, n), + l = q(a, o); + let h = r / (r + l), + c = l / (r + l); + (h = isNaN(h) ? 0 : h), (c = isNaN(c) ? 0 : c); + const d = s * h, + u = s * c; + return { + previous: { x: o.x - d * (a.x - n.x), y: o.y - d * (a.y - n.y) }, + next: { x: o.x + u * (a.x - n.x), y: o.y + u * (a.y - n.y) }, + }; + } + function ri(t, e = "x") { + const i = oi(e), + s = t.length, + n = Array(s).fill(0), + o = Array(s); + let a, + r, + l, + h = ni(t, 0); + for (a = 0; a < s; ++a) + if (((r = l), (l = h), (h = ni(t, a + 1)), l)) { + if (h) { + const t = h[e] - l[e]; + n[a] = 0 !== t ? (h[i] - l[i]) / t : 0; + } + o[a] = r + ? h + ? F(n[a - 1]) !== F(n[a]) + ? 0 + : (n[a - 1] + n[a]) / 2 + : n[a - 1] + : n[a]; + } + !(function (t, e, i) { + const s = t.length; + let n, + o, + a, + r, + l, + h = ni(t, 0); + for (let c = 0; c < s - 1; ++c) + (l = h), + (h = ni(t, c + 1)), + l && + h && + (V(e[c], 0, si) + ? (i[c] = i[c + 1] = 0) + : ((n = i[c] / e[c]), + (o = i[c + 1] / e[c]), + (r = Math.pow(n, 2) + Math.pow(o, 2)), + r <= 9 || + ((a = 3 / Math.sqrt(r)), + (i[c] = n * a * e[c]), + (i[c + 1] = o * a * e[c])))); + })(t, n, o), + (function (t, e, i = "x") { + const s = oi(i), + n = t.length; + let o, + a, + r, + l = ni(t, 0); + for (let h = 0; h < n; ++h) { + if (((a = r), (r = l), (l = ni(t, h + 1)), !r)) continue; + const n = r[i], + c = r[s]; + a && + ((o = (n - a[i]) / 3), + (r[`cp1${i}`] = n - o), + (r[`cp1${s}`] = c - o * e[h])), + l && + ((o = (l[i] - n) / 3), + (r[`cp2${i}`] = n + o), + (r[`cp2${s}`] = c + o * e[h])); + } + })(t, o, e); + } + function li(t, e, i) { + return Math.max(Math.min(t, i), e); + } + function hi(t, e, i, s, n) { + let o, a, r, l; + if ( + (e.spanGaps && (t = t.filter((t) => !t.skip)), + "monotone" === e.cubicInterpolationMode) + ) + ri(t, n); + else { + let i = s ? t[t.length - 1] : t[0]; + for (o = 0, a = t.length; o < a; ++o) + (r = t[o]), + (l = ai(i, r, t[Math.min(o + 1, a - (s ? 0 : 1)) % a], e.tension)), + (r.cp1x = l.previous.x), + (r.cp1y = l.previous.y), + (r.cp2x = l.next.x), + (r.cp2y = l.next.y), + (i = r); + } + e.capBezierPoints && + (function (t, e) { + let i, + s, + n, + o, + a, + r = Re(t[0], e); + for (i = 0, s = t.length; i < s; ++i) + (a = o), + (o = r), + (r = i < s - 1 && Re(t[i + 1], e)), + o && + ((n = t[i]), + a && + ((n.cp1x = li(n.cp1x, e.left, e.right)), + (n.cp1y = li(n.cp1y, e.top, e.bottom))), + r && + ((n.cp2x = li(n.cp2x, e.left, e.right)), + (n.cp2y = li(n.cp2y, e.top, e.bottom)))); + })(t, i); + } + const ci = (t) => 0 === t || 1 === t, + di = (t, e, i) => -Math.pow(2, 10 * (t -= 1)) * Math.sin(((t - e) * O) / i), + ui = (t, e, i) => Math.pow(2, -10 * t) * Math.sin(((t - e) * O) / i) + 1, + fi = { + linear: (t) => t, + easeInQuad: (t) => t * t, + easeOutQuad: (t) => -t * (t - 2), + easeInOutQuad: (t) => + (t /= 0.5) < 1 ? 0.5 * t * t : -0.5 * (--t * (t - 2) - 1), + easeInCubic: (t) => t * t * t, + easeOutCubic: (t) => (t -= 1) * t * t + 1, + easeInOutCubic: (t) => + (t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2), + easeInQuart: (t) => t * t * t * t, + easeOutQuart: (t) => -((t -= 1) * t * t * t - 1), + easeInOutQuart: (t) => + (t /= 0.5) < 1 + ? 0.5 * t * t * t * t + : -0.5 * ((t -= 2) * t * t * t - 2), + easeInQuint: (t) => t * t * t * t * t, + easeOutQuint: (t) => (t -= 1) * t * t * t * t + 1, + easeInOutQuint: (t) => + (t /= 0.5) < 1 + ? 0.5 * t * t * t * t * t + : 0.5 * ((t -= 2) * t * t * t * t + 2), + easeInSine: (t) => 1 - Math.cos(t * E), + easeOutSine: (t) => Math.sin(t * E), + easeInOutSine: (t) => -0.5 * (Math.cos(C * t) - 1), + easeInExpo: (t) => (0 === t ? 0 : Math.pow(2, 10 * (t - 1))), + easeOutExpo: (t) => (1 === t ? 1 : 1 - Math.pow(2, -10 * t)), + easeInOutExpo: (t) => + ci(t) + ? t + : t < 0.5 + ? 0.5 * Math.pow(2, 10 * (2 * t - 1)) + : 0.5 * (2 - Math.pow(2, -10 * (2 * t - 1))), + easeInCirc: (t) => (t >= 1 ? t : -(Math.sqrt(1 - t * t) - 1)), + easeOutCirc: (t) => Math.sqrt(1 - (t -= 1) * t), + easeInOutCirc: (t) => + (t /= 0.5) < 1 + ? -0.5 * (Math.sqrt(1 - t * t) - 1) + : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1), + easeInElastic: (t) => (ci(t) ? t : di(t, 0.075, 0.3)), + easeOutElastic: (t) => (ci(t) ? t : ui(t, 0.075, 0.3)), + easeInOutElastic(t) { + const e = 0.1125; + return ci(t) + ? t + : t < 0.5 + ? 0.5 * di(2 * t, e, 0.45) + : 0.5 + 0.5 * ui(2 * t - 1, e, 0.45); + }, + easeInBack(t) { + const e = 1.70158; + return t * t * ((e + 1) * t - e); + }, + easeOutBack(t) { + const e = 1.70158; + return (t -= 1) * t * ((e + 1) * t + e) + 1; + }, + easeInOutBack(t) { + let e = 1.70158; + return (t /= 0.5) < 1 + ? t * t * ((1 + (e *= 1.525)) * t - e) * 0.5 + : 0.5 * ((t -= 2) * t * ((1 + (e *= 1.525)) * t + e) + 2); + }, + easeInBounce: (t) => 1 - fi.easeOutBounce(1 - t), + easeOutBounce(t) { + const e = 7.5625, + i = 2.75; + return t < 1 / i + ? e * t * t + : t < 2 / i + ? e * (t -= 1.5 / i) * t + 0.75 + : t < 2.5 / i + ? e * (t -= 2.25 / i) * t + 0.9375 + : e * (t -= 2.625 / i) * t + 0.984375; + }, + easeInOutBounce: (t) => + t < 0.5 + ? 0.5 * fi.easeInBounce(2 * t) + : 0.5 * fi.easeOutBounce(2 * t - 1) + 0.5, + }; + function gi(t, e, i, s) { + return { x: t.x + i * (e.x - t.x), y: t.y + i * (e.y - t.y) }; + } + function pi(t, e, i, s) { + return { + x: t.x + i * (e.x - t.x), + y: + "middle" === s + ? i < 0.5 + ? t.y + : e.y + : "after" === s + ? i < 1 + ? t.y + : e.y + : i > 0 + ? e.y + : t.y, + }; + } + function mi(t, e, i, s) { + const n = { x: t.cp2x, y: t.cp2y }, + o = { x: e.cp1x, y: e.cp1y }, + a = gi(t, n, i), + r = gi(n, o, i), + l = gi(o, e, i), + h = gi(a, r, i), + c = gi(r, l, i); + return gi(h, c, i); + } + const xi = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/, + bi = + /^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/; + function _i(t, e) { + const i = ("" + t).match(xi); + if (!i || "normal" === i[1]) return 1.2 * e; + switch (((t = +i[2]), i[3])) { + case "px": + return t; + case "%": + t /= 100; + } + return e * t; + } + const yi = (t) => +t || 0; + function vi(t, e) { + const i = {}, + s = o(e), + n = s ? Object.keys(e) : e, + a = o(t) ? (s ? (i) => l(t[i], t[e[i]]) : (e) => t[e]) : () => t; + for (const t of n) i[t] = yi(a(t)); + return i; + } + function Mi(t) { + return vi(t, { top: "y", right: "x", bottom: "y", left: "x" }); + } + function wi(t) { + return vi(t, ["topLeft", "topRight", "bottomLeft", "bottomRight"]); + } + function ki(t) { + const e = Mi(t); + return (e.width = e.left + e.right), (e.height = e.top + e.bottom), e; + } + function Si(t, e) { + (t = t || {}), (e = e || ue.font); + let i = l(t.size, e.size); + "string" == typeof i && (i = parseInt(i, 10)); + let s = l(t.style, e.style); + s && + !("" + s).match(bi) && + (console.warn('Invalid font style specified: "' + s + '"'), (s = void 0)); + const n = { + family: l(t.family, e.family), + lineHeight: _i(l(t.lineHeight, e.lineHeight), i), + size: i, + style: s, + weight: l(t.weight, e.weight), + string: "", + }; + return (n.string = De(n)), n; + } + function Pi(t, e, i, s) { + let o, + a, + r, + l = !0; + for (o = 0, a = t.length; o < a; ++o) + if ( + ((r = t[o]), + void 0 !== r && + (void 0 !== e && "function" == typeof r && ((r = r(e)), (l = !1)), + void 0 !== i && n(r) && ((r = r[i % r.length]), (l = !1)), + void 0 !== r)) + ) + return s && !l && (s.cacheable = !1), r; + } + function Di(t, e, i) { + const { min: s, max: n } = t, + o = c(e, (n - s) / 2), + a = (t, e) => (i && 0 === t ? 0 : t + e); + return { min: a(s, -Math.abs(o)), max: a(n, o) }; + } + function Ci(t, e) { + return Object.assign(Object.create(t), e); + } + function Oi(t, e, i) { + return t + ? (function (t, e) { + return { + x: (i) => t + t + e - i, + setWidth(t) { + e = t; + }, + textAlign: (t) => + "center" === t ? t : "right" === t ? "left" : "right", + xPlus: (t, e) => t - e, + leftForLtr: (t, e) => t - e, + }; + })(e, i) + : { + x: (t) => t, + setWidth(t) {}, + textAlign: (t) => t, + xPlus: (t, e) => t + e, + leftForLtr: (t, e) => t, + }; + } + function Ai(t, e) { + let i, s; + ("ltr" !== e && "rtl" !== e) || + ((i = t.canvas.style), + (s = [ + i.getPropertyValue("direction"), + i.getPropertyPriority("direction"), + ]), + i.setProperty("direction", e, "important"), + (t.prevTextDirection = s)); + } + function Ti(t, e) { + void 0 !== e && + (delete t.prevTextDirection, + t.canvas.style.setProperty("direction", e[0], e[1])); + } + function Li(t) { + return "angle" === t + ? { between: Z, compare: K, normalize: G } + : { between: tt, compare: (t, e) => t - e, normalize: (t) => t }; + } + function Ei({ start: t, end: e, count: i, loop: s, style: n }) { + return { + start: t % i, + end: e % i, + loop: s && (e - t + 1) % i == 0, + style: n, + }; + } + function Ri(t, e, i) { + if (!i) return [t]; + const { property: s, start: n, end: o } = i, + a = e.length, + { compare: r, between: l, normalize: h } = Li(s), + { + start: c, + end: d, + loop: u, + style: f, + } = (function (t, e, i) { + const { property: s, start: n, end: o } = i, + { between: a, normalize: r } = Li(s), + l = e.length; + let h, + c, + { start: d, end: u, loop: f } = t; + if (f) { + for ( + d += l, u += l, h = 0, c = l; + h < c && a(r(e[d % l][s]), n, o); + ++h + ) + d--, u--; + (d %= l), (u %= l); + } + return u < d && (u += l), { start: d, end: u, loop: f, style: t.style }; + })(t, e, i), + g = []; + let p, + m, + x, + b = !1, + _ = null; + const y = () => b || (l(n, x, p) && 0 !== r(n, x)), + v = () => !b || 0 === r(o, p) || l(o, x, p); + for (let t = c, i = c; t <= d; ++t) + (m = e[t % a]), + m.skip || + ((p = h(m[s])), + p !== x && + ((b = l(p, n, o)), + null === _ && y() && (_ = 0 === r(p, n) ? t : i), + null !== _ && + v() && + (g.push(Ei({ start: _, end: t, loop: u, count: a, style: f })), + (_ = null)), + (i = t), + (x = p))); + return ( + null !== _ && + g.push(Ei({ start: _, end: d, loop: u, count: a, style: f })), + g + ); + } + function Ii(t, e) { + const i = [], + s = t.segments; + for (let n = 0; n < s.length; n++) { + const o = Ri(s[n], t.points, e); + o.length && i.push(...o); + } + return i; + } + function zi(t, e) { + const i = t.points, + s = t.options.spanGaps, + n = i.length; + if (!n) return []; + const o = !!t._loop, + { start: a, end: r } = (function (t, e, i, s) { + let n = 0, + o = e - 1; + if (i && !s) for (; n < e && !t[n].skip; ) n++; + for (; n < e && t[n].skip; ) n++; + for (n %= e, i && (o += n); o > n && t[o % e].skip; ) o--; + return (o %= e), { start: n, end: o }; + })(i, n, o, s); + if (!0 === s) return Fi(t, [{ start: a, end: r, loop: o }], i, e); + return Fi( + t, + (function (t, e, i, s) { + const n = t.length, + o = []; + let a, + r = e, + l = t[e]; + for (a = e + 1; a <= i; ++a) { + const i = t[a % n]; + i.skip || i.stop + ? l.skip || + ((s = !1), + o.push({ start: e % n, end: (a - 1) % n, loop: s }), + (e = r = i.stop ? a : null)) + : ((r = a), l.skip && (e = a)), + (l = i); + } + return null !== r && o.push({ start: e % n, end: r % n, loop: s }), o; + })(i, a, r < a ? r + n : r, !!t._fullLoop && 0 === a && r === n - 1), + i, + e + ); + } + function Fi(t, e, i, s) { + return s && s.setContext && i + ? (function (t, e, i, s) { + const n = t._chart.getContext(), + o = Vi(t.options), + { + _datasetIndex: a, + options: { spanGaps: r }, + } = t, + l = i.length, + h = []; + let c = o, + d = e[0].start, + u = d; + function f(t, e, s, n) { + const o = r ? -1 : 1; + if (t !== e) { + for (t += l; i[t % l].skip; ) t -= o; + for (; i[e % l].skip; ) e += o; + t % l != e % l && + (h.push({ start: t % l, end: e % l, loop: s, style: n }), + (c = n), + (d = e % l)); + } + } + for (const t of e) { + d = r ? d : t.start; + let e, + o = i[d % l]; + for (u = d + 1; u <= t.end; u++) { + const r = i[u % l]; + (e = Vi( + s.setContext( + Ci(n, { + type: "segment", + p0: o, + p1: r, + p0DataIndex: (u - 1) % l, + p1DataIndex: u % l, + datasetIndex: a, + }) + ) + )), + Bi(e, c) && f(d, u - 1, t.loop, c), + (o = r), + (c = e); + } + d < u - 1 && f(d, u - 1, t.loop, c); + } + return h; + })(t, e, i, s) + : e; + } + function Vi(t) { + return { + backgroundColor: t.backgroundColor, + borderCapStyle: t.borderCapStyle, + borderDash: t.borderDash, + borderDashOffset: t.borderDashOffset, + borderJoinStyle: t.borderJoinStyle, + borderWidth: t.borderWidth, + borderColor: t.borderColor, + }; + } + function Bi(t, e) { + if (!e) return !1; + const i = [], + s = function (t, e) { + return Jt(e) ? (i.includes(e) || i.push(e), i.indexOf(e)) : e; + }; + return JSON.stringify(t, s) !== JSON.stringify(e, s); + } + var Wi = Object.freeze({ + __proto__: null, + HALF_PI: E, + INFINITY: T, + PI: C, + PITAU: A, + QUARTER_PI: R, + RAD_PER_DEG: L, + TAU: O, + TWO_THIRDS_PI: I, + _addGrace: Di, + _alignPixel: Ae, + _alignStartEnd: ft, + _angleBetween: Z, + _angleDiff: K, + _arrayUnique: lt, + _attachContext: $e, + _bezierCurveTo: Ve, + _bezierInterpolation: mi, + _boundSegment: Ri, + _boundSegments: Ii, + _capitalize: w, + _computeSegments: zi, + _createResolver: je, + _decimalPlaces: U, + _deprecated: function (t, e, i, s) { + void 0 !== e && + console.warn( + t + ': "' + i + '" is deprecated. Please use "' + s + '" instead' + ); + }, + _descriptors: Ye, + _elementsEqual: f, + _factorize: W, + _filterBetween: nt, + _getParentNode: ge, + _getStartAndCountOfVisiblePoints: pt, + _int16Range: Q, + _isBetween: tt, + _isClickEvent: D, + _isDomSupported: fe, + _isPointInArea: Re, + _limitValue: J, + _longestText: Oe, + _lookup: et, + _lookupByKey: it, + _measureText: Ce, + _merger: m, + _mergerIf: _, + _normalizeAngle: G, + _parseObjectDataRadialScale: ii, + _pointInLine: gi, + _readValueToProps: vi, + _rlookupByKey: st, + _scaleRangesChanged: mt, + _setMinAndMaxByKey: j, + _splitKey: v, + _steppedInterpolation: pi, + _steppedLineTo: Fe, + _textX: gt, + _toLeftRightCenter: ut, + _updateBezierControlPoints: hi, + addRoundedRectPath: He, + almostEquals: V, + almostWhole: H, + callback: d, + clearCanvas: Te, + clipArea: Ie, + clone: g, + color: Qt, + createContext: Ci, + debounce: dt, + defined: k, + distanceBetweenPoints: q, + drawPoint: Le, + drawPointLegend: Ee, + each: u, + easingEffects: fi, + finiteOrDefault: r, + fontString: function (t, e, i) { + return e + " " + t + "px " + i; + }, + formatNumber: ne, + getAngleFromPoint: X, + getHoverColor: te, + getMaximumSize: we, + getRelativePosition: ve, + getRtlAdapter: Oi, + getStyle: xe, + isArray: n, + isFinite: a, + isFunction: S, + isNullOrUndef: s, + isNumber: N, + isObject: o, + isPatternOrGradient: Jt, + listenArrayEvents: at, + log10: z, + merge: x, + mergeIf: b, + niceNum: B, + noop: e, + overrideTextDirection: Ai, + readUsedSize: Pe, + renderText: Ne, + requestAnimFrame: ht, + resolve: Pi, + resolveObjectKey: M, + restoreTextDirection: Ti, + retinaScale: ke, + setsEqual: P, + sign: F, + splineCurve: ai, + splineCurveMonotone: ri, + supportsEventListenerOptions: Se, + throttled: ct, + toDegrees: Y, + toDimension: c, + toFont: Si, + toFontString: De, + toLineHeight: _i, + toPadding: ki, + toPercentage: h, + toRadians: $, + toTRBL: Mi, + toTRBLCorners: wi, + uid: i, + unclipArea: ze, + unlistenArrayEvents: rt, + valueOrDefault: l, + }); + function Ni(t, e, i, s) { + const { controller: n, data: o, _sorted: a } = t, + r = n._cachedMeta.iScale; + if (r && e === r.axis && "r" !== e && a && o.length) { + const t = r._reversePixels ? st : it; + if (!s) return t(o, e, i); + if (n._sharedOptions) { + const s = o[0], + n = "function" == typeof s.getRange && s.getRange(e); + if (n) { + const s = t(o, e, i - n), + a = t(o, e, i + n); + return { lo: s.lo, hi: a.hi }; + } + } + } + return { lo: 0, hi: o.length - 1 }; + } + function Hi(t, e, i, s, n) { + const o = t.getSortedVisibleDatasetMetas(), + a = i[e]; + for (let t = 0, i = o.length; t < i; ++t) { + const { index: i, data: r } = o[t], + { lo: l, hi: h } = Ni(o[t], e, a, n); + for (let t = l; t <= h; ++t) { + const e = r[t]; + e.skip || s(e, i, t); + } + } + } + function ji(t, e, i, s, n) { + const o = []; + if (!n && !t.isPointInArea(e)) return o; + return ( + Hi( + t, + i, + e, + function (i, a, r) { + (n || Re(i, t.chartArea, 0)) && + i.inRange(e.x, e.y, s) && + o.push({ element: i, datasetIndex: a, index: r }); + }, + !0 + ), + o + ); + } + function $i(t, e, i, s, n, o) { + let a = []; + const r = (function (t) { + const e = -1 !== t.indexOf("x"), + i = -1 !== t.indexOf("y"); + return function (t, s) { + const n = e ? Math.abs(t.x - s.x) : 0, + o = i ? Math.abs(t.y - s.y) : 0; + return Math.sqrt(Math.pow(n, 2) + Math.pow(o, 2)); + }; + })(i); + let l = Number.POSITIVE_INFINITY; + return ( + Hi(t, i, e, function (i, h, c) { + const d = i.inRange(e.x, e.y, n); + if (s && !d) return; + const u = i.getCenterPoint(n); + if (!(!!o || t.isPointInArea(u)) && !d) return; + const f = r(e, u); + f < l + ? ((a = [{ element: i, datasetIndex: h, index: c }]), (l = f)) + : f === l && a.push({ element: i, datasetIndex: h, index: c }); + }), + a + ); + } + function Yi(t, e, i, s, n, o) { + return o || t.isPointInArea(e) + ? "r" !== i || s + ? $i(t, e, i, s, n, o) + : (function (t, e, i, s) { + let n = []; + return ( + Hi(t, i, e, function (t, i, o) { + const { startAngle: a, endAngle: r } = t.getProps( + ["startAngle", "endAngle"], + s + ), + { angle: l } = X(t, { x: e.x, y: e.y }); + Z(l, a, r) && n.push({ element: t, datasetIndex: i, index: o }); + }), + n + ); + })(t, e, i, n) + : []; + } + function Ui(t, e, i, s, n) { + const o = [], + a = "x" === i ? "inXRange" : "inYRange"; + let r = !1; + return ( + Hi(t, i, e, (t, s, l) => { + t[a] && + t[a](e[i], n) && + (o.push({ element: t, datasetIndex: s, index: l }), + (r = r || t.inRange(e.x, e.y, n))); + }), + s && !r ? [] : o + ); + } + var Xi = { + evaluateInteractionItems: Hi, + modes: { + index(t, e, i, s) { + const n = ve(e, t), + o = i.axis || "x", + a = i.includeInvisible || !1, + r = i.intersect ? ji(t, n, o, s, a) : Yi(t, n, o, !1, s, a), + l = []; + return r.length + ? (t.getSortedVisibleDatasetMetas().forEach((t) => { + const e = r[0].index, + i = t.data[e]; + i && + !i.skip && + l.push({ element: i, datasetIndex: t.index, index: e }); + }), + l) + : []; + }, + dataset(t, e, i, s) { + const n = ve(e, t), + o = i.axis || "xy", + a = i.includeInvisible || !1; + let r = i.intersect ? ji(t, n, o, s, a) : Yi(t, n, o, !1, s, a); + if (r.length > 0) { + const e = r[0].datasetIndex, + i = t.getDatasetMeta(e).data; + r = []; + for (let t = 0; t < i.length; ++t) + r.push({ element: i[t], datasetIndex: e, index: t }); + } + return r; + }, + point: (t, e, i, s) => + ji(t, ve(e, t), i.axis || "xy", s, i.includeInvisible || !1), + nearest(t, e, i, s) { + const n = ve(e, t), + o = i.axis || "xy", + a = i.includeInvisible || !1; + return Yi(t, n, o, i.intersect, s, a); + }, + x: (t, e, i, s) => Ui(t, ve(e, t), "x", i.intersect, s), + y: (t, e, i, s) => Ui(t, ve(e, t), "y", i.intersect, s), + }, + }; + const qi = ["left", "top", "right", "bottom"]; + function Ki(t, e) { + return t.filter((t) => t.pos === e); + } + function Gi(t, e) { + return t.filter((t) => -1 === qi.indexOf(t.pos) && t.box.axis === e); + } + function Zi(t, e) { + return t.sort((t, i) => { + const s = e ? i : t, + n = e ? t : i; + return s.weight === n.weight ? s.index - n.index : s.weight - n.weight; + }); + } + function Ji(t, e) { + const i = (function (t) { + const e = {}; + for (const i of t) { + const { stack: t, pos: s, stackWeight: n } = i; + if (!t || !qi.includes(s)) continue; + const o = + e[t] || (e[t] = { count: 0, placed: 0, weight: 0, size: 0 }); + o.count++, (o.weight += n); + } + return e; + })(t), + { vBoxMaxWidth: s, hBoxMaxHeight: n } = e; + let o, a, r; + for (o = 0, a = t.length; o < a; ++o) { + r = t[o]; + const { fullSize: a } = r.box, + l = i[r.stack], + h = l && r.stackWeight / l.weight; + r.horizontal + ? ((r.width = h ? h * s : a && e.availableWidth), (r.height = n)) + : ((r.width = s), (r.height = h ? h * n : a && e.availableHeight)); + } + return i; + } + function Qi(t, e, i, s) { + return Math.max(t[i], e[i]) + Math.max(t[s], e[s]); + } + function ts(t, e) { + (t.top = Math.max(t.top, e.top)), + (t.left = Math.max(t.left, e.left)), + (t.bottom = Math.max(t.bottom, e.bottom)), + (t.right = Math.max(t.right, e.right)); + } + function es(t, e, i, s) { + const { pos: n, box: a } = i, + r = t.maxPadding; + if (!o(n)) { + i.size && (t[n] -= i.size); + const e = s[i.stack] || { size: 0, count: 1 }; + (e.size = Math.max(e.size, i.horizontal ? a.height : a.width)), + (i.size = e.size / e.count), + (t[n] += i.size); + } + a.getPadding && ts(r, a.getPadding()); + const l = Math.max(0, e.outerWidth - Qi(r, t, "left", "right")), + h = Math.max(0, e.outerHeight - Qi(r, t, "top", "bottom")), + c = l !== t.w, + d = h !== t.h; + return ( + (t.w = l), + (t.h = h), + i.horizontal ? { same: c, other: d } : { same: d, other: c } + ); + } + function is(t, e) { + const i = e.maxPadding; + function s(t) { + const s = { left: 0, top: 0, right: 0, bottom: 0 }; + return ( + t.forEach((t) => { + s[t] = Math.max(e[t], i[t]); + }), + s + ); + } + return s(t ? ["left", "right"] : ["top", "bottom"]); + } + function ss(t, e, i, s) { + const n = []; + let o, a, r, l, h, c; + for (o = 0, a = t.length, h = 0; o < a; ++o) { + (r = t[o]), + (l = r.box), + l.update(r.width || e.w, r.height || e.h, is(r.horizontal, e)); + const { same: a, other: d } = es(e, i, r, s); + (h |= a && n.length), (c = c || d), l.fullSize || n.push(r); + } + return (h && ss(n, e, i, s)) || c; + } + function ns(t, e, i, s, n) { + (t.top = i), + (t.left = e), + (t.right = e + s), + (t.bottom = i + n), + (t.width = s), + (t.height = n); + } + function os(t, e, i, s) { + const n = i.padding; + let { x: o, y: a } = e; + for (const r of t) { + const t = r.box, + l = s[r.stack] || { count: 1, placed: 0, weight: 1 }, + h = r.stackWeight / l.weight || 1; + if (r.horizontal) { + const s = e.w * h, + o = l.size || t.height; + k(l.start) && (a = l.start), + t.fullSize + ? ns(t, n.left, a, i.outerWidth - n.right - n.left, o) + : ns(t, e.left + l.placed, a, s, o), + (l.start = a), + (l.placed += s), + (a = t.bottom); + } else { + const s = e.h * h, + a = l.size || t.width; + k(l.start) && (o = l.start), + t.fullSize + ? ns(t, o, n.top, a, i.outerHeight - n.bottom - n.top) + : ns(t, o, e.top + l.placed, a, s), + (l.start = o), + (l.placed += s), + (o = t.right); + } + } + (e.x = o), (e.y = a); + } + var as = { + addBox(t, e) { + t.boxes || (t.boxes = []), + (e.fullSize = e.fullSize || !1), + (e.position = e.position || "top"), + (e.weight = e.weight || 0), + (e._layers = + e._layers || + function () { + return [ + { + z: 0, + draw(t) { + e.draw(t); + }, + }, + ]; + }), + t.boxes.push(e); + }, + removeBox(t, e) { + const i = t.boxes ? t.boxes.indexOf(e) : -1; + -1 !== i && t.boxes.splice(i, 1); + }, + configure(t, e, i) { + (e.fullSize = i.fullSize), + (e.position = i.position), + (e.weight = i.weight); + }, + update(t, e, i, s) { + if (!t) return; + const n = ki(t.options.layout.padding), + o = Math.max(e - n.width, 0), + a = Math.max(i - n.height, 0), + r = (function (t) { + const e = (function (t) { + const e = []; + let i, s, n, o, a, r; + for (i = 0, s = (t || []).length; i < s; ++i) + (n = t[i]), + ({ + position: o, + options: { stack: a, stackWeight: r = 1 }, + } = n), + e.push({ + index: i, + box: n, + pos: o, + horizontal: n.isHorizontal(), + weight: n.weight, + stack: a && o + a, + stackWeight: r, + }); + return e; + })(t), + i = Zi( + e.filter((t) => t.box.fullSize), + !0 + ), + s = Zi(Ki(e, "left"), !0), + n = Zi(Ki(e, "right")), + o = Zi(Ki(e, "top"), !0), + a = Zi(Ki(e, "bottom")), + r = Gi(e, "x"), + l = Gi(e, "y"); + return { + fullSize: i, + leftAndTop: s.concat(o), + rightAndBottom: n.concat(l).concat(a).concat(r), + chartArea: Ki(e, "chartArea"), + vertical: s.concat(n).concat(l), + horizontal: o.concat(a).concat(r), + }; + })(t.boxes), + l = r.vertical, + h = r.horizontal; + u(t.boxes, (t) => { + "function" == typeof t.beforeLayout && t.beforeLayout(); + }); + const c = + l.reduce( + (t, e) => + e.box.options && !1 === e.box.options.display ? t : t + 1, + 0 + ) || 1, + d = Object.freeze({ + outerWidth: e, + outerHeight: i, + padding: n, + availableWidth: o, + availableHeight: a, + vBoxMaxWidth: o / 2 / c, + hBoxMaxHeight: a / 2, + }), + f = Object.assign({}, n); + ts(f, ki(s)); + const g = Object.assign( + { maxPadding: f, w: o, h: a, x: n.left, y: n.top }, + n + ), + p = Ji(l.concat(h), d); + ss(r.fullSize, g, d, p), + ss(l, g, d, p), + ss(h, g, d, p) && ss(l, g, d, p), + (function (t) { + const e = t.maxPadding; + function i(i) { + const s = Math.max(e[i] - t[i], 0); + return (t[i] += s), s; + } + (t.y += i("top")), (t.x += i("left")), i("right"), i("bottom"); + })(g), + os(r.leftAndTop, g, d, p), + (g.x += g.w), + (g.y += g.h), + os(r.rightAndBottom, g, d, p), + (t.chartArea = { + left: g.left, + top: g.top, + right: g.left + g.w, + bottom: g.top + g.h, + height: g.h, + width: g.w, + }), + u(r.chartArea, (e) => { + const i = e.box; + Object.assign(i, t.chartArea), + i.update(g.w, g.h, { left: 0, top: 0, right: 0, bottom: 0 }); + }); + }, + }; + class rs { + acquireContext(t, e) {} + releaseContext(t) { + return !1; + } + addEventListener(t, e, i) {} + removeEventListener(t, e, i) {} + getDevicePixelRatio() { + return 1; + } + getMaximumSize(t, e, i, s) { + return ( + (e = Math.max(0, e || t.width)), + (i = i || t.height), + { width: e, height: Math.max(0, s ? Math.floor(e / s) : i) } + ); + } + isAttached(t) { + return !0; + } + updateConfig(t) {} + } + class ls extends rs { + acquireContext(t) { + return (t && t.getContext && t.getContext("2d")) || null; + } + updateConfig(t) { + t.options.animation = !1; + } + } + const hs = "$chartjs", + cs = { + touchstart: "mousedown", + touchmove: "mousemove", + touchend: "mouseup", + pointerenter: "mouseenter", + pointerdown: "mousedown", + pointermove: "mousemove", + pointerup: "mouseup", + pointerleave: "mouseout", + pointerout: "mouseout", + }, + ds = (t) => null === t || "" === t; + const us = !!Se && { passive: !0 }; + function fs(t, e, i) { + t && t.canvas && t.canvas.removeEventListener(e, i, us); + } + function gs(t, e) { + for (const i of t) if (i === e || i.contains(e)) return !0; + } + function ps(t, e, i) { + const s = t.canvas, + n = new MutationObserver((t) => { + let e = !1; + for (const i of t) + (e = e || gs(i.addedNodes, s)), (e = e && !gs(i.removedNodes, s)); + e && i(); + }); + return n.observe(document, { childList: !0, subtree: !0 }), n; + } + function ms(t, e, i) { + const s = t.canvas, + n = new MutationObserver((t) => { + let e = !1; + for (const i of t) + (e = e || gs(i.removedNodes, s)), (e = e && !gs(i.addedNodes, s)); + e && i(); + }); + return n.observe(document, { childList: !0, subtree: !0 }), n; + } + const xs = new Map(); + let bs = 0; + function _s() { + const t = window.devicePixelRatio; + t !== bs && + ((bs = t), + xs.forEach((e, i) => { + i.currentDevicePixelRatio !== t && e(); + })); + } + function ys(t, e, i) { + const s = t.canvas, + n = s && ge(s); + if (!n) return; + const o = ct((t, e) => { + const s = n.clientWidth; + i(t, e), s < n.clientWidth && i(); + }, window), + a = new ResizeObserver((t) => { + const e = t[0], + i = e.contentRect.width, + s = e.contentRect.height; + (0 === i && 0 === s) || o(i, s); + }); + return ( + a.observe(n), + (function (t, e) { + xs.size || window.addEventListener("resize", _s), xs.set(t, e); + })(t, o), + a + ); + } + function vs(t, e, i) { + i && i.disconnect(), + "resize" === e && + (function (t) { + xs.delete(t), xs.size || window.removeEventListener("resize", _s); + })(t); + } + function Ms(t, e, i) { + const s = t.canvas, + n = ct((e) => { + null !== t.ctx && + i( + (function (t, e) { + const i = cs[t.type] || t.type, + { x: s, y: n } = ve(t, e); + return { + type: i, + chart: e, + native: t, + x: void 0 !== s ? s : null, + y: void 0 !== n ? n : null, + }; + })(e, t) + ); + }, t); + return ( + (function (t, e, i) { + t && t.addEventListener(e, i, us); + })(s, e, n), + n + ); + } + class ws extends rs { + acquireContext(t, e) { + const i = t && t.getContext && t.getContext("2d"); + return i && i.canvas === t + ? ((function (t, e) { + const i = t.style, + s = t.getAttribute("height"), + n = t.getAttribute("width"); + if ( + ((t[hs] = { + initial: { + height: s, + width: n, + style: { + display: i.display, + height: i.height, + width: i.width, + }, + }, + }), + (i.display = i.display || "block"), + (i.boxSizing = i.boxSizing || "border-box"), + ds(n)) + ) { + const e = Pe(t, "width"); + void 0 !== e && (t.width = e); + } + if (ds(s)) + if ("" === t.style.height) t.height = t.width / (e || 2); + else { + const e = Pe(t, "height"); + void 0 !== e && (t.height = e); + } + })(t, e), + i) + : null; + } + releaseContext(t) { + const e = t.canvas; + if (!e[hs]) return !1; + const i = e[hs].initial; + ["height", "width"].forEach((t) => { + const n = i[t]; + s(n) ? e.removeAttribute(t) : e.setAttribute(t, n); + }); + const n = i.style || {}; + return ( + Object.keys(n).forEach((t) => { + e.style[t] = n[t]; + }), + (e.width = e.width), + delete e[hs], + !0 + ); + } + addEventListener(t, e, i) { + this.removeEventListener(t, e); + const s = t.$proxies || (t.$proxies = {}), + n = { attach: ps, detach: ms, resize: ys }[e] || Ms; + s[e] = n(t, e, i); + } + removeEventListener(t, e) { + const i = t.$proxies || (t.$proxies = {}), + s = i[e]; + if (!s) return; + (({ attach: vs, detach: vs, resize: vs })[e] || fs)(t, e, s), + (i[e] = void 0); + } + getDevicePixelRatio() { + return window.devicePixelRatio; + } + getMaximumSize(t, e, i, s) { + return we(t, e, i, s); + } + isAttached(t) { + const e = t && ge(t); + return !(!e || !e.isConnected); + } + } + function ks(t) { + return !fe() || + ("undefined" != typeof OffscreenCanvas && t instanceof OffscreenCanvas) + ? ls + : ws; + } + var Ss = Object.freeze({ + __proto__: null, + BasePlatform: rs, + BasicPlatform: ls, + DomPlatform: ws, + _detectPlatform: ks, + }); + const Ps = "transparent", + Ds = { + boolean: (t, e, i) => (i > 0.5 ? e : t), + color(t, e, i) { + const s = Qt(t || Ps), + n = s.valid && Qt(e || Ps); + return n && n.valid ? n.mix(s, i).hexString() : e; + }, + number: (t, e, i) => t + (e - t) * i, + }; + class Cs { + constructor(t, e, i, s) { + const n = e[i]; + s = Pi([t.to, s, n, t.from]); + const o = Pi([t.from, n, s]); + (this._active = !0), + (this._fn = t.fn || Ds[t.type || typeof o]), + (this._easing = fi[t.easing] || fi.linear), + (this._start = Math.floor(Date.now() + (t.delay || 0))), + (this._duration = this._total = Math.floor(t.duration)), + (this._loop = !!t.loop), + (this._target = e), + (this._prop = i), + (this._from = o), + (this._to = s), + (this._promises = void 0); + } + active() { + return this._active; + } + update(t, e, i) { + if (this._active) { + this._notify(!1); + const s = this._target[this._prop], + n = i - this._start, + o = this._duration - n; + (this._start = i), + (this._duration = Math.floor(Math.max(o, t.duration))), + (this._total += n), + (this._loop = !!t.loop), + (this._to = Pi([t.to, e, s, t.from])), + (this._from = Pi([t.from, s, e])); + } + } + cancel() { + this._active && + (this.tick(Date.now()), (this._active = !1), this._notify(!1)); + } + tick(t) { + const e = t - this._start, + i = this._duration, + s = this._prop, + n = this._from, + o = this._loop, + a = this._to; + let r; + if (((this._active = n !== a && (o || e < i)), !this._active)) + return (this._target[s] = a), void this._notify(!0); + e < 0 + ? (this._target[s] = n) + : ((r = (e / i) % 2), + (r = o && r > 1 ? 2 - r : r), + (r = this._easing(Math.min(1, Math.max(0, r)))), + (this._target[s] = this._fn(n, a, r))); + } + wait() { + const t = this._promises || (this._promises = []); + return new Promise((e, i) => { + t.push({ res: e, rej: i }); + }); + } + _notify(t) { + const e = t ? "res" : "rej", + i = this._promises || []; + for (let t = 0; t < i.length; t++) i[t][e](); + } + } + class Os { + constructor(t, e) { + (this._chart = t), (this._properties = new Map()), this.configure(e); + } + configure(t) { + if (!o(t)) return; + const e = Object.keys(ue.animation), + i = this._properties; + Object.getOwnPropertyNames(t).forEach((s) => { + const a = t[s]; + if (!o(a)) return; + const r = {}; + for (const t of e) r[t] = a[t]; + ((n(a.properties) && a.properties) || [s]).forEach((t) => { + (t !== s && i.has(t)) || i.set(t, r); + }); + }); + } + _animateOptions(t, e) { + const i = e.options, + s = (function (t, e) { + if (!e) return; + let i = t.options; + if (!i) return void (t.options = e); + i.$shared && + (t.options = i = + Object.assign({}, i, { $shared: !1, $animations: {} })); + return i; + })(t, i); + if (!s) return []; + const n = this._createAnimations(s, i); + return ( + i.$shared && + (function (t, e) { + const i = [], + s = Object.keys(e); + for (let e = 0; e < s.length; e++) { + const n = t[s[e]]; + n && n.active() && i.push(n.wait()); + } + return Promise.all(i); + })(t.options.$animations, i).then( + () => { + t.options = i; + }, + () => {} + ), + n + ); + } + _createAnimations(t, e) { + const i = this._properties, + s = [], + n = t.$animations || (t.$animations = {}), + o = Object.keys(e), + a = Date.now(); + let r; + for (r = o.length - 1; r >= 0; --r) { + const l = o[r]; + if ("$" === l.charAt(0)) continue; + if ("options" === l) { + s.push(...this._animateOptions(t, e)); + continue; + } + const h = e[l]; + let c = n[l]; + const d = i.get(l); + if (c) { + if (d && c.active()) { + c.update(d, h, a); + continue; + } + c.cancel(); + } + d && d.duration + ? ((n[l] = c = new Cs(d, t, l, h)), s.push(c)) + : (t[l] = h); + } + return s; + } + update(t, e) { + if (0 === this._properties.size) return void Object.assign(t, e); + const i = this._createAnimations(t, e); + return i.length ? (bt.add(this._chart, i), !0) : void 0; + } + } + function As(t, e) { + const i = (t && t.options) || {}, + s = i.reverse, + n = void 0 === i.min ? e : 0, + o = void 0 === i.max ? e : 0; + return { start: s ? o : n, end: s ? n : o }; + } + function Ts(t, e) { + const i = [], + s = t._getSortedDatasetMetas(e); + let n, o; + for (n = 0, o = s.length; n < o; ++n) i.push(s[n].index); + return i; + } + function Ls(t, e, i, s = {}) { + const n = t.keys, + o = "single" === s.mode; + let r, l, h, c; + if (null !== e) { + for (r = 0, l = n.length; r < l; ++r) { + if (((h = +n[r]), h === i)) { + if (s.all) continue; + break; + } + (c = t.values[h]), a(c) && (o || 0 === e || F(e) === F(c)) && (e += c); + } + return e; + } + } + function Es(t, e) { + const i = t && t.options.stacked; + return i || (void 0 === i && void 0 !== e.stack); + } + function Rs(t, e, i) { + const s = t[e] || (t[e] = {}); + return s[i] || (s[i] = {}); + } + function Is(t, e, i, s) { + for (const n of e.getMatchingVisibleMetas(s).reverse()) { + const e = t[n.index]; + if ((i && e > 0) || (!i && e < 0)) return n.index; + } + return null; + } + function zs(t, e) { + const { chart: i, _cachedMeta: s } = t, + n = i._stacks || (i._stacks = {}), + { iScale: o, vScale: a, index: r } = s, + l = o.axis, + h = a.axis, + c = (function (t, e, i) { + return `${t.id}.${e.id}.${i.stack || i.type}`; + })(o, a, s), + d = e.length; + let u; + for (let t = 0; t < d; ++t) { + const i = e[t], + { [l]: o, [h]: d } = i; + (u = (i._stacks || (i._stacks = {}))[h] = Rs(n, c, o)), + (u[r] = d), + (u._top = Is(u, a, !0, s.type)), + (u._bottom = Is(u, a, !1, s.type)); + (u._visualValues || (u._visualValues = {}))[r] = d; + } + } + function Fs(t, e) { + const i = t.scales; + return Object.keys(i) + .filter((t) => i[t].axis === e) + .shift(); + } + function Vs(t, e) { + const i = t.controller.index, + s = t.vScale && t.vScale.axis; + if (s) { + e = e || t._parsed; + for (const t of e) { + const e = t._stacks; + if (!e || void 0 === e[s] || void 0 === e[s][i]) return; + delete e[s][i], + void 0 !== e[s]._visualValues && + void 0 !== e[s]._visualValues[i] && + delete e[s]._visualValues[i]; + } + } + } + const Bs = (t) => "reset" === t || "none" === t, + Ws = (t, e) => (e ? t : Object.assign({}, t)); + class Ns { + static defaults = {}; + static datasetElementType = null; + static dataElementType = null; + constructor(t, e) { + (this.chart = t), + (this._ctx = t.ctx), + (this.index = e), + (this._cachedDataOpts = {}), + (this._cachedMeta = this.getMeta()), + (this._type = this._cachedMeta.type), + (this.options = void 0), + (this._parsing = !1), + (this._data = void 0), + (this._objectData = void 0), + (this._sharedOptions = void 0), + (this._drawStart = void 0), + (this._drawCount = void 0), + (this.enableOptionSharing = !1), + (this.supportsDecimation = !1), + (this.$context = void 0), + (this._syncList = []), + (this.datasetElementType = new.target.datasetElementType), + (this.dataElementType = new.target.dataElementType), + this.initialize(); + } + initialize() { + const t = this._cachedMeta; + this.configure(), + this.linkScales(), + (t._stacked = Es(t.vScale, t)), + this.addElements(), + this.options.fill && + !this.chart.isPluginEnabled("filler") && + console.warn( + "Tried to use the 'fill' option without the 'Filler' plugin enabled. Please import and register the 'Filler' plugin and make sure it is not disabled in the options" + ); + } + updateIndex(t) { + this.index !== t && Vs(this._cachedMeta), (this.index = t); + } + linkScales() { + const t = this.chart, + e = this._cachedMeta, + i = this.getDataset(), + s = (t, e, i, s) => ("x" === t ? e : "r" === t ? s : i), + n = (e.xAxisID = l(i.xAxisID, Fs(t, "x"))), + o = (e.yAxisID = l(i.yAxisID, Fs(t, "y"))), + a = (e.rAxisID = l(i.rAxisID, Fs(t, "r"))), + r = e.indexAxis, + h = (e.iAxisID = s(r, n, o, a)), + c = (e.vAxisID = s(r, o, n, a)); + (e.xScale = this.getScaleForId(n)), + (e.yScale = this.getScaleForId(o)), + (e.rScale = this.getScaleForId(a)), + (e.iScale = this.getScaleForId(h)), + (e.vScale = this.getScaleForId(c)); + } + getDataset() { + return this.chart.data.datasets[this.index]; + } + getMeta() { + return this.chart.getDatasetMeta(this.index); + } + getScaleForId(t) { + return this.chart.scales[t]; + } + _getOtherScale(t) { + const e = this._cachedMeta; + return t === e.iScale ? e.vScale : e.iScale; + } + reset() { + this._update("reset"); + } + _destroy() { + const t = this._cachedMeta; + this._data && rt(this._data, this), t._stacked && Vs(t); + } + _dataCheck() { + const t = this.getDataset(), + e = t.data || (t.data = []), + i = this._data; + if (o(e)) { + const t = this._cachedMeta; + this._data = (function (t, e) { + const { iScale: i, vScale: s } = e, + n = "x" === i.axis ? "x" : "y", + o = "x" === s.axis ? "x" : "y", + a = Object.keys(t), + r = new Array(a.length); + let l, h, c; + for (l = 0, h = a.length; l < h; ++l) + (c = a[l]), (r[l] = { [n]: c, [o]: t[c] }); + return r; + })(e, t); + } else if (i !== e) { + if (i) { + rt(i, this); + const t = this._cachedMeta; + Vs(t), (t._parsed = []); + } + e && Object.isExtensible(e) && at(e, this), + (this._syncList = []), + (this._data = e); + } + } + addElements() { + const t = this._cachedMeta; + this._dataCheck(), + this.datasetElementType && (t.dataset = new this.datasetElementType()); + } + buildOrUpdateElements(t) { + const e = this._cachedMeta, + i = this.getDataset(); + let s = !1; + this._dataCheck(); + const n = e._stacked; + (e._stacked = Es(e.vScale, e)), + e.stack !== i.stack && ((s = !0), Vs(e), (e.stack = i.stack)), + this._resyncElements(t), + (s || n !== e._stacked) && zs(this, e._parsed); + } + configure() { + const t = this.chart.config, + e = t.datasetScopeKeys(this._type), + i = t.getOptionScopes(this.getDataset(), e, !0); + (this.options = t.createResolver(i, this.getContext())), + (this._parsing = this.options.parsing), + (this._cachedDataOpts = {}); + } + parse(t, e) { + const { _cachedMeta: i, _data: s } = this, + { iScale: a, _stacked: r } = i, + l = a.axis; + let h, + c, + d, + u = (0 === t && e === s.length) || i._sorted, + f = t > 0 && i._parsed[t - 1]; + if (!1 === this._parsing) (i._parsed = s), (i._sorted = !0), (d = s); + else { + d = n(s[t]) + ? this.parseArrayData(i, s, t, e) + : o(s[t]) + ? this.parseObjectData(i, s, t, e) + : this.parsePrimitiveData(i, s, t, e); + const a = () => null === c[l] || (f && c[l] < f[l]); + for (h = 0; h < e; ++h) + (i._parsed[h + t] = c = d[h]), u && (a() && (u = !1), (f = c)); + i._sorted = u; + } + r && zs(this, d); + } + parsePrimitiveData(t, e, i, s) { + const { iScale: n, vScale: o } = t, + a = n.axis, + r = o.axis, + l = n.getLabels(), + h = n === o, + c = new Array(s); + let d, u, f; + for (d = 0, u = s; d < u; ++d) + (f = d + i), + (c[d] = { [a]: h || n.parse(l[f], f), [r]: o.parse(e[f], f) }); + return c; + } + parseArrayData(t, e, i, s) { + const { xScale: n, yScale: o } = t, + a = new Array(s); + let r, l, h, c; + for (r = 0, l = s; r < l; ++r) + (h = r + i), + (c = e[h]), + (a[r] = { x: n.parse(c[0], h), y: o.parse(c[1], h) }); + return a; + } + parseObjectData(t, e, i, s) { + const { xScale: n, yScale: o } = t, + { xAxisKey: a = "x", yAxisKey: r = "y" } = this._parsing, + l = new Array(s); + let h, c, d, u; + for (h = 0, c = s; h < c; ++h) + (d = h + i), + (u = e[d]), + (l[h] = { x: n.parse(M(u, a), d), y: o.parse(M(u, r), d) }); + return l; + } + getParsed(t) { + return this._cachedMeta._parsed[t]; + } + getDataElement(t) { + return this._cachedMeta.data[t]; + } + applyStack(t, e, i) { + const s = this.chart, + n = this._cachedMeta, + o = e[t.axis]; + return Ls( + { keys: Ts(s, !0), values: e._stacks[t.axis]._visualValues }, + o, + n.index, + { mode: i } + ); + } + updateRangeFromParsed(t, e, i, s) { + const n = i[e.axis]; + let o = null === n ? NaN : n; + const a = s && i._stacks[e.axis]; + s && a && ((s.values = a), (o = Ls(s, n, this._cachedMeta.index))), + (t.min = Math.min(t.min, o)), + (t.max = Math.max(t.max, o)); + } + getMinMax(t, e) { + const i = this._cachedMeta, + s = i._parsed, + n = i._sorted && t === i.iScale, + o = s.length, + r = this._getOtherScale(t), + l = ((t, e, i) => + t && !e.hidden && e._stacked && { keys: Ts(i, !0), values: null })( + e, + i, + this.chart + ), + h = { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY }, + { min: c, max: d } = (function (t) { + const { + min: e, + max: i, + minDefined: s, + maxDefined: n, + } = t.getUserBounds(); + return { + min: s ? e : Number.NEGATIVE_INFINITY, + max: n ? i : Number.POSITIVE_INFINITY, + }; + })(r); + let u, f; + function g() { + f = s[u]; + const e = f[r.axis]; + return !a(f[t.axis]) || c > e || d < e; + } + for ( + u = 0; + u < o && (g() || (this.updateRangeFromParsed(h, t, f, l), !n)); + ++u + ); + if (n) + for (u = o - 1; u >= 0; --u) + if (!g()) { + this.updateRangeFromParsed(h, t, f, l); + break; + } + return h; + } + getAllParsedValues(t) { + const e = this._cachedMeta._parsed, + i = []; + let s, n, o; + for (s = 0, n = e.length; s < n; ++s) + (o = e[s][t.axis]), a(o) && i.push(o); + return i; + } + getMaxOverflow() { + return !1; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = e.iScale, + s = e.vScale, + n = this.getParsed(t); + return { + label: i ? "" + i.getLabelForValue(n[i.axis]) : "", + value: s ? "" + s.getLabelForValue(n[s.axis]) : "", + }; + } + _update(t) { + const e = this._cachedMeta; + this.update(t || "default"), + (e._clip = (function (t) { + let e, i, s, n; + return ( + o(t) + ? ((e = t.top), (i = t.right), (s = t.bottom), (n = t.left)) + : (e = i = s = n = t), + { top: e, right: i, bottom: s, left: n, disabled: !1 === t } + ); + })( + l( + this.options.clip, + (function (t, e, i) { + if (!1 === i) return !1; + const s = As(t, i), + n = As(e, i); + return { + top: n.end, + right: s.end, + bottom: n.start, + left: s.start, + }; + })(e.xScale, e.yScale, this.getMaxOverflow()) + ) + )); + } + update(t) {} + draw() { + const t = this._ctx, + e = this.chart, + i = this._cachedMeta, + s = i.data || [], + n = e.chartArea, + o = [], + a = this._drawStart || 0, + r = this._drawCount || s.length - a, + l = this.options.drawActiveElementsOnTop; + let h; + for (i.dataset && i.dataset.draw(t, n, a, r), h = a; h < a + r; ++h) { + const e = s[h]; + e.hidden || (e.active && l ? o.push(e) : e.draw(t, n)); + } + for (h = 0; h < o.length; ++h) o[h].draw(t, n); + } + getStyle(t, e) { + const i = e ? "active" : "default"; + return void 0 === t && this._cachedMeta.dataset + ? this.resolveDatasetElementOptions(i) + : this.resolveDataElementOptions(t || 0, i); + } + getContext(t, e, i) { + const s = this.getDataset(); + let n; + if (t >= 0 && t < this._cachedMeta.data.length) { + const e = this._cachedMeta.data[t]; + (n = + e.$context || + (e.$context = (function (t, e, i) { + return Ci(t, { + active: !1, + dataIndex: e, + parsed: void 0, + raw: void 0, + element: i, + index: e, + mode: "default", + type: "data", + }); + })(this.getContext(), t, e))), + (n.parsed = this.getParsed(t)), + (n.raw = s.data[t]), + (n.index = n.dataIndex = t); + } else + (n = + this.$context || + (this.$context = (function (t, e) { + return Ci(t, { + active: !1, + dataset: void 0, + datasetIndex: e, + index: e, + mode: "default", + type: "dataset", + }); + })(this.chart.getContext(), this.index))), + (n.dataset = s), + (n.index = n.datasetIndex = this.index); + return (n.active = !!e), (n.mode = i), n; + } + resolveDatasetElementOptions(t) { + return this._resolveElementOptions(this.datasetElementType.id, t); + } + resolveDataElementOptions(t, e) { + return this._resolveElementOptions(this.dataElementType.id, e, t); + } + _resolveElementOptions(t, e = "default", i) { + const s = "active" === e, + n = this._cachedDataOpts, + o = t + "-" + e, + a = n[o], + r = this.enableOptionSharing && k(i); + if (a) return Ws(a, r); + const l = this.chart.config, + h = l.datasetElementScopeKeys(this._type, t), + c = s ? [`${t}Hover`, "hover", t, ""] : [t, ""], + d = l.getOptionScopes(this.getDataset(), h), + u = Object.keys(ue.elements[t]), + f = l.resolveNamedOptions(d, u, () => this.getContext(i, s, e), c); + return ( + f.$shared && ((f.$shared = r), (n[o] = Object.freeze(Ws(f, r)))), f + ); + } + _resolveAnimations(t, e, i) { + const s = this.chart, + n = this._cachedDataOpts, + o = `animation-${e}`, + a = n[o]; + if (a) return a; + let r; + if (!1 !== s.options.animation) { + const s = this.chart.config, + n = s.datasetAnimationScopeKeys(this._type, e), + o = s.getOptionScopes(this.getDataset(), n); + r = s.createResolver(o, this.getContext(t, i, e)); + } + const l = new Os(s, r && r.animations); + return r && r._cacheable && (n[o] = Object.freeze(l)), l; + } + getSharedOptions(t) { + if (t.$shared) + return ( + this._sharedOptions || (this._sharedOptions = Object.assign({}, t)) + ); + } + includeOptions(t, e) { + return !e || Bs(t) || this.chart._animationsDisabled; + } + _getSharedOptions(t, e) { + const i = this.resolveDataElementOptions(t, e), + s = this._sharedOptions, + n = this.getSharedOptions(i), + o = this.includeOptions(e, n) || n !== s; + return ( + this.updateSharedOptions(n, e, i), + { sharedOptions: n, includeOptions: o } + ); + } + updateElement(t, e, i, s) { + Bs(s) ? Object.assign(t, i) : this._resolveAnimations(e, s).update(t, i); + } + updateSharedOptions(t, e, i) { + t && !Bs(e) && this._resolveAnimations(void 0, e).update(t, i); + } + _setStyle(t, e, i, s) { + t.active = s; + const n = this.getStyle(e, s); + this._resolveAnimations(e, i, s).update(t, { + options: (!s && this.getSharedOptions(n)) || n, + }); + } + removeHoverStyle(t, e, i) { + this._setStyle(t, i, "active", !1); + } + setHoverStyle(t, e, i) { + this._setStyle(t, i, "active", !0); + } + _removeDatasetHoverStyle() { + const t = this._cachedMeta.dataset; + t && this._setStyle(t, void 0, "active", !1); + } + _setDatasetHoverStyle() { + const t = this._cachedMeta.dataset; + t && this._setStyle(t, void 0, "active", !0); + } + _resyncElements(t) { + const e = this._data, + i = this._cachedMeta.data; + for (const [t, e, i] of this._syncList) this[t](e, i); + this._syncList = []; + const s = i.length, + n = e.length, + o = Math.min(n, s); + o && this.parse(0, o), + n > s + ? this._insertElements(s, n - s, t) + : n < s && this._removeElements(n, s - n); + } + _insertElements(t, e, i = !0) { + const s = this._cachedMeta, + n = s.data, + o = t + e; + let a; + const r = (t) => { + for (t.length += e, a = t.length - 1; a >= o; a--) t[a] = t[a - e]; + }; + for (r(n), a = t; a < o; ++a) n[a] = new this.dataElementType(); + this._parsing && r(s._parsed), + this.parse(t, e), + i && this.updateElements(n, t, e, "reset"); + } + updateElements(t, e, i, s) {} + _removeElements(t, e) { + const i = this._cachedMeta; + if (this._parsing) { + const s = i._parsed.splice(t, e); + i._stacked && Vs(i, s); + } + i.data.splice(t, e); + } + _sync(t) { + if (this._parsing) this._syncList.push(t); + else { + const [e, i, s] = t; + this[e](i, s); + } + this.chart._dataChanges.push([this.index, ...t]); + } + _onDataPush() { + const t = arguments.length; + this._sync(["_insertElements", this.getDataset().data.length - t, t]); + } + _onDataPop() { + this._sync(["_removeElements", this._cachedMeta.data.length - 1, 1]); + } + _onDataShift() { + this._sync(["_removeElements", 0, 1]); + } + _onDataSplice(t, e) { + e && this._sync(["_removeElements", t, e]); + const i = arguments.length - 2; + i && this._sync(["_insertElements", t, i]); + } + _onDataUnshift() { + this._sync(["_insertElements", 0, arguments.length]); + } + } + class Hs { + static defaults = {}; + static defaultRoutes = void 0; + x; + y; + active = !1; + options; + $animations; + tooltipPosition(t) { + const { x: e, y: i } = this.getProps(["x", "y"], t); + return { x: e, y: i }; + } + hasValue() { + return N(this.x) && N(this.y); + } + getProps(t, e) { + const i = this.$animations; + if (!e || !i) return this; + const s = {}; + return ( + t.forEach((t) => { + s[t] = i[t] && i[t].active() ? i[t]._to : this[t]; + }), + s + ); + } + } + function js(t, e) { + const i = t.options.ticks, + n = (function (t) { + const e = t.options.offset, + i = t._tickSize(), + s = t._length / i + (e ? 0 : 1), + n = t._maxLength / i; + return Math.floor(Math.min(s, n)); + })(t), + o = Math.min(i.maxTicksLimit || n, n), + a = i.major.enabled + ? (function (t) { + const e = []; + let i, s; + for (i = 0, s = t.length; i < s; i++) t[i].major && e.push(i); + return e; + })(e) + : [], + r = a.length, + l = a[0], + h = a[r - 1], + c = []; + if (r > o) + return ( + (function (t, e, i, s) { + let n, + o = 0, + a = i[0]; + for (s = Math.ceil(s), n = 0; n < t.length; n++) + n === a && (e.push(t[n]), o++, (a = i[o * s])); + })(e, c, a, r / o), + c + ); + const d = (function (t, e, i) { + const s = (function (t) { + const e = t.length; + let i, s; + if (e < 2) return !1; + for (s = t[0], i = 1; i < e; ++i) + if (t[i] - t[i - 1] !== s) return !1; + return s; + })(t), + n = e.length / i; + if (!s) return Math.max(n, 1); + const o = W(s); + for (let t = 0, e = o.length - 1; t < e; t++) { + const e = o[t]; + if (e > n) return e; + } + return Math.max(n, 1); + })(a, e, o); + if (r > 0) { + let t, i; + const n = r > 1 ? Math.round((h - l) / (r - 1)) : null; + for ($s(e, c, d, s(n) ? 0 : l - n, l), t = 0, i = r - 1; t < i; t++) + $s(e, c, d, a[t], a[t + 1]); + return $s(e, c, d, h, s(n) ? e.length : h + n), c; + } + return $s(e, c, d), c; + } + function $s(t, e, i, s, n) { + const o = l(s, 0), + a = Math.min(l(n, t.length), t.length); + let r, + h, + c, + d = 0; + for ( + i = Math.ceil(i), n && ((r = n - s), (i = r / Math.floor(r / i))), c = o; + c < 0; + + ) + d++, (c = Math.round(o + d * i)); + for (h = Math.max(o, 0); h < a; h++) + h === c && (e.push(t[h]), d++, (c = Math.round(o + d * i))); + } + const Ys = (t, e, i) => ("top" === e || "left" === e ? t[e] + i : t[e] - i), + Us = (t, e) => Math.min(e || t, t); + function Xs(t, e) { + const i = [], + s = t.length / e, + n = t.length; + let o = 0; + for (; o < n; o += s) i.push(t[Math.floor(o)]); + return i; + } + function qs(t, e, i) { + const s = t.ticks.length, + n = Math.min(e, s - 1), + o = t._startPixel, + a = t._endPixel, + r = 1e-6; + let l, + h = t.getPixelForTick(n); + if ( + !( + i && + ((l = + 1 === s + ? Math.max(h - o, a - h) + : 0 === e + ? (t.getPixelForTick(1) - h) / 2 + : (h - t.getPixelForTick(n - 1)) / 2), + (h += n < e ? l : -l), + h < o - r || h > a + r) + ) + ) + return h; + } + function Ks(t) { + return t.drawTicks ? t.tickLength : 0; + } + function Gs(t, e) { + if (!t.display) return 0; + const i = Si(t.font, e), + s = ki(t.padding); + return (n(t.text) ? t.text.length : 1) * i.lineHeight + s.height; + } + function Zs(t, e, i) { + let s = ut(t); + return ( + ((i && "right" !== e) || (!i && "right" === e)) && + (s = ((t) => ("left" === t ? "right" : "right" === t ? "left" : t))(s)), + s + ); + } + class Js extends Hs { + constructor(t) { + super(), + (this.id = t.id), + (this.type = t.type), + (this.options = void 0), + (this.ctx = t.ctx), + (this.chart = t.chart), + (this.top = void 0), + (this.bottom = void 0), + (this.left = void 0), + (this.right = void 0), + (this.width = void 0), + (this.height = void 0), + (this._margins = { left: 0, right: 0, top: 0, bottom: 0 }), + (this.maxWidth = void 0), + (this.maxHeight = void 0), + (this.paddingTop = void 0), + (this.paddingBottom = void 0), + (this.paddingLeft = void 0), + (this.paddingRight = void 0), + (this.axis = void 0), + (this.labelRotation = void 0), + (this.min = void 0), + (this.max = void 0), + (this._range = void 0), + (this.ticks = []), + (this._gridLineItems = null), + (this._labelItems = null), + (this._labelSizes = null), + (this._length = 0), + (this._maxLength = 0), + (this._longestTextCache = {}), + (this._startPixel = void 0), + (this._endPixel = void 0), + (this._reversePixels = !1), + (this._userMax = void 0), + (this._userMin = void 0), + (this._suggestedMax = void 0), + (this._suggestedMin = void 0), + (this._ticksLength = 0), + (this._borderValue = 0), + (this._cache = {}), + (this._dataLimitsCached = !1), + (this.$context = void 0); + } + init(t) { + (this.options = t.setContext(this.getContext())), + (this.axis = t.axis), + (this._userMin = this.parse(t.min)), + (this._userMax = this.parse(t.max)), + (this._suggestedMin = this.parse(t.suggestedMin)), + (this._suggestedMax = this.parse(t.suggestedMax)); + } + parse(t, e) { + return t; + } + getUserBounds() { + let { + _userMin: t, + _userMax: e, + _suggestedMin: i, + _suggestedMax: s, + } = this; + return ( + (t = r(t, Number.POSITIVE_INFINITY)), + (e = r(e, Number.NEGATIVE_INFINITY)), + (i = r(i, Number.POSITIVE_INFINITY)), + (s = r(s, Number.NEGATIVE_INFINITY)), + { min: r(t, i), max: r(e, s), minDefined: a(t), maxDefined: a(e) } + ); + } + getMinMax(t) { + let e, + { min: i, max: s, minDefined: n, maxDefined: o } = this.getUserBounds(); + if (n && o) return { min: i, max: s }; + const a = this.getMatchingVisibleMetas(); + for (let r = 0, l = a.length; r < l; ++r) + (e = a[r].controller.getMinMax(this, t)), + n || (i = Math.min(i, e.min)), + o || (s = Math.max(s, e.max)); + return ( + (i = o && i > s ? s : i), + (s = n && i > s ? i : s), + { min: r(i, r(s, i)), max: r(s, r(i, s)) } + ); + } + getPadding() { + return { + left: this.paddingLeft || 0, + top: this.paddingTop || 0, + right: this.paddingRight || 0, + bottom: this.paddingBottom || 0, + }; + } + getTicks() { + return this.ticks; + } + getLabels() { + const t = this.chart.data; + return ( + this.options.labels || + (this.isHorizontal() ? t.xLabels : t.yLabels) || + t.labels || + [] + ); + } + getLabelItems(t = this.chart.chartArea) { + return ( + this._labelItems || (this._labelItems = this._computeLabelItems(t)) + ); + } + beforeLayout() { + (this._cache = {}), (this._dataLimitsCached = !1); + } + beforeUpdate() { + d(this.options.beforeUpdate, [this]); + } + update(t, e, i) { + const { beginAtZero: s, grace: n, ticks: o } = this.options, + a = o.sampleSize; + this.beforeUpdate(), + (this.maxWidth = t), + (this.maxHeight = e), + (this._margins = i = + Object.assign({ left: 0, right: 0, top: 0, bottom: 0 }, i)), + (this.ticks = null), + (this._labelSizes = null), + (this._gridLineItems = null), + (this._labelItems = null), + this.beforeSetDimensions(), + this.setDimensions(), + this.afterSetDimensions(), + (this._maxLength = this.isHorizontal() + ? this.width + i.left + i.right + : this.height + i.top + i.bottom), + this._dataLimitsCached || + (this.beforeDataLimits(), + this.determineDataLimits(), + this.afterDataLimits(), + (this._range = Di(this, n, s)), + (this._dataLimitsCached = !0)), + this.beforeBuildTicks(), + (this.ticks = this.buildTicks() || []), + this.afterBuildTicks(); + const r = a < this.ticks.length; + this._convertTicksToLabels(r ? Xs(this.ticks, a) : this.ticks), + this.configure(), + this.beforeCalculateLabelRotation(), + this.calculateLabelRotation(), + this.afterCalculateLabelRotation(), + o.display && + (o.autoSkip || "auto" === o.source) && + ((this.ticks = js(this, this.ticks)), + (this._labelSizes = null), + this.afterAutoSkip()), + r && this._convertTicksToLabels(this.ticks), + this.beforeFit(), + this.fit(), + this.afterFit(), + this.afterUpdate(); + } + configure() { + let t, + e, + i = this.options.reverse; + this.isHorizontal() + ? ((t = this.left), (e = this.right)) + : ((t = this.top), (e = this.bottom), (i = !i)), + (this._startPixel = t), + (this._endPixel = e), + (this._reversePixels = i), + (this._length = e - t), + (this._alignToPixels = this.options.alignToPixels); + } + afterUpdate() { + d(this.options.afterUpdate, [this]); + } + beforeSetDimensions() { + d(this.options.beforeSetDimensions, [this]); + } + setDimensions() { + this.isHorizontal() + ? ((this.width = this.maxWidth), + (this.left = 0), + (this.right = this.width)) + : ((this.height = this.maxHeight), + (this.top = 0), + (this.bottom = this.height)), + (this.paddingLeft = 0), + (this.paddingTop = 0), + (this.paddingRight = 0), + (this.paddingBottom = 0); + } + afterSetDimensions() { + d(this.options.afterSetDimensions, [this]); + } + _callHooks(t) { + this.chart.notifyPlugins(t, this.getContext()), + d(this.options[t], [this]); + } + beforeDataLimits() { + this._callHooks("beforeDataLimits"); + } + determineDataLimits() {} + afterDataLimits() { + this._callHooks("afterDataLimits"); + } + beforeBuildTicks() { + this._callHooks("beforeBuildTicks"); + } + buildTicks() { + return []; + } + afterBuildTicks() { + this._callHooks("afterBuildTicks"); + } + beforeTickToLabelConversion() { + d(this.options.beforeTickToLabelConversion, [this]); + } + generateTickLabels(t) { + const e = this.options.ticks; + let i, s, n; + for (i = 0, s = t.length; i < s; i++) + (n = t[i]), (n.label = d(e.callback, [n.value, i, t], this)); + } + afterTickToLabelConversion() { + d(this.options.afterTickToLabelConversion, [this]); + } + beforeCalculateLabelRotation() { + d(this.options.beforeCalculateLabelRotation, [this]); + } + calculateLabelRotation() { + const t = this.options, + e = t.ticks, + i = Us(this.ticks.length, t.ticks.maxTicksLimit), + s = e.minRotation || 0, + n = e.maxRotation; + let o, + a, + r, + l = s; + if ( + !this._isVisible() || + !e.display || + s >= n || + i <= 1 || + !this.isHorizontal() + ) + return void (this.labelRotation = s); + const h = this._getLabelSizes(), + c = h.widest.width, + d = h.highest.height, + u = J(this.chart.width - c, 0, this.maxWidth); + (o = t.offset ? this.maxWidth / i : u / (i - 1)), + c + 6 > o && + ((o = u / (i - (t.offset ? 0.5 : 1))), + (a = + this.maxHeight - + Ks(t.grid) - + e.padding - + Gs(t.title, this.chart.options.font)), + (r = Math.sqrt(c * c + d * d)), + (l = Y( + Math.min( + Math.asin(J((h.highest.height + 6) / o, -1, 1)), + Math.asin(J(a / r, -1, 1)) - Math.asin(J(d / r, -1, 1)) + ) + )), + (l = Math.max(s, Math.min(n, l)))), + (this.labelRotation = l); + } + afterCalculateLabelRotation() { + d(this.options.afterCalculateLabelRotation, [this]); + } + afterAutoSkip() {} + beforeFit() { + d(this.options.beforeFit, [this]); + } + fit() { + const t = { width: 0, height: 0 }, + { + chart: e, + options: { ticks: i, title: s, grid: n }, + } = this, + o = this._isVisible(), + a = this.isHorizontal(); + if (o) { + const o = Gs(s, e.options.font); + if ( + (a + ? ((t.width = this.maxWidth), (t.height = Ks(n) + o)) + : ((t.height = this.maxHeight), (t.width = Ks(n) + o)), + i.display && this.ticks.length) + ) { + const { + first: e, + last: s, + widest: n, + highest: o, + } = this._getLabelSizes(), + r = 2 * i.padding, + l = $(this.labelRotation), + h = Math.cos(l), + c = Math.sin(l); + if (a) { + const e = i.mirror ? 0 : c * n.width + h * o.height; + t.height = Math.min(this.maxHeight, t.height + e + r); + } else { + const e = i.mirror ? 0 : h * n.width + c * o.height; + t.width = Math.min(this.maxWidth, t.width + e + r); + } + this._calculatePadding(e, s, c, h); + } + } + this._handleMargins(), + a + ? ((this.width = this._length = + e.width - this._margins.left - this._margins.right), + (this.height = t.height)) + : ((this.width = t.width), + (this.height = this._length = + e.height - this._margins.top - this._margins.bottom)); + } + _calculatePadding(t, e, i, s) { + const { + ticks: { align: n, padding: o }, + position: a, + } = this.options, + r = 0 !== this.labelRotation, + l = "top" !== a && "x" === this.axis; + if (this.isHorizontal()) { + const a = this.getPixelForTick(0) - this.left, + h = this.right - this.getPixelForTick(this.ticks.length - 1); + let c = 0, + d = 0; + r + ? l + ? ((c = s * t.width), (d = i * e.height)) + : ((c = i * t.height), (d = s * e.width)) + : "start" === n + ? (d = e.width) + : "end" === n + ? (c = t.width) + : "inner" !== n && ((c = t.width / 2), (d = e.width / 2)), + (this.paddingLeft = Math.max( + ((c - a + o) * this.width) / (this.width - a), + 0 + )), + (this.paddingRight = Math.max( + ((d - h + o) * this.width) / (this.width - h), + 0 + )); + } else { + let i = e.height / 2, + s = t.height / 2; + "start" === n + ? ((i = 0), (s = t.height)) + : "end" === n && ((i = e.height), (s = 0)), + (this.paddingTop = i + o), + (this.paddingBottom = s + o); + } + } + _handleMargins() { + this._margins && + ((this._margins.left = Math.max(this.paddingLeft, this._margins.left)), + (this._margins.top = Math.max(this.paddingTop, this._margins.top)), + (this._margins.right = Math.max( + this.paddingRight, + this._margins.right + )), + (this._margins.bottom = Math.max( + this.paddingBottom, + this._margins.bottom + ))); + } + afterFit() { + d(this.options.afterFit, [this]); + } + isHorizontal() { + const { axis: t, position: e } = this.options; + return "top" === e || "bottom" === e || "x" === t; + } + isFullSize() { + return this.options.fullSize; + } + _convertTicksToLabels(t) { + let e, i; + for ( + this.beforeTickToLabelConversion(), + this.generateTickLabels(t), + e = 0, + i = t.length; + e < i; + e++ + ) + s(t[e].label) && (t.splice(e, 1), i--, e--); + this.afterTickToLabelConversion(); + } + _getLabelSizes() { + let t = this._labelSizes; + if (!t) { + const e = this.options.ticks.sampleSize; + let i = this.ticks; + e < i.length && (i = Xs(i, e)), + (this._labelSizes = t = + this._computeLabelSizes( + i, + i.length, + this.options.ticks.maxTicksLimit + )); + } + return t; + } + _computeLabelSizes(t, e, i) { + const { ctx: o, _longestTextCache: a } = this, + r = [], + l = [], + h = Math.floor(e / Us(e, i)); + let c, + d, + f, + g, + p, + m, + x, + b, + _, + y, + v, + M = 0, + w = 0; + for (c = 0; c < e; c += h) { + if ( + ((g = t[c].label), + (p = this._resolveTickFontOptions(c)), + (o.font = m = p.string), + (x = a[m] = a[m] || { data: {}, gc: [] }), + (b = p.lineHeight), + (_ = y = 0), + s(g) || n(g)) + ) { + if (n(g)) + for (d = 0, f = g.length; d < f; ++d) + (v = g[d]), + s(v) || n(v) || ((_ = Ce(o, x.data, x.gc, _, v)), (y += b)); + } else (_ = Ce(o, x.data, x.gc, _, g)), (y = b); + r.push(_), l.push(y), (M = Math.max(_, M)), (w = Math.max(y, w)); + } + !(function (t, e) { + u(t, (t) => { + const i = t.gc, + s = i.length / 2; + let n; + if (s > e) { + for (n = 0; n < s; ++n) delete t.data[i[n]]; + i.splice(0, s); + } + }); + })(a, e); + const k = r.indexOf(M), + S = l.indexOf(w), + P = (t) => ({ width: r[t] || 0, height: l[t] || 0 }); + return { + first: P(0), + last: P(e - 1), + widest: P(k), + highest: P(S), + widths: r, + heights: l, + }; + } + getLabelForValue(t) { + return t; + } + getPixelForValue(t, e) { + return NaN; + } + getValueForPixel(t) {} + getPixelForTick(t) { + const e = this.ticks; + return t < 0 || t > e.length - 1 + ? null + : this.getPixelForValue(e[t].value); + } + getPixelForDecimal(t) { + this._reversePixels && (t = 1 - t); + const e = this._startPixel + t * this._length; + return Q(this._alignToPixels ? Ae(this.chart, e, 0) : e); + } + getDecimalForPixel(t) { + const e = (t - this._startPixel) / this._length; + return this._reversePixels ? 1 - e : e; + } + getBasePixel() { + return this.getPixelForValue(this.getBaseValue()); + } + getBaseValue() { + const { min: t, max: e } = this; + return t < 0 && e < 0 ? e : t > 0 && e > 0 ? t : 0; + } + getContext(t) { + const e = this.ticks || []; + if (t >= 0 && t < e.length) { + const i = e[t]; + return ( + i.$context || + (i.$context = (function (t, e, i) { + return Ci(t, { tick: i, index: e, type: "tick" }); + })(this.getContext(), t, i)) + ); + } + return ( + this.$context || + (this.$context = Ci(this.chart.getContext(), { + scale: this, + type: "scale", + })) + ); + } + _tickSize() { + const t = this.options.ticks, + e = $(this.labelRotation), + i = Math.abs(Math.cos(e)), + s = Math.abs(Math.sin(e)), + n = this._getLabelSizes(), + o = t.autoSkipPadding || 0, + a = n ? n.widest.width + o : 0, + r = n ? n.highest.height + o : 0; + return this.isHorizontal() + ? r * i > a * s + ? a / i + : r / s + : r * s < a * i + ? r / i + : a / s; + } + _isVisible() { + const t = this.options.display; + return "auto" !== t ? !!t : this.getMatchingVisibleMetas().length > 0; + } + _computeGridLineItems(t) { + const e = this.axis, + i = this.chart, + s = this.options, + { grid: n, position: a, border: r } = s, + h = n.offset, + c = this.isHorizontal(), + d = this.ticks.length + (h ? 1 : 0), + u = Ks(n), + f = [], + g = r.setContext(this.getContext()), + p = g.display ? g.width : 0, + m = p / 2, + x = function (t) { + return Ae(i, t, p); + }; + let b, _, y, v, M, w, k, S, P, D, C, O; + if ("top" === a) + (b = x(this.bottom)), + (w = this.bottom - u), + (S = b - m), + (D = x(t.top) + m), + (O = t.bottom); + else if ("bottom" === a) + (b = x(this.top)), + (D = t.top), + (O = x(t.bottom) - m), + (w = b + m), + (S = this.top + u); + else if ("left" === a) + (b = x(this.right)), + (M = this.right - u), + (k = b - m), + (P = x(t.left) + m), + (C = t.right); + else if ("right" === a) + (b = x(this.left)), + (P = t.left), + (C = x(t.right) - m), + (M = b + m), + (k = this.left + u); + else if ("x" === e) { + if ("center" === a) b = x((t.top + t.bottom) / 2 + 0.5); + else if (o(a)) { + const t = Object.keys(a)[0], + e = a[t]; + b = x(this.chart.scales[t].getPixelForValue(e)); + } + (D = t.top), (O = t.bottom), (w = b + m), (S = w + u); + } else if ("y" === e) { + if ("center" === a) b = x((t.left + t.right) / 2); + else if (o(a)) { + const t = Object.keys(a)[0], + e = a[t]; + b = x(this.chart.scales[t].getPixelForValue(e)); + } + (M = b - m), (k = M - u), (P = t.left), (C = t.right); + } + const A = l(s.ticks.maxTicksLimit, d), + T = Math.max(1, Math.ceil(d / A)); + for (_ = 0; _ < d; _ += T) { + const t = this.getContext(_), + e = n.setContext(t), + s = r.setContext(t), + o = e.lineWidth, + a = e.color, + l = s.dash || [], + d = s.dashOffset, + u = e.tickWidth, + g = e.tickColor, + p = e.tickBorderDash || [], + m = e.tickBorderDashOffset; + (y = qs(this, _, h)), + void 0 !== y && + ((v = Ae(i, y, o)), + c ? (M = k = P = C = v) : (w = S = D = O = v), + f.push({ + tx1: M, + ty1: w, + tx2: k, + ty2: S, + x1: P, + y1: D, + x2: C, + y2: O, + width: o, + color: a, + borderDash: l, + borderDashOffset: d, + tickWidth: u, + tickColor: g, + tickBorderDash: p, + tickBorderDashOffset: m, + })); + } + return (this._ticksLength = d), (this._borderValue = b), f; + } + _computeLabelItems(t) { + const e = this.axis, + i = this.options, + { position: s, ticks: a } = i, + r = this.isHorizontal(), + l = this.ticks, + { align: h, crossAlign: c, padding: d, mirror: u } = a, + f = Ks(i.grid), + g = f + d, + p = u ? -d : g, + m = -$(this.labelRotation), + x = []; + let b, + _, + y, + v, + M, + w, + k, + S, + P, + D, + C, + O, + A = "middle"; + if ("top" === s) + (w = this.bottom - p), (k = this._getXAxisLabelAlignment()); + else if ("bottom" === s) + (w = this.top + p), (k = this._getXAxisLabelAlignment()); + else if ("left" === s) { + const t = this._getYAxisLabelAlignment(f); + (k = t.textAlign), (M = t.x); + } else if ("right" === s) { + const t = this._getYAxisLabelAlignment(f); + (k = t.textAlign), (M = t.x); + } else if ("x" === e) { + if ("center" === s) w = (t.top + t.bottom) / 2 + g; + else if (o(s)) { + const t = Object.keys(s)[0], + e = s[t]; + w = this.chart.scales[t].getPixelForValue(e) + g; + } + k = this._getXAxisLabelAlignment(); + } else if ("y" === e) { + if ("center" === s) M = (t.left + t.right) / 2 - g; + else if (o(s)) { + const t = Object.keys(s)[0], + e = s[t]; + M = this.chart.scales[t].getPixelForValue(e); + } + k = this._getYAxisLabelAlignment(f).textAlign; + } + "y" === e && + ("start" === h ? (A = "top") : "end" === h && (A = "bottom")); + const T = this._getLabelSizes(); + for (b = 0, _ = l.length; b < _; ++b) { + (y = l[b]), (v = y.label); + const t = a.setContext(this.getContext(b)); + (S = this.getPixelForTick(b) + a.labelOffset), + (P = this._resolveTickFontOptions(b)), + (D = P.lineHeight), + (C = n(v) ? v.length : 1); + const e = C / 2, + i = t.color, + o = t.textStrokeColor, + h = t.textStrokeWidth; + let d, + f = k; + if ( + (r + ? ((M = S), + "inner" === k && + (f = + b === _ - 1 + ? this.options.reverse + ? "left" + : "right" + : 0 === b + ? this.options.reverse + ? "right" + : "left" + : "center"), + (O = + "top" === s + ? "near" === c || 0 !== m + ? -C * D + D / 2 + : "center" === c + ? -T.highest.height / 2 - e * D + D + : -T.highest.height + D / 2 + : "near" === c || 0 !== m + ? D / 2 + : "center" === c + ? T.highest.height / 2 - e * D + : T.highest.height - C * D), + u && (O *= -1), + 0 === m || t.showLabelBackdrop || (M += (D / 2) * Math.sin(m))) + : ((w = S), (O = ((1 - C) * D) / 2)), + t.showLabelBackdrop) + ) { + const e = ki(t.backdropPadding), + i = T.heights[b], + s = T.widths[b]; + let n = O - e.top, + o = 0 - e.left; + switch (A) { + case "middle": + n -= i / 2; + break; + case "bottom": + n -= i; + } + switch (k) { + case "center": + o -= s / 2; + break; + case "right": + o -= s; + break; + case "inner": + b === _ - 1 ? (o -= s) : b > 0 && (o -= s / 2); + } + d = { + left: o, + top: n, + width: s + e.width, + height: i + e.height, + color: t.backdropColor, + }; + } + x.push({ + label: v, + font: P, + textOffset: O, + options: { + rotation: m, + color: i, + strokeColor: o, + strokeWidth: h, + textAlign: f, + textBaseline: A, + translation: [M, w], + backdrop: d, + }, + }); + } + return x; + } + _getXAxisLabelAlignment() { + const { position: t, ticks: e } = this.options; + if (-$(this.labelRotation)) return "top" === t ? "left" : "right"; + let i = "center"; + return ( + "start" === e.align + ? (i = "left") + : "end" === e.align + ? (i = "right") + : "inner" === e.align && (i = "inner"), + i + ); + } + _getYAxisLabelAlignment(t) { + const { + position: e, + ticks: { crossAlign: i, mirror: s, padding: n }, + } = this.options, + o = t + n, + a = this._getLabelSizes().widest.width; + let r, l; + return ( + "left" === e + ? s + ? ((l = this.right + n), + "near" === i + ? (r = "left") + : "center" === i + ? ((r = "center"), (l += a / 2)) + : ((r = "right"), (l += a))) + : ((l = this.right - o), + "near" === i + ? (r = "right") + : "center" === i + ? ((r = "center"), (l -= a / 2)) + : ((r = "left"), (l = this.left))) + : "right" === e + ? s + ? ((l = this.left + n), + "near" === i + ? (r = "right") + : "center" === i + ? ((r = "center"), (l -= a / 2)) + : ((r = "left"), (l -= a))) + : ((l = this.left + o), + "near" === i + ? (r = "left") + : "center" === i + ? ((r = "center"), (l += a / 2)) + : ((r = "right"), (l = this.right))) + : (r = "right"), + { textAlign: r, x: l } + ); + } + _computeLabelArea() { + if (this.options.ticks.mirror) return; + const t = this.chart, + e = this.options.position; + return "left" === e || "right" === e + ? { top: 0, left: this.left, bottom: t.height, right: this.right } + : "top" === e || "bottom" === e + ? { top: this.top, left: 0, bottom: this.bottom, right: t.width } + : void 0; + } + drawBackground() { + const { + ctx: t, + options: { backgroundColor: e }, + left: i, + top: s, + width: n, + height: o, + } = this; + e && (t.save(), (t.fillStyle = e), t.fillRect(i, s, n, o), t.restore()); + } + getLineWidthForValue(t) { + const e = this.options.grid; + if (!this._isVisible() || !e.display) return 0; + const i = this.ticks.findIndex((e) => e.value === t); + if (i >= 0) { + return e.setContext(this.getContext(i)).lineWidth; + } + return 0; + } + drawGrid(t) { + const e = this.options.grid, + i = this.ctx, + s = + this._gridLineItems || + (this._gridLineItems = this._computeGridLineItems(t)); + let n, o; + const a = (t, e, s) => { + s.width && + s.color && + (i.save(), + (i.lineWidth = s.width), + (i.strokeStyle = s.color), + i.setLineDash(s.borderDash || []), + (i.lineDashOffset = s.borderDashOffset), + i.beginPath(), + i.moveTo(t.x, t.y), + i.lineTo(e.x, e.y), + i.stroke(), + i.restore()); + }; + if (e.display) + for (n = 0, o = s.length; n < o; ++n) { + const t = s[n]; + e.drawOnChartArea && a({ x: t.x1, y: t.y1 }, { x: t.x2, y: t.y2 }, t), + e.drawTicks && + a( + { x: t.tx1, y: t.ty1 }, + { x: t.tx2, y: t.ty2 }, + { + color: t.tickColor, + width: t.tickWidth, + borderDash: t.tickBorderDash, + borderDashOffset: t.tickBorderDashOffset, + } + ); + } + } + drawBorder() { + const { + chart: t, + ctx: e, + options: { border: i, grid: s }, + } = this, + n = i.setContext(this.getContext()), + o = i.display ? n.width : 0; + if (!o) return; + const a = s.setContext(this.getContext(0)).lineWidth, + r = this._borderValue; + let l, h, c, d; + this.isHorizontal() + ? ((l = Ae(t, this.left, o) - o / 2), + (h = Ae(t, this.right, a) + a / 2), + (c = d = r)) + : ((c = Ae(t, this.top, o) - o / 2), + (d = Ae(t, this.bottom, a) + a / 2), + (l = h = r)), + e.save(), + (e.lineWidth = n.width), + (e.strokeStyle = n.color), + e.beginPath(), + e.moveTo(l, c), + e.lineTo(h, d), + e.stroke(), + e.restore(); + } + drawLabels(t) { + if (!this.options.ticks.display) return; + const e = this.ctx, + i = this._computeLabelArea(); + i && Ie(e, i); + const s = this.getLabelItems(t); + for (const t of s) { + const i = t.options, + s = t.font; + Ne(e, t.label, 0, t.textOffset, s, i); + } + i && ze(e); + } + drawTitle() { + const { + ctx: t, + options: { position: e, title: i, reverse: s }, + } = this; + if (!i.display) return; + const a = Si(i.font), + r = ki(i.padding), + l = i.align; + let h = a.lineHeight / 2; + "bottom" === e || "center" === e || o(e) + ? ((h += r.bottom), + n(i.text) && (h += a.lineHeight * (i.text.length - 1))) + : (h += r.top); + const { + titleX: c, + titleY: d, + maxWidth: u, + rotation: f, + } = (function (t, e, i, s) { + const { top: n, left: a, bottom: r, right: l, chart: h } = t, + { chartArea: c, scales: d } = h; + let u, + f, + g, + p = 0; + const m = r - n, + x = l - a; + if (t.isHorizontal()) { + if (((f = ft(s, a, l)), o(i))) { + const t = Object.keys(i)[0], + s = i[t]; + g = d[t].getPixelForValue(s) + m - e; + } else + g = "center" === i ? (c.bottom + c.top) / 2 + m - e : Ys(t, i, e); + u = l - a; + } else { + if (o(i)) { + const t = Object.keys(i)[0], + s = i[t]; + f = d[t].getPixelForValue(s) - x + e; + } else + f = "center" === i ? (c.left + c.right) / 2 - x + e : Ys(t, i, e); + (g = ft(s, r, n)), (p = "left" === i ? -E : E); + } + return { titleX: f, titleY: g, maxWidth: u, rotation: p }; + })(this, h, e, l); + Ne(t, i.text, 0, 0, a, { + color: i.color, + maxWidth: u, + rotation: f, + textAlign: Zs(l, e, s), + textBaseline: "middle", + translation: [c, d], + }); + } + draw(t) { + this._isVisible() && + (this.drawBackground(), + this.drawGrid(t), + this.drawBorder(), + this.drawTitle(), + this.drawLabels(t)); + } + _layers() { + const t = this.options, + e = (t.ticks && t.ticks.z) || 0, + i = l(t.grid && t.grid.z, -1), + s = l(t.border && t.border.z, 0); + return this._isVisible() && this.draw === Js.prototype.draw + ? [ + { + z: i, + draw: (t) => { + this.drawBackground(), this.drawGrid(t), this.drawTitle(); + }, + }, + { + z: s, + draw: () => { + this.drawBorder(); + }, + }, + { + z: e, + draw: (t) => { + this.drawLabels(t); + }, + }, + ] + : [ + { + z: e, + draw: (t) => { + this.draw(t); + }, + }, + ]; + } + getMatchingVisibleMetas(t) { + const e = this.chart.getSortedVisibleDatasetMetas(), + i = this.axis + "AxisID", + s = []; + let n, o; + for (n = 0, o = e.length; n < o; ++n) { + const o = e[n]; + o[i] !== this.id || (t && o.type !== t) || s.push(o); + } + return s; + } + _resolveTickFontOptions(t) { + return Si(this.options.ticks.setContext(this.getContext(t)).font); + } + _maxDigits() { + const t = this._resolveTickFontOptions(0).lineHeight; + return (this.isHorizontal() ? this.width : this.height) / t; + } + } + class Qs { + constructor(t, e, i) { + (this.type = t), + (this.scope = e), + (this.override = i), + (this.items = Object.create(null)); + } + isForType(t) { + return Object.prototype.isPrototypeOf.call( + this.type.prototype, + t.prototype + ); + } + register(t) { + const e = Object.getPrototypeOf(t); + let i; + (function (t) { + return "id" in t && "defaults" in t; + })(e) && (i = this.register(e)); + const s = this.items, + n = t.id, + o = this.scope + "." + n; + if (!n) throw new Error("class does not have id: " + t); + return ( + n in s || + ((s[n] = t), + (function (t, e, i) { + const s = x(Object.create(null), [ + i ? ue.get(i) : {}, + ue.get(e), + t.defaults, + ]); + ue.set(e, s), + t.defaultRoutes && + (function (t, e) { + Object.keys(e).forEach((i) => { + const s = i.split("."), + n = s.pop(), + o = [t].concat(s).join("."), + a = e[i].split("."), + r = a.pop(), + l = a.join("."); + ue.route(o, n, l, r); + }); + })(e, t.defaultRoutes); + t.descriptors && ue.describe(e, t.descriptors); + })(t, o, i), + this.override && ue.override(t.id, t.overrides)), + o + ); + } + get(t) { + return this.items[t]; + } + unregister(t) { + const e = this.items, + i = t.id, + s = this.scope; + i in e && delete e[i], + s && i in ue[s] && (delete ue[s][i], this.override && delete re[i]); + } + } + class tn { + constructor() { + (this.controllers = new Qs(Ns, "datasets", !0)), + (this.elements = new Qs(Hs, "elements")), + (this.plugins = new Qs(Object, "plugins")), + (this.scales = new Qs(Js, "scales")), + (this._typedRegistries = [ + this.controllers, + this.scales, + this.elements, + ]); + } + add(...t) { + this._each("register", t); + } + remove(...t) { + this._each("unregister", t); + } + addControllers(...t) { + this._each("register", t, this.controllers); + } + addElements(...t) { + this._each("register", t, this.elements); + } + addPlugins(...t) { + this._each("register", t, this.plugins); + } + addScales(...t) { + this._each("register", t, this.scales); + } + getController(t) { + return this._get(t, this.controllers, "controller"); + } + getElement(t) { + return this._get(t, this.elements, "element"); + } + getPlugin(t) { + return this._get(t, this.plugins, "plugin"); + } + getScale(t) { + return this._get(t, this.scales, "scale"); + } + removeControllers(...t) { + this._each("unregister", t, this.controllers); + } + removeElements(...t) { + this._each("unregister", t, this.elements); + } + removePlugins(...t) { + this._each("unregister", t, this.plugins); + } + removeScales(...t) { + this._each("unregister", t, this.scales); + } + _each(t, e, i) { + [...e].forEach((e) => { + const s = i || this._getRegistryForType(e); + i || s.isForType(e) || (s === this.plugins && e.id) + ? this._exec(t, s, e) + : u(e, (e) => { + const s = i || this._getRegistryForType(e); + this._exec(t, s, e); + }); + }); + } + _exec(t, e, i) { + const s = w(t); + d(i["before" + s], [], i), e[t](i), d(i["after" + s], [], i); + } + _getRegistryForType(t) { + for (let e = 0; e < this._typedRegistries.length; e++) { + const i = this._typedRegistries[e]; + if (i.isForType(t)) return i; + } + return this.plugins; + } + _get(t, e, i) { + const s = e.get(t); + if (void 0 === s) + throw new Error('"' + t + '" is not a registered ' + i + "."); + return s; + } + } + var en = new tn(); + class sn { + constructor() { + this._init = []; + } + notify(t, e, i, s) { + "beforeInit" === e && + ((this._init = this._createDescriptors(t, !0)), + this._notify(this._init, t, "install")); + const n = s ? this._descriptors(t).filter(s) : this._descriptors(t), + o = this._notify(n, t, e, i); + return ( + "afterDestroy" === e && + (this._notify(n, t, "stop"), + this._notify(this._init, t, "uninstall")), + o + ); + } + _notify(t, e, i, s) { + s = s || {}; + for (const n of t) { + const t = n.plugin; + if (!1 === d(t[i], [e, s, n.options], t) && s.cancelable) return !1; + } + return !0; + } + invalidate() { + s(this._cache) || + ((this._oldCache = this._cache), (this._cache = void 0)); + } + _descriptors(t) { + if (this._cache) return this._cache; + const e = (this._cache = this._createDescriptors(t)); + return this._notifyStateChanges(t), e; + } + _createDescriptors(t, e) { + const i = t && t.config, + s = l(i.options && i.options.plugins, {}), + n = (function (t) { + const e = {}, + i = [], + s = Object.keys(en.plugins.items); + for (let t = 0; t < s.length; t++) i.push(en.getPlugin(s[t])); + const n = t.plugins || []; + for (let t = 0; t < n.length; t++) { + const s = n[t]; + -1 === i.indexOf(s) && (i.push(s), (e[s.id] = !0)); + } + return { plugins: i, localIds: e }; + })(i); + return !1 !== s || e + ? (function (t, { plugins: e, localIds: i }, s, n) { + const o = [], + a = t.getContext(); + for (const r of e) { + const e = r.id, + l = nn(s[e], n); + null !== l && + o.push({ + plugin: r, + options: on(t.config, { plugin: r, local: i[e] }, l, a), + }); + } + return o; + })(t, n, s, e) + : []; + } + _notifyStateChanges(t) { + const e = this._oldCache || [], + i = this._cache, + s = (t, e) => + t.filter((t) => !e.some((e) => t.plugin.id === e.plugin.id)); + this._notify(s(e, i), t, "stop"), this._notify(s(i, e), t, "start"); + } + } + function nn(t, e) { + return e || !1 !== t ? (!0 === t ? {} : t) : null; + } + function on(t, { plugin: e, local: i }, s, n) { + const o = t.pluginScopeKeys(e), + a = t.getOptionScopes(s, o); + return ( + i && e.defaults && a.push(e.defaults), + t.createResolver(a, n, [""], { + scriptable: !1, + indexable: !1, + allKeys: !0, + }) + ); + } + function an(t, e) { + const i = ue.datasets[t] || {}; + return ( + ((e.datasets || {})[t] || {}).indexAxis || + e.indexAxis || + i.indexAxis || + "x" + ); + } + function rn(t) { + if ("x" === t || "y" === t || "r" === t) return t; + } + function ln(t, ...e) { + if (rn(t)) return t; + for (const s of e) { + const e = + s.axis || + ("top" === (i = s.position) || "bottom" === i + ? "x" + : "left" === i || "right" === i + ? "y" + : void 0) || + (t.length > 1 && rn(t[0].toLowerCase())); + if (e) return e; + } + var i; + throw new Error( + `Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.` + ); + } + function hn(t, e, i) { + if (i[e + "AxisID"] === t) return { axis: e }; + } + function cn(t, e) { + const i = re[t.type] || { scales: {} }, + s = e.scales || {}, + n = an(t.type, e), + a = Object.create(null); + return ( + Object.keys(s).forEach((e) => { + const r = s[e]; + if (!o(r)) + return console.error(`Invalid scale configuration for scale: ${e}`); + if (r._proxy) + return console.warn( + `Ignoring resolver passed as options for scale: ${e}` + ); + const l = ln( + e, + r, + (function (t, e) { + if (e.data && e.data.datasets) { + const i = e.data.datasets.filter( + (e) => e.xAxisID === t || e.yAxisID === t + ); + if (i.length) return hn(t, "x", i[0]) || hn(t, "y", i[0]); + } + return {}; + })(e, t), + ue.scales[r.type] + ), + h = (function (t, e) { + return t === e ? "_index_" : "_value_"; + })(l, n), + c = i.scales || {}; + a[e] = b(Object.create(null), [{ axis: l }, r, c[l], c[h]]); + }), + t.data.datasets.forEach((i) => { + const n = i.type || t.type, + o = i.indexAxis || an(n, e), + r = (re[n] || {}).scales || {}; + Object.keys(r).forEach((t) => { + const e = (function (t, e) { + let i = t; + return ( + "_index_" === t + ? (i = e) + : "_value_" === t && (i = "x" === e ? "y" : "x"), + i + ); + })(t, o), + n = i[e + "AxisID"] || e; + (a[n] = a[n] || Object.create(null)), + b(a[n], [{ axis: e }, s[n], r[t]]); + }); + }), + Object.keys(a).forEach((t) => { + const e = a[t]; + b(e, [ue.scales[e.type], ue.scale]); + }), + a + ); + } + function dn(t) { + const e = t.options || (t.options = {}); + (e.plugins = l(e.plugins, {})), (e.scales = cn(t, e)); + } + function un(t) { + return ( + ((t = t || {}).datasets = t.datasets || []), + (t.labels = t.labels || []), + t + ); + } + const fn = new Map(), + gn = new Set(); + function pn(t, e) { + let i = fn.get(t); + return i || ((i = e()), fn.set(t, i), gn.add(i)), i; + } + const mn = (t, e, i) => { + const s = M(e, i); + void 0 !== s && t.add(s); + }; + class xn { + constructor(t) { + (this._config = (function (t) { + return ((t = t || {}).data = un(t.data)), dn(t), t; + })(t)), + (this._scopeCache = new Map()), + (this._resolverCache = new Map()); + } + get platform() { + return this._config.platform; + } + get type() { + return this._config.type; + } + set type(t) { + this._config.type = t; + } + get data() { + return this._config.data; + } + set data(t) { + this._config.data = un(t); + } + get options() { + return this._config.options; + } + set options(t) { + this._config.options = t; + } + get plugins() { + return this._config.plugins; + } + update() { + const t = this._config; + this.clearCache(), dn(t); + } + clearCache() { + this._scopeCache.clear(), this._resolverCache.clear(); + } + datasetScopeKeys(t) { + return pn(t, () => [[`datasets.${t}`, ""]]); + } + datasetAnimationScopeKeys(t, e) { + return pn(`${t}.transition.${e}`, () => [ + [`datasets.${t}.transitions.${e}`, `transitions.${e}`], + [`datasets.${t}`, ""], + ]); + } + datasetElementScopeKeys(t, e) { + return pn(`${t}-${e}`, () => [ + [`datasets.${t}.elements.${e}`, `datasets.${t}`, `elements.${e}`, ""], + ]); + } + pluginScopeKeys(t) { + const e = t.id; + return pn(`${this.type}-plugin-${e}`, () => [ + [`plugins.${e}`, ...(t.additionalOptionScopes || [])], + ]); + } + _cachedScopes(t, e) { + const i = this._scopeCache; + let s = i.get(t); + return (s && !e) || ((s = new Map()), i.set(t, s)), s; + } + getOptionScopes(t, e, i) { + const { options: s, type: n } = this, + o = this._cachedScopes(t, i), + a = o.get(e); + if (a) return a; + const r = new Set(); + e.forEach((e) => { + t && (r.add(t), e.forEach((e) => mn(r, t, e))), + e.forEach((t) => mn(r, s, t)), + e.forEach((t) => mn(r, re[n] || {}, t)), + e.forEach((t) => mn(r, ue, t)), + e.forEach((t) => mn(r, le, t)); + }); + const l = Array.from(r); + return ( + 0 === l.length && l.push(Object.create(null)), + gn.has(e) && o.set(e, l), + l + ); + } + chartOptionScopes() { + const { options: t, type: e } = this; + return [t, re[e] || {}, ue.datasets[e] || {}, { type: e }, ue, le]; + } + resolveNamedOptions(t, e, i, s = [""]) { + const o = { $shared: !0 }, + { resolver: a, subPrefixes: r } = bn(this._resolverCache, t, s); + let l = a; + if ( + (function (t, e) { + const { isScriptable: i, isIndexable: s } = Ye(t); + for (const o of e) { + const e = i(o), + a = s(o), + r = (a || e) && t[o]; + if ((e && (S(r) || _n(r))) || (a && n(r))) return !0; + } + return !1; + })(a, e) + ) { + o.$shared = !1; + l = $e(a, (i = S(i) ? i() : i), this.createResolver(t, i, r)); + } + for (const t of e) o[t] = l[t]; + return o; + } + createResolver(t, e, i = [""], s) { + const { resolver: n } = bn(this._resolverCache, t, i); + return o(e) ? $e(n, e, void 0, s) : n; + } + } + function bn(t, e, i) { + let s = t.get(e); + s || ((s = new Map()), t.set(e, s)); + const n = i.join(); + let o = s.get(n); + if (!o) { + (o = { + resolver: je(e, i), + subPrefixes: i.filter((t) => !t.toLowerCase().includes("hover")), + }), + s.set(n, o); + } + return o; + } + const _n = (t) => o(t) && Object.getOwnPropertyNames(t).some((e) => S(t[e])); + const yn = ["top", "bottom", "left", "right", "chartArea"]; + function vn(t, e) { + return "top" === t || "bottom" === t || (-1 === yn.indexOf(t) && "x" === e); + } + function Mn(t, e) { + return function (i, s) { + return i[t] === s[t] ? i[e] - s[e] : i[t] - s[t]; + }; + } + function wn(t) { + const e = t.chart, + i = e.options.animation; + e.notifyPlugins("afterRender"), d(i && i.onComplete, [t], e); + } + function kn(t) { + const e = t.chart, + i = e.options.animation; + d(i && i.onProgress, [t], e); + } + function Sn(t) { + return ( + fe() && "string" == typeof t + ? (t = document.getElementById(t)) + : t && t.length && (t = t[0]), + t && t.canvas && (t = t.canvas), + t + ); + } + const Pn = {}, + Dn = (t) => { + const e = Sn(t); + return Object.values(Pn) + .filter((t) => t.canvas === e) + .pop(); + }; + function Cn(t, e, i) { + const s = Object.keys(t); + for (const n of s) { + const s = +n; + if (s >= e) { + const o = t[n]; + delete t[n], (i > 0 || s > e) && (t[s + i] = o); + } + } + } + function On(t, e, i) { + return t.options.clip ? t[i] : e[i]; + } + class An { + static defaults = ue; + static instances = Pn; + static overrides = re; + static registry = en; + static version = "4.4.4"; + static getChart = Dn; + static register(...t) { + en.add(...t), Tn(); + } + static unregister(...t) { + en.remove(...t), Tn(); + } + constructor(t, e) { + const s = (this.config = new xn(e)), + n = Sn(t), + o = Dn(n); + if (o) + throw new Error( + "Canvas is already in use. Chart with ID '" + + o.id + + "' must be destroyed before the canvas with ID '" + + o.canvas.id + + "' can be reused." + ); + const a = s.createResolver(s.chartOptionScopes(), this.getContext()); + (this.platform = new (s.platform || ks(n))()), + this.platform.updateConfig(s); + const r = this.platform.acquireContext(n, a.aspectRatio), + l = r && r.canvas, + h = l && l.height, + c = l && l.width; + (this.id = i()), + (this.ctx = r), + (this.canvas = l), + (this.width = c), + (this.height = h), + (this._options = a), + (this._aspectRatio = this.aspectRatio), + (this._layers = []), + (this._metasets = []), + (this._stacks = void 0), + (this.boxes = []), + (this.currentDevicePixelRatio = void 0), + (this.chartArea = void 0), + (this._active = []), + (this._lastEvent = void 0), + (this._listeners = {}), + (this._responsiveListeners = void 0), + (this._sortedMetasets = []), + (this.scales = {}), + (this._plugins = new sn()), + (this.$proxies = {}), + (this._hiddenIndices = {}), + (this.attached = !1), + (this._animationsDisabled = void 0), + (this.$context = void 0), + (this._doResize = dt((t) => this.update(t), a.resizeDelay || 0)), + (this._dataChanges = []), + (Pn[this.id] = this), + r && l + ? (bt.listen(this, "complete", wn), + bt.listen(this, "progress", kn), + this._initialize(), + this.attached && this.update()) + : console.error( + "Failed to create chart: can't acquire context from the given item" + ); + } + get aspectRatio() { + const { + options: { aspectRatio: t, maintainAspectRatio: e }, + width: i, + height: n, + _aspectRatio: o, + } = this; + return s(t) ? (e && o ? o : n ? i / n : null) : t; + } + get data() { + return this.config.data; + } + set data(t) { + this.config.data = t; + } + get options() { + return this._options; + } + set options(t) { + this.config.options = t; + } + get registry() { + return en; + } + _initialize() { + return ( + this.notifyPlugins("beforeInit"), + this.options.responsive + ? this.resize() + : ke(this, this.options.devicePixelRatio), + this.bindEvents(), + this.notifyPlugins("afterInit"), + this + ); + } + clear() { + return Te(this.canvas, this.ctx), this; + } + stop() { + return bt.stop(this), this; + } + resize(t, e) { + bt.running(this) + ? (this._resizeBeforeDraw = { width: t, height: e }) + : this._resize(t, e); + } + _resize(t, e) { + const i = this.options, + s = this.canvas, + n = i.maintainAspectRatio && this.aspectRatio, + o = this.platform.getMaximumSize(s, t, e, n), + a = i.devicePixelRatio || this.platform.getDevicePixelRatio(), + r = this.width ? "resize" : "attach"; + (this.width = o.width), + (this.height = o.height), + (this._aspectRatio = this.aspectRatio), + ke(this, a, !0) && + (this.notifyPlugins("resize", { size: o }), + d(i.onResize, [this, o], this), + this.attached && this._doResize(r) && this.render()); + } + ensureScalesHaveIDs() { + u(this.options.scales || {}, (t, e) => { + t.id = e; + }); + } + buildOrUpdateScales() { + const t = this.options, + e = t.scales, + i = this.scales, + s = Object.keys(i).reduce((t, e) => ((t[e] = !1), t), {}); + let n = []; + e && + (n = n.concat( + Object.keys(e).map((t) => { + const i = e[t], + s = ln(t, i), + n = "r" === s, + o = "x" === s; + return { + options: i, + dposition: n ? "chartArea" : o ? "bottom" : "left", + dtype: n ? "radialLinear" : o ? "category" : "linear", + }; + }) + )), + u(n, (e) => { + const n = e.options, + o = n.id, + a = ln(o, n), + r = l(n.type, e.dtype); + (void 0 !== n.position && vn(n.position, a) === vn(e.dposition)) || + (n.position = e.dposition), + (s[o] = !0); + let h = null; + if (o in i && i[o].type === r) h = i[o]; + else { + (h = new (en.getScale(r))({ + id: o, + type: r, + ctx: this.ctx, + chart: this, + })), + (i[h.id] = h); + } + h.init(n, t); + }), + u(s, (t, e) => { + t || delete i[e]; + }), + u(i, (t) => { + as.configure(this, t, t.options), as.addBox(this, t); + }); + } + _updateMetasets() { + const t = this._metasets, + e = this.data.datasets.length, + i = t.length; + if ((t.sort((t, e) => t.index - e.index), i > e)) { + for (let t = e; t < i; ++t) this._destroyDatasetMeta(t); + t.splice(e, i - e); + } + this._sortedMetasets = t.slice(0).sort(Mn("order", "index")); + } + _removeUnreferencedMetasets() { + const { + _metasets: t, + data: { datasets: e }, + } = this; + t.length > e.length && delete this._stacks, + t.forEach((t, i) => { + 0 === e.filter((e) => e === t._dataset).length && + this._destroyDatasetMeta(i); + }); + } + buildOrUpdateControllers() { + const t = [], + e = this.data.datasets; + let i, s; + for ( + this._removeUnreferencedMetasets(), i = 0, s = e.length; + i < s; + i++ + ) { + const s = e[i]; + let n = this.getDatasetMeta(i); + const o = s.type || this.config.type; + if ( + (n.type && + n.type !== o && + (this._destroyDatasetMeta(i), (n = this.getDatasetMeta(i))), + (n.type = o), + (n.indexAxis = s.indexAxis || an(o, this.options)), + (n.order = s.order || 0), + (n.index = i), + (n.label = "" + s.label), + (n.visible = this.isDatasetVisible(i)), + n.controller) + ) + n.controller.updateIndex(i), n.controller.linkScales(); + else { + const e = en.getController(o), + { datasetElementType: s, dataElementType: a } = ue.datasets[o]; + Object.assign(e, { + dataElementType: en.getElement(a), + datasetElementType: s && en.getElement(s), + }), + (n.controller = new e(this, i)), + t.push(n.controller); + } + } + return this._updateMetasets(), t; + } + _resetElements() { + u( + this.data.datasets, + (t, e) => { + this.getDatasetMeta(e).controller.reset(); + }, + this + ); + } + reset() { + this._resetElements(), this.notifyPlugins("reset"); + } + update(t) { + const e = this.config; + e.update(); + const i = (this._options = e.createResolver( + e.chartOptionScopes(), + this.getContext() + )), + s = (this._animationsDisabled = !i.animation); + if ( + (this._updateScales(), + this._checkEventBindings(), + this._updateHiddenIndices(), + this._plugins.invalidate(), + !1 === this.notifyPlugins("beforeUpdate", { mode: t, cancelable: !0 })) + ) + return; + const n = this.buildOrUpdateControllers(); + this.notifyPlugins("beforeElementsUpdate"); + let o = 0; + for (let t = 0, e = this.data.datasets.length; t < e; t++) { + const { controller: e } = this.getDatasetMeta(t), + i = !s && -1 === n.indexOf(e); + e.buildOrUpdateElements(i), (o = Math.max(+e.getMaxOverflow(), o)); + } + (o = this._minPadding = i.layout.autoPadding ? o : 0), + this._updateLayout(o), + s || + u(n, (t) => { + t.reset(); + }), + this._updateDatasets(t), + this.notifyPlugins("afterUpdate", { mode: t }), + this._layers.sort(Mn("z", "_idx")); + const { _active: a, _lastEvent: r } = this; + r + ? this._eventHandler(r, !0) + : a.length && this._updateHoverStyles(a, a, !0), + this.render(); + } + _updateScales() { + u(this.scales, (t) => { + as.removeBox(this, t); + }), + this.ensureScalesHaveIDs(), + this.buildOrUpdateScales(); + } + _checkEventBindings() { + const t = this.options, + e = new Set(Object.keys(this._listeners)), + i = new Set(t.events); + (P(e, i) && !!this._responsiveListeners === t.responsive) || + (this.unbindEvents(), this.bindEvents()); + } + _updateHiddenIndices() { + const { _hiddenIndices: t } = this, + e = this._getUniformDataChanges() || []; + for (const { method: i, start: s, count: n } of e) { + Cn(t, s, "_removeElements" === i ? -n : n); + } + } + _getUniformDataChanges() { + const t = this._dataChanges; + if (!t || !t.length) return; + this._dataChanges = []; + const e = this.data.datasets.length, + i = (e) => + new Set( + t + .filter((t) => t[0] === e) + .map((t, e) => e + "," + t.splice(1).join(",")) + ), + s = i(0); + for (let t = 1; t < e; t++) if (!P(s, i(t))) return; + return Array.from(s) + .map((t) => t.split(",")) + .map((t) => ({ method: t[1], start: +t[2], count: +t[3] })); + } + _updateLayout(t) { + if (!1 === this.notifyPlugins("beforeLayout", { cancelable: !0 })) return; + as.update(this, this.width, this.height, t); + const e = this.chartArea, + i = e.width <= 0 || e.height <= 0; + (this._layers = []), + u( + this.boxes, + (t) => { + (i && "chartArea" === t.position) || + (t.configure && t.configure(), this._layers.push(...t._layers())); + }, + this + ), + this._layers.forEach((t, e) => { + t._idx = e; + }), + this.notifyPlugins("afterLayout"); + } + _updateDatasets(t) { + if ( + !1 !== + this.notifyPlugins("beforeDatasetsUpdate", { mode: t, cancelable: !0 }) + ) { + for (let t = 0, e = this.data.datasets.length; t < e; ++t) + this.getDatasetMeta(t).controller.configure(); + for (let e = 0, i = this.data.datasets.length; e < i; ++e) + this._updateDataset(e, S(t) ? t({ datasetIndex: e }) : t); + this.notifyPlugins("afterDatasetsUpdate", { mode: t }); + } + } + _updateDataset(t, e) { + const i = this.getDatasetMeta(t), + s = { meta: i, index: t, mode: e, cancelable: !0 }; + !1 !== this.notifyPlugins("beforeDatasetUpdate", s) && + (i.controller._update(e), + (s.cancelable = !1), + this.notifyPlugins("afterDatasetUpdate", s)); + } + render() { + !1 !== this.notifyPlugins("beforeRender", { cancelable: !0 }) && + (bt.has(this) + ? this.attached && !bt.running(this) && bt.start(this) + : (this.draw(), wn({ chart: this }))); + } + draw() { + let t; + if (this._resizeBeforeDraw) { + const { width: t, height: e } = this._resizeBeforeDraw; + (this._resizeBeforeDraw = null), this._resize(t, e); + } + if ((this.clear(), this.width <= 0 || this.height <= 0)) return; + if (!1 === this.notifyPlugins("beforeDraw", { cancelable: !0 })) return; + const e = this._layers; + for (t = 0; t < e.length && e[t].z <= 0; ++t) e[t].draw(this.chartArea); + for (this._drawDatasets(); t < e.length; ++t) e[t].draw(this.chartArea); + this.notifyPlugins("afterDraw"); + } + _getSortedDatasetMetas(t) { + const e = this._sortedMetasets, + i = []; + let s, n; + for (s = 0, n = e.length; s < n; ++s) { + const n = e[s]; + (t && !n.visible) || i.push(n); + } + return i; + } + getSortedVisibleDatasetMetas() { + return this._getSortedDatasetMetas(!0); + } + _drawDatasets() { + if (!1 === this.notifyPlugins("beforeDatasetsDraw", { cancelable: !0 })) + return; + const t = this.getSortedVisibleDatasetMetas(); + for (let e = t.length - 1; e >= 0; --e) this._drawDataset(t[e]); + this.notifyPlugins("afterDatasetsDraw"); + } + _drawDataset(t) { + const e = this.ctx, + i = t._clip, + s = !i.disabled, + n = (function (t, e) { + const { xScale: i, yScale: s } = t; + return i && s + ? { + left: On(i, e, "left"), + right: On(i, e, "right"), + top: On(s, e, "top"), + bottom: On(s, e, "bottom"), + } + : e; + })(t, this.chartArea), + o = { meta: t, index: t.index, cancelable: !0 }; + !1 !== this.notifyPlugins("beforeDatasetDraw", o) && + (s && + Ie(e, { + left: !1 === i.left ? 0 : n.left - i.left, + right: !1 === i.right ? this.width : n.right + i.right, + top: !1 === i.top ? 0 : n.top - i.top, + bottom: !1 === i.bottom ? this.height : n.bottom + i.bottom, + }), + t.controller.draw(), + s && ze(e), + (o.cancelable = !1), + this.notifyPlugins("afterDatasetDraw", o)); + } + isPointInArea(t) { + return Re(t, this.chartArea, this._minPadding); + } + getElementsAtEventForMode(t, e, i, s) { + const n = Xi.modes[e]; + return "function" == typeof n ? n(this, t, i, s) : []; + } + getDatasetMeta(t) { + const e = this.data.datasets[t], + i = this._metasets; + let s = i.filter((t) => t && t._dataset === e).pop(); + return ( + s || + ((s = { + type: null, + data: [], + dataset: null, + controller: null, + hidden: null, + xAxisID: null, + yAxisID: null, + order: (e && e.order) || 0, + index: t, + _dataset: e, + _parsed: [], + _sorted: !1, + }), + i.push(s)), + s + ); + } + getContext() { + return ( + this.$context || + (this.$context = Ci(null, { chart: this, type: "chart" })) + ); + } + getVisibleDatasetCount() { + return this.getSortedVisibleDatasetMetas().length; + } + isDatasetVisible(t) { + const e = this.data.datasets[t]; + if (!e) return !1; + const i = this.getDatasetMeta(t); + return "boolean" == typeof i.hidden ? !i.hidden : !e.hidden; + } + setDatasetVisibility(t, e) { + this.getDatasetMeta(t).hidden = !e; + } + toggleDataVisibility(t) { + this._hiddenIndices[t] = !this._hiddenIndices[t]; + } + getDataVisibility(t) { + return !this._hiddenIndices[t]; + } + _updateVisibility(t, e, i) { + const s = i ? "show" : "hide", + n = this.getDatasetMeta(t), + o = n.controller._resolveAnimations(void 0, s); + k(e) + ? ((n.data[e].hidden = !i), this.update()) + : (this.setDatasetVisibility(t, i), + o.update(n, { visible: i }), + this.update((e) => (e.datasetIndex === t ? s : void 0))); + } + hide(t, e) { + this._updateVisibility(t, e, !1); + } + show(t, e) { + this._updateVisibility(t, e, !0); + } + _destroyDatasetMeta(t) { + const e = this._metasets[t]; + e && e.controller && e.controller._destroy(), delete this._metasets[t]; + } + _stop() { + let t, e; + for ( + this.stop(), bt.remove(this), t = 0, e = this.data.datasets.length; + t < e; + ++t + ) + this._destroyDatasetMeta(t); + } + destroy() { + this.notifyPlugins("beforeDestroy"); + const { canvas: t, ctx: e } = this; + this._stop(), + this.config.clearCache(), + t && + (this.unbindEvents(), + Te(t, e), + this.platform.releaseContext(e), + (this.canvas = null), + (this.ctx = null)), + delete Pn[this.id], + this.notifyPlugins("afterDestroy"); + } + toBase64Image(...t) { + return this.canvas.toDataURL(...t); + } + bindEvents() { + this.bindUserEvents(), + this.options.responsive + ? this.bindResponsiveEvents() + : (this.attached = !0); + } + bindUserEvents() { + const t = this._listeners, + e = this.platform, + i = (i, s) => { + e.addEventListener(this, i, s), (t[i] = s); + }, + s = (t, e, i) => { + (t.offsetX = e), (t.offsetY = i), this._eventHandler(t); + }; + u(this.options.events, (t) => i(t, s)); + } + bindResponsiveEvents() { + this._responsiveListeners || (this._responsiveListeners = {}); + const t = this._responsiveListeners, + e = this.platform, + i = (i, s) => { + e.addEventListener(this, i, s), (t[i] = s); + }, + s = (i, s) => { + t[i] && (e.removeEventListener(this, i, s), delete t[i]); + }, + n = (t, e) => { + this.canvas && this.resize(t, e); + }; + let o; + const a = () => { + s("attach", a), + (this.attached = !0), + this.resize(), + i("resize", n), + i("detach", o); + }; + (o = () => { + (this.attached = !1), + s("resize", n), + this._stop(), + this._resize(0, 0), + i("attach", a); + }), + e.isAttached(this.canvas) ? a() : o(); + } + unbindEvents() { + u(this._listeners, (t, e) => { + this.platform.removeEventListener(this, e, t); + }), + (this._listeners = {}), + u(this._responsiveListeners, (t, e) => { + this.platform.removeEventListener(this, e, t); + }), + (this._responsiveListeners = void 0); + } + updateHoverStyle(t, e, i) { + const s = i ? "set" : "remove"; + let n, o, a, r; + for ( + "dataset" === e && + ((n = this.getDatasetMeta(t[0].datasetIndex)), + n.controller["_" + s + "DatasetHoverStyle"]()), + a = 0, + r = t.length; + a < r; + ++a + ) { + o = t[a]; + const e = o && this.getDatasetMeta(o.datasetIndex).controller; + e && e[s + "HoverStyle"](o.element, o.datasetIndex, o.index); + } + } + getActiveElements() { + return this._active || []; + } + setActiveElements(t) { + const e = this._active || [], + i = t.map(({ datasetIndex: t, index: e }) => { + const i = this.getDatasetMeta(t); + if (!i) throw new Error("No dataset found at index " + t); + return { datasetIndex: t, element: i.data[e], index: e }; + }); + !f(i, e) && + ((this._active = i), + (this._lastEvent = null), + this._updateHoverStyles(i, e)); + } + notifyPlugins(t, e, i) { + return this._plugins.notify(this, t, e, i); + } + isPluginEnabled(t) { + return 1 === this._plugins._cache.filter((e) => e.plugin.id === t).length; + } + _updateHoverStyles(t, e, i) { + const s = this.options.hover, + n = (t, e) => + t.filter( + (t) => + !e.some( + (e) => t.datasetIndex === e.datasetIndex && t.index === e.index + ) + ), + o = n(e, t), + a = i ? t : n(t, e); + o.length && this.updateHoverStyle(o, s.mode, !1), + a.length && s.mode && this.updateHoverStyle(a, s.mode, !0); + } + _eventHandler(t, e) { + const i = { + event: t, + replay: e, + cancelable: !0, + inChartArea: this.isPointInArea(t), + }, + s = (e) => + (e.options.events || this.options.events).includes(t.native.type); + if (!1 === this.notifyPlugins("beforeEvent", i, s)) return; + const n = this._handleEvent(t, e, i.inChartArea); + return ( + (i.cancelable = !1), + this.notifyPlugins("afterEvent", i, s), + (n || i.changed) && this.render(), + this + ); + } + _handleEvent(t, e, i) { + const { _active: s = [], options: n } = this, + o = e, + a = this._getActiveElements(t, s, i, o), + r = D(t), + l = (function (t, e, i, s) { + return i && "mouseout" !== t.type ? (s ? e : t) : null; + })(t, this._lastEvent, i, r); + i && + ((this._lastEvent = null), + d(n.onHover, [t, a, this], this), + r && d(n.onClick, [t, a, this], this)); + const h = !f(a, s); + return ( + (h || e) && ((this._active = a), this._updateHoverStyles(a, s, e)), + (this._lastEvent = l), + h + ); + } + _getActiveElements(t, e, i, s) { + if ("mouseout" === t.type) return []; + if (!i) return e; + const n = this.options.hover; + return this.getElementsAtEventForMode(t, n.mode, n, s); + } + } + function Tn() { + return u(An.instances, (t) => t._plugins.invalidate()); + } + function Ln() { + throw new Error( + "This method is not implemented: Check that a complete date adapter is provided." + ); + } + class En { + static override(t) { + Object.assign(En.prototype, t); + } + options; + constructor(t) { + this.options = t || {}; + } + init() {} + formats() { + return Ln(); + } + parse() { + return Ln(); + } + format() { + return Ln(); + } + add() { + return Ln(); + } + diff() { + return Ln(); + } + startOf() { + return Ln(); + } + endOf() { + return Ln(); + } + } + var Rn = { _date: En }; + function In(t) { + const e = t.iScale, + i = (function (t, e) { + if (!t._cache.$bar) { + const i = t.getMatchingVisibleMetas(e); + let s = []; + for (let e = 0, n = i.length; e < n; e++) + s = s.concat(i[e].controller.getAllParsedValues(t)); + t._cache.$bar = lt(s.sort((t, e) => t - e)); + } + return t._cache.$bar; + })(e, t.type); + let s, + n, + o, + a, + r = e._length; + const l = () => { + 32767 !== o && + -32768 !== o && + (k(a) && (r = Math.min(r, Math.abs(o - a) || r)), (a = o)); + }; + for (s = 0, n = i.length; s < n; ++s) (o = e.getPixelForValue(i[s])), l(); + for (a = void 0, s = 0, n = e.ticks.length; s < n; ++s) + (o = e.getPixelForTick(s)), l(); + return r; + } + function zn(t, e, i, s) { + return ( + n(t) + ? (function (t, e, i, s) { + const n = i.parse(t[0], s), + o = i.parse(t[1], s), + a = Math.min(n, o), + r = Math.max(n, o); + let l = a, + h = r; + Math.abs(a) > Math.abs(r) && ((l = r), (h = a)), + (e[i.axis] = h), + (e._custom = { + barStart: l, + barEnd: h, + start: n, + end: o, + min: a, + max: r, + }); + })(t, e, i, s) + : (e[i.axis] = i.parse(t, s)), + e + ); + } + function Fn(t, e, i, s) { + const n = t.iScale, + o = t.vScale, + a = n.getLabels(), + r = n === o, + l = []; + let h, c, d, u; + for (h = i, c = i + s; h < c; ++h) + (u = e[h]), + (d = {}), + (d[n.axis] = r || n.parse(a[h], h)), + l.push(zn(u, d, o, h)); + return l; + } + function Vn(t) { + return t && void 0 !== t.barStart && void 0 !== t.barEnd; + } + function Bn(t, e, i, s) { + let n = e.borderSkipped; + const o = {}; + if (!n) return void (t.borderSkipped = o); + if (!0 === n) + return void (t.borderSkipped = { + top: !0, + right: !0, + bottom: !0, + left: !0, + }); + const { + start: a, + end: r, + reverse: l, + top: h, + bottom: c, + } = (function (t) { + let e, i, s, n, o; + return ( + t.horizontal + ? ((e = t.base > t.x), (i = "left"), (s = "right")) + : ((e = t.base < t.y), (i = "bottom"), (s = "top")), + e ? ((n = "end"), (o = "start")) : ((n = "start"), (o = "end")), + { start: i, end: s, reverse: e, top: n, bottom: o } + ); + })(t); + "middle" === n && + i && + ((t.enableBorderRadius = !0), + (i._top || 0) === s + ? (n = h) + : (i._bottom || 0) === s + ? (n = c) + : ((o[Wn(c, a, r, l)] = !0), (n = h))), + (o[Wn(n, a, r, l)] = !0), + (t.borderSkipped = o); + } + function Wn(t, e, i, s) { + var n, o, a; + return ( + s + ? ((a = i), + (t = Nn((t = (n = t) === (o = e) ? a : n === a ? o : n), i, e))) + : (t = Nn(t, e, i)), + t + ); + } + function Nn(t, e, i) { + return "start" === t ? e : "end" === t ? i : t; + } + function Hn(t, { inflateAmount: e }, i) { + t.inflateAmount = "auto" === e ? (1 === i ? 0.33 : 0) : e; + } + class jn extends Ns { + static id = "doughnut"; + static defaults = { + datasetElementType: !1, + dataElementType: "arc", + animation: { animateRotate: !0, animateScale: !1 }, + animations: { + numbers: { + type: "number", + properties: [ + "circumference", + "endAngle", + "innerRadius", + "outerRadius", + "startAngle", + "x", + "y", + "offset", + "borderWidth", + "spacing", + ], + }, + }, + cutout: "50%", + rotation: 0, + circumference: 360, + radius: "100%", + spacing: 0, + indexAxis: "r", + }; + static descriptors = { + _scriptable: (t) => "spacing" !== t, + _indexable: (t) => + "spacing" !== t && + !t.startsWith("borderDash") && + !t.startsWith("hoverBorderDash"), + }; + static overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(t) { + const e = t.data; + if (e.labels.length && e.datasets.length) { + const { + labels: { pointStyle: i, color: s }, + } = t.legend.options; + return e.labels.map((e, n) => { + const o = t.getDatasetMeta(0).controller.getStyle(n); + return { + text: e, + fillStyle: o.backgroundColor, + strokeStyle: o.borderColor, + fontColor: s, + lineWidth: o.borderWidth, + pointStyle: i, + hidden: !t.getDataVisibility(n), + index: n, + }; + }); + } + return []; + }, + }, + onClick(t, e, i) { + i.chart.toggleDataVisibility(e.index), i.chart.update(); + }, + }, + }, + }; + constructor(t, e) { + super(t, e), + (this.enableOptionSharing = !0), + (this.innerRadius = void 0), + (this.outerRadius = void 0), + (this.offsetX = void 0), + (this.offsetY = void 0); + } + linkScales() {} + parse(t, e) { + const i = this.getDataset().data, + s = this._cachedMeta; + if (!1 === this._parsing) s._parsed = i; + else { + let n, + a, + r = (t) => +i[t]; + if (o(i[t])) { + const { key: t = "value" } = this._parsing; + r = (e) => +M(i[e], t); + } + for (n = t, a = t + e; n < a; ++n) s._parsed[n] = r(n); + } + } + _getRotation() { + return $(this.options.rotation - 90); + } + _getCircumference() { + return $(this.options.circumference); + } + _getRotationExtents() { + let t = O, + e = -O; + for (let i = 0; i < this.chart.data.datasets.length; ++i) + if ( + this.chart.isDatasetVisible(i) && + this.chart.getDatasetMeta(i).type === this._type + ) { + const s = this.chart.getDatasetMeta(i).controller, + n = s._getRotation(), + o = s._getCircumference(); + (t = Math.min(t, n)), (e = Math.max(e, n + o)); + } + return { rotation: t, circumference: e - t }; + } + update(t) { + const e = this.chart, + { chartArea: i } = e, + s = this._cachedMeta, + n = s.data, + o = + this.getMaxBorderWidth() + + this.getMaxOffset(n) + + this.options.spacing, + a = Math.max((Math.min(i.width, i.height) - o) / 2, 0), + r = Math.min(h(this.options.cutout, a), 1), + l = this._getRingWeight(this.index), + { circumference: d, rotation: u } = this._getRotationExtents(), + { + ratioX: f, + ratioY: g, + offsetX: p, + offsetY: m, + } = (function (t, e, i) { + let s = 1, + n = 1, + o = 0, + a = 0; + if (e < O) { + const r = t, + l = r + e, + h = Math.cos(r), + c = Math.sin(r), + d = Math.cos(l), + u = Math.sin(l), + f = (t, e, s) => + Z(t, r, l, !0) ? 1 : Math.max(e, e * i, s, s * i), + g = (t, e, s) => + Z(t, r, l, !0) ? -1 : Math.min(e, e * i, s, s * i), + p = f(0, h, d), + m = f(E, c, u), + x = g(C, h, d), + b = g(C + E, c, u); + (s = (p - x) / 2), + (n = (m - b) / 2), + (o = -(p + x) / 2), + (a = -(m + b) / 2); + } + return { ratioX: s, ratioY: n, offsetX: o, offsetY: a }; + })(u, d, r), + x = (i.width - o) / f, + b = (i.height - o) / g, + _ = Math.max(Math.min(x, b) / 2, 0), + y = c(this.options.radius, _), + v = (y - Math.max(y * r, 0)) / this._getVisibleDatasetWeightTotal(); + (this.offsetX = p * y), + (this.offsetY = m * y), + (s.total = this.calculateTotal()), + (this.outerRadius = y - v * this._getRingWeightOffset(this.index)), + (this.innerRadius = Math.max(this.outerRadius - v * l, 0)), + this.updateElements(n, 0, n.length, t); + } + _circumference(t, e) { + const i = this.options, + s = this._cachedMeta, + n = this._getCircumference(); + return (e && i.animation.animateRotate) || + !this.chart.getDataVisibility(t) || + null === s._parsed[t] || + s.data[t].hidden + ? 0 + : this.calculateCircumference((s._parsed[t] * n) / O); + } + updateElements(t, e, i, s) { + const n = "reset" === s, + o = this.chart, + a = o.chartArea, + r = o.options.animation, + l = (a.left + a.right) / 2, + h = (a.top + a.bottom) / 2, + c = n && r.animateScale, + d = c ? 0 : this.innerRadius, + u = c ? 0 : this.outerRadius, + { sharedOptions: f, includeOptions: g } = this._getSharedOptions(e, s); + let p, + m = this._getRotation(); + for (p = 0; p < e; ++p) m += this._circumference(p, n); + for (p = e; p < e + i; ++p) { + const e = this._circumference(p, n), + i = t[p], + o = { + x: l + this.offsetX, + y: h + this.offsetY, + startAngle: m, + endAngle: m + e, + circumference: e, + outerRadius: u, + innerRadius: d, + }; + g && + (o.options = + f || this.resolveDataElementOptions(p, i.active ? "active" : s)), + (m += e), + this.updateElement(i, p, o, s); + } + } + calculateTotal() { + const t = this._cachedMeta, + e = t.data; + let i, + s = 0; + for (i = 0; i < e.length; i++) { + const n = t._parsed[i]; + null === n || + isNaN(n) || + !this.chart.getDataVisibility(i) || + e[i].hidden || + (s += Math.abs(n)); + } + return s; + } + calculateCircumference(t) { + const e = this._cachedMeta.total; + return e > 0 && !isNaN(t) ? O * (Math.abs(t) / e) : 0; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart, + s = i.data.labels || [], + n = ne(e._parsed[t], i.options.locale); + return { label: s[t] || "", value: n }; + } + getMaxBorderWidth(t) { + let e = 0; + const i = this.chart; + let s, n, o, a, r; + if (!t) + for (s = 0, n = i.data.datasets.length; s < n; ++s) + if (i.isDatasetVisible(s)) { + (o = i.getDatasetMeta(s)), (t = o.data), (a = o.controller); + break; + } + if (!t) return 0; + for (s = 0, n = t.length; s < n; ++s) + (r = a.resolveDataElementOptions(s)), + "inner" !== r.borderAlign && + (e = Math.max(e, r.borderWidth || 0, r.hoverBorderWidth || 0)); + return e; + } + getMaxOffset(t) { + let e = 0; + for (let i = 0, s = t.length; i < s; ++i) { + const t = this.resolveDataElementOptions(i); + e = Math.max(e, t.offset || 0, t.hoverOffset || 0); + } + return e; + } + _getRingWeightOffset(t) { + let e = 0; + for (let i = 0; i < t; ++i) + this.chart.isDatasetVisible(i) && (e += this._getRingWeight(i)); + return e; + } + _getRingWeight(t) { + return Math.max(l(this.chart.data.datasets[t].weight, 1), 0); + } + _getVisibleDatasetWeightTotal() { + return this._getRingWeightOffset(this.chart.data.datasets.length) || 1; + } + } + class $n extends Ns { + static id = "polarArea"; + static defaults = { + dataElementType: "arc", + animation: { animateRotate: !0, animateScale: !0 }, + animations: { + numbers: { + type: "number", + properties: [ + "x", + "y", + "startAngle", + "endAngle", + "innerRadius", + "outerRadius", + ], + }, + }, + indexAxis: "r", + startAngle: 0, + }; + static overrides = { + aspectRatio: 1, + plugins: { + legend: { + labels: { + generateLabels(t) { + const e = t.data; + if (e.labels.length && e.datasets.length) { + const { + labels: { pointStyle: i, color: s }, + } = t.legend.options; + return e.labels.map((e, n) => { + const o = t.getDatasetMeta(0).controller.getStyle(n); + return { + text: e, + fillStyle: o.backgroundColor, + strokeStyle: o.borderColor, + fontColor: s, + lineWidth: o.borderWidth, + pointStyle: i, + hidden: !t.getDataVisibility(n), + index: n, + }; + }); + } + return []; + }, + }, + onClick(t, e, i) { + i.chart.toggleDataVisibility(e.index), i.chart.update(); + }, + }, + }, + scales: { + r: { + type: "radialLinear", + angleLines: { display: !1 }, + beginAtZero: !0, + grid: { circular: !0 }, + pointLabels: { display: !1 }, + startAngle: 0, + }, + }, + }; + constructor(t, e) { + super(t, e), (this.innerRadius = void 0), (this.outerRadius = void 0); + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart, + s = i.data.labels || [], + n = ne(e._parsed[t].r, i.options.locale); + return { label: s[t] || "", value: n }; + } + parseObjectData(t, e, i, s) { + return ii.bind(this)(t, e, i, s); + } + update(t) { + const e = this._cachedMeta.data; + this._updateRadius(), this.updateElements(e, 0, e.length, t); + } + getMinMax() { + const t = this._cachedMeta, + e = { min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY }; + return ( + t.data.forEach((t, i) => { + const s = this.getParsed(i).r; + !isNaN(s) && + this.chart.getDataVisibility(i) && + (s < e.min && (e.min = s), s > e.max && (e.max = s)); + }), + e + ); + } + _updateRadius() { + const t = this.chart, + e = t.chartArea, + i = t.options, + s = Math.min(e.right - e.left, e.bottom - e.top), + n = Math.max(s / 2, 0), + o = + (n - + Math.max( + i.cutoutPercentage ? (n / 100) * i.cutoutPercentage : 1, + 0 + )) / + t.getVisibleDatasetCount(); + (this.outerRadius = n - o * this.index), + (this.innerRadius = this.outerRadius - o); + } + updateElements(t, e, i, s) { + const n = "reset" === s, + o = this.chart, + a = o.options.animation, + r = this._cachedMeta.rScale, + l = r.xCenter, + h = r.yCenter, + c = r.getIndexAngle(0) - 0.5 * C; + let d, + u = c; + const f = 360 / this.countVisibleElements(); + for (d = 0; d < e; ++d) u += this._computeAngle(d, s, f); + for (d = e; d < e + i; d++) { + const e = t[d]; + let i = u, + g = u + this._computeAngle(d, s, f), + p = o.getDataVisibility(d) + ? r.getDistanceFromCenterForValue(this.getParsed(d).r) + : 0; + (u = g), + n && (a.animateScale && (p = 0), a.animateRotate && (i = g = c)); + const m = { + x: l, + y: h, + innerRadius: 0, + outerRadius: p, + startAngle: i, + endAngle: g, + options: this.resolveDataElementOptions(d, e.active ? "active" : s), + }; + this.updateElement(e, d, m, s); + } + } + countVisibleElements() { + const t = this._cachedMeta; + let e = 0; + return ( + t.data.forEach((t, i) => { + !isNaN(this.getParsed(i).r) && this.chart.getDataVisibility(i) && e++; + }), + e + ); + } + _computeAngle(t, e, i) { + return this.chart.getDataVisibility(t) + ? $(this.resolveDataElementOptions(t, e).angle || i) + : 0; + } + } + var Yn = Object.freeze({ + __proto__: null, + BarController: class extends Ns { + static id = "bar"; + static defaults = { + datasetElementType: !1, + dataElementType: "bar", + categoryPercentage: 0.8, + barPercentage: 0.9, + grouped: !0, + animations: { + numbers: { + type: "number", + properties: ["x", "y", "base", "width", "height"], + }, + }, + }; + static overrides = { + scales: { + _index_: { type: "category", offset: !0, grid: { offset: !0 } }, + _value_: { type: "linear", beginAtZero: !0 }, + }, + }; + parsePrimitiveData(t, e, i, s) { + return Fn(t, e, i, s); + } + parseArrayData(t, e, i, s) { + return Fn(t, e, i, s); + } + parseObjectData(t, e, i, s) { + const { iScale: n, vScale: o } = t, + { xAxisKey: a = "x", yAxisKey: r = "y" } = this._parsing, + l = "x" === n.axis ? a : r, + h = "x" === o.axis ? a : r, + c = []; + let d, u, f, g; + for (d = i, u = i + s; d < u; ++d) + (g = e[d]), + (f = {}), + (f[n.axis] = n.parse(M(g, l), d)), + c.push(zn(M(g, h), f, o, d)); + return c; + } + updateRangeFromParsed(t, e, i, s) { + super.updateRangeFromParsed(t, e, i, s); + const n = i._custom; + n && + e === this._cachedMeta.vScale && + ((t.min = Math.min(t.min, n.min)), (t.max = Math.max(t.max, n.max))); + } + getMaxOverflow() { + return 0; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + { iScale: i, vScale: s } = e, + n = this.getParsed(t), + o = n._custom, + a = Vn(o) + ? "[" + o.start + ", " + o.end + "]" + : "" + s.getLabelForValue(n[s.axis]); + return { label: "" + i.getLabelForValue(n[i.axis]), value: a }; + } + initialize() { + (this.enableOptionSharing = !0), super.initialize(); + this._cachedMeta.stack = this.getDataset().stack; + } + update(t) { + const e = this._cachedMeta; + this.updateElements(e.data, 0, e.data.length, t); + } + updateElements(t, e, i, n) { + const o = "reset" === n, + { + index: a, + _cachedMeta: { vScale: r }, + } = this, + l = r.getBasePixel(), + h = r.isHorizontal(), + c = this._getRuler(), + { sharedOptions: d, includeOptions: u } = this._getSharedOptions( + e, + n + ); + for (let f = e; f < e + i; f++) { + const e = this.getParsed(f), + i = + o || s(e[r.axis]) + ? { base: l, head: l } + : this._calculateBarValuePixels(f), + g = this._calculateBarIndexPixels(f, c), + p = (e._stacks || {})[r.axis], + m = { + horizontal: h, + base: i.base, + enableBorderRadius: + !p || Vn(e._custom) || a === p._top || a === p._bottom, + x: h ? i.head : g.center, + y: h ? g.center : i.head, + height: h ? g.size : Math.abs(i.size), + width: h ? Math.abs(i.size) : g.size, + }; + u && + (m.options = + d || + this.resolveDataElementOptions(f, t[f].active ? "active" : n)); + const x = m.options || t[f].options; + Bn(m, x, p, a), Hn(m, x, c.ratio), this.updateElement(t[f], f, m, n); + } + } + _getStacks(t, e) { + const { iScale: i } = this._cachedMeta, + n = i + .getMatchingVisibleMetas(this._type) + .filter((t) => t.controller.options.grouped), + o = i.options.stacked, + a = [], + r = this._cachedMeta.controller.getParsed(e), + l = r && r[i.axis], + h = (t) => { + const e = t._parsed.find((t) => t[i.axis] === l), + n = e && e[t.vScale.axis]; + if (s(n) || isNaN(n)) return !0; + }; + for (const i of n) + if ( + (void 0 === e || !h(i)) && + ((!1 === o || + -1 === a.indexOf(i.stack) || + (void 0 === o && void 0 === i.stack)) && + a.push(i.stack), + i.index === t) + ) + break; + return a.length || a.push(void 0), a; + } + _getStackCount(t) { + return this._getStacks(void 0, t).length; + } + _getStackIndex(t, e, i) { + const s = this._getStacks(t, i), + n = void 0 !== e ? s.indexOf(e) : -1; + return -1 === n ? s.length - 1 : n; + } + _getRuler() { + const t = this.options, + e = this._cachedMeta, + i = e.iScale, + s = []; + let n, o; + for (n = 0, o = e.data.length; n < o; ++n) + s.push(i.getPixelForValue(this.getParsed(n)[i.axis], n)); + const a = t.barThickness; + return { + min: a || In(e), + pixels: s, + start: i._startPixel, + end: i._endPixel, + stackCount: this._getStackCount(), + scale: i, + grouped: t.grouped, + ratio: a ? 1 : t.categoryPercentage * t.barPercentage, + }; + } + _calculateBarValuePixels(t) { + const { + _cachedMeta: { vScale: e, _stacked: i, index: n }, + options: { base: o, minBarLength: a }, + } = this, + r = o || 0, + l = this.getParsed(t), + h = l._custom, + c = Vn(h); + let d, + u, + f = l[e.axis], + g = 0, + p = i ? this.applyStack(e, l, i) : f; + p !== f && ((g = p - f), (p = f)), + c && + ((f = h.barStart), + (p = h.barEnd - h.barStart), + 0 !== f && F(f) !== F(h.barEnd) && (g = 0), + (g += f)); + const m = s(o) || c ? g : o; + let x = e.getPixelForValue(m); + if ( + ((d = this.chart.getDataVisibility(t) + ? e.getPixelForValue(g + p) + : x), + (u = d - x), + Math.abs(u) < a) + ) { + (u = + (function (t, e, i) { + return 0 !== t + ? F(t) + : (e.isHorizontal() ? 1 : -1) * (e.min >= i ? 1 : -1); + })(u, e, r) * a), + f === r && (x -= u / 2); + const t = e.getPixelForDecimal(0), + s = e.getPixelForDecimal(1), + o = Math.min(t, s), + h = Math.max(t, s); + (x = Math.max(Math.min(x, h), o)), + (d = x + u), + i && + !c && + (l._stacks[e.axis]._visualValues[n] = + e.getValueForPixel(d) - e.getValueForPixel(x)); + } + if (x === e.getPixelForValue(r)) { + const t = (F(u) * e.getLineWidthForValue(r)) / 2; + (x += t), (u -= t); + } + return { size: u, base: x, head: d, center: d + u / 2 }; + } + _calculateBarIndexPixels(t, e) { + const i = e.scale, + n = this.options, + o = n.skipNull, + a = l(n.maxBarThickness, 1 / 0); + let r, h; + if (e.grouped) { + const i = o ? this._getStackCount(t) : e.stackCount, + l = + "flex" === n.barThickness + ? (function (t, e, i, s) { + const n = e.pixels, + o = n[t]; + let a = t > 0 ? n[t - 1] : null, + r = t < n.length - 1 ? n[t + 1] : null; + const l = i.categoryPercentage; + null === a && + (a = o - (null === r ? e.end - e.start : r - o)), + null === r && (r = o + o - a); + const h = o - ((o - Math.min(a, r)) / 2) * l; + return { + chunk: ((Math.abs(r - a) / 2) * l) / s, + ratio: i.barPercentage, + start: h, + }; + })(t, e, n, i) + : (function (t, e, i, n) { + const o = i.barThickness; + let a, r; + return ( + s(o) + ? ((a = e.min * i.categoryPercentage), + (r = i.barPercentage)) + : ((a = o * n), (r = 1)), + { chunk: a / n, ratio: r, start: e.pixels[t] - a / 2 } + ); + })(t, e, n, i), + c = this._getStackIndex( + this.index, + this._cachedMeta.stack, + o ? t : void 0 + ); + (r = l.start + l.chunk * c + l.chunk / 2), + (h = Math.min(a, l.chunk * l.ratio)); + } else + (r = i.getPixelForValue(this.getParsed(t)[i.axis], t)), + (h = Math.min(a, e.min * e.ratio)); + return { base: r - h / 2, head: r + h / 2, center: r, size: h }; + } + draw() { + const t = this._cachedMeta, + e = t.vScale, + i = t.data, + s = i.length; + let n = 0; + for (; n < s; ++n) + null === this.getParsed(n)[e.axis] || + i[n].hidden || + i[n].draw(this._ctx); + } + }, + BubbleController: class extends Ns { + static id = "bubble"; + static defaults = { + datasetElementType: !1, + dataElementType: "point", + animations: { + numbers: { + type: "number", + properties: ["x", "y", "borderWidth", "radius"], + }, + }, + }; + static overrides = { + scales: { x: { type: "linear" }, y: { type: "linear" } }, + }; + initialize() { + (this.enableOptionSharing = !0), super.initialize(); + } + parsePrimitiveData(t, e, i, s) { + const n = super.parsePrimitiveData(t, e, i, s); + for (let t = 0; t < n.length; t++) + n[t]._custom = this.resolveDataElementOptions(t + i).radius; + return n; + } + parseArrayData(t, e, i, s) { + const n = super.parseArrayData(t, e, i, s); + for (let t = 0; t < n.length; t++) { + const s = e[i + t]; + n[t]._custom = l(s[2], this.resolveDataElementOptions(t + i).radius); + } + return n; + } + parseObjectData(t, e, i, s) { + const n = super.parseObjectData(t, e, i, s); + for (let t = 0; t < n.length; t++) { + const s = e[i + t]; + n[t]._custom = l( + s && s.r && +s.r, + this.resolveDataElementOptions(t + i).radius + ); + } + return n; + } + getMaxOverflow() { + const t = this._cachedMeta.data; + let e = 0; + for (let i = t.length - 1; i >= 0; --i) + e = Math.max(e, t[i].size(this.resolveDataElementOptions(i)) / 2); + return e > 0 && e; + } + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart.data.labels || [], + { xScale: s, yScale: n } = e, + o = this.getParsed(t), + a = s.getLabelForValue(o.x), + r = n.getLabelForValue(o.y), + l = o._custom; + return { + label: i[t] || "", + value: "(" + a + ", " + r + (l ? ", " + l : "") + ")", + }; + } + update(t) { + const e = this._cachedMeta.data; + this.updateElements(e, 0, e.length, t); + } + updateElements(t, e, i, s) { + const n = "reset" === s, + { iScale: o, vScale: a } = this._cachedMeta, + { sharedOptions: r, includeOptions: l } = this._getSharedOptions( + e, + s + ), + h = o.axis, + c = a.axis; + for (let d = e; d < e + i; d++) { + const e = t[d], + i = !n && this.getParsed(d), + u = {}, + f = (u[h] = n + ? o.getPixelForDecimal(0.5) + : o.getPixelForValue(i[h])), + g = (u[c] = n ? a.getBasePixel() : a.getPixelForValue(i[c])); + (u.skip = isNaN(f) || isNaN(g)), + l && + ((u.options = + r || + this.resolveDataElementOptions(d, e.active ? "active" : s)), + n && (u.options.radius = 0)), + this.updateElement(e, d, u, s); + } + } + resolveDataElementOptions(t, e) { + const i = this.getParsed(t); + let s = super.resolveDataElementOptions(t, e); + s.$shared && (s = Object.assign({}, s, { $shared: !1 })); + const n = s.radius; + return ( + "active" !== e && (s.radius = 0), + (s.radius += l(i && i._custom, n)), + s + ); + } + }, + DoughnutController: jn, + LineController: class extends Ns { + static id = "line"; + static defaults = { + datasetElementType: "line", + dataElementType: "point", + showLine: !0, + spanGaps: !1, + }; + static overrides = { + scales: { _index_: { type: "category" }, _value_: { type: "linear" } }, + }; + initialize() { + (this.enableOptionSharing = !0), + (this.supportsDecimation = !0), + super.initialize(); + } + update(t) { + const e = this._cachedMeta, + { dataset: i, data: s = [], _dataset: n } = e, + o = this.chart._animationsDisabled; + let { start: a, count: r } = pt(e, s, o); + (this._drawStart = a), + (this._drawCount = r), + mt(e) && ((a = 0), (r = s.length)), + (i._chart = this.chart), + (i._datasetIndex = this.index), + (i._decimated = !!n._decimated), + (i.points = s); + const l = this.resolveDatasetElementOptions(t); + this.options.showLine || (l.borderWidth = 0), + (l.segment = this.options.segment), + this.updateElement(i, void 0, { animated: !o, options: l }, t), + this.updateElements(s, a, r, t); + } + updateElements(t, e, i, n) { + const o = "reset" === n, + { iScale: a, vScale: r, _stacked: l, _dataset: h } = this._cachedMeta, + { sharedOptions: c, includeOptions: d } = this._getSharedOptions( + e, + n + ), + u = a.axis, + f = r.axis, + { spanGaps: g, segment: p } = this.options, + m = N(g) ? g : Number.POSITIVE_INFINITY, + x = this.chart._animationsDisabled || o || "none" === n, + b = e + i, + _ = t.length; + let y = e > 0 && this.getParsed(e - 1); + for (let i = 0; i < _; ++i) { + const g = t[i], + _ = x ? g : {}; + if (i < e || i >= b) { + _.skip = !0; + continue; + } + const v = this.getParsed(i), + M = s(v[f]), + w = (_[u] = a.getPixelForValue(v[u], i)), + k = (_[f] = + o || M + ? r.getBasePixel() + : r.getPixelForValue(l ? this.applyStack(r, v, l) : v[f], i)); + (_.skip = isNaN(w) || isNaN(k) || M), + (_.stop = i > 0 && Math.abs(v[u] - y[u]) > m), + p && ((_.parsed = v), (_.raw = h.data[i])), + d && + (_.options = + c || + this.resolveDataElementOptions(i, g.active ? "active" : n)), + x || this.updateElement(g, i, _, n), + (y = v); + } + } + getMaxOverflow() { + const t = this._cachedMeta, + e = t.dataset, + i = (e.options && e.options.borderWidth) || 0, + s = t.data || []; + if (!s.length) return i; + const n = s[0].size(this.resolveDataElementOptions(0)), + o = s[s.length - 1].size( + this.resolveDataElementOptions(s.length - 1) + ); + return Math.max(i, n, o) / 2; + } + draw() { + const t = this._cachedMeta; + t.dataset.updateControlPoints(this.chart.chartArea, t.iScale.axis), + super.draw(); + } + }, + PieController: class extends jn { + static id = "pie"; + static defaults = { + cutout: 0, + rotation: 0, + circumference: 360, + radius: "100%", + }; + }, + PolarAreaController: $n, + RadarController: class extends Ns { + static id = "radar"; + static defaults = { + datasetElementType: "line", + dataElementType: "point", + indexAxis: "r", + showLine: !0, + elements: { line: { fill: "start" } }, + }; + static overrides = { + aspectRatio: 1, + scales: { r: { type: "radialLinear" } }, + }; + getLabelAndValue(t) { + const e = this._cachedMeta.vScale, + i = this.getParsed(t); + return { + label: e.getLabels()[t], + value: "" + e.getLabelForValue(i[e.axis]), + }; + } + parseObjectData(t, e, i, s) { + return ii.bind(this)(t, e, i, s); + } + update(t) { + const e = this._cachedMeta, + i = e.dataset, + s = e.data || [], + n = e.iScale.getLabels(); + if (((i.points = s), "resize" !== t)) { + const e = this.resolveDatasetElementOptions(t); + this.options.showLine || (e.borderWidth = 0); + const o = { _loop: !0, _fullLoop: n.length === s.length, options: e }; + this.updateElement(i, void 0, o, t); + } + this.updateElements(s, 0, s.length, t); + } + updateElements(t, e, i, s) { + const n = this._cachedMeta.rScale, + o = "reset" === s; + for (let a = e; a < e + i; a++) { + const e = t[a], + i = this.resolveDataElementOptions(a, e.active ? "active" : s), + r = n.getPointPositionForValue(a, this.getParsed(a).r), + l = o ? n.xCenter : r.x, + h = o ? n.yCenter : r.y, + c = { + x: l, + y: h, + angle: r.angle, + skip: isNaN(l) || isNaN(h), + options: i, + }; + this.updateElement(e, a, c, s); + } + } + }, + ScatterController: class extends Ns { + static id = "scatter"; + static defaults = { + datasetElementType: !1, + dataElementType: "point", + showLine: !1, + fill: !1, + }; + static overrides = { + interaction: { mode: "point" }, + scales: { x: { type: "linear" }, y: { type: "linear" } }, + }; + getLabelAndValue(t) { + const e = this._cachedMeta, + i = this.chart.data.labels || [], + { xScale: s, yScale: n } = e, + o = this.getParsed(t), + a = s.getLabelForValue(o.x), + r = n.getLabelForValue(o.y); + return { label: i[t] || "", value: "(" + a + ", " + r + ")" }; + } + update(t) { + const e = this._cachedMeta, + { data: i = [] } = e, + s = this.chart._animationsDisabled; + let { start: n, count: o } = pt(e, i, s); + if ( + ((this._drawStart = n), + (this._drawCount = o), + mt(e) && ((n = 0), (o = i.length)), + this.options.showLine) + ) { + this.datasetElementType || this.addElements(); + const { dataset: n, _dataset: o } = e; + (n._chart = this.chart), + (n._datasetIndex = this.index), + (n._decimated = !!o._decimated), + (n.points = i); + const a = this.resolveDatasetElementOptions(t); + (a.segment = this.options.segment), + this.updateElement(n, void 0, { animated: !s, options: a }, t); + } else + this.datasetElementType && + (delete e.dataset, (this.datasetElementType = !1)); + this.updateElements(i, n, o, t); + } + addElements() { + const { showLine: t } = this.options; + !this.datasetElementType && + t && + (this.datasetElementType = this.chart.registry.getElement("line")), + super.addElements(); + } + updateElements(t, e, i, n) { + const o = "reset" === n, + { iScale: a, vScale: r, _stacked: l, _dataset: h } = this._cachedMeta, + c = this.resolveDataElementOptions(e, n), + d = this.getSharedOptions(c), + u = this.includeOptions(n, d), + f = a.axis, + g = r.axis, + { spanGaps: p, segment: m } = this.options, + x = N(p) ? p : Number.POSITIVE_INFINITY, + b = this.chart._animationsDisabled || o || "none" === n; + let _ = e > 0 && this.getParsed(e - 1); + for (let c = e; c < e + i; ++c) { + const e = t[c], + i = this.getParsed(c), + p = b ? e : {}, + y = s(i[g]), + v = (p[f] = a.getPixelForValue(i[f], c)), + M = (p[g] = + o || y + ? r.getBasePixel() + : r.getPixelForValue(l ? this.applyStack(r, i, l) : i[g], c)); + (p.skip = isNaN(v) || isNaN(M) || y), + (p.stop = c > 0 && Math.abs(i[f] - _[f]) > x), + m && ((p.parsed = i), (p.raw = h.data[c])), + u && + (p.options = + d || + this.resolveDataElementOptions(c, e.active ? "active" : n)), + b || this.updateElement(e, c, p, n), + (_ = i); + } + this.updateSharedOptions(d, n, c); + } + getMaxOverflow() { + const t = this._cachedMeta, + e = t.data || []; + if (!this.options.showLine) { + let t = 0; + for (let i = e.length - 1; i >= 0; --i) + t = Math.max(t, e[i].size(this.resolveDataElementOptions(i)) / 2); + return t > 0 && t; + } + const i = t.dataset, + s = (i.options && i.options.borderWidth) || 0; + if (!e.length) return s; + const n = e[0].size(this.resolveDataElementOptions(0)), + o = e[e.length - 1].size( + this.resolveDataElementOptions(e.length - 1) + ); + return Math.max(s, n, o) / 2; + } + }, + }); + function Un(t, e, i, s) { + const n = vi(t.options.borderRadius, [ + "outerStart", + "outerEnd", + "innerStart", + "innerEnd", + ]); + const o = (i - e) / 2, + a = Math.min(o, (s * e) / 2), + r = (t) => { + const e = ((i - Math.min(o, t)) * s) / 2; + return J(t, 0, Math.min(o, e)); + }; + return { + outerStart: r(n.outerStart), + outerEnd: r(n.outerEnd), + innerStart: J(n.innerStart, 0, a), + innerEnd: J(n.innerEnd, 0, a), + }; + } + function Xn(t, e, i, s) { + return { x: i + t * Math.cos(e), y: s + t * Math.sin(e) }; + } + function qn(t, e, i, s, n, o) { + const { x: a, y: r, startAngle: l, pixelMargin: h, innerRadius: c } = e, + d = Math.max(e.outerRadius + s + i - h, 0), + u = c > 0 ? c + s + i + h : 0; + let f = 0; + const g = n - l; + if (s) { + const t = ((c > 0 ? c - s : 0) + (d > 0 ? d - s : 0)) / 2; + f = (g - (0 !== t ? (g * t) / (t + s) : g)) / 2; + } + const p = (g - Math.max(0.001, g * d - i / C) / d) / 2, + m = l + p + f, + x = n - p - f, + { + outerStart: b, + outerEnd: _, + innerStart: y, + innerEnd: v, + } = Un(e, u, d, x - m), + M = d - b, + w = d - _, + k = m + b / M, + S = x - _ / w, + P = u + y, + D = u + v, + O = m + y / P, + A = x - v / D; + if ((t.beginPath(), o)) { + const e = (k + S) / 2; + if ((t.arc(a, r, d, k, e), t.arc(a, r, d, e, S), _ > 0)) { + const e = Xn(w, S, a, r); + t.arc(e.x, e.y, _, S, x + E); + } + const i = Xn(D, x, a, r); + if ((t.lineTo(i.x, i.y), v > 0)) { + const e = Xn(D, A, a, r); + t.arc(e.x, e.y, v, x + E, A + Math.PI); + } + const s = (x - v / u + (m + y / u)) / 2; + if ( + (t.arc(a, r, u, x - v / u, s, !0), + t.arc(a, r, u, s, m + y / u, !0), + y > 0) + ) { + const e = Xn(P, O, a, r); + t.arc(e.x, e.y, y, O + Math.PI, m - E); + } + const n = Xn(M, m, a, r); + if ((t.lineTo(n.x, n.y), b > 0)) { + const e = Xn(M, k, a, r); + t.arc(e.x, e.y, b, m - E, k); + } + } else { + t.moveTo(a, r); + const e = Math.cos(k) * d + a, + i = Math.sin(k) * d + r; + t.lineTo(e, i); + const s = Math.cos(S) * d + a, + n = Math.sin(S) * d + r; + t.lineTo(s, n); + } + t.closePath(); + } + function Kn(t, e, i, s, n) { + const { fullCircles: o, startAngle: a, circumference: r, options: l } = e, + { + borderWidth: h, + borderJoinStyle: c, + borderDash: d, + borderDashOffset: u, + } = l, + f = "inner" === l.borderAlign; + if (!h) return; + t.setLineDash(d || []), + (t.lineDashOffset = u), + f + ? ((t.lineWidth = 2 * h), (t.lineJoin = c || "round")) + : ((t.lineWidth = h), (t.lineJoin = c || "bevel")); + let g = e.endAngle; + if (o) { + qn(t, e, i, s, g, n); + for (let e = 0; e < o; ++e) t.stroke(); + isNaN(r) || (g = a + (r % O || O)); + } + f && + (function (t, e, i) { + const { + startAngle: s, + pixelMargin: n, + x: o, + y: a, + outerRadius: r, + innerRadius: l, + } = e; + let h = n / r; + t.beginPath(), + t.arc(o, a, r, s - h, i + h), + l > n + ? ((h = n / l), t.arc(o, a, l, i + h, s - h, !0)) + : t.arc(o, a, n, i + E, s - E), + t.closePath(), + t.clip(); + })(t, e, g), + o || (qn(t, e, i, s, g, n), t.stroke()); + } + function Gn(t, e, i = e) { + (t.lineCap = l(i.borderCapStyle, e.borderCapStyle)), + t.setLineDash(l(i.borderDash, e.borderDash)), + (t.lineDashOffset = l(i.borderDashOffset, e.borderDashOffset)), + (t.lineJoin = l(i.borderJoinStyle, e.borderJoinStyle)), + (t.lineWidth = l(i.borderWidth, e.borderWidth)), + (t.strokeStyle = l(i.borderColor, e.borderColor)); + } + function Zn(t, e, i) { + t.lineTo(i.x, i.y); + } + function Jn(t, e, i = {}) { + const s = t.length, + { start: n = 0, end: o = s - 1 } = i, + { start: a, end: r } = e, + l = Math.max(n, a), + h = Math.min(o, r), + c = (n < a && o < a) || (n > r && o > r); + return { + count: s, + start: l, + loop: e.loop, + ilen: h < l && !c ? s + h - l : h - l, + }; + } + function Qn(t, e, i, s) { + const { points: n, options: o } = e, + { count: a, start: r, loop: l, ilen: h } = Jn(n, i, s), + c = (function (t) { + return t.stepped + ? Fe + : t.tension || "monotone" === t.cubicInterpolationMode + ? Ve + : Zn; + })(o); + let d, + u, + f, + { move: g = !0, reverse: p } = s || {}; + for (d = 0; d <= h; ++d) + (u = n[(r + (p ? h - d : d)) % a]), + u.skip || + (g ? (t.moveTo(u.x, u.y), (g = !1)) : c(t, f, u, p, o.stepped), + (f = u)); + return l && ((u = n[(r + (p ? h : 0)) % a]), c(t, f, u, p, o.stepped)), !!l; + } + function to(t, e, i, s) { + const n = e.points, + { count: o, start: a, ilen: r } = Jn(n, i, s), + { move: l = !0, reverse: h } = s || {}; + let c, + d, + u, + f, + g, + p, + m = 0, + x = 0; + const b = (t) => (a + (h ? r - t : t)) % o, + _ = () => { + f !== g && (t.lineTo(m, g), t.lineTo(m, f), t.lineTo(m, p)); + }; + for (l && ((d = n[b(0)]), t.moveTo(d.x, d.y)), c = 0; c <= r; ++c) { + if (((d = n[b(c)]), d.skip)) continue; + const e = d.x, + i = d.y, + s = 0 | e; + s === u + ? (i < f ? (f = i) : i > g && (g = i), (m = (x * m + e) / ++x)) + : (_(), t.lineTo(e, i), (u = s), (x = 0), (f = g = i)), + (p = i); + } + _(); + } + function eo(t) { + const e = t.options, + i = e.borderDash && e.borderDash.length; + return !( + t._decimated || + t._loop || + e.tension || + "monotone" === e.cubicInterpolationMode || + e.stepped || + i + ) + ? to + : Qn; + } + const io = "function" == typeof Path2D; + function so(t, e, i, s) { + io && !e.options.segment + ? (function (t, e, i, s) { + let n = e._path; + n || ((n = e._path = new Path2D()), e.path(n, i, s) && n.closePath()), + Gn(t, e.options), + t.stroke(n); + })(t, e, i, s) + : (function (t, e, i, s) { + const { segments: n, options: o } = e, + a = eo(e); + for (const r of n) + Gn(t, o, r.style), + t.beginPath(), + a(t, e, r, { start: i, end: i + s - 1 }) && t.closePath(), + t.stroke(); + })(t, e, i, s); + } + class no extends Hs { + static id = "line"; + static defaults = { + borderCapStyle: "butt", + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: "miter", + borderWidth: 3, + capBezierPoints: !0, + cubicInterpolationMode: "default", + fill: !1, + spanGaps: !1, + stepped: !1, + tension: 0, + }; + static defaultRoutes = { + backgroundColor: "backgroundColor", + borderColor: "borderColor", + }; + static descriptors = { + _scriptable: !0, + _indexable: (t) => "borderDash" !== t && "fill" !== t, + }; + constructor(t) { + super(), + (this.animated = !0), + (this.options = void 0), + (this._chart = void 0), + (this._loop = void 0), + (this._fullLoop = void 0), + (this._path = void 0), + (this._points = void 0), + (this._segments = void 0), + (this._decimated = !1), + (this._pointsUpdated = !1), + (this._datasetIndex = void 0), + t && Object.assign(this, t); + } + updateControlPoints(t, e) { + const i = this.options; + if ( + (i.tension || "monotone" === i.cubicInterpolationMode) && + !i.stepped && + !this._pointsUpdated + ) { + const s = i.spanGaps ? this._loop : this._fullLoop; + hi(this._points, i, t, s, e), (this._pointsUpdated = !0); + } + } + set points(t) { + (this._points = t), + delete this._segments, + delete this._path, + (this._pointsUpdated = !1); + } + get points() { + return this._points; + } + get segments() { + return ( + this._segments || (this._segments = zi(this, this.options.segment)) + ); + } + first() { + const t = this.segments, + e = this.points; + return t.length && e[t[0].start]; + } + last() { + const t = this.segments, + e = this.points, + i = t.length; + return i && e[t[i - 1].end]; + } + interpolate(t, e) { + const i = this.options, + s = t[e], + n = this.points, + o = Ii(this, { property: e, start: s, end: s }); + if (!o.length) return; + const a = [], + r = (function (t) { + return t.stepped + ? pi + : t.tension || "monotone" === t.cubicInterpolationMode + ? mi + : gi; + })(i); + let l, h; + for (l = 0, h = o.length; l < h; ++l) { + const { start: h, end: c } = o[l], + d = n[h], + u = n[c]; + if (d === u) { + a.push(d); + continue; + } + const f = r(d, u, Math.abs((s - d[e]) / (u[e] - d[e])), i.stepped); + (f[e] = t[e]), a.push(f); + } + return 1 === a.length ? a[0] : a; + } + pathSegment(t, e, i) { + return eo(this)(t, this, e, i); + } + path(t, e, i) { + const s = this.segments, + n = eo(this); + let o = this._loop; + (e = e || 0), (i = i || this.points.length - e); + for (const a of s) o &= n(t, this, a, { start: e, end: e + i - 1 }); + return !!o; + } + draw(t, e, i, s) { + const n = this.options || {}; + (this.points || []).length && + n.borderWidth && + (t.save(), so(t, this, i, s), t.restore()), + this.animated && ((this._pointsUpdated = !1), (this._path = void 0)); + } + } + function oo(t, e, i, s) { + const n = t.options, + { [i]: o } = t.getProps([i], s); + return Math.abs(e - o) < n.radius + n.hitRadius; + } + function ao(t, e) { + const { + x: i, + y: s, + base: n, + width: o, + height: a, + } = t.getProps(["x", "y", "base", "width", "height"], e); + let r, l, h, c, d; + return ( + t.horizontal + ? ((d = a / 2), + (r = Math.min(i, n)), + (l = Math.max(i, n)), + (h = s - d), + (c = s + d)) + : ((d = o / 2), + (r = i - d), + (l = i + d), + (h = Math.min(s, n)), + (c = Math.max(s, n))), + { left: r, top: h, right: l, bottom: c } + ); + } + function ro(t, e, i, s) { + return t ? 0 : J(e, i, s); + } + function lo(t) { + const e = ao(t), + i = e.right - e.left, + s = e.bottom - e.top, + n = (function (t, e, i) { + const s = t.options.borderWidth, + n = t.borderSkipped, + o = Mi(s); + return { + t: ro(n.top, o.top, 0, i), + r: ro(n.right, o.right, 0, e), + b: ro(n.bottom, o.bottom, 0, i), + l: ro(n.left, o.left, 0, e), + }; + })(t, i / 2, s / 2), + a = (function (t, e, i) { + const { enableBorderRadius: s } = t.getProps(["enableBorderRadius"]), + n = t.options.borderRadius, + a = wi(n), + r = Math.min(e, i), + l = t.borderSkipped, + h = s || o(n); + return { + topLeft: ro(!h || l.top || l.left, a.topLeft, 0, r), + topRight: ro(!h || l.top || l.right, a.topRight, 0, r), + bottomLeft: ro(!h || l.bottom || l.left, a.bottomLeft, 0, r), + bottomRight: ro(!h || l.bottom || l.right, a.bottomRight, 0, r), + }; + })(t, i / 2, s / 2); + return { + outer: { x: e.left, y: e.top, w: i, h: s, radius: a }, + inner: { + x: e.left + n.l, + y: e.top + n.t, + w: i - n.l - n.r, + h: s - n.t - n.b, + radius: { + topLeft: Math.max(0, a.topLeft - Math.max(n.t, n.l)), + topRight: Math.max(0, a.topRight - Math.max(n.t, n.r)), + bottomLeft: Math.max(0, a.bottomLeft - Math.max(n.b, n.l)), + bottomRight: Math.max(0, a.bottomRight - Math.max(n.b, n.r)), + }, + }, + }; + } + function ho(t, e, i, s) { + const n = null === e, + o = null === i, + a = t && !(n && o) && ao(t, s); + return a && (n || tt(e, a.left, a.right)) && (o || tt(i, a.top, a.bottom)); + } + function co(t, e) { + t.rect(e.x, e.y, e.w, e.h); + } + function uo(t, e, i = {}) { + const s = t.x !== i.x ? -e : 0, + n = t.y !== i.y ? -e : 0, + o = (t.x + t.w !== i.x + i.w ? e : 0) - s, + a = (t.y + t.h !== i.y + i.h ? e : 0) - n; + return { x: t.x + s, y: t.y + n, w: t.w + o, h: t.h + a, radius: t.radius }; + } + var fo = Object.freeze({ + __proto__: null, + ArcElement: class extends Hs { + static id = "arc"; + static defaults = { + borderAlign: "center", + borderColor: "#fff", + borderDash: [], + borderDashOffset: 0, + borderJoinStyle: void 0, + borderRadius: 0, + borderWidth: 2, + offset: 0, + spacing: 0, + angle: void 0, + circular: !0, + }; + static defaultRoutes = { backgroundColor: "backgroundColor" }; + static descriptors = { + _scriptable: !0, + _indexable: (t) => "borderDash" !== t, + }; + circumference; + endAngle; + fullCircles; + innerRadius; + outerRadius; + pixelMargin; + startAngle; + constructor(t) { + super(), + (this.options = void 0), + (this.circumference = void 0), + (this.startAngle = void 0), + (this.endAngle = void 0), + (this.innerRadius = void 0), + (this.outerRadius = void 0), + (this.pixelMargin = 0), + (this.fullCircles = 0), + t && Object.assign(this, t); + } + inRange(t, e, i) { + const s = this.getProps(["x", "y"], i), + { angle: n, distance: o } = X(s, { x: t, y: e }), + { + startAngle: a, + endAngle: r, + innerRadius: h, + outerRadius: c, + circumference: d, + } = this.getProps( + [ + "startAngle", + "endAngle", + "innerRadius", + "outerRadius", + "circumference", + ], + i + ), + u = (this.options.spacing + this.options.borderWidth) / 2, + f = l(d, r - a), + g = Z(n, a, r) && a !== r, + p = f >= O || g, + m = tt(o, h + u, c + u); + return p && m; + } + getCenterPoint(t) { + const { + x: e, + y: i, + startAngle: s, + endAngle: n, + innerRadius: o, + outerRadius: a, + } = this.getProps( + ["x", "y", "startAngle", "endAngle", "innerRadius", "outerRadius"], + t + ), + { offset: r, spacing: l } = this.options, + h = (s + n) / 2, + c = (o + a + l + r) / 2; + return { x: e + Math.cos(h) * c, y: i + Math.sin(h) * c }; + } + tooltipPosition(t) { + return this.getCenterPoint(t); + } + draw(t) { + const { options: e, circumference: i } = this, + s = (e.offset || 0) / 4, + n = (e.spacing || 0) / 2, + o = e.circular; + if ( + ((this.pixelMargin = "inner" === e.borderAlign ? 0.33 : 0), + (this.fullCircles = i > O ? Math.floor(i / O) : 0), + 0 === i || this.innerRadius < 0 || this.outerRadius < 0) + ) + return; + t.save(); + const a = (this.startAngle + this.endAngle) / 2; + t.translate(Math.cos(a) * s, Math.sin(a) * s); + const r = s * (1 - Math.sin(Math.min(C, i || 0))); + (t.fillStyle = e.backgroundColor), + (t.strokeStyle = e.borderColor), + (function (t, e, i, s, n) { + const { fullCircles: o, startAngle: a, circumference: r } = e; + let l = e.endAngle; + if (o) { + qn(t, e, i, s, l, n); + for (let e = 0; e < o; ++e) t.fill(); + isNaN(r) || (l = a + (r % O || O)); + } + qn(t, e, i, s, l, n), t.fill(); + })(t, this, r, n, o), + Kn(t, this, r, n, o), + t.restore(); + } + }, + BarElement: class extends Hs { + static id = "bar"; + static defaults = { + borderSkipped: "start", + borderWidth: 0, + borderRadius: 0, + inflateAmount: "auto", + pointStyle: void 0, + }; + static defaultRoutes = { + backgroundColor: "backgroundColor", + borderColor: "borderColor", + }; + constructor(t) { + super(), + (this.options = void 0), + (this.horizontal = void 0), + (this.base = void 0), + (this.width = void 0), + (this.height = void 0), + (this.inflateAmount = void 0), + t && Object.assign(this, t); + } + draw(t) { + const { + inflateAmount: e, + options: { borderColor: i, backgroundColor: s }, + } = this, + { inner: n, outer: o } = lo(this), + a = + (r = o.radius).topLeft || + r.topRight || + r.bottomLeft || + r.bottomRight + ? He + : co; + var r; + t.save(), + (o.w === n.w && o.h === n.h) || + (t.beginPath(), + a(t, uo(o, e, n)), + t.clip(), + a(t, uo(n, -e, o)), + (t.fillStyle = i), + t.fill("evenodd")), + t.beginPath(), + a(t, uo(n, e)), + (t.fillStyle = s), + t.fill(), + t.restore(); + } + inRange(t, e, i) { + return ho(this, t, e, i); + } + inXRange(t, e) { + return ho(this, t, null, e); + } + inYRange(t, e) { + return ho(this, null, t, e); + } + getCenterPoint(t) { + const { + x: e, + y: i, + base: s, + horizontal: n, + } = this.getProps(["x", "y", "base", "horizontal"], t); + return { x: n ? (e + s) / 2 : e, y: n ? i : (i + s) / 2 }; + } + getRange(t) { + return "x" === t ? this.width / 2 : this.height / 2; + } + }, + LineElement: no, + PointElement: class extends Hs { + static id = "point"; + parsed; + skip; + stop; + static defaults = { + borderWidth: 1, + hitRadius: 1, + hoverBorderWidth: 1, + hoverRadius: 4, + pointStyle: "circle", + radius: 3, + rotation: 0, + }; + static defaultRoutes = { + backgroundColor: "backgroundColor", + borderColor: "borderColor", + }; + constructor(t) { + super(), + (this.options = void 0), + (this.parsed = void 0), + (this.skip = void 0), + (this.stop = void 0), + t && Object.assign(this, t); + } + inRange(t, e, i) { + const s = this.options, + { x: n, y: o } = this.getProps(["x", "y"], i); + return ( + Math.pow(t - n, 2) + Math.pow(e - o, 2) < + Math.pow(s.hitRadius + s.radius, 2) + ); + } + inXRange(t, e) { + return oo(this, t, "x", e); + } + inYRange(t, e) { + return oo(this, t, "y", e); + } + getCenterPoint(t) { + const { x: e, y: i } = this.getProps(["x", "y"], t); + return { x: e, y: i }; + } + size(t) { + let e = (t = t || this.options || {}).radius || 0; + e = Math.max(e, (e && t.hoverRadius) || 0); + return 2 * (e + ((e && t.borderWidth) || 0)); + } + draw(t, e) { + const i = this.options; + this.skip || + i.radius < 0.1 || + !Re(this, e, this.size(i) / 2) || + ((t.strokeStyle = i.borderColor), + (t.lineWidth = i.borderWidth), + (t.fillStyle = i.backgroundColor), + Le(t, i, this.x, this.y)); + } + getRange() { + const t = this.options || {}; + return t.radius + t.hitRadius; + } + }, + }); + function go(t, e, i, s) { + const n = t.indexOf(e); + if (-1 === n) + return ((t, e, i, s) => ( + "string" == typeof e + ? ((i = t.push(e) - 1), s.unshift({ index: i, label: e })) + : isNaN(e) && (i = null), + i + ))(t, e, i, s); + return n !== t.lastIndexOf(e) ? i : n; + } + function po(t) { + const e = this.getLabels(); + return t >= 0 && t < e.length ? e[t] : t; + } + function mo(t, e, { horizontal: i, minRotation: s }) { + const n = $(s), + o = (i ? Math.sin(n) : Math.cos(n)) || 0.001, + a = 0.75 * e * ("" + t).length; + return Math.min(e / o, a); + } + class xo extends Js { + constructor(t) { + super(t), + (this.start = void 0), + (this.end = void 0), + (this._startValue = void 0), + (this._endValue = void 0), + (this._valueRange = 0); + } + parse(t, e) { + return s(t) || + (("number" == typeof t || t instanceof Number) && !isFinite(+t)) + ? null + : +t; + } + handleTickRangeOptions() { + const { beginAtZero: t } = this.options, + { minDefined: e, maxDefined: i } = this.getUserBounds(); + let { min: s, max: n } = this; + const o = (t) => (s = e ? s : t), + a = (t) => (n = i ? n : t); + if (t) { + const t = F(s), + e = F(n); + t < 0 && e < 0 ? a(0) : t > 0 && e > 0 && o(0); + } + if (s === n) { + let e = 0 === n ? 1 : Math.abs(0.05 * n); + a(n + e), t || o(s - e); + } + (this.min = s), (this.max = n); + } + getTickLimit() { + const t = this.options.ticks; + let e, + { maxTicksLimit: i, stepSize: s } = t; + return ( + s + ? ((e = Math.ceil(this.max / s) - Math.floor(this.min / s) + 1), + e > 1e3 && + (console.warn( + `scales.${this.id}.ticks.stepSize: ${s} would result generating up to ${e} ticks. Limiting to 1000.` + ), + (e = 1e3))) + : ((e = this.computeTickLimit()), (i = i || 11)), + i && (e = Math.min(i, e)), + e + ); + } + computeTickLimit() { + return Number.POSITIVE_INFINITY; + } + buildTicks() { + const t = this.options, + e = t.ticks; + let i = this.getTickLimit(); + i = Math.max(2, i); + const n = (function (t, e) { + const i = [], + { + bounds: n, + step: o, + min: a, + max: r, + precision: l, + count: h, + maxTicks: c, + maxDigits: d, + includeBounds: u, + } = t, + f = o || 1, + g = c - 1, + { min: p, max: m } = e, + x = !s(a), + b = !s(r), + _ = !s(h), + y = (m - p) / (d + 1); + let v, + M, + w, + k, + S = B((m - p) / g / f) * f; + if (S < 1e-14 && !x && !b) return [{ value: p }, { value: m }]; + (k = Math.ceil(m / S) - Math.floor(p / S)), + k > g && (S = B((k * S) / g / f) * f), + s(l) || ((v = Math.pow(10, l)), (S = Math.ceil(S * v) / v)), + "ticks" === n + ? ((M = Math.floor(p / S) * S), (w = Math.ceil(m / S) * S)) + : ((M = p), (w = m)), + x && b && o && H((r - a) / o, S / 1e3) + ? ((k = Math.round(Math.min((r - a) / S, c))), + (S = (r - a) / k), + (M = a), + (w = r)) + : _ + ? ((M = x ? a : M), (w = b ? r : w), (k = h - 1), (S = (w - M) / k)) + : ((k = (w - M) / S), + (k = V(k, Math.round(k), S / 1e3) + ? Math.round(k) + : Math.ceil(k))); + const P = Math.max(U(S), U(M)); + (v = Math.pow(10, s(l) ? P : l)), + (M = Math.round(M * v) / v), + (w = Math.round(w * v) / v); + let D = 0; + for ( + x && + (u && M !== a + ? (i.push({ value: a }), + M < a && D++, + V(Math.round((M + D * S) * v) / v, a, mo(a, y, t)) && D++) + : M < a && D++); + D < k; + ++D + ) { + const t = Math.round((M + D * S) * v) / v; + if (b && t > r) break; + i.push({ value: t }); + } + return ( + b && u && w !== r + ? i.length && V(i[i.length - 1].value, r, mo(r, y, t)) + ? (i[i.length - 1].value = r) + : i.push({ value: r }) + : (b && w !== r) || i.push({ value: w }), + i + ); + })( + { + maxTicks: i, + bounds: t.bounds, + min: t.min, + max: t.max, + precision: e.precision, + step: e.stepSize, + count: e.count, + maxDigits: this._maxDigits(), + horizontal: this.isHorizontal(), + minRotation: e.minRotation || 0, + includeBounds: !1 !== e.includeBounds, + }, + this._range || this + ); + return ( + "ticks" === t.bounds && j(n, this, "value"), + t.reverse + ? (n.reverse(), (this.start = this.max), (this.end = this.min)) + : ((this.start = this.min), (this.end = this.max)), + n + ); + } + configure() { + const t = this.ticks; + let e = this.min, + i = this.max; + if ((super.configure(), this.options.offset && t.length)) { + const s = (i - e) / Math.max(t.length - 1, 1) / 2; + (e -= s), (i += s); + } + (this._startValue = e), (this._endValue = i), (this._valueRange = i - e); + } + getLabelForValue(t) { + return ne(t, this.chart.options.locale, this.options.ticks.format); + } + } + class bo extends xo { + static id = "linear"; + static defaults = { ticks: { callback: ae.formatters.numeric } }; + determineDataLimits() { + const { min: t, max: e } = this.getMinMax(!0); + (this.min = a(t) ? t : 0), + (this.max = a(e) ? e : 1), + this.handleTickRangeOptions(); + } + computeTickLimit() { + const t = this.isHorizontal(), + e = t ? this.width : this.height, + i = $(this.options.ticks.minRotation), + s = (t ? Math.sin(i) : Math.cos(i)) || 0.001, + n = this._resolveTickFontOptions(0); + return Math.ceil(e / Math.min(40, n.lineHeight / s)); + } + getPixelForValue(t) { + return null === t + ? NaN + : this.getPixelForDecimal((t - this._startValue) / this._valueRange); + } + getValueForPixel(t) { + return this._startValue + this.getDecimalForPixel(t) * this._valueRange; + } + } + const _o = (t) => Math.floor(z(t)), + yo = (t, e) => Math.pow(10, _o(t) + e); + function vo(t) { + return 1 === t / Math.pow(10, _o(t)); + } + function Mo(t, e, i) { + const s = Math.pow(10, i), + n = Math.floor(t / s); + return Math.ceil(e / s) - n; + } + function wo(t, { min: e, max: i }) { + e = r(t.min, e); + const s = [], + n = _o(e); + let o = (function (t, e) { + let i = _o(e - t); + for (; Mo(t, e, i) > 10; ) i++; + for (; Mo(t, e, i) < 10; ) i--; + return Math.min(i, _o(t)); + })(e, i), + a = o < 0 ? Math.pow(10, Math.abs(o)) : 1; + const l = Math.pow(10, o), + h = n > o ? Math.pow(10, n) : 0, + c = Math.round((e - h) * a) / a, + d = Math.floor((e - h) / l / 10) * l * 10; + let u = Math.floor((c - d) / Math.pow(10, o)), + f = r(t.min, Math.round((h + d + u * Math.pow(10, o)) * a) / a); + for (; f < i; ) + s.push({ value: f, major: vo(f), significand: u }), + u >= 10 ? (u = u < 15 ? 15 : 20) : u++, + u >= 20 && (o++, (u = 2), (a = o >= 0 ? 1 : a)), + (f = Math.round((h + d + u * Math.pow(10, o)) * a) / a); + const g = r(t.max, f); + return s.push({ value: g, major: vo(g), significand: u }), s; + } + class ko extends Js { + static id = "logarithmic"; + static defaults = { + ticks: { callback: ae.formatters.logarithmic, major: { enabled: !0 } }, + }; + constructor(t) { + super(t), + (this.start = void 0), + (this.end = void 0), + (this._startValue = void 0), + (this._valueRange = 0); + } + parse(t, e) { + const i = xo.prototype.parse.apply(this, [t, e]); + if (0 !== i) return a(i) && i > 0 ? i : null; + this._zero = !0; + } + determineDataLimits() { + const { min: t, max: e } = this.getMinMax(!0); + (this.min = a(t) ? Math.max(0, t) : null), + (this.max = a(e) ? Math.max(0, e) : null), + this.options.beginAtZero && (this._zero = !0), + this._zero && + this.min !== this._suggestedMin && + !a(this._userMin) && + (this.min = + t === yo(this.min, 0) ? yo(this.min, -1) : yo(this.min, 0)), + this.handleTickRangeOptions(); + } + handleTickRangeOptions() { + const { minDefined: t, maxDefined: e } = this.getUserBounds(); + let i = this.min, + s = this.max; + const n = (e) => (i = t ? i : e), + o = (t) => (s = e ? s : t); + i === s && (i <= 0 ? (n(1), o(10)) : (n(yo(i, -1)), o(yo(s, 1)))), + i <= 0 && n(yo(s, -1)), + s <= 0 && o(yo(i, 1)), + (this.min = i), + (this.max = s); + } + buildTicks() { + const t = this.options, + e = wo({ min: this._userMin, max: this._userMax }, this); + return ( + "ticks" === t.bounds && j(e, this, "value"), + t.reverse + ? (e.reverse(), (this.start = this.max), (this.end = this.min)) + : ((this.start = this.min), (this.end = this.max)), + e + ); + } + getLabelForValue(t) { + return void 0 === t + ? "0" + : ne(t, this.chart.options.locale, this.options.ticks.format); + } + configure() { + const t = this.min; + super.configure(), + (this._startValue = z(t)), + (this._valueRange = z(this.max) - z(t)); + } + getPixelForValue(t) { + return ( + (void 0 !== t && 0 !== t) || (t = this.min), + null === t || isNaN(t) + ? NaN + : this.getPixelForDecimal( + t === this.min ? 0 : (z(t) - this._startValue) / this._valueRange + ) + ); + } + getValueForPixel(t) { + const e = this.getDecimalForPixel(t); + return Math.pow(10, this._startValue + e * this._valueRange); + } + } + function So(t) { + const e = t.ticks; + if (e.display && t.display) { + const t = ki(e.backdropPadding); + return l(e.font && e.font.size, ue.font.size) + t.height; + } + return 0; + } + function Po(t, e, i, s, n) { + return t === s || t === n + ? { start: e - i / 2, end: e + i / 2 } + : t < s || t > n + ? { start: e - i, end: e } + : { start: e, end: e + i }; + } + function Do(t) { + const e = { + l: t.left + t._padding.left, + r: t.right - t._padding.right, + t: t.top + t._padding.top, + b: t.bottom - t._padding.bottom, + }, + i = Object.assign({}, e), + s = [], + o = [], + a = t._pointLabels.length, + r = t.options.pointLabels, + l = r.centerPointLabels ? C / a : 0; + for (let u = 0; u < a; u++) { + const a = r.setContext(t.getPointLabelContext(u)); + o[u] = a.padding; + const f = t.getPointPosition(u, t.drawingArea + o[u], l), + g = Si(a.font), + p = + ((h = t.ctx), + (c = g), + (d = n((d = t._pointLabels[u])) ? d : [d]), + { w: Oe(h, c.string, d), h: d.length * c.lineHeight }); + s[u] = p; + const m = G(t.getIndexAngle(u) + l), + x = Math.round(Y(m)); + Co(i, e, m, Po(x, f.x, p.w, 0, 180), Po(x, f.y, p.h, 90, 270)); + } + var h, c, d; + t.setCenterPoint(e.l - i.l, i.r - e.r, e.t - i.t, i.b - e.b), + (t._pointLabelItems = (function (t, e, i) { + const s = [], + n = t._pointLabels.length, + o = t.options, + { centerPointLabels: a, display: r } = o.pointLabels, + l = { extra: So(o) / 2, additionalAngle: a ? C / n : 0 }; + let h; + for (let o = 0; o < n; o++) { + (l.padding = i[o]), (l.size = e[o]); + const n = Oo(t, o, l); + s.push(n), + "auto" === r && ((n.visible = Ao(n, h)), n.visible && (h = n)); + } + return s; + })(t, s, o)); + } + function Co(t, e, i, s, n) { + const o = Math.abs(Math.sin(i)), + a = Math.abs(Math.cos(i)); + let r = 0, + l = 0; + s.start < e.l + ? ((r = (e.l - s.start) / o), (t.l = Math.min(t.l, e.l - r))) + : s.end > e.r && + ((r = (s.end - e.r) / o), (t.r = Math.max(t.r, e.r + r))), + n.start < e.t + ? ((l = (e.t - n.start) / a), (t.t = Math.min(t.t, e.t - l))) + : n.end > e.b && + ((l = (n.end - e.b) / a), (t.b = Math.max(t.b, e.b + l))); + } + function Oo(t, e, i) { + const s = t.drawingArea, + { extra: n, additionalAngle: o, padding: a, size: r } = i, + l = t.getPointPosition(e, s + n + a, o), + h = Math.round(Y(G(l.angle + E))), + c = (function (t, e, i) { + 90 === i || 270 === i ? (t -= e / 2) : (i > 270 || i < 90) && (t -= e); + return t; + })(l.y, r.h, h), + d = (function (t) { + if (0 === t || 180 === t) return "center"; + if (t < 180) return "left"; + return "right"; + })(h), + u = (function (t, e, i) { + "right" === i ? (t -= e) : "center" === i && (t -= e / 2); + return t; + })(l.x, r.w, d); + return { + visible: !0, + x: l.x, + y: c, + textAlign: d, + left: u, + top: c, + right: u + r.w, + bottom: c + r.h, + }; + } + function Ao(t, e) { + if (!e) return !0; + const { left: i, top: s, right: n, bottom: o } = t; + return !( + Re({ x: i, y: s }, e) || + Re({ x: i, y: o }, e) || + Re({ x: n, y: s }, e) || + Re({ x: n, y: o }, e) + ); + } + function To(t, e, i) { + const { left: n, top: o, right: a, bottom: r } = i, + { backdropColor: l } = e; + if (!s(l)) { + const i = wi(e.borderRadius), + s = ki(e.backdropPadding); + t.fillStyle = l; + const h = n - s.left, + c = o - s.top, + d = a - n + s.width, + u = r - o + s.height; + Object.values(i).some((t) => 0 !== t) + ? (t.beginPath(), + He(t, { x: h, y: c, w: d, h: u, radius: i }), + t.fill()) + : t.fillRect(h, c, d, u); + } + } + function Lo(t, e, i, s) { + const { ctx: n } = t; + if (i) n.arc(t.xCenter, t.yCenter, e, 0, O); + else { + let i = t.getPointPosition(0, e); + n.moveTo(i.x, i.y); + for (let o = 1; o < s; o++) + (i = t.getPointPosition(o, e)), n.lineTo(i.x, i.y); + } + } + class Eo extends xo { + static id = "radialLinear"; + static defaults = { + display: !0, + animate: !0, + position: "chartArea", + angleLines: { + display: !0, + lineWidth: 1, + borderDash: [], + borderDashOffset: 0, + }, + grid: { circular: !1 }, + startAngle: 0, + ticks: { showLabelBackdrop: !0, callback: ae.formatters.numeric }, + pointLabels: { + backdropColor: void 0, + backdropPadding: 2, + display: !0, + font: { size: 10 }, + callback: (t) => t, + padding: 5, + centerPointLabels: !1, + }, + }; + static defaultRoutes = { + "angleLines.color": "borderColor", + "pointLabels.color": "color", + "ticks.color": "color", + }; + static descriptors = { angleLines: { _fallback: "grid" } }; + constructor(t) { + super(t), + (this.xCenter = void 0), + (this.yCenter = void 0), + (this.drawingArea = void 0), + (this._pointLabels = []), + (this._pointLabelItems = []); + } + setDimensions() { + const t = (this._padding = ki(So(this.options) / 2)), + e = (this.width = this.maxWidth - t.width), + i = (this.height = this.maxHeight - t.height); + (this.xCenter = Math.floor(this.left + e / 2 + t.left)), + (this.yCenter = Math.floor(this.top + i / 2 + t.top)), + (this.drawingArea = Math.floor(Math.min(e, i) / 2)); + } + determineDataLimits() { + const { min: t, max: e } = this.getMinMax(!1); + (this.min = a(t) && !isNaN(t) ? t : 0), + (this.max = a(e) && !isNaN(e) ? e : 0), + this.handleTickRangeOptions(); + } + computeTickLimit() { + return Math.ceil(this.drawingArea / So(this.options)); + } + generateTickLabels(t) { + xo.prototype.generateTickLabels.call(this, t), + (this._pointLabels = this.getLabels() + .map((t, e) => { + const i = d(this.options.pointLabels.callback, [t, e], this); + return i || 0 === i ? i : ""; + }) + .filter((t, e) => this.chart.getDataVisibility(e))); + } + fit() { + const t = this.options; + t.display && t.pointLabels.display + ? Do(this) + : this.setCenterPoint(0, 0, 0, 0); + } + setCenterPoint(t, e, i, s) { + (this.xCenter += Math.floor((t - e) / 2)), + (this.yCenter += Math.floor((i - s) / 2)), + (this.drawingArea -= Math.min( + this.drawingArea / 2, + Math.max(t, e, i, s) + )); + } + getIndexAngle(t) { + return G( + t * (O / (this._pointLabels.length || 1)) + + $(this.options.startAngle || 0) + ); + } + getDistanceFromCenterForValue(t) { + if (s(t)) return NaN; + const e = this.drawingArea / (this.max - this.min); + return this.options.reverse ? (this.max - t) * e : (t - this.min) * e; + } + getValueForDistanceFromCenter(t) { + if (s(t)) return NaN; + const e = t / (this.drawingArea / (this.max - this.min)); + return this.options.reverse ? this.max - e : this.min + e; + } + getPointLabelContext(t) { + const e = this._pointLabels || []; + if (t >= 0 && t < e.length) { + const i = e[t]; + return (function (t, e, i) { + return Ci(t, { label: i, index: e, type: "pointLabel" }); + })(this.getContext(), t, i); + } + } + getPointPosition(t, e, i = 0) { + const s = this.getIndexAngle(t) - E + i; + return { + x: Math.cos(s) * e + this.xCenter, + y: Math.sin(s) * e + this.yCenter, + angle: s, + }; + } + getPointPositionForValue(t, e) { + return this.getPointPosition(t, this.getDistanceFromCenterForValue(e)); + } + getBasePosition(t) { + return this.getPointPositionForValue(t || 0, this.getBaseValue()); + } + getPointLabelPosition(t) { + const { left: e, top: i, right: s, bottom: n } = this._pointLabelItems[t]; + return { left: e, top: i, right: s, bottom: n }; + } + drawBackground() { + const { + backgroundColor: t, + grid: { circular: e }, + } = this.options; + if (t) { + const i = this.ctx; + i.save(), + i.beginPath(), + Lo( + this, + this.getDistanceFromCenterForValue(this._endValue), + e, + this._pointLabels.length + ), + i.closePath(), + (i.fillStyle = t), + i.fill(), + i.restore(); + } + } + drawGrid() { + const t = this.ctx, + e = this.options, + { angleLines: i, grid: s, border: n } = e, + o = this._pointLabels.length; + let a, r, l; + if ( + (e.pointLabels.display && + (function (t, e) { + const { + ctx: i, + options: { pointLabels: s }, + } = t; + for (let n = e - 1; n >= 0; n--) { + const e = t._pointLabelItems[n]; + if (!e.visible) continue; + const o = s.setContext(t.getPointLabelContext(n)); + To(i, o, e); + const a = Si(o.font), + { x: r, y: l, textAlign: h } = e; + Ne(i, t._pointLabels[n], r, l + a.lineHeight / 2, a, { + color: o.color, + textAlign: h, + textBaseline: "middle", + }); + } + })(this, o), + s.display && + this.ticks.forEach((t, e) => { + if (0 !== e || (0 === e && this.min < 0)) { + r = this.getDistanceFromCenterForValue(t.value); + const i = this.getContext(e), + a = s.setContext(i), + l = n.setContext(i); + !(function (t, e, i, s, n) { + const o = t.ctx, + a = e.circular, + { color: r, lineWidth: l } = e; + (!a && !s) || + !r || + !l || + i < 0 || + (o.save(), + (o.strokeStyle = r), + (o.lineWidth = l), + o.setLineDash(n.dash), + (o.lineDashOffset = n.dashOffset), + o.beginPath(), + Lo(t, i, a, s), + o.closePath(), + o.stroke(), + o.restore()); + })(this, a, r, o, l); + } + }), + i.display) + ) { + for (t.save(), a = o - 1; a >= 0; a--) { + const s = i.setContext(this.getPointLabelContext(a)), + { color: n, lineWidth: o } = s; + o && + n && + ((t.lineWidth = o), + (t.strokeStyle = n), + t.setLineDash(s.borderDash), + (t.lineDashOffset = s.borderDashOffset), + (r = this.getDistanceFromCenterForValue( + e.reverse ? this.min : this.max + )), + (l = this.getPointPosition(a, r)), + t.beginPath(), + t.moveTo(this.xCenter, this.yCenter), + t.lineTo(l.x, l.y), + t.stroke()); + } + t.restore(); + } + } + drawBorder() {} + drawLabels() { + const t = this.ctx, + e = this.options, + i = e.ticks; + if (!i.display) return; + const s = this.getIndexAngle(0); + let n, o; + t.save(), + t.translate(this.xCenter, this.yCenter), + t.rotate(s), + (t.textAlign = "center"), + (t.textBaseline = "middle"), + this.ticks.forEach((s, a) => { + if (0 === a && this.min >= 0 && !e.reverse) return; + const r = i.setContext(this.getContext(a)), + l = Si(r.font); + if ( + ((n = this.getDistanceFromCenterForValue(this.ticks[a].value)), + r.showLabelBackdrop) + ) { + (t.font = l.string), + (o = t.measureText(s.label).width), + (t.fillStyle = r.backdropColor); + const e = ki(r.backdropPadding); + t.fillRect( + -o / 2 - e.left, + -n - l.size / 2 - e.top, + o + e.width, + l.size + e.height + ); + } + Ne(t, s.label, 0, -n, l, { + color: r.color, + strokeColor: r.textStrokeColor, + strokeWidth: r.textStrokeWidth, + }); + }), + t.restore(); + } + drawTitle() {} + } + const Ro = { + millisecond: { common: !0, size: 1, steps: 1e3 }, + second: { common: !0, size: 1e3, steps: 60 }, + minute: { common: !0, size: 6e4, steps: 60 }, + hour: { common: !0, size: 36e5, steps: 24 }, + day: { common: !0, size: 864e5, steps: 30 }, + week: { common: !1, size: 6048e5, steps: 4 }, + month: { common: !0, size: 2628e6, steps: 12 }, + quarter: { common: !1, size: 7884e6, steps: 4 }, + year: { common: !0, size: 3154e7 }, + }, + Io = Object.keys(Ro); + function zo(t, e) { + return t - e; + } + function Fo(t, e) { + if (s(e)) return null; + const i = t._adapter, + { parser: n, round: o, isoWeekday: r } = t._parseOpts; + let l = e; + return ( + "function" == typeof n && (l = n(l)), + a(l) || (l = "string" == typeof n ? i.parse(l, n) : i.parse(l)), + null === l + ? null + : (o && + (l = + "week" !== o || (!N(r) && !0 !== r) + ? i.startOf(l, o) + : i.startOf(l, "isoWeek", r)), + +l) + ); + } + function Vo(t, e, i, s) { + const n = Io.length; + for (let o = Io.indexOf(t); o < n - 1; ++o) { + const t = Ro[Io[o]], + n = t.steps ? t.steps : Number.MAX_SAFE_INTEGER; + if (t.common && Math.ceil((i - e) / (n * t.size)) <= s) return Io[o]; + } + return Io[n - 1]; + } + function Bo(t, e, i) { + if (i) { + if (i.length) { + const { lo: s, hi: n } = et(i, e); + t[i[s] >= e ? i[s] : i[n]] = !0; + } + } else t[e] = !0; + } + function Wo(t, e, i) { + const s = [], + n = {}, + o = e.length; + let a, r; + for (a = 0; a < o; ++a) + (r = e[a]), (n[r] = a), s.push({ value: r, major: !1 }); + return 0 !== o && i + ? (function (t, e, i, s) { + const n = t._adapter, + o = +n.startOf(e[0].value, s), + a = e[e.length - 1].value; + let r, l; + for (r = o; r <= a; r = +n.add(r, 1, s)) + (l = i[r]), l >= 0 && (e[l].major = !0); + return e; + })(t, s, n, i) + : s; + } + class No extends Js { + static id = "time"; + static defaults = { + bounds: "data", + adapters: {}, + time: { + parser: !1, + unit: !1, + round: !1, + isoWeekday: !1, + minUnit: "millisecond", + displayFormats: {}, + }, + ticks: { source: "auto", callback: !1, major: { enabled: !1 } }, + }; + constructor(t) { + super(t), + (this._cache = { data: [], labels: [], all: [] }), + (this._unit = "day"), + (this._majorUnit = void 0), + (this._offsets = {}), + (this._normalized = !1), + (this._parseOpts = void 0); + } + init(t, e = {}) { + const i = t.time || (t.time = {}), + s = (this._adapter = new Rn._date(t.adapters.date)); + s.init(e), + b(i.displayFormats, s.formats()), + (this._parseOpts = { + parser: i.parser, + round: i.round, + isoWeekday: i.isoWeekday, + }), + super.init(t), + (this._normalized = e.normalized); + } + parse(t, e) { + return void 0 === t ? null : Fo(this, t); + } + beforeLayout() { + super.beforeLayout(), (this._cache = { data: [], labels: [], all: [] }); + } + determineDataLimits() { + const t = this.options, + e = this._adapter, + i = t.time.unit || "day"; + let { + min: s, + max: n, + minDefined: o, + maxDefined: r, + } = this.getUserBounds(); + function l(t) { + o || isNaN(t.min) || (s = Math.min(s, t.min)), + r || isNaN(t.max) || (n = Math.max(n, t.max)); + } + (o && r) || + (l(this._getLabelBounds()), + ("ticks" === t.bounds && "labels" === t.ticks.source) || + l(this.getMinMax(!1))), + (s = a(s) && !isNaN(s) ? s : +e.startOf(Date.now(), i)), + (n = a(n) && !isNaN(n) ? n : +e.endOf(Date.now(), i) + 1), + (this.min = Math.min(s, n - 1)), + (this.max = Math.max(s + 1, n)); + } + _getLabelBounds() { + const t = this.getLabelTimestamps(); + let e = Number.POSITIVE_INFINITY, + i = Number.NEGATIVE_INFINITY; + return ( + t.length && ((e = t[0]), (i = t[t.length - 1])), { min: e, max: i } + ); + } + buildTicks() { + const t = this.options, + e = t.time, + i = t.ticks, + s = + "labels" === i.source ? this.getLabelTimestamps() : this._generate(); + "ticks" === t.bounds && + s.length && + ((this.min = this._userMin || s[0]), + (this.max = this._userMax || s[s.length - 1])); + const n = this.min, + o = nt(s, n, this.max); + return ( + (this._unit = + e.unit || + (i.autoSkip + ? Vo(e.minUnit, this.min, this.max, this._getLabelCapacity(n)) + : (function (t, e, i, s, n) { + for (let o = Io.length - 1; o >= Io.indexOf(i); o--) { + const i = Io[o]; + if (Ro[i].common && t._adapter.diff(n, s, i) >= e - 1) + return i; + } + return Io[i ? Io.indexOf(i) : 0]; + })(this, o.length, e.minUnit, this.min, this.max))), + (this._majorUnit = + i.major.enabled && "year" !== this._unit + ? (function (t) { + for (let e = Io.indexOf(t) + 1, i = Io.length; e < i; ++e) + if (Ro[Io[e]].common) return Io[e]; + })(this._unit) + : void 0), + this.initOffsets(s), + t.reverse && o.reverse(), + Wo(this, o, this._majorUnit) + ); + } + afterAutoSkip() { + this.options.offsetAfterAutoskip && + this.initOffsets(this.ticks.map((t) => +t.value)); + } + initOffsets(t = []) { + let e, + i, + s = 0, + n = 0; + this.options.offset && + t.length && + ((e = this.getDecimalForValue(t[0])), + (s = 1 === t.length ? 1 - e : (this.getDecimalForValue(t[1]) - e) / 2), + (i = this.getDecimalForValue(t[t.length - 1])), + (n = + 1 === t.length + ? i + : (i - this.getDecimalForValue(t[t.length - 2])) / 2)); + const o = t.length < 3 ? 0.5 : 0.25; + (s = J(s, 0, o)), + (n = J(n, 0, o)), + (this._offsets = { start: s, end: n, factor: 1 / (s + 1 + n) }); + } + _generate() { + const t = this._adapter, + e = this.min, + i = this.max, + s = this.options, + n = s.time, + o = n.unit || Vo(n.minUnit, e, i, this._getLabelCapacity(e)), + a = l(s.ticks.stepSize, 1), + r = "week" === o && n.isoWeekday, + h = N(r) || !0 === r, + c = {}; + let d, + u, + f = e; + if ( + (h && (f = +t.startOf(f, "isoWeek", r)), + (f = +t.startOf(f, h ? "day" : o)), + t.diff(i, e, o) > 1e5 * a) + ) + throw new Error( + e + " and " + i + " are too far apart with stepSize of " + a + " " + o + ); + const g = "data" === s.ticks.source && this.getDataTimestamps(); + for (d = f, u = 0; d < i; d = +t.add(d, a, o), u++) Bo(c, d, g); + return ( + (d !== i && "ticks" !== s.bounds && 1 !== u) || Bo(c, d, g), + Object.keys(c) + .sort(zo) + .map((t) => +t) + ); + } + getLabelForValue(t) { + const e = this._adapter, + i = this.options.time; + return i.tooltipFormat + ? e.format(t, i.tooltipFormat) + : e.format(t, i.displayFormats.datetime); + } + format(t, e) { + const i = this.options.time.displayFormats, + s = this._unit, + n = e || i[s]; + return this._adapter.format(t, n); + } + _tickFormatFunction(t, e, i, s) { + const n = this.options, + o = n.ticks.callback; + if (o) return d(o, [t, e, i], this); + const a = n.time.displayFormats, + r = this._unit, + l = this._majorUnit, + h = r && a[r], + c = l && a[l], + u = i[e], + f = l && c && u && u.major; + return this._adapter.format(t, s || (f ? c : h)); + } + generateTickLabels(t) { + let e, i, s; + for (e = 0, i = t.length; e < i; ++e) + (s = t[e]), (s.label = this._tickFormatFunction(s.value, e, t)); + } + getDecimalForValue(t) { + return null === t ? NaN : (t - this.min) / (this.max - this.min); + } + getPixelForValue(t) { + const e = this._offsets, + i = this.getDecimalForValue(t); + return this.getPixelForDecimal((e.start + i) * e.factor); + } + getValueForPixel(t) { + const e = this._offsets, + i = this.getDecimalForPixel(t) / e.factor - e.end; + return this.min + i * (this.max - this.min); + } + _getLabelSize(t) { + const e = this.options.ticks, + i = this.ctx.measureText(t).width, + s = $(this.isHorizontal() ? e.maxRotation : e.minRotation), + n = Math.cos(s), + o = Math.sin(s), + a = this._resolveTickFontOptions(0).size; + return { w: i * n + a * o, h: i * o + a * n }; + } + _getLabelCapacity(t) { + const e = this.options.time, + i = e.displayFormats, + s = i[e.unit] || i.millisecond, + n = this._tickFormatFunction(t, 0, Wo(this, [t], this._majorUnit), s), + o = this._getLabelSize(n), + a = + Math.floor( + this.isHorizontal() ? this.width / o.w : this.height / o.h + ) - 1; + return a > 0 ? a : 1; + } + getDataTimestamps() { + let t, + e, + i = this._cache.data || []; + if (i.length) return i; + const s = this.getMatchingVisibleMetas(); + if (this._normalized && s.length) + return (this._cache.data = s[0].controller.getAllParsedValues(this)); + for (t = 0, e = s.length; t < e; ++t) + i = i.concat(s[t].controller.getAllParsedValues(this)); + return (this._cache.data = this.normalize(i)); + } + getLabelTimestamps() { + const t = this._cache.labels || []; + let e, i; + if (t.length) return t; + const s = this.getLabels(); + for (e = 0, i = s.length; e < i; ++e) t.push(Fo(this, s[e])); + return (this._cache.labels = this._normalized ? t : this.normalize(t)); + } + normalize(t) { + return lt(t.sort(zo)); + } + } + function Ho(t, e, i) { + let s, + n, + o, + a, + r = 0, + l = t.length - 1; + i + ? (e >= t[r].pos && e <= t[l].pos && ({ lo: r, hi: l } = it(t, "pos", e)), + ({ pos: s, time: o } = t[r]), + ({ pos: n, time: a } = t[l])) + : (e >= t[r].time && + e <= t[l].time && + ({ lo: r, hi: l } = it(t, "time", e)), + ({ time: s, pos: o } = t[r]), + ({ time: n, pos: a } = t[l])); + const h = n - s; + return h ? o + ((a - o) * (e - s)) / h : o; + } + var jo = Object.freeze({ + __proto__: null, + CategoryScale: class extends Js { + static id = "category"; + static defaults = { ticks: { callback: po } }; + constructor(t) { + super(t), + (this._startValue = void 0), + (this._valueRange = 0), + (this._addedLabels = []); + } + init(t) { + const e = this._addedLabels; + if (e.length) { + const t = this.getLabels(); + for (const { index: i, label: s } of e) t[i] === s && t.splice(i, 1); + this._addedLabels = []; + } + super.init(t); + } + parse(t, e) { + if (s(t)) return null; + const i = this.getLabels(); + return ((t, e) => (null === t ? null : J(Math.round(t), 0, e)))( + (e = + isFinite(e) && i[e] === t + ? e + : go(i, t, l(e, t), this._addedLabels)), + i.length - 1 + ); + } + determineDataLimits() { + const { minDefined: t, maxDefined: e } = this.getUserBounds(); + let { min: i, max: s } = this.getMinMax(!0); + "ticks" === this.options.bounds && + (t || (i = 0), e || (s = this.getLabels().length - 1)), + (this.min = i), + (this.max = s); + } + buildTicks() { + const t = this.min, + e = this.max, + i = this.options.offset, + s = []; + let n = this.getLabels(); + (n = 0 === t && e === n.length - 1 ? n : n.slice(t, e + 1)), + (this._valueRange = Math.max(n.length - (i ? 0 : 1), 1)), + (this._startValue = this.min - (i ? 0.5 : 0)); + for (let i = t; i <= e; i++) s.push({ value: i }); + return s; + } + getLabelForValue(t) { + return po.call(this, t); + } + configure() { + super.configure(), + this.isHorizontal() || (this._reversePixels = !this._reversePixels); + } + getPixelForValue(t) { + return ( + "number" != typeof t && (t = this.parse(t)), + null === t + ? NaN + : this.getPixelForDecimal((t - this._startValue) / this._valueRange) + ); + } + getPixelForTick(t) { + const e = this.ticks; + return t < 0 || t > e.length - 1 + ? null + : this.getPixelForValue(e[t].value); + } + getValueForPixel(t) { + return Math.round( + this._startValue + this.getDecimalForPixel(t) * this._valueRange + ); + } + getBasePixel() { + return this.bottom; + } + }, + LinearScale: bo, + LogarithmicScale: ko, + RadialLinearScale: Eo, + TimeScale: No, + TimeSeriesScale: class extends No { + static id = "timeseries"; + static defaults = No.defaults; + constructor(t) { + super(t), + (this._table = []), + (this._minPos = void 0), + (this._tableRange = void 0); + } + initOffsets() { + const t = this._getTimestampsForTable(), + e = (this._table = this.buildLookupTable(t)); + (this._minPos = Ho(e, this.min)), + (this._tableRange = Ho(e, this.max) - this._minPos), + super.initOffsets(t); + } + buildLookupTable(t) { + const { min: e, max: i } = this, + s = [], + n = []; + let o, a, r, l, h; + for (o = 0, a = t.length; o < a; ++o) + (l = t[o]), l >= e && l <= i && s.push(l); + if (s.length < 2) + return [ + { time: e, pos: 0 }, + { time: i, pos: 1 }, + ]; + for (o = 0, a = s.length; o < a; ++o) + (h = s[o + 1]), + (r = s[o - 1]), + (l = s[o]), + Math.round((h + r) / 2) !== l && + n.push({ time: l, pos: o / (a - 1) }); + return n; + } + _generate() { + const t = this.min, + e = this.max; + let i = super.getDataTimestamps(); + return ( + (i.includes(t) && i.length) || i.splice(0, 0, t), + (i.includes(e) && 1 !== i.length) || i.push(e), + i.sort((t, e) => t - e) + ); + } + _getTimestampsForTable() { + let t = this._cache.all || []; + if (t.length) return t; + const e = this.getDataTimestamps(), + i = this.getLabelTimestamps(); + return ( + (t = + e.length && i.length + ? this.normalize(e.concat(i)) + : e.length + ? e + : i), + (t = this._cache.all = t), + t + ); + } + getDecimalForValue(t) { + return (Ho(this._table, t) - this._minPos) / this._tableRange; + } + getValueForPixel(t) { + const e = this._offsets, + i = this.getDecimalForPixel(t) / e.factor - e.end; + return Ho(this._table, i * this._tableRange + this._minPos, !0); + } + }, + }); + const $o = [ + "rgb(54, 162, 235)", + "rgb(255, 99, 132)", + "rgb(255, 159, 64)", + "rgb(255, 205, 86)", + "rgb(75, 192, 192)", + "rgb(153, 102, 255)", + "rgb(201, 203, 207)", + ], + Yo = $o.map((t) => t.replace("rgb(", "rgba(").replace(")", ", 0.5)")); + function Uo(t) { + return $o[t % $o.length]; + } + function Xo(t) { + return Yo[t % Yo.length]; + } + function qo(t) { + let e = 0; + return (i, s) => { + const n = t.getDatasetMeta(s).controller; + n instanceof jn + ? (e = (function (t, e) { + return (t.backgroundColor = t.data.map(() => Uo(e++))), e; + })(i, e)) + : n instanceof $n + ? (e = (function (t, e) { + return (t.backgroundColor = t.data.map(() => Xo(e++))), e; + })(i, e)) + : n && + (e = (function (t, e) { + return (t.borderColor = Uo(e)), (t.backgroundColor = Xo(e)), ++e; + })(i, e)); + }; + } + function Ko(t) { + let e; + for (e in t) if (t[e].borderColor || t[e].backgroundColor) return !0; + return !1; + } + var Go = { + id: "colors", + defaults: { enabled: !0, forceOverride: !1 }, + beforeLayout(t, e, i) { + if (!i.enabled) return; + const { + data: { datasets: s }, + options: n, + } = t.config, + { elements: o } = n; + if ( + !i.forceOverride && + (Ko(s) || + ((a = n) && (a.borderColor || a.backgroundColor)) || + (o && Ko(o))) + ) + return; + var a; + const r = qo(t); + s.forEach(r); + }, + }; + function Zo(t) { + if (t._decimated) { + const e = t._data; + delete t._decimated, + delete t._data, + Object.defineProperty(t, "data", { + configurable: !0, + enumerable: !0, + writable: !0, + value: e, + }); + } + } + function Jo(t) { + t.data.datasets.forEach((t) => { + Zo(t); + }); + } + var Qo = { + id: "decimation", + defaults: { algorithm: "min-max", enabled: !1 }, + beforeElementsUpdate: (t, e, i) => { + if (!i.enabled) return void Jo(t); + const n = t.width; + t.data.datasets.forEach((e, o) => { + const { _data: a, indexAxis: r } = e, + l = t.getDatasetMeta(o), + h = a || e.data; + if ("y" === Pi([r, t.options.indexAxis])) return; + if (!l.controller.supportsDecimation) return; + const c = t.scales[l.xAxisID]; + if ("linear" !== c.type && "time" !== c.type) return; + if (t.options.parsing) return; + let { start: d, count: u } = (function (t, e) { + const i = e.length; + let s, + n = 0; + const { iScale: o } = t, + { + min: a, + max: r, + minDefined: l, + maxDefined: h, + } = o.getUserBounds(); + return ( + l && (n = J(it(e, o.axis, a).lo, 0, i - 1)), + (s = h ? J(it(e, o.axis, r).hi + 1, n, i) - n : i - n), + { start: n, count: s } + ); + })(l, h); + if (u <= (i.threshold || 4 * n)) return void Zo(e); + let f; + switch ( + (s(a) && + ((e._data = h), + delete e.data, + Object.defineProperty(e, "data", { + configurable: !0, + enumerable: !0, + get: function () { + return this._decimated; + }, + set: function (t) { + this._data = t; + }, + })), + i.algorithm) + ) { + case "lttb": + f = (function (t, e, i, s, n) { + const o = n.samples || s; + if (o >= i) return t.slice(e, e + i); + const a = [], + r = (i - 2) / (o - 2); + let l = 0; + const h = e + i - 1; + let c, + d, + u, + f, + g, + p = e; + for (a[l++] = t[p], c = 0; c < o - 2; c++) { + let s, + n = 0, + o = 0; + const h = Math.floor((c + 1) * r) + 1 + e, + m = Math.min(Math.floor((c + 2) * r) + 1, i) + e, + x = m - h; + for (s = h; s < m; s++) (n += t[s].x), (o += t[s].y); + (n /= x), (o /= x); + const b = Math.floor(c * r) + 1 + e, + _ = Math.min(Math.floor((c + 1) * r) + 1, i) + e, + { x: y, y: v } = t[p]; + for (u = f = -1, s = b; s < _; s++) + (f = + 0.5 * + Math.abs((y - n) * (t[s].y - v) - (y - t[s].x) * (o - v))), + f > u && ((u = f), (d = t[s]), (g = s)); + (a[l++] = d), (p = g); + } + return (a[l++] = t[h]), a; + })(h, d, u, n, i); + break; + case "min-max": + f = (function (t, e, i, n) { + let o, + a, + r, + l, + h, + c, + d, + u, + f, + g, + p = 0, + m = 0; + const x = [], + b = e + i - 1, + _ = t[e].x, + y = t[b].x - _; + for (o = e; o < e + i; ++o) { + (a = t[o]), (r = ((a.x - _) / y) * n), (l = a.y); + const e = 0 | r; + if (e === h) + l < f ? ((f = l), (c = o)) : l > g && ((g = l), (d = o)), + (p = (m * p + a.x) / ++m); + else { + const i = o - 1; + if (!s(c) && !s(d)) { + const e = Math.min(c, d), + s = Math.max(c, d); + e !== u && e !== i && x.push({ ...t[e], x: p }), + s !== u && s !== i && x.push({ ...t[s], x: p }); + } + o > 0 && i !== u && x.push(t[i]), + x.push(a), + (h = e), + (m = 0), + (f = g = l), + (c = d = u = o); + } + } + return x; + })(h, d, u, n); + break; + default: + throw new Error( + `Unsupported decimation algorithm '${i.algorithm}'` + ); + } + e._decimated = f; + }); + }, + destroy(t) { + Jo(t); + }, + }; + function ta(t, e, i, s) { + if (s) return; + let n = e[t], + o = i[t]; + return ( + "angle" === t && ((n = G(n)), (o = G(o))), + { property: t, start: n, end: o } + ); + } + function ea(t, e, i) { + for (; e > t; e--) { + const t = i[e]; + if (!isNaN(t.x) && !isNaN(t.y)) break; + } + return e; + } + function ia(t, e, i, s) { + return t && e ? s(t[i], e[i]) : t ? t[i] : e ? e[i] : 0; + } + function sa(t, e) { + let i = [], + s = !1; + return ( + n(t) + ? ((s = !0), (i = t)) + : (i = (function (t, e) { + const { x: i = null, y: s = null } = t || {}, + n = e.points, + o = []; + return ( + e.segments.forEach(({ start: t, end: e }) => { + e = ea(t, e, n); + const a = n[t], + r = n[e]; + null !== s + ? (o.push({ x: a.x, y: s }), o.push({ x: r.x, y: s })) + : null !== i && + (o.push({ x: i, y: a.y }), o.push({ x: i, y: r.y })); + }), + o + ); + })(t, e)), + i.length + ? new no({ points: i, options: { tension: 0 }, _loop: s, _fullLoop: s }) + : null + ); + } + function na(t) { + return t && !1 !== t.fill; + } + function oa(t, e, i) { + let s = t[e].fill; + const n = [e]; + let o; + if (!i) return s; + for (; !1 !== s && -1 === n.indexOf(s); ) { + if (!a(s)) return s; + if (((o = t[s]), !o)) return !1; + if (o.visible) return s; + n.push(s), (s = o.fill); + } + return !1; + } + function aa(t, e, i) { + const s = (function (t) { + const e = t.options, + i = e.fill; + let s = l(i && i.target, i); + void 0 === s && (s = !!e.backgroundColor); + if (!1 === s || null === s) return !1; + if (!0 === s) return "origin"; + return s; + })(t); + if (o(s)) return !isNaN(s.value) && s; + let n = parseFloat(s); + return a(n) && Math.floor(n) === n + ? (function (t, e, i, s) { + ("-" !== t && "+" !== t) || (i = e + i); + if (i === e || i < 0 || i >= s) return !1; + return i; + })(s[0], e, n, i) + : ["origin", "start", "end", "stack", "shape"].indexOf(s) >= 0 && s; + } + function ra(t, e, i) { + const s = []; + for (let n = 0; n < i.length; n++) { + const o = i[n], + { first: a, last: r, point: l } = la(o, e, "x"); + if (!(!l || (a && r))) + if (a) s.unshift(l); + else if ((t.push(l), !r)) break; + } + t.push(...s); + } + function la(t, e, i) { + const s = t.interpolate(e, i); + if (!s) return {}; + const n = s[i], + o = t.segments, + a = t.points; + let r = !1, + l = !1; + for (let t = 0; t < o.length; t++) { + const e = o[t], + s = a[e.start][i], + h = a[e.end][i]; + if (tt(n, s, h)) { + (r = n === s), (l = n === h); + break; + } + } + return { first: r, last: l, point: s }; + } + class ha { + constructor(t) { + (this.x = t.x), (this.y = t.y), (this.radius = t.radius); + } + pathSegment(t, e, i) { + const { x: s, y: n, radius: o } = this; + return ( + (e = e || { start: 0, end: O }), + t.arc(s, n, o, e.end, e.start, !0), + !i.bounds + ); + } + interpolate(t) { + const { x: e, y: i, radius: s } = this, + n = t.angle; + return { x: e + Math.cos(n) * s, y: i + Math.sin(n) * s, angle: n }; + } + } + function ca(t) { + const { chart: e, fill: i, line: s } = t; + if (a(i)) + return (function (t, e) { + const i = t.getDatasetMeta(e), + s = i && t.isDatasetVisible(e); + return s ? i.dataset : null; + })(e, i); + if ("stack" === i) + return (function (t) { + const { scale: e, index: i, line: s } = t, + n = [], + o = s.segments, + a = s.points, + r = (function (t, e) { + const i = [], + s = t.getMatchingVisibleMetas("line"); + for (let t = 0; t < s.length; t++) { + const n = s[t]; + if (n.index === e) break; + n.hidden || i.unshift(n.dataset); + } + return i; + })(e, i); + r.push(sa({ x: null, y: e.bottom }, s)); + for (let t = 0; t < o.length; t++) { + const e = o[t]; + for (let t = e.start; t <= e.end; t++) ra(n, a[t], r); + } + return new no({ points: n, options: {} }); + })(t); + if ("shape" === i) return !0; + const n = (function (t) { + const e = t.scale || {}; + if (e.getPointPositionForValue) + return (function (t) { + const { scale: e, fill: i } = t, + s = e.options, + n = e.getLabels().length, + a = s.reverse ? e.max : e.min, + r = (function (t, e, i) { + let s; + return ( + (s = + "start" === t + ? i + : "end" === t + ? e.options.reverse + ? e.min + : e.max + : o(t) + ? t.value + : e.getBaseValue()), + s + ); + })(i, e, a), + l = []; + if (s.grid.circular) { + const t = e.getPointPositionForValue(0, a); + return new ha({ + x: t.x, + y: t.y, + radius: e.getDistanceFromCenterForValue(r), + }); + } + for (let t = 0; t < n; ++t) l.push(e.getPointPositionForValue(t, r)); + return l; + })(t); + return (function (t) { + const { scale: e = {}, fill: i } = t, + s = (function (t, e) { + let i = null; + return ( + "start" === t + ? (i = e.bottom) + : "end" === t + ? (i = e.top) + : o(t) + ? (i = e.getPixelForValue(t.value)) + : e.getBasePixel && (i = e.getBasePixel()), + i + ); + })(i, e); + if (a(s)) { + const t = e.isHorizontal(); + return { x: t ? s : null, y: t ? null : s }; + } + return null; + })(t); + })(t); + return n instanceof ha ? n : sa(n, s); + } + function da(t, e, i) { + const s = ca(e), + { line: n, scale: o, axis: a } = e, + r = n.options, + l = r.fill, + h = r.backgroundColor, + { above: c = h, below: d = h } = l || {}; + s && + n.points.length && + (Ie(t, i), + (function (t, e) { + const { line: i, target: s, above: n, below: o, area: a, scale: r } = e, + l = i._loop ? "angle" : e.axis; + t.save(), + "x" === l && + o !== n && + (ua(t, s, a.top), + fa(t, { line: i, target: s, color: n, scale: r, property: l }), + t.restore(), + t.save(), + ua(t, s, a.bottom)); + fa(t, { line: i, target: s, color: o, scale: r, property: l }), + t.restore(); + })(t, { + line: n, + target: s, + above: c, + below: d, + area: i, + scale: o, + axis: a, + }), + ze(t)); + } + function ua(t, e, i) { + const { segments: s, points: n } = e; + let o = !0, + a = !1; + t.beginPath(); + for (const r of s) { + const { start: s, end: l } = r, + h = n[s], + c = n[ea(s, l, n)]; + o + ? (t.moveTo(h.x, h.y), (o = !1)) + : (t.lineTo(h.x, i), t.lineTo(h.x, h.y)), + (a = !!e.pathSegment(t, r, { move: a })), + a ? t.closePath() : t.lineTo(c.x, i); + } + t.lineTo(e.first().x, i), t.closePath(), t.clip(); + } + function fa(t, e) { + const { line: i, target: s, property: n, color: o, scale: a } = e, + r = (function (t, e, i) { + const s = t.segments, + n = t.points, + o = e.points, + a = []; + for (const t of s) { + let { start: s, end: r } = t; + r = ea(s, r, n); + const l = ta(i, n[s], n[r], t.loop); + if (!e.segments) { + a.push({ source: t, target: l, start: n[s], end: n[r] }); + continue; + } + const h = Ii(e, l); + for (const e of h) { + const s = ta(i, o[e.start], o[e.end], e.loop), + r = Ri(t, n, s); + for (const t of r) + a.push({ + source: t, + target: e, + start: { [i]: ia(l, s, "start", Math.max) }, + end: { [i]: ia(l, s, "end", Math.min) }, + }); + } + } + return a; + })(i, s, n); + for (const { source: e, target: l, start: h, end: c } of r) { + const { style: { backgroundColor: r = o } = {} } = e, + d = !0 !== s; + t.save(), (t.fillStyle = r), ga(t, a, d && ta(n, h, c)), t.beginPath(); + const u = !!i.pathSegment(t, e); + let f; + if (d) { + u ? t.closePath() : pa(t, s, c, n); + const e = !!s.pathSegment(t, l, { move: u, reverse: !0 }); + (f = u && e), f || pa(t, s, h, n); + } + t.closePath(), t.fill(f ? "evenodd" : "nonzero"), t.restore(); + } + } + function ga(t, e, i) { + const { top: s, bottom: n } = e.chart.chartArea, + { property: o, start: a, end: r } = i || {}; + "x" === o && (t.beginPath(), t.rect(a, s, r - a, n - s), t.clip()); + } + function pa(t, e, i, s) { + const n = e.interpolate(i, s); + n && t.lineTo(n.x, n.y); + } + var ma = { + id: "filler", + afterDatasetsUpdate(t, e, i) { + const s = (t.data.datasets || []).length, + n = []; + let o, a, r, l; + for (a = 0; a < s; ++a) + (o = t.getDatasetMeta(a)), + (r = o.dataset), + (l = null), + r && + r.options && + r instanceof no && + (l = { + visible: t.isDatasetVisible(a), + index: a, + fill: aa(r, a, s), + chart: t, + axis: o.controller.options.indexAxis, + scale: o.vScale, + line: r, + }), + (o.$filler = l), + n.push(l); + for (a = 0; a < s; ++a) + (l = n[a]), l && !1 !== l.fill && (l.fill = oa(n, a, i.propagate)); + }, + beforeDraw(t, e, i) { + const s = "beforeDraw" === i.drawTime, + n = t.getSortedVisibleDatasetMetas(), + o = t.chartArea; + for (let e = n.length - 1; e >= 0; --e) { + const i = n[e].$filler; + i && + (i.line.updateControlPoints(o, i.axis), + s && i.fill && da(t.ctx, i, o)); + } + }, + beforeDatasetsDraw(t, e, i) { + if ("beforeDatasetsDraw" !== i.drawTime) return; + const s = t.getSortedVisibleDatasetMetas(); + for (let e = s.length - 1; e >= 0; --e) { + const i = s[e].$filler; + na(i) && da(t.ctx, i, t.chartArea); + } + }, + beforeDatasetDraw(t, e, i) { + const s = e.meta.$filler; + na(s) && "beforeDatasetDraw" === i.drawTime && da(t.ctx, s, t.chartArea); + }, + defaults: { propagate: !0, drawTime: "beforeDatasetDraw" }, + }; + const xa = (t, e) => { + let { boxHeight: i = e, boxWidth: s = e } = t; + return ( + t.usePointStyle && + ((i = Math.min(i, e)), (s = t.pointStyleWidth || Math.min(s, e))), + { boxWidth: s, boxHeight: i, itemHeight: Math.max(e, i) } + ); + }; + class ba extends Hs { + constructor(t) { + super(), + (this._added = !1), + (this.legendHitBoxes = []), + (this._hoveredItem = null), + (this.doughnutMode = !1), + (this.chart = t.chart), + (this.options = t.options), + (this.ctx = t.ctx), + (this.legendItems = void 0), + (this.columnSizes = void 0), + (this.lineWidths = void 0), + (this.maxHeight = void 0), + (this.maxWidth = void 0), + (this.top = void 0), + (this.bottom = void 0), + (this.left = void 0), + (this.right = void 0), + (this.height = void 0), + (this.width = void 0), + (this._margins = void 0), + (this.position = void 0), + (this.weight = void 0), + (this.fullSize = void 0); + } + update(t, e, i) { + (this.maxWidth = t), + (this.maxHeight = e), + (this._margins = i), + this.setDimensions(), + this.buildLabels(), + this.fit(); + } + setDimensions() { + this.isHorizontal() + ? ((this.width = this.maxWidth), + (this.left = this._margins.left), + (this.right = this.width)) + : ((this.height = this.maxHeight), + (this.top = this._margins.top), + (this.bottom = this.height)); + } + buildLabels() { + const t = this.options.labels || {}; + let e = d(t.generateLabels, [this.chart], this) || []; + t.filter && (e = e.filter((e) => t.filter(e, this.chart.data))), + t.sort && (e = e.sort((e, i) => t.sort(e, i, this.chart.data))), + this.options.reverse && e.reverse(), + (this.legendItems = e); + } + fit() { + const { options: t, ctx: e } = this; + if (!t.display) return void (this.width = this.height = 0); + const i = t.labels, + s = Si(i.font), + n = s.size, + o = this._computeTitleHeight(), + { boxWidth: a, itemHeight: r } = xa(i, n); + let l, h; + (e.font = s.string), + this.isHorizontal() + ? ((l = this.maxWidth), (h = this._fitRows(o, n, a, r) + 10)) + : ((h = this.maxHeight), (l = this._fitCols(o, s, a, r) + 10)), + (this.width = Math.min(l, t.maxWidth || this.maxWidth)), + (this.height = Math.min(h, t.maxHeight || this.maxHeight)); + } + _fitRows(t, e, i, s) { + const { + ctx: n, + maxWidth: o, + options: { + labels: { padding: a }, + }, + } = this, + r = (this.legendHitBoxes = []), + l = (this.lineWidths = [0]), + h = s + a; + let c = t; + (n.textAlign = "left"), (n.textBaseline = "middle"); + let d = -1, + u = -h; + return ( + this.legendItems.forEach((t, f) => { + const g = i + e / 2 + n.measureText(t.text).width; + (0 === f || l[l.length - 1] + g + 2 * a > o) && + ((c += h), (l[l.length - (f > 0 ? 0 : 1)] = 0), (u += h), d++), + (r[f] = { left: 0, top: u, row: d, width: g, height: s }), + (l[l.length - 1] += g + a); + }), + c + ); + } + _fitCols(t, e, i, s) { + const { + ctx: n, + maxHeight: o, + options: { + labels: { padding: a }, + }, + } = this, + r = (this.legendHitBoxes = []), + l = (this.columnSizes = []), + h = o - t; + let c = a, + d = 0, + u = 0, + f = 0, + g = 0; + return ( + this.legendItems.forEach((t, o) => { + const { itemWidth: p, itemHeight: m } = (function (t, e, i, s, n) { + const o = (function (t, e, i, s) { + let n = t.text; + n && + "string" != typeof n && + (n = n.reduce((t, e) => (t.length > e.length ? t : e))); + return e + i.size / 2 + s.measureText(n).width; + })(s, t, e, i), + a = (function (t, e, i) { + let s = t; + "string" != typeof e.text && (s = _a(e, i)); + return s; + })(n, s, e.lineHeight); + return { itemWidth: o, itemHeight: a }; + })(i, e, n, t, s); + o > 0 && + u + m + 2 * a > h && + ((c += d + a), + l.push({ width: d, height: u }), + (f += d + a), + g++, + (d = u = 0)), + (r[o] = { left: f, top: u, col: g, width: p, height: m }), + (d = Math.max(d, p)), + (u += m + a); + }), + (c += d), + l.push({ width: d, height: u }), + c + ); + } + adjustHitBoxes() { + if (!this.options.display) return; + const t = this._computeTitleHeight(), + { + legendHitBoxes: e, + options: { + align: i, + labels: { padding: s }, + rtl: n, + }, + } = this, + o = Oi(n, this.left, this.width); + if (this.isHorizontal()) { + let n = 0, + a = ft(i, this.left + s, this.right - this.lineWidths[n]); + for (const r of e) + n !== r.row && + ((n = r.row), + (a = ft(i, this.left + s, this.right - this.lineWidths[n]))), + (r.top += this.top + t + s), + (r.left = o.leftForLtr(o.x(a), r.width)), + (a += r.width + s); + } else { + let n = 0, + a = ft(i, this.top + t + s, this.bottom - this.columnSizes[n].height); + for (const r of e) + r.col !== n && + ((n = r.col), + (a = ft( + i, + this.top + t + s, + this.bottom - this.columnSizes[n].height + ))), + (r.top = a), + (r.left += this.left + s), + (r.left = o.leftForLtr(o.x(r.left), r.width)), + (a += r.height + s); + } + } + isHorizontal() { + return ( + "top" === this.options.position || "bottom" === this.options.position + ); + } + draw() { + if (this.options.display) { + const t = this.ctx; + Ie(t, this), this._draw(), ze(t); + } + } + _draw() { + const { options: t, columnSizes: e, lineWidths: i, ctx: s } = this, + { align: n, labels: o } = t, + a = ue.color, + r = Oi(t.rtl, this.left, this.width), + h = Si(o.font), + { padding: c } = o, + d = h.size, + u = d / 2; + let f; + this.drawTitle(), + (s.textAlign = r.textAlign("left")), + (s.textBaseline = "middle"), + (s.lineWidth = 0.5), + (s.font = h.string); + const { boxWidth: g, boxHeight: p, itemHeight: m } = xa(o, d), + x = this.isHorizontal(), + b = this._computeTitleHeight(); + (f = x + ? { + x: ft(n, this.left + c, this.right - i[0]), + y: this.top + c + b, + line: 0, + } + : { + x: this.left + c, + y: ft(n, this.top + b + c, this.bottom - e[0].height), + line: 0, + }), + Ai(this.ctx, t.textDirection); + const _ = m + c; + this.legendItems.forEach((y, v) => { + (s.strokeStyle = y.fontColor), (s.fillStyle = y.fontColor); + const M = s.measureText(y.text).width, + w = r.textAlign(y.textAlign || (y.textAlign = o.textAlign)), + k = g + u + M; + let S = f.x, + P = f.y; + r.setWidth(this.width), + x + ? v > 0 && + S + k + c > this.right && + ((P = f.y += _), + f.line++, + (S = f.x = ft(n, this.left + c, this.right - i[f.line]))) + : v > 0 && + P + _ > this.bottom && + ((S = f.x = S + e[f.line].width + c), + f.line++, + (P = f.y = + ft(n, this.top + b + c, this.bottom - e[f.line].height))); + if ( + ((function (t, e, i) { + if (isNaN(g) || g <= 0 || isNaN(p) || p < 0) return; + s.save(); + const n = l(i.lineWidth, 1); + if ( + ((s.fillStyle = l(i.fillStyle, a)), + (s.lineCap = l(i.lineCap, "butt")), + (s.lineDashOffset = l(i.lineDashOffset, 0)), + (s.lineJoin = l(i.lineJoin, "miter")), + (s.lineWidth = n), + (s.strokeStyle = l(i.strokeStyle, a)), + s.setLineDash(l(i.lineDash, [])), + o.usePointStyle) + ) { + const a = { + radius: (p * Math.SQRT2) / 2, + pointStyle: i.pointStyle, + rotation: i.rotation, + borderWidth: n, + }, + l = r.xPlus(t, g / 2); + Ee(s, a, l, e + u, o.pointStyleWidth && g); + } else { + const o = e + Math.max((d - p) / 2, 0), + a = r.leftForLtr(t, g), + l = wi(i.borderRadius); + s.beginPath(), + Object.values(l).some((t) => 0 !== t) + ? He(s, { x: a, y: o, w: g, h: p, radius: l }) + : s.rect(a, o, g, p), + s.fill(), + 0 !== n && s.stroke(); + } + s.restore(); + })(r.x(S), P, y), + (S = gt(w, S + g + u, x ? S + k : this.right, t.rtl)), + (function (t, e, i) { + Ne(s, i.text, t, e + m / 2, h, { + strikethrough: i.hidden, + textAlign: r.textAlign(i.textAlign), + }); + })(r.x(S), P, y), + x) + ) + f.x += k + c; + else if ("string" != typeof y.text) { + const t = h.lineHeight; + f.y += _a(y, t) + c; + } else f.y += _; + }), + Ti(this.ctx, t.textDirection); + } + drawTitle() { + const t = this.options, + e = t.title, + i = Si(e.font), + s = ki(e.padding); + if (!e.display) return; + const n = Oi(t.rtl, this.left, this.width), + o = this.ctx, + a = e.position, + r = i.size / 2, + l = s.top + r; + let h, + c = this.left, + d = this.width; + if (this.isHorizontal()) + (d = Math.max(...this.lineWidths)), + (h = this.top + l), + (c = ft(t.align, c, this.right - d)); + else { + const e = this.columnSizes.reduce((t, e) => Math.max(t, e.height), 0); + h = + l + + ft( + t.align, + this.top, + this.bottom - e - t.labels.padding - this._computeTitleHeight() + ); + } + const u = ft(a, c, c + d); + (o.textAlign = n.textAlign(ut(a))), + (o.textBaseline = "middle"), + (o.strokeStyle = e.color), + (o.fillStyle = e.color), + (o.font = i.string), + Ne(o, e.text, u, h, i); + } + _computeTitleHeight() { + const t = this.options.title, + e = Si(t.font), + i = ki(t.padding); + return t.display ? e.lineHeight + i.height : 0; + } + _getLegendItemAt(t, e) { + let i, s, n; + if (tt(t, this.left, this.right) && tt(e, this.top, this.bottom)) + for (n = this.legendHitBoxes, i = 0; i < n.length; ++i) + if ( + ((s = n[i]), + tt(t, s.left, s.left + s.width) && tt(e, s.top, s.top + s.height)) + ) + return this.legendItems[i]; + return null; + } + handleEvent(t) { + const e = this.options; + if ( + !(function (t, e) { + if ( + ("mousemove" === t || "mouseout" === t) && + (e.onHover || e.onLeave) + ) + return !0; + if (e.onClick && ("click" === t || "mouseup" === t)) return !0; + return !1; + })(t.type, e) + ) + return; + const i = this._getLegendItemAt(t.x, t.y); + if ("mousemove" === t.type || "mouseout" === t.type) { + const o = this._hoveredItem, + a = + ((n = i), + null !== (s = o) && + null !== n && + s.datasetIndex === n.datasetIndex && + s.index === n.index); + o && !a && d(e.onLeave, [t, o, this], this), + (this._hoveredItem = i), + i && !a && d(e.onHover, [t, i, this], this); + } else i && d(e.onClick, [t, i, this], this); + var s, n; + } + } + function _a(t, e) { + return e * (t.text ? t.text.length : 0); + } + var ya = { + id: "legend", + _element: ba, + start(t, e, i) { + const s = (t.legend = new ba({ ctx: t.ctx, options: i, chart: t })); + as.configure(t, s, i), as.addBox(t, s); + }, + stop(t) { + as.removeBox(t, t.legend), delete t.legend; + }, + beforeUpdate(t, e, i) { + const s = t.legend; + as.configure(t, s, i), (s.options = i); + }, + afterUpdate(t) { + const e = t.legend; + e.buildLabels(), e.adjustHitBoxes(); + }, + afterEvent(t, e) { + e.replay || t.legend.handleEvent(e.event); + }, + defaults: { + display: !0, + position: "top", + align: "center", + fullSize: !0, + reverse: !1, + weight: 1e3, + onClick(t, e, i) { + const s = e.datasetIndex, + n = i.chart; + n.isDatasetVisible(s) + ? (n.hide(s), (e.hidden = !0)) + : (n.show(s), (e.hidden = !1)); + }, + onHover: null, + onLeave: null, + labels: { + color: (t) => t.chart.options.color, + boxWidth: 40, + padding: 10, + generateLabels(t) { + const e = t.data.datasets, + { + labels: { + usePointStyle: i, + pointStyle: s, + textAlign: n, + color: o, + useBorderRadius: a, + borderRadius: r, + }, + } = t.legend.options; + return t._getSortedDatasetMetas().map((t) => { + const l = t.controller.getStyle(i ? 0 : void 0), + h = ki(l.borderWidth); + return { + text: e[t.index].label, + fillStyle: l.backgroundColor, + fontColor: o, + hidden: !t.visible, + lineCap: l.borderCapStyle, + lineDash: l.borderDash, + lineDashOffset: l.borderDashOffset, + lineJoin: l.borderJoinStyle, + lineWidth: (h.width + h.height) / 4, + strokeStyle: l.borderColor, + pointStyle: s || l.pointStyle, + rotation: l.rotation, + textAlign: n || l.textAlign, + borderRadius: a && (r || l.borderRadius), + datasetIndex: t.index, + }; + }, this); + }, + }, + title: { + color: (t) => t.chart.options.color, + display: !1, + position: "center", + text: "", + }, + }, + descriptors: { + _scriptable: (t) => !t.startsWith("on"), + labels: { + _scriptable: (t) => !["generateLabels", "filter", "sort"].includes(t), + }, + }, + }; + class va extends Hs { + constructor(t) { + super(), + (this.chart = t.chart), + (this.options = t.options), + (this.ctx = t.ctx), + (this._padding = void 0), + (this.top = void 0), + (this.bottom = void 0), + (this.left = void 0), + (this.right = void 0), + (this.width = void 0), + (this.height = void 0), + (this.position = void 0), + (this.weight = void 0), + (this.fullSize = void 0); + } + update(t, e) { + const i = this.options; + if (((this.left = 0), (this.top = 0), !i.display)) + return void (this.width = this.height = this.right = this.bottom = 0); + (this.width = this.right = t), (this.height = this.bottom = e); + const s = n(i.text) ? i.text.length : 1; + this._padding = ki(i.padding); + const o = s * Si(i.font).lineHeight + this._padding.height; + this.isHorizontal() ? (this.height = o) : (this.width = o); + } + isHorizontal() { + const t = this.options.position; + return "top" === t || "bottom" === t; + } + _drawArgs(t) { + const { top: e, left: i, bottom: s, right: n, options: o } = this, + a = o.align; + let r, + l, + h, + c = 0; + return ( + this.isHorizontal() + ? ((l = ft(a, i, n)), (h = e + t), (r = n - i)) + : ("left" === o.position + ? ((l = i + t), (h = ft(a, s, e)), (c = -0.5 * C)) + : ((l = n - t), (h = ft(a, e, s)), (c = 0.5 * C)), + (r = s - e)), + { titleX: l, titleY: h, maxWidth: r, rotation: c } + ); + } + draw() { + const t = this.ctx, + e = this.options; + if (!e.display) return; + const i = Si(e.font), + s = i.lineHeight / 2 + this._padding.top, + { titleX: n, titleY: o, maxWidth: a, rotation: r } = this._drawArgs(s); + Ne(t, e.text, 0, 0, i, { + color: e.color, + maxWidth: a, + rotation: r, + textAlign: ut(e.align), + textBaseline: "middle", + translation: [n, o], + }); + } + } + var Ma = { + id: "title", + _element: va, + start(t, e, i) { + !(function (t, e) { + const i = new va({ ctx: t.ctx, options: e, chart: t }); + as.configure(t, i, e), as.addBox(t, i), (t.titleBlock = i); + })(t, i); + }, + stop(t) { + const e = t.titleBlock; + as.removeBox(t, e), delete t.titleBlock; + }, + beforeUpdate(t, e, i) { + const s = t.titleBlock; + as.configure(t, s, i), (s.options = i); + }, + defaults: { + align: "center", + display: !1, + font: { weight: "bold" }, + fullSize: !0, + padding: 10, + position: "top", + text: "", + weight: 2e3, + }, + defaultRoutes: { color: "color" }, + descriptors: { _scriptable: !0, _indexable: !1 }, + }; + const wa = new WeakMap(); + var ka = { + id: "subtitle", + start(t, e, i) { + const s = new va({ ctx: t.ctx, options: i, chart: t }); + as.configure(t, s, i), as.addBox(t, s), wa.set(t, s); + }, + stop(t) { + as.removeBox(t, wa.get(t)), wa.delete(t); + }, + beforeUpdate(t, e, i) { + const s = wa.get(t); + as.configure(t, s, i), (s.options = i); + }, + defaults: { + align: "center", + display: !1, + font: { weight: "normal" }, + fullSize: !0, + padding: 0, + position: "top", + text: "", + weight: 1500, + }, + defaultRoutes: { color: "color" }, + descriptors: { _scriptable: !0, _indexable: !1 }, + }; + const Sa = { + average(t) { + if (!t.length) return !1; + let e, + i, + s = new Set(), + n = 0, + o = 0; + for (e = 0, i = t.length; e < i; ++e) { + const i = t[e].element; + if (i && i.hasValue()) { + const t = i.tooltipPosition(); + s.add(t.x), (n += t.y), ++o; + } + } + if (0 === o || 0 === s.size) return !1; + return { x: [...s].reduce((t, e) => t + e) / s.size, y: n / o }; + }, + nearest(t, e) { + if (!t.length) return !1; + let i, + s, + n, + o = e.x, + a = e.y, + r = Number.POSITIVE_INFINITY; + for (i = 0, s = t.length; i < s; ++i) { + const s = t[i].element; + if (s && s.hasValue()) { + const t = q(e, s.getCenterPoint()); + t < r && ((r = t), (n = s)); + } + } + if (n) { + const t = n.tooltipPosition(); + (o = t.x), (a = t.y); + } + return { x: o, y: a }; + }, + }; + function Pa(t, e) { + return e && (n(e) ? Array.prototype.push.apply(t, e) : t.push(e)), t; + } + function Da(t) { + return ("string" == typeof t || t instanceof String) && t.indexOf("\n") > -1 + ? t.split("\n") + : t; + } + function Ca(t, e) { + const { element: i, datasetIndex: s, index: n } = e, + o = t.getDatasetMeta(s).controller, + { label: a, value: r } = o.getLabelAndValue(n); + return { + chart: t, + label: a, + parsed: o.getParsed(n), + raw: t.data.datasets[s].data[n], + formattedValue: r, + dataset: o.getDataset(), + dataIndex: n, + datasetIndex: s, + element: i, + }; + } + function Oa(t, e) { + const i = t.chart.ctx, + { body: s, footer: n, title: o } = t, + { boxWidth: a, boxHeight: r } = e, + l = Si(e.bodyFont), + h = Si(e.titleFont), + c = Si(e.footerFont), + d = o.length, + f = n.length, + g = s.length, + p = ki(e.padding); + let m = p.height, + x = 0, + b = s.reduce( + (t, e) => t + e.before.length + e.lines.length + e.after.length, + 0 + ); + if ( + ((b += t.beforeBody.length + t.afterBody.length), + d && + (m += + d * h.lineHeight + (d - 1) * e.titleSpacing + e.titleMarginBottom), + b) + ) { + m += + g * (e.displayColors ? Math.max(r, l.lineHeight) : l.lineHeight) + + (b - g) * l.lineHeight + + (b - 1) * e.bodySpacing; + } + f && + (m += e.footerMarginTop + f * c.lineHeight + (f - 1) * e.footerSpacing); + let _ = 0; + const y = function (t) { + x = Math.max(x, i.measureText(t).width + _); + }; + return ( + i.save(), + (i.font = h.string), + u(t.title, y), + (i.font = l.string), + u(t.beforeBody.concat(t.afterBody), y), + (_ = e.displayColors ? a + 2 + e.boxPadding : 0), + u(s, (t) => { + u(t.before, y), u(t.lines, y), u(t.after, y); + }), + (_ = 0), + (i.font = c.string), + u(t.footer, y), + i.restore(), + (x += p.width), + { width: x, height: m } + ); + } + function Aa(t, e, i, s) { + const { x: n, width: o } = i, + { + width: a, + chartArea: { left: r, right: l }, + } = t; + let h = "center"; + return ( + "center" === s + ? (h = n <= (r + l) / 2 ? "left" : "right") + : n <= o / 2 + ? (h = "left") + : n >= a - o / 2 && (h = "right"), + (function (t, e, i, s) { + const { x: n, width: o } = s, + a = i.caretSize + i.caretPadding; + return ( + ("left" === t && n + o + a > e.width) || + ("right" === t && n - o - a < 0) || + void 0 + ); + })(h, t, e, i) && (h = "center"), + h + ); + } + function Ta(t, e, i) { + const s = + i.yAlign || + e.yAlign || + (function (t, e) { + const { y: i, height: s } = e; + return i < s / 2 ? "top" : i > t.height - s / 2 ? "bottom" : "center"; + })(t, i); + return { xAlign: i.xAlign || e.xAlign || Aa(t, e, i, s), yAlign: s }; + } + function La(t, e, i, s) { + const { caretSize: n, caretPadding: o, cornerRadius: a } = t, + { xAlign: r, yAlign: l } = i, + h = n + o, + { topLeft: c, topRight: d, bottomLeft: u, bottomRight: f } = wi(a); + let g = (function (t, e) { + let { x: i, width: s } = t; + return "right" === e ? (i -= s) : "center" === e && (i -= s / 2), i; + })(e, r); + const p = (function (t, e, i) { + let { y: s, height: n } = t; + return "top" === e ? (s += i) : (s -= "bottom" === e ? n + i : n / 2), s; + })(e, l, h); + return ( + "center" === l + ? "left" === r + ? (g += h) + : "right" === r && (g -= h) + : "left" === r + ? (g -= Math.max(c, u) + n) + : "right" === r && (g += Math.max(d, f) + n), + { x: J(g, 0, s.width - e.width), y: J(p, 0, s.height - e.height) } + ); + } + function Ea(t, e, i) { + const s = ki(i.padding); + return "center" === e + ? t.x + t.width / 2 + : "right" === e + ? t.x + t.width - s.right + : t.x + s.left; + } + function Ra(t) { + return Pa([], Da(t)); + } + function Ia(t, e) { + const i = + e && e.dataset && e.dataset.tooltip && e.dataset.tooltip.callbacks; + return i ? t.override(i) : t; + } + const za = { + beforeTitle: e, + title(t) { + if (t.length > 0) { + const e = t[0], + i = e.chart.data.labels, + s = i ? i.length : 0; + if (this && this.options && "dataset" === this.options.mode) + return e.dataset.label || ""; + if (e.label) return e.label; + if (s > 0 && e.dataIndex < s) return i[e.dataIndex]; + } + return ""; + }, + afterTitle: e, + beforeBody: e, + beforeLabel: e, + label(t) { + if (this && this.options && "dataset" === this.options.mode) + return t.label + ": " + t.formattedValue || t.formattedValue; + let e = t.dataset.label || ""; + e && (e += ": "); + const i = t.formattedValue; + return s(i) || (e += i), e; + }, + labelColor(t) { + const e = t.chart + .getDatasetMeta(t.datasetIndex) + .controller.getStyle(t.dataIndex); + return { + borderColor: e.borderColor, + backgroundColor: e.backgroundColor, + borderWidth: e.borderWidth, + borderDash: e.borderDash, + borderDashOffset: e.borderDashOffset, + borderRadius: 0, + }; + }, + labelTextColor() { + return this.options.bodyColor; + }, + labelPointStyle(t) { + const e = t.chart + .getDatasetMeta(t.datasetIndex) + .controller.getStyle(t.dataIndex); + return { pointStyle: e.pointStyle, rotation: e.rotation }; + }, + afterLabel: e, + afterBody: e, + beforeFooter: e, + footer: e, + afterFooter: e, + }; + function Fa(t, e, i, s) { + const n = t[e].call(i, s); + return void 0 === n ? za[e].call(i, s) : n; + } + class Va extends Hs { + static positioners = Sa; + constructor(t) { + super(), + (this.opacity = 0), + (this._active = []), + (this._eventPosition = void 0), + (this._size = void 0), + (this._cachedAnimations = void 0), + (this._tooltipItems = []), + (this.$animations = void 0), + (this.$context = void 0), + (this.chart = t.chart), + (this.options = t.options), + (this.dataPoints = void 0), + (this.title = void 0), + (this.beforeBody = void 0), + (this.body = void 0), + (this.afterBody = void 0), + (this.footer = void 0), + (this.xAlign = void 0), + (this.yAlign = void 0), + (this.x = void 0), + (this.y = void 0), + (this.height = void 0), + (this.width = void 0), + (this.caretX = void 0), + (this.caretY = void 0), + (this.labelColors = void 0), + (this.labelPointStyles = void 0), + (this.labelTextColors = void 0); + } + initialize(t) { + (this.options = t), + (this._cachedAnimations = void 0), + (this.$context = void 0); + } + _resolveAnimations() { + const t = this._cachedAnimations; + if (t) return t; + const e = this.chart, + i = this.options.setContext(this.getContext()), + s = i.enabled && e.options.animation && i.animations, + n = new Os(this.chart, s); + return s._cacheable && (this._cachedAnimations = Object.freeze(n)), n; + } + getContext() { + return ( + this.$context || + (this.$context = + ((t = this.chart.getContext()), + (e = this), + (i = this._tooltipItems), + Ci(t, { tooltip: e, tooltipItems: i, type: "tooltip" }))) + ); + var t, e, i; + } + getTitle(t, e) { + const { callbacks: i } = e, + s = Fa(i, "beforeTitle", this, t), + n = Fa(i, "title", this, t), + o = Fa(i, "afterTitle", this, t); + let a = []; + return (a = Pa(a, Da(s))), (a = Pa(a, Da(n))), (a = Pa(a, Da(o))), a; + } + getBeforeBody(t, e) { + return Ra(Fa(e.callbacks, "beforeBody", this, t)); + } + getBody(t, e) { + const { callbacks: i } = e, + s = []; + return ( + u(t, (t) => { + const e = { before: [], lines: [], after: [] }, + n = Ia(i, t); + Pa(e.before, Da(Fa(n, "beforeLabel", this, t))), + Pa(e.lines, Fa(n, "label", this, t)), + Pa(e.after, Da(Fa(n, "afterLabel", this, t))), + s.push(e); + }), + s + ); + } + getAfterBody(t, e) { + return Ra(Fa(e.callbacks, "afterBody", this, t)); + } + getFooter(t, e) { + const { callbacks: i } = e, + s = Fa(i, "beforeFooter", this, t), + n = Fa(i, "footer", this, t), + o = Fa(i, "afterFooter", this, t); + let a = []; + return (a = Pa(a, Da(s))), (a = Pa(a, Da(n))), (a = Pa(a, Da(o))), a; + } + _createItems(t) { + const e = this._active, + i = this.chart.data, + s = [], + n = [], + o = []; + let a, + r, + l = []; + for (a = 0, r = e.length; a < r; ++a) l.push(Ca(this.chart, e[a])); + return ( + t.filter && (l = l.filter((e, s, n) => t.filter(e, s, n, i))), + t.itemSort && (l = l.sort((e, s) => t.itemSort(e, s, i))), + u(l, (e) => { + const i = Ia(t.callbacks, e); + s.push(Fa(i, "labelColor", this, e)), + n.push(Fa(i, "labelPointStyle", this, e)), + o.push(Fa(i, "labelTextColor", this, e)); + }), + (this.labelColors = s), + (this.labelPointStyles = n), + (this.labelTextColors = o), + (this.dataPoints = l), + l + ); + } + update(t, e) { + const i = this.options.setContext(this.getContext()), + s = this._active; + let n, + o = []; + if (s.length) { + const t = Sa[i.position].call(this, s, this._eventPosition); + (o = this._createItems(i)), + (this.title = this.getTitle(o, i)), + (this.beforeBody = this.getBeforeBody(o, i)), + (this.body = this.getBody(o, i)), + (this.afterBody = this.getAfterBody(o, i)), + (this.footer = this.getFooter(o, i)); + const e = (this._size = Oa(this, i)), + a = Object.assign({}, t, e), + r = Ta(this.chart, i, a), + l = La(i, a, r, this.chart); + (this.xAlign = r.xAlign), + (this.yAlign = r.yAlign), + (n = { + opacity: 1, + x: l.x, + y: l.y, + width: e.width, + height: e.height, + caretX: t.x, + caretY: t.y, + }); + } else 0 !== this.opacity && (n = { opacity: 0 }); + (this._tooltipItems = o), + (this.$context = void 0), + n && this._resolveAnimations().update(this, n), + t && + i.external && + i.external.call(this, { + chart: this.chart, + tooltip: this, + replay: e, + }); + } + drawCaret(t, e, i, s) { + const n = this.getCaretPosition(t, i, s); + e.lineTo(n.x1, n.y1), e.lineTo(n.x2, n.y2), e.lineTo(n.x3, n.y3); + } + getCaretPosition(t, e, i) { + const { xAlign: s, yAlign: n } = this, + { caretSize: o, cornerRadius: a } = i, + { topLeft: r, topRight: l, bottomLeft: h, bottomRight: c } = wi(a), + { x: d, y: u } = t, + { width: f, height: g } = e; + let p, m, x, b, _, y; + return ( + "center" === n + ? ((_ = u + g / 2), + "left" === s + ? ((p = d), (m = p - o), (b = _ + o), (y = _ - o)) + : ((p = d + f), (m = p + o), (b = _ - o), (y = _ + o)), + (x = p)) + : ((m = + "left" === s + ? d + Math.max(r, h) + o + : "right" === s + ? d + f - Math.max(l, c) - o + : this.caretX), + "top" === n + ? ((b = u), (_ = b - o), (p = m - o), (x = m + o)) + : ((b = u + g), (_ = b + o), (p = m + o), (x = m - o)), + (y = b)), + { x1: p, x2: m, x3: x, y1: b, y2: _, y3: y } + ); + } + drawTitle(t, e, i) { + const s = this.title, + n = s.length; + let o, a, r; + if (n) { + const l = Oi(i.rtl, this.x, this.width); + for ( + t.x = Ea(this, i.titleAlign, i), + e.textAlign = l.textAlign(i.titleAlign), + e.textBaseline = "middle", + o = Si(i.titleFont), + a = i.titleSpacing, + e.fillStyle = i.titleColor, + e.font = o.string, + r = 0; + r < n; + ++r + ) + e.fillText(s[r], l.x(t.x), t.y + o.lineHeight / 2), + (t.y += o.lineHeight + a), + r + 1 === n && (t.y += i.titleMarginBottom - a); + } + } + _drawColorBox(t, e, i, s, n) { + const a = this.labelColors[i], + r = this.labelPointStyles[i], + { boxHeight: l, boxWidth: h } = n, + c = Si(n.bodyFont), + d = Ea(this, "left", n), + u = s.x(d), + f = l < c.lineHeight ? (c.lineHeight - l) / 2 : 0, + g = e.y + f; + if (n.usePointStyle) { + const e = { + radius: Math.min(h, l) / 2, + pointStyle: r.pointStyle, + rotation: r.rotation, + borderWidth: 1, + }, + i = s.leftForLtr(u, h) + h / 2, + o = g + l / 2; + (t.strokeStyle = n.multiKeyBackground), + (t.fillStyle = n.multiKeyBackground), + Le(t, e, i, o), + (t.strokeStyle = a.borderColor), + (t.fillStyle = a.backgroundColor), + Le(t, e, i, o); + } else { + (t.lineWidth = o(a.borderWidth) + ? Math.max(...Object.values(a.borderWidth)) + : a.borderWidth || 1), + (t.strokeStyle = a.borderColor), + t.setLineDash(a.borderDash || []), + (t.lineDashOffset = a.borderDashOffset || 0); + const e = s.leftForLtr(u, h), + i = s.leftForLtr(s.xPlus(u, 1), h - 2), + r = wi(a.borderRadius); + Object.values(r).some((t) => 0 !== t) + ? (t.beginPath(), + (t.fillStyle = n.multiKeyBackground), + He(t, { x: e, y: g, w: h, h: l, radius: r }), + t.fill(), + t.stroke(), + (t.fillStyle = a.backgroundColor), + t.beginPath(), + He(t, { x: i, y: g + 1, w: h - 2, h: l - 2, radius: r }), + t.fill()) + : ((t.fillStyle = n.multiKeyBackground), + t.fillRect(e, g, h, l), + t.strokeRect(e, g, h, l), + (t.fillStyle = a.backgroundColor), + t.fillRect(i, g + 1, h - 2, l - 2)); + } + t.fillStyle = this.labelTextColors[i]; + } + drawBody(t, e, i) { + const { body: s } = this, + { + bodySpacing: n, + bodyAlign: o, + displayColors: a, + boxHeight: r, + boxWidth: l, + boxPadding: h, + } = i, + c = Si(i.bodyFont); + let d = c.lineHeight, + f = 0; + const g = Oi(i.rtl, this.x, this.width), + p = function (i) { + e.fillText(i, g.x(t.x + f), t.y + d / 2), (t.y += d + n); + }, + m = g.textAlign(o); + let x, b, _, y, v, M, w; + for ( + e.textAlign = o, + e.textBaseline = "middle", + e.font = c.string, + t.x = Ea(this, m, i), + e.fillStyle = i.bodyColor, + u(this.beforeBody, p), + f = a && "right" !== m ? ("center" === o ? l / 2 + h : l + 2 + h) : 0, + y = 0, + M = s.length; + y < M; + ++y + ) { + for ( + x = s[y], + b = this.labelTextColors[y], + e.fillStyle = b, + u(x.before, p), + _ = x.lines, + a && + _.length && + (this._drawColorBox(e, t, y, g, i), + (d = Math.max(c.lineHeight, r))), + v = 0, + w = _.length; + v < w; + ++v + ) + p(_[v]), (d = c.lineHeight); + u(x.after, p); + } + (f = 0), (d = c.lineHeight), u(this.afterBody, p), (t.y -= n); + } + drawFooter(t, e, i) { + const s = this.footer, + n = s.length; + let o, a; + if (n) { + const r = Oi(i.rtl, this.x, this.width); + for ( + t.x = Ea(this, i.footerAlign, i), + t.y += i.footerMarginTop, + e.textAlign = r.textAlign(i.footerAlign), + e.textBaseline = "middle", + o = Si(i.footerFont), + e.fillStyle = i.footerColor, + e.font = o.string, + a = 0; + a < n; + ++a + ) + e.fillText(s[a], r.x(t.x), t.y + o.lineHeight / 2), + (t.y += o.lineHeight + i.footerSpacing); + } + } + drawBackground(t, e, i, s) { + const { xAlign: n, yAlign: o } = this, + { x: a, y: r } = t, + { width: l, height: h } = i, + { + topLeft: c, + topRight: d, + bottomLeft: u, + bottomRight: f, + } = wi(s.cornerRadius); + (e.fillStyle = s.backgroundColor), + (e.strokeStyle = s.borderColor), + (e.lineWidth = s.borderWidth), + e.beginPath(), + e.moveTo(a + c, r), + "top" === o && this.drawCaret(t, e, i, s), + e.lineTo(a + l - d, r), + e.quadraticCurveTo(a + l, r, a + l, r + d), + "center" === o && "right" === n && this.drawCaret(t, e, i, s), + e.lineTo(a + l, r + h - f), + e.quadraticCurveTo(a + l, r + h, a + l - f, r + h), + "bottom" === o && this.drawCaret(t, e, i, s), + e.lineTo(a + u, r + h), + e.quadraticCurveTo(a, r + h, a, r + h - u), + "center" === o && "left" === n && this.drawCaret(t, e, i, s), + e.lineTo(a, r + c), + e.quadraticCurveTo(a, r, a + c, r), + e.closePath(), + e.fill(), + s.borderWidth > 0 && e.stroke(); + } + _updateAnimationTarget(t) { + const e = this.chart, + i = this.$animations, + s = i && i.x, + n = i && i.y; + if (s || n) { + const i = Sa[t.position].call(this, this._active, this._eventPosition); + if (!i) return; + const o = (this._size = Oa(this, t)), + a = Object.assign({}, i, this._size), + r = Ta(e, t, a), + l = La(t, a, r, e); + (s._to === l.x && n._to === l.y) || + ((this.xAlign = r.xAlign), + (this.yAlign = r.yAlign), + (this.width = o.width), + (this.height = o.height), + (this.caretX = i.x), + (this.caretY = i.y), + this._resolveAnimations().update(this, l)); + } + } + _willRender() { + return !!this.opacity; + } + draw(t) { + const e = this.options.setContext(this.getContext()); + let i = this.opacity; + if (!i) return; + this._updateAnimationTarget(e); + const s = { width: this.width, height: this.height }, + n = { x: this.x, y: this.y }; + i = Math.abs(i) < 0.001 ? 0 : i; + const o = ki(e.padding), + a = + this.title.length || + this.beforeBody.length || + this.body.length || + this.afterBody.length || + this.footer.length; + e.enabled && + a && + (t.save(), + (t.globalAlpha = i), + this.drawBackground(n, t, s, e), + Ai(t, e.textDirection), + (n.y += o.top), + this.drawTitle(n, t, e), + this.drawBody(n, t, e), + this.drawFooter(n, t, e), + Ti(t, e.textDirection), + t.restore()); + } + getActiveElements() { + return this._active || []; + } + setActiveElements(t, e) { + const i = this._active, + s = t.map(({ datasetIndex: t, index: e }) => { + const i = this.chart.getDatasetMeta(t); + if (!i) throw new Error("Cannot find a dataset at index " + t); + return { datasetIndex: t, element: i.data[e], index: e }; + }), + n = !f(i, s), + o = this._positionChanged(s, e); + (n || o) && + ((this._active = s), + (this._eventPosition = e), + (this._ignoreReplayEvents = !0), + this.update(!0)); + } + handleEvent(t, e, i = !0) { + if (e && this._ignoreReplayEvents) return !1; + this._ignoreReplayEvents = !1; + const s = this.options, + n = this._active || [], + o = this._getActiveElements(t, n, e, i), + a = this._positionChanged(o, t), + r = e || !f(o, n) || a; + return ( + r && + ((this._active = o), + (s.enabled || s.external) && + ((this._eventPosition = { x: t.x, y: t.y }), this.update(!0, e))), + r + ); + } + _getActiveElements(t, e, i, s) { + const n = this.options; + if ("mouseout" === t.type) return []; + if (!s) + return e.filter( + (t) => + this.chart.data.datasets[t.datasetIndex] && + void 0 !== + this.chart + .getDatasetMeta(t.datasetIndex) + .controller.getParsed(t.index) + ); + const o = this.chart.getElementsAtEventForMode(t, n.mode, n, i); + return n.reverse && o.reverse(), o; + } + _positionChanged(t, e) { + const { caretX: i, caretY: s, options: n } = this, + o = Sa[n.position].call(this, t, e); + return !1 !== o && (i !== o.x || s !== o.y); + } + } + var Ba = { + id: "tooltip", + _element: Va, + positioners: Sa, + afterInit(t, e, i) { + i && (t.tooltip = new Va({ chart: t, options: i })); + }, + beforeUpdate(t, e, i) { + t.tooltip && t.tooltip.initialize(i); + }, + reset(t, e, i) { + t.tooltip && t.tooltip.initialize(i); + }, + afterDraw(t) { + const e = t.tooltip; + if (e && e._willRender()) { + const i = { tooltip: e }; + if ( + !1 === t.notifyPlugins("beforeTooltipDraw", { ...i, cancelable: !0 }) + ) + return; + e.draw(t.ctx), t.notifyPlugins("afterTooltipDraw", i); + } + }, + afterEvent(t, e) { + if (t.tooltip) { + const i = e.replay; + t.tooltip.handleEvent(e.event, i, e.inChartArea) && (e.changed = !0); + } + }, + defaults: { + enabled: !0, + external: null, + position: "average", + backgroundColor: "rgba(0,0,0,0.8)", + titleColor: "#fff", + titleFont: { weight: "bold" }, + titleSpacing: 2, + titleMarginBottom: 6, + titleAlign: "left", + bodyColor: "#fff", + bodySpacing: 2, + bodyFont: {}, + bodyAlign: "left", + footerColor: "#fff", + footerSpacing: 2, + footerMarginTop: 6, + footerFont: { weight: "bold" }, + footerAlign: "left", + padding: 6, + caretPadding: 2, + caretSize: 5, + cornerRadius: 6, + boxHeight: (t, e) => e.bodyFont.size, + boxWidth: (t, e) => e.bodyFont.size, + multiKeyBackground: "#fff", + displayColors: !0, + boxPadding: 0, + borderColor: "rgba(0,0,0,0)", + borderWidth: 0, + animation: { duration: 400, easing: "easeOutQuart" }, + animations: { + numbers: { + type: "number", + properties: ["x", "y", "width", "height", "caretX", "caretY"], + }, + opacity: { easing: "linear", duration: 200 }, + }, + callbacks: za, + }, + defaultRoutes: { bodyFont: "font", footerFont: "font", titleFont: "font" }, + descriptors: { + _scriptable: (t) => + "filter" !== t && "itemSort" !== t && "external" !== t, + _indexable: !1, + callbacks: { _scriptable: !1, _indexable: !1 }, + animation: { _fallback: !1 }, + animations: { _fallback: "animation" }, + }, + additionalOptionScopes: ["interaction"], + }; + return ( + An.register(Yn, jo, fo, t), + (An.helpers = { ...Wi }), + (An._adapters = Rn), + (An.Animation = Cs), + (An.Animations = Os), + (An.animator = bt), + (An.controllers = en.controllers.items), + (An.DatasetController = Ns), + (An.Element = Hs), + (An.elements = fo), + (An.Interaction = Xi), + (An.layouts = as), + (An.platforms = Ss), + (An.Scale = Js), + (An.Ticks = ae), + Object.assign(An, Yn, jo, fo, t, Ss), + (An.Chart = An), + "undefined" != typeof window && (window.Chart = An), + An + ); +}); +//# sourceMappingURL=chart.umd.js.map + +/*! + * chartjs-plugin-datalabels v2.2.0 + * https://chartjs-plugin-datalabels.netlify.app + * (c) 2017-2022 chartjs-plugin-datalabels contributors + * Released under the MIT license + */ +!(function (t, e) { + "object" == typeof exports && "undefined" != typeof module + ? (module.exports = e(require("chart.js/helpers"), require("chart.js"))) + : "function" == typeof define && define.amd + ? define(["chart.js/helpers", "chart.js"], e) + : ((t = + "undefined" != typeof globalThis + ? globalThis + : t || self).ChartDataLabels = e(t.Chart.helpers, t.Chart)); +})(this, function (t, e) { + "use strict"; + var r = (function () { + if ("undefined" != typeof window) { + if (window.devicePixelRatio) return window.devicePixelRatio; + var t = window.screen; + if (t) return (t.deviceXDPI || 1) / (t.logicalXDPI || 1); + } + return 1; + })(), + a = function (e) { + var r, + a = []; + for (e = [].concat(e); e.length; ) + "string" == typeof (r = e.pop()) + ? a.unshift.apply(a, r.split("\n")) + : Array.isArray(r) + ? e.push.apply(e, r) + : t.isNullOrUndef(e) || a.unshift("" + r); + return a; + }, + o = function (t, e, r) { + var a, + o = [].concat(e), + n = o.length, + i = t.font, + l = 0; + for (t.font = r.string, a = 0; a < n; ++a) + l = Math.max(t.measureText(o[a]).width, l); + return (t.font = i), { height: n * r.lineHeight, width: l }; + }, + n = function (t, e, r) { + return Math.max(t, Math.min(e, r)); + }, + i = function (t, e) { + var r, + a, + o, + n, + i = t.slice(), + l = []; + for (r = 0, o = e.length; r < o; ++r) + (n = e[r]), -1 === (a = i.indexOf(n)) ? l.push([n, 1]) : i.splice(a, 1); + for (r = 0, o = i.length; r < o; ++r) l.push([i[r], -1]); + return l; + }; + function l(t, e) { + var r = e.x, + a = e.y; + if (null === r) return { x: 0, y: -1 }; + if (null === a) return { x: 1, y: 0 }; + var o = t.x - r, + n = t.y - a, + i = Math.sqrt(o * o + n * n); + return { x: i ? o / i : 0, y: i ? n / i : -1 }; + } + function s(t, e, r) { + var a = 0; + return ( + t < r.left ? (a |= 1) : t > r.right && (a |= 2), + e < r.top ? (a |= 8) : e > r.bottom && (a |= 4), + a + ); + } + function u(t, e) { + var r, + a, + o = e.anchor, + n = t; + return ( + e.clamp && + (n = (function (t, e) { + for ( + var r, + a, + o, + n = t.x0, + i = t.y0, + l = t.x1, + u = t.y1, + d = s(n, i, e), + c = s(l, u, e); + d | c && !(d & c); + + ) + 8 & (r = d || c) + ? ((a = n + ((l - n) * (e.top - i)) / (u - i)), (o = e.top)) + : 4 & r + ? ((a = n + ((l - n) * (e.bottom - i)) / (u - i)), (o = e.bottom)) + : 2 & r + ? ((o = i + ((u - i) * (e.right - n)) / (l - n)), (a = e.right)) + : 1 & r && + ((o = i + ((u - i) * (e.left - n)) / (l - n)), (a = e.left)), + r === d + ? (d = s((n = a), (i = o), e)) + : (c = s((l = a), (u = o), e)); + return { x0: n, x1: l, y0: i, y1: u }; + })(n, e.area)), + "start" === o + ? ((r = n.x0), (a = n.y0)) + : "end" === o + ? ((r = n.x1), (a = n.y1)) + : ((r = (n.x0 + n.x1) / 2), (a = (n.y0 + n.y1) / 2)), + (function (t, e, r, a, o) { + switch (o) { + case "center": + r = a = 0; + break; + case "bottom": + (r = 0), (a = 1); + break; + case "right": + (r = 1), (a = 0); + break; + case "left": + (r = -1), (a = 0); + break; + case "top": + (r = 0), (a = -1); + break; + case "start": + (r = -r), (a = -a); + break; + case "end": + break; + default: + (o *= Math.PI / 180), (r = Math.cos(o)), (a = Math.sin(o)); + } + return { x: t, y: e, vx: r, vy: a }; + })(r, a, t.vx, t.vy, e.align) + ); + } + var d = function (t, e) { + var r = (t.startAngle + t.endAngle) / 2, + a = Math.cos(r), + o = Math.sin(r), + n = t.innerRadius, + i = t.outerRadius; + return u( + { + x0: t.x + a * n, + y0: t.y + o * n, + x1: t.x + a * i, + y1: t.y + o * i, + vx: a, + vy: o, + }, + e + ); + }, + c = function (t, e) { + var r = l(t, e.origin), + a = r.x * t.options.radius, + o = r.y * t.options.radius; + return u( + { + x0: t.x - a, + y0: t.y - o, + x1: t.x + a, + y1: t.y + o, + vx: r.x, + vy: r.y, + }, + e + ); + }, + h = function (t, e) { + var r = l(t, e.origin), + a = t.x, + o = t.y, + n = 0, + i = 0; + return ( + t.horizontal + ? ((a = Math.min(t.x, t.base)), (n = Math.abs(t.base - t.x))) + : ((o = Math.min(t.y, t.base)), (i = Math.abs(t.base - t.y))), + u({ x0: a, y0: o + i, x1: a + n, y1: o, vx: r.x, vy: r.y }, e) + ); + }, + f = function (t, e) { + var r = l(t, e.origin); + return u( + { + x0: t.x, + y0: t.y, + x1: t.x + (t.width || 0), + y1: t.y + (t.height || 0), + vx: r.x, + vy: r.y, + }, + e + ); + }, + x = function (t) { + return Math.round(t * r) / r; + }; + function y(t, e) { + var r = e.chart.getDatasetMeta(e.datasetIndex).vScale; + if (!r) return null; + if (void 0 !== r.xCenter && void 0 !== r.yCenter) + return { x: r.xCenter, y: r.yCenter }; + var a = r.getBasePixel(); + return t.horizontal ? { x: a, y: null } : { x: null, y: a }; + } + function v(t, e, r) { + var a = r.backgroundColor, + o = r.borderColor, + n = r.borderWidth; + (a || (o && n)) && + (t.beginPath(), + (function (t, e, r, a, o, n) { + var i = Math.PI / 2; + if (n) { + var l = Math.min(n, o / 2, a / 2), + s = e + l, + u = r + l, + d = e + a - l, + c = r + o - l; + t.moveTo(e, u), + s < d && u < c + ? (t.arc(s, u, l, -Math.PI, -i), + t.arc(d, u, l, -i, 0), + t.arc(d, c, l, 0, i), + t.arc(s, c, l, i, Math.PI)) + : s < d + ? (t.moveTo(s, r), + t.arc(d, u, l, -i, i), + t.arc(s, u, l, i, Math.PI + i)) + : u < c + ? (t.arc(s, u, l, -Math.PI, 0), t.arc(s, c, l, 0, Math.PI)) + : t.arc(s, u, l, -Math.PI, Math.PI), + t.closePath(), + t.moveTo(e, r); + } else t.rect(e, r, a, o); + })( + t, + x(e.x) + n / 2, + x(e.y) + n / 2, + x(e.w) - n, + x(e.h) - n, + r.borderRadius + ), + t.closePath(), + a && ((t.fillStyle = a), t.fill()), + o && + n && + ((t.strokeStyle = o), + (t.lineWidth = n), + (t.lineJoin = "miter"), + t.stroke())); + } + function b(t, e, r) { + var a = t.shadowBlur, + o = r.stroked, + n = x(r.x), + i = x(r.y), + l = x(r.w); + o && t.strokeText(e, n, i, l), + r.filled && + (a && o && (t.shadowBlur = 0), + t.fillText(e, n, i, l), + a && o && (t.shadowBlur = a)); + } + var _ = function (t, e, r, a) { + var o = this; + (o._config = t), + (o._index = a), + (o._model = null), + (o._rects = null), + (o._ctx = e), + (o._el = r); + }; + t.merge(_.prototype, { + _modelize: function (r, a, n, i) { + var l, + s = this, + u = s._index, + x = t.toFont(t.resolve([n.font, {}], i, u)), + v = t.resolve([n.color, e.defaults.color], i, u); + return { + align: t.resolve([n.align, "center"], i, u), + anchor: t.resolve([n.anchor, "center"], i, u), + area: i.chart.chartArea, + backgroundColor: t.resolve([n.backgroundColor, null], i, u), + borderColor: t.resolve([n.borderColor, null], i, u), + borderRadius: t.resolve([n.borderRadius, 0], i, u), + borderWidth: t.resolve([n.borderWidth, 0], i, u), + clamp: t.resolve([n.clamp, !1], i, u), + clip: t.resolve([n.clip, !1], i, u), + color: v, + display: r, + font: x, + lines: a, + offset: t.resolve([n.offset, 4], i, u), + opacity: t.resolve([n.opacity, 1], i, u), + origin: y(s._el, i), + padding: t.toPadding(t.resolve([n.padding, 4], i, u)), + positioner: + ((l = s._el), + l instanceof e.ArcElement + ? d + : l instanceof e.PointElement + ? c + : l instanceof e.BarElement + ? h + : f), + rotation: t.resolve([n.rotation, 0], i, u) * (Math.PI / 180), + size: o(s._ctx, a, x), + textAlign: t.resolve([n.textAlign, "start"], i, u), + textShadowBlur: t.resolve([n.textShadowBlur, 0], i, u), + textShadowColor: t.resolve([n.textShadowColor, v], i, u), + textStrokeColor: t.resolve([n.textStrokeColor, v], i, u), + textStrokeWidth: t.resolve([n.textStrokeWidth, 0], i, u), + }; + }, + update: function (e) { + var r, + o, + n, + i = this, + l = null, + s = null, + u = i._index, + d = i._config, + c = t.resolve([d.display, !0], e, u); + c && + ((r = e.dataset.data[u]), + (o = t.valueOrDefault(t.callback(d.formatter, [r, e]), r)), + (n = t.isNullOrUndef(o) ? [] : a(o)).length && + (s = (function (t) { + var e = t.borderWidth || 0, + r = t.padding, + a = t.size.height, + o = t.size.width, + n = -o / 2, + i = -a / 2; + return { + frame: { + x: n - r.left - e, + y: i - r.top - e, + w: o + r.width + 2 * e, + h: a + r.height + 2 * e, + }, + text: { x: n, y: i, w: o, h: a }, + }; + })((l = i._modelize(c, n, d, e))))), + (i._model = l), + (i._rects = s); + }, + geometry: function () { + return this._rects ? this._rects.frame : {}; + }, + rotation: function () { + return this._model ? this._model.rotation : 0; + }, + visible: function () { + return this._model && this._model.opacity; + }, + model: function () { + return this._model; + }, + draw: function (t, e) { + var r, + a = t.ctx, + o = this._model, + i = this._rects; + this.visible() && + (a.save(), + o.clip && + ((r = o.area), + a.beginPath(), + a.rect(r.left, r.top, r.right - r.left, r.bottom - r.top), + a.clip()), + (a.globalAlpha = n(0, o.opacity, 1)), + a.translate(x(e.x), x(e.y)), + a.rotate(o.rotation), + v(a, i.frame, o), + (function (t, e, r, a) { + var o, + n = a.textAlign, + i = a.color, + l = !!i, + s = a.font, + u = e.length, + d = a.textStrokeColor, + c = a.textStrokeWidth, + h = d && c; + if (u && (l || h)) + for ( + r = (function (t, e, r) { + var a = r.lineHeight, + o = t.w, + n = t.x; + return ( + "center" === e + ? (n += o / 2) + : ("end" !== e && "right" !== e) || (n += o), + { h: a, w: o, x: n, y: t.y + a / 2 } + ); + })(r, n, s), + t.font = s.string, + t.textAlign = n, + t.textBaseline = "middle", + t.shadowBlur = a.textShadowBlur, + t.shadowColor = a.textShadowColor, + l && (t.fillStyle = i), + h && + ((t.lineJoin = "round"), + (t.lineWidth = c), + (t.strokeStyle = d)), + o = 0, + u = e.length; + o < u; + ++o + ) + b(t, e[o], { + stroked: h, + filled: l, + w: r.w, + x: r.x, + y: r.y + r.h * o, + }); + })(a, o.lines, i.text, o), + a.restore()); + }, + }); + var p = Number.MIN_SAFE_INTEGER || -9007199254740991, + g = Number.MAX_SAFE_INTEGER || 9007199254740991; + function m(t, e, r) { + var a = Math.cos(r), + o = Math.sin(r), + n = e.x, + i = e.y; + return { + x: n + a * (t.x - n) - o * (t.y - i), + y: i + o * (t.x - n) + a * (t.y - i), + }; + } + function w(t, e) { + var r, + a, + o, + n, + i, + l = g, + s = p, + u = e.origin; + for (r = 0; r < t.length; ++r) + (o = (a = t[r]).x - u.x), + (n = a.y - u.y), + (i = e.vx * o + e.vy * n), + (l = Math.min(l, i)), + (s = Math.max(s, i)); + return { min: l, max: s }; + } + function M(t, e) { + var r = e.x - t.x, + a = e.y - t.y, + o = Math.sqrt(r * r + a * a); + return { vx: (e.x - t.x) / o, vy: (e.y - t.y) / o, origin: t, ln: o }; + } + var k = function () { + (this._rotation = 0), (this._rect = { x: 0, y: 0, w: 0, h: 0 }); + }; + function $(t, e, r) { + var a = e.positioner(t, e), + o = a.vx, + n = a.vy; + if (!o && !n) return { x: a.x, y: a.y }; + var i = r.w, + l = r.h, + s = e.rotation, + u = Math.abs((i / 2) * Math.cos(s)) + Math.abs((l / 2) * Math.sin(s)), + d = Math.abs((i / 2) * Math.sin(s)) + Math.abs((l / 2) * Math.cos(s)), + c = 1 / Math.max(Math.abs(o), Math.abs(n)); + return ( + (u *= o * c), + (d *= n * c), + (u += e.offset * o), + (d += e.offset * n), + { x: a.x + u, y: a.y + d } + ); + } + t.merge(k.prototype, { + center: function () { + var t = this._rect; + return { x: t.x + t.w / 2, y: t.y + t.h / 2 }; + }, + update: function (t, e, r) { + (this._rotation = r), + (this._rect = { x: e.x + t.x, y: e.y + t.y, w: e.w, h: e.h }); + }, + contains: function (t) { + var e = this, + r = e._rect; + return !( + (t = m(t, e.center(), -e._rotation)).x < r.x - 1 || + t.y < r.y - 1 || + t.x > r.x + r.w + 2 || + t.y > r.y + r.h + 2 + ); + }, + intersects: function (t) { + var e, + r, + a, + o = this._points(), + n = t._points(), + i = [M(o[0], o[1]), M(o[0], o[3])]; + for ( + this._rotation !== t._rotation && i.push(M(n[0], n[1]), M(n[0], n[3])), + e = 0; + e < i.length; + ++e + ) + if ( + ((r = w(o, i[e])), (a = w(n, i[e])), r.max < a.min || a.max < r.min) + ) + return !1; + return !0; + }, + _points: function () { + var t = this, + e = t._rect, + r = t._rotation, + a = t.center(); + return [ + m({ x: e.x, y: e.y }, a, r), + m({ x: e.x + e.w, y: e.y }, a, r), + m({ x: e.x + e.w, y: e.y + e.h }, a, r), + m({ x: e.x, y: e.y + e.h }, a, r), + ]; + }, + }); + var C = { + prepare: function (t) { + var e, + r, + a, + o, + n, + i = []; + for (e = 0, a = t.length; e < a; ++e) + for (r = 0, o = t[e].length; r < o; ++r) + (n = t[e][r]), + i.push(n), + (n.$layout = { + _box: new k(), + _hidable: !1, + _visible: !0, + _set: e, + _idx: n._index, + }); + return ( + i.sort(function (t, e) { + var r = t.$layout, + a = e.$layout; + return r._idx === a._idx ? a._set - r._set : a._idx - r._idx; + }), + this.update(i), + i + ); + }, + update: function (t) { + var e, + r, + a, + o, + n, + i = !1; + for (e = 0, r = t.length; e < r; ++e) + (o = (a = t[e]).model()), + ((n = a.$layout)._hidable = o && "auto" === o.display), + (n._visible = a.visible()), + (i |= n._hidable); + i && + (function (t) { + var e, r, a, o, n, i, l; + for (e = 0, r = t.length; e < r; ++e) + (o = (a = t[e]).$layout)._visible && + ((l = new Proxy(a._el, { + get: (t, e) => t.getProps([e], !0)[e], + })), + (n = a.geometry()), + (i = $(l, a.model(), n)), + o._box.update(i, n, a.rotation())); + (function (t, e) { + var r, a, o, n; + for (r = t.length - 1; r >= 0; --r) + for (o = t[r].$layout, a = r - 1; a >= 0 && o._visible; --a) + (n = t[a].$layout)._visible && + o._box.intersects(n._box) && + e(o, n); + })(t, function (t, e) { + var r = t._hidable, + a = e._hidable; + (r && a) || a ? (e._visible = !1) : r && (t._visible = !1); + }); + })(t); + }, + lookup: function (t, e) { + var r, a; + for (r = t.length - 1; r >= 0; --r) + if ((a = t[r].$layout) && a._visible && a._box.contains(e)) + return t[r]; + return null; + }, + draw: function (t, e) { + var r, a, o, n, i, l; + for (r = 0, a = e.length; r < a; ++r) + (n = (o = e[r]).$layout)._visible && + ((i = o.geometry()), + (l = $(o._el, o.model(), i)), + n._box.update(l, i, o.rotation()), + o.draw(t, l)); + }, + }, + P = "$default"; + function S(e, r, a, o) { + if (r) { + var n, + i = a.$context, + l = a.$groups; + r[l._set] && + (n = r[l._set][l._key]) && + !0 === t.callback(n, [i, o]) && + ((e.$datalabels._dirty = !0), a.update(i)); + } + } + function I(t, e) { + var r, + a, + o = t.$datalabels, + n = o._listeners; + if (n.enter || n.leave) { + if ("mousemove" === e.type) a = C.lookup(o._labels, e); + else if ("mouseout" !== e.type) return; + (r = o._hovered), + (o._hovered = a), + (function (t, e, r, a, o) { + var n, i; + (r || a) && + (r ? (a ? r !== a && (i = n = !0) : (i = !0)) : (n = !0), + i && S(t, e.leave, r, o), + n && S(t, e.enter, a, o)); + })(t, n, r, a, e); + } + } + return { + id: "datalabels", + defaults: { + align: "center", + anchor: "center", + backgroundColor: null, + borderColor: null, + borderRadius: 0, + borderWidth: 0, + clamp: !1, + clip: !1, + color: void 0, + display: !0, + font: { + family: void 0, + lineHeight: 1.2, + size: void 0, + style: void 0, + weight: null, + }, + formatter: function (e) { + if (t.isNullOrUndef(e)) return null; + var r, + a, + o, + n = e; + if (t.isObject(e)) + if (t.isNullOrUndef(e.label)) + if (t.isNullOrUndef(e.r)) + for (n = "", o = 0, a = (r = Object.keys(e)).length; o < a; ++o) + n += (0 !== o ? ", " : "") + r[o] + ": " + e[r[o]]; + else n = e.r; + else n = e.label; + return "" + n; + }, + labels: void 0, + listeners: {}, + offset: 4, + opacity: 1, + padding: { top: 4, right: 4, bottom: 4, left: 4 }, + rotation: 0, + textAlign: "start", + textStrokeColor: void 0, + textStrokeWidth: 0, + textShadowBlur: 0, + textShadowColor: void 0, + }, + beforeInit: function (t) { + t.$datalabels = { _actives: [] }; + }, + beforeUpdate: function (t) { + var e = t.$datalabels; + (e._listened = !1), + (e._listeners = {}), + (e._datasets = []), + (e._labels = []); + }, + afterDatasetUpdate: function (e, r, a) { + var o, + n, + i, + l, + s, + u, + d, + c, + h = r.index, + f = e.$datalabels, + x = (f._datasets[h] = []), + y = e.isDatasetVisible(h), + v = e.data.datasets[h], + b = (function (e, r) { + var a, + o, + n, + i = e.datalabels, + l = []; + return !1 === i + ? null + : (!0 === i && (i = {}), + (r = t.merge({}, [r, i])), + (o = r.labels || {}), + (n = Object.keys(o)), + delete r.labels, + n.length + ? n.forEach(function (e) { + o[e] && l.push(t.merge({}, [r, o[e], { _key: e }])); + }) + : l.push(r), + (a = l.reduce(function (e, r) { + return ( + t.each(r.listeners || {}, function (t, a) { + (e[a] = e[a] || {}), (e[a][r._key || P] = t); + }), + delete r.listeners, + e + ); + }, {})), + { labels: l, listeners: a }); + })(v, a), + p = r.meta.data || [], + g = e.ctx; + for (g.save(), o = 0, i = p.length; o < i; ++o) + if ( + (((d = p[o]).$datalabels = []), + y && d && e.getDataVisibility(o) && !d.skip) + ) + for (n = 0, l = b.labels.length; n < l; ++n) + (u = (s = b.labels[n])._key), + ((c = new _(s, g, d, o)).$groups = { _set: h, _key: u || P }), + (c.$context = { + active: !1, + chart: e, + dataIndex: o, + dataset: v, + datasetIndex: h, + }), + c.update(c.$context), + d.$datalabels.push(c), + x.push(c); + g.restore(), + t.merge(f._listeners, b.listeners, { + merger: function (t, e, a) { + (e[t] = e[t] || {}), (e[t][r.index] = a[t]), (f._listened = !0); + }, + }); + }, + afterUpdate: function (t) { + t.$datalabels._labels = C.prepare(t.$datalabels._datasets); + }, + afterDatasetsDraw: function (t) { + C.draw(t, t.$datalabels._labels); + }, + beforeEvent: function (t, e) { + if (t.$datalabels._listened) { + var r = e.event; + switch (r.type) { + case "mousemove": + case "mouseout": + I(t, r); + break; + case "click": + !(function (t, e) { + var r = t.$datalabels, + a = r._listeners.click, + o = a && C.lookup(r._labels, e); + o && S(t, a, o, e); + })(t, r); + } + } + }, + afterEvent: function (t) { + var e, + r, + a, + o, + n, + l, + s, + u = t.$datalabels, + d = u._actives, + c = (u._actives = t.getActiveElements()), + h = i(d, c); + for (e = 0, r = h.length; e < r; ++e) + if ((n = h[e])[1]) + for ( + a = 0, o = (s = n[0].element.$datalabels || []).length; + a < o; + ++a + ) + ((l = s[a]).$context.active = 1 === n[1]), l.update(l.$context); + (u._dirty || h.length) && (C.update(u._labels), t.render()), + delete u._dirty; + }, + }; +}); diff --git a/web/assets/js/dependencies.js b/web/assets/js/dependencies.js new file mode 100644 index 000000000..a0d603940 --- /dev/null +++ b/web/assets/js/dependencies.js @@ -0,0 +1,105 @@ +var footer = document.querySelector('footer') +var link = document.createElement('a') + +// Add multiple classes correctly using the spread operator +link.classList.add('built-with', 'svelte-1ax1toq') +link.id = 'show_resource_monitor' +link.text = 'Resource Monitor' +link.onclick = function () { + showPerfMonitor() +} // Use function reference instead of string + +var linkImg = document.createElement('img') +linkImg.src = '/file=web/assets/img/monitor.svg' +linkImg.classList.add('svelte-1ax1toq') +link.appendChild(linkImg) +footer.appendChild(link) + +var script = document.createElement('script') +script.src = '/file=web/assets/js/jquery-3.7.1.min.js' +document.body.appendChild(script) + +var script = document.createElement('script') +script.src = '/file=web/assets/js/elegant-resource-monitor.js' +document.body.appendChild(script) + +var script = document.createElement('script') +script.src = '/file=web/assets/js/socket.io.min.js' +document.body.appendChild(script) + +var script = document.createElement('script') +script.src = '/file=web/assets/js/chart.js' +document.body.appendChild(script) + +var fa = document.createElement('link') +fa.href = '/file=web/assets/css/material-icon.css' +fa.property = 'stylesheet' +fa.rel = 'stylesheet' +document.body.appendChild(fa) + +var styles = document.createElement('link') +styles.href = '/file=web/assets/css/styles.css' +styles.property = 'stylesheet' +styles.rel = 'stylesheet' +document.body.appendChild(styles) +styles.onload = async function () { + if (localStorage.getItem('lastClass') && localStorage.getItem('lastInactiveClass')) { + var lastClass = JSON.parse(localStorage.getItem('lastClass')) + var lastInactiveClass = JSON.parse(localStorage.getItem('lastInactiveClass')) + addCSS(lastInactiveClass.key, lastInactiveClass.values[0]) + addCSS(lastClass.key, lastClass.values[0]) + } + + function getCSSRule(ruleName) { + ruleName = ruleName.toLowerCase() + var result = null + var find = Array.prototype.find + + Array.prototype.find.call(document.styleSheets, (styleSheet) => { + try { + if (styleSheet.cssRules) { + result = find.call(styleSheet.cssRules, (cssRule) => { + return cssRule instanceof CSSStyleRule && cssRule.selectorText.toLowerCase() == ruleName + }) + } + } catch (e) { + // Handle cross-origin or other access errors + // console.info("Cannot access cssRules for stylesheet:", e); + } + return result != null + }) + return result + } + + function addCSS(selector, styles) { + var rule = getCSSRule(selector) + + for (var property in styles) { + if (styles.hasOwnProperty(property)) { + rule.style.setProperty(property, styles[property], 'important') + } + } + } + + async function loadHtmlContent() { + const response = await fetch('/file=web/templates/perf-monitor/perf-monitor.html') + var resourceMonitorContent = document.getElementById('perf-monitor-container') + resourceMonitorContent.innerHTML = await response.text() + const chartButton = resourceMonitorContent.querySelector('#chart-button') + const savedPosition = localStorage.getItem('perf-monitor-position') || 'bottom-right' + + if (chartButton) { + // Set the savedPosition class on the #chart-button element + chartButton.classList.add(savedPosition) + } + + var script = document.createElement('script') + script.src = '/file=web/assets/js/script.js' + document.body.appendChild(script) + + var chart = document.createElement('script') + chart.src = '/file=web/assets/js/chart-settings.js' + document.body.appendChild(chart) + } + await loadHtmlContent() +} diff --git a/web/assets/js/elegant-resource-monitor-service.js b/web/assets/js/elegant-resource-monitor-service.js new file mode 100644 index 000000000..798d4ec8d --- /dev/null +++ b/web/assets/js/elegant-resource-monitor-service.js @@ -0,0 +1,211 @@ +import { api } from '/scripts/api.js' +import { getResolver } from './shared_utils.js' +export class ElegantResourceMonitorExecution { + constructor(id) { + this.promptApi = null + this.executedNodeIds = [] + this.totalNodes = 0 + this.currentlyExecuting = null + this.errorDetails = null + this.apiPrompt = getResolver() + this.id = id + } + setPrompt(prompt) { + this.promptApi = prompt.output + this.totalNodes = Object.keys(this.promptApi).length + this.apiPrompt.resolve(null) + } + getApiNode(nodeId) { + var _a + return ((_a = this.promptApi) === null || _a === void 0 ? void 0 : _a[String(nodeId)]) || null + } + getNodeLabel(nodeId) { + var _a, _b + const apiNode = this.getApiNode(nodeId) + let label = + ((_a = apiNode === null || apiNode === void 0 ? void 0 : apiNode._meta) === null || + _a === void 0 + ? void 0 + : _a.title) || + (apiNode === null || apiNode === void 0 ? void 0 : apiNode.class_type) || + undefined + if (!label) { + const graphNode = + (_b = this.maybeGetComfyGraph()) === null || _b === void 0 + ? void 0 + : _b.getNodeById(Number(nodeId)) + label = + (graphNode === null || graphNode === void 0 ? void 0 : graphNode.title) || + (graphNode === null || graphNode === void 0 ? void 0 : graphNode.type) || + undefined + } + return label + } + executing(nodeId, step, maxSteps) { + var _a + if (nodeId == null) { + this.currentlyExecuting = null + return + } + if ( + ((_a = this.currentlyExecuting) === null || _a === void 0 ? void 0 : _a.nodeId) !== nodeId + ) { + if (this.currentlyExecuting != null) { + this.executedNodeIds.push(nodeId) + } + this.currentlyExecuting = { nodeId, nodeLabel: this.getNodeLabel(nodeId), pass: 0 } + this.apiPrompt.promise.then(() => { + var _a + if (this.currentlyExecuting == null) { + return + } + const apiNode = this.getApiNode(nodeId) + if (!this.currentlyExecuting.nodeLabel) { + this.currentlyExecuting.nodeLabel = this.getNodeLabel(nodeId) + } + if ( + (apiNode === null || apiNode === void 0 ? void 0 : apiNode.class_type) === + 'UltimateSDUpscale' + ) { + this.currentlyExecuting.pass-- + this.currentlyExecuting.maxPasses = -1 + } else if ( + (apiNode === null || apiNode === void 0 ? void 0 : apiNode.class_type) === + 'IterativeImageUpscale' + ) { + this.currentlyExecuting.maxPasses = + (_a = apiNode === null || apiNode === void 0 ? void 0 : apiNode.inputs['steps']) !== + null && _a !== void 0 + ? _a + : -1 + } + }) + } + if (step != null) { + if (!this.currentlyExecuting.step || step < this.currentlyExecuting.step) { + this.currentlyExecuting.pass++ + } + this.currentlyExecuting.step = step + this.currentlyExecuting.maxSteps = maxSteps + } + } + error(details) { + this.errorDetails = details + } + maybeGetComfyGraph() { + var _a + return ( + ((_a = window === null || window === void 0 ? void 0 : window.app) === null || _a === void 0 + ? void 0 + : _a.graph) || null + ) + } +} +class ElegantResourceMonitorService extends EventTarget { + constructor(api) { + super() + this.promptsMap = new Map() + this.currentExecution = null + this.lastQueueRemaining = 0 + const that = this + const queuePrompt = api.queuePrompt + api.queuePrompt = async function (num, prompt) { + let response + try { + response = await queuePrompt.apply(api, [...arguments]) + } catch (e) { + const promptExecution = that.getOrMakePrompt('error') + promptExecution.error({ exception_type: 'Unknown.' }) + throw e + } + const promptExecution = that.getOrMakePrompt(response.prompt_id) + promptExecution.setPrompt(prompt) + if (!that.currentExecution) { + that.currentExecution = promptExecution + } + that.promptsMap.set(response.prompt_id, promptExecution) + + return response + } + api.addEventListener('status', (e) => { + var _a + if (!((_a = e.detail) === null || _a === void 0 ? void 0 : _a.exec_info)) return + this.lastQueueRemaining = e.detail.exec_info.queue_remaining + this.dispatchProgressUpdate() + }) + api.addEventListener('execution_start', (e) => { + if (!this.promptsMap.has(e.detail.prompt_id)) { + console.warn("'execution_start' fired before prompt was made.") + } + const prompt = this.getOrMakePrompt(e.detail.prompt_id) + this.currentExecution = prompt + this.dispatchProgressUpdate() + }) + api.addEventListener('executing', (e) => { + if (!this.currentExecution) { + this.currentExecution = this.getOrMakePrompt('unknown') + console.warn("'executing' fired before prompt was made.") + } + this.currentExecution.executing(e.detail) + this.dispatchProgressUpdate() + if (e.detail == null) { + this.currentExecution = null + } + }) + api.addEventListener('progress', (e) => { + if (!this.currentExecution) { + this.currentExecution = this.getOrMakePrompt(e.detail.prompt_id) + console.warn("'progress' fired before prompt was made.") + } + this.currentExecution.executing(e.detail.node, e.detail.value, e.detail.max) + this.dispatchProgressUpdate() + }) + api.addEventListener('execution_cached', (e) => { + if (!this.currentExecution) { + this.currentExecution = this.getOrMakePrompt(e.detail.prompt_id) + console.warn("'execution_cached' fired before prompt was made.") + } + for (const cached of e.detail.nodes) { + this.currentExecution.executing(cached) + } + this.dispatchProgressUpdate() + }) + api.addEventListener('executed', (e) => { + if (!this.currentExecution) { + this.currentExecution = this.getOrMakePrompt(e.detail.prompt_id) + console.warn("'executed' fired before prompt was made.") + } + }) + api.addEventListener('execution_error', (e) => { + var _a + if (!this.currentExecution) { + this.currentExecution = this.getOrMakePrompt(e.detail.prompt_id) + console.warn("'execution_error' fired before prompt was made.") + } + ;(_a = this.currentExecution) === null || _a === void 0 ? void 0 : _a.error(e.detail) + this.dispatchProgressUpdate() + }) + } + async queuePrompt(prompt) { + return await api.queuePrompt(-1, prompt) + } + dispatchProgressUpdate() { + this.dispatchEvent( + new CustomEvent('elegant-resource-monitor-update', { + detail: { + queue: this.lastQueueRemaining, + prompt: this.currentExecution, + }, + }), + ) + } + getOrMakePrompt(id) { + let prompt = this.promptsMap.get(id) + if (!prompt) { + prompt = new ElegantResourceMonitorExecution(id) + this.promptsMap.set(id, prompt) + } + return prompt + } +} +export const MONITOR_SERVICE = new ElegantResourceMonitorService(api) diff --git a/web/assets/js/elegant-resource-monitor.js b/web/assets/js/elegant-resource-monitor.js new file mode 100644 index 000000000..9359bea84 --- /dev/null +++ b/web/assets/js/elegant-resource-monitor.js @@ -0,0 +1,288 @@ +class ElegantResourceMonitor extends HTMLElement { + constructor() { + super() + this.shadow = null + this.currentPromptExecution = null + this.onProgressUpdateBound = this.onProgressUpdate.bind(this) + this.connected = false + } + + render() { + this.innerHTML = ` +
+
+
+
+ settings +
+
Settings
+
+
+
+
Layout:
+
+ 1 | + 2 +
+
+
+
Size:
+
+ S | + M +
+
+
+
+
Position
+
+ + + + + + + + + +
+
+
+
+
+ close +
+
+
+
+
+
+
+ +
+
+
+ + + + +
+
+
+
+
+ ` + } + + addEventListeners() { + this.querySelector('#popupTrigger').addEventListener('click', () => { + const settingsMenu = this.querySelector('#settingsMenu') + settingsMenu.style.display = settingsMenu.style.display === 'block' ? 'none' : 'block' + }) + + this.querySelector('#close-button').addEventListener('click', () => { + this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true })) + }) + } + + getSizes() { + const savedChart = localStorage.getItem('active-chart') ?? 'bar' + var sizes = {} + if (savedChart == 'bar') { + sizes = { + small: { height: '120', width: '150' }, + medium: { height: '300', width: '410' }, + large: { height: '450', width: '700' }, + } + } else { + sizes = { + small: { height: '110', width: '160' }, + medium: { height: '245', width: '425' }, + large: { height: '380', width: '700' }, + } + } + return sizes + } + getButtonSize() { + const size = localStorage.getItem('chart-size') ?? 'medium' + const sizes = this.getSizes() + const sizeStyles = sizes[size] + var buttonHeight = +sizeStyles.height + 25 + const buttonWidth = +sizeStyles.width + 25 + var table = this.querySelector('#item-table') + var totalRowCount = table.rows.length // 5 + var tableHeight = totalRowCount * 30 + if (size == 'large') { + buttonHeight = +buttonHeight + tableHeight + } + + return { buttonHeight, buttonWidth } + } + + addListItem(itemContent) { + const itemBody = this.querySelector('#item-body') + const row = document.createElement('tr') + const cell = document.createElement('td') + cell.innerText = itemContent + row.appendChild(cell) + itemBody.appendChild(row) + } + + get currentNodeId() { + var _a, _b + const prompt = this.currentPromptExecution + const nodeId = + ((_a = prompt === null || prompt === void 0 ? void 0 : prompt.errorDetails) === null || + _a === void 0 + ? void 0 + : _a.node_id) || + ((_b = prompt === null || prompt === void 0 ? void 0 : prompt.currentlyExecuting) === null || + _b === void 0 + ? void 0 + : _b.nodeId) + return nodeId || null + } + + adjustButtonHeight(e) { + const chartButtonContainer = this.querySelector('#chart-button-container') + + const shouldShowPerfMonitor = JSON.parse(localStorage.getItem('shouldShowPerfMonitor')) ?? false + if (!shouldShowPerfMonitor || chartButtonContainer.clientHeight == 0) return + + const { buttonHeight, buttonWidth } = this.getButtonSize() + const chartContainer = this.querySelector('#chart-container') + const savedChart = localStorage.getItem('active-chart') ?? 'bar' + const size = localStorage.getItem('chart-size') ?? 'medium' + + const height = this.querySelector('#chart-container').style.height.replace('px', '') + let bar = this.querySelector('#progress-bar-container') + + var actulaButtonHeight = 0 + var actualButtonWidth = 0 + + var viewportHeight = +buttonHeight + var viewportWidth = +buttonWidth + + $(chartButtonContainer).each(function () { + this.style.setProperty('height', `${viewportHeight}px`, 'important') + this.style.setProperty('width', `${viewportWidth}px`, 'important') + }) + var table = this.querySelector('#item-table') + var totalRowCount = table.rows.length // 5 + var tableHeight = totalRowCount * 35 + var workArea = actulaButtonHeight + if (size == 'large') { + workArea = viewportHeight - tableHeight + } + + const prompt = e.detail.prompt + if (prompt && prompt.currentlyExecuting) { + bar.style.display = 'block' + workArea = workArea - 30 + } else { + bar.style.display = 'none' + } + + if (savedChart == 'bar') { + $(chartContainer).each(function () { + this.style.setProperty('height', `${workArea * 0.95}px`, 'important') + }) + } else { + $(chartContainer).each(function () { + this.style.setProperty('height', `${workArea * 0.87}px`, 'important') + }) + } + } + + onProgressUpdate(e) { + // this.adjustButtonHeight(e) + var _a, _b, _c, _d + if (!this.connected) return + const prompt = e.detail.prompt + + this.currentPromptExecution = prompt + + // Default progress to 0 if no totalNodes + let progressPercentage = 0 + + if (prompt === null || prompt === void 0 ? void 0 : prompt.errorDetails) { + let progressText = `${ + (_a = prompt.errorDetails) === null || _a === void 0 ? void 0 : _a.exception_type + } ${((_b = prompt.errorDetails) === null || _b === void 0 ? void 0 : _b.node_id) || ''} ${ + ((_c = prompt.errorDetails) === null || _c === void 0 ? void 0 : _c.node_type) || '' + }` + console.log(progressText) + // Set the progress bar to 0% or some error state if needed + this.querySelector('#progress-bar').style.width = '0%' + return + } + if (prompt === null || prompt === void 0 ? void 0 : prompt.currentlyExecuting) { + const current = prompt === null || prompt === void 0 ? void 0 : prompt.currentlyExecuting + + let progressText = `(${e.detail.queue}) ` + if (!prompt.totalNodes) { + progressText += `??%` + } else { + progressPercentage = (prompt.executedNodeIds.length / prompt.totalNodes) * 100 + progressText += `${Math.round(progressPercentage)}%` + } + + let nodeLabel = (_d = current.nodeLabel) === null || _d === void 0 ? void 0 : _d.trim() + const monitor = document.querySelector('elegant-resource-monitor') + // monitor.addListItem(nodeLabel) + + let stepsLabel = '' + if (current.step != null && current.maxSteps) { + const percent = (current.step / current.maxSteps) * 100 + if (current.pass > 1 || current.maxPasses != null) { + stepsLabel += `#${current.pass}` + if (current.maxPasses && current.maxPasses > 0) { + stepsLabel += `/${current.maxPasses}` + } + stepsLabel += ` - ` + } + stepsLabel += `${Math.round(percent)}%` + } + + if (nodeLabel || stepsLabel) { + progressText += ` - ${nodeLabel || '???'}${stepsLabel ? ` (${stepsLabel})` : ''}` + } + console.log(progressText) + } else { + if (e === null || e === void 0 ? void 0 : e.detail.queue) { + console.log(`(${e.detail.queue}) Running... in another tab`) + } else { + console.log('Idle') + } + } + + // Update the progress bar width + this.querySelector('#progress-bar').style.width = `${Math.round(progressPercentage)}%` + } + + connectedCallback() { + if (!this.connected) { + console.log('Adding event listener to MONITOR_SERVICE') + // MONITOR_SERVICE.addEventListener( + // 'elegant-resource-monitor-update', + // this.onProgressUpdateBound, + // ) + + this.render() + this.addEventListeners() + + this.connected = true + } + } + + disconnectedCallback() { + this.connected = false + // MONITOR_SERVICE.removeEventListener( + // 'elegant-resource-monitor-update', + // this.onProgressUpdateBound, + // ) + } +} + +// Register the custom element +customElements.define('elegant-resource-monitor', ElegantResourceMonitor) diff --git a/web/assets/js/jquery-3.7.1.min.js b/web/assets/js/jquery-3.7.1.min.js new file mode 100644 index 000000000..7f37b5d99 --- /dev/null +++ b/web/assets/js/jquery-3.7.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.7.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(ie,e){"use strict";var oe=[],r=Object.getPrototypeOf,ae=oe.slice,g=oe.flat?function(e){return oe.flat.call(e)}:function(e){return oe.concat.apply([],e)},s=oe.push,se=oe.indexOf,n={},i=n.toString,ue=n.hasOwnProperty,o=ue.toString,a=o.call(Object),le={},v=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},y=function(e){return null!=e&&e===e.window},C=ie.document,u={type:!0,src:!0,nonce:!0,noModule:!0};function m(e,t,n){var r,i,o=(n=n||C).createElement("script");if(o.text=e,t)for(r in u)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[i.call(e)]||"object":typeof e}var t="3.7.1",l=/HTML$/i,ce=function(e,t){return new ce.fn.init(e,t)};function c(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!v(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+ge+")"+ge+"*"),x=new RegExp(ge+"|>"),j=new RegExp(g),A=new RegExp("^"+t+"$"),D={ID:new RegExp("^#("+t+")"),CLASS:new RegExp("^\\.("+t+")"),TAG:new RegExp("^("+t+"|[*])"),ATTR:new RegExp("^"+p),PSEUDO:new RegExp("^"+g),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ge+"*(even|odd|(([+-]|)(\\d*)n|)"+ge+"*(?:([+-]|)"+ge+"*(\\d+)|))"+ge+"*\\)|)","i"),bool:new RegExp("^(?:"+f+")$","i"),needsContext:new RegExp("^"+ge+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ge+"*((?:-\\d)?\\d*)"+ge+"*\\)|)(?=[^-]|$)","i")},N=/^(?:input|select|textarea|button)$/i,q=/^h\d$/i,L=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,H=/[+~]/,O=new RegExp("\\\\[\\da-fA-F]{1,6}"+ge+"?|\\\\([^\\r\\n\\f])","g"),P=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(){V()},R=J(function(e){return!0===e.disabled&&fe(e,"fieldset")},{dir:"parentNode",next:"legend"});try{k.apply(oe=ae.call(ye.childNodes),ye.childNodes),oe[ye.childNodes.length].nodeType}catch(e){k={apply:function(e,t){me.apply(e,ae.call(t))},call:function(e){me.apply(e,ae.call(arguments,1))}}}function I(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(V(e),e=e||T,C)){if(11!==p&&(u=L.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return k.call(n,a),n}else if(f&&(a=f.getElementById(i))&&I.contains(e,a)&&a.id===i)return k.call(n,a),n}else{if(u[2])return k.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&e.getElementsByClassName)return k.apply(n,e.getElementsByClassName(i)),n}if(!(h[t+" "]||d&&d.test(t))){if(c=t,f=e,1===p&&(x.test(t)||m.test(t))){(f=H.test(t)&&U(e.parentNode)||e)==e&&le.scope||((s=e.getAttribute("id"))?s=ce.escapeSelector(s):e.setAttribute("id",s=S)),o=(l=Y(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+Q(l[o]);c=l.join(",")}try{return k.apply(n,f.querySelectorAll(c)),n}catch(e){h(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return re(t.replace(ve,"$1"),e,n,r)}function W(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function F(e){return e[S]=!0,e}function $(e){var t=T.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function B(t){return function(e){return fe(e,"input")&&e.type===t}}function _(t){return function(e){return(fe(e,"input")||fe(e,"button"))&&e.type===t}}function z(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&R(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function X(a){return F(function(o){return o=+o,F(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function U(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function V(e){var t,n=e?e.ownerDocument||e:ye;return n!=T&&9===n.nodeType&&n.documentElement&&(r=(T=n).documentElement,C=!ce.isXMLDoc(T),i=r.matches||r.webkitMatchesSelector||r.msMatchesSelector,r.msMatchesSelector&&ye!=T&&(t=T.defaultView)&&t.top!==t&&t.addEventListener("unload",M),le.getById=$(function(e){return r.appendChild(e).id=ce.expando,!T.getElementsByName||!T.getElementsByName(ce.expando).length}),le.disconnectedMatch=$(function(e){return i.call(e,"*")}),le.scope=$(function(){return T.querySelectorAll(":scope")}),le.cssHas=$(function(){try{return T.querySelector(":has(*,:jqfake)"),!1}catch(e){return!0}}),le.getById?(b.filter.ID=function(e){var t=e.replace(O,P);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(O,P);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&C){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):t.querySelectorAll(e)},b.find.CLASS=function(e,t){if("undefined"!=typeof t.getElementsByClassName&&C)return t.getElementsByClassName(e)},d=[],$(function(e){var t;r.appendChild(e).innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+ge+"*(?:value|"+f+")"),e.querySelectorAll("[id~="+S+"-]").length||d.push("~="),e.querySelectorAll("a#"+S+"+*").length||d.push(".#.+[+~]"),e.querySelectorAll(":checked").length||d.push(":checked"),(t=T.createElement("input")).setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),r.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&d.push(":enabled",":disabled"),(t=T.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||d.push("\\["+ge+"*name"+ge+"*="+ge+"*(?:''|\"\")")}),le.cssHas||d.push(":has"),d=d.length&&new RegExp(d.join("|")),l=function(e,t){if(e===t)return a=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!le.sortDetached&&t.compareDocumentPosition(e)===n?e===T||e.ownerDocument==ye&&I.contains(ye,e)?-1:t===T||t.ownerDocument==ye&&I.contains(ye,t)?1:o?se.call(o,e)-se.call(o,t):0:4&n?-1:1)}),T}for(e in I.matches=function(e,t){return I(e,null,null,t)},I.matchesSelector=function(e,t){if(V(e),C&&!h[t+" "]&&(!d||!d.test(t)))try{var n=i.call(e,t);if(n||le.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){h(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(O,P),e[3]=(e[3]||e[4]||e[5]||"").replace(O,P),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||I.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&I.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return D.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&j.test(n)&&(t=Y(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(O,P).toLowerCase();return"*"===e?function(){return!0}:function(e){return fe(e,t)}},CLASS:function(e){var t=s[e+" "];return t||(t=new RegExp("(^|"+ge+")"+e+"("+ge+"|$)"))&&s(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=I.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function T(e,n,r){return v(n)?ce.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?ce.grep(e,function(e){return e===n!==r}):"string"!=typeof n?ce.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(ce.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||k,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:S.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof ce?t[0]:t,ce.merge(this,ce.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:C,!0)),w.test(r[1])&&ce.isPlainObject(t))for(r in t)v(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=C.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):v(e)?void 0!==n.ready?n.ready(e):e(ce):ce.makeArray(e,this)}).prototype=ce.fn,k=ce(C);var E=/^(?:parents|prev(?:Until|All))/,j={children:!0,contents:!0,next:!0,prev:!0};function A(e,t){while((e=e[t])&&1!==e.nodeType);return e}ce.fn.extend({has:function(e){var t=ce(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,Ce=/^$|^module$|\/(?:java|ecma)script/i;xe=C.createDocumentFragment().appendChild(C.createElement("div")),(be=C.createElement("input")).setAttribute("type","radio"),be.setAttribute("checked","checked"),be.setAttribute("name","t"),xe.appendChild(be),le.checkClone=xe.cloneNode(!0).cloneNode(!0).lastChild.checked,xe.innerHTML="",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.innerHTML="",le.option=!!xe.lastChild;var ke={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function Se(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&fe(e,t)?ce.merge([e],n):n}function Ee(e,t){for(var n=0,r=e.length;n",""]);var je=/<|&#?\w+;/;function Ae(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function Re(e,t){return fe(e,"table")&&fe(11!==t.nodeType?t:t.firstChild,"tr")&&ce(e).children("tbody")[0]||e}function Ie(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function We(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Fe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(_.hasData(e)&&(s=_.get(e).events))for(i in _.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),C.head.appendChild(r[0])},abort:function(){i&&i()}}});var Jt,Kt=[],Zt=/(=)\?(?=&|$)|\?\?/;ce.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Kt.pop()||ce.expando+"_"+jt.guid++;return this[e]=!0,e}}),ce.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Zt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Zt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=v(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Zt,"$1"+r):!1!==e.jsonp&&(e.url+=(At.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||ce.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=ie[r],ie[r]=function(){o=arguments},n.always(function(){void 0===i?ce(ie).removeProp(r):ie[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Kt.push(r)),o&&v(i)&&i(o[0]),o=i=void 0}),"script"}),le.createHTMLDocument=((Jt=C.implementation.createHTMLDocument("").body).innerHTML="
",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(le.createHTMLDocument?((r=(t=C.implementation.createHTMLDocument("")).createElement("base")).href=C.location.href,t.head.appendChild(r)):t=C),o=!n&&[],(i=w.exec(e))?[t.createElement(i[1])]:(i=Ae([e],t,o),o&&o.length&&ce(o).remove(),ce.merge([],i.childNodes)));var r,i,o},ce.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(ce.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},ce.expr.pseudos.animated=function(t){return ce.grep(ce.timers,function(e){return t===e.elem}).length},ce.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=ce.css(e,"position"),c=ce(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=ce.css(e,"top"),u=ce.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),v(t)&&(t=t.call(e,n,ce.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},ce.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){ce.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===ce.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===ce.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=ce(e).offset()).top+=ce.css(e,"borderTopWidth",!0),i.left+=ce.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-ce.css(r,"marginTop",!0),left:t.left-i.left-ce.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===ce.css(e,"position"))e=e.offsetParent;return e||J})}}),ce.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;ce.fn[t]=function(e){return M(this,function(e,t,n){var r;if(y(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),ce.each(["top","left"],function(e,n){ce.cssHooks[n]=Ye(le.pixelPosition,function(e,t){if(t)return t=Ge(e,n),_e.test(t)?ce(e).position()[n]+"px":t})}),ce.each({Height:"height",Width:"width"},function(a,s){ce.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){ce.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return M(this,function(e,t,n){var r;return y(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?ce.css(e,t,i):ce.style(e,t,n,i)},s,n?e:void 0,n)}})}),ce.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){ce.fn[t]=function(e){return this.on(t,e)}}),ce.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.on("mouseenter",e).on("mouseleave",t||e)}}),ce.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){ce.fn[n]=function(e,t){return 0 { + if (!isDragging) { + elmnt.style.cursor = 'auto' + } + }, 1000) + isDragging = false + getNearestPosition() + } +} + +function moveButtonToCenter(duration = 300) { + const { buttonHeight, buttonWidth } = getButtonSize() + // Get button dimensions and viewport dimensions + const widgetWidth = buttonWidth + const widgetHeight = buttonHeight + const viewportWidth = window.innerWidth + const viewportHeight = window.innerHeight + + // Calculate center of the viewport + const windowCenterX = viewportWidth / 2 + const windowCenterY = viewportHeight / 2 + + // Calculate button center + const buttonCenterX = widgetWidth / 2 + const buttonCenterY = widgetHeight / 2 + + // Calculate the translation offsets needed to center the button + const posx = windowCenterX - buttonCenterX + const posy = windowCenterY - buttonCenterY + + goToPosition({ x: posx, y: posy }) +} + +// Call the function to move the button + +// HELPER FUNCTIONS // + +function getCSSRule(ruleName) { + ruleName = ruleName.toLowerCase() + var result = null + var find = Array.prototype.find + + Array.prototype.find.call(document.styleSheets, (styleSheet) => { + try { + if (styleSheet.cssRules) { + result = find.call(styleSheet.cssRules, (cssRule) => { + return cssRule instanceof CSSStyleRule && cssRule.selectorText.toLowerCase() == ruleName + }) + } + } catch (e) { + // Handle cross-origin or other access errors + // console.info("Cannot access cssRules for stylesheet:", e); + } + return result != null + }) + return result +} + +window.barChart = async function () { + checkForUpdates('active-chart', 'bar') + await updateChartSize() +} + +window.lineChart = async function () { + checkForUpdates('active-chart', 'line') + await updateChartSize() +} + +window.smallChart = async function () { + checkForUpdates('chart-size', 'small') + await updateChartSize() +} + +window.mediumChart = async function () { + checkForUpdates('chart-size', 'medium') + await updateChartSize() +} + +window.largeChart = async function () { + setTimeout(async () => { + checkForUpdates('perf-monitor-position', 'center') + checkForUpdates('chart-size', 'large') + await updateChartSize() + }, 50) +} + +function moveToCenter() { + if (localStorage.getItem('perf-monitor-position') === 'center') { + moveButtonToCenter(150) + } +} +function checkForUpdates(key, value) { + var previous = localStorage.getItem(key) + var updated = previous != value + localStorage.setItem('hasUpdates', updated) + localStorage.setItem(key, value) +} + +function isWindowOutsideWorkingArea() { + const { buttonHeight, buttonWidth } = getButtonSize() + + // Get display bounds + const { displayBounds } = getDisplayAndWindowBounds() + + const widget = document.getElementById('chart-button-container') + const rect = widget.getBoundingClientRect() + const currentTop = rect.top + window.scrollY + const currentLeft = rect.left + window.scrollX + + const windowLeft = currentLeft + const windowTop = currentTop + const windowRight = windowLeft + buttonWidth + const windowBottom = windowTop + buttonHeight + + const displayLeft = 0 + const displayTop = 0 + const displayRight = displayLeft + displayBounds.width + const displayBottom = displayTop + displayBounds.height + let isOutside = + windowLeft < displayLeft || + windowTop < displayTop || + windowRight > displayRight || + windowBottom > displayBottom + + if (isOutside) { + console.log('The window is outside the working area.') + } else { + console.log('The window is within the working area.') + } + + return isOutside +} + +function getSizes() { + const savedChart = localStorage.getItem('active-chart') ?? 'bar' + var sizes = {} + if (savedChart == 'bar') { + sizes = { + small: { height: '120', width: '150' }, + medium: { height: '300', width: '410' }, + large: { height: '450', width: '700' }, + } + } else { + sizes = { + small: { height: '110', width: '160' }, + medium: { height: '245', width: '425' }, + large: { height: '380', width: '700' }, + } + } + return sizes +} + +// SETTINGS MENU // +// POSITIONS BUTTONS +document.querySelectorAll('.position-clickable').forEach((button) => { + button.addEventListener('click', async function () { + const position = this.id + wasDragged = false + + localStorage.setItem('perf-monitor-position', position) + + //the position we should be going to + const pos = getCoordinates(false) + if (pos) { + goToPosition(pos) + } else { + console.error('Invalid position:', pos) + } + + // Optionally hide the settings menu and adjust UI + const settingsMenu = document.getElementById('settingsMenu') + settingsMenu.classList.remove('show') // Hide the menu if visible + + document.querySelectorAll('.chart-row').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) + }) +}) + +// Show or hide the settings menu when the settings icon is clicked +document.getElementById('popupTrigger').addEventListener('click', function (event) { + const settingsMenu = document.getElementById('settingsMenu') + settingsMenu.classList.toggle('show') // Toggle the 'show' class for animation + + document.querySelectorAll('.chart-row').forEach((row) => { + row.classList.add('no-drag') + row.classList.remove('drag') + }) + document.querySelectorAll('canvas').forEach((row) => { + row.classList.add('no-drag') + row.classList.remove('drag') + }) + setTimeout(() => { + const settingsMenuHr = document.getElementById('settings-hr') + settingsMenuHr.classList.add('show') // Toggle the 'show' class for animation + }, 300) + + event.stopPropagation() +}) + +// Hide the settings menu when clicking outside +window.addEventListener('click', function (e) { + if (e.target.className.includes('settings')) { + return + } + + const settingsMenu = document.getElementById('settingsMenu') + const trigger = document.getElementById('popupTrigger') + if (!settingsMenu.contains(e.target) && e.target !== trigger) { + settingsMenu.classList.remove('show') // Hide the menu if clicking outside + } + document.querySelectorAll('canvas').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) + document.querySelectorAll('.chart-row').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) +}) + +// Calculate if the menu will overflow the bottom of the viewport +document.getElementById('popupTrigger').addEventListener('click', function () { + const menu = document.getElementById('settingsMenu') + const menuRect = menu.getBoundingClientRect() + const buttonRect = this.getBoundingClientRect() + const viewportHeight = window.innerHeight + if (menu.offsetTop < 0) { + menu.style.position = 'absolute' + menu.style.top = `29px` + } + let topPosition = buttonRect.bottom + if (topPosition + menuRect.height > viewportHeight) { + // Calculate how much the menu overflows the viewport + const overflowAmount = topPosition + menuRect.height - viewportHeight + // Apply the calculated position + menu.style.position = 'absolute' // Ensure the menu is positioned absolutely + menu.style.top = `-${overflowAmount}px` + } +}) + +function goToPosition(pos) { + const widget = document.getElementById('chart-button-container') + + // Set transition for smooth animation + // widget.style.transition = 'transform .7s ease-in-out' + widget.style.transition = `top .4s ease, left .4s ease` + + const currentTop = +widget.style.top.replace('px', '') + const currentLeft = +widget.style.left.replace('px', '') + + // Target position + + const offsetX = pos.x - currentLeft + const offsetY = pos.y - currentTop + + // Set transition duration and easing + widget.style.transition = `transform .7s ease-in-out` + + // Animate to the center + widget.style.transform = `translate(${offsetX}px, ${offsetY}px)` +} +// MAIN METHODS // + +function getDisplayAndWindowBounds() { + const availWidth = window.screen.availWidth + const availHeight = window.screen.availHeight + + // Assume work area starts at (0, 0) + // Work area dimensions approximate available screen area minus some margins + const workArea = { + x: 0, // Typically starts at (0, 0) + y: 0, // Typically starts at (0, 0) + width: availWidth, + height: availHeight, + } + + const displayBounds = { + width: window.screen.width, + height: window.screen.height, + availableWidth: window.screen.availWidth, + availableHeight: window.screen.availHeight, + workArea, + } + + const windowBounds = { + width: window.innerWidth, + height: window.innerHeight, + } + + return { displayBounds, windowBounds } +} + +function getPositions() { + const { buttonHeight, buttonWidth } = getButtonSize() + + // Get button dimensions and viewport dimensions + const widgetWidth = buttonWidth + const widgetHeight = buttonHeight + const viewportWidth = window.innerWidth + const viewportHeight = window.innerHeight + + // Calculate center of the viewport + const windowCenterX = viewportWidth / 2 + const windowCenterY = viewportHeight / 2 + + // Calculate button center + const buttonCenterX = widgetWidth / 2 + const buttonCenterY = widgetHeight / 2 + + // Calculate the translation offsets needed to center the button + + // Define positions based on work area + const positions = { + 'bottom-right': { + x: viewportWidth - widgetWidth - 10, + y: viewportHeight - widgetHeight - 10, + }, + 'bottom-left': { + x: 10, + y: viewportHeight - widgetHeight - 10, + }, + 'bottom-center': { + x: (viewportWidth - widgetWidth) / 2, + y: viewportHeight - widgetHeight - 10, + }, + 'top-right': { + x: viewportWidth - widgetWidth - 10, + y: 10, + }, + 'top-left': { x: 10, y: 10 }, + 'top-center': { + x: (viewportWidth - widgetWidth) / 2, + y: 10, + }, + 'left-center': { + x: 10, + y: windowCenterY - buttonCenterY, + }, + 'right-center': { + x: viewportWidth - widgetWidth - 10, + y: windowCenterY - buttonCenterY, + }, + center: { + x: (viewportWidth - widgetWidth) / 2, + y: windowCenterY - buttonCenterY, + }, + } + return positions +} + +function getCoordinates(isOutside) { + var position = localStorage.getItem('perf-monitor-position') + + if (isOutside) { + var outsidePosition = getNearestPosition() + return outsidePosition + } + + const positions = getPositions() + const pos = positions[position] + return pos +} + +function getNearestPosition() { + const { buttonHeight, buttonWidth } = getButtonSize() + const widget = document.getElementById('chart-button-container') + const viewportWidth = window.innerWidth + const viewportHeight = window.innerHeight + // Get display bounds + const { displayBounds } = getDisplayAndWindowBounds() + // Define positions based on work area + const positions = getPositions() + + // Get current window position + const currentX = $(widget).offset().left + let currentY = $(widget).offset().top + const windowCenter = { + x: $(widget).offset().left, + y: $(widget).offset().top, + } + const workAreaCenter = { + x: viewportWidth / 2, + y: viewportHeight / 2, + } + const distanceToCenter = { + x: Math.abs(workAreaCenter.x - windowCenter.x), + y: Math.abs(workAreaCenter.y - windowCenter.y), + } + var threshold = 100 // Define a threshold to determine proximity + const size = localStorage.getItem('chart-size') ?? 'medium' + switch (size) { + case 'small': + threshold = 250 + + break + case 'medium': + threshold = 200 + default: + threshold = 150 + + break + } + + var nearestPosition = '' + + if (distanceToCenter.x < threshold && distanceToCenter.y < threshold) { + nearestPosition = 'center' + } else { + // Function to calculate distance + function calculateDistance(x1, y1, x2, y2) { + return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) + } + + // Find the nearest position + let minDistance = Infinity + + for (const [key, pos] of Object.entries(positions)) { + // Adjust for edge cases + const adjustedPosX = Math.max( + displayBounds.workArea.x, + Math.min(pos.x, displayBounds.width - buttonWidth), + ) + const adjustedPosY = Math.max( + displayBounds.workArea.y, + Math.min(pos.y, displayBounds.height - buttonHeight), + ) + + const distance = calculateDistance(currentX, currentY, adjustedPosX, adjustedPosY) + if (distance < minDistance) { + minDistance = distance + nearestPosition = key + } + } + } + + // Output or use the nearest position + console.log('Nearest position:', nearestPosition) + // Set the position + const pos = positions[nearestPosition] + localStorage.setItem('perf-monitor-position', nearestPosition) + + return pos +} +function getButtonSize() { + const size = localStorage.getItem('chart-size') ?? 'medium' + const sizes = getSizes() + const sizeStyles = sizes[size] + var buttonHeight = +sizeStyles.height + 25 + const buttonWidth = +sizeStyles.width + 25 + var table = document.getElementById('item-table') + var totalRowCount = table.rows.length // 5 + var tableHeight = totalRowCount * 30 + if (size == 'large') { + buttonHeight = +buttonHeight + tableHeight + } + + return { buttonHeight, buttonWidth } +} +async function updateChartSize() { + var table = document.getElementById('item-table') + table.style.display = 'none' + + const settingsMenu = document.getElementById('settingsMenu') + settingsMenu.classList.remove('show') // Hide the menu if visible + $('#chart-wrapper').fadeOut() + document.querySelectorAll('.chart-row').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) + + document.querySelectorAll('canvas').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) + + const size = localStorage.getItem('chart-size') ?? 'medium' + const chartContainer = document.getElementById('chart-container') + const savedChart = localStorage.getItem('active-chart') ?? 'bar' + + chartContainer.classList.remove('small', 'medium', 'large', 'bar', 'line') + chartContainer.classList.add(size) + chartContainer.classList.add(savedChart) + + const { buttonHeight, buttonWidth } = getButtonSize() + const chartButtonContainer = document.getElementById('chart-button-container') + + var actulaButtonHeight = 0 + var actualButtonWidth = 0 + + var viewportHeight = +buttonHeight + var viewportWidth = +buttonWidth + + $(chartButtonContainer).each(function () { + this.style.setProperty('height', `${viewportHeight}px`, 'important') + this.style.setProperty('width', `${viewportWidth}px`, 'important') + }) + + var position = localStorage.getItem('perf-monitor-position') + if (position === 'center') { + moveToCenter() + } else { + const isOutside = isWindowOutsideWorkingArea() + const pos = getCoordinates(isOutside) + if (pos && isOutside && wasDragged) { + goToPosition(pos) + } else if (pos && !wasDragged) { + goToPosition(pos) + } else { + // do nothing + } + } + + var sizeClasses = ['small', 'medium', 'large'] + + const chartButton = document.getElementById('chart-button') + chartButton.classList.add(size) + sizeClasses.forEach((prop) => { + if (prop != size) { + setTimeout(() => { + chartButton.classList.remove(prop) + }, 500) + } + }) + + switch (size) { + case 'small': + actulaButtonHeight = viewportHeight * 0.83 + actualButtonWidth = viewportWidth * 0.83 + break + case 'medium': + actulaButtonHeight = viewportHeight * 0.93 + actualButtonWidth = viewportWidth * 0.93 + break + default: + actulaButtonHeight = viewportHeight * 0.96 + actualButtonWidth = viewportWidth * 0.96 + break + } + + const bottom = `12.5px` + const right = `12.5px` + + $(chartButton).each(function () { + this.style.setProperty('bottom', bottom, 'important') + this.style.setProperty('right', right, 'important') + + if (size === 'large') { + this.style.setProperty('background-color', ` #000000d6`, 'important') + } else { + this.style.setProperty('background-color', ` #00000096`, 'important') + } + }) + var totalRowCount = table.rows.length // 5 + var tableHeight = totalRowCount * 30 + var workArea = actulaButtonHeight + if (size == 'large') { + workArea = viewportHeight - tableHeight + } + + const hasUpdates = localStorage.getItem('hasUpdates') ?? 'false' + if (hasUpdates === 'true') { + if (savedChart == 'bar') { + $(chartContainer).each(function () { + this.style.setProperty('height', `${workArea * 0.95}px`, 'important') + }) + + initializeBarChart() + } else { + $(chartContainer).each(function () { + this.style.setProperty('height', `${workArea * 0.87}px`, 'important') + }) + setTimeout(() => { + initializeLineChart() + }, 500) + } + } else { + $('#chart-wrapper').fadeIn() + } + + localStorage.setItem('hasUpdates', 'false') + + const active = `#chart-button.top-left.active` + var positionStyles = { + bottom: bottom, + right: right, + } + var lastClass = { + key: active, + values: [positionStyles], + } + var lastClassString = JSON.stringify(lastClass) + localStorage.setItem('lastClass', lastClassString) +} + +const pos = getCoordinates(false) +goToPosition(pos) +var appIsLoaded = false +var shouldShowPerfMonitor = false +if (JSON.parse(localStorage.getItem('shouldShowPerfMonitor')) ?? false) { + setTimeout(() => { + // showPerfMonitor() + }, 1500) +} + +window.showPerfMonitor = async function () { + shouldShowPerfMonitor = !shouldShowPerfMonitor + localStorage.setItem('shouldShowPerfMonitor', shouldShowPerfMonitor) + const chartButton = document.getElementById('chart-button') + const chartWrapper = document.getElementById('chart-wrapper') + const chartButtonContainer = document.getElementById('chart-button-container') + const resourceMonitorLink = document.getElementById('show_resource_monitor') + + if (shouldShowPerfMonitor === true) { + $(chartButtonContainer).toggleClass('active') + + localStorage.setItem('hasUpdates', 'true') + await updateChartSize() + $(resourceMonitorLink).fadeOut(500) + appIsLoaded = true + } else { + setTimeout(() => { + $(chartButtonContainer).toggleClass('active') + $(chartButtonContainer).each(function () { + this.style.setProperty('height', `0px`, 'important') + }) + }, 500) + chartButton.classList.remove('small', 'medium', 'large') + $(chartWrapper).fadeOut() + $(resourceMonitorLink).fadeIn(500) + } + + $(chartButton).toggleClass('active') +} + +// when the close button is clicked +document.getElementById('close-button').addEventListener('click', function () { + document.getElementById('settingsMenu').classList.remove('show') // Hide the menu + document.querySelectorAll('.chart-row').forEach((row) => { + row.classList.remove('no-drag') + row.classList.add('drag') + }) + showPerfMonitor() +}) diff --git a/web/assets/js/shared_utils.js b/web/assets/js/shared_utils.js new file mode 100644 index 000000000..dad89f6ce --- /dev/null +++ b/web/assets/js/shared_utils.js @@ -0,0 +1,115 @@ +export function getResolver(timeout = 5000) { + const resolver = {} + resolver.id = generateId(8) + resolver.completed = false + resolver.resolved = false + resolver.rejected = false + resolver.promise = new Promise((resolve, reject) => { + resolver.reject = () => { + resolver.completed = true + resolver.rejected = true + reject() + } + resolver.resolve = (data) => { + resolver.completed = true + resolver.resolved = true + resolve(data) + } + }) + resolver.timeout = setTimeout(() => { + if (!resolver.completed) { + resolver.reject() + } + }, timeout) + return resolver +} +const DEBOUNCE_FN_TO_PROMISE = new WeakMap() +export function debounce(fn, ms = 64) { + if (!DEBOUNCE_FN_TO_PROMISE.get(fn)) { + DEBOUNCE_FN_TO_PROMISE.set( + fn, + wait(ms).then(() => { + DEBOUNCE_FN_TO_PROMISE.delete(fn) + fn() + }), + ) + } + return DEBOUNCE_FN_TO_PROMISE.get(fn) +} +export function wait(ms = 16) { + if (ms === 16) { + return new Promise((resolve) => { + requestAnimationFrame(() => { + resolve() + }) + }) + } + return new Promise((resolve) => { + setTimeout(() => { + resolve() + }, ms) + }) +} +function dec2hex(dec) { + return dec.toString(16).padStart(2, '0') +} +export function generateId(length) { + const arr = new Uint8Array(length / 2) + crypto.getRandomValues(arr) + return Array.from(arr, dec2hex).join('') +} +export function getObjectValue(obj, objKey, def) { + if (!obj || !objKey) return def + const keys = objKey.split('.') + const key = keys.shift() + const found = obj[key] + if (keys.length) { + return getObjectValue(found, keys.join('.'), def) + } + return found +} +export function setObjectValue(obj, objKey, value, createMissingObjects = true) { + if (!obj || !objKey) return obj + const keys = objKey.split('.') + const key = keys.shift() + if (obj[key] === undefined) { + if (!createMissingObjects) { + return + } + obj[key] = {} + } + if (!keys.length) { + obj[key] = value + } else { + if (typeof obj[key] != 'object') { + obj[key] = {} + } + setObjectValue(obj[key], keys.join('.'), value, createMissingObjects) + } + return obj +} +export function moveArrayItem(arr, itemOrFrom, to) { + const from = typeof itemOrFrom === 'number' ? itemOrFrom : arr.indexOf(itemOrFrom) + arr.splice(to, 0, arr.splice(from, 1)[0]) +} +export function removeArrayItem(arr, itemOrIndex) { + const index = typeof itemOrIndex === 'number' ? itemOrIndex : arr.indexOf(itemOrIndex) + arr.splice(index, 1) +} +export function injectCss(href) { + if (document.querySelector(`link[href^="${href}"]`)) { + return Promise.resolve() + } + return new Promise((resolve) => { + const link = document.createElement('link') + link.setAttribute('rel', 'stylesheet') + link.setAttribute('type', 'text/css') + const timeout = setTimeout(resolve, 1000) + link.addEventListener('load', (e) => { + clearInterval(timeout) + resolve() + }) + link.href = href + document.head.appendChild(link) + }) +} diff --git a/web/assets/js/socket.io.min.js b/web/assets/js/socket.io.min.js new file mode 100644 index 000000000..0ae3cf09d --- /dev/null +++ b/web/assets/js/socket.io.min.js @@ -0,0 +1,7 @@ +/*! + * Socket.IO v4.0.1 + * (c) 2014-2021 Guillermo Rauch + * Released under the MIT License. + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.io=e():t.io=e()}(self,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},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 r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},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=18)}([function(t,e,n){var r=n(24),o=n(25),i=String.fromCharCode(30);t.exports={protocol:4,encodePacket:r,encodePayload:function(t,e){var n=t.length,o=new Array(n),s=0;t.forEach((function(t,c){r(t,!1,(function(t){o[c]=t,++s===n&&e(o.join(i))}))}))},decodePacket:o,decodePayload:function(t,e){for(var n=t.split(i),r=[],s=0;s0;case l.ACK:case l.BINARY_ACK:return Array.isArray(e)}}}]),n}(h);e.Decoder=b;var m=function(){function t(e){u(this,t),this.packet=e,this.buffers=[],this.reconPack=e}return p(t,[{key:"takeBinaryData",value:function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=y.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}},{key:"finishedReconstruction",value:function(){this.reconPack=null,this.buffers=[]}}]),t}()},function(t,e){var n=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,r=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.exports=function(t){var e=t,o=t.indexOf("["),i=t.indexOf("]");-1!=o&&-1!=i&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,";")+t.substring(i,t.length));for(var s,c,a=n.exec(t||""),u={},f=14;f--;)u[r[f]]=a[f]||"";return-1!=o&&-1!=i&&(u.source=e,u.host=u.host.substring(1,u.host.length-1).replace(/;/g,":"),u.authority=u.authority.replace("[","").replace("]","").replace(/;/g,":"),u.ipv6uri=!0),u.pathNames=function(t,e){var n=e.replace(/\/{2,9}/g,"/").split("/");"/"!=e.substr(0,1)&&0!==e.length||n.splice(0,1);"/"==e.substr(e.length-1,1)&&n.splice(n.length-1,1);return n}(0,u.path),u.queryKey=(s=u.query,c={},s.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(t,e,n){e&&(c[e]=n)})),c),u}},function(t,e,n){"use strict";function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){for(var n=0;n=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{var n=this.backoff.duration();this._reconnecting=!0;var r=setTimeout((function(){e.skipReconnect||(t.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((function(n){n?(e._reconnecting=!1,e.reconnect(),t.emitReserved("reconnect_error",n)):e.onreconnect()})))}),n);this.opts.autoUnref&&r.unref(),this.subs.push((function(){clearTimeout(r)}))}}},{key:"onreconnect",value:function(){var t=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",t)}}])&&o(e.prototype,n),c&&o(e,c),y}(n(17).StrictEventEmitter);e.Manager=y},function(t,e,n){var r=n(9),o=n(23),i=n(27),s=n(28);e.polling=function(t){var e=!1,n=!1,s=!1!==t.jsonp;if("undefined"!=typeof location){var c="https:"===location.protocol,a=location.port;a||(a=c?443:80),e=t.hostname!==location.hostname||a!==t.port,n=t.secure!==c}if(t.xdomain=e,t.xscheme=n,"open"in new r(t)&&!t.forceJSONP)return new o(t);if(!s)throw new Error("JSONP disabled");return new i(t)},e.websocket=s},function(t,e,n){var r=n(22),o=n(2);t.exports=function(t){var e=t.xdomain,n=t.xscheme,i=t.enablesXDR;try{if("undefined"!=typeof XMLHttpRequest&&(!e||r))return new XMLHttpRequest}catch(t){}try{if("undefined"!=typeof XDomainRequest&&!n&&i)return new XDomainRequest}catch(t){}if(!e)try{return new(o[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(t){}}},function(t,e,n){function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){for(var n=0;n0);return e}function u(){var t=a(+new Date);return t!==r?(s=0,r=t):t+"."+a(s++)}for(;c<64;c++)i[o[c]]=c;u.encode=a,u.decode=function(t){var e=0;for(c=0;c1?e-1:0),r=1;r=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,c=!0,a=!1;return{s:function(){n=t[Symbol.iterator]()},n:function(){var t=n.next();return c=t.done,t},e:function(t){a=!0,s=t},f:function(){try{c||null==n.return||n.return()}finally{if(a)throw s}}}}function i(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n1?e-1:0),r=1;r1?n-1:0),o=1;o1?n-1:0),o=1;o1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,o=t;n=n||"undefined"!=typeof location&&location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(t=void 0!==n?n.protocol+"//"+t:"https://"+t),o=r(t)),o.port||(/^(http|ws)$/.test(o.protocol)?o.port="80":/^(http|ws)s$/.test(o.protocol)&&(o.port="443")),o.path=o.path||"/";var i=-1!==o.host.indexOf(":"),s=i?"["+o.host+"]":o.host;return o.id=o.protocol+"://"+s+":"+o.port+e,o.href=o.protocol+"://"+s+(n&&n.port===o.port?"":":"+o.port),o}},function(t,e,n){var r=n(21);t.exports=function(t,e){return new r(t,e)},t.exports.Socket=r,t.exports.protocol=r.protocol,t.exports.Transport=n(3),t.exports.transports=n(8),t.exports.parser=n(0)},function(t,e,n){function r(){return(r=Object.assign||function(t){for(var e=1;e1&&void 0!==arguments[1]?arguments[1]:{};return i(this,l),e=f.call(this),t&&"object"===o(t)&&(n=t,t=null),t?(t=y(t),n.hostname=t.host,n.secure="https"===t.protocol||"wss"===t.protocol,n.port=t.port,t.query&&(n.query=t.query)):n.host&&(n.hostname=y(n.host).host),e.secure=null!=n.secure?n.secure:"undefined"!=typeof location&&"https:"===location.protocol,n.hostname&&!n.port&&(n.port=e.secure?"443":"80"),e.hostname=n.hostname||("undefined"!=typeof location?location.hostname:"localhost"),e.port=n.port||("undefined"!=typeof location&&location.port?location.port:e.secure?443:80),e.transports=n.transports||["polling","websocket"],e.readyState="",e.writeBuffer=[],e.prevBufferLen=0,e.opts=r({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,jsonp:!0,timestampParam:"t",rememberUpgrade:!1,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{}},n),e.opts.path=e.opts.path.replace(/\/$/,"")+"/","string"==typeof e.opts.query&&(e.opts.query=d.decode(e.opts.query)),e.id=null,e.upgrades=null,e.pingInterval=null,e.pingTimeout=null,e.pingTimeoutTimer=null,"function"==typeof addEventListener&&(addEventListener("beforeunload",(function(){e.transport&&(e.transport.removeAllListeners(),e.transport.close())}),!1),"localhost"!==e.hostname&&(e.offlineEventListener=function(){e.onClose("transport close")},addEventListener("offline",e.offlineEventListener,!1))),e.open(),e}return e=l,(n=[{key:"createTransport",value:function(t){var e=function(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}(this.opts.query);e.EIO=h.protocol,e.transport=t,this.id&&(e.sid=this.id);var n=r({},this.opts.transportOptions[t],this.opts,{query:e,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return new p[t](n)}},{key:"open",value:function(){var t;if(this.opts.rememberUpgrade&&l.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))t="websocket";else{if(0===this.transports.length){var e=this;return void setTimeout((function(){e.emit("error","No transports available")}),0)}t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)}},{key:"setTransport",value:function(t){var e=this;this.transport&&this.transport.removeAllListeners(),this.transport=t,t.on("drain",(function(){e.onDrain()})).on("packet",(function(t){e.onPacket(t)})).on("error",(function(t){e.onError(t)})).on("close",(function(){e.onClose("transport close")}))}},{key:"probe",value:function(t){var e=this.createTransport(t,{probe:1}),n=!1,r=this;function o(){if(r.onlyBinaryUpgrades){var t=!this.supportsBinary&&r.transport.supportsBinary;n=n||t}n||(e.send([{type:"ping",data:"probe"}]),e.once("packet",(function(t){if(!n)if("pong"===t.type&&"probe"===t.data){if(r.upgrading=!0,r.emit("upgrading",e),!e)return;l.priorWebsocketSuccess="websocket"===e.name,r.transport.pause((function(){n||"closed"!==r.readyState&&(f(),r.setTransport(e),e.send([{type:"upgrade"}]),r.emit("upgrade",e),e=null,r.upgrading=!1,r.flush())}))}else{var o=new Error("probe error");o.transport=e.name,r.emit("upgradeError",o)}})))}function i(){n||(n=!0,f(),e.close(),e=null)}function s(t){var n=new Error("probe error: "+t);n.transport=e.name,i(),r.emit("upgradeError",n)}function c(){s("transport closed")}function a(){s("socket closed")}function u(t){e&&t.name!==e.name&&i()}function f(){e.removeListener("open",o),e.removeListener("error",s),e.removeListener("close",c),r.removeListener("close",a),r.removeListener("upgrading",u)}l.priorWebsocketSuccess=!1,e.once("open",o),e.once("error",s),e.once("close",c),this.once("close",a),this.once("upgrading",u),e.open()}},{key:"onOpen",value:function(){if(this.readyState="open",l.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade&&this.transport.pause)for(var t=0,e=this.upgrades.length;t0&&void 0!==arguments[0]?arguments[0]:{};return o(t,{xd:this.xd,xs:this.xs},this.opts),new w(this.uri(),t)}},{key:"doWrite",value:function(t,e){var n=this.request({method:"POST",data:t}),r=this;n.on("success",e),n.on("error",(function(t){r.onError("xhr post error",t)}))}},{key:"doPoll",value:function(){var t=this.request(),e=this;t.on("data",(function(t){e.onData(t)})),t.on("error",(function(t){e.onError("xhr poll error",t)})),this.pollXhr=t}}]),n}(y),w=function(t){a(n,t);var e=f(n);function n(t,r){var o;return i(this,n),(o=e.call(this)).opts=r,o.method=r.method||"GET",o.uri=t,o.async=!1!==r.async,o.data=void 0!==r.data?r.data:null,o.create(),o}return c(n,[{key:"create",value:function(){var t=v(this.opts,"agent","enablesXDR","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");t.xdomain=!!this.opts.xd,t.xscheme=!!this.opts.xs;var e=this.xhr=new h(t),r=this;try{e.open(this.method,this.uri,this.async);try{if(this.opts.extraHeaders)for(var o in e.setDisableHeaderCheck&&e.setDisableHeaderCheck(!0),this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(o)&&e.setRequestHeader(o,this.opts.extraHeaders[o])}catch(t){}if("POST"===this.method)try{e.setRequestHeader("Content-type","text/plain;charset=UTF-8")}catch(t){}try{e.setRequestHeader("Accept","*/*")}catch(t){}"withCredentials"in e&&(e.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(e.timeout=this.opts.requestTimeout),this.hasXDR()?(e.onload=function(){r.onLoad()},e.onerror=function(){r.onError(e.responseText)}):e.onreadystatechange=function(){4===e.readyState&&(200===e.status||1223===e.status?r.onLoad():setTimeout((function(){r.onError("number"==typeof e.status?e.status:0)}),0))},e.send(this.data)}catch(t){return void setTimeout((function(){r.onError(t)}),0)}"undefined"!=typeof document&&(this.index=n.requestsCount++,n.requests[this.index]=this)}},{key:"onSuccess",value:function(){this.emit("success"),this.cleanup()}},{key:"onData",value:function(t){this.emit("data",t),this.onSuccess()}},{key:"onError",value:function(t){this.emit("error",t),this.cleanup(!0)}},{key:"cleanup",value:function(t){if(void 0!==this.xhr&&null!==this.xhr){if(this.hasXDR()?this.xhr.onload=this.xhr.onerror=m:this.xhr.onreadystatechange=m,t)try{this.xhr.abort()}catch(t){}"undefined"!=typeof document&&delete n.requests[this.index],this.xhr=null}}},{key:"onLoad",value:function(){var t=this.xhr.responseText;null!==t&&this.onData(t)}},{key:"hasXDR",value:function(){return"undefined"!=typeof XDomainRequest&&!this.xs&&this.enablesXDR}},{key:"abort",value:function(){this.cleanup()}}]),n}(d);if(w.requestsCount=0,w.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",_);else if("function"==typeof addEventListener){addEventListener("onpagehide"in b?"pagehide":"unload",_,!1)}function _(){for(var t in w.requests)w.requests.hasOwnProperty(t)&&w.requests[t].abort()}t.exports=k,t.exports.Request=w},function(t,e,n){var r=n(11).PACKET_TYPES,o="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),i="function"==typeof ArrayBuffer,s=function(t,e){var n=new FileReader;return n.onload=function(){var t=n.result.split(",")[1];e("b"+t)},n.readAsDataURL(t)};t.exports=function(t,e,n){var c,a=t.type,u=t.data;return o&&u instanceof Blob?e?n(u):s(u,n):i&&(u instanceof ArrayBuffer||(c=u,"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(c):c&&c.buffer instanceof ArrayBuffer))?e?n(u instanceof ArrayBuffer?u:u.buffer):s(new Blob([u]),n):n(r[a]+(u||""))}},function(t,e,n){var r,o=n(11),i=o.PACKET_TYPES_REVERSE,s=o.ERROR_PACKET;"function"==typeof ArrayBuffer&&(r=n(26));var c=function(t,e){if(r){var n=r.decode(t);return a(n,e)}return{base64:!0,data:t}},a=function(t,e){switch(e){case"blob":return t instanceof ArrayBuffer?new Blob([t]):t;case"arraybuffer":default:return t}};t.exports=function(t,e){if("string"!=typeof t)return{type:"message",data:a(t,e)};var n=t.charAt(0);return"b"===n?{type:"message",data:c(t.substring(1),e)}:i[n]?t.length>1?{type:i[n],data:t.substring(1)}:{type:i[n]}:s}},function(t,e){!function(t){"use strict";e.encode=function(e){var n,r=new Uint8Array(e),o=r.length,i="";for(n=0;n>2],i+=t[(3&r[n])<<4|r[n+1]>>4],i+=t[(15&r[n+1])<<2|r[n+2]>>6],i+=t[63&r[n+2]];return o%3==2?i=i.substring(0,i.length-1)+"=":o%3==1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(e){var n,r,o,i,s,c=.75*e.length,a=e.length,u=0;"="===e[e.length-1]&&(c--,"="===e[e.length-2]&&c--);var f=new ArrayBuffer(c),p=new Uint8Array(f);for(n=0;n>4,p[u++]=(15&o)<<4|i>>2,p[u++]=(3&i)<<6|63&s;return f}}("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")},function(t,e,n){function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){for(var n=0;n';n=document.createElement(t)}catch(t){(n=document.createElement("iframe")).name=r.iframeId,n.src="javascript:0"}n.id=r.iframeId,r.form.appendChild(n),r.iframe=n}this.form.action=this.uri(),a(),t=t.replace(d,"\\\n"),this.area.value=t.replace(y,"\\n");try{this.form.submit()}catch(t){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===r.iframe.readyState&&c()}:this.iframe.onload=c}},{key:"supportsBinary",get:function(){return!1}}])&&o(e.prototype,n),r&&o(e,r),l}(l);t.exports=v},function(t,e,n){function r(t){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){for(var n=0;n0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=n,n.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n}return 0|Math.min(t,this.max)},n.prototype.reset=function(){this.attempts=0},n.prototype.setMin=function(t){this.ms=t},n.prototype.setMax=function(t){this.max=t},n.prototype.setJitter=function(t){this.jitter=t}}])})); +//# sourceMappingURL=socket.io.min.js.map \ No newline at end of file diff --git a/web/templates/index.html b/web/templates/index.html new file mode 100644 index 000000000..eda8d51d2 --- /dev/null +++ b/web/templates/index.html @@ -0,0 +1,61 @@ + + + + + + + System Monitor + + + + +

Real-Time System Monitor

+
+
CPU: Loading...
+
RAM: Loading...
+
GPU: Loading...
+
VRAM: Loading...
+
HDD: Loading...
+
Temperature: Loading...
+
+ + + + diff --git a/web/templates/perf-monitor/index.html b/web/templates/perf-monitor/index.html new file mode 100644 index 000000000..10d91dbc0 --- /dev/null +++ b/web/templates/perf-monitor/index.html @@ -0,0 +1,9 @@ +
+ diff --git a/web/templates/perf-monitor/perf-monitor.html b/web/templates/perf-monitor/perf-monitor.html new file mode 100644 index 000000000..adc628016 --- /dev/null +++ b/web/templates/perf-monitor/perf-monitor.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui.py b/webui.py index b8159d855..2f062b1ba 100644 --- a/webui.py +++ b/webui.py @@ -6,6 +6,7 @@ import shared import modules.config import fooocus_version +from api.gradio_helper import * import modules.html import modules.async_worker as worker import modules.constants as constants @@ -23,6 +24,8 @@ from modules.ui_gradio_extensions import reload_javascript from modules.auth import auth_enabled, check_auth from modules.util import is_json +from modules.bulk_enhance_helpers import * + def get_task(*args): args = list(args) @@ -30,6 +33,7 @@ def get_task(*args): return worker.AsyncTask(args=args) + def generate_clicked(task: worker.AsyncTask): import ldm_patched.modules.model_management as model_management @@ -58,7 +62,8 @@ def generate_clicked(task: worker.AsyncTask): # help bad internet connection by skipping duplicated preview if len(task.yields) > 0: # if we have the next item - if task.yields[0][0] == 'preview': # if the next item is also a preview + # if the next item is also a preview + if task.yields[0][0] == 'preview': # print('Skipped one preview for better internet connection.') continue @@ -73,8 +78,6 @@ def generate_clicked(task: worker.AsyncTask): gr.update(visible=True, value=product), \ gr.update(visible=False) if flag == 'finish': - if not args_manager.args.disable_enhance_output_sorting: - product = sort_enhance_images(product, task) yield gr.update(visible=False), \ gr.update(visible=False), \ @@ -122,7 +125,8 @@ def inpaint_mode_change(mode, inpaint_engine_version): if mode == modules.flags.inpaint_option_detail: return [ gr.update(visible=True), gr.update(visible=False, value=[]), - gr.Dataset.update(visible=True, samples=modules.config.example_inpaint_prompts), + gr.Dataset.update( + visible=True, samples=modules.config.example_inpaint_prompts), False, 'None', 0.5, 0.0 ] @@ -132,19 +136,22 @@ def inpaint_mode_change(mode, inpaint_engine_version): if mode == modules.flags.inpaint_option_modify: return [ gr.update(visible=True), gr.update(visible=False, value=[]), - gr.Dataset.update(visible=False, samples=modules.config.example_inpaint_prompts), + gr.Dataset.update( + visible=False, samples=modules.config.example_inpaint_prompts), True, inpaint_engine_version, 1.0, 0.0 ] return [ gr.update(visible=False, value=''), gr.update(visible=True), - gr.Dataset.update(visible=False, samples=modules.config.example_inpaint_prompts), + gr.Dataset.update( + visible=False, samples=modules.config.example_inpaint_prompts), False, inpaint_engine_version, 1.0, 0.618 ] reload_javascript() + title = f'Fooocus {fooocus_version.version}' if isinstance(args_manager.args.preset, str): @@ -165,7 +172,8 @@ def inpaint_mode_change(mode, inpaint_engine_version): progress_html = gr.HTML(value=modules.html.make_progress_html(32, 'Progress 32%'), visible=False, elem_id='progress-bar', elem_classes='progress-bar') gallery = gr.Gallery(label='Gallery', show_label=False, object_fit='contain', visible=True, height=768, - elem_classes=['resizable_area', 'main_view', 'final_gallery', 'image_gallery'], + elem_classes=[ + 'resizable_area', 'main_view', 'final_gallery', 'image_gallery'], elem_id='final_gallery') with gr.Row(): with gr.Column(scale=17): @@ -174,14 +182,20 @@ def inpaint_mode_change(mode, inpaint_engine_version): default_prompt = modules.config.default_prompt if isinstance(default_prompt, str) and default_prompt != '': - shared.gradio_root.load(lambda: default_prompt, outputs=prompt) + shared.gradio_root.load( + lambda: default_prompt, outputs=prompt) with gr.Column(scale=3, min_width=0): - generate_button = gr.Button(label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True) - reset_button = gr.Button(label="Reconnect", value="Reconnect", elem_classes='type_row', elem_id='reset_button', visible=False) - load_parameter_button = gr.Button(label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False) - skip_button = gr.Button(label="Skip", value="Skip", elem_classes='type_row_half', elem_id='skip_button', visible=False) - stop_button = gr.Button(label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False) + generate_button = gr.Button( + label="Generate", value="Generate", elem_classes='type_row', elem_id='generate_button', visible=True) + reset_button = gr.Button(label="Reconnect", value="Reconnect", + elem_classes='type_row', elem_id='reset_button', visible=False) + load_parameter_button = gr.Button( + label="Load Parameters", value="Load Parameters", elem_classes='type_row', elem_id='load_parameter_button', visible=False) + skip_button = gr.Button( + label="Skip", value="Skip", elem_classes='type_row_half', elem_id='skip_button', visible=False) + stop_button = gr.Button( + label="Stop", value="Stop", elem_classes='type_row_half', elem_id='stop_button', visible=False) def stop_clicked(currentTask): import ldm_patched.modules.model_management as model_management @@ -197,21 +211,29 @@ def skip_clicked(currentTask): model_management.interrupt_current_processing() return currentTask - stop_button.click(stop_clicked, inputs=currentTask, outputs=currentTask, queue=False, show_progress=False, _js='cancelGenerateForever') - skip_button.click(skip_clicked, inputs=currentTask, outputs=currentTask, queue=False, show_progress=False) + stop_button.click(stop_clicked, inputs=currentTask, outputs=currentTask, + queue=False, show_progress=False, _js='cancelGenerateForever') + skip_button.click(skip_clicked, inputs=currentTask, + outputs=currentTask, queue=False, show_progress=False) with gr.Row(elem_classes='advanced_check_row'): - input_image_checkbox = gr.Checkbox(label='Input Image', value=modules.config.default_image_prompt_checkbox, container=False, elem_classes='min_check') - enhance_checkbox = gr.Checkbox(label='Enhance', value=modules.config.default_enhance_checkbox, container=False, elem_classes='min_check') - advanced_checkbox = gr.Checkbox(label='Advanced', value=modules.config.default_advanced_checkbox, container=False, elem_classes='min_check') + input_image_checkbox = gr.Checkbox( + label='Input Image', value=modules.config.default_image_prompt_checkbox, container=False, elem_classes='min_check') + enhance_checkbox = gr.Checkbox( + label='Enhance', value=modules.config.default_enhance_checkbox, container=False, elem_classes='min_check') + advanced_checkbox = gr.Checkbox( + label='Advanced', value=modules.config.default_advanced_checkbox, container=False, elem_classes='min_check') with gr.Row(visible=modules.config.default_image_prompt_checkbox) as image_input_panel: with gr.Tabs(selected=modules.config.default_selected_image_input_tab_id): with gr.Tab(label='Upscale or Variation', id='uov_tab') as uov_tab: with gr.Row(): with gr.Column(): - uov_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False) + uov_input_image = grh.Image( + label='Image', source='upload', type='numpy', show_label=False) with gr.Column(): - uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, value=modules.config.default_uov_method) - gr.HTML('\U0001F4D4 Documentation') + uov_method = gr.Radio( + label='Upscale or Variation:', choices=flags.uov_list, value=modules.config.default_uov_method) + gr.HTML( + '\U0001F4D4 Documentation') with gr.Tab(label='Image Prompt', id='ip_tab') as ip_tab: with gr.Row(): ip_images = [] @@ -223,33 +245,40 @@ def skip_clicked(currentTask): for image_count in range(modules.config.default_controlnet_image_count): image_count += 1 with gr.Column(): - ip_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False, height=300, value=modules.config.default_ip_images[image_count]) + ip_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False, + height=300, value=modules.config.default_ip_images[image_count]) ip_images.append(ip_image) ip_ctrls.append(ip_image) with gr.Column(visible=modules.config.default_image_prompt_advanced_checkbox) as ad_col: with gr.Row(): - ip_stop = gr.Slider(label='Stop At', minimum=0.0, maximum=1.0, step=0.001, value=modules.config.default_ip_stop_ats[image_count]) + ip_stop = gr.Slider( + label='Stop At', minimum=0.0, maximum=1.0, step=0.001, value=modules.config.default_ip_stop_ats[image_count]) ip_stops.append(ip_stop) ip_ctrls.append(ip_stop) - ip_weight = gr.Slider(label='Weight', minimum=0.0, maximum=2.0, step=0.001, value=modules.config.default_ip_weights[image_count]) + ip_weight = gr.Slider( + label='Weight', minimum=0.0, maximum=2.0, step=0.001, value=modules.config.default_ip_weights[image_count]) ip_weights.append(ip_weight) ip_ctrls.append(ip_weight) - ip_type = gr.Radio(label='Type', choices=flags.ip_list, value=modules.config.default_ip_types[image_count], container=False) + ip_type = gr.Radio( + label='Type', choices=flags.ip_list, value=modules.config.default_ip_types[image_count], container=False) ip_types.append(ip_type) ip_ctrls.append(ip_type) - ip_type.change(lambda x: flags.default_parameters[x], inputs=[ip_type], outputs=[ip_stop, ip_weight], queue=False, show_progress=False) + ip_type.change(lambda x: flags.default_parameters[x], inputs=[ip_type], outputs=[ + ip_stop, ip_weight], queue=False, show_progress=False) ip_ad_cols.append(ad_col) - ip_advanced = gr.Checkbox(label='Advanced', value=modules.config.default_image_prompt_advanced_checkbox, container=False) + ip_advanced = gr.Checkbox( + label='Advanced', value=modules.config.default_image_prompt_advanced_checkbox, container=False) gr.HTML('* \"Image Prompt\" is powered by Fooocus Image Mixture Engine (v1.0.1). \U0001F4D4 Documentation') def ip_advance_checked(x): return [gr.update(visible=x)] * len(ip_ad_cols) + \ [flags.default_ip] * len(ip_types) + \ [flags.default_parameters[flags.default_ip][0]] * len(ip_stops) + \ - [flags.default_parameters[flags.default_ip][1]] * len(ip_weights) + [flags.default_parameters[flags.default_ip] + [1]] * len(ip_weights) ip_advanced.change(ip_advance_checked, inputs=ip_advanced, outputs=ip_ad_cols + ip_types + ip_stops + ip_weights, @@ -258,29 +287,40 @@ def ip_advance_checked(x): with gr.Tab(label='Inpaint or Outpaint', id='inpaint_tab') as inpaint_tab: with gr.Row(): with gr.Column(): - inpaint_input_image = grh.Image(label='Image', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas', show_label=False) - inpaint_advanced_masking_checkbox = gr.Checkbox(label='Enable Advanced Masking Features', value=modules.config.default_inpaint_advanced_masking_checkbox) - inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, value=modules.config.default_inpaint_method, label='Method') - inpaint_additional_prompt = gr.Textbox(placeholder="Describe what you want to inpaint.", elem_id='inpaint_additional_prompt', label='Inpaint Additional Prompt', visible=False) - outpaint_selections = gr.CheckboxGroup(choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint Direction') + inpaint_input_image = grh.Image(label='Image', source='upload', type='numpy', tool='sketch', + height=500, brush_color="#FFFFFF", elem_id='inpaint_canvas', show_label=False) + inpaint_advanced_masking_checkbox = gr.Checkbox( + label='Enable Advanced Masking Features', value=modules.config.default_inpaint_advanced_masking_checkbox) + inpaint_mode = gr.Dropdown( + choices=modules.flags.inpaint_options, value=modules.config.default_inpaint_method, label='Method') + inpaint_additional_prompt = gr.Textbox( + placeholder="Describe what you want to inpaint.", elem_id='inpaint_additional_prompt', label='Inpaint Additional Prompt', visible=False) + outpaint_selections = gr.CheckboxGroup( + choices=['Left', 'Right', 'Top', 'Bottom'], value=[], label='Outpaint Direction') example_inpaint_prompts = gr.Dataset(samples=modules.config.example_inpaint_prompts, label='Additional Prompt Quick List', - components=[inpaint_additional_prompt], + components=[ + inpaint_additional_prompt], visible=False) - gr.HTML('* Powered by Fooocus Inpaint Engine \U0001F4D4 Documentation') - example_inpaint_prompts.click(lambda x: x[0], inputs=example_inpaint_prompts, outputs=inpaint_additional_prompt, show_progress=False, queue=False) + gr.HTML( + '* Powered by Fooocus Inpaint Engine \U0001F4D4 Documentation') + example_inpaint_prompts.click( + lambda x: x[0], inputs=example_inpaint_prompts, outputs=inpaint_additional_prompt, show_progress=False, queue=False) with gr.Column(visible=modules.config.default_inpaint_advanced_masking_checkbox) as inpaint_mask_generation_col: - inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', tool='sketch', height=500, brush_color="#FFFFFF", mask_opacity=1, elem_id='inpaint_mask_canvas') - invert_mask_checkbox = gr.Checkbox(label='Invert Mask When Generating', value=modules.config.default_invert_mask_checkbox) + inpaint_mask_image = grh.Image(label='Mask Upload', source='upload', type='numpy', tool='sketch', + height=500, brush_color="#FFFFFF", mask_opacity=1, elem_id='inpaint_mask_canvas') + invert_mask_checkbox = gr.Checkbox( + label='Invert Mask When Generating', value=modules.config.default_invert_mask_checkbox) inpaint_mask_model = gr.Dropdown(label='Mask generation model', choices=flags.inpaint_mask_models, value=modules.config.default_inpaint_mask_model) inpaint_mask_cloth_category = gr.Dropdown(label='Cloth category', - choices=flags.inpaint_mask_cloth_category, - value=modules.config.default_inpaint_mask_cloth_category, - visible=False) - inpaint_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', value='', visible=False, info='Use singular whenever possible', placeholder='Describe what you want to detect.') + choices=flags.inpaint_mask_cloth_category, + value=modules.config.default_inpaint_mask_cloth_category, + visible=False) + inpaint_mask_dino_prompt_text = gr.Textbox( + label='Detection prompt', value='', visible=False, info='Use singular whenever possible', placeholder='Describe what you want to detect.') example_inpaint_mask_dino_prompt_text = gr.Dataset( samples=modules.config.example_enhance_detection_prompts, label='Detection Prompt Quick List', @@ -292,11 +332,16 @@ def ip_advance_checked(x): show_progress=False, queue=False) with gr.Accordion("Advanced options", visible=False, open=False) as inpaint_mask_advanced_options: - inpaint_mask_sam_model = gr.Dropdown(label='SAM model', choices=flags.inpaint_mask_sam_model, value=modules.config.default_inpaint_mask_sam_model) - inpaint_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, maximum=1.0, value=0.3, step=0.05) - inpaint_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, maximum=1.0, value=0.25, step=0.05) - inpaint_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", info="Set to 0 to detect all", minimum=0, maximum=10, value=modules.config.default_sam_max_detections, step=1, interactive=True) - generate_mask_button = gr.Button(value='Generate mask from image') + inpaint_mask_sam_model = gr.Dropdown( + label='SAM model', choices=flags.inpaint_mask_sam_model, value=modules.config.default_inpaint_mask_sam_model) + inpaint_mask_box_threshold = gr.Slider( + label="Box Threshold", minimum=0.0, maximum=1.0, value=0.3, step=0.05) + inpaint_mask_text_threshold = gr.Slider( + label="Text Threshold", minimum=0.0, maximum=1.0, value=0.25, step=0.05) + inpaint_mask_sam_max_detections = gr.Slider( + label="Maximum number of detections", info="Set to 0 to detect all", minimum=0, maximum=10, value=modules.config.default_sam_max_detections, step=1, interactive=True) + generate_mask_button = gr.Button( + value='Generate mask from image') def generate_mask(image, mask_model, cloth_category, dino_prompt_text, sam_model, box_threshold, text_threshold, sam_max_detections, dino_erode_or_dilate, dino_debug): from extras.inpaint_mask import generate_mask_from_image @@ -316,11 +361,11 @@ def generate_mask(image, mask_model, cloth_category, dino_prompt_text, sam_model model_type=sam_model ) - mask, _, _, _ = generate_mask_from_image(image, mask_model, extras, sam_options) + mask, _, _, _ = generate_mask_from_image( + image, mask_model, extras, sam_options) return mask - inpaint_mask_model.change(lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] + [gr.update(visible=x == 'sam')] * 2 + [gr.Dataset.update(visible=x == 'sam', @@ -335,19 +380,25 @@ def generate_mask(image, mask_model, cloth_category, dino_prompt_text, sam_model with gr.Tab(label='Describe', id='describe_tab') as describe_tab: with gr.Row(): with gr.Column(): - describe_input_image = grh.Image(label='Image', source='upload', type='numpy', show_label=False) + describe_input_image = grh.Image( + label='Image', source='upload', type='numpy', show_label=False) with gr.Column(): describe_methods = gr.CheckboxGroup( label='Content Type', choices=flags.describe_types, value=modules.config.default_describe_content_type) - describe_apply_styles = gr.Checkbox(label='Apply Styles', value=modules.config.default_describe_apply_prompts_checkbox) - describe_btn = gr.Button(value='Describe this Image into Prompt') - describe_image_size = gr.Textbox(label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False) - gr.HTML('\U0001F4D4 Documentation') + describe_apply_styles = gr.Checkbox( + label='Apply Styles', value=modules.config.default_describe_apply_prompts_checkbox) + describe_btn = gr.Button( + value='Describe this Image into Prompt') + describe_image_size = gr.Textbox( + label='Image Size and Recommended Size', elem_id='describe_image_size', visible=False) + gr.HTML( + '\U0001F4D4 Documentation') def trigger_show_image_properties(image): - value = modules.util.get_image_size_info(image, modules.flags.sdxl_aspect_ratios) + value = modules.util.get_image_size_info( + image, modules.flags.sdxl_aspect_ratios) return gr.update(value=value, visible=True) describe_input_image.upload(trigger_show_image_properties, inputs=describe_input_image, @@ -356,17 +407,22 @@ def trigger_show_image_properties(image): with gr.Tab(label='Enhance', id='enhance_tab') as enhance_tab: with gr.Row(): with gr.Column(): - enhance_input_image = grh.Image(label='Use with Enhance, skips image generation', source='upload', type='numpy') - gr.HTML('\U0001F4D4 Documentation') + enhance_input_image = grh.Image( + label='Use with Enhance, skips image generation', source='upload', type='numpy') + gr.HTML( + '\U0001F4D4 Documentation') with gr.Tab(label='Metadata', id='metadata_tab') as metadata_tab: with gr.Column(): - metadata_input_image = grh.Image(label='For images created by Fooocus', source='upload', type='pil') + metadata_input_image = grh.Image( + label='For images created by Fooocus', source='upload', type='pil') metadata_json = gr.JSON(label='Metadata') - metadata_import_button = gr.Button(value='Apply Metadata') + metadata_import_button = gr.Button( + value='Apply Metadata') def trigger_metadata_preview(file): - parameters, metadata_scheme = modules.meta_parser.read_info_from_image(file) + parameters, metadata_scheme = modules.meta_parser.read_info_from_image( + file) results = {} if parameters is not None: @@ -379,181 +435,265 @@ def trigger_metadata_preview(file): metadata_input_image.upload(trigger_metadata_preview, inputs=metadata_input_image, outputs=metadata_json, queue=False, show_progress=True) - + bulk_enhance_ctrls = [] with gr.Row(visible=modules.config.default_enhance_checkbox) as enhance_input_panel: with gr.Tabs(): - with gr.Tab(label='Upscale or Variation'): + with gr.Tab(label='Bulk Enhance'): + bulk_enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check', + container=False, visible=False) + with gr.Row(): + bulk_enhance_data_type = gr.Radio( + choices=["Files", "Folder"], value="Files", label="Select Files or Folder:") + + with gr.Row(elem_id="file_row", visible=False) as bulk_enhance_file_row: + bulk_enhance_file_explorer = gr.File( + label="Selected Files", + file_count="multiple", + root_dir=".", + show_label=True, + elem_id="file_explorer", + name="file_explorer" + ) + + with gr.Row(elem_id="folder_row", visible=False) as bulk_enhance_folder_row: + + bulk_enhance_input_path = gr.Textbox( + label="Selected Folder", max_lines=1, show_label=True, scale=5, interactive=False) with gr.Row(): with gr.Column(): - enhance_uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, - value=modules.config.default_enhance_uov_method) - enhance_uov_processing_order = gr.Radio(label='Order of Processing', - info='Use before to enhance small details and after to enhance large areas.', - choices=flags.enhancement_uov_processing_order, - value=modules.config.default_enhance_uov_processing_order) - enhance_uov_prompt_type = gr.Radio(label='Prompt', - info='Choose which prompt to use for Upscale or Variation.', - choices=flags.enhancement_uov_prompt_types, - value=modules.config.default_enhance_uov_prompt_type, - visible=modules.config.default_enhance_uov_processing_order == flags.enhancement_uov_after) - - enhance_uov_processing_order.change(lambda x: gr.update(visible=x == flags.enhancement_uov_after), - inputs=enhance_uov_processing_order, - outputs=enhance_uov_prompt_type, - queue=False, show_progress=False) - gr.HTML('\U0001F4D4 Documentation') - enhance_ctrls = [] - enhance_inpaint_mode_ctrls = [] - enhance_inpaint_engine_ctrls = [] - enhance_inpaint_update_ctrls = [] - for index in range(modules.config.default_enhance_tabs): - with gr.Tab(label=f'#{index + 1}') as enhance_tab_item: - enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check', - container=False) - - enhance_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', - info='Use singular whenever possible', - placeholder='Describe what you want to detect.', - interactive=True, - visible=modules.config.default_enhance_inpaint_mask_model == 'sam') - example_enhance_mask_dino_prompt_text = gr.Dataset( - samples=modules.config.example_enhance_detection_prompts, - label='Detection Prompt Quick List', - components=[enhance_mask_dino_prompt_text], - visible=modules.config.default_enhance_inpaint_mask_model == 'sam') - example_enhance_mask_dino_prompt_text.click(lambda x: x[0], - inputs=example_enhance_mask_dino_prompt_text, - outputs=enhance_mask_dino_prompt_text, - show_progress=False, queue=False) - - enhance_prompt = gr.Textbox(label="Enhancement positive prompt", - placeholder="Uses original prompt instead if empty.", - elem_id='enhance_prompt') - enhance_negative_prompt = gr.Textbox(label="Enhancement negative prompt", - placeholder="Uses original negative prompt instead if empty.", - elem_id='enhance_negative_prompt') - - with gr.Accordion("Detection", open=False): - enhance_mask_model = gr.Dropdown(label='Mask generation model', - choices=flags.inpaint_mask_models, - value=modules.config.default_enhance_inpaint_mask_model) - enhance_mask_cloth_category = gr.Dropdown(label='Cloth category', - choices=flags.inpaint_mask_cloth_category, - value=modules.config.default_inpaint_mask_cloth_category, - visible=modules.config.default_enhance_inpaint_mask_model == 'u2net_cloth_seg', - interactive=True) - - with gr.Accordion("SAM Options", - visible=modules.config.default_enhance_inpaint_mask_model == 'sam', - open=False) as sam_options: - enhance_mask_sam_model = gr.Dropdown(label='SAM model', - choices=flags.inpaint_mask_sam_model, - value=modules.config.default_inpaint_mask_sam_model, - interactive=True) - enhance_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, - maximum=1.0, value=0.3, step=0.05, - interactive=True) - enhance_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, - maximum=1.0, value=0.25, step=0.05, - interactive=True) - enhance_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", - info="Set to 0 to detect all", - minimum=0, maximum=10, - value=modules.config.default_sam_max_detections, - step=1, interactive=True) - - with gr.Accordion("Inpaint", visible=True, open=False): - enhance_inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, - value=modules.config.default_inpaint_method, - label='Method', interactive=True) - enhance_inpaint_disable_initial_latent = gr.Checkbox( - label='Disable initial latent in inpaint', value=False) - enhance_inpaint_engine = gr.Dropdown(label='Inpaint Engine', - value=modules.config.default_inpaint_engine_version, - choices=flags.inpaint_engine_versions, - info='Version of Fooocus inpaint model. If set, use performance Quality or Speed (no performance LoRAs) for best results.') - enhance_inpaint_strength = gr.Slider(label='Inpaint Denoising Strength', - minimum=0.0, maximum=1.0, step=0.001, - value=1.0, - info='Same as the denoising strength in A1111 inpaint. ' - 'Only used in inpaint, not used in outpaint. ' - '(Outpaint always use 1.0)') - enhance_inpaint_respective_field = gr.Slider(label='Inpaint Respective Field', - minimum=0.0, maximum=1.0, step=0.001, - value=0.618, - info='The area to inpaint. ' - 'Value 0 is same as "Only Masked" in A1111. ' - 'Value 1 is same as "Whole Image" in A1111. ' - 'Only used in inpaint, not used in outpaint. ' - '(Outpaint always use 1.0)') - enhance_inpaint_erode_or_dilate = gr.Slider(label='Mask Erode or Dilate', - minimum=-64, maximum=64, step=1, value=0, - info='Positive value will make white area in the mask larger, ' - 'negative value will make white area smaller. ' - '(default is 0, always processed before any mask invert)') - enhance_mask_invert = gr.Checkbox(label='Invert Mask', value=False) - - gr.HTML('\U0001F4D4 Documentation') - - enhance_ctrls += [ - enhance_enabled, - enhance_mask_dino_prompt_text, - enhance_prompt, - enhance_negative_prompt, - enhance_mask_model, - enhance_mask_cloth_category, - enhance_mask_sam_model, - enhance_mask_text_threshold, - enhance_mask_box_threshold, - enhance_mask_sam_max_detections, - enhance_inpaint_disable_initial_latent, - enhance_inpaint_engine, - enhance_inpaint_strength, - enhance_inpaint_respective_field, - enhance_inpaint_erode_or_dilate, - enhance_mask_invert - ] - - enhance_inpaint_mode_ctrls += [enhance_inpaint_mode] - enhance_inpaint_engine_ctrls += [enhance_inpaint_engine] - - enhance_inpaint_update_ctrls += [[ - enhance_inpaint_mode, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, - enhance_inpaint_strength, enhance_inpaint_respective_field - ]] - - enhance_inpaint_mode.change(inpaint_mode_change, inputs=[enhance_inpaint_mode, inpaint_engine_state], outputs=[ - inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, - enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, - enhance_inpaint_strength, enhance_inpaint_respective_field - ], show_progress=False, queue=False) - - enhance_mask_model.change( - lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] + - [gr.update(visible=x == 'sam')] * 2 + - [gr.Dataset.update(visible=x == 'sam', - samples=modules.config.example_enhance_detection_prompts)], - inputs=enhance_mask_model, - outputs=[enhance_mask_cloth_category, enhance_mask_dino_prompt_text, sam_options, - example_enhance_mask_dino_prompt_text], - queue=False, show_progress=False) + pass + + with gr.Column(): + with gr.Blocks(): + with gr.Row(): + bulk_enhance_image_browse_btn = gr.Button( + "Browse", elem_id="browse-button", size="sm") + + bulk_enhance_clear_button = gr.ClearButton(elem_id="clear-button", + size="sm") + + bulk_enhance_clear_button.click(on_click_clear, outputs=[ + bulk_enhance_file_explorer, bulk_enhance_input_path, bulk_enhance_file_row, bulk_enhance_folder_row]) + with gr.Column(): + pass + + bulk_enhance_ctrls += [ + bulk_enhance_enabled, + bulk_enhance_data_type, + bulk_enhance_file_explorer, + bulk_enhance_input_path + ] + with gr.Row(visible=modules.config.default_enhance_checkbox) as enhance_input_panel2: + + with gr.Row(): + with gr.Tabs(): + with gr.Tab(label='Upscale or Variation'): + with gr.Row(): + with gr.Column(): + enhance_uov_method = gr.Radio(label='Upscale or Variation:', choices=flags.uov_list, + value=modules.config.default_enhance_uov_method) + enhance_uov_processing_order = gr.Radio(label='Order of Processing', + info='Use before to enhance small details and after to enhance large areas.', + choices=flags.enhancement_uov_processing_order, + value=modules.config.default_enhance_uov_processing_order) + enhance_uov_prompt_type = gr.Radio(label='Prompt', + info='Choose which prompt to use for Upscale or Variation.', + choices=flags.enhancement_uov_prompt_types, + value=modules.config.default_enhance_uov_prompt_type, + visible=modules.config.default_enhance_uov_processing_order == flags.enhancement_uov_after) + + enhance_uov_processing_order.change(lambda x: gr.update(visible=x == flags.enhancement_uov_after), + inputs=enhance_uov_processing_order, + outputs=enhance_uov_prompt_type, + queue=False, show_progress=False) + gr.HTML( + '\U0001F4D4 Documentation') + enhance_ctrls = [] + enhance_inpaint_mode_ctrls = [] + enhance_inpaint_engine_ctrls = [] + enhance_inpaint_update_ctrls = [] + for index in range(modules.config.default_enhance_tabs): + with gr.Tab(label=f'#{index + 1}') as enhance_tab_item: + enhance_enabled = gr.Checkbox(label='Enable', value=False, elem_classes='min_check', + container=False) + + enhance_mask_dino_prompt_text = gr.Textbox(label='Detection prompt', + info='Use singular whenever possible', + placeholder='Describe what you want to detect.', + interactive=True, + visible=modules.config.default_enhance_inpaint_mask_model == 'sam') + example_enhance_mask_dino_prompt_text = gr.Dataset( + samples=modules.config.example_enhance_detection_prompts, + label='Detection Prompt Quick List', + components=[enhance_mask_dino_prompt_text], + visible=modules.config.default_enhance_inpaint_mask_model == 'sam') + example_enhance_mask_dino_prompt_text.click(lambda x: x[0], + inputs=example_enhance_mask_dino_prompt_text, + outputs=enhance_mask_dino_prompt_text, + show_progress=False, queue=False) + + enhance_prompt = gr.Textbox(label="Enhancement positive prompt", + placeholder="Uses original prompt instead if empty.", + elem_id='enhance_prompt') + enhance_negative_prompt = gr.Textbox(label="Enhancement negative prompt", + placeholder="Uses original negative prompt instead if empty.", + elem_id='enhance_negative_prompt') + + # Bind the callback to the selection change + example_enhance_mask_dino_prompt_text.select( + on_selection_change, + inputs=example_enhance_mask_dino_prompt_text, + outputs=[enhance_prompt, + enhance_negative_prompt] + ) + with gr.Accordion("Detection", open=False): + enhance_mask_model = gr.Dropdown(label='Mask generation model', + choices=flags.inpaint_mask_models, + value=modules.config.default_enhance_inpaint_mask_model) + enhance_mask_cloth_category = gr.Dropdown(label='Cloth category', + choices=flags.inpaint_mask_cloth_category, + value=modules.config.default_inpaint_mask_cloth_category, + visible=modules.config.default_enhance_inpaint_mask_model == 'u2net_cloth_seg', + interactive=True) + + with gr.Accordion("SAM Options", + visible=modules.config.default_enhance_inpaint_mask_model == 'sam', + open=False) as sam_options: + enhance_mask_sam_model = gr.Dropdown(label='SAM model', + choices=flags.inpaint_mask_sam_model, + value=modules.config.default_inpaint_mask_sam_model, + interactive=True) + enhance_mask_box_threshold = gr.Slider(label="Box Threshold", minimum=0.0, + maximum=1.0, value=0.3, step=0.05, + interactive=True) + enhance_mask_text_threshold = gr.Slider(label="Text Threshold", minimum=0.0, + maximum=1.0, value=0.25, step=0.05, + interactive=True) + enhance_mask_sam_max_detections = gr.Slider(label="Maximum number of detections", + info="Set to 0 to detect all", + minimum=0, maximum=10, + value=modules.config.default_sam_max_detections, + step=1, interactive=True) + + with gr.Accordion("Inpaint", visible=True, open=False): + enhance_inpaint_mode = gr.Dropdown(choices=modules.flags.inpaint_options, + value=modules.config.default_inpaint_method, + label='Method', interactive=True) + enhance_inpaint_disable_initial_latent = gr.Checkbox( + label='Disable initial latent in inpaint', value=False) + enhance_inpaint_engine = gr.Dropdown(label='Inpaint Engine', + value=modules.config.default_inpaint_engine_version, + choices=flags.inpaint_engine_versions, + info='Version of Fooocus inpaint model. If set, use performance Quality or Speed (no performance LoRAs) for best results.') + enhance_inpaint_strength = gr.Slider(label='Inpaint Denoising Strength', + minimum=0.0, maximum=1.0, step=0.001, + value=1.0, + info='Same as the denoising strength in A1111 inpaint. ' + 'Only used in inpaint, not used in outpaint. ' + '(Outpaint always use 1.0)') + enhance_inpaint_respective_field = gr.Slider(label='Inpaint Respective Field', + minimum=0.0, maximum=1.0, step=0.001, + value=0.618, + info='The area to inpaint. ' + 'Value 0 is same as "Only Masked" in A1111. ' + 'Value 1 is same as "Whole Image" in A1111. ' + 'Only used in inpaint, not used in outpaint. ' + '(Outpaint always use 1.0)') + enhance_inpaint_erode_or_dilate = gr.Slider(label='Mask Erode or Dilate', + minimum=-64, maximum=64, step=1, value=0, + info='Positive value will make white area in the mask larger, ' + 'negative value will make white area smaller. ' + '(default is 0, always processed before any mask invert)') + enhance_mask_invert = gr.Checkbox( + label='Invert Mask', value=False) + + gr.HTML( + '\U0001F4D4 Documentation') + + enhance_ctrls += [ + enhance_enabled, + enhance_mask_dino_prompt_text, + enhance_prompt, + enhance_negative_prompt, + enhance_mask_model, + enhance_mask_cloth_category, + enhance_mask_sam_model, + enhance_mask_text_threshold, + enhance_mask_box_threshold, + enhance_mask_sam_max_detections, + enhance_inpaint_disable_initial_latent, + enhance_inpaint_engine, + enhance_inpaint_strength, + enhance_inpaint_respective_field, + enhance_inpaint_erode_or_dilate, + enhance_mask_invert + ] + + enhance_inpaint_mode_ctrls += [ + enhance_inpaint_mode] + enhance_inpaint_engine_ctrls += [ + enhance_inpaint_engine] + + enhance_inpaint_update_ctrls += [[ + enhance_inpaint_mode, enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, + enhance_inpaint_strength, enhance_inpaint_respective_field + ]] + + enhance_inpaint_mode.change(inpaint_mode_change, inputs=[enhance_inpaint_mode, inpaint_engine_state], outputs=[ + inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, + enhance_inpaint_disable_initial_latent, enhance_inpaint_engine, + enhance_inpaint_strength, enhance_inpaint_respective_field + ], show_progress=False, queue=False) + + enhance_mask_model.change( + lambda x: [gr.update(visible=x == 'u2net_cloth_seg')] + + [gr.update(visible=x == 'sam')] * 2 + + [gr.Dataset.update(visible=x == 'sam', + samples=modules.config.example_enhance_detection_prompts)], + inputs=enhance_mask_model, + outputs=[enhance_mask_cloth_category, enhance_mask_dino_prompt_text, sam_options, + example_enhance_mask_dino_prompt_text], + queue=False, show_progress=False) switch_js = "(x) => {if(x){viewer_to_bottom(100);viewer_to_bottom(500);}else{viewer_to_top();} return x;}" down_js = "() => {viewer_to_bottom();}" - input_image_checkbox.change(lambda x: gr.update(visible=x), inputs=input_image_checkbox, outputs=image_input_panel, queue=False, show_progress=False, _js=switch_js) - ip_advanced.change(lambda: None, queue=False, show_progress=False, _js=down_js) - - current_tab = gr.Textbox(value='uov', visible=False) - uov_tab.select(lambda: 'uov', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - ip_tab.select(lambda: 'ip', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - describe_tab.select(lambda: 'desc', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - enhance_tab.select(lambda: 'enhance', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - metadata_tab.select(lambda: 'metadata', outputs=current_tab, queue=False, _js=down_js, show_progress=False) - enhance_checkbox.change(lambda x: gr.update(visible=x), inputs=enhance_checkbox, - outputs=enhance_input_panel, queue=False, show_progress=False, _js=switch_js) + + ip_advanced.change(lambda: None, queue=False, + show_progress=False, _js=down_js) + + current_tab = gr.Textbox(value='uov', visible=False) + uov_tab.select(lambda: 'uov', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + inpaint_tab.select(lambda: 'inpaint', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + ip_tab.select(lambda: 'ip', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + describe_tab.select(lambda: 'desc', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + enhance_tab.select(lambda: 'enhance', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + metadata_tab.select(lambda: 'metadata', outputs=current_tab, + queue=False, _js=down_js, show_progress=False) + enhance_checkbox.change(fn=update_visibility, inputs=enhance_checkbox, + outputs=[enhance_input_panel, enhance_input_panel2], queue=False, show_progress=False, _js=switch_js) + bulk_enhance_image_browse_btn.click( + on_browse, inputs=bulk_enhance_data_type, outputs=bulk_enhance_input_path, show_progress="hidden") + bulk_enhance_file_explorer.change( + on_file_change, + inputs=[bulk_enhance_file_explorer, bulk_enhance_data_type], + # Note: output both file_row and input_path + outputs=[bulk_enhance_file_row, + bulk_enhance_input_path, bulk_enhance_enabled] + ) + bulk_enhance_input_path.change( + on_input_change, + inputs=[bulk_enhance_input_path, bulk_enhance_file_explorer], + # Update file_explorer based on input_path + outputs=[bulk_enhance_file_explorer, + bulk_enhance_folder_row, bulk_enhance_enabled] + ) with gr.Column(scale=1, visible=modules.config.default_advanced_checkbox) as advanced_column: with gr.Tab(label='Settings'): @@ -575,10 +715,13 @@ def trigger_metadata_preview(file): info='width × height', elem_classes='aspect_ratios') - aspect_ratios_selection.change(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') - shared.gradio_root.load(lambda x: None, inputs=aspect_ratios_selection, queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') + aspect_ratios_selection.change(lambda x: None, inputs=aspect_ratios_selection, + queue=False, show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') + shared.gradio_root.load(lambda x: None, inputs=aspect_ratios_selection, queue=False, + show_progress=False, _js='(x)=>{refresh_aspect_ratios_label(x);}') - image_number = gr.Slider(label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number) + image_number = gr.Slider( + label='Image Number', minimum=1, maximum=modules.config.default_max_image_number, step=1, value=modules.config.default_image_number) output_format = gr.Radio(label='Output Format', choices=flags.OutputFormat.list(), @@ -589,7 +732,9 @@ def trigger_metadata_preview(file): elem_id='negative_prompt', value=modules.config.default_prompt_negative) seed_random = gr.Checkbox(label='Random', value=True) - image_seed = gr.Textbox(label='Seed', value=0, max_lines=1, visible=False) # workaround for https://github.com/gradio-app/gradio/issues/5354 + # workaround for https://github.com/gradio-app/gradio/issues/5354 + image_seed = gr.Textbox( + label='Seed', value=0, max_lines=1, visible=False) def random_checked(r): return gr.update(visible=not r) @@ -616,7 +761,8 @@ def update_history_link(): return gr.update(value=f'\U0001F4DA History Log') history_link = gr.HTML() - shared.gradio_root.load(update_history_link, outputs=history_link, queue=False, show_progress=False) + shared.gradio_root.load( + update_history_link, outputs=history_link, queue=False, show_progress=False) with gr.Tab(label='Styles', elem_classes=['style_selections_tab']): style_sorter.try_load_sorted_styles( @@ -628,17 +774,21 @@ def update_history_link(): value="", label='Search Styles') style_selections = gr.CheckboxGroup(show_label=False, container=False, - choices=copy.deepcopy(style_sorter.all_styles), - value=copy.deepcopy(modules.config.default_styles), + choices=copy.deepcopy( + style_sorter.all_styles), + value=copy.deepcopy( + modules.config.default_styles), label='Selected Styles', elem_classes=['style_selections']) - gradio_receiver_style_selections = gr.Textbox(elem_id='gradio_receiver_style_selections', visible=False) + gradio_receiver_style_selections = gr.Textbox( + elem_id='gradio_receiver_style_selections', visible=False) shared.gradio_root.load(lambda: gr.update(choices=copy.deepcopy(style_sorter.all_styles)), outputs=style_selections) style_search_bar.change(style_sorter.search_styles, - inputs=[style_selections, style_search_bar], + inputs=[style_selections, + style_search_bar], outputs=style_selections, queue=False, show_progress=False).then( @@ -654,8 +804,10 @@ def update_history_link(): with gr.Tab(label='Models'): with gr.Group(): with gr.Row(): - base_model = gr.Dropdown(label='Base Model (SDXL only)', choices=modules.config.model_filenames, value=modules.config.default_base_model_name, show_label=True) - refiner_model = gr.Dropdown(label='Refiner (SDXL or SD 1.5)', choices=['None'] + modules.config.model_filenames, value=modules.config.default_refiner_model_name, show_label=True) + base_model = gr.Dropdown(label='Base Model (SDXL only)', choices=modules.config.model_filenames, + value=modules.config.default_base_model_name, show_label=True) + refiner_model = gr.Dropdown(label='Refiner (SDXL or SD 1.5)', choices=[ + 'None'] + modules.config.model_filenames, value=modules.config.default_refiner_model_name, show_label=True) refiner_switch = gr.Slider(label='Refiner Switch At', minimum=0.1, maximum=1.0, step=0.0001, info='Use 0.4 for SD1.5 realistic models; ' @@ -681,10 +833,12 @@ def update_history_link(): lora_weight = gr.Slider(label='Weight', minimum=modules.config.default_loras_min_weight, maximum=modules.config.default_loras_max_weight, step=0.01, value=weight, elem_classes='lora_weight', scale=5) - lora_ctrls += [lora_enabled, lora_model, lora_weight] + lora_ctrls += [lora_enabled, + lora_model, lora_weight] with gr.Row(): - refresh_files = gr.Button(label='Refresh', value='\U0001f504 Refresh All Files', variant='secondary', elem_classes='refresh_button') + refresh_files = gr.Button( + label='Refresh', value='\U0001f504 Refresh All Files', variant='secondary', elem_classes='refresh_button') with gr.Tab(label='Advanced'): guidance_scale = gr.Slider(label='Guidance Scale', minimum=1.0, maximum=30.0, step=0.01, value=modules.config.default_cfg_scale, @@ -692,8 +846,10 @@ def update_history_link(): sharpness = gr.Slider(label='Image Sharpness', minimum=0.0, maximum=30.0, step=0.001, value=modules.config.default_sample_sharpness, info='Higher value means image and texture are sharper.') - gr.HTML('\U0001F4D4 Documentation') - dev_mode = gr.Checkbox(label='Developer Debug Mode', value=modules.config.default_developer_debug_mode_checkbox, container=False) + gr.HTML( + '\U0001F4D4 Documentation') + dev_mode = gr.Checkbox( + label='Developer Debug Mode', value=modules.config.default_developer_debug_mode_checkbox, container=False) with gr.Column(visible=modules.config.default_developer_debug_mode_checkbox) as dev_tools: with gr.Tab(label='Debug Tools'): @@ -713,8 +869,8 @@ def update_history_link(): info='Enabling Fooocus\'s implementation of CFG mimicking for TSNR ' '(effective when real CFG > mimicked CFG).') clip_skip = gr.Slider(label='CLIP Skip', minimum=1, maximum=flags.clip_skip_max, step=1, - value=modules.config.default_clip_skip, - info='Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).') + value=modules.config.default_clip_skip, + info='Bypass CLIP layers to avoid overfitting (use 1 to not skip any layers, 2 is recommended).') sampler_name = gr.Dropdown(label='Sampler', choices=flags.sampler_list, value=modules.config.default_sampler) scheduler_name = gr.Dropdown(label='Scheduler', choices=flags.scheduler_list, @@ -754,13 +910,15 @@ def update_history_link(): interactive=not modules.config.default_black_out_nsfw, info='Disable preview during generation.') disable_intermediate_results = gr.Checkbox(label='Disable Intermediate Results', - value=flags.Performance.has_restricted_features(modules.config.default_performance), - info='Disable intermediate results during generation, only show final gallery.') + value=flags.Performance.has_restricted_features( + modules.config.default_performance), + info='Disable intermediate results during generation, only show final gallery.') disable_seed_increment = gr.Checkbox(label='Disable seed increment', info='Disable automatic seed increment when image number is > 1.', value=False) - read_wildcards_in_order = gr.Checkbox(label="Read wildcards in order", value=False) + read_wildcards_in_order = gr.Checkbox( + label="Read wildcards in order", value=False) black_out_nsfw = gr.Checkbox(label='Black Out NSFW', value=modules.config.default_black_out_nsfw, interactive=not modules.config.default_black_out_nsfw, @@ -806,12 +964,14 @@ def update_history_link(): step=1, value=128) with gr.Tab(label='Inpaint'): - debugging_inpaint_preprocessor = gr.Checkbox(label='Debug Inpaint Preprocessing', value=False) + debugging_inpaint_preprocessor = gr.Checkbox( + label='Debug Inpaint Preprocessing', value=False) debugging_enhance_masks_checkbox = gr.Checkbox(label='Debug Enhance Masks', value=False, info='Show enhance masks in preview and final results') debugging_dino = gr.Checkbox(label='Debug GroundingDINO', value=False, info='Use GroundingDINO boxes instead of more detailed SAM masks') - inpaint_disable_initial_latent = gr.Checkbox(label='Disable initial latent in inpaint', value=False) + inpaint_disable_initial_latent = gr.Checkbox( + label='Disable initial latent in inpaint', value=False) inpaint_engine = gr.Dropdown(label='Inpaint Engine', value=modules.config.default_inpaint_engine_version, choices=flags.inpaint_engine_versions, @@ -839,7 +999,8 @@ def update_history_link(): 'negative value will make white area smaller. ' '(default is 0, processed before SAM)') - inpaint_mask_color = gr.ColorPicker(label='Inpaint brush color', value='#FFFFFF', elem_id='inpaint_brush_color') + inpaint_mask_color = gr.ColorPicker( + label='Inpaint brush color', value='#FFFFFF', elem_id='inpaint_brush_color') inpaint_ctrls = [debugging_inpaint_preprocessor, inpaint_disable_initial_latent, inpaint_engine, inpaint_strength, inpaint_respective_field, @@ -847,7 +1008,8 @@ def update_history_link(): inpaint_advanced_masking_checkbox.change(lambda x: [gr.update(visible=x)] * 2, inputs=inpaint_advanced_masking_checkbox, - outputs=[inpaint_mask_image, inpaint_mask_generation_col], + outputs=[ + inpaint_mask_image, inpaint_mask_generation_col], queue=False, show_progress=False) inpaint_mask_color.change(lambda x: gr.update(brush_color=x), inputs=inpaint_mask_color, @@ -855,12 +1017,18 @@ def update_history_link(): queue=False, show_progress=False) with gr.Tab(label='FreeU'): - freeu_enabled = gr.Checkbox(label='Enabled', value=False) - freeu_b1 = gr.Slider(label='B1', minimum=0, maximum=2, step=0.01, value=1.01) - freeu_b2 = gr.Slider(label='B2', minimum=0, maximum=2, step=0.01, value=1.02) - freeu_s1 = gr.Slider(label='S1', minimum=0, maximum=4, step=0.01, value=0.99) - freeu_s2 = gr.Slider(label='S2', minimum=0, maximum=4, step=0.01, value=0.95) - freeu_ctrls = [freeu_enabled, freeu_b1, freeu_b2, freeu_s1, freeu_s2] + freeu_enabled = gr.Checkbox( + label='Enabled', value=False) + freeu_b1 = gr.Slider( + label='B1', minimum=0, maximum=2, step=0.01, value=1.01) + freeu_b2 = gr.Slider( + label='B2', minimum=0, maximum=2, step=0.01, value=1.02) + freeu_s1 = gr.Slider( + label='S1', minimum=0, maximum=4, step=0.01, value=0.99) + freeu_s2 = gr.Slider( + label='S2', minimum=0, maximum=4, step=0.01, value=0.95) + freeu_ctrls = [freeu_enabled, freeu_b1, + freeu_b2, freeu_s1, freeu_s2] def dev_mode_checked(r): return gr.update(visible=r) @@ -870,9 +1038,12 @@ def dev_mode_checked(r): def refresh_files_clicked(): modules.config.update_files() - results = [gr.update(choices=modules.config.model_filenames)] - results += [gr.update(choices=['None'] + modules.config.model_filenames)] - results += [gr.update(choices=[flags.default_vae] + modules.config.vae_filenames)] + results = [ + gr.update(choices=modules.config.model_filenames)] + results += [gr.update(choices=['None'] + + modules.config.model_filenames)] + results += [gr.update(choices=[flags.default_vae] + + modules.config.vae_filenames)] if not args_manager.args.disable_preset_selection: results += [gr.update(choices=modules.config.available_presets)] for i in range(modules.config.default_max_lora_number): @@ -895,17 +1066,22 @@ def refresh_files_clicked(): base_model, refiner_model, refiner_switch, sampler_name, scheduler_name, vae_name, seed_random, image_seed, inpaint_engine, inpaint_engine_state, inpaint_mode] + enhance_inpaint_mode_ctrls + [generate_button, - load_parameter_button] + freeu_ctrls + lora_ctrls + load_parameter_button] + freeu_ctrls + lora_ctrls if not args_manager.args.disable_preset_selection: def preset_selection_change(preset, is_generating, inpaint_mode): - preset_content = modules.config.try_get_preset_content(preset) if preset != 'initial' else {} - preset_prepared = modules.meta_parser.parse_meta_from_preset(preset_content) + preset_content = modules.config.try_get_preset_content( + preset) if preset != 'initial' else {} + preset_prepared = modules.meta_parser.parse_meta_from_preset( + preset_content) default_model = preset_prepared.get('base_model') - previous_default_models = preset_prepared.get('previous_default_models', []) - checkpoint_downloads = preset_prepared.get('checkpoint_downloads', {}) - embeddings_downloads = preset_prepared.get('embeddings_downloads', {}) + previous_default_models = preset_prepared.get( + 'previous_default_models', []) + checkpoint_downloads = preset_prepared.get( + 'checkpoint_downloads', {}) + embeddings_downloads = preset_prepared.get( + 'embeddings_downloads', {}) lora_downloads = preset_prepared.get('lora_downloads', {}) vae_downloads = preset_prepared.get('vae_downloads', {}) @@ -918,7 +1094,6 @@ def preset_selection_change(preset, is_generating, inpaint_mode): return modules.meta_parser.load_parameter_button_click(json.dumps(preset_prepared), is_generating, inpaint_mode) - def inpaint_engine_state_change(inpaint_engine_version, *args): if inpaint_engine_version == 'empty': inpaint_engine_version = modules.config.default_inpaint_engine_version @@ -939,15 +1114,17 @@ def inpaint_engine_state_change(inpaint_engine_version, *args): performance_selection.change(lambda x: [gr.update(interactive=not flags.Performance.has_restricted_features(x))] * 11 + [gr.update(visible=not flags.Performance.has_restricted_features(x))] * 1 + - [gr.update(value=flags.Performance.has_restricted_features(x))] * 1, + [gr.update( + value=flags.Performance.has_restricted_features(x))] * 1, inputs=performance_selection, outputs=[ guidance_scale, sharpness, adm_scaler_end, adm_scaler_positive, adm_scaler_negative, refiner_switch, refiner_model, sampler_name, scheduler_name, adaptive_cfg, refiner_swap_method, negative_prompt, disable_intermediate_results - ], queue=False, show_progress=False) + ], queue=False, show_progress=False) - output_format.input(lambda x: gr.update(output_format=x), inputs=output_format) + output_format.input(lambda x: gr.update( + output_format=x), inputs=output_format) advanced_checkbox.change(lambda x: gr.update(visible=x), advanced_checkbox, advanced_column, queue=False, show_progress=False) \ @@ -960,7 +1137,8 @@ def inpaint_engine_state_change(inpaint_engine_version, *args): ], show_progress=False, queue=False) # load configured default_inpaint_method - default_inpaint_ctrls = [inpaint_mode, inpaint_disable_initial_latent, inpaint_engine, inpaint_strength, inpaint_respective_field] + default_inpaint_ctrls = [inpaint_mode, inpaint_disable_initial_latent, + inpaint_engine, inpaint_strength, inpaint_respective_field] for mode, disable_initial_latent, engine, strength, respective_field in [default_inpaint_ctrls] + enhance_inpaint_update_ctrls: shared.gradio_root.load(inpaint_mode_change, inputs=[mode, inpaint_engine_state], outputs=[ inpaint_additional_prompt, outpaint_selections, example_inpaint_prompts, disable_initial_latent, @@ -984,13 +1162,19 @@ def inpaint_engine_state_change(inpaint_engine_version, *args): ctrls += [base_model, refiner_model, refiner_switch] + lora_ctrls ctrls += [input_image_checkbox, current_tab] ctrls += [uov_method, uov_input_image] - ctrls += [outpaint_selections, inpaint_input_image, inpaint_additional_prompt, inpaint_mask_image] - ctrls += [disable_preview, disable_intermediate_results, disable_seed_increment, black_out_nsfw] - ctrls += [adm_scaler_positive, adm_scaler_negative, adm_scaler_end, adaptive_cfg, clip_skip] + ctrls += [outpaint_selections, inpaint_input_image, + inpaint_additional_prompt, inpaint_mask_image] + ctrls += [disable_preview, disable_intermediate_results, + disable_seed_increment, black_out_nsfw] + ctrls += [adm_scaler_positive, adm_scaler_negative, + adm_scaler_end, adaptive_cfg, clip_skip] ctrls += [sampler_name, scheduler_name, vae_name] - ctrls += [overwrite_step, overwrite_switch, overwrite_width, overwrite_height, overwrite_vary_strength] - ctrls += [overwrite_upscale_strength, mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint] - ctrls += [debugging_cn_preprocessor, skipping_cn_preprocessor, canny_low_threshold, canny_high_threshold] + ctrls += [overwrite_step, overwrite_switch, overwrite_width, + overwrite_height, overwrite_vary_strength] + ctrls += [overwrite_upscale_strength, + mixing_image_prompt_and_vary_upscale, mixing_image_prompt_and_inpaint] + ctrls += [debugging_cn_preprocessor, skipping_cn_preprocessor, + canny_low_threshold, canny_high_threshold] ctrls += [refiner_swap_method, controlnet_softness] ctrls += freeu_ctrls ctrls += inpaint_ctrls @@ -1006,6 +1190,7 @@ def inpaint_engine_state_change(inpaint_engine_version, *args): enhance_input_image, enhance_checkbox, enhance_uov_method, enhance_uov_processing_order, enhance_uov_prompt_type] ctrls += enhance_ctrls + ctrls += bulk_enhance_ctrls def parse_meta(raw_prompt_txt, is_generating): loaded_json = None @@ -1020,17 +1205,21 @@ def parse_meta(raw_prompt_txt, is_generating): return json.dumps(loaded_json), gr.update(visible=False), gr.update(visible=True) - prompt.input(parse_meta, inputs=[prompt, state_is_generating], outputs=[prompt, generate_button, load_parameter_button], queue=False, show_progress=False) + prompt.input(parse_meta, inputs=[prompt, state_is_generating], outputs=[ + prompt, generate_button, load_parameter_button], queue=False, show_progress=False) - load_parameter_button.click(modules.meta_parser.load_parameter_button_click, inputs=[prompt, state_is_generating, inpaint_mode], outputs=load_data_outputs, queue=False, show_progress=False) + load_parameter_button.click(modules.meta_parser.load_parameter_button_click, inputs=[ + prompt, state_is_generating, inpaint_mode], outputs=load_data_outputs, queue=False, show_progress=False) def trigger_metadata_import(file, state_is_generating): - parameters, metadata_scheme = modules.meta_parser.read_info_from_image(file) + parameters, metadata_scheme = modules.meta_parser.read_info_from_image( + file) if parameters is None: print('Could not find metadata in the image!') parsed_parameters = {} else: - metadata_parser = modules.meta_parser.get_metadata_parser(metadata_scheme) + metadata_parser = modules.meta_parser.get_metadata_parser( + metadata_scheme) parsed_parameters = metadata_parser.to_json(parameters) return modules.meta_parser.load_parameter_button_click(parsed_parameters, state_is_generating, inpaint_mode) @@ -1058,7 +1247,8 @@ def trigger_metadata_import(file, state_is_generating): for notification_file in ['notification.ogg', 'notification.mp3']: if os.path.exists(notification_file): - gr.Audio(interactive=False, value=notification_file, elem_id='audio_notification', visible=False) + gr.Audio(interactive=False, value=notification_file, + elem_id='audio_notification', visible=False) break def trigger_describe(modes, img, apply_styles): @@ -1068,7 +1258,8 @@ def trigger_describe(modes, img, apply_styles): if flags.describe_type_photo in modes: from extras.interrogate import default_interrogator as default_interrogator_photo describe_prompts.append(default_interrogator_photo(img)) - styles.update(["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"]) + styles.update( + ["Fooocus V2", "Fooocus Enhance", "Fooocus Sharp"]) if flags.describe_type_anime in modes: from extras.wd14tagger import default_interrogator as default_interrogator_anime @@ -1098,7 +1289,6 @@ def trigger_auto_describe(mode, img, prompt, apply_styles): if prompt == '': return trigger_describe(mode, img, apply_styles) return gr.update(), gr.update() - uov_input_image.upload(trigger_auto_describe, inputs=[describe_methods, uov_input_image, prompt, describe_apply_styles], outputs=[prompt, style_selections], show_progress=True, queue=True) \ .then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \ @@ -1109,6 +1299,8 @@ def trigger_auto_describe(mode, img, prompt, apply_styles): outputs=[prompt, style_selections], show_progress=True, queue=True) \ .then(fn=style_sorter.sort_styles, inputs=style_selections, outputs=style_selections, queue=False, show_progress=False) \ .then(lambda: None, _js='()=>{refresh_style_localization();}') + addResourceMonitor() + def dump_default_english_config(): from modules.localization import dump_english_config @@ -1122,7 +1314,8 @@ def dump_default_english_config(): server_name=args_manager.args.listen, server_port=args_manager.args.port, share=args_manager.args.share, - auth=check_auth if (args_manager.args.share or args_manager.args.listen) and auth_enabled else None, + auth=check_auth if ( + args_manager.args.share or args_manager.args.listen) and auth_enabled else None, allowed_paths=[modules.config.path_outputs], blocked_paths=[constants.AUTH_FILENAME] )