From f9e61fd184ef36a235671b04c14b50215e476c5c Mon Sep 17 00:00:00 2001 From: snowy Date: Sun, 31 Mar 2024 06:22:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=85=8D=E7=BD=AE=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=9A=84=E7=83=AD=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/deployment/config.md | 11 +- liteyuki/liteyuki_main/core.py | 6 +- liteyuki/liteyuki_main/webdash.py | 124 +- liteyuki/plugins/liteyuki_eventpush.py | 4 +- liteyuki/plugins/liteyuki_markdowntest.py | 8 +- .../plugins/liteyuki_minigame/minesweeper.py | 10 +- liteyuki/plugins/liteyuki_npm/installer.py | 10 +- liteyuki/plugins/liteyuki_npm/manager.py | 10 +- .../plugins/liteyuki_user/profile_manager.py | 6 +- liteyuki/resources/lang/zh-CN.lang | 10 + liteyuki/resources/templates/css/fonts.css | 13 + liteyuki/resources/templates/img/bg1.jpg | Bin 0 -> 921865 bytes liteyuki/resources/templates/js/echarts.js | 85683 ++++++++++++++++ liteyuki/resources/templates/stats.html | 232 +- liteyuki/resources/templates/stats.json | 29 - liteyuki/resources/templates/test.html | 93 + liteyuki/utils/htmlrender/browser.py | 2 +- liteyuki/utils/language.py | 14 + liteyuki/utils/liteyuki_api.py | 28 + liteyuki/utils/message.py | 224 +- liteyuki/utils/resource.py | 14 +- requirements.txt | 1 + 22 files changed, 86394 insertions(+), 138 deletions(-) create mode 100644 liteyuki/resources/templates/css/fonts.css create mode 100644 liteyuki/resources/templates/img/bg1.jpg create mode 100644 liteyuki/resources/templates/js/echarts.js delete mode 100644 liteyuki/resources/templates/stats.json create mode 100644 liteyuki/resources/templates/test.html diff --git a/docs/deployment/config.md b/docs/deployment/config.md index 412f5872..97038933 100644 --- a/docs/deployment/config.md +++ b/docs/deployment/config.md @@ -9,6 +9,7 @@ tag: --- ### 轻雪配置项(Nonebot插件配置项也可以写在此,与dotenv格式不同,应为小写) + 配置文件会在首次启动后生成,你可以在`config.yaml`中修改配置项后重启轻雪 如果不确定字段的含义,请不要修改(部分在自动生成配置文件中未列出,需手动添加) @@ -26,6 +27,14 @@ default_language: "zh-CN" # 默认语言,支持i18n部分语言和自行扩展 log_level: "INFO" # 日志等级 log_icon: true # 是否显示日志等级图标(某些控制台字体不可用) auto_report: true # 是否自动上报问题给轻雪服务器,仅包含硬件信息和运行软件版本 +fake_device_info: # 统计卡片显示的虚假设备信息,用于保护隐私 + cpu: + brand: AMD + cores: 16 # 物理核心数 + logical_cores: 32 # 逻辑核心数 + frequency: 3600 # CPU主频:MHz + mem: + total: 32768000000 # 内存总数:字节 # 其他Nonebot插件的配置项 custom_config_1: "custom_value1" @@ -43,8 +52,8 @@ custom_config_2: "custom_value2" | 地址 | ws://`address`/onebot/v11 | 地址取决于配置文件,本机默认为`127.0.0.1:20216` | | AccessToken | `""` | 如果你给轻雪配置了`AccessToken`,请在此填写相同的值 | - ### 其他通信方式 + - 实现端与轻雪的通信方式不局限为反向WebSocket,但是推荐使用反向WebSocket。 - 反向WebSocket的优点是轻雪作为服务端,可以更好的控制连接,适用于生产环境。 - 在某些情况下,你也可以使用正向WebSocket,比如你在开发轻雪插件时,可以使用正向WebSocket主动连接实现端 \ No newline at end of file diff --git a/liteyuki/liteyuki_main/core.py b/liteyuki/liteyuki_main/core.py index 29a47183..8ea9acb8 100644 --- a/liteyuki/liteyuki_main/core.py +++ b/liteyuki/liteyuki_main/core.py @@ -9,7 +9,7 @@ from liteyuki.utils.config import config, load_from_yaml from liteyuki.utils.data_manager import StoredConfig, common_db from liteyuki.utils.language import get_user_lang from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent -from liteyuki.utils.message import Markdown as md, send_markdown +from liteyuki.utils.message import Markdown as md from .reloader import Reloader from liteyuki.utils import htmlrender @@ -80,7 +80,7 @@ async def _(bot: T_Bot, event: T_MessageEvent): reply += f"```\n{logs}\n```\n" btn_restart = md.button(ulang.get("liteyuki.restart_now"), "restart-liteyuki") reply += f"{ulang.get('liteyuki.update_restart', RESTART=btn_restart)}" - await send_markdown(reply, bot, event=event, at_sender=False) + await md.send_md(reply, bot, event=event, at_sender=False) @reload_liteyuki.handle() @@ -119,7 +119,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): for k, v in stored_config.config.items(): reply += f"\n{k}={v}" reply += "\n```" - await send_markdown(reply, bot, event=event) + await md.send_md(reply, bot, event=event) @driver.on_startup diff --git a/liteyuki/liteyuki_main/webdash.py b/liteyuki/liteyuki_main/webdash.py index 63ae3559..3c64c24e 100644 --- a/liteyuki/liteyuki_main/webdash.py +++ b/liteyuki/liteyuki_main/webdash.py @@ -1,19 +1,131 @@ +import json +import random + +import psutil import requests +from PIL import Image from nonebot.adapters.onebot.v11 import MessageSegment from nonebot.permission import SUPERUSER from liteyuki.utils.htmlrender import template_to_pic, html_to_pic - +from liteyuki.utils.language import get_user_lang +from liteyuki.utils.liteyuki_api import liteyuki_api +from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent +from liteyuki.utils.message import Markdown as md from liteyuki.utils.resource import get_path from nonebot import on_command +from cpuinfo import get_cpu_info -stats = on_command("stats", priority=5, permission=SUPERUSER) +from liteyuki.utils.tools import convert_size + +stats = on_command("stats", aliases={"状态"}, priority=5, permission=SUPERUSER) + +protocol_names = { + 0: "iPad", + 1: "Android Phone", + 2: "Android Watch", + 3: "Mac", + 5: "iPad", + 6: "Android Pad", +} @stats.handle() -async def _(): - image_bytes = await template_to_pic( - template_path=get_path("templates/index.html", abs_path=True), - templates={} +async def _(bot: T_Bot, event: T_MessageEvent): + ulang = get_user_lang(str(event.user_id)) + fake_device_info: dict = bot.config.dict().get("fake_device_info", {}) + mem_total = fake_device_info.get('mem', {}).get('total', psutil.virtual_memory().total) + mem_used_bot = psutil.Process().memory_info().rss + mem_used_other = psutil.virtual_memory().used - mem_used_bot + mem_free = mem_total - mem_used_other - mem_used_bot + + groups = len(await bot.get_group_list()) + friends = len(await bot.get_friend_list()) + + status = await bot.get_status() + statistics = status.get("stat", {}) + version_info = await bot.get_version_info() + + cpu_info = get_cpu_info() + if "AMD" in cpu_info.get("brand_raw", ""): + brand = "AMD" + elif "Intel" in cpu_info.get("brand_raw", ""): + brand = "Intel" + else: + brand = "Unknown" + + if fake_device_info.get("cpu", {}).get("brand"): + brand = fake_device_info.get("cpu", {}).get("brand") + + cpu_info = get_cpu_info() + templ = { + "CPUDATA" : [ + { + "name" : "USED", + "value": psutil.cpu_percent(interval=1) + }, + { + "name" : "FREE", + "value": 100 - psutil.cpu_percent(interval=1) + } + ], + "MEMDATA" : [ + + { + "name" : "OTHER", + "value": mem_used_other + }, + { + "name" : "FREE", + "value": mem_free + }, + { + "name" : "BOT", + "value": mem_used_bot + }, + ], + "SWAPDATA" : [ + { + "name" : "USED", + "value": psutil.swap_memory().used + }, + { + "name" : "FREE", + "value": psutil.swap_memory().free + } + ], + "BOT_ID" : bot.self_id, + "BOT_NAME" : (await bot.get_login_info())["nickname"], + "BOT_TAGS" : [ + protocol_names.get(version_info.get("protocol_name"), "Linux"), version_info.get("app_name"), version_info.get("app_version"), + f"{ulang.get('liteyuki.stats.groups')} {groups}", f"{ulang.get('liteyuki.stats.friends')} {friends}", + f"{ulang.get('liteyuki.stats.sent')} {statistics.get('message_sent', 0)}", + f"{ulang.get('liteyuki.stats.received')} {statistics.get('message_received', 0)}" \ + ], + "CPU_TAGS" : [ + f"{brand} {cpu_info.get('arch', 'Unknown')}", + f"{fake_device_info.get('cpu', {}).get('cores', psutil.cpu_count(logical=False))}C " + f"{fake_device_info.get('cpu', {}).get('logical_cores', psutil.cpu_count(logical=True))}T", + f"{fake_device_info.get('cpu', {}).get('frequency', psutil.cpu_freq().current) / 1000}GHz" + ], + "MEM_TAGS" : [ + f"Bot {convert_size(mem_used_bot, 1)}", + f"{ulang.get('main.monitor.used')} {convert_size(mem_used_other + mem_used_bot, 1)}", + f"{ulang.get('main.monitor.total')} {convert_size(mem_total, 1)}", + ], + "SWAP_TAGS": [ + f"{ulang.get('main.monitor.used')} {convert_size(psutil.swap_memory().used, 1)}", + f"{ulang.get('main.monitor.total')} {convert_size(psutil.swap_memory().total, 1)}", + ], + "CPU" : ulang.get("main.monitor.cpu"), + "MEM" : ulang.get("main.monitor.memory"), + "SWAP" : ulang.get("main.monitor.swap"), + } + image_bytes = await template_to_pic( + template_path=get_path("templates/stats.html", abs_path=True), + templates=templ, + wait=1, + device_scale_factor=4, ) + # await md.send_image(image_bytes, bot, event=event) await stats.finish(MessageSegment.image(image_bytes)) diff --git a/liteyuki/plugins/liteyuki_eventpush.py b/liteyuki/plugins/liteyuki_eventpush.py index 13d302af..f93edd05 100644 --- a/liteyuki/plugins/liteyuki_eventpush.py +++ b/liteyuki/plugins/liteyuki_eventpush.py @@ -4,7 +4,7 @@ from nonebot.plugin import PluginMetadata from liteyuki.utils.data import Database, LiteModel from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent -from liteyuki.utils.message import send_markdown +from liteyuki.utils.message import Markdown as md require("nonebot_plugin_alconna") from nonebot_plugin_alconna import on_alconna @@ -108,7 +108,7 @@ async def _(event: T_MessageEvent, bot: T_Bot): push_message = ( f"> From {event.sender.nickname}@{push.source.session_type}.{push.source.session_id}\n> Bot {bot.self_id}\n\n" f"{msg_formatted}") - await send_markdown(push_message, bot2, message_type=push.target.session_type, session_id=push.target.session_id) + await md.send_md(push_message, bot2, message_type=push.target.session_type, session_id=push.target.session_id) return diff --git a/liteyuki/plugins/liteyuki_markdowntest.py b/liteyuki/plugins/liteyuki_markdowntest.py index 3f8832bc..6edccf10 100644 --- a/liteyuki/plugins/liteyuki_markdowntest.py +++ b/liteyuki/plugins/liteyuki_markdowntest.py @@ -5,7 +5,7 @@ from nonebot.permission import SUPERUSER from nonebot.plugin import PluginMetadata from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent, v11 -from liteyuki.utils.message import send_markdown +from liteyuki.utils.message import Markdown as md md_test = on_command("mdts", aliases={"会话md"}, permission=SUPERUSER) md_group = on_command("mdg", aliases={"群md"}, permission=SUPERUSER) @@ -23,7 +23,7 @@ placeholder = { @md_test.handle() async def _(bot: T_Bot, event: T_MessageEvent, arg: v11.Message = CommandArg()): - await send_markdown( + await md.send_md( str(arg), bot, message_type=event.message_type, @@ -40,8 +40,8 @@ async def _(bot: v11.Bot, event: T_MessageEvent, arg: v11.Message = CommandArg() if str(event.user_id) == str(bot.self_id) and str(bot.self_id) in ["2751454815"]: nonebot.logger.info("开始处理:%s" % str(event.message_id)) - data = await send_markdown(str(arg), bot, message_type=event.message_type, - session_id=event.user_id if event.message_type == "private" else event.group_id) + data = await md.send_md(str(arg), bot, message_type=event.message_type, + session_id=event.user_id if event.message_type == "private" else event.group_id) await bot.delete_msg(message_id=event.message_id) diff --git a/liteyuki/plugins/liteyuki_minigame/minesweeper.py b/liteyuki/plugins/liteyuki_minigame/minesweeper.py index c2f0c23e..5318a5a1 100644 --- a/liteyuki/plugins/liteyuki_minigame/minesweeper.py +++ b/liteyuki/plugins/liteyuki_minigame/minesweeper.py @@ -1,7 +1,7 @@ from nonebot import require from ...utils.ly_typing import T_Bot, T_MessageEvent -from ...utils.message import send_markdown +from ...utils.message import Markdown as md require("nonebot_plugin_alconna") from .game import Minesweeper @@ -63,7 +63,7 @@ async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot): ) minesweeper_cache.append(new_game) await minesweeper.send("游戏开始") - await send_markdown(new_game.board_markdown(), bot, event=event) + await md.send_md(new_game.board_markdown(), bot, event=event) except AssertionError: await minesweeper.finish("参数错误") elif result.subcommands.get("end"): @@ -82,9 +82,9 @@ async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot): await minesweeper.finish("参数错误") if not game.reveal(row, col): minesweeper_cache.remove(game) - await send_markdown(game.board_markdown(), bot, event=event) + await md.send_md(game.board_markdown(), bot, event=event) await minesweeper.finish("游戏结束") - await send_markdown(game.board_markdown(), bot, event=event) + await md.send_md(game.board_markdown(), bot, event=event) if game.is_win(): minesweeper_cache.remove(game) await minesweeper.finish("游戏胜利") @@ -97,6 +97,6 @@ async def _(event: T_MessageEvent, result: Arparma, bot: T_Bot): if not (0 <= row < game.rows and 0 <= col < game.cols): await minesweeper.finish("参数错误") game.board[row][col].flagged = not game.board[row][col].flagged - await send_markdown(game.board_markdown(), bot, event=event) + await md.send_md(game.board_markdown(), bot, event=event) else: await minesweeper.finish("参数错误") diff --git a/liteyuki/plugins/liteyuki_npm/installer.py b/liteyuki/plugins/liteyuki_npm/installer.py index 98a24986..a7c7792e 100644 --- a/liteyuki/plugins/liteyuki_npm/installer.py +++ b/liteyuki/plugins/liteyuki_npm/installer.py @@ -10,7 +10,7 @@ from nonebot import require from nonebot.permission import SUPERUSER from liteyuki.utils.language import get_user_lang from liteyuki.utils.ly_typing import T_Bot -from liteyuki.utils.message import Markdown as md, send_markdown +from liteyuki.utils.message import Markdown as md from .common import * require("nonebot_plugin_alconna") @@ -81,7 +81,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): reply += f"\n{ulang.get('npm.too_many_results', HIDE_NUM=len(rs) - max_show)}" else: reply = ulang.get("npm.search_no_result") - await send_markdown(reply, bot, event=event) + await md.send_md(reply, bot, event=event) elif result.subcommands.get("install"): plugin_module_name: str = result.subcommands["install"].args.get("plugin_name") @@ -104,7 +104,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): if found_in_db_plugin is None: plugin_db.upsert(installed_plugin) info = md.escape(ulang.get("npm.install_success", NAME=store_plugin.name)) # markdown转义 - await send_markdown( + await md.send_md( f"{info}\n\n" f"```\n{log}\n```", bot, @@ -114,7 +114,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): await npm_alc.finish(ulang.get("npm.plugin_already_installed", NAME=store_plugin.name)) else: info = ulang.get("npm.load_failed", NAME=plugin_module_name, HOMEPAGE=homepage_btn).replace("_", r"\\_") - await send_markdown( + await md.send_md( f"{info}\n\n" f"```\n{log}\n```\n", bot, @@ -122,7 +122,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): ) else: info = ulang.get("npm.install_failed", NAME=plugin_module_name, HOMEPAGE=homepage_btn).replace("_", r"\\_") - await send_markdown( + await md.send_md( f"{info}\n\n" f"```\n{log}\n```", bot, diff --git a/liteyuki/plugins/liteyuki_npm/manager.py b/liteyuki/plugins/liteyuki_npm/manager.py index d38409c9..baaa5857 100644 --- a/liteyuki/plugins/liteyuki_npm/manager.py +++ b/liteyuki/plugins/liteyuki_npm/manager.py @@ -13,7 +13,7 @@ from nonebot.plugin import Plugin from liteyuki.utils.data_manager import GlobalPlugin, Group, InstalledPlugin, User, group_db, plugin_db, user_db from liteyuki.utils.language import get_user_lang from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent -from liteyuki.utils.message import Markdown as md, send_markdown +from liteyuki.utils.message import Markdown as md from liteyuki.utils.permission import GROUP_ADMIN, GROUP_OWNER from .common import get_plugin_can_be_toggle, get_plugin_default_enable, get_plugin_global_enable, get_plugin_session_enable from .installer import get_store_plugin, npm_update @@ -107,7 +107,7 @@ async def _(event: T_MessageEvent, bot: T_Bot): reply += f" {btn_uninstall} {btn_toggle_global}" reply += "\n\n***\n" - await send_markdown(reply, bot, event=event) + await md.send_md(reply, bot, event=event) @toggle_plugin.handle() @@ -228,6 +228,6 @@ async def pre_handle(event: Event, matcher: Matcher): raise IgnoredException("Plugin disabled in session") -@Bot.on_calling_api -async def _(bot: Bot, api: str, data: dict[str, any]): - nonebot.logger.info(f"Plugin Callapi: {api}: {data}") +# @Bot.on_calling_api +# async def _(bot: Bot, api: str, data: dict[str, any]): +# nonebot.logger.info(f"Plugin Callapi: {api}: {data}") diff --git a/liteyuki/plugins/liteyuki_user/profile_manager.py b/liteyuki/plugins/liteyuki_user/profile_manager.py index b1124ff4..730be345 100644 --- a/liteyuki/plugins/liteyuki_user/profile_manager.py +++ b/liteyuki/plugins/liteyuki_user/profile_manager.py @@ -7,7 +7,7 @@ from liteyuki.utils.data import LiteModel from liteyuki.utils.data_manager import User, user_db from liteyuki.utils.language import Language, get_all_lang, get_user_lang from liteyuki.utils.ly_typing import T_Bot, T_MessageEvent -from liteyuki.utils.message import Markdown as md, send_markdown +from liteyuki.utils.message import Markdown as md from .const import representative_timezones_list require("nonebot_plugin_alconna") @@ -62,7 +62,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): # 未输入值,尝试呼出菜单 menu = get_profile_menu(result.args["key"], ulang) if menu: - await send_markdown(menu, bot, event=event) + await md.send_md(menu, bot, event=event) else: await profile_alc.finish(ulang.get("user.profile.input_value", ATTR=ulang.get(f"user.profile.{result.args['key']}"))) @@ -94,7 +94,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot): reply += (f"\n**{key_text}** **{val}**\n" f"\n> {ulang.get(f'user.profile.{key}.desc')}" f"\n> {btn_set} \n\n***\n") - await send_markdown(reply, bot, event=event) + await md.send_md(reply, bot, event=event) def get_profile_menu(key: str, ulang: Language) -> Optional[str]: diff --git a/liteyuki/resources/lang/zh-CN.lang b/liteyuki/resources/lang/zh-CN.lang index 6edd17f4..7146ec69 100644 --- a/liteyuki/resources/lang/zh-CN.lang +++ b/liteyuki/resources/lang/zh-CN.lang @@ -13,6 +13,14 @@ liteyuki.current_config=当前配置项如下 liteyuki.static_config=静态文件配置项 liteyuki.stored_config=储存的配置项 liteyuki.config_set_success=配置项 {KEY}={VAL} 设置成功 +liteyuki.stats.group=群 +liteyuki.stats.user=好友 +liteyuki.stats.plugin=插件 +liteyuki.stats.sent=发送 +liteyuki.stats.received=接收 +liteyuki.stats.run_time=运行时间 +liteyuki.stats.groups=群 +liteyuki.stats.friends=好友 main.current_language=当前配置语言为: {LANG} main.enable_webdash=已启用网页监控面板: {URL} @@ -23,6 +31,8 @@ main.monitor.memory=内存 main.monitor.swap=交换空间 main.monitor.disk=磁盘 main.monitor.usage=使用率 +main.monitor.total=总计 +main.monitor.used=已用 data_manager.migrate_success=数据模型{NAME}迁移成功 diff --git a/liteyuki/resources/templates/css/fonts.css b/liteyuki/resources/templates/css/fonts.css new file mode 100644 index 00000000..f33f5267 --- /dev/null +++ b/liteyuki/resources/templates/css/fonts.css @@ -0,0 +1,13 @@ +@font-face { + font-family: 'MiSans'; + src: url('../../fonts/normal.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'MiSans'; + src: url('../../fonts/bold.ttf') format('truetype'); + font-weight: bold; + font-style: normal; +} diff --git a/liteyuki/resources/templates/img/bg1.jpg b/liteyuki/resources/templates/img/bg1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f2ff3d4d0f8a32aeda0574a365a7069d48c4a11a GIT binary patch literal 921865 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

{Oa1wL`MZCmJ+ow~s52QxYZrxT3T;$Z#CAYNQ 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 uHd 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%0AgW776$uFu>&IKqQTW;9 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 z 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�UK0B?R5D88Ge*d+yO=*==-Yz<*x65A!aT%E0L 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ⅈeQ@q?PANl7eOr&tfeoRJEcqh4-onDEYWpK;Mt71 zpGJsLQitEt4aLi{=g?+r9Uuyu6p1=e(d_Wk-($#R}^C7ecz zb6~~dudb&`%O90%*FAd{z+`V%eDTkjVS07l`WEh2*J6s{n`s$MW6udjO9Fh2^haRZ z;*$B-*vYAY3sWcOX=ri*GMwlf^Q3e*IRL$R@{`Sm0-7|7erCiH4d+4no`z? za2}A2Yqnj>mYd8-(mKXSw?gomFm(6@p4V^LLmPxjEN#``)%Sy;=k%`5wht`mNWsDJ zI@UAjo`RqnE_Gsp)^Y9+|GB{Bhg}>}u+(C0dGD4wvmfLO>0}io=`oilJ{O{66K6T; z-%8GdM~Zx^U`=0}R*aAqOaWWB7(;gQp<2Dbio9TCfWt#iRh8WJmxy#5@?cIC7rzET zatDE#UoSehX2%`O%cyX{BaL0EoE>z8<6;1dpSa|q9jiLC7B|0yo6@*E$;aF8gBNaq zb#TZyTvs?9X(eC6g6_Q=9sHDkOar^%XXk zI%(9@b`NEEr{HzCxnG7oY%A&dtvrPVZylFQGriOzDQ;It#A2 zxMB&GeIuwzqs7BM3(CnEM(+~-M`hb);8Smcm<^bgHKJx4S^BS4AGtVM8}mk`#wUqe zpsTjtH~z=&(jVuUzD_T(>frnj(>gyc5Zki|IC&%7{8HeBoIkjLPOE9KQ`%~G(`92p zhm5P0A-%chEh<(gu!kZ9YmD8b>v(*SdCT#=uGQ9Qr5;-E)AveaMT~zTGA}OM*@Gi%esmcy$Lfz^=!)w#ap$6G&CDwVxw!Ql?*|o(f-bDraiG zAU;<^{d#L_p0<{8*hnEU@fu|G(q32ex9>(H`c~Pg%AN-uLd$h-&p|`AN9kxxd+89S zth1x>eL>H^^8WzOw)0#H`Z7G26dEM4W2C$I@vk0n*qqShsS$%IN5lJM!njD({@tqr zgJoAfDHX1?mr59xSBK+QV#lequ~yxQK>6jUpMQSo>))P!RvN8-{tAx2t`giJO&}DY z&^T5TZXsOay6zfNPi&ybz6hv9iUBNL>WTzi>%yGo+xGN(o6uN3c%}KQ_Vb$e1p8D; zN|;Vq6D!6M`!e<`ShGs>GHqw#Pr4Kn-;%cwcxy#xb*!03;Fa|LMlujjn51tLuR8XL zT?%HI6VDMZcRv+;i3;`cl7qF=avHlXK5*1GzXmXp8z*jzY=Z5#A@8+9QPh(aV#$tlHaz@^zaM) z51_;go;*0t^Y=BCJ;zW2nwyCh>y^O(C_fa8M|Pa=sKd&B%yo_!CRAI0F&Z)H?IxG)g%|M zO?}g+DO;_Wfg84s(=#wKgEu8=tQq{3Ei$h}C;e{g)zq0)8iRMOiQd`yhAAca$N``4 zwv;U?{q|H2FeGhpnN3gzztsCmWy;93Jo~JNJPIsr+C;UfWma<&s zli)gQGCtl#+aK0sYrIbRV6$qiW#@7%WS?7JHwAvM^GWBz-cyQ(JP)fE{_wniph3b# zqTMBzKhhnIZGN{O34CqUVcSMztS#*AFrE;#I6gh#c<~?iY1_K)ejChpx?b0WHX?o} zyVciE)o|-fv;KreXk8=Ec?0#3e>MVst_qaM= z^sX8|y^##FU5Z!d>&fe!- zYj4L?B#H{ObS!cA;vM+|Y(H&(Xt=Na@I<~;`-bxHYrog8BVIMl=*F#?6E|PjFs%wd zF-JR`MufCgxpXDt-S)c9ya=}X)$^stNzpBv>lsDM@kQ|c8$71}??b%j2K$k<(w0la zN6t20T$7#|cQW?BFOlYM-Va?D*S1q$*1Ej^`6YbA;w*6d_S~z0gE^V{(Wo+7PuWdU z32A#jN;^QxF|Yv+fA7-CpKVP?@{(3^Lw>E%t=kh#+L&QC9n0uS16uce z`Jz`}o6lAe&z7qFAl1@J6lYD{#FCE%JisjP$=_5~&Jt(3-ajttyFpP^qf%3yzSH^C z1XTJ)@*=aEIQz$awb!1GT`shrEfQF0sKN`W)8C=Hgk-*HhVkDv2x}WJax{Cx&&d-w zbT%a_gJa3#zP02HOenIYpe=psBK8zdBP;uR8cIu6rr&buSd#vWSU43w3+g|5FiQKT zVo}|bvzBw_XwkR$P#_Zr3%h6)TGIKvgL3D^!J*FnEF{<*V^MSpyq#0LFo*xCDHA9l zQW`QN7Q!9#uK$04n$n<)$<-bIq&2{bxkdjRWE&!&an?8kZ19bS%tp*7XT{^RoYEEc zUowNTSsq|k6U+o55=W(iezFsFWLw#4yWjo)UrggPeK?^SKfz_yQo#jZ1zy@*Vx`B0 z1N!k>_62f2Ky*QkjqwnOr?KPJzy+~0>VD9}(?geEKPaK#kI+7_z{RGyWRIu`P+=9d z3{zucIgMUH`($Q6#ySA_x zkNWwMtRbJ!N`NAm*0s&v!WBogoRrb};&;j#quVx%t;BO5I$g=2=}cd1e=*f{$qSyV z+$QIrER~FtgCzbX;eHI^gj*kft&Z-JmS#=Fba}HE?~RUQTh=EBCJ9L#ETZoN9OrS? zz!JZjyhj+q9Y>&g4kb05@qdMC&Eu?I(moC-{{B>Jg{RJe+4N01qql<3aa~i)r@W#T zEHL?>)x=?_kW4r_$3^~ILsfir(*v>#>kK;j#@@%H_5wM2|EgN|()10(pzBK}-ScS1 z!gKJ*bU2qpQaI>T$Mqr}JM$M+%pY25r#_KVDm5o(<0n~h)N>F0i~H`i4pNHFYCkLa z-6(kaoMvOl1OK77n{~r-HQI`>$p+cxuiJv>MTQ4Lt`)RrRa!F7E1aIIHEHydyA*_{ zR{6V0PB=e_br0>nEZ@`eS7WyG)4`P}F;)I%QVG%s)%v6*_Q`P47bCiv1U1AC4kdWC zJ!f+T=aTeO2CRNAi6Hh|{bBWKZ@vF0zP)GssbI&xwlJl#l>+&U$yM2r`E?Yl5{6(H zNptz>m=%1^^T*3z)P2+EGsE6mri=g(+u$5bJ_PJnwkd(gUcB^w8Br!#8AMqeG&^4y zC9^KY?VQFV1oQ_0&fRf?a@Z%xAs^Uf9WPlT z?urDcIza&vrmk!~pV9O(Vgw@K+D!91@USw=GuE zo*1B(TX4pKFqlRewR-50-cr)7F07`LzT31MP3Q5b^Z~#MV2woWAw~J?1}t#&ckF}* zf#oOWQ=g0({(88|7dTNHhy%pQ65rXw6WsBcCME%wKnc=N5NIr&Ei^IG;Np-+*sj{? zQ$b*#;(g`Pe-2=~o2ljVRC6CoZ9}_fM64OT&}%uyHSep##n&e|L)izZb+q-sn-3+S zes`V9MDn$UbU>M8t@iduhd>LZ#qaB8!CQLYzpo!WxNsW)eUbS;Kqu>-Sod0!m2Igd z6$X7jIF1)|yryPvxFGSHy?M=?X_$OuHi|Gg9!>vb!7X_v`>doh?QUfWE*L_&e~ZQ1 z+>sr#@YOu{V%o*|I8|W}xBE_um)uCOG_I#`Hwz2o3USF14yAk&={qtT)2#cqwyC*C zZY&q-gHWy$D%S`JX#qEUwt8SSw-K(^J4~acI1^PNDp8fXn)RFWtSLVXqyGqXD$BqE-O2j7bgGjPmO39Z2^MNc6ld zyPX6~mYigcda(E@tV6HpiA?1Wz0Shg zNo-DGI%2870xm@X2W`)U47PsuAFE3NUEv*+Fi#}mr@1)pTy?ytT(W@CH|VjJwq4sX zeqL}^MI&z$Kn75;QBfEwD=6C!xZMcauK0BSB-Lz_SBx-1xxL48gkqP*Dhr~may=zX zcG9knIFAWaCEXw?&qly06Eub^V!M5S4G+>51k;lh<%nf;Z-LL!m5n@%*o-7P15Jer zVVz-e($}VCZ^fOadRu&dAV{ztFCrBTo~q5oemEHtIji+x(5gO}MjpLUTDioqGPtzw z?b#uvM9{wYQ4U3TNDKw}#c4Id>rtopYGH&)!tIW8E;VlFx)U679|=6b8cCbuOQ0yF zIH?MzruV67x0P#1*LsYHpwL*sE@W9cLakIzO+}QGDcwBEBggiQkOA+35FOF@GALEN zR;tWQHVoc^eObs*AnPgT0W`GrvMBS?(l-cH0(1>EyH%h25C&}P9_pLCBO#^Z81_S; zGv6z|8c5r==m;%FE&MAGu6sxz(JQp#lA#`zdvZLv5f`>Eyu;iP$-$c650D@|@Tbs{ zPs}|(q}prkI7~8(Q7GF*6tS%eoP;F71>W*bRw4}}v1BrgCP#2~V3#KuO;UjvVZ2{QqDk$j4>!t- z<{JjosHO?D(d$47^dxEr3~JB%uL_S}uajdf=CB!+0RqZCE)~+|0Fem*B5w-*gyiRi<8?B7sy2PfnJTM}IQ=*b$eE6cU;^7&KO8@>B~H4$|6k z1?$W@VAwM&`}C!H95-h{vm(+&>Rvk8ww`#CWZ6U|SQmA(D_~jZ@@}IZBBN8FBjH`| zOyi84ya4lPom6JU+B*VdU<~cV(0j|LB0Oz#83L6??W}?~1Uo^d9hpEx5kSdGuZ!tx zwJ2yzhdv1|)H%I|J2))xnZ7JRxWtr-tI|dwf}HEAZ)h-~Pt%jO?Vo^1=;Gw6u<&%8 z8xVKtqISS(+;_q>Lk;^QZapD1kL{RNU9I)hGMn3%(hj6!(@%=1uHs2MGl2_?t$dP^65!QiS;HvM1l}GTiY^U9Yv!t zTUNE|oJ>XlOq67t(d22mB0!)5@M9-%MA}j(shL7!1yinL*kw$-b5x>V3`A+&fof)K zgfhoo&SYpzdy|F>iF^=5TvDPde=7&+hWi5}Zup2O4?SscB4)#w(Ky$pa*z|WzQ(%1nww&@Z8509+ATV+k3 z#P4#obwTS9@fV#d-ab>mBSV(hY+7)lP$PbNJ~}gAj)8FJJNwu2I0+RW6|tCijQTzL z6g7HFV}~M0Q!4tXd@b+|du<=`b~w&F zWAooBVMu(fO@{$)cjD*!JHpeuwnl9&1>N~5$FnuNj~^}O)O#;QcOIV;p+$}fn-p>n zkkQp9WA^0-=L}SI{|6YBsQ>1tkxjpM{ax7m^ExU8C5BT=RAs9lb#k4Lb2S&mp~nX)Jk{y##$|B-Gt=c`N!Gi}7D9>e4z+$>%fn zx1Ca{DQlxS>WV2?rc?(1*uKNP96WY8SvvTRyL}@Nc_FRafb->o|3Eu5)NrAWBW03w ziJCpv3VC2!?m@MSxDpLgD~gVyW3t+RhVpZn(=+)bchE#G$j5zGi479hNv z6c5U=Y3aTD=jwLfuw+L%daTaDUop5w$o!K$=tEr85QgLX-!~j0>23nMBxiqLe-mt4 z4}$GZYV6$ZfWCaL%Ew(x>wBc)8vpC$5`Z(;uhw6Qt!sNa7BVAi{{K06*NFuCr(}>x zu0CJ?Mf|$(?Y_LF)2=OTrit(Opb%WH!)IkVY4Pqx+s9R!P%!yyrZyq@$+EVs+t$5{ z!nFV`E~_$`q1TfUIe)Ar+V;ZO%!5zMS(Ei4q}jRGRS;d4_%h zS0o#rA$QE91e(u3-K<0&=6fVWv#q$h;t~h)ZOtC4c%y7(UrtZJ10&nN#5A^caiYCp zZ-YB2=nl|h3a;#Kyc7>TCVcJUl}m)*s>j-nrZ_LTp#qSHs z>iYK|TY26%Z?~>#f*k{1Mg!=)Q-Ya9XZ=R~v$a^gG43TVhEta%%O4n}7ccnF)xIbW zMekx%RO&HAqZUxi6{&f|CCeuc_%Wa7=O-UZ`gBfTNGq?ICL#O{WCaI8^DQRS-6sko7f4#H=czefP$kD)%UQ?y1eu{A)y_{Etq=W= zcVAsA_Gcq!+bJu@y;Cv@I@t#R1`-69C^*?9dD#@V$n0cId#7jb2($vu*HjP&DW?O1 zn*iYv>2m&Q>+R#L3=8d%#l0}OVS-gIywvn=-nId&x9HFpQgW|gYD&6&Qe~om(fOlk zpUOeiDxXqVGT{}?taKs^3%)G5)voi0)>MQAi9OgMSzM9-kc)3;2>2M7I9`nCPsf3mO`tl z4(^+K#k@yi5{lBKbxR}~FS-bdyioW^?iIuMo1?zxF1*ggfpnh?l>@ZuG+8 zS+=Sv?mVBtn1vS`3ma*ha}iWoUboOdyt#=F*~e$eUy;RYc@lFO~bxyx5j`_6n1^Th~qC)z$Rne*K4RXX>RTUY_;ZM>017q(qZ3&aPg;=?P1R&@V{e8t-HJKX~ z;_xaC`jc<|5P34mO(rV+6J#S3h?cn&GV`B+zu`KK&8zmuC%M+zS6P2kr~d>F3sX0u z2H^rNcSseckN^HG{i?eW?#(2WItemxwn0@pH2UZT?y`rmO((q9(^*l;-7`3nCyo$D z%<~_&zOxj5l^X7}`j)R~UWA6yP)S?mW8MUZQ{Vx6n3ip^yx(flL1}_B4{kf_s(4_Uw)Oh1{fYEn9JN}Efv|kr1Rexp? zT=~j~9upcwp=x3~{bWq4w8XG{63x}~Rh&Kgv?oqlnI4<;aI(@>HrBiN7!){|sMyFoZnj-$eX_3@nM-=VQBWIpu?kvlM(-xqynHCfW^E1 zuG`webLQ!CFbg+EyU=XzKa<6!wGR{phaJT&))*g;D|%5~a0m0j9eFIRlXo)UoUky+ zx<#T%FXa28qn)<&86O%}3Q9{;c%1SbQVd}8|Cn-+L^i0bF}FnTQJr*3z>u#2-wYz7 z)1*K>HQ3GRwZ`@%=@OXHfJsLLXN#cTjQ6Qw1{o?NuJ)f%U=ukCrFgAl^l_wE=h#Z?tvGjK5LtgcS@MDH= zLod;WkqWfU=CMMkc=Z=5gcSGWd=*~x$DWymrwpXC*TBdmcW7u{SYUJv+z&{e6iN`+ z&c5XxmrCq_(Vq{JFuS$fM)_h+KDY6mKUyfkDhY(C58jS=up`#OG;YE2gHr@<3dP zQfHttK-p~C?~F7aMh2rhUx~LRcIS#I07f<>Li%X=fE^-P)8sh@%cF!NaC9*IKw_W+ zSBZ?Qp)b+<+2^gP*v(Of4S7b+tHIk!y2HoZPBbZYk8=eLkqL+SbdX*rFDw!Zn+XDuLMr%I(OqNTz2sUo{!n(t`HGbTr@8oH#Rk+r4Ou)R~*YSF5Nh7wl`) zy0{ardCgh<71zNe(0eO1cOQzreqH{XnDv!Mie}sdCurCltm7mP0uOKfH@o+BFxdQ& z&2?^aoyW^PgfKQtu@-mn>YJ9<$KArtcMZz|k)6{u_i;qX)oRP1du@-L7EUX)L@dZ_ zb-x8^i5!Oae_{2yM)L1=vA1bO)3Jfiw-x|Ij>|GPglgL2z zz#&pTWepMAgH`FujSz}}2ZXCM(f>^(qznJJf>#ha&5-U5+EtvO5crVAnlL{cs{7{` zEh`e)83a?u@)kg42Iu`=+dUKsotO^*<@?S%zjJbVs}ByY zvD;$KP9|&|(2kzD7MEmK;PYBOMHra#dXdITNJK=zv=mAvyhO*~+|;cNYCLv2ffSlA zr&OB#*4^N@mynRLNu$<1Z-=sUER{XK`n`Hbye=TheE_m&mFj#>Z;b$Q{=rZnidrn? zEm_SbPIIXRTggF{E4hPCW1Y#oy49!k6NE;*Ckqrii#o6*#Ay8dKWEv}ki5Z9(3fG2 zNJNEDot9pK%tCS+IQ~|e>|%5_T8X#1&U1m%vY2J3MXCLMiC&RL-1Xgy?c=@B{mlQ- zheDG0tnF{FJmi&^ygY*o}n`BJ0nN0@FJOe{@;%v@^~sqQM|U?TC~ks-Nz%11Xn! zeb($B|LfNwHi9b1^95ZKBziK^T8AsXK6dDnQKAMH!u(WMCaJ@cE@x^7l%$kI3NVQ{ z2^l|mnZKNZf}XExS`4nJI?n)?rt(X^2?yVB(W*1H?o4`)#a4mh({c(C_#P?_ki<}Z zC8W5DZiV`L33FrlC07`b_<z2T%B-QZzg_j6ra>IG%rfj4l5m_w$!4r>!ExEPI2$pRqf1O$oey&`An(UJ!{i&onoB8lVD;(uSBXTm_GkY>CG0a za=o(bUNF~Jgq-d_&+tu=Yw^epGcDM0{8O?$Xbe9^vQtUnuPk6nBW_T zKi6>TCqN`oq)e88E!fiO@J2UTc9ov_^-DHs@A|c;O0Dw$fVd%U6(;)(f(exhFfmP6 zxYIhnzxtU;&wQxN%w*IpWLye5SU?foo>z!K@wr@ft8BA@U!`x@8sYYwwA(Ee<1x*8 z(o;+X9^Krsvi|Yf;5p7}b2}C;r@`%`J(W&J?6MwVlOa3coYKp_{GGu%_nk((fv~*P&p_E-wzRL?=}h-9IF+*UBNIk7;ph9&n=YI;Fm~U1 zZPA=r&j>T8kUAZ4Au5O*q5-Qe*o3Fnd6n0M)hwF;1fVk&COO3X)^RK3Qvj-=aW za_MMnSR7)PfH)l(^D>1s094vUK%P3=;rA78p+@mJ1MW6Y(d z*00jraP^z-iLT>ZVaW}BTV{h*Nu9I#SfP59{AAH!FBM(m3y(MsQla%`=!^s`LmU4O zRp;W)^dJBIZRW5!&rnVc8#afKQ%Y@f+U7Jf5|U$uh@zy3nVgcDIVY!$IVE!bl5;s# zaws8Z3h9InI^DbP>$+^oSp3leQ`H{p-)2|LjYnEn?*7oaW2`EqA zxh*7xp=tgXU|7l4#y=L1J}L)mluT;3NWl`Nz|rZm39Zf)bEI{@>W;~qGbbRo6JoTle3=o#M^*h26W+1+u|LyC{zbjCocmar`c~~s_Xf;n zZ|`HF*28JpERAGUZ?!K94*vjR9!=+2r-}O)N6R&@XBJ=v&P9(BAe5Uweq*d&iRSSX z8Q8|57FfX2^DBy3_Ns0(F8+sFE5tK9>V`WihIWtIXgReq6Rx+e8+tGU!{pXZ59cS^ zO_o|Mi~($g?#g7@1mCldW3w8v1_~y%AA*Hs(S(i<+Ms?_bb&Y$E; zt|!Raz1s>AMrYV>8y*ocKTwJ}-d{U>IU>TYTNe@E=sq7=|25mizbqQgxRs@Zbd4Db1DYdEnn8y0i?D5gaZtJ-yjq_+yo=L-~68;YFjOJ#h<(j%ZA1H-8kHnHRqW$WV-x3T`=W_!u)* zuKz;g3#@_deP6jD<RY#wr30J|p~$GZmp&>C$`3B6OBSMNh-FGUI{Q*g23*~70- zaFZvxI+9xi(rPs(fpQc)g;4@>Dfz3qJ$vDs=%tfkPPDH!`O_&b?oLO=VR!=q%D$nz zn|IY%IFWH2@r59mFY!nMUN5fjNl_gh_U(z0c1H)JRM#j2K>=`xv)t5=a06H zD=>CnfJmSQ z%kg$!(JjeMAGPZdTgpTzls||;&@WL&6b#T8Ix-Y4a4y)w*wrI%uqs-Q^Xb8t%HM^x zoji)+BR#$x0pDktso3p{cKvo7vQO=U@avb;aP+O8A-O!(y+mit4fhZ;3@|J_-Jq8+ zb|QRxQ|d_P$6G(0-VM{l*a7%XB`+XC($u~`0;clWi*O7>=Fkk7oG5aqF{@w7$M##Y z-mzg(N(enk0))p(fGhyV!ag1BR&Z(bo_5hpV!||Z-z^}yLZT(46K{ z_KQ96w@#w_k)JeIuNfbzYU5n+KO)G$GZVClB%oV%u>?IC`AZBwdC;x7kp*KPcjjp6 z4@LOJsrvw%w$087-370<3ux9G7`)4cpSge{x-ei4RniZgYaksN0+9trjdLIdX$Fk~ z=y5u|fzb(Z%=_9uN4h!%vj6Qeg7&sVS(=KuJ8OV;u(&a2%0u;NEAZ?DA9o5-fIeJK zSo?8LpcOoERUO&ELXOxpfVwS8GH`i$+JfcW(eA9z6Ag~znGKIxPAy9l? zg7P5nAab{}^?b}TI+}Ou{3|7C@CHBN}m>T}$t;!pJ z21jSaP773wz*Cq+{(z4bGwZ($xMHIIa*S}U+vi|iTM0ti1N9K;JiGOrbjr_al$a6j z)q4zWy%chmotybrsiF?Rw9Zx}&q>-JNu)eVnY^H7@QQ_2?nGUfM41!GX)Ld6gvPta z1DdDZ+)488+wkt!P#px*xkiElKqDnGfY{#5>Uf3g-FGbChX)3rDU=*<0@)V%2|3KP zIAp7_ff&+8Lw*Ai(48bvCsPvTVZx!!lNth4XU?G`rA`W>>@|qxgFA0rMH|IR1sD_U zcHOoK^7~$hcqD*~r6SQKjeUQ8?nwx)umd0&k6)HAsC&y@wa@j+YVeqA-H8w+EokFh z8o(P9o0JsnTR*rqR-L2k5_4CZyH-#PC!}P(19QvvWh?^B1Bb5BMMrS0?y;G z(;-z!U=!z6E=PlGISw~?!l+bnm;9Oq6jIjaC~=I~NZR@a&O`jG1jyR;d>q(0$}sM*U@xD89^bY;~N=YnchSK@oWwH zM1&qx82GKReb!5Y$%cTe7`TfPCpp`?=3(6yk4KUcHk_2Gwf4p(80@&T_ru-(IGDG- z`$eUsh?pQv)4lS_+Muu#r2RB+rFup3 z#AN9RDPy(RY@GTy07IkU4K0wHqRPr*(gKd6Y=BF6d<74#S`6QC++}Xy3k*#XjIk{3 zhL!^XP)Y&i(;7U$)HDx5A8yP`BRUE_Bc0YMzCgL1?ed1?t+L6F)4POr^FS86lbqN; zFW!lCy8S+ibQKZ^=M9A~VjZ{=WLBo+x;wGRmc4l$bG(;X$WOBID)&1W3(L|-(=(+& zb_W>D+{x#6Az|Z;VukSkDg&%p_KlK$$x2y=^o$%&yKoMXJ&tW2vTfan#;I04)EVY)diQ>N%Ankz7~cb0FweE%3Q~BfB7_t>HVyQBEk^;1 zdge+Vx|1PD=@>PS3zE3GngepI#GxZBExPjxt(}~zbe1`nR|T#|jd_r>ha-wj=Gm-$ z`z9XkR+d;aA+05mw|jvM1}qsPWA6%qwoV@%t$mbH zEyHPUSeA!gE_0(UQ{V7FhC+pzHmj-%djutx-?)#OYl) zRIecSsy#?3;4Ob}ia!Q}8P9>(`0x@K5ls%GxHKwWTJFLAj(Ikp#Yh2+y zr63wEV-B~y|M%tFR%@REDURg}p(6e9ulznO+;iKOykZ=*_qtO|*iU1`D+vH+7dTw% zqFmLzipYMby-A0jhb5#1qMc~NdXby8FYG>I^`3lsB`~Lfb>MVESxY=NaGDx%yc)70 zKWIJa@;EJf0&XLzy5TD?@-l z)EO$?cy3hu^XGz^CvVCtB32tW#tA!6LnG;FuEq-+u`j-QxtuFDmHx|A>hpbCMkJJ$ zXU7Cdv-v`$HZqV|CfV{H;}5@&*tX``*w^r*T4O=pXkD+tRHXvI=Mg}H*GhAebcj!N zE!*7yI|o=P0b_J{k{2zkEbXF5XfB)UZddEf=X^U`c!o*r_`WgQQolHrkD}BxdQu7y zSpu(^1xY_?b#2qT#OHSLWT`!>^`l9injlQ$@;K!hZS%poMJ1nWB^#3{~G5eE$I3`sXvb${4r4|>wJ2hF+}!74W@DO?LGm14Bc zsxg4>@=_3a^M7ir_w!cH-u4a=Jfn%v{Hno^Q@IBC2YCCbZ9jDEYxpr8Euh#BIiIa+ z3Ukx_A`8u?Q8OYjNw!fj@9IGBi^v@H>;ky2eO;gOBnzoU*D9m5caMwU8voK3 znVTt-4ZrTx>aC&G$DtLKtz>=R@9!#vl`Qv^0LGdMR31%R2~0p9Q1zW;UX6ttAwPTO zV-j6|utdErEhOo7*pu6)y=;YI`< z4bT`35JF^3w5nZ<{>%RCSz{TVEZUcfFPBKL{Pt0NFMO{1alg51vb(vHrVEGEw}Do( zt=`cy(=uI125xJ;e$c+o5R_HPPl+%gF_vc>Gk;(Z+_-=}>xD)!xadpvm|SsI4$N*8 z3=m5Fj=;>$`iRCgCo;DzMHPcQv-^^FnZdG>9d`wclc@$du1@GN_Wo%=vHHWfR$A?^ z(a%P$X%qJhUqXYwAG1MOyNsyUyoXmrPX;=RgvA&E>zHfUb_Vz-1Q*9AGBz&|9Jq|!h z#w1M_#GQ09g~~;goRpwZ85!%9musDP^`0t3fWU=lTkrI2pN9W*PFzq8T zvoAW7#Pf>7ntNFs)loZEMKqZTD+TELI;tRbDA(wDDvQOfzy^_LlzZmkA8Vh&5(RY3aO*mg{sI~ z+~o2jqUb0~2|R%>k_=Fs@cxLN+?ScjE?cXFOfDjY`K4iE9QmlYz0pkoTAR;~>B0ZJ z@b($=A^5765oJ7k8Zwz%L_HW^-VZ?Va+yHK#FxrbCUqEjN~b@dg7SQQh-aU?R8Ipc zM1{`qmZ|UuBwF~iL-ns)Ij${&lcFhBeTInyG}-NpQLE7hcB~q%xrs0@u#&4L|DPYe z%8}ccUo>Jjx1xxXB|dl}sK-%1JzxFGWm|<4!%F^k z226%Lf_-2&=`J0(mm&YclND}{s6@3;nH#1ukGcZVeUH5I8)D8Y1zcw8&?rtSx!Mt8 z6*kX@(6*WLe~?Tc0Kb)bWrGGRs|CT;BE8M5GL#ay!B_ujQfP&vCP;0b7YlMz2$e86 zpr+#G`tjJ186>vchgMZ61SJ)kCn=S1NRRtxW`Eg6`(LUsaCx%w`pnG~ncKs#nPjrQ z^7-iPPa%IE|Bh;AHL{J$0KE*WTJyR7Pl~3WOHCsEF1YKvlu2%qWl{lA{1NZHf>mL+ zPhU8?+A1wUaYJrH?-nhq*~O-Y$UKecJ+>8&vnZdb^z|rd)H;B_G=5_CR*VdMRYRvZ zp&UrOL^N`6Pga|qEjCsFvc0EX^XHq`Fy(H4>naC3*d@)2d-Cf~g+-5TN_{3Wv&R|X z9w>8*HHlLn`C0tvG&?=A|P3s_tmJ@`b}z+n4&fCf-!uwWYv z?myeno7UZe`Xo1|l|ihH6igC3&Z;^;{7S;~QvGaY>sA+tyRj9h5M=I?r!3$vN7zkn zj0eF(urkL0pdAsoK}xjrvx(MakV1~!j#L@pq%QA|!6MVg4?iN~EAjxolx+Z4{{q1p z#{me(Bx6=RE#hArnct+d-axHS$r(sXqtL*sNG=;+DmpRt58!WoZbM#Fdxp8?ZR!P3 zrWE3(Z)AW}EaF3j7JKH7ZbDAolSts{j)WRv$mv0tDX6$qivZTv_CobLx0i5ZUXc7W*ZzU+DKq@m=wrSQqd#>G z`1w?%lnM%qadU*ba+VVjLiLP98tDvn(p!#25nG9zv0B|z)DgYFJ$;kW0!;>>igW}@ z8P;3}f3X!?29~@M;O~0*UPUQ>&gLKBZe8N35>p*urs|DnzSJopAO32RH^FcJ&S; zpoAf0WlL<8@%?hq8n3Kp=g!+{E2=346jaytyFuK=kc#|z$d42tn9v&ECYTcSQvFfJ z5b1*AuS)~kUz;e^dfjdjKxd6eiXrv)--BU?^jS;PUJpl{F0wGIuONx6)#rS%$c;h; z-QkDREV^5TVWtiuSE22f@jei(_nJsbQHM;`#)4?c z9UsNsbwNp(sw!}8d$d>^1;(l*H;uaW)p5h;---)LNAmtCeppMcE5ouQ+^#d%yyjdCCn(glB76*I?6=x3#?0`p;3xrlYh{|M>oDjNRqW)L-3~S|ZJ2YPD zA_@Zos6gUw6edp~Unm58epLW9i5thdbu@N>l-0~gfM?H{z)2C;XaBuE;!b+x$;&d= zAZi~{gfi-|kf}7InMz4m6oQUK;?tX^NBrBLb7=u9+b{ zLcJZ<+qdn^f{H{5og|V%I9vtJrt|5Zd|);%PFSp+GnO9EHO;?i)IoXdsr|4*@Xyh4 zQbnZv#`Mz|xNukASY5NX?PHPNYJX?Eh43zWGke*MGED8& z$x35I0L%P>c|eYFi?4recMja3?zM94wUdzugRRXry)Zz;c$u1xF(2(_PrCnH8U3|S zUXkQ^!ERl*%OCZUCT}Lu%%BOUt%(xQWTenTNB|<_-YaHs`)uyPS#3X&M=9s;J$3)w z>LN0eRU4=C_zS&pLYLs4>IeXYXi>rowXdPR+MoCna`@R_-*LH%e+u7_bbm{a!|rv( zOY=EkU(WFcE;VVdeZi8wx6q3Rx{mKy$v?ZZW9@7`G790bP}TKy5Is4F-ms|ksgrkb z{NV+n*_* zc~%_Y**;G_{!~7!Bn++Yh$g5}$yk{@^$U0Z3mbAV;h<2&jaJc3y*Y^drEg6?0b1wR&Bo8D)PoC>_^9lmQPey@~L(k0qB@qd5{piHpEZ zuhiL?m;FcAebaYdoV@SILn1x*_{tlV`_vUg+%d6<%+}m_=?&AO2#FtOs9=d5L*A(! zQ8oQrrKJ@**IKW$jT3bo|GT^5aAoI+%7eTF{a-f=M}F{xl!;40E}2P_>fTOdS~xwK z#Gi}=k2;aIKhBKJz&W*IX}TivFD`4D@opSCirIlK?w0<}mV83!;O2wbPO|i7AwIu! z@9EUghT;(_H9V+X%ybhp&I!5xqf9e8DN9yO0x1Dra z*Ac^;OcHQHIn`blM9c zw~HKl3prt;b~1ZH2jCx2#5$A)Y z2bcV;-G)y@)|c8@V=iZIZ;IEyuq`ehM1tz>;;~2%p$Lk_f%^(aiD3whK)@E^a)?n2 zPx0{Qzt609*}|vOmv1sgZ1Oj4rJtO<>-}+gK z>}h%zbKIdfHkLL?GteaKighc^*DIFKemS?fDu^DY-K7Ma)rwD|dKwk=f4a51!uvFg z0sB_3?Is9rg>t-c>-+p~e8cz+KkFjTz0)$TU`Yuk^pubB#uK6c*|7K2rkoVB4qycRBs_a&W036CcFQqh(U^zW$q_d5FUL= z`f+uAN}ypLh)ZrRkk$PVR_b#Na++xLUf|pJC6oKsJO1xF8dvWSTcArJa<7;wxB6WK zzQa^hx~0v#Bbux)zRajR1fQ{dlzoaN58G=G^GaDO3&d$%dWm+;#WZXklc z%eiS8WY<41s^RuH z*^S`tHY0`eCm%Yz^LTVii0naqcZbbz+G?oJ_ru@0c3S>ySO_xa=u%FeNnoLi(jEDO z!8(N+75z6W0J#Z*F!1)tGqq6{Eu=IWX0Lj0rwA_CFv{W=m8EKih)k*!5;@l+%R9ye zY50%0(~oFFp|_IFtL}VDz2N~+oVicD;@C*9R`&@a#qte?HA$+84QF(1ba?{RGmpQV z%p3gCkyXAa#eb@C+vmYcz<~`cbgUMsWAkrQBp$qwf>1LXFTCqpD$FAo&Pj`3DXf$q6k^LNoIa62=1B+j0(< zw6b8C98{nl63hArnA7pvO)a{9tMaDNj%V4(PH2Zx>e*-SR=8w`*H(3r$s3KirWKzn z?pGlueNLZC$}&%Kst%9n%utJvCv?@TCp?O4xYTGFJQ=J8L`d=ZeT=-+R^e79=DH(S z(pdMbe8{z9!?E6ABhemAuhtCI9&5D8oo~6LzjwpU4Zl0(60!*{y0+W6<$WgBj zhNmt4ltqpiECm}}Hx~7yS1lwGW+E_fp;*g7`D-tG`_5)*LMRkx3_1jkqo$1^_Y=ya zNwVZNB_Y}?!32G3!c8M#+Zp8z!I6L?+|gY9;?m+1t|v+(O@X)oG^t`;*h#(stBysh zdt?e29&L0`GE{3gHWg4Na{)5H=7!(l4TA=GY(5Ug;4QMdWiOR^zk4C|=iTik_|&C*M+nbr|sZPt^tOVvQZDkpK5hTpb&c!gXTnJ(?r99vn3D65ZY zBR-8k{kjD1P3Ei2L?&1Whn)L1ohNnUh44*Li?t4qg})pu)8cpMuNz^`YJ)mPXp{=z zduQF#d*=`KNM(m|hjp}kcO@!#iStGSqGLxdmO?zD23%-fI?T&s(=`wHj!sHQbOxG7 zu`(nGlAqq1Rh00&b>4(i9uZR_XVz&fTJf9U7s)?e0OQtNaF2R)lg6HeOI9&Tf(}^1 z1SArJIZ>rwMdE75;HMCxPZ&aXq8;zJWUz|}KXJ{mCy)Jo!7t>=V6#dBPFa>O_5k8L z%de$E1VbdtdS$_0GDYTN6eJL;ynaTv=0CkrO(oa3NWmVjNDCd+8 z4+2z%a4U(9u!JWK#rVA{EI6pCnl*t9xCOQWIH-fu*`F zJ>;Nss7pKdt`=t@tMJ3I$l*?Gnm)*><84OzHob$)lM4IyGCjOkYgk4JQ8O+K>Cox` zivzoAH~|nSayUZpP{p{!j5IYin-a@LJ{TieXu47}(2N8%uo^VSg%d<1{VwIO%3M?9 z*%krxER8p+>w1dEnjF`uPVG(jObRoi4a09UNzKA^3%&76b~Lj!d1P^WBf20E?%umi z**xu|ka&EY0kh=`<3}bLkw|f*Bz3w~oqJRYr41-12rJS8!I%kBn-Gkv;F(>Tmzs&o zd1?N`;=4K7DZvCl!p+*!*HhkY;5Z0^K8-raUn+ymCPB}TcmV>7Wp>fh!@Vs z$xLQqK=PhrY0Avv=}?oLVF{=8>+g}YIZ;h05s(A`h+Jnl!^91QSti{QGWrj|bXuhgSkon!b%S{eIk0aF#1H@ zJZ8L8b(mIjMFxie47@_2nYrf)h@ki;{vgCwZd)0Ae<0LLy>$??1O1raB9Z;P>}wT4 zqLygaI|?_kFLqGc3OZMKe&q6dF+3={b{#Gpb-PhQfC2I=(2|v1;~ffAFq_5C56ZGd zDr2*vuslku#n}g&Tn)#IGzOC`lN)WRY??k?Yay>4Fr9SdGI23`N~dzr;vb;L`7Y{G zyEMC9fkdHFa|~(*+w>bQ?drgULdcDW5C9Je*sQRUPk-&%!sLfzL^8sS^!af-Q;9U^ zNO35=A>|CrBf;?MBXT1$VpMnVZ8Z=u380d0XWM z;Datu65R>Xknv@i5{xFGd+G$cc#eJZGgbn3GI@_1>H0;1Qk8o7kWq|EJ_=DDH!eXl zN{hOai^7k?9te7W(m3U~5wUCM1SDuqW&t7>H)0?rRIlm3nD>0?Z6J6$gr-t%fvWAu1s8j8~9O_5rG;-h&vMUFN z*jqT0zTjmN>Uy=3`bjQvHrUQ-OGg<^VbU&^T~>KoF@HY2O64)OBam#7IL|V8QFXV2 zTFayqS}#Rl5i~3~y!ChOQ|PyiEZ3CwU`X&ch!a5;M|qI<4e=)(7+-r_&dmcV27nRt z9WLDvU%)jr4wXfVD%(V80vYWuhP_CSBxgTiR1t5snt@Lc3> zBXR4}gPzmBN#_+^snxc&?Btni9fbWa^Hue)VF$uFPvv3L_%%{obeiQPti1_|u$zo3tuoZ`Ns`VI)#s(&W?W*3dFZ*^L%4}6 zP+NRTd5JhD!H_)uhj_?DUv#i&3~Q8DEPGA?VpSpy?-A9gwu!N!YiRyfHZS3K1kCNq z|Fqw>J4TcaMkg{~@|zkA&78wcs94PR1${nuK?^2o$H2Mnp;X>W#BK^F18_p5Tjk$p z0LOOK1P}J4nHj+oW0Y5FpQ=(~6gbwwQ!%{@cFij$jt7rL{{zssqz1@qvblXVB~=DS z2?h|NZYh?Jlq9SEOKR6lsk$ccHm8jA2CQMURRGi zQr3h{pkU=xYbje48>@z(U7P+0RDieP>=Gs>nzuEx04Hv~lUcIsDMU90AefMXK$&9( zd<734r`AT}BPJZJ-eN{dIPDM(J3YcwrK$5DBL*%htwq^GwOTJwZRBsbNjBV&UWp;7 zY{)t%jdKP5-v5GZau)lqa+vYUm>{Ahj`@stuGuI^LTaQtBCrsWZ{*q$ZI0*z_Y0bX z7ttgNILU4uticy~{~^po!}szW52YsM;Sr9j{-Q2RVNd8Y3&}Uj`!$3>uTmk?9v8Jt z95CgLQ;8=IY7bcCz}`y$=fn!dKVt74Jdkj%1}A1RVcS7(Cf8*z3L%I`CZ==a&ZAA# z4s4-gnpSD=Ux^jZP>{AnePmpf*WkQg+A|%Vlnjht)GC111W8MK^K*`8+OrG_c^sQY z05T@=ZsP;)@#W1oVnZ2X_P?0+?_x5-KJ^Y)0L-Vvk6Or82IFvz&7bwFLH6ChhfWkg z=I2DpMy4t6Upjki-9Ja;Fh8aWwMkA&hP^b89(2DR=o?|rFcTV+er#7TD>Z{F_ElI5Fm;?5-At&8a?@6RGOLqL#Dr=* z>)Do1YPs^!WXhmYWU2(%Xg^@XU~R}c#!uRP+wkQcDuYT3Ikn#K=Gjl=g&Vm+F$?WnIcrIk zST;de2OWmBjnvtsJKgvBrUE(M{Ol5I*D*etDiHzsE)RmM2=v7@Qd8_O1#b6j=Ft zOXd9G>)yEK2sXXnGEOF5&lpml`Z=WBHMsl*b6g>4EwoW;{trO@YFlzH8vaKZ{``8d zTEB828v8SGr16*JUJ6Wkap&rc+kIVY*SYy9ZtA)2gsL)3KZOEl$U=2=+h zE!w6$_S6zB6;PcgDgS9#(o2=nf>k<$C*iG__6hi_g7d1=-5s$eN;|x75=(Nf?1nSA zlw?2v^4YUz{{W90j@>yTTu$m48>IB$Z;r7T#`>lVaQ8{wq`PtiQ`l*EzIL&IXy|Dxd^U~P;VD~Bd!}<7<1eA7cPLdl(-R7E_$gf*e?8))- zt$Jm}Y%%nL-fI^!hZ-Qq%0(m)>s!GQm<+y($dof6^a1OB)7buE$xR|Sf|_}R9sIcW z+~VhAc1b0Kq)g zrA{!l!h5C<%O*M8uMKpl9(pOCW?f?HyD>>1MXH8Ol=|K?L9nJB7%O+dMyy&*0zN%I z_lT4p8iH0FQUni8Pbn1xk9@5PHu`h&e2m%Rc>evucaruisnESZZKOsZCbhaLQ$F=m zq(Iwpsnq3IFsl+tZJ?@-_S>mc-RFNLW1V`s2!?LzS{&4`4> z&AZ7dcvkZ)hZH_%W~?@DIvp&4{Pj8TEk7dB+>Hu^pb>0G zbHY;IyZhFBGx(I+@OV}GFFuFIiY|54`)h{8*ZPB7Ynw#Vo~=}>HqZ8xveWJ2h@#v9 z77`>F(<}Ndy-T>QK~+xS7t5El{{G6CO9=7Sm`bcyF*(*M7oEi9@DNQ4J{LlynX58K-G^$xYio=Su_=)I`?IwdGtg&qrGZ zr9lcXc7x3NZzt^q7C#*x{EEq3=REo$Qk!nbe||qC#s?hdW5?0QQCdE;MjNw}vF#Q}o)oKj`Z*)wpbuwsplG3Ash#7e^^Iz=Yy0ohhUBEp^#qJ1P1B+iG? zywCPuJ7)Iu>qyXl793SdL)87=H}TM+~Bk+$D%^* z?jZB(^@E?b#(!uXNq4%gJSIfM(F8exS*ZjH6I-~ShhDs*)8I&9JN8ylOCP$^+eOFK z_B)T5`kl70O;!)dD)$sK$|<7T2Es-*PYwz(*;XRHb}c`d`;-n7kxjjMH| zljAT%aT*e%jg&HyMTH_Vd;E2dred1&cKj78%0R@a=ZzM-O zZpy4(G@~o`{q^?teE&^+M`PI#M1yJ7d5U4hwf%?%Qlz`1%Cjgt7gbV+Eb((X+~@VW z&=h=ZGWJ+T{aox#XS2)kAxAvkk0ofEzU94Rj0VJWMcLA$6(#pYpDDVQ2NZ8Iw+Ivq zQHbO8&0_4dyVs50L=W(+N$I84rZoPlyj49e$e@Ee1;REaW#O1!Po18mEn)&r1f?IT z)o|Q<)5zs3C(hZzY=tcjGOlJ!UUqG zP7b&f5x*%3L0PR^c1w^*9%nRsYr|pr3~A|laS`KC^& z-OIs!C65pZs(6^tm?IxoihWQvmf^)FdBEDFeO=#94RY-uXU zy>2MR(Jf(&^_aun&^_1RlT>&|vS(j+&8H-MSNiTmSFNz<&zU_$9*docU|2A8i181L zt8!uAewKc}nsIll?&}?H<>tl_8Mo7-DS;nwEl@`Y08MO_rzgPpm&ih_`2MQU&b3MP zVFF2kmro^_h9hapDnjbTU`a1hcm0AUGa71oQ{f(_UE&7tuv1XG-FY$XzI=0s}eq-p69+Q$>c71byt)ZE< zW9d^!J4c4b+6n^%Y8P)7Cmok-7;*Dt*Kr6MQ)Kf9eYj=Hp7bEYG7HrI0#_@K@v+>s z`0&cbr)kSyoKs)bov~p#WMR1tGo85zTp2h~9;PiVt<&D;z~cU_MyOqqQtRiN$Hv#M zWZBDPQ?^}{61s?TIS)O#OhWU1xz{x#8eXP=0?G@suO{diuk{cU0=JNWyo?LRX{}Bn z5YZe3bNvZpp7@rI6|%F{dHcOhsUUQ>y*bf{9%T5q6LFS~WRg_Y=`hnW0f6vd4~09{cH8&9HS2s~Z@jGe-QA=h>pTzrSy|YN zGp$q8p8Ra!-vXQ?*!Dt*_O%*V6b4#|wS4xjvqtpC=6dU7)_WmRvIC1sOm&a^CTsS? z>I&b9wRv~H-{(!&E5@G-(;iihy;wHg8SvI>7}lyo&8%w~9Ivb}$|wYvwR6jKssL@di@ID+XO1wDdy`X1{m& zh}_0_R*oLbX2ZUzCYz9|Hs5+~y1gp3T2+}z28Z5W6LT1i*lU_wSXcGF^XuLPw@ZB8 zvKTA(ZdSw*i*X#Qy{m8vh$5;ufj|F6CE6@!g|wzzecn7Kw1;#B1j`=r-r!BqVb1=- ze6AfM2m7NRb7#M$b!jFz`|dg0E`5NHw+gveT{q3DR40Rw7lHG?t_hNU4z$LlsRW@0 z>OnVUUiEH4(Hp-rs+_JPzU>wqD6Cbmy>m9gmcSo6oV6E`WoV~TBg0&76vq%Q60t@P zvy|RmQcn!XcXLxfjXL({WY}kE8}vtEp(6G?i9uGdi#n*<^i90#` zu&RQ>;idbknhE=}EizvQtiOh;>qaiGt;rdjc!giJ_Z^sI_ja^YZ&p@H)CHJ_T+Lga zGhx>mbKO25#C^N+{~rL6UMs^x)gFAR^Z!}366Q~xHn^pqXJcVYmbc85`Z4YS`ZnPQ zw7rRiAJ)wME}r*2ZvR)aY+x_y%Yz%2`P3RX0V8i5bbVhmGrV+4UR91gn%qav%Y3|c zU(_B_ScSUv4{+`uz#=JzhZacKC;!CSGntLK08cfuyQ`KjV&Db_hXpn;FFo5c?diQx z&>ic_BWCT`U!fizXQVF>L_Bbng)L)502TLZ37p_p)R)P4N17_F17zXIdC*3oXc>I59lCgEK{`Ze5nf@d=pyczT7lINl zvW`rT3VS*Td;Qg{-ieV@zig9__MiIFnxNTx>v#%&QhpfXHlFzhQsu|J&j6@>`4eZ} z;PSDgR^wFPgb};7@u+>~+I2P)D*z)jf`nA}ivtOuC^~2>@b+s>oca4e(3%Cq%xvi7 zr1BBOeygZ8|81=-X-+FXiR`E`F_&Qq&}UlLu`}Kf^&4>bcx8K7MmU{e&a6(hjY_UZ zYF;inV0!f8n=*VpnNAy|$Cla^^M{@Lc{xTPeO;3DQs9fS z)c7A`1(&nxjG`Y2Xt(TiiUd82lr}2CRj2X^P`vWGDpCzbj(lcWA_DDHks7&g)7zNWGgKi7KeN2eAWW6rRhes6qewhq#$ zh_GkV-cT*41f$JFKLrmi;j*WyWy=KLB^s)?l$^C87^i-Inhqqm;a@8tm#5eq!f!`^ ziw}ptFMOJyJrL3J$xMC??}uik6Sj=soBab!ezf`F@^>u%<++4=qJ41ovH=RCp;lk7 zF%OMB`L0_>z0toJ_5%a-kdm7hhS^fLjm+-;-B z-+4p1E>Uz5*%5hxuVpXf>TJbAf@g^O@`Zuj+tMIjIKBq~%#| z1;xdwyrcfS@pKndjFY%@R z(QS2o5H}9s7ey;0;Fa}ZRUwvtck}}88k;9k#uV`3Q8%CA!O3a#xG;pr{EkBCiiygy z&b{N7Po7iDb{ktfF7R)Xt^GHR-W; zhMJC(Qkwc+$Gm?6zbUA!tNG1^-xpNM*&~RhRwGgCtXetBa)flS)GJ)e^dE7^f~>>s zo9dB8u9w;3e-l3}8jPz_vTtRd+%QWhkCHH&Q&f)H0|}vx??1itJGAgi6sNlPTCyL? zUj6y1Q6MQ6#w!f<-PbMV?`Ok&W_K5N{{jB&p4}7YVk<+xU5k=Fr6hN>h`z&@{l*%| zK0Lm1^gL?G?%YkW2L+aPM(ut-gESB%8JJ6!u!Pk&g_B~hd?qiY2+CEAf5xA#bS3ha z{84X;YwuF-7c8yuw0n0puHz5w@X}|`u&>&8Dhl(;u|nMH;y!a$$gxZN|A(vhjAyfr z`?!;c5i?@d4r0WrO|>*pBZ(ccN6ofYjMAd5+BK?;NR+CbAXaf%b!m&*I%8B(N=u8X zU8>sa$$dZ1{pNXjzB)gj^ZcCWaUB2O_-5f9vKD|a^HSw!n%@ReRl-B7H8jkPMAjAN z+N<{NnO5KBXn9r^^!Xq6R!W@Obaw@gG|o=@c0~}W$pc=ORNgiCcI%u(M~x5G+06NC z`h|4!eo6%Vfb;3$77-@w=J~37PA%3?gH%1;aj#J;W)xsK9G%~Hpzm!P$Dlcl5W9;k z)YDXjsxe}O-bvm+i6_?KnU3x3r-DNY>srE%mkY5zNll|Bd{x1cr=%nLoGA4T^;e~j zy0c@1j#9Dd2B+J&x*XXS5_+|>_pF_)TvkpGYgCFpw6cG|U0CvZ?^o2IQI-+D`2lh+ zs#~iHpGJ~WVZ$o;n+F?8st(t11A$6@v2UlkHo^#653pD?aw3`xwz|;8ilV%PT4g^_ zn{;lR>Sp@+_+#Wb8Z+DP|2^F&tr*a*f~1-C`3m2udxvxc0|LdB5P6aZKQ28qp5@HT z4)q|Ev*oIcBzKgyf`6Pg$XWv0fmtjC^%Rnbp?#p&9q0sCu%+b;vr<0 zQRnV*N75sPsv@p^R(0oA$LxuUG}Ft6%#E+bB|-7({4Z+Ug!=LAX%33ZFBLn6RxAxiPYeHODlWb9R&>$z{+MzXZc*rkER@GiH>3D@OuZv~ZGLod>5L#DZ@{*~C|u4nIhwv~Ct z4~(uUTidK(@VCyTX&R98YOywbLb~rmBO@W#X8Akp6lw0r9bUnQy!^=p*$fg(!QH2?MP;g-W24Xk9|aFz)hVwm@>T4X~T8XxZeqJ zy0g|ZolU7ZN2XD)C=EbB;OYo=(6U3tI3J^?A$Iy^$4v@CbRMaI#t@o@Ct_b00d@Vk z4y1LhDpYN)D0RO13_4r+05Hf604-dkAL0+J^}aD`l}ZX2slMHnQ|)HS+<6cvA^aIb zX}kPEN~s1l)CvDW)tVhe=XAuuDkNNMuS-}K=k2a3M02uFiR`b3>CH-Fe90+-^eguU z;YqlZJJs*)K0ADJzHsEobHTr$V!*ClT?Ap1qR;l6VF@N*KE4m>PIbV~7Qi|QNvP)c zw+oA&-c2a46};8Oe<|sKR`&;^Pcnm1v(71Q zg~nO(=7Kkhhkob%@C|$rY2o+sjsmp0(!LSP<6%YV6oz570ZEAtXr1uTD@C6dS1}%` zSGbUTryXJ&u}*>i6G(@fdnNav*>fr*@Glw>r(Yhh1F*Va8=HB7wh_m?*7uaYIBNt*!l#h&fBkReyf``jT zMYQmsrU;@@Bc;3up3iZb#@Cj@1)y!;tByT>cxP?!48&|KR;$rGcR#QDF1t|h4`UJC znEP60CAINZXoxlc$umlC3ZyKbHquN7s&90#ENGZel8KGba(_3NR>7GR7DHcf3J2R zp&hF&=xb0asqpVVfkF}Ut`}D|l*gv2U=-4k7^}oIPw{sFTmI(fc<%BbHK<*MRy{sZ zD=4bF8KvAbx9y6XnXci|tO%KWko{>O_pVRO%K>v>Es(pQIP90TI+9Lj^8?+!`h2m5 zPr$%3G$*}#W4X(zbvbVXmp_j*mlPDs2{S0UH=WAyA-I+`Y36dUIdyjNaANZ%10(Si zo4k&ngztY3hzM0FIr{VwvxlduP^o}fg1k8iD+r!Rj64m0`gnSDrRnPRjmIkzitIR? zFa@jh4otqmzeuc_yX%ed)4_F8Poqm%?)Q%3{{VTP@$2|P97eF3i7t};dj9HgW8?c0 zjE0Cod&(yO4xQWiZ1KUWWeV;SN`iQ|2v2!aVrQ)>r!dKC3Xl1DXyb9XDaBV+8})Ne zga?Qr{2KZ)v{2tm*0gdu+`99RE#BHr7|D&9s6|a^U1nK`=d@Y3wDZ&Z-eql{3oj30 zo4-j%K`)&RpS$DnTV5Psz!F>VFqv>GCK_$_)NGsUdDKDYjmVpW_(haig(fGO3!tba zG;5n})~9HUO1h<8lgj~{Yq)c>Kg|%Nx6Xy_sI>hQyeT@LkZrEOH=fS(OPBwELKS9e*@;nQLI8MzAr-LA{A|4m2tmyiD z6!unYnilkqFnwLk>aioqwx2CUlgMy%kh*?Bz=&2bo^?lH8LSRMpRJF{l27QT0H+?i zdSom2w8=U010Kuq%3Bt48Dmm`n8Hr87?Q&SNEip(K_u!$>}v57DUu{MN7TwQ=TODE?)G zEOwI?{iA_%j-OYqe!ooIdBkh-BD+oaZJQhW#hH7OpOsHD2LBy!LbcOY>W!n);>D-aZnalEC$zt{W{{ zfZxVOvL;Wgicyv>s~j$a1skX~WK_q6G@E`;cTO)@BLbZ%-BlV(_&%-G?J?w5Pmhw* zY$5c<^VqyZg3&?-a~aBdNABBSO9*>Ua>!oYb&P9U&hLUczWKNxMkS@Y9hB`aRbSuc zQWm%(%Iri#p}T2CdRlP5wEfAy&-@+A*O(=!h2v{&KS69YN&+*g}?{75*fwNVj?1#mIB=6B%D-_b)PiCTY#hq#2CjtCoV zQ_dCv)WOUWho|B&JId*l+sLmAX2WweNn=+{Z4xq}*T5aiqdIoG0?(Z<#LEu;mA%t$ zzWht5u!nLb_}I&iWi&FkF;0Dly1m;Qdrm``pH3lPw=W`FpAwyG>i)Mf*drLHH+tc; zNd1@@DP~=U!L|Z=d7P97wX5>Sbtil=UGa8$RW8@+kmrDQu9bI~$#k3dGdD#}gLq;Q>DQ~o$M zYzi#-cHxNb6R@~K&h*&JPUmWJd9nN1)7^ErsBC6qEN}Yb*H;8eo?5M~zptHfYci06 z`XBwZ?r+|?|JiL?q+ok`Li1_%bf=v@R~e3WN*czSAD7y%EUT8cHR{sA>SkN#b<&OdXh!JjIAt(?<|FuZn zQt*XjjF`3r0E89%_o5M}g_4x^k3U1BIkl_A^JStPXCV2~uec{2bXS9e$9Vs-?rAVy zeV}^3{6#n-U3gSsWoc|>SRt=K`?7A+t>KTW_PQIG@b61GPV^2Hl7h3Tv|Xi>&0CHy ze3rKYu^`*$m>1#eVTuLCS1L4W-b#kU*MqgVm`PPga0)ufOb@CSFd0MVQVbC=&?~oU zvfKtB-Jdjv0V?RKZe?nZ9)D{-PPi@76i>l?=NO}_PKG(OfTqo5Mge4?F74F0D2M4; zY6Y#)80XT8Z?DGW#Tmj+J$-&C!zax2<0n%Qx(pm2<9r&m+BAs9kms_Q9|61~2@aIs z$CIC`5JICkvQw3RH6_uQVg(wWg#AAp{Que?OPmX&jp@`-X(p zZp3fwul?Gof88-25;Y7R4~I9t_ilwH$HMA?!B6HAPk=8G-)Imv&pOU5f66I+zH4gP zQxi)^E@HUPNB87hz81`B$Qe^u6FLauca)HhJ7b5Zc>8bg=eGNXV-0i{dUPmqIXT!) z*^3I6A~woSQjW=E&L0D@@(#Zq=saD{*LzNF4(SX&XBu=am~Zu*N6{ON_+eP!{a@Ey z6#m+_gV!C-XI}eY9F(r8sL!q-Ou>YZOUWgCOy-1s1z%epO%CqH%0O$#K7cJ}>UlMs zzoPxXHZH(8>Kx00)*K;xDVJ){`k)Nw)M)#>!Kjg@dU!m*1q!$yIe)E^p6>G8=A({e ztc8XV)}7^TJG*%mKxWS(J+f27Vz<8|47!I&6|0lbFth~fX-CR+9fQ2#nD>)CgBBvX zgg#`jbrj720D%SOoAwiLDO}YxRY{&d*mf6%3C8MtD;o}J@wgMip)DfhP$NvEb77jF zebm%21i@tk;tGJdpRq2d9wsDkdV~wK&gOGgPAC2oU-lfLr#FJqbX4}t;f}YUl({Qm z=MN5(9yeT1EXdRt#*?NCzOTQgg0m1~_BGKqw;*S?f2J^vHkfUU0Q&lGr%(t$FOzA02=-t4d7_(u|ig`P~a+K*JRHCkW0N zzsjK8DTyjQ!dwI*x@7b>|A~r7+WB;(F4i9ZfJC9TEtFS*fXGajIV;Ec$U~#l(Af80 zT~M4tU)0TsTpcL~OKGzg4=FUZ{{T1vHxBUTrk}r&`@g6Cu$Rgi5v|MZU34mip{ip8 z^W`Fka|a54JgLI*~?AS3%-cBShQHU28)pLZO59OZHT zg)GGE*X=iV|AL%Z&2-Ncv{xnwAiHk$v$kqr8Rnh1)*rgMOdfS7i*lYCN9q?eo(jnCJk*ApO z*g;6tbJ0@uoer|_3-M<;5bJW3eeZ<92b8ubUM%8v?XtI!?rRYr&D}9ieq>}wkvne6 z+m-=77IJ`!hQrKqvxM6E*t!&PL5KAmDk=_iydE%kXfnOY>ZRh@91raNkFYr1ihh-{ z=t{c8> zYS}zK?c@#;A%9P3EsF!FDoN%t!0Dg1VLzpKMNFbv8IP8CDX|1y%VSq%*C&hqL*7Lx zT5AS{8L%cFVaSA({^ ziJ>8L88!FwX;M)nq59=><<+F-f@y+qRV{!4P7Pf++_>~4hOVba$5KutSs}G|f42V4 zyF@7G8~wI@pva?6zvAY)oastSJ@4ikzG~F^9X!acqA;8aNAwQUwf%7s?L|R;pX^ExXcb z(l}^4tI}BzJlLl@Z9e9)g`jOgX5TX&4 zf+Oee=Hz6_U-I?2mV=SJtHSHiMboel$-$Fp#eC)iCr|lC7tR9v@FEp*s@!QK>{w14 zqIn@$I6N@-2YBbzhsdc@>9iRJJinFf6wrQuk{>)#Z)?px=|ulRq7CHyP+BbccN>34 zm>XZF1>$0o2#g+J?e8A}p*hkDNH6x)0%3T!f3s`SkVSbFRuAuywYC2_Bnt_Kp4f zEz@c(NnxE`=AAo``^Sd4r6r%|7Jpywi2p|kR(`(s*&ZM!e{JLa9%thv_>V>u@crE0 zpM*D$%SvP2?C3C7JmstFjMqNQFi&yq>5}z(Y1!=@G{2Iwa}AT+XypjU{0bM#ebyr% zTaNS#dcwiqXrLWg%VFpmDsrt<_;r^U1)x(I#bP9&$hS2{C3{UY`qiJ&ALxfbU{#H5 z*t6Avr;G#bCpP-^pSXI#?#_8&u~^kMad4sb^xqdzE$7X*vX^DH>C1|WIMd7Wy2j*I zr)aJBEM+rG30wFyS+cNgUtL7KNY0${()JVi`u^bSchkOA1Hb+Q{3LwsC!)U)P2rW7 zS*M}Xv>V0yi>XfcPs093pg|W6SS5B4E>lHE$4$X94JUpXd%*P&#J|xCJvy}pB+QRl z6nLPo4bsBi@6Ig1y`Tvz(!J__N<8i$3!qScE8`kII@Dq`hx`yQzrJ9*#|4eziyx+?8vm-{l)G9kBDElTviRA1UR3oc35&?rUHkTeVFl)8)B9@2{@Yx<7*5^=DMRoZ<@3Ct|~9ftjs)uX-3q^@?r9&kLhhI84Z zFuGbylx}66GySfi#!`S9fgYTTO8tTJ~K~V?6Vpu37FL+D&zV_JhiDpv?--OYD zaUcOulpiPblk?BDPfnU0gNoYxzg8$U?t3hR-2T08+X1ir`MhC@(K;pMp@qan&(D9o z8s#hiN`^u(|3i@Zj6F|O-D;^y-}<@-&Skx>f});WF(6J2H|hL)I1}|qsnkxvtD3Sy zx1ucjwezr$G^9(b{hF4wIis&nc`b#TjxP18I@Z$B!oy)_f6sGmihVP*Fk1Z7y6{7m7tV9%@et1-1s&jUY;UH z5<$jCcKI zXI53e$~5e{@v)$%ehH_*N%=kbH(r0TShg5-(Y@dF6cf&>sCP6Lx9O7}D6X~p#FOZy zqVq={dHZV~-p3msQ>*bx!KF5J4Us}`kMd&~VSWN{!GcB^-qFDg-qN>ho%qv{y^>+- zD#w9&=TDj!pPh(LR(4nX3}F!?vc+i`kmT)A z;6ieA&9^zAx)ZV&tLBYMcJ&n!1Q~AJfTGTU+=CI|bU4zXOdFN|M6lBiu~RE>kPa(Y zhi+d9-h;MqUNxPl_Mh`GJp0d1u$ogYy|o6_8fKQ;ew}h$p0j`wm0zf+sp7uC|Ib%t zY$O}4+Hk+xJjE#*n?*900sQOfQBd-BmI|iG^Nvl#%dhJY^5^qrdSCT*2c|l=JJd#1 ztf-AS)$W6<;=ANIr;jh;&0igHGc6Q?oi{e|=};P7sBT;8U=+X2#G)gf{0#Kl$qA3d*(T^{Y}HeucM| zgAk`32{}OU4Q!9BqixowA#&MkH#!|So2HVK;XrP!QUX8?QSG@M&)Fyaj3v<*jz0z~ zJB=ijgUts7qrtBzoZ(87Mz37h9A3FdbWGsq?7n$W$YvDf0Bs$vBrr+-r{h(BL8*-^ zhN#qg2A?;d+OHH8z)hcH;Z#goYB#p0xU}PJcg9Dc&95uQC~oxGwCWwDT}j<(I1wSE zyraJ;ltp;voyd4atUbfRx4_BRN&jB^dD7(&M8|!h2#3zbw!kS59EI}jQula`yr zxhCz+G7nYom9t#t1E~tNN$S4gT9+H{@p)LM(2+U?|3ezlS63-np4bZ`-m2zl6+^_{ z8yQNUl3Sgrsj+P5<5msu$UK{t3ILD|}?$Kc~_=_sxMSH(F$UudN>IE5~`pV?frI5W5QK>(gYf;MtHXyw84q%Y6>2e?wS zq-N!@q0yl#?qEepmoLwQ>I2U&u_Fziz)vZe^HmK5m&O^|J@B&=T(26KYCa2Wz~zLfnmN}>TLgwcFw#}=6FrGHPj%kaDyVLGe#g^lN0_x?%lqT9 zMQkxK2R{>Vp&KB*9Gx}u4{@yOa zz`<^w#R2cVuNnq#WdYK?>iQIMh~HH(vG7V8OZV=)^zg4qb3Twi>Vz$F_jX7UEm zyn0G?mt0M5>msxrz=dw5nb%!AA1ajLK%+DfX1P&Vn)P{^7V0dsp=p znhsSI4QCfVsWvVBT)s9vFK~lBkzDq2k3_40-`BXF>j`A;Mbx}cvcnr4lPF3`ODbWb zV$E~AKUs~fPWH}Da2;>_Te=s^xw~*$P>I*`*0Fe!=#NJ~y{Cbv{=szwuYKfJi<&A| z8Np)C0^7)ZTb*yAF$Mh&X^lQ1Bs8}h>!*Ex(MKrvvCu7~1S&Y`W~;dpdy^fj?VTs$i_?pXaDXzzc{k(H9#?JCh6O0isLgF-0mJ@fhuHT?D9MO8{}RdsANBV=9mo!#f-XQvRL za$2p;wWKO;B}I7*3CrsG3%(N~Ze|9ArVWvk;qY=O#P!Jb5yIvSgUb*#>YH2I6r?!7 zAa=B{WB3O^PPBzJEd#A0)5bo;Wicl?+m`*HxE5a ztnhmdnQ*t9y9oQuP&CHBS>{v5h227}{~~H^hVp$J+@79|I*w#A4~*yH`BWC&bnCX| z*((KFlAtwulHVyl9L-t)_J z$V(09=;wxCYDB7pR^zE1_G-_Rp{$wQ>0F$N#Q%X~Co`ri zi;FxcT!N;BIv9@unC&Q_wl*?qSPUw{qfrrp*%#yVl>XF95}1i(f`YGLtyJEE8!LGn zsK^jx5~WDAc!>U{PA>__u85MU;*vA=CIXl~GL>)`&eWH)mJAfVmqs-%O8LNL)~Av2 zwj&#cd15ZEWp>{T6stUOwa>a?@O%N3{+Cw~XsoZ2iEeGHi;>0ol9k`XT!RKHlold9 zgt^Tq6!MIwxeBe1Ta3k{%*p!vAP-DOv$v=J8Zq-z1sAe|1#ekmQZUjQeYu1OSXFLv zE8qQt(()pfHh%z()$YG6JbM5vuile5ooq>3~q%bTl{=(6hcgh?0%iCDyuQ{>Q2 zh{nhi$Uv9R)eAY@DJgStC4jY_5Db{)s)$n_2a}6D?oAgs9#!;`i=0BZ*>Ii^I78e2 zW=T2JGbtU=it2fhz*0f`J!B82^VHYU7})ehtn~$EC5v1~O^@w!@{ec!u7p9`Sv<$8 z`cZ7g+nDl33l&7NM76y9J9fD7=suNk0gv~>5@EFn5B2c)w#ALUrxs`M1!Uee{9;fm zT^eG;E=(>l#@m2|5V>%=1KBH7q`%olr&H?Xsrpl3_c0Ne)4Y0iAftYtths#Dzc*mS&r*{-90F6hG?QOThyHec$J@fgmvaXWy zIMLJfeEO~vFT++=`$@?i+ONWYTVt1w5(X*r82rFG8OZcY2>6g=aMV!8=+k*7cQ??U zpF+ij$_&p;*=B!*rsu}&FDpK@7LPJ>{VTLv?GW_nc|q%tN8j!}*S_FLFI~tL{S3dO z1U-YiW*8lY%c82xla#y8g@J#mzdn68Kd#c09zE7mKmO!0s5kOa2cL{ks#eCgZ?jA& z1x^MS+kK5^>CAuNpM_{Q`0M3>F(XNBs_^KbtpU_^OTHo6d^Mz3UK zHgz}b$;(J|*ts=?hKoGv*~i?kDGAp@J;L?ZokCCRA60OocVChah0Ttelj5WcW%nnS zV)tJWrk1i|!_KTIvU@;n#I@KP6dwBnOL=C?`*&s)ovI64Kb~U@Aoo*M#XvUs@QWif8stD&X#Lv48fLrinTha+&Y$3Y}Ipr)3&Fb1BBPCdHgFFr(cZ5 zf?=~^wS~F}{?2c{_l!dHHk!KR-SoV^m3j#YjT|Kkx>^u6>W5@4tIIT0T`c>wOccUb zl^efg{3BiI$#*7w^4Y6U>l2q`+>(p#?hG(0{&|ZWeXuOJZvLzw&GU9fyHaDQT^1HJ zC!`G@$diWetX8|CvKj2~YTZ2|a1 zZK(-Pl0LsO5QZs1l9{kD%g+o^`TCdVM;SZavkxoqT0Qbjj9}|&WH7fo-YNnuY4ua% zj|)P=SEJ{gPG`wv(lY98-W5^OC9oHzv;fFg>an|P8B*0>mpW5$soGKqEvF^j6f68q z;t>36y3ON8&2qN_0DqBU=$daPSP*XZ2mF zU6-E*D|gwyvPQ*^^iBR}V*Dt2g_R2!iW(wPbmS(BONZzGvX+092#Ryls?F$| zn;&*8I(5s=?y-AZus$)HsgE+3^R#JV|EXHi7+=_-ewT-9D5}PFr3%tlugX(ATrf6u zzDD-kGnu+63sUWl^CVbJ=w)2ho8}~pgST0K0s0tNZP1yh-aLJ&c%BjUd|z^%zOfd$ z^y8m2)|!6W>`w?qTvRL%ku?i`DZ`0Z_{4lOJl(wxWA&gAfI11aDKRNdi4Th1l$)KG zyQ=K(isNpzF=<`*_1J?m(`x%#5Xlo8I6*99`d9Kh0=9nVFjWGCv|&Sx~2iOG|6|0dVAS6bvqVhc&Qd$WmdPT!dGhXl=-V1KFcAQ?k*r!MMOvDo`;lTL#c2@fJj_Kyc z0eKC+C( z8b1zv^D8k`FKBFdecJ}5{^|lQd`QZyt?>c5>L@e6d`Z|Md>!<54P)3JiJx2`XbZ-c0KNAbwpBjo~UNd#-l zYG1MSKKtDYca@f2rHizRd9E6_TPkvrGBbkBl+{SfTpo|!rCg~Ul{e*sm#NV9b6H9F z;p)$Kb5T37BWjl=h3mp7bh<3==7vZyR}V647h71%`svo3Jd2u zB?O4iw^(1}(~*zlj-Q^2=4Qflj{a-&v1&lF* zJPvguO!*q2L=;PU_V0gy=f?+2eiXEw>2$#)pvHmw9k};U)U>pO!aKixhBH~A1GO`i zw%qk7Ph(004IqP62Zm?Z;BPv?4=1@K+A3^oQ^H%sBx5iND=@gZ6^GF~1(AV~Np4k& zUpowXBPT}Autk1e5Q5E9fv1oSIlKM{ET;P!VEP>kqq$VUNwoYUKPZB2AC9o=w=GXr zsGYWOvcTc_Mm(?9-fTq~lAL5J4Ss{bwRAMLm3-rG>F+;(PfjsavOAdM7-5uMX_js7 z;Gx!yYoebY31xd;`g&(iDIdv%rai_?`lbsZ+k}+fpBz~=hVqdsv5@r9o#mQxt1vIl6pJsmP4Yhj@1r{i-M;aCGMTMC!wmHqsBtV6TdWV!#gQ;!UfSteq= zn>fP*SDFJdHD>q}=I%A9JUC^WFTyth$;wKV#mt&!#3_)A*qK|#PgtZSYnpJrIhphc z30i;B-U;I6S!|GF{+f2IZB%tWKnPVL>}lVLSXnz-y>9t=X)F)nN^DnDnXc`xn(vs|bO@{4YN=GoQRS7xz0f3_--ef3$?>0fQrbn%Dl(U|g_86O% zFF%n>*fKiX$xNJbIoY z5+C46#Nrzu(Gl_g0Zaw6z3TB>YWcNgj-{QFe~%*#%Ec}|*3<6fP4WxEaX9(gkKrl< z7q2|~(>IwP(7@*#;*=?Y1!0u}ix9)jo#;U|0JPBoT;sR_^?x^=IJeVEplTp(D$Jn{ zdCF7o;6$SBn@(=fMi)K5=08C5BBftj`x~L88+N)Y_2(QBIzVDglCEvuxHfmv*R?u|gjDjtZ~WV3)f_+Huf z#7D5B28Oq6x~w2Sjr5RcaY>qOOs8DyFfd>HmQruAkDnR8gop6DgEgEJ&KP_(3gas2 zI61OEY6{k5*_Y&9k{e%}uX?l2dT6(hRWJYyfO|3xb^C&28#8aO%Q{p|hC(({DzlAa z0;DRo(+OVAI*zZss(o&v_$or(e5xda&lXE!C8I8P(KSqj`m>%o!zUYU3$nnpOptF5 zWouK)*;ushHlI{-U6e(~_RE9(>EFG1E2+-qb;JNis|AC3kJqP`8D+b3F$Ug2YW$iu zs^NZudxE#VTYic<@B^E@LG={WxPM$vs}T!q&JJv=OhUQGRwydmrpTlfHn+^BoD*+< zdbg>@KHt`|C@5W=-6;aQprmAYzvZ8Zj*pjAUg3l5C>`9{7Rksi!Tr2N=Jk1CePzrN zq~V2HwvPan;D}HGwpnglUA4$`Z8E9($wRIadE3vR5i9JVTf&$l?XV^O(HX zll+I13e#PrB>A-QE7rZ#VAogL?~MHqOK9^W`NNlU4(vMq;2E}R!lQcy-duRJm-$Z1NoA zL{{6h1g$-NN#LvUyif=)8XH{gFwxXCTTqvaqB0cC$8P=5^!I1lK3Q6R(&N!ozyR_Z z@K6S;N6g+ua`kwiHhjTu=)9WhTJH1EL>(8==45aZnj#6+x7Y$-e9U`>NZ|x!zfYHM zs-5IsX`L;_Q*q-Hk>~?Nj;v!Tll{xiql5+6rlNIhga)13Gix2JU_1JBBD5^xiuO<> zvP~g7F1mcZZb$|BsICF;X$DEFnyNp6Uq`S~plaL9OAXA2yV(gY1xcjlck%=}OgO!*a_2$ zP#Ye;r$_vk+6Bw>$Ccl%OlhU&w7rsitzMQHkCq?ZPp$?Yu+2t@&b-ohi;e;ntoKy+ zzj=YA&{0aaED&x$Zuu`z+xrYvz~lbQ5d*owwCV-e(^pX1r1n z{MnLW5Z#tJp5f*b^y$h*wM*5dY}Sr%V^UCL|HF+o9`l+c?rER#T(#Lpmk3i1A#^0( zg;Fc0gvax`rM{T&Bc5&+a>1T_Hf`q~iVTzs63k)8*+<>*I zpJ-Nn!Yukd!`JmuJZYJbl~|~LOjU%Qh62|4`ikVgNP6Bes6IPG{P)9`ZfC~i6TWJ^ zsIPJL=mAI_xNi9D-83)k7jNa$5INp)1?Gf20I8a)#W6mw6t25YmN@Z_b7rHq=LEHB zppnybcr82MS#yg|IZ49jX)=P8_`k`+OLoW(X0s>D=KHdbZPk=AZ#g%*B&}FRrB9n|FBjR-ZL9s-%Aq2`nO!N6t#uIda*$?8T}XyQN~dV$ zg=|YI&sY+C1d(=9b^I##zSIIfSZ=tEFyo{{%bKt3|4jzYr{umVmOnPUWVji98j#bBa1;s#QTgcvpNHd6|Nm7QhR2k^cD)5UC|_ zp}#SEwtDUYA2LF50ErFvp%o@w3SUPM6MWF0IJ{pPT~g|HssF&hjgN7gK_3?!J9n)+ zJ$KVJT*kc*@>1jJcfO0)+taHo;>LT&(7|cFlknraVG6D-AKcHKCa&#S9=#q@J8zY} z9)@gZS+moHk7IgjH^+Jk{~SGD+~6|g$m%NXX1mn-M}BgBTz7r z>v-l&yMx$@@(H|P=eTeL`^bm`R42KTWI1AcNeaj##tCM-*L z(sMC1*E|MdB7&nCOTM=&#|ujdnE4i?4@vADfE+Ku&u3PX-Ll`^@#Cw!SHdiihv9Qk zNieg=@R`lXgzaxDVQ=ytR@Edt%N3Y91DrZn0!P#^yZ%-YTg|GbZBAoyc!)_Z&$&%U{6k17ncL9()4)(?=bOARTaNyXn0dEMm@ z1>%{@UR%lUeg#0PY*8^IDUQCkOsXnrSeOy!-BlT?#?zgdt62#qJmD62;C}!?&=;~i zJm%-pMZ0C7iY3|iBPc#N@;El*m4zLtl2q_c(aM<&G2{%048(#Lz{|p$OoQq}B zihv8FytG^u>Daa3Pkll7c_UsJ_c$a^ePUga+B2zW{^shW2LLd0gHmFz7iG1;@@Zp( zTZJOZgMc!l%ZbH_H_a{M+wb?ZHRs`G$Cl)Rb+~3Me=E=VVxYj! zuCLU`Ef0%#%o@K+PkfeTYKCHp+W zAI@*s5MPO#vrX}nb8QxsoMoQ(jp7zxZ8}HpT$h!FKE{}R#06Pl@{wg&$v!}Z_vdtW zS*?F!!*$!S10`>{z?3?cKhP#+U^-tT~_Qwo@m*wxVRLip%@S;1+9Opn#M%~!%L_vOwIThwaI z=wo^764hUTg3~7VdNUP_3Q031-6zTxHorF$yZ7+e`JlQ>qZ$R%9s6AaC~d&YH-Fc1 ze~Pw7KbEPBX&eoqwoB)M1m4p}z8htp8rI%(ws6X%4VFYr(Cr$|R1q+$ld$WExrS5C zY^f)Pg?b7+QEr<7)L8%8Q1f(fhk&=GA(kWJt&pffl)=CEFUCA)(x3i>{E2x;6Pb8_ z--8^~u6T{bh#LBw_-tN@u_vN$a2kE*lFkp2I{_k?OC23WbYzCz5-e^ z%bNC&(J)(`_k7ABcOe_k^gk8ut#M5@6JK#++Dfi9y7SP1M9T|O5+-vYF0Kt~xwKBZ zz8KV0$aV~fb5XaBaC8=|8g-K!8gqTxZ%Kyl&&!BC>U{lZVEZ@PvdL}gKfwBJRTsMP zGrQ%ZI}ft`l9?_QX_HNMB!WG;g>FtRM3jolDDZr2dN6a)S|Y@Ys*nC`KKd)!e!=oT zz|k@?@VrE+?A3oi^VGh^j>MDHY+YPeEwuYKB%*!k7B9*dnAE(8F$afbH=@dzs@A+& z4zwnkJ48j5v@Xom$w1tDWEVQSnXSK7YbkYE{Ge=eD02V*8}$9Z3ef)*fkZl??f_0u z1p9*Zr%Gs69(|L1y>PP)lu(Q(l&BtTK!9)4ALC6>M2IQfiWY+DSpPI}A7o~Z03Zt4 z7>fs=$?i(3!9$%QYlJSsV>dX7E}n|Csa5xZLnHY4xfiOT6%}oaHmFn>OxU4#28uQ; zvZ)rTkB)OA=+{pcBGQE#xgDE1KAM9uAPB2w?YjImD%yhGIMab$u83{ixfiJ9AK+35 z6Ix?Ke@O!uzNJE2Dd}_sCpVGf%dJm|UhL``NHg-gEH{aZ4%`m8ilG~U2uYO%gCfIQ znE=pOSSyK|sZ~#tUGM3qt%HDhHAzX7c^E>l6SD;444i1{?pDCxT*Son5N7xk+u1dH zaimJR@QmE`BrNgMIw=SJ{;S-oX6~ctKn)>)np;bK_jusDix3~} zvOO08YAqWY8vz?Ila51Rc0Z6?gHS2Sy3lYbbDMyHg1A-P*N)L@>T10GAm`P+i-o5= zGyUXEaFs76&E}syReS-^1OSHlL#6gA&(O2CEvzaxbh_nhtk1PlfBs4W~X$v~_ReR4*88UJdj-MZURP{o2 z0E|SZvBiwAp0<45u94f7W>w`H}NY zQ&Z0FQxhctJFOv!kb6bdZyy@FIMFp|Fp%%ff0J(=MX6}(JElAR3Tu48`Z{z~s0`7( z9>IgH7ojUoQ{Br&o`g|NSjM84P$H>Na?`~0M7G7QQtRN!4o|$IIiV4U@E|I^p#ShU zIf@Sktq{Cw_%vEyBEOqBv#SBs^ahm2XJF2|N%|PC{{S{nK(eq~PB>?3L!*8jxT`<- zsRI_^GZEj0bOI5ReE+!rAoeIRYX&i5j~Z1gY9z5^&suHmtwn3^sv=_779^-q`>XbS9ZKIJ#jhceRY_emIl&j)ydTqLu^wx;JH<<&vElQBaFn zs|`%DAG=Fc*-n~4n6ENRaG~x{K9D5x?es6PRMx&G#ac)DoPnX9&jE-N8mC1#vxe5= zmFV`i6eUBcdj(!c;TZ$d%kCj)mL)DhE!3l^p~%wI%yZv;)#y@s1%|q1l z0i-Z_Kz=@D0JxiD`F4b1*^-{|>lQvWBDs>>0G7&h${g8F($P?X#XtNeS74!GjR}AK zbXy5(Nrp8+ZSx>Et6k9hABorzp#gXa`D3^1^BFNdjXt&5a`xY;q zXU$uFC?yf1s|s!3TbrbaxEPK)HZgbd7(#uE3W6o!NNNSm4ilQvgv=_K42_R0&1#7M zyKmA--ou+(6^s+g=Z}Zv+J*dIn|#7&NtuUPzj4ApB#~}C0!*rjAqYr-?L@R0Ic(A+ zD4HkaNXzS0#1n%{S!DNzmulI~x@38OQVLBd3v=akM26T>8Wp5ISD~mo16%+a8^A8> zq=c!U8S}tFrfs!Ii}7IP9i`*1cfx2%@s>i?T3(>ti>~kbPSKxLtdUuAA)2ow+eO`^ z9FOB9T4UT)vC&(qpcI)^zBtpebcIK1M$+2>kKsaIVy2`B*+J%sZkr#2lYVreAoORS z8^2!rmh2)LmKGxkD)0|;iy-G8k^-YYDE)(%Q|9`)^691AUBu^ISL;O<}^+op{d_12Ws-iqxhQPuOIj@07J zg!hE&=)P*@!yodwcW)>0NU#UaD10n)6J|v{pCj8#Wsgq&?mFiyxpNW!wd8%bV31=z z)-y^`RxhTRP&mtiA0_0mcgEwFLpDhs-=fN|g_wR?2-K6aa1B)>CnK%Mxx3G9*2w9j zSX!^%HVd$(eqy4CIUGp>RtEQfZ2i%Eajoy_?*YAMmGK{r*sJ2zvc_;&y7j!YkL;xNngn`ug!(Vr>IBS?{q*%G7Lx0IzRR>z(JV z=JD|ftn^IIlth&x!=Uq^+10(Hm-39i_PV8FJPeJ@w|o2skB}BrX2#s6 zOYsR$a`x=nVSP>8ueRmh{8imp>kcrhCa(9C+`jeyljCTeXo8P>^ZSSY_!Qf_F0ITQ zSFiJCU0*l^us(Nv+*FskUC^-AM#v($usg(X~cfy^Gs6kV<2~C>ax#%-!pSdGq!1ZOe?IEw3EiCX{mT-V*-C&3p04;KLnj zAFhJneVuzyKO3I8YSZ5$F9wH?m|uks9|l-fhSk2_Wvq8T!J1}*MtfB*@=Sx~-r zrBPX!fXb80iKQO>#qgSslyZKUw{q$M-E~EY*Oxv7<=L_2zX$D?d>_(bCGJbQYaJ!+ zw=bh|5B~v%*DKtPyrk*UdV7|m%C0)BzJ0V*&Ie00ze6qVsXxHP$oP4Y`)6KjeE3aN z-TbP4KM=4Ty!eL!V4fdW-R^M6bub9IP7us|BByatB}~JGxc?0(q&-wf;{ILd`sOt& zl^lYJ4ajw~(-7GXT)UmV-I0gJyr>3M!2qN>(J6bt7_VC{GzuI71Vnab)aqY7hmr9w zX!(bG|C=GqqA5aj@)@uF&HIG+Le*>jN!Ac6_gTO#`US}!^gElE{7Qj{{Qi6GG?0L_ zux05*VOSu^(d6YC?Y-^X*SX4{#pG)=CPhBPwzhAT#z}?i97blk|79AGNmGA1MT4Q5 z1>=N|AHRF`4^Vt1{x-aCsAwet+DWvg#TU0zdJU~rtw2~B*;+hdv*if*Vk#-ncC7(XenV??}R{Z=NpinANa;O zg)U`!D9~rM4-0<^5DERga<0Dqs}iGO-a3WV;L?f5xG$({#?1Kr)mW0tr|=o2+Q;S-bd}o0l&WlHm*37wP8yBqM>P z_RYq9ccOh_g)8;_#<2K^59pB1^zKujOc|~nZZ$4dU#_>?zO4oB#?+yy;UcC*<$iou zIE|8)x`5GOr%)&4^X6q5V;l(tRKe8psvY3h`I&+xs~Q2#o52d19*=H8i7#gn7mOX8 z;MUyx`ia!>3Do}p-{Mxo|MH!8JB6skYGK?KGkHI?ot(XUX%|gC1OUfz0c$wYalPZ} zU3wQCzirWF)X;Fi1xsp4iE+I zTIDn~nR!44RKhHcXI#d^kXhRt(BmBPb@}jh(z&~H;c-Noi+nvdRyS?A{PR|x zvor!^bc{BP#+=Rd??#^!hg<&_xGdaBZh4^IYLNS!dy74zaj8bUxpvq33)rPD4irw# z+kP29T%-?$xPZ6D54O5Ml!Mtw7BDMwD{+YQC)H-%^iI)w_7135pT(L4=5rwODb;L( zV-@!I$m7vl8sBV>ko%jJLe70|kiyz$KU~zR2*z`6|8+htb-XVl{4~76k|y9*s-Zgf zr#B9Fkq!nhk;?wq$G-0_3^!Xs6I7(gS57P0R90z&e8@SeFt602o_`;kih!hswP-Tu-E86ULKw$M#I6(Z29#0 z*~S-mmGHJ%B=l-@AX+Kr#7EYd?|VhnalVC*f|&;3#LKeu_R@ORy<`EH$hP^&il9Hz zz_TdV6N9(f^>|@l9Zq!0B!(L{kgeWaiRIy3%HN$Nn}bef36=bc)@iePHr;-LWlceK zAnPAp>*f~)d-x`ry~Hm*zbad}3_uw^O0rDXZDcVY40*&Gz|eWi;|wkzObw}Vqnsbb zCog)8@-LT{a=<@R@>|uq@#QcAL*L3}PfoOw>O)0&`0^ZsP&^j>Im3dtc25>;_`76x zrH}-@i4bpT=7U={ns2f_o=vhud6z-Rmh`3{0i^ZZR2rqifB=EPDBn#`AV zG9jf)&G6M}=jOimK{d`2ciWE!gzU!at|#Y_BQlT}kmMJcEIe6M_3!fav$>n(L8e~d z%+?$KhfDUnxPl!XTMUa`{3Rk;!?m~inEBE1f5kMFkvI;NV}*|=u#(RP-WUTRyN)>r zB0`QU1ca#WE3Y0t;m>sPMoaG2+8j%B~1K!{${ z$@QU}p0r$t2bCpRd>ad$TqOYX|$;_pZH)#}suuwy_p!K4@-`$8$RH zGB`#}*$c-R-etXiIxX6uGtrfZ&PC01q0%tfnxx;1ch;^tOifR+81KJKkT=1!lhd5} zDsoZr?H&#lJkISzB)+ikm-&Oldfs;%$AWDMl1y@LKYT_#z2k@iK=N*8dhc3fYa&^k z7xA<8Kq*<{ubcncLw8kQ%XAr{+)ph%iI;=s)b0!vL9#2d!Gzi|M8nW{< zi=!Zh|Ax7|KJbkha?JtSOC?c^bc_JD7%?{9uMM1*PPfAnksiP5WO}!m1u#%l=AJN+ z-0>LrU^(4G##^X4zBj>nN*PiwMIhJdql%{Un>FZ*aWVPyHr8w30Svqb-Gc-Thq*K@ zjd(cDm+e~nZo-S&)#GpMmg6QqZVESW4ExWKS&y)YQ8iTfK(IdW)4WcVkXuBtY@BV3 z?r~~xo+Ee8xJ0}OWYo;1IGTxlKM?%upj2nPoQS@*>F2tnYoOM0tF&(FNIAqRDqEvs@|ElIZ7U^F?bL^?F2P zv7?~91Qo}pHk3=|2IHEsf(?=MIRA00rTc3X38cRmp|Kzs8@Q++a>k|o{%h+KM#73m z0VfST$YOgJ1nXFPM#A_MxJtFLI$V{snfHF#U(-~cb7JmJgIEdu;A*|8VgMKm(b;^O zave(g@~~hT33>^_kxc+G!qbZTk<(8ZRcu{lV`oI=%7^)9pXjJq2>3pNw=FOMi4=EF z)qA&|84tP_L^H0zEhNu4k$r~XrRocp0 zp?S2ur;B8iWqC!1@f8~)E_Tx^jIT2y+zBY7Zmd zZhj`qS+!eDRJyWsS{1QwQ4wk1%Z3ux^`%RXKu+WJ$0;H$ryI667CS!V)XsH<_M7II z$^xrG8$Ku8y}#PG8rXF1BbPWL%T6(2n$akyG17%Y+4;Y^qcV6od$U*k!$OFRBCs%w z{nDPxS1oPHf5~<7-esEZPD4R$PGFR3&T)y6(P##3RPu@t`^rCn#ZQa1Gc-8xu$Xi3 zE7VFz6drRVu}2_Jx<&w>oO9d$hcV!9Whq5-@+17rYep9qhbG5Xe%&r$Tk)HX)`WOc zvB^DBk1>fl&jh4Eucb2kWj2{RLjG7Jt{Uu|7^)ED-Z?Q`VMDw{W0+K0r-okrS5i#^ z{p0*`tKP3#)XF?Ou~C*H#k2)j^lTiK?(v)L$u`|KR;Z;N~qx6im9aJ!>fP;jZ8KHxI-+&s*!>=I+USts&X6Y@;(A z&Xtk$xFR~nJE0$Q3xaf+>VLUC*Lmi??=u(5HTw6+`#>qlq7tKE)LK*IKCKWpA0>Nm z>92hab@rsIDuNPuY$qX^9i*F2G^*c~T&onkS8Hy*-&H#rCcVidK{3T_v{ISCvPp+C zmn|MBCkAi z+qg!#JEFnxor!{Ncwg_%Kn3l$_T(SX@y9bSr8jUt<6G0>!(lhsWQUadJbG-h@WEF= z04728$ly;rHIUE6n>t^{WiaRULb4b5hsn}|dxyT&Og%g%P8NQm2l26s< zyGsM1zIO=7d&P6rJL$K{nrpA0ZSyg^^Aje6yL{wVi;Ou$4@U*#dGt4;?7DWqdeB@EDs5^@)7A=@fCv%SzAWpcJx)G0xvd+sQJIig7Y;?1zrAd$mA$&L z3|N5S@Ef<{U&c(td%C;5&MvK1WZfANXdhmj231i4Ff~Ub(1^uFTI@6-1tM@M^5NxE z15VEdY0B*eiGVYH4iQ;7b%}PA`AqQbH~Di=0U=HD_lCcdHy-OhfZ1l?RkdR4#r_y7 zc(N-}O#8WLxTfddL>`wwoH&GelAWx@LmiMSgv~`fio`oby-15;KAKrr;A@J)d%j!t z)sw1iACwQC@a5@*IB6gkOl71%0^g_6X!x00E=>WMQYG=nPkz6CD5z!BoFb<^vM@{#HSI$ zbRvAX%-?YAAA~RB0m@X1wReUBy1A%)j9og~ZYRnuJzS8LfYAvF2qR1Z z) z`kUU?ACW)bDe5;ge9Fncc=&397F44g1^uHo5$Wb4rL{B^KnL0A@WnE^;oN843Tt*3 zy$jhIm?mzd?qW7I3b$8i(bkek)wW-CCCC?bI`AeTW1i%H0G6>BwTtJmIZ5Mc{Eg}T z5A(B0?oCFsvF(F&k|SaEV@rJ~s3 z*WYXzdj!W92O-OdNec!=tAd~{RFle_)N0dqT;eE3k-+28K0s8Y|6r+t&$W%HJnaryEl*--2%G)d>SL}tK!f5s zjTe?8ZApE<7{c>p)5z3M$nlTk_0;aG5f3ki5QPhC2XR`f&6(ZA6_Xz|DW_*Gb{^cc zRXS#8^a8!CAtN#7lJf(xusohk@5tz>uSwF}A6k6ZC(NDC%C=;-v(38TbEKqBKU}-> zxY8~jH5DxFJ-GBRgFl|upUzZ?A+j^rO1$!8l_Os_ELhIW?|EuOS#6iOJ+S#m(3 z6S53y2x#)kJR0DucbX}3Wah5cRYKOEjj!II?@t0)kXxpWKQ-hKH0=pEKK zCwL~4L0<@#ih9~DuBTjk$oNK)q-Ofsj@5X6w(X0)Cr0rMQ~3`NkFJ2|>HD0m|92Ar zzwZ_Lr?lD(hYY!Gby}ne04ai(yO~CT8*lhWi&#Had?eWGJ5dWZ%-^}O%2yS%^DoI? z;1rBX(elWN@BBWm-+r1Lvla#nR#T=Kkt1-4de*ch{r1rs=aD=l9r+Eo>I;~=qAAAG znG0jl(PR2OLB%yRl}1CzKk69Q0vq6U%S|su2jP^i{(SRd+uzNI3Lw)R;GEuQq0Ig z%olBmS_c<&SKfw&s18gEt|Q(~4uR!gMr!64lWux1xZH&3I0Q+ne1O4n*|O;&OiDn$ zLfk!C9?bemh0f@RnfHrPQJfc)g#_nW^qrPk5gNZzrI}GEwhc5IFDV&JtPfdbx*K5f zJ@>&-(}IIg2MV(d`P2K1C3ST)*<}h9_%2?@LHPvPO9h2#rchdbOSYUZa-aqpNNWgfC&Em7m&+(Ex%@@Ni0vgv$>0VR7T@7 z?T$*cj0>)Zuv3|!-ADkdzAh~+9c^3>60A_Dg=rV_S7r;-3kvnQivdDXiY;eIw0V{s z_{WpYhT~%05mmX1l5fKd0Yjd$ys#NTwt+v0;(({u%Cu+St^I%vPvF%0AE3^|8B|bR z9DF@nyDR1Ehd=w1$iZfsdFDy`sN17Sjj@l3w7V3$*8fu|`oHH4ta^_GPQ*IpKX`W< zt?U1(9b&Vn5H*QzORbe+*j^r)SL+BeU$P??!?a~lqKvYdIx<6Szks8DQXSq-)|D){ zl6dI9X?fDU=f`59SdToe$)C62Oo$gd^73o4gNpi^Jfwl~8&ydHSz&CX@Koy|QbgL} zLvozMUZYy=Kfuqz@3pBz=VXSNoogTJ&XVpFARWCvX+-acJ$Xm{jH%N79Snt%+zxwq zp0XkYWdi|=Z}_rnt)x0D;<_-3b+kntcv7vH)Bg;SJ6qxoH}kJJZgFMfY9@Nvn_C4v z9QhwinI)@y)WD@CsZd!jyYCjYC@y6SA;HURikL$2JabqcV5yx21 zOQJmGZ;3Z`2|hp{S-tdp(4X}WAht^&vC~{!6jo+7{;H9`(xTUYk@su4__zz2Sn$d< zFfr36OR5VdIAP`?wCAK*Brc_Qk#6wq;-P`jw7uj*MM*<>U00X7`!6>|w;pgif`U@$ zoxA^zSv%OMcgw3-)k@D+e1_NSTeWBkpKpvc95HVk(qdWj($)V!b&zJbTfLLs(#RyO z3^5_QQ{WL`CCZFcY8l_C%yNU%4txr>a|IC^)kDwC$LWU{E^bj*|dVn zO6~fm0+yOukM?+64<-IY6bGft+)9=TC$#Gqy&mMQ*s7LUZ;|J<&iSoB2S z4a%{gF7|!VER{3W+@LX;(&V#mCsUImq;GTighF0FSB5)CW}#m4lasQ7g^%lt&yq5P z*QOep=+LSX+M2NEh5Z40n_LZ3d8?9Xn+P#zg5|t&Q-tQ{Hv~0E@=W#1>OJ6>RmS;$ zfcI;L77H$$3~z$|kjsY!ZYU3nzd&txKDJDZoi?xVU^kxL%M27DM)H(2P!Ej$5Czxq zfF&j!;|bCRAv{zbU-qw%iZA4D+~+Hw~7nrFnKgju)~ z7;oDM2J9|o)xeNXRzgE`V#EnW9bLgVe2b_u0TYaq%P7x=^a<#$_b>kb>wmDpptx-~ z6w3|yvz}oBMwV+IHnp7oR+Xc$#NJZy(zor$-FNFC)q;p10YzHy4kCToKVgbCRZTBu z(?^Ak;9#q%#e>Kdo`x^N6yw6c-d^8)2DsBEq?GK<`Tg(ogVWUN2|hhDezP7jBpEVG4`xr))DW?Kp*X$p8M3zcgzgNamO zDtTd8w9zNUjaNr^s+7O5f7Rx;Y>BVb|CSLA0GyM4A@)9L5!7rnsOlNswF|!Ll@FX6 zS4M5ra(k~zal2>gUw*5y5q)k>08^1_o01Q|!~C152Tk5Bh$8`vKuG-3=sy70@BO}U zQgzAtbyCAR5=Ql0S)u^vTfZsLD9fLDSH%25^2vRgVM{m^My&+Y+ekZqm3ZCdpKcZpHT8jz(6roY?IP-L);74I^5L_x zu2@Jm1WIlHv+@#_L6=qaMM|-lDb++A6&qXGFBAZ#V&3CQ-km+);85jPRLmv$o)Cr? z<(CpifDE4TW}RK`YHWTDx7Z~vC$a}Zs*p{ul9uvnr>118FJ_M=!b8N=c;a3rakTx~ zO7&^A#)$+u(gn+TcDHE`%)GW@A6`FLm+~d1+-m1>?~c2cITptv$A4@moLPiqa%zt-F_bswmPIyJ^81L>Oh&!Zcd304jF&qu#x2^sT>nS%KC77qc zsbxU+*GfG90GC@n+;>X>U4qw*?-+PAv5$rr%Oc7VYn3HkJ+EEnH?%@TOR@gc_PT2} zsm~U9uRLmMID|Brgxs`?D$%;X|JJ6nJZwgxLD~QWqe!of;7K9VZ z5#Miy?d?>!PcjDNaQa>{e{UtCH*btc^J8GzaLmpinYBxcnN@+OmiWC_zX}Cs!;D5~ zNRTi^NJ-@K1MDa#DG&-Fn^oK};JLgWU5q$iLn0u1fbi}?CJKr#8ZA#Zo`}vj+o-Yx z`Up@9Nm+uT&48Jt8ZHs*SpzZ+$mp)p%MC5M+u-2z)4%?8c-!o34-ym83+&$PyF=HG zT^?z#q7FS~IUmSmd_bR#9NP-li36&UB^I7Rg2W(*jGkEFt*FzjP0Q->U+tR;TkK@+ z-=sWL%u|`K>epGpXo0U~O&tVMndbw2D>k~6s5?3n}fuetmzBQ5FCsH#wll>1I@XTYdAGq2IElz9as%Lo5!#MY72IdpT7I z|E3J@EF>9wQiEV9rG0?!T0UrBYVm(;i~ALnduItT81S0B3^HuCo=V``ewLGOI+ckCB&@8n#77;)t z)R(pV9enF!(}twUmUu||j51Gs`TbOVb!TwA@z42^nOIfzs_r|J&LJ3n0zeC;xngI2 zO{cqki*n<#-J=L+p`f5;Cv&)w=Gp$o_Wb5bG$I{B8m!BqD;_uV(vBWlXZVO;i=0}V zoFs^ZIHbie;?w`%9Z5-11!;Z`w8sGI>jw-8sgS8_mGKHS52_9vZU;}sNr_zitd6(N|HE&kC&IdKv> zfOG(R$Ofv^y zdr|_@P9!3gd>z-J`Y(+i49=)5p6ucY<_}C^rrl3=m3mf36StMRe)O^Uwk0^|vK1hs z67{7iN~G{Z$-NE}sdj5U{^A_2pdga+hEi;qZQ;-~x(bhVV>HN+ZvCmA!x)sv#^)J1NyPgUP>qBipT`Tdjo5L9v;m<%)1xOZb&OOR z>=&Bv>s_^mrcGMU#?@aU^tmm=!W%_I8JIdL8hFg#^j6Z z+-~NxQf84lYhT}=kLm%5HS_k+)7a>87R@HWDFpUc>tzPQF0Z)GsQd%9Kcq5}r%x=C z@)q`G+D$oupC8gpQ3)uus7wJ0KN^Y)OQtk^)r}=>`Hv>L zS&YTl=3dI5wDjJo6RRY!W*h?YLf9pinVMw4weJZVl>uv* z0QpA8H0fT9GL8#j6>Kf3b%7l?2FoxhdLvdG*hhE-u^P;^N~P3|p0mttVOu_q^P=&N z6Q@w&Eeu|r2^jbvxG!Eu-f#dJ`&u6$+P@1j*j+)iRFLKU1dJ}qPt zTLUS26F1+tf&l8QV?;7^V?@?;e9VNTn>Wmm*@`RF=2Cz2Z;HxC`WAl)XU>mz=iJ6Q zi=twDK9kb*@3YeYpJfro_f)FvAG*6tVzn%>6MY6g9$=%2AmAm8GWIgN?3NI1b+r~3 z1ZtQz!E|^mC+bq#0Ep^q7#h5NAuYga`s{B7gvD{f<^)c6xkJQ`o^`}HE zVm1M4*wG+41g-=|2gl6XkJ_=)Qjt&6s}T!7`zi|vTUDt}ed}ZUxyppMTYIt|vw4fW zvFpiA9p)AEc%*)sS;G#cWi4L#W}BH=*iCN&&iisw>mOiarm>Zu91LllkC1KTx;6C# zk$D=LW~i^@L6rM>kdTY1zpd{4O&J;gpzGzkkOc^ogsYUpLDE+5^`b;ZFh=#xm;wDM z`l)#pGlXifn1VpC;veP4nRyc3gU(P>TWy$}u;AsRb*{M1A`0PS7JF(GwZ8zDxtCGL z_eazSVTQib1Rm}olZx#k->^Xwhr@elr?fY8{c-*z8{RXcZxAtCT~gE+<(dql*mh91 zFOFKVw-d-56%eYI;?g9Pb>yo`s?Bfs9SHBLSIltAKh2ELgO6LWJGRXj8nvH+?Cjhj zSpiOZO_Vhv?ww{91oMsAgZdFD6NAHPT`p;VxLy=fH`Dyx)edg< zlOh8(ZQv;*U_^}VbuU>oAx!-V>dblOps>RV;-Umk`KXACtB1HPnMs~v>WdQCw9@|G@%5j z2XeNhqa0Jubn${6yo#9X&OC76a85Ma>?rJ|O>}OxI0HjrU5Z<_03q#^@6_lgf&rgE zKNYljP`1^Q9bXXlUxZA9NMwwT*zf7Gch#m>Q#| z&7rg$U&Zc#x&$pxR7}{;+DDKfsVV-h2KO(0adoP`$d2SN)l}&NR#C^SooGbzG>bEI zeb1q5%SI){eM=BRaz;~g#^Xu2><>M6t(-yy=}XiYC6azj z7JO4;y-LkZqSYf@7DAWjqbX>Mm3tfp44L@}`wQ?K-nI8`yRO z8+6o%wW^{@(R&gC)SVg9&&~upR6`Vk=P*-AYm!0;D0Kky9yNszGcd|Mg`31UCta?f z;N4lJXi;K(WeClzVpAsR0;4evhvvIn>O0K}6_L4gE}4D+bEE~)@X@dIBch$>>33xb zGgkP}1LeTw_*g7N`A!Nug1I}n+voRJ3PJeSG|A2VT5m>k6r)9L)e+!ScsUkKibr|y zXs{dDdwm34$;BrhiYy7%m;RtOk!zJ1^UaXN_h7xe#yFQ2pgsVmAbIDA`qjZ|a^X&7 z%`urVfSuM~!4+(OmRfk=x|YP+WgtnbKr3V+Jnv{3X9`29`o1Z0d)C3#Uwwzm}r$gH39|^2;TI0&9 z;hNSA;n*!*EF_bHyeq9vMW@66zG3VSENHXegsdhtb8YpDGKodvoi+p8PeI?p^Q@!zdg*d}g12hC_^<3Y7M0BNf&qVie@C6*!o2K~$&aJ@ciKPH+v8=Idem|UH#lxI`_W+1@U_P3#h3Ml6)!43f`%?jvP zF3bPZ5x~2BPoib;0i~tqeZ#UdV(bctt4S6Hre3Rcq|rOad@JhOe=lbnpz12MA=Nn( ze=mJ&R{4XziA(Z0+iJ7HGXNaO_>}aP5#N+eYx42V;Gu+UMarl2?bHZqLbrmXM0QQK z7*$hVJ|OiV@-`hiCvM8zz@#d3$I2Xj5i*0px>fEG>Pu6Yz>5*^9>2%Z7i6MNc_ITM zEaAn4I^=>llu29#(o!q@DNmA-0C7fi{t=hB?Q?~m_iy%4ffWUzPdxiOi&9K^EW(sz z2xhY?9se0aiX2=HYM#XZAhPvDWMp1+W#GqK?z||7)i1W8(F?=@cyvG%=}jOwEqSus!R-5yl6n@z!4&D=e05>tq)KfHOM6iG^d>tW-&#cnahAojR@kk zP`1!N*bSLe#?nJk^JaMP<&(Iu)|g(pDpY+49w^S~gPpa=+`@T6_>sHahcyNn&U(H) zWPKP0yRR zfK`=%{vY8zMcmsUu~d z;#1FD2agaEg%=WvDW9f6Z^eg1YpC5^iB|&c(jC9`aT|{N)|?1VG@sA}7+IR{8EPT5 zx(9_BN*Ru|V}kx14R>|l#r|itt$c<6s8I`R9Iz*!o{H>qi!x=Q#QAFQRn_E3OFZFl z{B!5N+Q-?+E_RLF8joVs=Q{lqWW{)Oa0cvEQ;T1q+h706w{N=s2-ufbx>yHCFno*&XaZ8> zmFZ(F^VSY8LBDLbQ>$zy1AVjN2o%}z8rs-Ftk33zGLD#?puOEfvV`6IMye&|dU8-7 zVlF*fJ`eG0)`iS4H>y#sKM}k1S9R%Rcx!I0gFD6P(=yTzZ;xI+$xHnumCiHl)x)$6 z&5AdjNnoikuSr9dsWd?1mex!M(0#rk4mH}h*7pW>#RhjnRdPX0+vIR^v#{@v>u7C# z`q{6>3w%C8RRGks!_npwMumA|s|?G*4lb>p!#GctT>AS%U>v_pDTFWQ-Yt(_pt9?$ zGU>*zMsv^{3v4c<;!PP-r;3krXRIdB?LkAhX+X1(OG-6<8E=O`EqcdGl)~2t(qRW@ zuexuWo8@AvKW9U*LIXPAmjC`mKXO~W#cGw%J?2GOWvF9@`v)kj2qO_i_BDyP|MB_5 zEZ-+~#S?rW);l4bN3s)LuLD>j*>CP~Z`>b9bsXJUc%YXy{X-1bg6#@0+6-0(E8~!L zx5Aj8-D(ILTwbSS@-2=l{wx{z#mI7pJ}}e3wAxS&eoM)(oCvq_6+8!S(vA`Kk^{u2 z$x17eDfA-$<0D+D@G{}E;zMh8Zfrb;o+~PSnE3Bymda~r(a=XWOB!CO!>meL}iO0ZFs+;uHnC{E1YgsRJv0y;I_PFuDL!)3P zpKTSEQ~IPW=`tBjj3`G7FZII2N4`Ays^a#n*D)V)5ARz-bAu)=sdx?O3!dMtJiI&{ zHpsIuV<<>?${?)=o?XQK8EuPm#d|;M3n5+y{DyCncKC7`UjNG`x}&sSJfMVe)(1TF zg+F-1s!>=yE@qDV=TU@J{T*IV^DD-mS!GwoL-gX4hMTA_if=~dZ?1qFXVEvaetYH* zPH23YfYDmf!YuUZ)O2Sha#j+JG!qLG1`9FY>e@B)@xyOf13i{0ZZL6swkeFDXoL)) zFo2%z`O_W3U*$KUTgC!1FXA!y67V{HM3yltKtX{yKvtCcUO!ug&9i}A$;R$M{i3ke zJb5eipfftDLYD0(5?;F=4(#)jG`;jPROw0cwmgL;VZmJ%DN^s1^C(b&lP z6ZmQlm&r6Cw`NaFR@NhnJui!2>x$DQEJXp~@~Wud5fq2c7_c?Qk~v9e%Km`Rw}Go| zZC|5EXR)HYB$_Lv+P;sUQvL(TfzP;JO2wND=Drx*45{jRKHk%rZlU+O+oe_Wm(PSq z)=DuFWVudl1WlbaU>GuMehOLD3w(wN;50H1eY@4>WmmuP$M1Ehq>;twEL`7x*v_~p zsb;C45WR5o?NxPR9A-aA^^sBViAOBH!22Iy^0{F*`S~06_>D{i9EF+8-s-^d=KDWr zU|54Tv++$Cv(!B1s=*I8MZA}hQo&}mcg+98=lfEfg&8VhX3TX0#Ls!HJ>FVW2F^-$8x0T^Q4uH`u=B#0r!NWF^Mq z@|W$1(?H4bxEO&ZdNv!J2Kg;Z_SgTs=_fmFL$0}Adf-B?4Nk`<7yNBvpgEQ{Z`HaA zZ_ith^I4goOvWx(4|7RmMbo^G`_^T8^``mPU-e$EttHQkiR|Ib(f>#PM( zgdka@e0-tg!a$B}0qIAk~sF^RtUNWJ{EGt0QU+N+LHReTK zD{(*^lf;eSeQS*Rs*m{2Z4Z(j@oEiK9`?{0Dd=dT4m+7`Xr)DM*p zh|0mrN^n+bHq*9%nyj22e09b6s(iyx-h&1QyYn7{)!u`!R5I7up=Nk09Y{lC!USGd zvsaZQCbDs8|A!Vg`jDdQ9YcSGhBL$Fd14{uTL zSPC>Im8&1VNX5=A5~GEwY9Y^ACZU%sMQFmnEQu*>V5W`}bAj)(WmwznT(KO4f3`4< z7TN&v17@mtQ)=*2*bkVnTs|VJyKKknQiz<7t`x8iYYh$&Euz>^tM~2(# zm2qO2Mabm&<Zo0p#yi2%bY9( zS|=*8G-4msyu+XfA|OH0jBST|>*D<57l}{=Ktu@1LhpY2a)LSvMIn`edn=A96s9tt zOpco^MnC(NRN3IX4&v54jb)1(gisL)FL6z&{vG39)+5tg(dGlONM9Iu3H%T6Ld01P z)=1|6tF)VuTAQP5MOV_Ep*}J|h}Kk0G1{WH*30%NsFxfkZoq@e&#GA-c|bg_4~_Zsywd4N6Fu_`v=Hpj(pWf!bpOVnv?*c z4a9Vdz?zF(>5b_J?zfZb3g&0HG6DEcfhs&nzaiG7v@c$vE9>7=n?uElvkraVIi0V( z9O$OJ_Fr2;I7Dez?VDOeorn&I=NCI8w_R_g^` zlk(1`tg96@~Bn>7wO?LYIBiO6Iv1b@n@#sj`U8U2jaSu?|zd; zzWKr9Eyga;p7Zz!M(phnA9<6g`%4PR5tbPVWL4URM2sCJ&cA$0?^1zeA2*QxJwtJ5pG9H{;JZ zk=o`T<{yrqc51nZqon(gkleSY*!fq!_}yAXdC6&G4QMoNFA*}r0Di8JYn|^?!M{n zU1JYLv?AkBci5Ft37y;LRfBc5WaZ+muC)vNQ}dkvaSKj6C}X*=8&UbwFiP@?F+^qI#Tvl*sKKGS;Ix@@lQ zythcYLYUa=e>UI>XXu##4yX-EFeImIW5XjbQM68#_?mq{I^wJ)4@XPO$$e6$RJ*!1 zCU$7`{DQ!#hLayJv_sxr5tax=0|16flU>m8Fcs-v#biJG@A?A%Vgk&3<&JXVYet=F zRa?Jqgx&&ct$UBNM~FEL%roS6%xZGiX#NWKvQJx`I_tV7*hO!#e1<*KwJAZSXOD_= z{BeDp8F{Ni>#qMp(6CmGS_qLq*weH+Cty&CVDm#z5aOAoLIT6fBiG!o?%%kf{bQ?M zyianl1^6g=I+$%AZB}jX7698)UM(ennqU0i2M(yKsbCN55zu$63s#16mcxx zylZh9*mwi2sCb}A;K>$NTz=3(c_69<;nWR)pH;%%#UiIFk~3fq6K3ShWY^HAW$8lm zSv;C_mY_(TvHgnOHyzXslQeC5h123sl5o6$nB`zdmNQTt`O_VE>Jv$1^AkXKHsJ(4 zadyxL%1E0EkhO=D+{ddr{$1Z+apGsBcF8yek@^w~=)mMqrMRR@S*B)hgqF@^Xd|L0 zK2TZtssOb-xWHiYHAq+0->#20YIcmzvwTVD7Qf`wg7cNvZc4=}q#kmBI=Y~j969Aw3IY?oz8_quB8qw(^{l|S_kM@WtnC`P_X(63vTxF&GU2KZc#PM zaO?*^fj;1$@t)B5D$6z_6RSHUMbVL_oF zml`_+K*X$mGpWsMP>nR;6j^0p*+kG<$hqX8&;3M~@Z)CNhBSSjvj$gXcXT7hDeP5ZhmG3f0}ff{P{@ z)rkx-{mqHSOw*iSCzkeL(uioErI5Ic?h77CsCv+aHV#v+b${esME!&Qa;D8{N7_K)7SgN|wzmzKkK6C$R?JtkPIMJ0A>gXI z?8ofl4cD6vVu0rv3wyJ57TW1gke=dc6Ip>UIwwq{{=-2*ou)sG!u2%8X43`lJX^xh z=*Y7m8Yco$`5#KlJ>vZet{PbiyZ@X7ObTG3TXNrgHqr|RpKYFO(krLaQinH-J8ZrWdhfwR z{Hu4H+#vdoDI{>52?>x(6Ie*;ym$cJ>s2^Irc&o{3RZA9oD$BZ8F|<*-j)cGRy5Yp zH;wvBko4u#$Wg{5`*H$I$2#xIe!q=lU+z!Tkk-@_?D*(g7l2FM$ihaEBHK929DJ z(qiA7%dNR4-yn0Et4fVW&9yS|{FGX%^BLvryq1=QC#A|4k2G`wrp$>R0o`?WglH$h-R1%Uo9)chIyw%r-ukhmu z(&Jjh2atL?a^&QZVS!oxD@C|U^YcBZN?Vq1LDxUdB;Hq|Lbl|w5{k$vJ4 zRasibCzHr8gL7+opwbLb>#19laP_Md0_^v9Qge(0!m4ahNxWDpqHXtR?{_Y{l$4biT}7sqWDf52fAluKt(n>Zzy~RR6e{?%=WX5ha)~YfbuRB*f2rTjr^sLg@{i73 z%A)LNWUj1DbBC8ZCc28=NpD-QJro7?U@DYCc*N!2jJcVxz3-t_S@scJ$wR0lEw0iY zJK~a3_xUrvU*Y87a%StXCYHKjC-m>?dRPUiTl(vzPf<76U36qop0>e)nw*t?8=>#) zI30flEXH1nS$%m8#Ee3s<&a=nU4Cf#LW8`n#xB ztl91$Aw}nu6(~VEv>#R)6b+@!afnR2Ht(s=%A8WnS(%@qRj37N4%9?r=HGw4M0$CA zC3RR|NhA3+*j-PkG4|v^r$-*1+`8DUvL4BuQ-Cg1WOZJ&O`7%mF|jqqsScgn6`A8v zY6#Yd`$zVIm%-E~0tpWcRZ|xgb*#;qhTQlX7FRJ^Sww4NtT1$3&#wR~O#NebZbdvv zb21(%5^t+OR@egaYY{LAz@>6-uNzx%7^Pq<08dM4tg%zD@dYH`z-7mhg%4!-haeOo zg7RlwUMwDuEuM?%2uN*4+2?Z*2qbf9u;qSyU3Jy;+OC2h5B$upHdV&gO|nh*X7t+B&`Dt8nJuKI&|QZ~5<($9L@L!QFy| zr@yY?zLp34_UFWR1+G8lC8Iv46BYgN=%`xR1f zEkxy`-z0h2eEdAO_6)e;23v5KUct_%x&Rh@`eteA9Gl5JjN(%hp$BbzKWXalLk4-0 z%VEG_LRY30?VIBERCvzQt8SQk@#l+?- zy6lYShd&bRVcorEDZEi-RG~pW#y@mR0F_M`xF=1L;kkM*KQv2B>*uFhcLWyyF2aA~ zJ>W)Cq=gEcWhbb!Ld*^m z-0t0RMa>BNQZe*|k1q^g2F&|zYdqj3%~T;iDWkz!Je@%V`yrnM!*lRIAijT()FmA& ze(~3Sg7#^e^0m;Pp)LoY#czZ?{+=DbiGMc#LCuA;c}t9kU_F_{%dapknO6` zX1a`ZJx8qo{J?o~m928Qy0XwxwQ1hrO-LBxK!g#N+yY{r0flNRV-M{rI!w7av^?$A zE;8e7&HSlF3W{tQn{5k^-&c&S>JokLzVXfTYP5DBAlCx9Zah#{TYCzf zWNO2e=fB%ecI|1WO^b<+?ulFth_{TU8vBsX^))+)_v}4K74!P4RI$-xl+}=&(P#j)BUDi#*C7zLim={bD2~Q>CwC=v-^Y##b@*h(U1UzK2FDSpnIHS|DC z{A^vaSIkGouxPRi%PB~FAxKCA@qCc7^D)1wxQw*_lr@#<9>-2)Wgl#jT4bY>HfRDw zjXTk~-DUe*DN*{QNe*lW1^lAt)YmINH#x}LyWh0a>O~$x>)LBY$LwH0iU@QOl{xp~ zMS~z$^T-1LhOI-euut_269mJyZuuT(^>yyhEv;-#x9 z(WQ`bX`b`K_q?L2L++mXRO&qApQJ7tEc&9c|Hf1`Rx^TPDeY(r;OWkB!FvuiF|vxx z6zsPmd88axWZ{@Y48-v9Lny_425o49?ppT0ey6y>|q=x@v!bTOG|bh+@$qCjjbe zdt64#_p09ejnE1{Wc>65a;oLW=*qH@bMHI#eKzN5Xyi)YZ>%frKuxvL$tgN$Y0-Srw>VD=uW0GGf;p?ElFvR8&!WDOMu1oFR2)H;n()RS{@4iE781+!L3~^`$y`U-p?9C zyn*ghEpKc}cw!V78;wcZ#MOd?M6ahxCvteL2r5pPP=AJ-Yr7KmBR?7hKlGM%!R3w6 zpNYEgG6z{yG_A@BnBIN7+((Ak%E(@)@i4~KDD zGAuS6FJ^q}|KVE5P)DA0S;683&|HxAUFADoR7&%-%egWg z$H%CesmZAy7U8EtAH<LimJQVltL zF4CVnBTA!Q&@J;m$Gs^6=qrj(k%NtdI&uuJjcP<*fW&B%Z zV#2e;^}M7*)z+IS?Y_AHcP#wSgMdrd)BlWEb#(8(;<@4-IdtDNgkTwySpsF#W*?kp zM|VTr1qBR956(`sIG4E*E}SGa_+b<%TWuyRBOCq1gVT`Ln84r`#bC3<86}jXV$P0~(2?kq=O<#O_y)VU$S}YwlBu)s)10SC6>UKh}$lluhg&liTxm z7~tle``KPP&OZ~apSjOmiCeyzZzjNkzkT(u>Q-4zSjG5DU%%DYX}eTuzo8_+$f`$J zz}{-^&fcH-nxb>60z=sRI{|d(z&}G{9E8&mb*Y5f-~1ZN9zszm^8u=9QJv{OQ|@=^ zh^{5T-88Shjf|zU7)MD;4@*Kp2c}$7cja~);Yw#8?J&RnZ=z&}bZ3FwoeLA`aF`|- z!m~<$ZiE;-ZJTLb=iX?G6ah)l#wwVxa}MtXWS4or^PQ{PvVqH}vjx-O7YNRL$%1IJ zwHe`|GUB6}?G_8Fu9AkKU8_uF6+f#V$PX{qc*O)6wx;%XaX0Lk_FfYsDvdR<_}fCy zFChV8ih2|iyHjWYZzLl@-X;CX&Yoriur|g47R+hDt{KC{_d7?0o73kY{K3(iW8t~I zNrOFa{O-D=v|@g7vwG&&B2|xo8i&vk$C@>&i&lnT`>6%y0e6rzsm><*h2&r#+leNs zM;c4`X9_^i+!jCjQVZO&JF!!4nx}7|n-?|=cE7!a>!=`?MUtoex6w#mY(_~7WnOgow&k}^QQsi%6fmezh{EH{84L&JOH-RhzWA8wJT|-eK2N_j zrJ;_|5MF%qkw!%z9#<9>V^}&>Oh{RFdcCu4HeWSBuH6PEzGArV=Bq29NOU$*>Tl(1 z?bjr|2`_#XM!d1HD*CRvwd4^@Yt4!(M9~v@LJK10N+DO0&U0JZQ1!wzDZ)KuIoa>8 zPN3Si{&^~1N1j?MwdIbb)3)55nI6uGkbpv?jrRrOT;DHsTfJ~@5FBV=i7pzlpRZ z5C+HG#~a9}>kO<(f5j`ARwWl0h_?Qx&e>8nDgY)s&(E$zN{J z*e3E>#!M%eEZ0!igBqp3()D;9-uI6_D!|kR#@l(&nH9VDzau+A`bPAwPK;((Aqz{e zBtn~_OL7Db-umd>Kj&h1Nz<7Dy6AM42tr|S6XNWa!jZz4%nxK=V5}}PRHWdpH05CB z*R*2P9V2QMnxf01!PNtLO3!CI~Zzo z?gbFFF|ik8s$wS{b~B8?IXQ3LcS1$R#Ro%E3`-LoA3roHF=gJAPely5W3;Th5T?Gr z7ZMV~;%qoWB2F1+Vn<3Lb|N~+{{SBvmw!%``Efo>tTadW?vjH+kjg=W~Kzjrf_Ct$?(bUkZk(<90^cM!s}j4sUjM)=#&y3w~y)U z>|U{g6rE-fqSTakZGC~`N#dCj6yyH@HEc*ut?NgY{FVh%{{gNYKmPT#kE@!d2BZD} zDH{U{1#A@RRo?cge@u?Ic=2B=%G-`|FG!_ z8@$^dxRfhlD|qDhKXv5rPaaIkpUdCs%p2nJzMYUOEN$;ZYz_ng1lZePrE`h)`}4l> zS2hQ*NH6jh94U(9oNycD6sS5{_!3yf7;;%DO!zdx(>((K zn}RDNrThpqwAIuy((tWX($Q@hzV{!!1z_ z>*KLb#VWyc4RC(0-H)aMLArlV`_-S^*Tyg3g*Mcc{-eV65w9En_mA2yx;K$*3I)$5cy2NS-q#J^2+~J)pa+V1bSc3il5{G?Kk54j;_kjy}nhP_ss8f_mU89&1f*`AQ(~?O?9L` zvqBDVO~#e%%h!N4OrMx;)JxU=bWIj-GtR-7cRulmD6SrI{sKl#rMcDO2(SFj!v6yR|N4Xh{jVFX2Fw!he&6tBeOfXFO-EE4 zTjcM1DdY#?r@2OmJF{7~k`+?Ce%%UU0H4oQM;S>^_<=zvB9=bkm-`z2S&Fq=UD4kt z@X%A;_c*ga_Rd`Yii1$Fr^N`PXmgWBQ9nP6qK5nOWWU>!CH4E)3#EHh^Al&cX3~a& zb;26^jh%0snfSk&z)z-2TjUyS0S`lXRZpS|iUCw#o6Cj$KLfyY2H=xRs|HF_C@e?`tZY#=~`mxJr(qO4p4}b*<6Z*_lUI zTwgd5y^ITMFeC-73lUbkCA#vs6H8nB?%ti=)@PxjaHs`&2>`i5Ofm2|uo;~5Md z9H|-Shx_xAQoASt*@?c3)PN`hdF^xd{P9lqh3z~}&GRy_kqVm1r7yFaij6;vdR9)O zf4I+XH+9%=6J2bIiUZCfwA$FaTso;|Tn_tzSem|`b|D#04Xq@`z%M9lcg&{JL?4o4%r|lZv6)sKy`q-xqe=Y zOBU@F4|dA+emrJWCxM#yO$2+^V5!T^F2|_Jy0NcTlD%-LwWxcah&eN>Z0lqN4r~KWL)huU^sN&z;6XCW=25QR65hpp$hciI0THH8 zE9J!OQ>(Af$GPonbUp6bJ4C)V?hk6v-=)rbMVzZ;fx|>u_|L`=wwycVf{q-Et{Qt1 zed$@H)-{k@8H#;{1yf{d0^KsQdRw1m>mdNga3b49>)10&Pb#^!g<5U#f-;SZd03%m z^`v1?WQ_nLpEyZWSG0fr3eS-qsG7d>NZ0zQrNdpRR>sD&XOULH?iiq*qCOj}PYO|P zsj@Prx?AFMp`?%4nsyP#FYE41X&kU8^J7}JqNz>;Ih4W1swty(^-6XC*Q9XR)8@I- z_4@`0Mb$*yKpYbC-B1MFB zA@94S;z71BQ1aD#vuH8|8+CdH40NB-F}t|XoK0klb1ZBbUd$X;&8H(a8^Y zIgs|b-E(ufFtw4S84DlCNp7n6j8Pfv+}=7^JT>r4BzIltRK zRe`ep?Y*{UI3Jd9SKSP-1%PHEoB<@I&5-?fj$JDzcl0K_cq3RrxU5T4jv?6q(sG=D zT)$r}+8-E}1#h0ea2Iu*!Gkw$m&acI4}h7wZXS|yMd!!8A2Z_QLw`b{7}}-gcf65k zogrjeo#h^RI#CYV4+cif!+u+y>QYMCPyh2mH>~kh+G%|Jn2w0j-12__#-j+=mRTf4 zXJ*^ddKp~7A46B@OESYPoGkntEp=&0UQ^>Gfz8}AGj{t-fhUKUdt&wk{9)m<6HNUW zC~$U3&VA}E6BR&39QscJ=i{2cC_^Bv>y?^vMH7XUOYrhBz-G#&t8FhUpy@YANS#O5 ziyH^-h83#0IVBMBKbBCL7@()X+IOzFuOOib@Hl}{#bxTCi7V$V7yN8Cn=AGb58fph z(K#6;jjlCNs2Fj#Pn$hj3j?wnUBT|`8MxM6hCJ4LrdjQ$;H0Iv!tQK!V3Gq@aDOc# zwPrEtw8o1!c{hS?I5X5DJ zp$JG1s?*XyD55B8qS5#4GgKBs849QjIDM7vEfl?SOf#T$vwTIVI7S_^eCXTM!WSWp zq{V_~GV67Y=aLAGtwp7wnwMXSHEc@cIn-CAJN#Xg#QQyO9$>k1PMyWOdlrf;lZ3^S z7Lu?rn)OK6R7tYY%6wcrHsRy45oDB8r-p4&G!@y^@%u~jvQj!PmBO_=`dAbc=vSSb zIG*-QQKI#+BH-`}eY~Umz!vSu5IT{@)03ta?=3lg=%W@5ngz}@zdtEdl0*)UJerQR zfQ_9pfxMU?_BHIsNnf6%*Y|2VzAIcc-fnmuWtI0@XO-0VP}9#3X|HUqH5CobnR%ey zy>j)`Pd0+HhQYK%%HM);=KBb~uB|DC!C*EPhN5y+^UOPv@QUBNgwSn9>j`wX(I@}@ z-ZhX|UW{g)S6H@9CH$3SsH3uGz7{P2sE+tfgjg>~0_v0d%(p1Hh`sE(F!0vkrLcFu zz8a}VM@mH(R*g)4WP-C$ed1sY@Jkmo_du>t`qRy-HhLjr@{`3CeEIJ3jX%e~wqdW| zSxT%DCm2SOerMFduW0OrDwgvHDTi{Lih?g{FsgCU+rR(*iB0plN|?k(5p}CT>hRwQ z_gh9XR7g4%o$XJ^ieHUha~x=TK8-|*OI zPaTjLpc3eQ12DX{ts9Gr!m|4bH;$k0C=u$;w}~kmTPi4zYS~E_yn7PI|7&*Di}+M%Wr9f_ zSOldUF3-082S|`D`(UW~GP{-NO*Du6b`Tcu$KftCaIL-(8h z15~ZNEGiJarPqEqP};vqm<2%>lb*!vw|wbSuDeUIGDzzq*j8Cq*#gQvmbA=ur%ZGk ztAmuvHaY`Gill;u*dbJqtZEpUBprYA1NT(&Jc95ykli=4`OnEj{UKP!xU?OzUW=WB zeu^1FVM#yH`|svqucX(VmD@WBg&p&r&hX3q7h1_*HQTNml&)t!5#3JwEoy~FlMUK> zn4^k%_Kg*==MONS>UxmHbhK#qaP1+ zj_Ut2?6G5lcJgo_CM>t?1&n*)sxyQIUbbqZ;uX5D0;`TcIu#yO$&b|8%kLS9?{lG-}?uI=SAm zpL@dhU3*PZ8$ZrGZ(=p7@OtB$3$(JQZ^L)yGC_xSDJD0b@SCU@_t6C!Oc|49XO%nE zM|on}6HyrR3CF9Uq%Z~?vmhN!y_cCTUwLcRSmeE{!Ro|(a`g}Z-MvpM2s#gM@vKj| zZOnSW817{frN|d_IEWW5QE`&l!gJ9Sd2Sb-`WC5B`@r#7Q42BD>ae7i-qUNH7M(Ym z=av+pWG_pG>zN8rO|3l|abFKut2WO+lp@kmenVW3S>IR_%xX7IbJ`FZcf;Wo7wSJh zAT@rysWQ~u@0In(TFhl<>Z`UqWQ<(zuYx{7(rUJ+gR&ahHY0i?vwC{oojdJx##D+5 zWux;XlT?=1_=?Z?mIu@`ht%Kt1r1AinVdPVG>-f68&;sg_chemVe$GkuzlP!clew_ z)kF70b^IKsG+MmVC+G=xi0$}bdF4%)4yXSBdRr50DBlw2C>`IDu8ds4oui_;vkE^d zpEV+RL}1tdWTl7fm`c9?!ec*TTA=NTN84Km$Iu%Rl$`G<5H0BtZi}0rOrG!Xo(;I` zUCnZH*Uq|8QjLf_N~f|pJJN}Ye7i{iA`e=ectx$j`LDeL+fgp)Z;<~4jETbWzI%nq} zCs>=5F8vf7{B0&wgY$Po^PPm!nNpJ2^X9AzR8!~S;OO_Xe(~ndmjy2kg4S%j;wLrE zP^W$wXlF{@G8gfZc1RT6q82Lq;Z-z@MP7gF^agG1MAwS{Ok)u>Qdv15Kp`j(XDU$M z$AyqMukpRC=ec$J;^o%IY3>BD6kQX@fPZqnycYlTC^;-Exu@{sR^B}T$ZvvHTbt5M zh?RvcJhAWc_=So9!j?~!M0^fB{mb4FY%THqrE{ANT~=M%du8|E{E$(j*un$T1in52FzT9Wn`FWNsOK`QM)%kjU$2&Ax{!zL+BsbAkT{{t8|R-xwLkt5?Wp_zvW zppb%1kQP9en|oa%tSHmYZ(2{ZuPYFvZJ3dCvvl52b?eSm#y(ifhB$D3I5}8mPeEPv z58}v9y5hldz=uD&0yTjhqI57LiOz~(cx$K_DvrE1l)zm}eOdo7YJesGb1oY0LU;m& zvcn;wi7gP&W2RlQ!+NT1uzP|ID=KL`mQq-y?%5AGQ3skVFYK)v;azmgtml+Eh*$nEWmnGI-Wlt?#O{ zaK;5bACW%Lhn0d{2no?vRNB%Y7QIk+j4JMi&X>+yT3qi|uRjo#qp zOaB2ze0bzo4}&KKYZ@-!YfT!5PTN~t4Hk<|ggVy7ugC98tfwg_5=?zC*0u`zW0*P~ zZ8oz9!VzX6-q0OCFDI`|gKvEm&h|NAcx#cO9glRhbiph$&uNYKVa<^6&Y84LzhaA2 zxU%bOl?}{Su6JSGT%%rd?QeFNbmm1SKf$n(O@TRzi-5)NCG@K5T8kDGfA2V@Q;7T5 zlXIXQAe3O`)AOQJy);`+;F4G-=B!hj>-yEfdf#M-RKZRFl8CUb0O8tnpTesSJHcy0R}3M+>7K*Hr~R> zhqH7ZWO%GdwfGzhiXM`N#Y`wM1!c{I{A0CnNYuvAbt&}%jt`_1WqsWd(r*^VyleBYn#*L1$Pqx ztCy54=8|K3<|1Sm>X5{_E&yC7@!@y7P`&bOS(VGCjw*1Zqx(d8WUH(uRo)_aik9-4 z4heOvd5B&#C~1G)# z=1YrH36>Z*cjPfDyhQ}?{f#k=LaR;ol%ml_!6h_uML+ZyN|NfpDPV{S7!B@JuUSw#2a1u?)zBEGA#3Mg>78VdRXugM;Rcz|04JQ22JW#{|~LCsE>m~k<~Ap z?0ZsqIEhjP?foX9f(vB*1iPD%51ck7NY|oeJ2i+U>SN?p`=0st64H0p?DVho|HjHt zhR=QlbqxN9_sAx>Y=zys3lSfXJxwU!ST+V2XGjk(O1VdGN`{p-Qzq5eXjM{vQ2w<^ z9g_6%FX54hf@yo)Eh`xAso)o0qwg3jS&b9K#k0kiH^uR^H<-Zy5sFYa8nwN)k0WF4 zz{+z*`ubs@H(gYXjx^WHmOp;<+x-vfoy^<4=Zw{-Wp;kqB)1Ne)b=DitDVtRJM0r& z`U1)xpfFnqq`EAi&tn_L?zE@3)v53jBX{rX&5^ejg}>=IUYep{Lh3kwonk#|nUF0l>LO{15XsEYMy+6{dJd&+-g4XMh zbNSP?i=o?KW7~k;cBZC5_V;tGabs2-4)8FnbHPR>)w0TrPI;97En$Bsvnw$c3?vs^h z4jgE@p+Zp+)selE556gOj`hCB6@_-n;3Q5%M`3fN7yO5R|*N|m2T1+hhVMCj4}N|9?}B7Ut?d@3LSL!C2$7zIWF zB8@N5p&`z&a}PdDhDi4C;N3S|HYHD9SL4alMvS`CD}xGk=*3wGLPZoDuNxx)JFJos zJwiLs+frdBn=|@IG?fZ*rhvEuV>1mW2Prvak1+7iMqUqJ2G+OZ0t1YBJf>Zx4#0x2 zxT>~P6<6n3vSyfO$J=6|g2rw(c=t~p3a(Z>Kf<3Iq(hU{DB69t1@dhSiBxaJqBOVP zT7E_YEQJT|pVlbbYYZVQd8yFRE|bTWYROmd&O@NN(?_^Z5Zwc^ZC2%7N1>DHbryx^ zG=+^_)b(NUl+y{EjN@ulB8WGr%8LF8kbjfgWh20cdEDR-=>;0v22b)5SD3H2CNO`_ z!A5mTv1p8m8}A&M)|ZInn87mbjx~dhKQ~S(Jh>ialj-6#e(~XrXDBKptBpN}4Z(|1 z92&JNk6P6Dv2g`F6!gO0oU&@yL1*Hc*}zcCoV@s)kI`e`35*mbS%99>b+CQAlWPt& zwE-LHl)dPMxCjiMX)$!kop_*awzkEyqEXls)#sRYx#{vhLQY@Ar$0$i=YX82?&WvM z-^G-!8ZZjco6QszuGv*k8BKab{TAJ9hRooJ=)F$CtFsSiv}K6Di}#dh$z8Jxk{9wu zsR$iKJwLecV)h9~Xf&H?Zb0nCa~M1ZD8`#PQJepSy*BY`2Srv*UMM3;eA?vr?~@uH zW6!BGr``So%=h4Tlj!%a#e4}9jA$56dy+wb7N4A#AbI1PA%3VC{F8&Do!pRy>{2pg znFw&vvg!}YzdcKS!hP<(@Zj(zz=cT=gB$zKh7kVmhIt$n);r$zDXKl#Sw)cmq*C|I77Ac zWH#V;om!3UBU~Ks0M14}aep=uwa|(pLd+QM&bv~`e z*plgMF#=xWgFq?~e{So%xjjI8oyux|fL(;5F~FuD{vx#={szk)>DSbY!T$GWR@2{n zs1-AEY3kdYlp;wuR9A;T%nbp7a$-1~dx%4fsm-WB-s>>PP+m0W#|p3ytRwrAu5T;b4p!gOFT(nNXz&n0q(5B`^Ya!Q;^kwZ=Rnr70A? z5{}=bfO!aO-x(GG1>oC|F{=xyKEj{Q5YJv!)f26}U!f`(4H9Y2c1J$$6ILu_w7(HP zk%jGnm7|_>{mFZKp$59eZv*Rg*rx^GC@!%dQ!)Qg4mlYRCdjONy#JS7U3AYW`%&P@ zezg5)lKHgZ=D}*?d#KZ`tPfg#YEB=EOWeWY|9})LPU6o00V)~?OB&4iYy8|&#v}YQ zT^Z-6HZy@t6L}oRV!Y!dj_My?>1}TGOgFvMx@mTz=!G$4{i_07tbWe59}DxAaYPYW zzGn9Zv&Yz(1#|=*N-#$G;W==s8(b#wzic0;nc#xH-!ZNlN!1O~r@d4qWEmF_RUI00 zm_KFUaBrqN@AQ|BPqnar@aUr`SV=NsUt~}931`$H6R0cr@IhbQU1r@tfkKFu7eb#Kxh6qP+2AZ!SYA)rK5?3 zms`;-iKP}}cKwNbd*8x3_E$pO;!n4`xju-kIpe8_)Nb#2)frwn7B!i2w%gJuSG3nY zM6<&uRMXP=@N&R6fZXwK(wwO-Kdvq7dUvf#ylph><;r!4&Bt#8HK!UYt7j2??)nXY zVu`xJwj1OxCVQ%6{wSwHt0N%cUka7dQ}~jWSz-M~?*7kCP}#6vv**QdTPN4!i$S6) zshy%i9rH_ye!A`bvJJrSTdT%1XBxzICckQuk{;!Q6$40$TdIEhPfk6R&^cm!l{vmG zUnoe_O8j{i*h*|`|79+|W*jxi?(p|}^WqL+LnK?z<${wFwzcpjxdx3@?U?KsIyUCe zzF@a@>Q{Q*{Ev)1BdaeVykBo*KYhK4zw@CVT#WV7QoKo83 z41*B(G$QqU?YdVdY&$?y4uRD81NgC*Hl*W=PONO z-*4|M{8zIm^46d=WM4Mb$)H%I3R4Yo6EQ5fw8`-${0i5o-oIJrD*ESv)6@9(&y_VV z_#I~57k9aeDn$IGXS66>Fcp1}aBqSq_|9{3E7{5Si#dy>b~)1>g9EbLSst0>OU#cK z#TYN0B2BBY6aulNqIThJDm&_NItnTvbN3M~f--u-aIr{*9r8FK`q`0SL&I$qbHskf zGVAzro}n~w9Oo~2Z1H&txAIEiOh%N$B#;)IIaOk=HF-4*R&tpA!rnf!j`C0{^d#$j z(xJw+s@_~giXUHoCKc=+;4-Wu1ytjdMF2;Wu6?Tu59-?P+e&0skZM>n*jWK((kaVq zbk3c>y&_6s^wt>3FJNW**?%k?((B5N{M@l_4wJ8mioe+wbB*tsQKoCP3N;JQPQ5BM zbIVA17am68v*EhWvj#77Qv0#MC9;8?;2S%keQITP-+y`Y!$r$4x_PWvD~m)f4@K)L zS!Bxf@Cm->azc8t5(YR&FP|3?l7Z?|siMLF$t}AvRr;|(L7#X#+%0*EJ(JT?^@CE} zVx~~^llp`zi&Rtey1*TWG6wc)1qNvu>uSTW^uTqMAFO9fS`ed%KjhI93^V8`LKVN+ zrWQ9Qy)&yNluu3j`~H**zte<{?dP^u^0+IN#F5Z<@81Q|$jdjK_?^1Ttmf_9*l`#7 zCXlxTu*jjyF+Z5QCrQ%5y10#A478HNP#;4nSUaIGp7K%|HLY9C$6m5t@ut!~+}0Dt zt|xAIT$nE{Q?80@tL3xrJyBifq!oTccavnJ1@Ayylpg-{Jx-NA#1V46 zY}(e5@S8KhsY(eezG~dS#^sAKRAi%(llkP%t-4i5jHfnBpVj##pY>3)CJCA za(hKDOyk>%F7niR~SFZf!}TbI*r@o*MSkWe_& z0=i9DJYyRke0P&{4^m1qwR>@t6aZ~_6mO>IjB&Q=y1zkgKjZC#p-eEyxrvvCIynA*+ zCVSF$LTs)bYTqqi&uFx5kA-%v+y7ZVg48;*^T<`I>`%(5sV_XJVp^~oh(Rb*jIEUd zh<)&9TX=g{R6|pMy6+I~?MT|J|dV?RI z-ia$Dx?HMKxOC*O*7y9l$&hP1Q1Y4x>a|=q&La!XK2Jm9q*YlQ%isIw-5Z})3=Y2P zvI2t_urmY^(|y&(45}l^>1{)?wla5BYzuS2A`_;Ocdi*JEI*@RoFCR~d3|f6kZ^w7 zHE-nFU+SA$1oy^QQx8Xr&o@uup8bkbkN=>G^TQAbm44b=5^bBhUCUR$XLXvrh+NVCEL%v z&f~sN>q5?NDnAU`;*N_6W&8(-?^#E8>Y~IR;}~>KH9xmGaP9D#kBGfTI=OAO z#Cb<`Ljsaa2o3lG$jBu}wmL;COOo1kyxeI*#iA4n=gU&zgLln2N2;5VVw0|2W!T{K zi(;H828?Aq3Qpw7Q29N)aVb^P%1VMJQ_YA;6PhXe;L?L8JgTVV$?rd1|I4!HX!n*b z@xF4=zy;X5DHkq`D)EU^&*GATp~Xa7_eR$zr!cj86arv(Z-L$5X!nzv@wl2#%q*c3 zo9s!N-c0LTTwd`w+324ei5Tj5s}IK7`a{;78L-=nmdj6XAFO0O(Z@|sG!-T7dw2~p z2B>;N4SyDf;@HkB|2iYkdSUHcfl&MwpFz&Xi*xoAuNg=QS7_D5F)wIGrnAJh{iAi;AwWNNpB>Vb{e;4##+@56dz}#PZ(HQupS3t6s3hg)EdlQQ3jsD39`np1jj8SBc&X$(iX$LwGYP6tpNx)5Mdc zq~yxjR*gD5tF8_qT6yT#4>5kX@GkLQ9PAHCA*fbC>i>v3&v>@KKkUb>m=UWrDu|KT zTdmq82$E2>ilRni6s=KvQ%dZ;l1OZ7S6jQK+A1*`YPGazt4oK`-<|(`Ke!*|K^`UN zyuRmrKi74=FGG>z%yrJ6>dWCrxtB}4&p(pP|6?nyTS)F(<%pgvXJwou8Syf66lia4 zg0CZ3NR%@ka{`*NhOXu-598aGUO^m@M)d48>csBfO)jmKV|P&R z?%)kLE?~wz)z!@qA;fYwbJmlbm9@aNlb5MVoKKw#7S_I+e&{YzL=h;z`7Nss8Wu1k%;R~(bMvq-WM_nC(?RR@W zj+-(|Z`%aG_oxwF$6-amL%SEVV{V&cg16aSs$46IjG0GJ--37rSH7BLPYh-)HEvGo zQeg8}Cp2?y;eN)$E_eO|*bB2}u|DeuQDznD3lZr zJ*hWj$-rjLT}PnE2Am88tj-JcWEsT1Jhj5L7iPaJc5!lvsasOb}gP@eLc_v~TpbWrhU z3UxuDUckJQ;LpNL;G?~5`+T(3^qYCNb)2FKIa%?}Rp%VuQ&ba1b24HzX$rtjp)P&d zmIDCpzrZ)5_4z^PG0M%sY9~B%87_BT z*v3mxaFBJzNdHzcBH|N78D4Fv{)>#n5ysd^`^)ElU8Xgh%%AH?A-UBzJR)2^42cM<=t4aOU zB&A{6w7u+gf;ef!N{hDrgLENfYi3-$X}pFKNeg2GE}7Ula@PT~M5_PDd6P8)!8EI@ zDEuBWl_{L?>|D@O=CVAT1uuI1P=a;{Gg35n8IS#<-T(3Wr;O=PW)KFKxudXH78Iy* z46ohEg)%bb5t5Vfn6NLEW&H<#jG%u*gyD1&QD#-CC8E zSq26(J|P3$Z!e*>hL8Po2egekg=IrScSL3du*3Uwn;eL3IhsR!K18a6j1v`pXtB_O zATJ$uTq!bEd}FqDVVE3e)B_Z~9+jrw9n^+Z+}j;iO2%WFl+(O$z^6*bkX3fG}C{h(MFM3aS>x<`7Txd6G>H00Jj36R&Uq^_3D+!W@)+68+}$BU zW#(c$=#3lEkb+S4WKIdYjB07lzAUe-iGE?^I@KE8+UFy9kMWL(RoA`%Uq4e6pox)j zYcrd;&uKkj=nzd*QTgZVu>H{=PnYB;{DrAz{{c3v27gy_%RUiU-nt`TA--*0b0L)C z(lwh?dhiBmJl|+@uoqge#y*u=>-BjuJ4{QG(|fu*{^)IY9)dm7_4&|Z|Fyo=P&MubliYcLZJo%*lYfT8){%(ow;mXkNj+?LBAenM z5(iYAYIkX|u@!N@fYbOb0!5U%s%;K`+Bd4$5PoJ95kQS*~z`|R~y-E8vUu zi7VDWYBt8?=rRxFEAeZy&U2Qr{#GBv2BuJieYpIl6;ba;tuF4k(g(`d7lPfrTa4^) zh?2uW-+If|{ps+y{{TD#k&3A92K&z*lD!0m%UdRmy9l>-{-0`9 z66%<&i`5*GT* zl6q)XNrU1+VUiURQb%vNEeCTKBHS_PBX{d!9?+E!GbW35(v|^qA1kla%4ebMc(so! zV&kkR$`M!cS2ZMVG>!!m8qlQ*U5i!5FPgR4H>z z9a!UjTwX^Lh_0On=6Hzx823z{={oHxnem=@$M3zg#`Xom6F$*u{;f*O{_=Kw8h2W^ z)?>AV_lEVr=V`s(J;B;6%F=<DJdXv2`EK^_C7f5hqV`6cW;V4c;>M(py4U&9o!zeDT~cTVI{%K<;6i}*>by7I zbt(B$QB^GXaRzRU`Xmge)ejN_M7+dBjjEWevvOmDci%^>ht2_2c0&By2v zqjP%*0!4cDoOtWzb8{AWEV#pncK~XZ2m#-nn#%8yutvahSb?8k3Ia6Far1ZWf zTNq4&PPR)ME5dRbseWmsvAj==ip*aVy4ANd4|XI<1i($u2-{2tt3_55w9RN4<9M2d z6I`GjTw1wkVvMY0=rsb7lR}H7>SZ0&qhRJbo`hBf2^R-qovS@;o(QmbFD;a{@0rv5 zJuATRFjWBBUN+`bK#@evzG zd-3YUs53ZNwrfqiiAmKlC3BfSF(1AJK&a|wUJgIW*G?apel=WRx_%GKYiDfa*@+ti9Vegh=991OBL&HdMydgrqZ zr{XQ~3T)#&Y8{!sjzH8anlaCkIs_Y0Z>x_5SlVpFWp2{>n@@ktoG=_%62wo=0e3+4 zgG_5j@5=Ngq+HyN-cPu#e6rb~d+_3-L^4{lX<{&{xvQ_78A3RfLF@Dn%N`)n?r`<@ zai}^E^2#-nVtPQaB?RplYV7zS&HTX~i#OxXHGgh-jTv(M2Pg=vUj8Q@aPri7ODz*p zlva*z#7+-ZAKTINLK|LkpjCBE46h}#AVkP%aQd~AoXCu?9x1mDlwhIj?nHB_1j}S1 z*!%b639xPwbB#c4m+&(y7pxVs36Tca-EjRS^($)8kW?%#u<_|hZsumLbc=2YzphbH0UQg^bpsgj><&0lFq2OCrTkV5fPFcDT}#+rC!>LxoPmu= zNmI6c0ns{cqtU#lzEGc2x*W#@QxNc(OhO^OiABWq0iKWRW9lwK+(8aC0^L$NKHTa#l3w85yr z1$7}-Mk_10%~b@(MTzfLj#P|gqz)|$&Vl$|LXZn&251yt7%+y$B0gfb{$z)?j4fyS z&AS8jGiTFm`@eH(pukqLVHA~M&zkD*Xl;pBkX;b_6#vld%TB-TRZ~sP;5G&7WeKGM5?9(bYHYpWGq0RDPxH7q z{g{MPHgH}6(|E|V$+@_Hdt zY{Mh1tpWvV9jLEGutX2Bt?$M3&#j9tAX`0OQwdVfaOlB-;`5!UN1uOfX{fTNug2G$ zk7=&7C&ZadcL7$z1Wzuqd4vmkDPc=KIyJdGG=Ct}L$AYhABtOO^R{d4o2l}?rz5e9 zeJyX5q*8ZBMLD7UX?i-SWRrvVV3GQgZiYlM$lwGOmAb8yF>f`HpP=(mhxN?dU5raHXr=Ls zG>61dfUYK^#xr%j7X#KJ#PB5D_SU|I_QA|$ui$J=i*&b}0Vpygmw>yDYunIy=a}Uc zH55>OU4TNQ+6#T)V^5h&$Xs0+{d7ZTxARB0CF8qCMRZ<2XUNMxo&4Jv1~&imefwSg zWY^ZiwJ*t)obK#_cPpBm+#-#Iof5O8(2*txR>^Pwl92wL5~S@`n@!!kP4p(7TZI^4qz*syFQJG|4w@KYn6s>a}wq?uoV!jvZ0+>A1pI5_L1vDmI&K;~w8 z9d+UFqMZipjk81`uFRh#!#9@+=K58AICU@?+Te~7qW~8uF7i*tIOPa@-I?kJx`-`Y zx#nz8zs1j=k#f{+id=I>75ajSBvyOS#^OU5>(Wrvx83>B{p=|W@_h4rhXRIY3=)tS zTix_%1c1L(<&rcbD`F~dIx3!S-zD73K}2K@nPdWhqU`@{oy;}Wi&?OA8m$e0^h|aE zP+-^?{cj9SlLkndFSPLJ#9_YFD^eBV{yp`rH*u0Or@=?pO?j;;1m*e#QgKIyw7iwB zc>_zUf+Xu#Uq{yz^j*njr>7-OZ_L%7hdI$fGq~uF5GL8dcX)1<7n*uE9n{{{#bhrj zifKIK)iai49S_#CFOLZiU z3ga6zW|5GtVIJ7&b2VB#8=!-@msrTs^l0wR9bbn)AVNd+ba7vvIy<9H(KZF zyF}%`4PgV>nkcsjm)L$3_$`?Q&LfiX=TK)~9)2u&|8ejVO59wJYhJ6ll!C`@kGUWn zDIOHt+~iEAQ0RV6sK*8S`86MSv4VudH|{+sne@ZkJWI9P{Mb$`P#)fV;Ovj4+7IvK zq~; z@X5}O1e+wdZHV#mm(_5vZDQwM$`ETwo?}L;gv;Gy>aZZ%wim<)VWMdU9Or3IsJiJfwbYGwTE5O%pmvLr zDXe>;iAb(ZBaw9+(@>j@8d0C0!6Eah)@E^W-HpQTp^b^fjLX^XLA4s&0rV~7eP zntApl<+@muE1}zN@m-|jk>avJ2Bo4R;y7ngNkC@ZB`b&P(V@3Y)TLOEHTK>UO2t%8 z?m=iSM`FBZ%Ahodlz(0w-^h&9x~t;d>vGb)jLT&vRCXVrHj4wRSxDz5T!nOSkn%;J z1r{w0AQ$1SSgaR4D;a`##ia7yN`JZMfPQC3;aPxm5s^H96YHS1R2%;4zS?IOnbl5y zwi_MrC=T>T3y8|lka=_MRo12%1VZw?u~4@U;v{m@R*ALh zjYp`PQo--Qy{nMR_Pa$5MnE_;=UKU$lJcMdvD6)k@|}e3&AWL;dfdtkSd_KNnU)Q5 z^{44RbMRpoU7ocJlDoC2DB$RE5y81cWu1Bbi71EY1lf)Z?q91tmI^Ul&HSe5xAZPyM$qgka9%pAmoN`@r5!F{R#rH%l)`CRZTgbPF*iZ9L z+nQz#xus|mb^_w!t`@K4hvO&*Rbz`CBXVo}#as8rfVDxIkkY`AWMmI7GQ z_AFG%hX0dUeu&MPwZ?OD%Vp8yxk#;aWe{^BS$*)MpJi8(M5mCAaM5bQ_C%95`aZQhF_Rnqby{#)((i7yH%~Zxe=YL20@K~DM zh!t78z?4oI7S4MhG|p74_KFjqh>@qy?cepJ2~#L^ zvX7FLBOYxajXoEYs8<6D>TthVwiWIZ?tGv?F{a90LZUatIc*~zd@Je@bzeGWQ z(16};+&T>h#Ic?LmN*0>SJr_ljuiEquQk4zh#JItSbx?g_u=zR%(xJGm66aht&0+J z?4AzY&6_lnTKT3j2YTi&_`_-k`vOjgwIo%z;Zc9sxd3FnD*gTRSge{q`_ROL!{|DeriRC`!B9kJ0PmPBJ%Y3KCY>ekw`g%ueN($*HyP+p;+RnEXsv;9OPZS=Z>Y+sV&1B|e=*y=~ zrmJZJGT;}QvDsdWuj>~G3y-(v@UpwA1`myE(w{D)0{VvbLy6m(WkFfoGT199afEef zC3)6n#l>=NjrWDu^E>?H$Qeu6d~;|vGJ`dL`(6?0eP(|3@iXSH-mNZuRoOi+L zG)CG*!TjWDznH4cxY6p*h?g2&yEx=2AN$Z(rE~NLamW2u0`OKv zxjN}F6GzLEwZV9`r>r)`sh>er_Vj$nt#5MdK|EY{#VukMZQuEf9I{o7?LzC_TUd!-O zLhFYKS4`BQ!2zP!d?NREU5;OZg<+$(VCnSvD?@T}%}(MR*2T!?T%?R+XWwdG-9`W` zZn+R8|IKp4Nl+3?+c8FCzMq}k+1&04GW88X%ak?#bL{Coki(j1fs77t9efOJOzZfu z;|5M-J%5pnF6f{*65)WkNp5`zhcfznCdE&MjGA-BYg%UYU$YpeewG7lUqQ=gmDSqj zj{N%?r9lqGpr1I+IPP@b|C?GUzn~7W_*zpPleNhb7gFe8m*wcBKQmuQD(^|6I0SLe z>5lZCw5{;C$Afn)sjUl-hgvQ9NaonVKfs$aP4NvQ=T%pDg24kN>aLFV+3aTiE_RzR zAI8+mqP^Ua!-daJZS*cSlNz1m{`wcs#Gi-nSkSKQONLwyc*5e}WbTeug_f?Fn||#` z^T5w_TG_ABWF(b>dDxYMP$V=qp+j>Y%-Ie zYk0*a#aO;sd2V7P>7&!dQ<;o1QY8!A2!tDe7HlL?w)D4KT@Z+)Ka9m^x5%IfwC&rfp}O;Tb&KgUCLQ zxDkz#bu0-1QUoIr2O?{Sqqjburpj-@umKodHxrXY-f%A_x^vKw0AKqNnC2SEqhCrf z3V&%2TNEJ}7lcO(0*p6OXh@;JJuGQIQ4MX6p+Ex!tDlC<=z@2X`rQgqNzy?AXc~G^ zDj=pln9W4Y%Bg%%yzaQ*r;NX&6*s!+<-DVknrztc{gC)I*|+WWBl3Y&@g%o+!al_s zU$SWuA_>4-_M471{%sS}ggcmLsbfnv*zYESiwHkuV#*KZUyDL>gWrZ8{6&a7fSyz4 z7`!%hLov|3gz`7-`Mv2hMdXk*wXNT&Or4KZl=b*b_sxF*0rSeRLc8sAW9yzxY(`DH z>P7G{$w=)?m4IVD=7WQfa8Z}Tb1A>B>e?p1RKNF5M@{(ue(XG__tV^aSE@iStYKQx z>JNZd7;(7vJV)}XLJ1=^{2ay8q^IAs!;lnsn8pi*bhFfvvND6~*mbT$;ASrDI~89f zAZ=z+iEKdO&xLnFY}V`?p59r-Zv)}tphyLRb3RwAG66BbsI~I#MX0+&wXTz9s|>bn zCMgLw`)r`n85&4g5joG`fZFy5?qn3GQ;g3-M${E{TlS8^a_=Q|0qwZ%=niIXzy}et z^unQCt+DTq9XcrEXE7O5{^)?$5Dzz3qYx+*_4Q-JND7I=hnLKs%-G8y)U&ZtI4Ejb zdp^M8W)Oa@5ci(>hij!qyB|rEe((-_B}$T-u%uB`w$C#en`y1h$Pf^w)E!*@==}(P z#n

#m`?NeDt{oClj>qu@D$5s!I$<6;gyLt6U9$7_lob`>L}Nc zYf2u@$bwf}83S}z+3D|OP#(-}k*fwQHS9D6t$SEGI@3|GPQPTS0Sx&0g}E$g-I(^h z!1Pa?1@M~1+T$vd^4$$ITXJzOlu5>nFXVdv1&-fCfR4i(?$#dm^pM~;JNRH^+3?ub z)OQaNbdyYQAIJ{%^tEp7)E%@q0TGV$0Bs>fzMf1sE~fOpxm*iw z$%3Xdi%eYsJ)p1r$Dx??RnzAI0luShS8eZa@@Ieao)ft+`v7+Z*+{L?*71w5y^%%l>#12_?M0*su zQz5NQ^pQ3trns&sNwzxQeIT**8ZiR5?CZ45rA|8Vc^2;{A_=X zY97Lz%C?^jZQ&v>HD`<@hhR8+ZRsM)bH&wQw|+vxH4LG-JF?6Fe8b!SvUt? zf5%1D1+VUMuEc<2*h&QJT@#f{_whP2Tmv&-or+4q@34Ew`64LD1d<73WL;$v@zALn zbQ}v9!w%IwkznrLX9Ef;)83i^oMMq7=F|5`ezBsPng2&{(amQV{5x*7vexK_+zkIC2K#yoSc2R z_!fYiGrjU4l!uV7V7Ku5WN->Vyyut4OOb|GL+xffH)wu8n1X@t6aB21f@_~_bc!bp zq`t#e)CEB3Apooljz`10ZCFW=bz0J&EZJBfe&?SY#+lo#jNCIB_kW&V#213S;QI4rP7=5O_-V7 za6yX#0 ztzV08{|9*A_x6xfGZftNvZV@L3;~)eG@;p~JzK0;=cSEY7|N(xFq7?$dY8|C60+1E z*ZxC9pDMRMIl0e$FNzWJI?7%5UfYBmdH?DgsIdchAc=CF%*-$#ocy?BJlz0hSyFLRuj!jq;S&~>C zD2&Y~e zl8?JtG^nUjP^FD6EE_RoMTV6PaOTqj$NU;S#enqO4){33&a=6 zjG7iji5OpT!M~6?CN?X3XDmO2j~Gz4;&!*xejWgY{SB@SYT{D~3{v!*M202HQPG_d z6j;$-64|_gPpk9sB#{CMFcU?Md-H$=!r|Y)+T!(Ga&`F{vx*amvTx+g@iM zVj7%OmD46(JD}iin~4;YFgR805c zffoDiDOZaU)v>u;kw_~d)zfP&RnP|ZaoKl7rZ;kFS0_1i@a}Ux_$HSgzEESDP4mXp z+&{1lGpX+LM?uBGvM&haI9+B!fR*HTrPx>fG#v;}^z-%r=NK55x@UGq!LG+0sN3%+ zOQ*2?=5==oO2V|Sf;O|HjAhUCIhtW|l-Xfl3kc7!=!|$=7 zewEA=^K4kf2MTO>*MD!L$;6aW=9-Ur86wbIc1 zRh}u{KAsK+Sn&n#NMA3~pk{EItLGA}%{@b7UNk-WSI~c9tex2V$Yfp09b4gp=|GBj zKC=g#pyrhOPnfDWcOU0G(7$O&$!@xPJt~P1IX87N5!nMhUZCjJl4~-Wm|u8h#!)p< zfqbZ@%)mDON!!0+0U*dTz|Ujz*{>?DaRC%xMZtnU@z)vP&;p#xjbps_LD#!JX;*K9%Y(PiBywY@j_AT^o^I^%nL9%=#BjR zRmS>DR21X;Qz>hthi=_vh)Gerdr5v{c4{_EcE^d&}yI+>haah z8nf!&jB&kx)iz?jjT<&&Kaa7mDDirW2^SoW{V8*X@banbz?81H5fwXTTP{XWATnqH zzFXy&;r9|XFI4{#O7|>}$KxBywrop9{U?oY@q1~NuYB=|+_Fw@uNKghsC$3{v73&e zYWI)N^gP~k106E87MwOqSu^) z)e#VD1y3P3PfnafRyGua-}9zo3j7=#-S;V=5UpA>?~(|qO4RRXI~(CC+XMFEwpF%vxQx@#|U>-NSR=UcHB|8*T399_#WiX*EYuoebZ|q0c7@gp;{a zUliH?oWKJCDyIR9bDX*roxS$o+1Di!$>{dRJ&^X{;G_3p(;ATyzH$@Qa17C$rL#fA zX@o0%>f8K?2L_GFc`Q%gS-zf>wq`F|s6b}x z`4?&ppXGK8zZ|)+xy9P4H0bY}zRG^3q`^umTuo+oTX*5!gE_51@N0P`#?^|%G}qRnCp6=A;JfG$)hkoMl`5YMAJ?*Xn7EKV zNDdAteum%l)9X;gk||74f;aj1`$EmA@Ox@we7pW(2qm=>V`ll*m#_EN$4>7|blUAo zS44R8E4-E|fLq(Ny9dPJBdyf?3V8>9*|QAhh)#a1*x9xN?O(Gjcj~&nRcVxQJ#OERAszGHh8t*UtZ`nvLsB7F4lc3Vo^2b zSZ@SFGK+l(?|U8Bw<)H@W1Uz}3&{$5uTODTfdp8sJY(Et$y?1^CD!z0v%zVsfAFy%=U2`#(RI3}9 zx!N3X+*j>O9rK?@5%}1ttS7%z=V&~fwx7%w*)VzhP2Ujb>_Oxw4!SP|c1n*1pHdRRRMvBaO1)OyJ3r_2k*jo*POFt~LIABSZuDiz3cT*shVhfIdY zjC#{2QbJyJy)dwqAdVN`rG^jJoM}Un}L+G}P~U#M-+o z*GUu1@n*TRv#*Z7#xKEtk2~~cP>EX%hOVvoSF~(LVVQZ2UQf&hthTQtm-0B8FvSGf zBICHU_K*&YI~G2+{l7Po*=z5Qk`HR#T?G^#wkb-e;B#AzC4Vz+%7;-y8*r1l2fY#A zdBwpGc!Y^${&KK+-8AgcF;2#c&VMo$$aQLY;ZpKM`FKLi67`L4rZQZUA!>ljk5jz` z0-_x?XRHqg9NT){8l=)*pY*e=RD8R=2ZIGPH;V`K@~A+_%^|TO!7(A!pSR^ZXV`zc znxM0`$wd|9+R!#Q)DUoi6V4r8IK@m(DuqM&=2GbX|=Wkc>>;F)TyqF}znYYVw zoV8_||MjL6SOIfAtEr`hi!QEBja4G0nj3ye1;34oknjHIYL!PmULw6Ak3d{Ph-%SkZ3%zbvtl|ga7(E*m@b{nq{a`99xh&XO* z9nqsJ(rk+Vk+ujfx?hP<(;V&lMo++wzbii!dc!^T%VNd#<-lt+^$*3Ah)V0RcXdz$ z`~pk99~E)t&O@tX(5u|@(QQ~64>TVK9D&LgSm!6xiJYRb>3Zl4d8fC58zeH)Tn(S^ z2vg~_0vN;ghGPpyK3r>cye=R~i1B&2E4A26^Ne8RS#7$JbCpjy{o{>Mtk!3hXt{af z2s})ek6jHoP#bjEj{-0LISPtue4%B(5NuhIDh6}OWRBz=MwH=gzG@3mCQ53veJ5Ib zkj+dFi}Dh0vi=Zj8^_pQ@z|BPX20ptRI_a5j;_ovh-A1}i!S{8>a6a?jQR1_(#P9& zz6R(wq?q!#i>4ow^CJKcO%k8rntp6{_YRr&?ktupy@sjz7-#jndf0i!Tua|;^=t0> zAXkH4wML@pHS+ehiBN{%O-qE>pZ%c<-AL*J@olgan%^hD#q9VY!|hpPnlbOl`a-O1 zF9BbwQRXDvC(`Oy1nGW1AmkwJ?>x_ZftjP6Z$Q%gkIi-z3jB_vm2|2L?|-^U1Vsi8 zmhpYBJYRFdtxtGK?IHis1`)M>ydVBmPbMN|{h1HxZLNEw3+o6MxQi*b*Uyg?OeVP~ z;$#2_ZISP5lo%ql@+_^r=3~|9t=`7*kD;`P$0FhH+-`iOmLxkBP2%}FA^FTls#K=u z?BlBGwYN#_l%U|YVwtr4B9l4xRyG+$rtsys&7o4zgsSk8ail-4-_JFWks-ga3DI+& z`Q@4R=J8O|RzQK{wf6db-*H3NY=j$I868W=)e+=!J?lz;^Ev*DWQj*1e`l5v_M;Qv zWa*C!LgkJ7w|C`b1(uAVk7_I_4jh`Vh%`SOFy1ROAwE_N^UZGo)-(& z?pn(eoilzd-9sPY8NOxVi}({hx#Xk?BknuT8Ib6xvDMF`DIL98f0Z;|*edEocE1a_ zx(JDhDI`=LLX20^LermPKY3B5kM~Q`eC5!mrJPz2x2&GrG2^w6Xx85HM2t>HQXp~D zsbMg1K`nwb#+Lgf#+ZuaYd@#}vh6shU?T(+y=DTQ!tS?K1{iDjxlsi@Bl_u@{=21rP}lpJ#c`@p%NP zEpF?p1sQUhLxY`?|1L4_GqQWqM@BsY&)bInG=W^3`1?D?=_RmXd}e}^wuW^J_2yp8 z^kH;z{59MkmK03iXQ_xiN)#RGf#YJG+2~S&Zd?}>Gm?$Xm{1R5d&+BPw#w1ainIq^sn1EMX%oq zh?SJ7P7pt#C=tz&sG!Sr*dGj*d?GNvBNu(7Q_M?%9F!NveLnh+ZuEUoT(tNnCCKy% zXY(xzX2{{(=l)rP%^HxV{XypZn8(!+8POO4!hkN9E zm1RKnFLj)`9@d0ZE?_P{m*Iq`?@LcJ8-^t%Nv+K%q7zl*7VE4y-}f=Y)ER8D`NF5z zU!6&;=m9_drrcl&2ODgF1Eo3$jvA!eStrT7wG#~!-cY2mpeZx`fq0@*Jg@yiOGFJx zc^EkSqNsOPSGGT5%hOI8N3oONZWeZS5a2Eauo&W%anJsp4bUEl2Np!-w>mdA*q^#w zfk`U?Z0BNts^>btcyXjVQC0K$#A7!27Ln*OhUGJjl78;I6{0I2{Y%O|s5tG`&+I*`b-)qOvUC-rb&Mbi= zh0G*lV07~Bg)H9$*)_0K$d8CMb+k%?`P-4XS{13EwBEi^?r=J2k%r#Pb9yPB<)tZL zZCBB=ET%Ywm~Xs@2d;CyJXKzc$QEfd7`u|dudY;?=q>7i9ZrunMMz3pTs+49_&6~EEC-+=}vp_&L{N%MSx;GL0 z{KYyDDdaO_D+ouWZCxO$eM!%aKjBhXc5By553J)u>^mL7u(6=f7(y|Bom0IRE%w<( zic~aSOK8hi7@tbCMB$uF8%O8&zL5%bR$Ry=Nc+7yQ-;vvRP1(&laxwtMUJxf` zbGXmcM!3nQ61MziS3Fl?(XC&!d2~?V`7f2BYl>qc=X)dcxAHrxB%Vom+iR(-U+0~a z>$JJdH`R58&o;wzAZXD!CmYpJaoeG7#Ox<9>9Xwz>_qYbaeJKAb(x;CCY1i zWhZc{yZe0PPWl}8duc}232Dg@k%`G&gUMt=(UGS@0&-!XR&(WhS~=VBHy2&t7ZXea zQWhHeFE{lw_fRAGHCng)%8?2kSJ_Yv2%+4`&3_-#iW$G|boMMLJlK_^jJz3uCiP^l z{3{U+$y#h-Go~~*CT!W)A=fSP311pTRxIBBeg_{f^lXhCLB88;X#5-OvAb&?J{vGw z{<76qVV=J@fYkolX{N8+Dl%-YTrXdzVn!)O!~bu3FZ`?S$UbT#de}HwG-bBO>YL`v zQ*fzkz{^W~a6fh@EIayLYj%Zh8Z>vr+UCp(Y)O(Ws}6vSd{zjV~j_!xU~Q}J*$ zO;wxKD2M1UM}M4!98N}>n?8K5@loKOYWmamy>nSUq$-CyoOhJ?A_%Fd9}lgo!9cpbBpjw^&hJO^^O(-7TZ?b>ZvM5=X334 zQ4~%I2kG&TGAk0Agq+6qgg^Ip4l_Q_G{GF0#4HrW`sfZyM6 z0ANSCa>Id!X-2KJrQyM_^6<-AmxvCgS6>)CGs5#sxd|9g8ywG1PD=#)fV+Jd9jPm{n>Md!?ssf` zoT-XL>6wj{#92BfN?ZO1C@D9HNm9pF4Sk4@cy&JH^hW+)-F_v zKeeoV<6U1u_f(YG2XY7SQI?r6qqd4-&ZS-qzTV4bUPG954oa6B zmFi2_Utk>Kc>VU!U)M~h;@;mA7C`?_@ zcGlU#Z{%PuUWk9R1$}~~|6X7kBZM%@47!~s*U~w3YA|AVA%wiJO)_@1!~CmE0-CE| z_l&o#Fr8x4fQDVajbqQ=#eg{$Y1iy{%t zj)A2o>LO*JaymVKHNMXU9>CL`T!nxKw(*HbsNFD`812Bo6@0CpHDU?xN8*K8<>Ld@ zK4YL8hrIX0;+{8OEXBhjFp^*boMTB$bwhSMD?ZIhWDwra`6h@h;GY8sL&1yV5%mji)kkzyK!WD<`$rjtd=zNq3}@3Rk6V9k z2Hre)-sFolI(5SFAXB&{=oJN822OD(WXh4(W4| zu*1PEreS%?jk`!)2cjE8B8>qrqqEUik(y6~_N=l$(cRVvt8!@9fl+yNon%pFxPm&N zSy1^}3D2w0r5^HcQr{F+l^DnXKzXXAs_k72jEEeA)>8CzqO^hVST+G9zC=Nmd`o9> zLT(Fe@w~c@m&_tBqLE;j0@OJuOAN%TLN&vjP%@0ui$qiwuvW`gg2^ZUK-A=qnAUx; zn{`NEe|<=YU5Z4P^L%rl5Icvj$RY7zq?vhknpHVi3RW%2QW1E|m~={(a(^j2KO#Ko zufEqk>G>25EHl;XS`0@D##bm_i2DOn0rXW^T$f=;DHRp+%$>fm<$S~8Ni8BlctDc! zAVK9v%9`!YY=%&YMSnbbgaj5sWis1=^7*L0ry}RML8xWj1<-Fzg|q{}l)ZQ{m0qI1 zD;+ClSUg7tCpoO|93g3wCrlb(x&RKSP|6~X8QORZs{;y>{JCQN;I6mh_x&lG@86L_ z0bzRU5|b&qI$LIk4IN!a%2cLpa$5v;IK1F6Ax5&*A3{EC1CkSl_Ssce(-m&{!tz+K zpg*{p8dqhrhj*1si(oWzpj#kuSI>MQijb4;`a~-L5j}*wMn*?y}3^X*rO4+Ay%7M3Y zMg4pveCU7xx?;){HgP{%N~eN=o?l5mfwg#s#BP?c!rJGdwU21*aYL#=>i3tW>nCHO zU8?bCBZW8lWghQ!`PAU>eiTnxLX^~u0HX9wqe@7td3bqpI{XKtYKcnq>33)u4tErF zyscPqr-*Bmxx^*;Y(nl?bK#moNxfGgP3boLdHtN@1*;faBysaEa+mF+msgy!qVk9Drg~QKvuyqvTMAkq4ABX761~&VewSD~r!}{sXX$@&^MctCTN{ z7TGqIh2kHU{E-W#&^QY!b1>#bG4YM_BfpDFgd~4vLLgqk3)w&W`b&EP(AbxlXx-Kv zrTn;ZyDn^w3@0UjuFI3K&e;0;#s}6fMp|C8zC$fannlob`b+F=1z{|a@5 z1bcoLcl}YDe7fz8_u*+a6$sOl!3I!KMZ3FJo=NL`N;Msk5w*z)l`4!_l!7%D&-@3V zvHNX4WIv)_XA+PE5h}@7IT%^6N$bj7eYon{_dY^+519lYo#asT{CfQPea*uN7QPOL zqWlfEJrwGubO0NNEu;z4!brFdsD5Dv0u8d<_;CMdh=RHUAj$I}y(^-*N-6Mpdo@r+ ztEpxshl$n~iC+@H>l3%V=-gXn_S|)@efXcYZoZE|1ln2?;9Ey~)&92w1D zZDcUHH{)RQ74ouH^dW|AWq!rwM zbg9X2ZN1Ho_VeoVWS40kJ`cJB%>50NX1x^e%%yIA%_xFjFtg%vXiszTuiG4w|Mbm2 z`m~$V)U{yIK|nYq)$i`85W+E{0|C*Us|*i-LEqA=1v_wrKq zui-&rF|jlj9Czzwmo}@WU#pj3t(Tjbl+HOed@b4$#`&s=v6p;oYF~z%dJr{fn&jk( zGARj|MA|nq#HZyo6mfKn`Rb03uRT!s5eomrC%fuoxB8h|sipnCd8pacWuLuBmqxYO zF>gr<8|fRalm(Ij^eFnt%H`rMo*(~&9MeBDuR$R+OD{j*V%^DtG4b)_&+4Sl4wKwO z=s-xW>j5f_AIDwDV=4_dbK$yaNSJ-7QMF*pbH(|oNa>q(PH~O|&RR!D;&ZHt$D4&C z@@N62ni2d#RYYA06@5Kj$fY9~KKoJBnuR>2_I!z%mKXI=efd3&oma?H_K$Rd8GXej zp9}t)L?!|#?7qApvZAux1Jr8l&Mg#r;lNc-u?K_2w_<`ioD%3~5Q{fxB-faIj$tToz9m_%4?kb(XB2MN9`HMv(COAHp%V1?WSFh5@ z6Otw0$%}n&v@YPh75Q(6Baq)FRrBh3yAd@Hno@;|`oC1Q>f zbfJg0z;f`58KqOK40R6JZKy34`8vRzWi`)6Q`CP*;J8aV1P0V}Z`&BC{$8UqFHPV>w8_s2HeOBd(GvHo3BOf;}WF0li#fI?+LmX8Qz*V3?{aQ zzXd$@+IHBnJ@I;NbZz}1<5jeF9I5c@ghXF}wNqeXb@U2}(-BGW?5fa3pUbRvrvu)Q zs}Mn(ts)Pg{e8cif5pc4jQGphiM>YHr-uff@u}{21Vzd`s&)p(W7t#ALg0lMcsY8& z{Dq+D`&7SUdl+*G3(Fm-+H5m56cWt>tPN;gJxJoDEE~(E_XU^BGR1NjQC;J7QXS<0 zBEvCBF_Xn7gT7xMGK|`!{{g^9I^ikTK6G5vWP(4dh=0}e^Ovb7QEqDP2L*S{Rzl1a zcq1j*=z&PFK%x9)H|?>x*S~GSft4Ban#blkO<2+3S~H^o=rQ#}9GaZH!528DJHP8B zOu~TCQ$IqRK*aqfSO0L@z8>R+Km!6_*FpkbHT!fTOG@}--`o?H(i1rvpaQQ)jA@^_ z1ZUNN>4?#kpn?-I=wBL&k_RZr#`_VfF;wgQ6b)+pIo4tr@fq19NeAkJQ`NZ}TP2=t3> zc1uO$G(mVrwWOr+b5^aog^S}s3YjBO%8yjosm{m%>Q00jMiUy)2|$5L1+I>|JyBeH5z~ut@WFQux)`QIL(04{WZ-JiS;$@T>UAiJBb2m?KzC?vGZ`?piuj~SI=GV zq69*s;!zpdDrFz+SeFAeM{f=V|Itopcw);$kK98iQGsc82@kD=Br2=SuXOc7Z$Tj) zTNm^QC94CXffsrb9#1_H@a)s?Q^)H@pCbG0C^uiqzS<8I7Vi)HUd0(?giNh7=c&DA z&Rjm9Q1R#u#Hei%Q0Isf+!?8_N{2-F2Z(W%bKc|{(90R{c%ZoP#Z|a+y!=MLL zVjlg5%&{$|$>Vvj2OIxjP2h@l0ucsesn30G=8!Q^u=GCEyl1tr#DW%U9Wp>=-j2p( z%iprJX^U4Em?y7d7?JoeO2uMhVI!6;DoP}04FOno!=u-;hYC;~5bGFy?ki7;7n46w zi88UOD}w7GH-YvM5(hB3G`PdY{G-V~b?TQygoI4(Yl}C6H(-8D18|r!L@t?=4>GK5qUTLEZ z)3u~RT9%>DTF6EAuLR;w=YH-=FQ9ed1=nV!W8%g;-FUaZ%eu+ zm!D)tij{Tuom6r?hd#v{!d9Od6b{iF9ydhoDktB2=N4>Fq z8mXw3%5%npX?h~LZY~^Pqa!HEpE|B5bfQ25F;+G*fgehi#V*eoJhmT+XYqH_ZTLxF z{4-pWWYGY7RFM|zLkj3`L(Zbo5qiGtJu-U z-r8!{114!wSN?Jr8;2K4{%iJVEuiRTW!ShBj(6m)5kE<;j%5_Ct4>bu&&8~2uO&+} zh6eE_=f)wie&=@|)mC&ZTy05H2>t06O5OCnXW{nYR(&;CtZ7m6gGhz0Ek5 z`rELgd20MUgwzj!%*Wg4h5BYxMR7=m85FNxecmv)%+i6j_@ARl8T@>bYGKDD1 z2Lf{a9vw+y7cywywl`11x_qpLd2HRF9L(f~Rjx7~mexH7!x52c)_56P(_#-Jmcl?s z`7)RsY6c(I9mh#iuZu-$K_^Iywm*Kt z%h9!@jMeYS>xI$6yyF7pE?lQW7@}nn6kk+<(5Z<|u9&bgEBHksK73>6H$l~L?X`b! z+BN2b0wcI7s%da4Cwwxibt9$t`VkjvvC85%#@~CTkg({8Us;dX20d)-p%zDP-KI=u zprYZ2g%?mHCdp4v?A%9WUcB#M>#eW;qKy?{k==k6NB={me%t74@t>&T41*R7- zYNeZhax2Y%&vKBsjRR|P54*v!#g0>fx>uw>2KJN9?B-o-QAXQ%m~qK^ez2CD>`Emw z_poNyLB)g8nv3e0?4`V9RH7~=6j5w(cH>i%60bNHP}v2HJYujAHX8wHA4ob_t6xa4 zN5fhI7MfN_J2gM>(3_j1@!ZPc4|QQNEhhm=X&(PI){vj%UT(1sd!f1TjDY9i^|4I3 z|HlwY5m+&PsmZ;tDLKaFaCDJ_MTyc(*W;=5#cQPcabQZZHic#CB5`X`h^TaE`b@2+JyyYt;mNDD&K(%(<&VuJu{hcX z`Jca=!Cz69QKpD$f%Gck|8xZ?Wp`&RDIMA)N>f1K8W-Jb&p}dmz)Ux@!%Uinz-CvCjm7dESzDXV*lSd7iA;N*mbl|tMHvw?C+}yy-zq1U)+A-T0r0L1~^MqdN3A5MION# zEAIAHPn9gMnPJi!=+pbfy*<{*Uk>JX(5p6!D8Rnvh*0c4FAguy}sX8H%ovef( zBJXM1as>3K!k%ba0}oc~8n7XS6%G1cvIR#HNq}@}+N6bFx_l9%92tIaJh9NHgQcEx z+a^kA3c*UC?HUzebTSWJ!9U(&zYYwqL9H+t*T!9T_E7|Cs;NjjIo<@jD0-z-iR-~K zgZk5Z4j;*BU71q&U3A-<##%q%OmD>gIuC~HR8cO6fk+uD+hKee?*Gd}RgU`2U~U=98&t&vuq zpU!tj1p+H-DVD8N8t)eedc%Y4zHSD9|RUJ49{bI7>o<9 zhs3_L4fKdUD9jE-VAH}CQgE#TCUc+ya4St%j6n50P8qlGxn3(N;8xVGK2Nlv`5>T7 z-}7=qNPz9@ZkNwSL_q&Z;pZV=e;0eP$Lhv^seUsA?{6$QxkMi|Vs1ZX*I*8)J2(JK zmB8h4@4U2{2RbJmip3=6{AKK>Q5#t?=*f0cV;&X15yZ%2fMhi!M^nj2m^>pKZT}z@JfkRpAp-d5nyt0dFtq{CQT&@w00qX?a^o-TWBvUv@gKli_F8$powES?kPMaK3RxM<*3oJDpSIbCES(pgP5J z`*Zqnc}>aD_B}qEq}R*ai~Dlb_{2v?shMPZTm!q2cIXDOqb z9^AB?g~0k#+0+%5ZehVTB7>mSM=To5f%G6Q&b5}76rr%}*Ah(8L#2DvzB`vv*3En8 zxf&ApM&}do;>;N0Ul=;%fKfR1rwhOpcqWC$gf!^FT%i{d z6)@0x!SIKO!gbQ!;H*Lt{f?(856yyXCrcIsRW5Jo_U-ca+wGc}%AY;u8E@a_vkTnC zdTBqLAt`V7ijgyth00Yr*yI@75f>pkF!`>~XsV~K+8JTdwJeIo!3Q{*|B-;0EFtoN|^0Yn^k^e=#W17S5giHJzWh}X^ zo05_;b~P2AMj&)@e3Y+4Xzg(4^gXQEZh27VAU+z@5O6jo!;R|q3tfFqAO3>zopbqY zq(Y(yl?%g<3-hF>|LVXx-JCZ1pQUX{fFFu%;dy7M!mYn*4eQQLkwvHVsjlX)zo{?F ze=nUTc8X68S|{5oJ9iGr<&Fmc%{cJ4VnTeN`s;9BeOMtDfWibuYBGko0r0QXSR0YEIR_Rega(-iN!2#yNdQw)w|AK^+H|Yna&2pPv29{bv{V>EDZ^apU1( z#$1n2#{#5*?lTfDOAjnE$vbOuJ+YQ53L*>bhyMWKnn3sLnxDtMKEM3AEfe;t{DXK= zdR_C}&4TQn->GHNyI-Y~>@&9)<^Kb~a`FYb5iz+iT{L^vsrB3omWvl2#c7hg45`-V z6s1ZhQBhU=IPrfx(@?Q*?zZ{{XmT$lbJIF4H^7w9*$J}ep3T_d%eTH`N7os1Ph2Y0 zmgN+IaA(*J;2ayk8|%vIsT#NQ-3v;`IE(NpUvsEiKl@#*`OE2Mz4~gy)cOmXjK{P) zc8qpLb|Mw(c};O`^gLZv$v%437Den^d7l3SlizQJ6N>}+p(bf=kzQ(k3?rx02%Ck4 z4qC!OKs5jb$ai&}>d@U>jvsstOEeR@E+sh#_44bCn$?@cbg5%j$YIIQ&?FKVRUwJR zZ}{88*(*Q2$RgP=F~(wi`0Z%Ex+XnqYU&NQuaJV69Fd5)xM9UF!sjY6g95T{S4FqK zJ611HKBxN9lWtDu7FS>u|uXHUIP zpWoF01%5vN$65dK3KO2!r|SQde(8yb+y02A##t%J6D=}kWM!k_NYE1*+>jpSJpG56KMO*C#V~qLI=~1Ytg@-mlzO1C z&PVajRnTH4Zjsw~6bn0hJl4ktmg3uQ^i$m|*TaW0q&ub8EwG%^_bJ;P3Wpsng=_w- zbQ0HH{sO9YQLmoM#XvJUe{P7J`_YWtp(qWOiP~9pbf%E@;I7MJ{_@DN8seMTmx*<& zYXi0BV{GNN3AG(Vj(Usons?am4s>xeGd!voEtA>I7aFTG>N}l{#E<)g=rHpQVIy{W z2*Imf2?sodYsdIkh72HvDfv);wT5w=q+jdh`1cTxzLJzVYbp;0#!fQxG+*AOM09Hh zIhu<7FpE20Y==EN?X7C@rlM{90htUc5eY|0dq83S5WNoIi|-C*P_pEi^1<2|@Xqcl zo0i%avh$dpZp4cHpLWW1C=bf0K-D@sfhQ_0HJP%nH2ikAy0+x&jVim-@b1We*r#Wo zIGXuadbrKQD*PO=rY7f5~H<6p*+SNKH{DqLDPD2Ldf=L-Yjx0(rg*QG!v0n`$Ex?JpLL} zxd+7LiA==G+2eQM2Jq4W7bz1ZVu+$MrjVE zryyM?N#q>Eg5{PmMP)Hpo7oUb3CTr_p7~I432*W{I1=v4ec@0|PQBOJfu?${?|KBY zs;;;Ncm3sIo*3tX%I|xaD!JhaN2|5|1;2KO?}G}CK!-Fnkb}cd3pggFuwvrWGP5-f zgcIYnZ~T&S*H{~|Vs;5E*$f)|!xl&$M=dlD*E25CgB9BLnRkMLd0yhPAbth9byhyE zd+TTvssIZ!Lp55n@x;>**e^K>wxARNf10ML+v65)&& z5gp=0?&%I`Qxql}ca+K>BuYnI5FTmV-te&Wo2+*h|FMKav&W;QO+`MM>1UKx%*FT6xB}i~ zBc_eh5!Iuk%;wER^60xN@ieR-o#*fnG-5G9WG_2EtozUeV4`%-jgo!>pz|Z^tqMND z4+r_NuN7DuwD`pSLr4@otf6&Qn0BEKSew48AD{(vr&APr@P5ix3XWl*0wOIN?TyR_ zoSY2@#nWd`l#iTIfBZySpVON=1>h1z%)}$b@kx{cnw$f3JZVJ?8L zirVhtiI`wh=um4#fT*0s|H$)hjI_#Mh%uT?o88H#i2o(aJh~HW9#;OS7w+tyt;DYX z0i;S; zXg!3QDS9;aK_>fdd?41;0f5T-6%cEmv%P9N5$Ap$yz_?PpD4eJL6u->sA<&0+i)Fg zsi?1d#BRxgLxiN^MSN^7@R09PKb#oH?ofu*4XV|@vaWf`xWjcjPVgT#LfXq9BbaRe zQS+D|MOT%Ki1ztteGP7ox!2m(Fu3rr7$W@#nh4kHIOy$vS~!vV0^Oq}V>4O8D*?v` zzDo3phDGNk=Oy=~rWHTen4WsQe6Tn+a)2w!P%lIrMI0w$WgSOc^Tn-;U;hq2;ImTF zk;ciL|S5E)Q7dvOVU@xHpr#)z39aP%K}oAPnny*WnT z@xu8B6V=~bV*>*Jx$X9Ga>|3#Y;-36>uSC zAt<#(Qh9#*chLQdiE3L(IoC$!Iiu-)rlkE4#eKtXcrG#Kufd|okJOyW*3FVpm*~BY z0;-qdp1 zpp?lGuq0<~!6$W<7q5>>;?bjYK!UyoUCO9ezy(ZUHzHD{q%^0|3$F=NrcBO4AVUaO zh1jssAoatP9KbUj7We0DIGaxP4Oof}5-I93`>nW);a|BBo_F}I%5P$1Xb1-QCf6nd zVg;rK?CW5@c{1={A#9k%awIbk2%SDIQr?7Ok-@RPhK2cw8+V`(x~{Agr@j2XWldt= zG&@XLqTWI9_9f`u7yhe_5_%lO5!QK|ivICmFojt@L1kdiatHr>WQoJ#Xx2tl6dyq! zXfN)^qN~E+or{a!!5-k;Mr{aiUmuc-0_i%)LfdoHy*rprXOKd$+7>Bet@Ei%P{f;t z*g;wAVx|&QfUeM1E8$forOzCO+P=1XzMd6~xGbldlfWfS@pUgqBhzz(sR}7EkP#BI zhI!HSE9WO4les(jf?0iWKp;Thl}c1GRLC+c;PdeW;C4}1!)N%2AwJb+PkFDxLYPJ5 zp}jtHPg!2%kxN3ya}#UCNFbGvcQ|hJg#O^?+Qp_{mXWfSxmpCar!(8nhr%tCEGy%e z|E(~7aEPNK)WFEO>CUube0=v#HyuI6P3fhw&`H@BWa=h#$Tw|S9+wjsD@a@{KHbbgIk2cn$JUh&JJBN&~U)= zQQY6FZ@;~tD_u<=OpFMN?P}w=z`sW)7UC$krZG6*nsTG=Bne`h)eTfHE0v2Zr$)}t zP5wuGV_rPK(xQs`2YBHQHC@;Xt<5Aq!G$=MubCfn(y96F6YZC@Er;qVmQ6DQ#;!eO ztq8`;psfD@<-u{+)dsmQ)4+LhM^rL*5ZHoSF<(_XB2M_k{N%*^9N<1HAykEFrQ1MC z2?u_WQGf`jxS}s8zSD6ny-sN|g#EPe(=E7NL9fu18EsW_j}uF=qd}*^+6aT_uoRvm zNX<09-y>LP&4=RY*V~@KO%l?zVE9MJVN)+7&^ycb#`t$CG!(*k=ma^p+=d@QQx%d>TBN(WJ?ah+4KDpK{;o36H)YSVb*4I)|$g1Cm#ruUP(Ed0e z(0oefj}uK-wzo95H*(x%dEm3O%2fN9mG^7F3hyZS{8|}?+oq`K`)y&7hAOt<6x$%= zUPJ8;W)J`jVlkZj9mFS&I#SDyL8LL%UQt&%et?KdF#wKNmpA;i`$4vu!U2j_bNueB zv4s2@ay>RzvEShx{5vKzcx+2(PHlSuePaU_BguhoqBAveq5uQTe;;3WbYv5}ty`Z0 zm3k8WjtIotsRtuapwO_znd?e+!m1jDA*rEkV+Mbc1^@>qVd>in02OJ3^)R=tB@DH* zwL<_bN>m+^u+SejK_ixkreo%^JNWD?*#6EfS-Y_Fsh``!G~CWdHu5@wi|pv@9G^UHDf( z{~0P?PaSfZDC^DHzp73hp-ym0t_cL9K-_5lw-vuuZy=fZ1K8&Bq8q-dS8;m2w;Qn7 z<>A@`KZofj))G9PLIw3{x5P8lPDAz@~mfMZ7j@j>N5#+x0h^2 z@hii>5>#KD28RWt!o=pAZLTD@_@A1~V z>B!6i$9{hHDZFw#hn#KQL%+BG63O=Co+mbWFym`S8+^)ZUG4-CQG=4{Xp@DfK74#! zEU9-usWd5@y|3Hb(4X0R9jwHhAGNizbWo^p_Lh~TuSWB=L7u5{S@1SD{FGeNw~#0j zE`p+cVD7@bo=qv$+(8?5EnreT5) zS3UYYGXnnd!5Wh9HpjvnZ#OL?(y41n! z<67@DG;f~6pdn1&3w{;p0JiRhmEgB0bB{(0OFbymw1h?u%4={iRIS}SF^}}P*X7S=ol<5Rj$VZNN7qu0T(wTek0D&6!l3YE&G z%k5HIomFOrec5cMBm=C(=&FfSQXVyz`? z7q7MRmAame#C0yYnRQ`$BmLY(1r8Cwab;A6ae+*@q1N~M?Bf_%+$3YiQVJ!f4LN{N zHuZP~?YajK6DJz~NimN)-8E==ukn0NFF6mZE147o)d@T^p$LOo@FdGe-m*|DNaCt8 z(t1#D5Kt3Dev7~ix#b0v6+2Y}j+y&$1giMXf3~Rq?nAB{(Yqo6`qOPzpaUtGFcXHl zDB3bA+L)X51*g=|Q%UqCK!i^E-)^hF<9Ealm10M|<@$nhaib*g(nlH#OnZ&a9kkhS zvfxZqKw?PGh2f9LcE8d`6?=rWF*LGOhXKNid}z7O`nQMb**4VMh9v2#PRW7X*$W+S4K6ldulq;L7_y+m?6X;xO$g9sg5*Dgigk>hWHat=wqc+?ygeI`> zuG+caQ`ZBro$VdhN$>~;peir_gu!}n+>ufQ$X@M3Ahzy;NRaffuhVvYiXmHt=me@G z#Bzak@c#h3Q?8V4*1I^vVkxAXuF>9Ss8&zxWKiiQEfh?BzhTq{R!5IV#Mho%FdFnv zc3A%204z}{+WL`QWXAe3(8HjTtPy6I3lXvK6*`uliBs`YvlppEzOgiS`lfiF1Mc~K z7HU#h#>8nu<23ytT|->C_9Rrx(VFbO-%p_poi-VOQWR6)Ke)Fw_Lj1%)~@I(&*Nt* zg&fTyW`1DBg+CgD+WC~fopN4T!iRnE$P?r*bNzc+ES@>9SbIu@!h3~k?LZ@QcxH({FC~=(tvV!o&)wr7)WG>842L^x@TFhsW$fzwG$W)(s--Xa zZW1;f>?iD1>U9M5y(shE6zXZw^{%d2cJr(Y2PCs0M$E8rPTJ8wDJOw!y`uN^h}mVX zCJkG)lA|F{WO`SXZ2EbfufwcfP5k%@y%nbA8FVZD>Sevn?f0CT4W3b^xF^#blv8bo zSciasu^vs#%yE@UOIz2G5BoT#YaWC%#Hy0(U9d3U52QZ|Hsfg?N5g&Px_nTYeLB+c z4k&9(2JL|mWMESYvRQ`0Y)i=-st?7YtZF*b`0=6oh>`@%57m>NgODNRK$*^E;xh7s zTq}PtE$qQzWrgLZZYJ$JwniC*PTi041~-I6nd#^|*3GI;OS+c%#BHF-SanICnGDhB z^4MPF4MP_9&%G5B^t3be_d(T_k~)@|c7!g5S!uK8IrF~Ho-2vriRQmp9=`so6xSS~ ztOGQH?XL-?u0mVMp`2|%P*z5KATtxc0GY*G(n^zmqK6u(-`pQ@)2>)>XY9*~XFML# zUyXIF@EO;I@RUZcebp_|n&eLz|06x9^(6Y2%$7Uy$K8Zkkr&$sd`j;muf4iE@U%S? zFw6T^{9UZUy0Zq#Zr2IFlF~&RqLK8L!_~0#e&#Sr$BkkQ0mcwPa9>^eD@%)HmDMjZ zzU1lIgWY$vZqrx~+puB+nE#kkSNPwaH1%T<6W^z!k7pYE*$wz|ynGK|FTE^k^FFim z!E4!BpQYi-SdH1r3TfWIu|3Met?FhEdFw}5GgOVzicH4bBVdG7LLT&jaVMRB>R3Gf zO6vHJ%Uu%6n5Ph-D*?O#iN9Sl&)D@DR&Ky7T?&3+g;CnW)`~o))0&9dcpoJ

m?C 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 Hg@6CqrJAKh literal 0 HcmV?d00001 diff --git a/liteyuki/resources/templates/js/echarts.js b/liteyuki/resources/templates/js/echarts.js new file mode 100644 index 00000000..2b6e9707 --- /dev/null +++ b/liteyuki/resources/templates/js/echarts.js @@ -0,0 +1,85683 @@ + +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.echarts = {})); +}(this, (function (exports) { 'use strict'; + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var Browser = (function () { + function Browser() { + this.firefox = false; + this.ie = false; + this.edge = false; + this.newEdge = false; + this.weChat = false; + } + return Browser; + }()); + var Env = (function () { + function Env() { + this.browser = new Browser(); + this.node = false; + this.wxa = false; + this.worker = false; + this.svgSupported = false; + this.touchEventsSupported = false; + this.pointerEventsSupported = false; + this.domSupported = false; + this.transformSupported = false; + this.transform3dSupported = false; + this.hasGlobalWindow = typeof window !== 'undefined'; + } + return Env; + }()); + var env = new Env(); + if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') { + env.wxa = true; + env.touchEventsSupported = true; + } + else if (typeof document === 'undefined' && typeof self !== 'undefined') { + env.worker = true; + } + else if (typeof navigator === 'undefined' + || navigator.userAgent.indexOf('Node.js') === 0) { + env.node = true; + env.svgSupported = true; + } + else { + detect(navigator.userAgent, env); + } + function detect(ua, env) { + var browser = env.browser; + var firefox = ua.match(/Firefox\/([\d.]+)/); + var ie = ua.match(/MSIE\s([\d.]+)/) + || ua.match(/Trident\/.+?rv:(([\d.]+))/); + var edge = ua.match(/Edge?\/([\d.]+)/); + var weChat = (/micromessenger/i).test(ua); + if (firefox) { + browser.firefox = true; + browser.version = firefox[1]; + } + if (ie) { + browser.ie = true; + browser.version = ie[1]; + } + if (edge) { + browser.edge = true; + browser.version = edge[1]; + browser.newEdge = +edge[1].split('.')[0] > 18; + } + if (weChat) { + browser.weChat = true; + } + env.svgSupported = typeof SVGRect !== 'undefined'; + env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge; + env.pointerEventsSupported = 'onpointerdown' in window + && (browser.edge || (browser.ie && +browser.version >= 11)); + env.domSupported = typeof document !== 'undefined'; + var style = document.documentElement.style; + env.transform3dSupported = ((browser.ie && 'transition' in style) + || browser.edge + || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix())) + || 'MozPerspective' in style) + && !('OTransition' in style); + env.transformSupported = env.transform3dSupported + || (browser.ie && +browser.version >= 9); + } + + var DEFAULT_FONT_SIZE = 12; + var DEFAULT_FONT_FAMILY = 'sans-serif'; + var DEFAULT_FONT = DEFAULT_FONT_SIZE + "px " + DEFAULT_FONT_FAMILY; + var OFFSET = 20; + var SCALE = 100; + var defaultWidthMapStr = "007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N"; + function getTextWidthMap(mapStr) { + var map = {}; + if (typeof JSON === 'undefined') { + return map; + } + for (var i = 0; i < mapStr.length; i++) { + var char = String.fromCharCode(i + 32); + var size = (mapStr.charCodeAt(i) - OFFSET) / SCALE; + map[char] = size; + } + return map; + } + var DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr); + var platformApi = { + createCanvas: function () { + return typeof document !== 'undefined' + && document.createElement('canvas'); + }, + measureText: (function () { + var _ctx; + var _cachedFont; + return function (text, font) { + if (!_ctx) { + var canvas = platformApi.createCanvas(); + _ctx = canvas && canvas.getContext('2d'); + } + if (_ctx) { + if (_cachedFont !== font) { + _cachedFont = _ctx.font = font || DEFAULT_FONT; + } + return _ctx.measureText(text); + } + else { + text = text || ''; + font = font || DEFAULT_FONT; + var res = /(\d+)px/.exec(font); + var fontSize = res && +res[1] || DEFAULT_FONT_SIZE; + var width = 0; + if (font.indexOf('mono') >= 0) { + width = fontSize * text.length; + } + else { + for (var i = 0; i < text.length; i++) { + var preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]]; + width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize); + } + } + return { width: width }; + } + }; + })(), + loadImage: function (src, onload, onerror) { + var image = new Image(); + image.onload = onload; + image.onerror = onerror; + image.src = src; + return image; + } + }; + function setPlatformAPI(newPlatformApis) { + for (var key in platformApi) { + if (newPlatformApis[key]) { + platformApi[key] = newPlatformApis[key]; + } + } + } + + var BUILTIN_OBJECT = reduce([ + 'Function', + 'RegExp', + 'Date', + 'Error', + 'CanvasGradient', + 'CanvasPattern', + 'Image', + 'Canvas' + ], function (obj, val) { + obj['[object ' + val + ']'] = true; + return obj; + }, {}); + var TYPED_ARRAY = reduce([ + 'Int8', + 'Uint8', + 'Uint8Clamped', + 'Int16', + 'Uint16', + 'Int32', + 'Uint32', + 'Float32', + 'Float64' + ], function (obj, val) { + obj['[object ' + val + 'Array]'] = true; + return obj; + }, {}); + var objToString = Object.prototype.toString; + var arrayProto = Array.prototype; + var nativeForEach = arrayProto.forEach; + var nativeFilter = arrayProto.filter; + var nativeSlice = arrayProto.slice; + var nativeMap = arrayProto.map; + var ctorFunction = function () { }.constructor; + var protoFunction = ctorFunction ? ctorFunction.prototype : null; + var protoKey = '__proto__'; + var idStart = 0x0907; + function guid() { + return idStart++; + } + function logError() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + if (typeof console !== 'undefined') { + console.error.apply(console, args); + } + } + function clone(source) { + if (source == null || typeof source !== 'object') { + return source; + } + var result = source; + var typeStr = objToString.call(source); + if (typeStr === '[object Array]') { + if (!isPrimitive(source)) { + result = []; + for (var i = 0, len = source.length; i < len; i++) { + result[i] = clone(source[i]); + } + } + } + else if (TYPED_ARRAY[typeStr]) { + if (!isPrimitive(source)) { + var Ctor = source.constructor; + if (Ctor.from) { + result = Ctor.from(source); + } + else { + result = new Ctor(source.length); + for (var i = 0, len = source.length; i < len; i++) { + result[i] = source[i]; + } + } + } + } + else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) { + result = {}; + for (var key in source) { + if (source.hasOwnProperty(key) && key !== protoKey) { + result[key] = clone(source[key]); + } + } + } + return result; + } + function merge(target, source, overwrite) { + if (!isObject(source) || !isObject(target)) { + return overwrite ? clone(source) : target; + } + for (var key in source) { + if (source.hasOwnProperty(key) && key !== protoKey) { + var targetProp = target[key]; + var sourceProp = source[key]; + if (isObject(sourceProp) + && isObject(targetProp) + && !isArray(sourceProp) + && !isArray(targetProp) + && !isDom(sourceProp) + && !isDom(targetProp) + && !isBuiltInObject(sourceProp) + && !isBuiltInObject(targetProp) + && !isPrimitive(sourceProp) + && !isPrimitive(targetProp)) { + merge(targetProp, sourceProp, overwrite); + } + else if (overwrite || !(key in target)) { + target[key] = clone(source[key]); + } + } + } + return target; + } + function mergeAll(targetAndSources, overwrite) { + var result = targetAndSources[0]; + for (var i = 1, len = targetAndSources.length; i < len; i++) { + result = merge(result, targetAndSources[i], overwrite); + } + return result; + } + function extend(target, source) { + if (Object.assign) { + Object.assign(target, source); + } + else { + for (var key in source) { + if (source.hasOwnProperty(key) && key !== protoKey) { + target[key] = source[key]; + } + } + } + return target; + } + function defaults(target, source, overlay) { + var keysArr = keys(source); + for (var i = 0; i < keysArr.length; i++) { + var key = keysArr[i]; + if ((overlay ? source[key] != null : target[key] == null)) { + target[key] = source[key]; + } + } + return target; + } + var createCanvas = platformApi.createCanvas; + function indexOf(array, value) { + if (array) { + if (array.indexOf) { + return array.indexOf(value); + } + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + } + return -1; + } + function inherits(clazz, baseClazz) { + var clazzPrototype = clazz.prototype; + function F() { } + F.prototype = baseClazz.prototype; + clazz.prototype = new F(); + for (var prop in clazzPrototype) { + if (clazzPrototype.hasOwnProperty(prop)) { + clazz.prototype[prop] = clazzPrototype[prop]; + } + } + clazz.prototype.constructor = clazz; + clazz.superClass = baseClazz; + } + function mixin(target, source, override) { + target = 'prototype' in target ? target.prototype : target; + source = 'prototype' in source ? source.prototype : source; + if (Object.getOwnPropertyNames) { + var keyList = Object.getOwnPropertyNames(source); + for (var i = 0; i < keyList.length; i++) { + var key = keyList[i]; + if (key !== 'constructor') { + if ((override ? source[key] != null : target[key] == null)) { + target[key] = source[key]; + } + } + } + } + else { + defaults(target, source, override); + } + } + function isArrayLike(data) { + if (!data) { + return false; + } + if (typeof data === 'string') { + return false; + } + return typeof data.length === 'number'; + } + function each(arr, cb, context) { + if (!(arr && cb)) { + return; + } + if (arr.forEach && arr.forEach === nativeForEach) { + arr.forEach(cb, context); + } + else if (arr.length === +arr.length) { + for (var i = 0, len = arr.length; i < len; i++) { + cb.call(context, arr[i], i, arr); + } + } + else { + for (var key in arr) { + if (arr.hasOwnProperty(key)) { + cb.call(context, arr[key], key, arr); + } + } + } + } + function map(arr, cb, context) { + if (!arr) { + return []; + } + if (!cb) { + return slice(arr); + } + if (arr.map && arr.map === nativeMap) { + return arr.map(cb, context); + } + else { + var result = []; + for (var i = 0, len = arr.length; i < len; i++) { + result.push(cb.call(context, arr[i], i, arr)); + } + return result; + } + } + function reduce(arr, cb, memo, context) { + if (!(arr && cb)) { + return; + } + for (var i = 0, len = arr.length; i < len; i++) { + memo = cb.call(context, memo, arr[i], i, arr); + } + return memo; + } + function filter(arr, cb, context) { + if (!arr) { + return []; + } + if (!cb) { + return slice(arr); + } + if (arr.filter && arr.filter === nativeFilter) { + return arr.filter(cb, context); + } + else { + var result = []; + for (var i = 0, len = arr.length; i < len; i++) { + if (cb.call(context, arr[i], i, arr)) { + result.push(arr[i]); + } + } + return result; + } + } + function find(arr, cb, context) { + if (!(arr && cb)) { + return; + } + for (var i = 0, len = arr.length; i < len; i++) { + if (cb.call(context, arr[i], i, arr)) { + return arr[i]; + } + } + } + function keys(obj) { + if (!obj) { + return []; + } + if (Object.keys) { + return Object.keys(obj); + } + var keyList = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + keyList.push(key); + } + } + return keyList; + } + function bindPolyfill(func, context) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + return function () { + return func.apply(context, args.concat(nativeSlice.call(arguments))); + }; + } + var bind = (protoFunction && isFunction(protoFunction.bind)) + ? protoFunction.call.bind(protoFunction.bind) + : bindPolyfill; + function curry(func) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + return function () { + return func.apply(this, args.concat(nativeSlice.call(arguments))); + }; + } + function isArray(value) { + if (Array.isArray) { + return Array.isArray(value); + } + return objToString.call(value) === '[object Array]'; + } + function isFunction(value) { + return typeof value === 'function'; + } + function isString(value) { + return typeof value === 'string'; + } + function isStringSafe(value) { + return objToString.call(value) === '[object String]'; + } + function isNumber(value) { + return typeof value === 'number'; + } + function isObject(value) { + var type = typeof value; + return type === 'function' || (!!value && type === 'object'); + } + function isBuiltInObject(value) { + return !!BUILTIN_OBJECT[objToString.call(value)]; + } + function isTypedArray(value) { + return !!TYPED_ARRAY[objToString.call(value)]; + } + function isDom(value) { + return typeof value === 'object' + && typeof value.nodeType === 'number' + && typeof value.ownerDocument === 'object'; + } + function isGradientObject(value) { + return value.colorStops != null; + } + function isImagePatternObject(value) { + return value.image != null; + } + function isRegExp(value) { + return objToString.call(value) === '[object RegExp]'; + } + function eqNaN(value) { + return value !== value; + } + function retrieve() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + for (var i = 0, len = args.length; i < len; i++) { + if (args[i] != null) { + return args[i]; + } + } + } + function retrieve2(value0, value1) { + return value0 != null + ? value0 + : value1; + } + function retrieve3(value0, value1, value2) { + return value0 != null + ? value0 + : value1 != null + ? value1 + : value2; + } + function slice(arr) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + return nativeSlice.apply(arr, args); + } + function normalizeCssArray(val) { + if (typeof (val) === 'number') { + return [val, val, val, val]; + } + var len = val.length; + if (len === 2) { + return [val[0], val[1], val[0], val[1]]; + } + else if (len === 3) { + return [val[0], val[1], val[2], val[1]]; + } + return val; + } + function assert(condition, message) { + if (!condition) { + throw new Error(message); + } + } + function trim(str) { + if (str == null) { + return null; + } + else if (typeof str.trim === 'function') { + return str.trim(); + } + else { + return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); + } + } + var primitiveKey = '__ec_primitive__'; + function setAsPrimitive(obj) { + obj[primitiveKey] = true; + } + function isPrimitive(obj) { + return obj[primitiveKey]; + } + var MapPolyfill = (function () { + function MapPolyfill() { + this.data = {}; + } + MapPolyfill.prototype["delete"] = function (key) { + var existed = this.has(key); + if (existed) { + delete this.data[key]; + } + return existed; + }; + MapPolyfill.prototype.has = function (key) { + return this.data.hasOwnProperty(key); + }; + MapPolyfill.prototype.get = function (key) { + return this.data[key]; + }; + MapPolyfill.prototype.set = function (key, value) { + this.data[key] = value; + return this; + }; + MapPolyfill.prototype.keys = function () { + return keys(this.data); + }; + MapPolyfill.prototype.forEach = function (callback) { + var data = this.data; + for (var key in data) { + if (data.hasOwnProperty(key)) { + callback(data[key], key); + } + } + }; + return MapPolyfill; + }()); + var isNativeMapSupported = typeof Map === 'function'; + function maybeNativeMap() { + return (isNativeMapSupported ? new Map() : new MapPolyfill()); + } + var HashMap = (function () { + function HashMap(obj) { + var isArr = isArray(obj); + this.data = maybeNativeMap(); + var thisMap = this; + (obj instanceof HashMap) + ? obj.each(visit) + : (obj && each(obj, visit)); + function visit(value, key) { + isArr ? thisMap.set(value, key) : thisMap.set(key, value); + } + } + HashMap.prototype.hasKey = function (key) { + return this.data.has(key); + }; + HashMap.prototype.get = function (key) { + return this.data.get(key); + }; + HashMap.prototype.set = function (key, value) { + this.data.set(key, value); + return value; + }; + HashMap.prototype.each = function (cb, context) { + this.data.forEach(function (value, key) { + cb.call(context, value, key); + }); + }; + HashMap.prototype.keys = function () { + var keys = this.data.keys(); + return isNativeMapSupported + ? Array.from(keys) + : keys; + }; + HashMap.prototype.removeKey = function (key) { + this.data["delete"](key); + }; + return HashMap; + }()); + function createHashMap(obj) { + return new HashMap(obj); + } + function concatArray(a, b) { + var newArray = new a.constructor(a.length + b.length); + for (var i = 0; i < a.length; i++) { + newArray[i] = a[i]; + } + var offset = a.length; + for (var i = 0; i < b.length; i++) { + newArray[i + offset] = b[i]; + } + return newArray; + } + function createObject(proto, properties) { + var obj; + if (Object.create) { + obj = Object.create(proto); + } + else { + var StyleCtor = function () { }; + StyleCtor.prototype = proto; + obj = new StyleCtor(); + } + if (properties) { + extend(obj, properties); + } + return obj; + } + function disableUserSelect(dom) { + var domStyle = dom.style; + domStyle.webkitUserSelect = 'none'; + domStyle.userSelect = 'none'; + domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)'; + domStyle['-webkit-touch-callout'] = 'none'; + } + function hasOwn(own, prop) { + return own.hasOwnProperty(prop); + } + function noop() { } + var RADIAN_TO_DEGREE = 180 / Math.PI; + + var util = /*#__PURE__*/Object.freeze({ + __proto__: null, + guid: guid, + logError: logError, + clone: clone, + merge: merge, + mergeAll: mergeAll, + extend: extend, + defaults: defaults, + createCanvas: createCanvas, + indexOf: indexOf, + inherits: inherits, + mixin: mixin, + isArrayLike: isArrayLike, + each: each, + map: map, + reduce: reduce, + filter: filter, + find: find, + keys: keys, + bind: bind, + curry: curry, + isArray: isArray, + isFunction: isFunction, + isString: isString, + isStringSafe: isStringSafe, + isNumber: isNumber, + isObject: isObject, + isBuiltInObject: isBuiltInObject, + isTypedArray: isTypedArray, + isDom: isDom, + isGradientObject: isGradientObject, + isImagePatternObject: isImagePatternObject, + isRegExp: isRegExp, + eqNaN: eqNaN, + retrieve: retrieve, + retrieve2: retrieve2, + retrieve3: retrieve3, + slice: slice, + normalizeCssArray: normalizeCssArray, + assert: assert, + trim: trim, + setAsPrimitive: setAsPrimitive, + isPrimitive: isPrimitive, + HashMap: HashMap, + createHashMap: createHashMap, + concatArray: concatArray, + createObject: createObject, + disableUserSelect: disableUserSelect, + hasOwn: hasOwn, + noop: noop, + RADIAN_TO_DEGREE: RADIAN_TO_DEGREE + }); + + function create(x, y) { + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + return [x, y]; + } + function copy(out, v) { + out[0] = v[0]; + out[1] = v[1]; + return out; + } + function clone$1(v) { + return [v[0], v[1]]; + } + function set(out, a, b) { + out[0] = a; + out[1] = b; + return out; + } + function add(out, v1, v2) { + out[0] = v1[0] + v2[0]; + out[1] = v1[1] + v2[1]; + return out; + } + function scaleAndAdd(out, v1, v2, a) { + out[0] = v1[0] + v2[0] * a; + out[1] = v1[1] + v2[1] * a; + return out; + } + function sub(out, v1, v2) { + out[0] = v1[0] - v2[0]; + out[1] = v1[1] - v2[1]; + return out; + } + function len(v) { + return Math.sqrt(lenSquare(v)); + } + var length = len; + function lenSquare(v) { + return v[0] * v[0] + v[1] * v[1]; + } + var lengthSquare = lenSquare; + function mul(out, v1, v2) { + out[0] = v1[0] * v2[0]; + out[1] = v1[1] * v2[1]; + return out; + } + function div(out, v1, v2) { + out[0] = v1[0] / v2[0]; + out[1] = v1[1] / v2[1]; + return out; + } + function dot(v1, v2) { + return v1[0] * v2[0] + v1[1] * v2[1]; + } + function scale(out, v, s) { + out[0] = v[0] * s; + out[1] = v[1] * s; + return out; + } + function normalize(out, v) { + var d = len(v); + if (d === 0) { + out[0] = 0; + out[1] = 0; + } + else { + out[0] = v[0] / d; + out[1] = v[1] / d; + } + return out; + } + function distance(v1, v2) { + return Math.sqrt((v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1])); + } + var dist = distance; + function distanceSquare(v1, v2) { + return (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]); + } + var distSquare = distanceSquare; + function negate(out, v) { + out[0] = -v[0]; + out[1] = -v[1]; + return out; + } + function lerp(out, v1, v2, t) { + out[0] = v1[0] + t * (v2[0] - v1[0]); + out[1] = v1[1] + t * (v2[1] - v1[1]); + return out; + } + function applyTransform(out, v, m) { + var x = v[0]; + var y = v[1]; + out[0] = m[0] * x + m[2] * y + m[4]; + out[1] = m[1] * x + m[3] * y + m[5]; + return out; + } + function min(out, v1, v2) { + out[0] = Math.min(v1[0], v2[0]); + out[1] = Math.min(v1[1], v2[1]); + return out; + } + function max(out, v1, v2) { + out[0] = Math.max(v1[0], v2[0]); + out[1] = Math.max(v1[1], v2[1]); + return out; + } + + var vector = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create, + copy: copy, + clone: clone$1, + set: set, + add: add, + scaleAndAdd: scaleAndAdd, + sub: sub, + len: len, + length: length, + lenSquare: lenSquare, + lengthSquare: lengthSquare, + mul: mul, + div: div, + dot: dot, + scale: scale, + normalize: normalize, + distance: distance, + dist: dist, + distanceSquare: distanceSquare, + distSquare: distSquare, + negate: negate, + lerp: lerp, + applyTransform: applyTransform, + min: min, + max: max + }); + + var Param = (function () { + function Param(target, e) { + this.target = target; + this.topTarget = e && e.topTarget; + } + return Param; + }()); + var Draggable = (function () { + function Draggable(handler) { + this.handler = handler; + handler.on('mousedown', this._dragStart, this); + handler.on('mousemove', this._drag, this); + handler.on('mouseup', this._dragEnd, this); + } + Draggable.prototype._dragStart = function (e) { + var draggingTarget = e.target; + while (draggingTarget && !draggingTarget.draggable) { + draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget; + } + if (draggingTarget) { + this._draggingTarget = draggingTarget; + draggingTarget.dragging = true; + this._x = e.offsetX; + this._y = e.offsetY; + this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragstart', e.event); + } + }; + Draggable.prototype._drag = function (e) { + var draggingTarget = this._draggingTarget; + if (draggingTarget) { + var x = e.offsetX; + var y = e.offsetY; + var dx = x - this._x; + var dy = y - this._y; + this._x = x; + this._y = y; + draggingTarget.drift(dx, dy, e); + this.handler.dispatchToElement(new Param(draggingTarget, e), 'drag', e.event); + var dropTarget = this.handler.findHover(x, y, draggingTarget).target; + var lastDropTarget = this._dropTarget; + this._dropTarget = dropTarget; + if (draggingTarget !== dropTarget) { + if (lastDropTarget && dropTarget !== lastDropTarget) { + this.handler.dispatchToElement(new Param(lastDropTarget, e), 'dragleave', e.event); + } + if (dropTarget && dropTarget !== lastDropTarget) { + this.handler.dispatchToElement(new Param(dropTarget, e), 'dragenter', e.event); + } + } + } + }; + Draggable.prototype._dragEnd = function (e) { + var draggingTarget = this._draggingTarget; + if (draggingTarget) { + draggingTarget.dragging = false; + } + this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event); + if (this._dropTarget) { + this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event); + } + this._draggingTarget = null; + this._dropTarget = null; + }; + return Draggable; + }()); + + var Eventful = (function () { + function Eventful(eventProcessors) { + if (eventProcessors) { + this._$eventProcessor = eventProcessors; + } + } + Eventful.prototype.on = function (event, query, handler, context) { + if (!this._$handlers) { + this._$handlers = {}; + } + var _h = this._$handlers; + if (typeof query === 'function') { + context = handler; + handler = query; + query = null; + } + if (!handler || !event) { + return this; + } + var eventProcessor = this._$eventProcessor; + if (query != null && eventProcessor && eventProcessor.normalizeQuery) { + query = eventProcessor.normalizeQuery(query); + } + if (!_h[event]) { + _h[event] = []; + } + for (var i = 0; i < _h[event].length; i++) { + if (_h[event][i].h === handler) { + return this; + } + } + var wrap = { + h: handler, + query: query, + ctx: (context || this), + callAtLast: handler.zrEventfulCallAtLast + }; + var lastIndex = _h[event].length - 1; + var lastWrap = _h[event][lastIndex]; + (lastWrap && lastWrap.callAtLast) + ? _h[event].splice(lastIndex, 0, wrap) + : _h[event].push(wrap); + return this; + }; + Eventful.prototype.isSilent = function (eventName) { + var _h = this._$handlers; + return !_h || !_h[eventName] || !_h[eventName].length; + }; + Eventful.prototype.off = function (eventType, handler) { + var _h = this._$handlers; + if (!_h) { + return this; + } + if (!eventType) { + this._$handlers = {}; + return this; + } + if (handler) { + if (_h[eventType]) { + var newList = []; + for (var i = 0, l = _h[eventType].length; i < l; i++) { + if (_h[eventType][i].h !== handler) { + newList.push(_h[eventType][i]); + } + } + _h[eventType] = newList; + } + if (_h[eventType] && _h[eventType].length === 0) { + delete _h[eventType]; + } + } + else { + delete _h[eventType]; + } + return this; + }; + Eventful.prototype.trigger = function (eventType) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + if (!this._$handlers) { + return this; + } + var _h = this._$handlers[eventType]; + var eventProcessor = this._$eventProcessor; + if (_h) { + var argLen = args.length; + var len = _h.length; + for (var i = 0; i < len; i++) { + var hItem = _h[i]; + if (eventProcessor + && eventProcessor.filter + && hItem.query != null + && !eventProcessor.filter(eventType, hItem.query)) { + continue; + } + switch (argLen) { + case 0: + hItem.h.call(hItem.ctx); + break; + case 1: + hItem.h.call(hItem.ctx, args[0]); + break; + case 2: + hItem.h.call(hItem.ctx, args[0], args[1]); + break; + default: + hItem.h.apply(hItem.ctx, args); + break; + } + } + } + eventProcessor && eventProcessor.afterTrigger + && eventProcessor.afterTrigger(eventType); + return this; + }; + Eventful.prototype.triggerWithContext = function (type) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + if (!this._$handlers) { + return this; + } + var _h = this._$handlers[type]; + var eventProcessor = this._$eventProcessor; + if (_h) { + var argLen = args.length; + var ctx = args[argLen - 1]; + var len = _h.length; + for (var i = 0; i < len; i++) { + var hItem = _h[i]; + if (eventProcessor + && eventProcessor.filter + && hItem.query != null + && !eventProcessor.filter(type, hItem.query)) { + continue; + } + switch (argLen) { + case 0: + hItem.h.call(ctx); + break; + case 1: + hItem.h.call(ctx, args[0]); + break; + case 2: + hItem.h.call(ctx, args[0], args[1]); + break; + default: + hItem.h.apply(ctx, args.slice(1, argLen - 1)); + break; + } + } + } + eventProcessor && eventProcessor.afterTrigger + && eventProcessor.afterTrigger(type); + return this; + }; + return Eventful; + }()); + + var LN2 = Math.log(2); + function determinant(rows, rank, rowStart, rowMask, colMask, detCache) { + var cacheKey = rowMask + '-' + colMask; + var fullRank = rows.length; + if (detCache.hasOwnProperty(cacheKey)) { + return detCache[cacheKey]; + } + if (rank === 1) { + var colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2); + return rows[rowStart][colStart]; + } + var subRowMask = rowMask | (1 << rowStart); + var subRowStart = rowStart + 1; + while (rowMask & (1 << subRowStart)) { + subRowStart++; + } + var sum = 0; + for (var j = 0, colLocalIdx = 0; j < fullRank; j++) { + var colTag = 1 << j; + if (!(colTag & colMask)) { + sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j] + * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache); + colLocalIdx++; + } + } + detCache[cacheKey] = sum; + return sum; + } + function buildTransformer(src, dest) { + var mA = [ + [src[0], src[1], 1, 0, 0, 0, -dest[0] * src[0], -dest[0] * src[1]], + [0, 0, 0, src[0], src[1], 1, -dest[1] * src[0], -dest[1] * src[1]], + [src[2], src[3], 1, 0, 0, 0, -dest[2] * src[2], -dest[2] * src[3]], + [0, 0, 0, src[2], src[3], 1, -dest[3] * src[2], -dest[3] * src[3]], + [src[4], src[5], 1, 0, 0, 0, -dest[4] * src[4], -dest[4] * src[5]], + [0, 0, 0, src[4], src[5], 1, -dest[5] * src[4], -dest[5] * src[5]], + [src[6], src[7], 1, 0, 0, 0, -dest[6] * src[6], -dest[6] * src[7]], + [0, 0, 0, src[6], src[7], 1, -dest[7] * src[6], -dest[7] * src[7]] + ]; + var detCache = {}; + var det = determinant(mA, 8, 0, 0, 0, detCache); + if (det === 0) { + return; + } + var vh = []; + for (var i = 0; i < 8; i++) { + for (var j = 0; j < 8; j++) { + vh[j] == null && (vh[j] = 0); + vh[j] += ((i + j) % 2 ? -1 : 1) + * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache) + / det * dest[i]; + } + } + return function (out, srcPointX, srcPointY) { + var pk = srcPointX * vh[6] + srcPointY * vh[7] + 1; + out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk; + out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk; + }; + } + + var EVENT_SAVED_PROP = '___zrEVENTSAVED'; + var _calcOut = []; + function transformLocalCoord(out, elFrom, elTarget, inX, inY) { + return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true) + && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]); + } + function transformCoordWithViewport(out, el, inX, inY, inverse) { + if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) { + var saved = el[EVENT_SAVED_PROP] || (el[EVENT_SAVED_PROP] = {}); + var markers = prepareCoordMarkers(el, saved); + var transformer = preparePointerTransformer(markers, saved, inverse); + if (transformer) { + transformer(out, inX, inY); + return true; + } + } + return false; + } + function prepareCoordMarkers(el, saved) { + var markers = saved.markers; + if (markers) { + return markers; + } + markers = saved.markers = []; + var propLR = ['left', 'right']; + var propTB = ['top', 'bottom']; + for (var i = 0; i < 4; i++) { + var marker = document.createElement('div'); + var stl = marker.style; + var idxLR = i % 2; + var idxTB = (i >> 1) % 2; + stl.cssText = [ + 'position: absolute', + 'visibility: hidden', + 'padding: 0', + 'margin: 0', + 'border-width: 0', + 'user-select: none', + 'width:0', + 'height:0', + propLR[idxLR] + ':0', + propTB[idxTB] + ':0', + propLR[1 - idxLR] + ':auto', + propTB[1 - idxTB] + ':auto', + '' + ].join('!important;'); + el.appendChild(marker); + markers.push(marker); + } + return markers; + } + function preparePointerTransformer(markers, saved, inverse) { + var transformerName = inverse ? 'invTrans' : 'trans'; + var transformer = saved[transformerName]; + var oldSrcCoords = saved.srcCoords; + var srcCoords = []; + var destCoords = []; + var oldCoordTheSame = true; + for (var i = 0; i < 4; i++) { + var rect = markers[i].getBoundingClientRect(); + var ii = 2 * i; + var x = rect.left; + var y = rect.top; + srcCoords.push(x, y); + oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1]; + destCoords.push(markers[i].offsetLeft, markers[i].offsetTop); + } + return (oldCoordTheSame && transformer) + ? transformer + : (saved.srcCoords = srcCoords, + saved[transformerName] = inverse + ? buildTransformer(destCoords, srcCoords) + : buildTransformer(srcCoords, destCoords)); + } + function isCanvasEl(el) { + return el.nodeName.toUpperCase() === 'CANVAS'; + } + var replaceReg = /([&<>"'])/g; + var replaceMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + '\'': ''' + }; + function encodeHTML(source) { + return source == null + ? '' + : (source + '').replace(replaceReg, function (str, c) { + return replaceMap[c]; + }); + } + + var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; + var _calcOut$1 = []; + var firefoxNotSupportOffsetXY = env.browser.firefox + && +env.browser.version.split('.')[0] < 39; + function clientToLocal(el, e, out, calculate) { + out = out || {}; + if (calculate) { + calculateZrXY(el, e, out); + } + else if (firefoxNotSupportOffsetXY + && e.layerX != null + && e.layerX !== e.offsetX) { + out.zrX = e.layerX; + out.zrY = e.layerY; + } + else if (e.offsetX != null) { + out.zrX = e.offsetX; + out.zrY = e.offsetY; + } + else { + calculateZrXY(el, e, out); + } + return out; + } + function calculateZrXY(el, e, out) { + if (env.domSupported && el.getBoundingClientRect) { + var ex = e.clientX; + var ey = e.clientY; + if (isCanvasEl(el)) { + var box = el.getBoundingClientRect(); + out.zrX = ex - box.left; + out.zrY = ey - box.top; + return; + } + else { + if (transformCoordWithViewport(_calcOut$1, el, ex, ey)) { + out.zrX = _calcOut$1[0]; + out.zrY = _calcOut$1[1]; + return; + } + } + } + out.zrX = out.zrY = 0; + } + function getNativeEvent(e) { + return e + || window.event; + } + function normalizeEvent(el, e, calculate) { + e = getNativeEvent(e); + if (e.zrX != null) { + return e; + } + var eventType = e.type; + var isTouch = eventType && eventType.indexOf('touch') >= 0; + if (!isTouch) { + clientToLocal(el, e, e, calculate); + var wheelDelta = getWheelDeltaMayPolyfill(e); + e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3; + } + else { + var touch = eventType !== 'touchend' + ? e.targetTouches[0] + : e.changedTouches[0]; + touch && clientToLocal(el, touch, e, calculate); + } + var button = e.button; + if (e.which == null && button !== undefined && MOUSE_EVENT_REG.test(e.type)) { + e.which = (button & 1 ? 1 : (button & 2 ? 3 : (button & 4 ? 2 : 0))); + } + return e; + } + function getWheelDeltaMayPolyfill(e) { + var rawWheelDelta = e.wheelDelta; + if (rawWheelDelta) { + return rawWheelDelta; + } + var deltaX = e.deltaX; + var deltaY = e.deltaY; + if (deltaX == null || deltaY == null) { + return rawWheelDelta; + } + var delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX); + var sign = deltaY > 0 ? -1 + : deltaY < 0 ? 1 + : deltaX > 0 ? -1 + : 1; + return 3 * delta * sign; + } + function addEventListener(el, name, handler, opt) { + el.addEventListener(name, handler, opt); + } + function removeEventListener(el, name, handler, opt) { + el.removeEventListener(name, handler, opt); + } + var stop = function (e) { + e.preventDefault(); + e.stopPropagation(); + e.cancelBubble = true; + }; + function isMiddleOrRightButtonOnMouseUpDown(e) { + return e.which === 2 || e.which === 3; + } + + var GestureMgr = (function () { + function GestureMgr() { + this._track = []; + } + GestureMgr.prototype.recognize = function (event, target, root) { + this._doTrack(event, target, root); + return this._recognize(event); + }; + GestureMgr.prototype.clear = function () { + this._track.length = 0; + return this; + }; + GestureMgr.prototype._doTrack = function (event, target, root) { + var touches = event.touches; + if (!touches) { + return; + } + var trackItem = { + points: [], + touches: [], + target: target, + event: event + }; + for (var i = 0, len = touches.length; i < len; i++) { + var touch = touches[i]; + var pos = clientToLocal(root, touch, {}); + trackItem.points.push([pos.zrX, pos.zrY]); + trackItem.touches.push(touch); + } + this._track.push(trackItem); + }; + GestureMgr.prototype._recognize = function (event) { + for (var eventName in recognizers) { + if (recognizers.hasOwnProperty(eventName)) { + var gestureInfo = recognizers[eventName](this._track, event); + if (gestureInfo) { + return gestureInfo; + } + } + } + }; + return GestureMgr; + }()); + function dist$1(pointPair) { + var dx = pointPair[1][0] - pointPair[0][0]; + var dy = pointPair[1][1] - pointPair[0][1]; + return Math.sqrt(dx * dx + dy * dy); + } + function center(pointPair) { + return [ + (pointPair[0][0] + pointPair[1][0]) / 2, + (pointPair[0][1] + pointPair[1][1]) / 2 + ]; + } + var recognizers = { + pinch: function (tracks, event) { + var trackLen = tracks.length; + if (!trackLen) { + return; + } + var pinchEnd = (tracks[trackLen - 1] || {}).points; + var pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd; + if (pinchPre + && pinchPre.length > 1 + && pinchEnd + && pinchEnd.length > 1) { + var pinchScale = dist$1(pinchEnd) / dist$1(pinchPre); + !isFinite(pinchScale) && (pinchScale = 1); + event.pinchScale = pinchScale; + var pinchCenter = center(pinchEnd); + event.pinchX = pinchCenter[0]; + event.pinchY = pinchCenter[1]; + return { + type: 'pinch', + target: tracks[0].target, + event: event + }; + } + } + }; + + function create$1() { + return [1, 0, 0, 1, 0, 0]; + } + function identity(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + } + function copy$1(out, m) { + out[0] = m[0]; + out[1] = m[1]; + out[2] = m[2]; + out[3] = m[3]; + out[4] = m[4]; + out[5] = m[5]; + return out; + } + function mul$1(out, m1, m2) { + var out0 = m1[0] * m2[0] + m1[2] * m2[1]; + var out1 = m1[1] * m2[0] + m1[3] * m2[1]; + var out2 = m1[0] * m2[2] + m1[2] * m2[3]; + var out3 = m1[1] * m2[2] + m1[3] * m2[3]; + var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4]; + var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5]; + out[0] = out0; + out[1] = out1; + out[2] = out2; + out[3] = out3; + out[4] = out4; + out[5] = out5; + return out; + } + function translate(out, a, v) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4] + v[0]; + out[5] = a[5] + v[1]; + return out; + } + function rotate(out, a, rad, pivot) { + if (pivot === void 0) { pivot = [0, 0]; } + var aa = a[0]; + var ac = a[2]; + var atx = a[4]; + var ab = a[1]; + var ad = a[3]; + var aty = a[5]; + var st = Math.sin(rad); + var ct = Math.cos(rad); + out[0] = aa * ct + ab * st; + out[1] = -aa * st + ab * ct; + out[2] = ac * ct + ad * st; + out[3] = -ac * st + ct * ad; + out[4] = ct * (atx - pivot[0]) + st * (aty - pivot[1]) + pivot[0]; + out[5] = ct * (aty - pivot[1]) - st * (atx - pivot[0]) + pivot[1]; + return out; + } + function scale$1(out, a, v) { + var vx = v[0]; + var vy = v[1]; + out[0] = a[0] * vx; + out[1] = a[1] * vy; + out[2] = a[2] * vx; + out[3] = a[3] * vy; + out[4] = a[4] * vx; + out[5] = a[5] * vy; + return out; + } + function invert(out, a) { + var aa = a[0]; + var ac = a[2]; + var atx = a[4]; + var ab = a[1]; + var ad = a[3]; + var aty = a[5]; + var det = aa * ad - ab * ac; + if (!det) { + return null; + } + det = 1.0 / det; + out[0] = ad * det; + out[1] = -ab * det; + out[2] = -ac * det; + out[3] = aa * det; + out[4] = (ac * aty - ad * atx) * det; + out[5] = (ab * atx - aa * aty) * det; + return out; + } + function clone$2(a) { + var b = create$1(); + copy$1(b, a); + return b; + } + + var matrix = /*#__PURE__*/Object.freeze({ + __proto__: null, + create: create$1, + identity: identity, + copy: copy$1, + mul: mul$1, + translate: translate, + rotate: rotate, + scale: scale$1, + invert: invert, + clone: clone$2 + }); + + var Point = (function () { + function Point(x, y) { + this.x = x || 0; + this.y = y || 0; + } + Point.prototype.copy = function (other) { + this.x = other.x; + this.y = other.y; + return this; + }; + Point.prototype.clone = function () { + return new Point(this.x, this.y); + }; + Point.prototype.set = function (x, y) { + this.x = x; + this.y = y; + return this; + }; + Point.prototype.equal = function (other) { + return other.x === this.x && other.y === this.y; + }; + Point.prototype.add = function (other) { + this.x += other.x; + this.y += other.y; + return this; + }; + Point.prototype.scale = function (scalar) { + this.x *= scalar; + this.y *= scalar; + }; + Point.prototype.scaleAndAdd = function (other, scalar) { + this.x += other.x * scalar; + this.y += other.y * scalar; + }; + Point.prototype.sub = function (other) { + this.x -= other.x; + this.y -= other.y; + return this; + }; + Point.prototype.dot = function (other) { + return this.x * other.x + this.y * other.y; + }; + Point.prototype.len = function () { + return Math.sqrt(this.x * this.x + this.y * this.y); + }; + Point.prototype.lenSquare = function () { + return this.x * this.x + this.y * this.y; + }; + Point.prototype.normalize = function () { + var len = this.len(); + this.x /= len; + this.y /= len; + return this; + }; + Point.prototype.distance = function (other) { + var dx = this.x - other.x; + var dy = this.y - other.y; + return Math.sqrt(dx * dx + dy * dy); + }; + Point.prototype.distanceSquare = function (other) { + var dx = this.x - other.x; + var dy = this.y - other.y; + return dx * dx + dy * dy; + }; + Point.prototype.negate = function () { + this.x = -this.x; + this.y = -this.y; + return this; + }; + Point.prototype.transform = function (m) { + if (!m) { + return; + } + var x = this.x; + var y = this.y; + this.x = m[0] * x + m[2] * y + m[4]; + this.y = m[1] * x + m[3] * y + m[5]; + return this; + }; + Point.prototype.toArray = function (out) { + out[0] = this.x; + out[1] = this.y; + return out; + }; + Point.prototype.fromArray = function (input) { + this.x = input[0]; + this.y = input[1]; + }; + Point.set = function (p, x, y) { + p.x = x; + p.y = y; + }; + Point.copy = function (p, p2) { + p.x = p2.x; + p.y = p2.y; + }; + Point.len = function (p) { + return Math.sqrt(p.x * p.x + p.y * p.y); + }; + Point.lenSquare = function (p) { + return p.x * p.x + p.y * p.y; + }; + Point.dot = function (p0, p1) { + return p0.x * p1.x + p0.y * p1.y; + }; + Point.add = function (out, p0, p1) { + out.x = p0.x + p1.x; + out.y = p0.y + p1.y; + }; + Point.sub = function (out, p0, p1) { + out.x = p0.x - p1.x; + out.y = p0.y - p1.y; + }; + Point.scale = function (out, p0, scalar) { + out.x = p0.x * scalar; + out.y = p0.y * scalar; + }; + Point.scaleAndAdd = function (out, p0, p1, scalar) { + out.x = p0.x + p1.x * scalar; + out.y = p0.y + p1.y * scalar; + }; + Point.lerp = function (out, p0, p1, t) { + var onet = 1 - t; + out.x = onet * p0.x + t * p1.x; + out.y = onet * p0.y + t * p1.y; + }; + return Point; + }()); + + var mathMin = Math.min; + var mathMax = Math.max; + var lt = new Point(); + var rb = new Point(); + var lb = new Point(); + var rt = new Point(); + var minTv = new Point(); + var maxTv = new Point(); + var BoundingRect = (function () { + function BoundingRect(x, y, width, height) { + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + BoundingRect.prototype.union = function (other) { + var x = mathMin(other.x, this.x); + var y = mathMin(other.y, this.y); + if (isFinite(this.x) && isFinite(this.width)) { + this.width = mathMax(other.x + other.width, this.x + this.width) - x; + } + else { + this.width = other.width; + } + if (isFinite(this.y) && isFinite(this.height)) { + this.height = mathMax(other.y + other.height, this.y + this.height) - y; + } + else { + this.height = other.height; + } + this.x = x; + this.y = y; + }; + BoundingRect.prototype.applyTransform = function (m) { + BoundingRect.applyTransform(this, this, m); + }; + BoundingRect.prototype.calculateTransform = function (b) { + var a = this; + var sx = b.width / a.width; + var sy = b.height / a.height; + var m = create$1(); + translate(m, m, [-a.x, -a.y]); + scale$1(m, m, [sx, sy]); + translate(m, m, [b.x, b.y]); + return m; + }; + BoundingRect.prototype.intersect = function (b, mtv) { + if (!b) { + return false; + } + if (!(b instanceof BoundingRect)) { + b = BoundingRect.create(b); + } + var a = this; + var ax0 = a.x; + var ax1 = a.x + a.width; + var ay0 = a.y; + var ay1 = a.y + a.height; + var bx0 = b.x; + var bx1 = b.x + b.width; + var by0 = b.y; + var by1 = b.y + b.height; + var overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0); + if (mtv) { + var dMin = Infinity; + var dMax = 0; + var d0 = Math.abs(ax1 - bx0); + var d1 = Math.abs(bx1 - ax0); + var d2 = Math.abs(ay1 - by0); + var d3 = Math.abs(by1 - ay0); + var dx = Math.min(d0, d1); + var dy = Math.min(d2, d3); + if (ax1 < bx0 || bx1 < ax0) { + if (dx > dMax) { + dMax = dx; + if (d0 < d1) { + Point.set(maxTv, -d0, 0); + } + else { + Point.set(maxTv, d1, 0); + } + } + } + else { + if (dx < dMin) { + dMin = dx; + if (d0 < d1) { + Point.set(minTv, d0, 0); + } + else { + Point.set(minTv, -d1, 0); + } + } + } + if (ay1 < by0 || by1 < ay0) { + if (dy > dMax) { + dMax = dy; + if (d2 < d3) { + Point.set(maxTv, 0, -d2); + } + else { + Point.set(maxTv, 0, d3); + } + } + } + else { + if (dx < dMin) { + dMin = dx; + if (d2 < d3) { + Point.set(minTv, 0, d2); + } + else { + Point.set(minTv, 0, -d3); + } + } + } + } + if (mtv) { + Point.copy(mtv, overlap ? minTv : maxTv); + } + return overlap; + }; + BoundingRect.prototype.contain = function (x, y) { + var rect = this; + return x >= rect.x + && x <= (rect.x + rect.width) + && y >= rect.y + && y <= (rect.y + rect.height); + }; + BoundingRect.prototype.clone = function () { + return new BoundingRect(this.x, this.y, this.width, this.height); + }; + BoundingRect.prototype.copy = function (other) { + BoundingRect.copy(this, other); + }; + BoundingRect.prototype.plain = function () { + return { + x: this.x, + y: this.y, + width: this.width, + height: this.height + }; + }; + BoundingRect.prototype.isFinite = function () { + return isFinite(this.x) + && isFinite(this.y) + && isFinite(this.width) + && isFinite(this.height); + }; + BoundingRect.prototype.isZero = function () { + return this.width === 0 || this.height === 0; + }; + BoundingRect.create = function (rect) { + return new BoundingRect(rect.x, rect.y, rect.width, rect.height); + }; + BoundingRect.copy = function (target, source) { + target.x = source.x; + target.y = source.y; + target.width = source.width; + target.height = source.height; + }; + BoundingRect.applyTransform = function (target, source, m) { + if (!m) { + if (target !== source) { + BoundingRect.copy(target, source); + } + return; + } + if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) { + var sx = m[0]; + var sy = m[3]; + var tx = m[4]; + var ty = m[5]; + target.x = source.x * sx + tx; + target.y = source.y * sy + ty; + target.width = source.width * sx; + target.height = source.height * sy; + if (target.width < 0) { + target.x += target.width; + target.width = -target.width; + } + if (target.height < 0) { + target.y += target.height; + target.height = -target.height; + } + return; + } + lt.x = lb.x = source.x; + lt.y = rt.y = source.y; + rb.x = rt.x = source.x + source.width; + rb.y = lb.y = source.y + source.height; + lt.transform(m); + rt.transform(m); + rb.transform(m); + lb.transform(m); + target.x = mathMin(lt.x, rb.x, lb.x, rt.x); + target.y = mathMin(lt.y, rb.y, lb.y, rt.y); + var maxX = mathMax(lt.x, rb.x, lb.x, rt.x); + var maxY = mathMax(lt.y, rb.y, lb.y, rt.y); + target.width = maxX - target.x; + target.height = maxY - target.y; + }; + return BoundingRect; + }()); + + var SILENT = 'silent'; + function makeEventPacket(eveType, targetInfo, event) { + return { + type: eveType, + event: event, + target: targetInfo.target, + topTarget: targetInfo.topTarget, + cancelBubble: false, + offsetX: event.zrX, + offsetY: event.zrY, + gestureEvent: event.gestureEvent, + pinchX: event.pinchX, + pinchY: event.pinchY, + pinchScale: event.pinchScale, + wheelDelta: event.zrDelta, + zrByTouch: event.zrByTouch, + which: event.which, + stop: stopEvent + }; + } + function stopEvent() { + stop(this.event); + } + var EmptyProxy = (function (_super) { + __extends(EmptyProxy, _super); + function EmptyProxy() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.handler = null; + return _this; + } + EmptyProxy.prototype.dispose = function () { }; + EmptyProxy.prototype.setCursor = function () { }; + return EmptyProxy; + }(Eventful)); + var HoveredResult = (function () { + function HoveredResult(x, y) { + this.x = x; + this.y = y; + } + return HoveredResult; + }()); + var handlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove', 'contextmenu' + ]; + var tmpRect = new BoundingRect(0, 0, 0, 0); + var Handler = (function (_super) { + __extends(Handler, _super); + function Handler(storage, painter, proxy, painterRoot, pointerSize) { + var _this = _super.call(this) || this; + _this._hovered = new HoveredResult(0, 0); + _this.storage = storage; + _this.painter = painter; + _this.painterRoot = painterRoot; + _this._pointerSize = pointerSize; + proxy = proxy || new EmptyProxy(); + _this.proxy = null; + _this.setHandlerProxy(proxy); + _this._draggingMgr = new Draggable(_this); + return _this; + } + Handler.prototype.setHandlerProxy = function (proxy) { + if (this.proxy) { + this.proxy.dispose(); + } + if (proxy) { + each(handlerNames, function (name) { + proxy.on && proxy.on(name, this[name], this); + }, this); + proxy.handler = this; + } + this.proxy = proxy; + }; + Handler.prototype.mousemove = function (event) { + var x = event.zrX; + var y = event.zrY; + var isOutside = isOutsideBoundary(this, x, y); + var lastHovered = this._hovered; + var lastHoveredTarget = lastHovered.target; + if (lastHoveredTarget && !lastHoveredTarget.__zr) { + lastHovered = this.findHover(lastHovered.x, lastHovered.y); + lastHoveredTarget = lastHovered.target; + } + var hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y); + var hoveredTarget = hovered.target; + var proxy = this.proxy; + proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default'); + if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) { + this.dispatchToElement(lastHovered, 'mouseout', event); + } + this.dispatchToElement(hovered, 'mousemove', event); + if (hoveredTarget && hoveredTarget !== lastHoveredTarget) { + this.dispatchToElement(hovered, 'mouseover', event); + } + }; + Handler.prototype.mouseout = function (event) { + var eventControl = event.zrEventControl; + if (eventControl !== 'only_globalout') { + this.dispatchToElement(this._hovered, 'mouseout', event); + } + if (eventControl !== 'no_globalout') { + this.trigger('globalout', { type: 'globalout', event: event }); + } + }; + Handler.prototype.resize = function () { + this._hovered = new HoveredResult(0, 0); + }; + Handler.prototype.dispatch = function (eventName, eventArgs) { + var handler = this[eventName]; + handler && handler.call(this, eventArgs); + }; + Handler.prototype.dispose = function () { + this.proxy.dispose(); + this.storage = null; + this.proxy = null; + this.painter = null; + }; + Handler.prototype.setCursorStyle = function (cursorStyle) { + var proxy = this.proxy; + proxy.setCursor && proxy.setCursor(cursorStyle); + }; + Handler.prototype.dispatchToElement = function (targetInfo, eventName, event) { + targetInfo = targetInfo || {}; + var el = targetInfo.target; + if (el && el.silent) { + return; + } + var eventKey = ('on' + eventName); + var eventPacket = makeEventPacket(eventName, targetInfo, event); + while (el) { + el[eventKey] + && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket)); + el.trigger(eventName, eventPacket); + el = el.__hostTarget ? el.__hostTarget : el.parent; + if (eventPacket.cancelBubble) { + break; + } + } + if (!eventPacket.cancelBubble) { + this.trigger(eventName, eventPacket); + if (this.painter && this.painter.eachOtherLayer) { + this.painter.eachOtherLayer(function (layer) { + if (typeof (layer[eventKey]) === 'function') { + layer[eventKey].call(layer, eventPacket); + } + if (layer.trigger) { + layer.trigger(eventName, eventPacket); + } + }); + } + } + }; + Handler.prototype.findHover = function (x, y, exclude) { + var list = this.storage.getDisplayList(); + var out = new HoveredResult(x, y); + setHoverTarget(list, out, x, y, exclude); + if (this._pointerSize && !out.target) { + var candidates = []; + var pointerSize = this._pointerSize; + var targetSizeHalf = pointerSize / 2; + var pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize); + for (var i = list.length - 1; i >= 0; i--) { + var el = list[i]; + if (el !== exclude + && !el.ignore + && !el.ignoreCoarsePointer + && (!el.parent || !el.parent.ignoreCoarsePointer)) { + tmpRect.copy(el.getBoundingRect()); + if (el.transform) { + tmpRect.applyTransform(el.transform); + } + if (tmpRect.intersect(pointerRect)) { + candidates.push(el); + } + } + } + if (candidates.length) { + var rStep = 4; + var thetaStep = Math.PI / 12; + var PI2 = Math.PI * 2; + for (var r = 0; r < targetSizeHalf; r += rStep) { + for (var theta = 0; theta < PI2; theta += thetaStep) { + var x1 = x + r * Math.cos(theta); + var y1 = y + r * Math.sin(theta); + setHoverTarget(candidates, out, x1, y1, exclude); + if (out.target) { + return out; + } + } + } + } + } + return out; + }; + Handler.prototype.processGesture = function (event, stage) { + if (!this._gestureMgr) { + this._gestureMgr = new GestureMgr(); + } + var gestureMgr = this._gestureMgr; + stage === 'start' && gestureMgr.clear(); + var gestureInfo = gestureMgr.recognize(event, this.findHover(event.zrX, event.zrY, null).target, this.proxy.dom); + stage === 'end' && gestureMgr.clear(); + if (gestureInfo) { + var type = gestureInfo.type; + event.gestureEvent = type; + var res = new HoveredResult(); + res.target = gestureInfo.target; + this.dispatchToElement(res, type, gestureInfo.event); + } + }; + return Handler; + }(Eventful)); + each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { + Handler.prototype[name] = function (event) { + var x = event.zrX; + var y = event.zrY; + var isOutside = isOutsideBoundary(this, x, y); + var hovered; + var hoveredTarget; + if (name !== 'mouseup' || !isOutside) { + hovered = this.findHover(x, y); + hoveredTarget = hovered.target; + } + if (name === 'mousedown') { + this._downEl = hoveredTarget; + this._downPoint = [event.zrX, event.zrY]; + this._upEl = hoveredTarget; + } + else if (name === 'mouseup') { + this._upEl = hoveredTarget; + } + else if (name === 'click') { + if (this._downEl !== this._upEl + || !this._downPoint + || dist(this._downPoint, [event.zrX, event.zrY]) > 4) { + return; + } + this._downPoint = null; + } + this.dispatchToElement(hovered, name, event); + }; + }); + function isHover(displayable, x, y) { + if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) { + var el = displayable; + var isSilent = void 0; + var ignoreClip = false; + while (el) { + if (el.ignoreClip) { + ignoreClip = true; + } + if (!ignoreClip) { + var clipPath = el.getClipPath(); + if (clipPath && !clipPath.contain(x, y)) { + return false; + } + } + if (el.silent) { + isSilent = true; + } + var hostEl = el.__hostTarget; + el = hostEl ? hostEl : el.parent; + } + return isSilent ? SILENT : true; + } + return false; + } + function setHoverTarget(list, out, x, y, exclude) { + for (var i = list.length - 1; i >= 0; i--) { + var el = list[i]; + var hoverCheckResult = void 0; + if (el !== exclude + && !el.ignore + && (hoverCheckResult = isHover(el, x, y))) { + !out.topTarget && (out.topTarget = el); + if (hoverCheckResult !== SILENT) { + out.target = el; + break; + } + } + } + } + function isOutsideBoundary(handlerInstance, x, y) { + var painter = handlerInstance.painter; + return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight(); + } + + var DEFAULT_MIN_MERGE = 32; + var DEFAULT_MIN_GALLOPING = 7; + function minRunLength(n) { + var r = 0; + while (n >= DEFAULT_MIN_MERGE) { + r |= n & 1; + n >>= 1; + } + return n + r; + } + function makeAscendingRun(array, lo, hi, compare) { + var runHi = lo + 1; + if (runHi === hi) { + return 1; + } + if (compare(array[runHi++], array[lo]) < 0) { + while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) { + runHi++; + } + reverseRun(array, lo, runHi); + } + else { + while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) { + runHi++; + } + } + return runHi - lo; + } + function reverseRun(array, lo, hi) { + hi--; + while (lo < hi) { + var t = array[lo]; + array[lo++] = array[hi]; + array[hi--] = t; + } + } + function binaryInsertionSort(array, lo, hi, start, compare) { + if (start === lo) { + start++; + } + for (; start < hi; start++) { + var pivot = array[start]; + var left = lo; + var right = start; + var mid; + while (left < right) { + mid = left + right >>> 1; + if (compare(pivot, array[mid]) < 0) { + right = mid; + } + else { + left = mid + 1; + } + } + var n = start - left; + switch (n) { + case 3: + array[left + 3] = array[left + 2]; + case 2: + array[left + 2] = array[left + 1]; + case 1: + array[left + 1] = array[left]; + break; + default: + while (n > 0) { + array[left + n] = array[left + n - 1]; + n--; + } + } + array[left] = pivot; + } + } + function gallopLeft(value, array, start, length, hint, compare) { + var lastOffset = 0; + var maxOffset = 0; + var offset = 1; + if (compare(value, array[start + hint]) > 0) { + maxOffset = length - hint; + while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + if (offset <= 0) { + offset = maxOffset; + } + } + if (offset > maxOffset) { + offset = maxOffset; + } + lastOffset += hint; + offset += hint; + } + else { + maxOffset = hint + 1; + while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + if (offset <= 0) { + offset = maxOffset; + } + } + if (offset > maxOffset) { + offset = maxOffset; + } + var tmp = lastOffset; + lastOffset = hint - offset; + offset = hint - tmp; + } + lastOffset++; + while (lastOffset < offset) { + var m = lastOffset + (offset - lastOffset >>> 1); + if (compare(value, array[start + m]) > 0) { + lastOffset = m + 1; + } + else { + offset = m; + } + } + return offset; + } + function gallopRight(value, array, start, length, hint, compare) { + var lastOffset = 0; + var maxOffset = 0; + var offset = 1; + if (compare(value, array[start + hint]) < 0) { + maxOffset = hint + 1; + while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + if (offset <= 0) { + offset = maxOffset; + } + } + if (offset > maxOffset) { + offset = maxOffset; + } + var tmp = lastOffset; + lastOffset = hint - offset; + offset = hint - tmp; + } + else { + maxOffset = length - hint; + while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + if (offset <= 0) { + offset = maxOffset; + } + } + if (offset > maxOffset) { + offset = maxOffset; + } + lastOffset += hint; + offset += hint; + } + lastOffset++; + while (lastOffset < offset) { + var m = lastOffset + (offset - lastOffset >>> 1); + if (compare(value, array[start + m]) < 0) { + offset = m; + } + else { + lastOffset = m + 1; + } + } + return offset; + } + function TimSort(array, compare) { + var minGallop = DEFAULT_MIN_GALLOPING; + var runStart; + var runLength; + var stackSize = 0; + var tmp = []; + runStart = []; + runLength = []; + function pushRun(_runStart, _runLength) { + runStart[stackSize] = _runStart; + runLength[stackSize] = _runLength; + stackSize += 1; + } + function mergeRuns() { + while (stackSize > 1) { + var n = stackSize - 2; + if ((n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1]) + || (n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1])) { + if (runLength[n - 1] < runLength[n + 1]) { + n--; + } + } + else if (runLength[n] > runLength[n + 1]) { + break; + } + mergeAt(n); + } + } + function forceMergeRuns() { + while (stackSize > 1) { + var n = stackSize - 2; + if (n > 0 && runLength[n - 1] < runLength[n + 1]) { + n--; + } + mergeAt(n); + } + } + function mergeAt(i) { + var start1 = runStart[i]; + var length1 = runLength[i]; + var start2 = runStart[i + 1]; + var length2 = runLength[i + 1]; + runLength[i] = length1 + length2; + if (i === stackSize - 3) { + runStart[i + 1] = runStart[i + 2]; + runLength[i + 1] = runLength[i + 2]; + } + stackSize--; + var k = gallopRight(array[start2], array, start1, length1, 0, compare); + start1 += k; + length1 -= k; + if (length1 === 0) { + return; + } + length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare); + if (length2 === 0) { + return; + } + if (length1 <= length2) { + mergeLow(start1, length1, start2, length2); + } + else { + mergeHigh(start1, length1, start2, length2); + } + } + function mergeLow(start1, length1, start2, length2) { + var i = 0; + for (i = 0; i < length1; i++) { + tmp[i] = array[start1 + i]; + } + var cursor1 = 0; + var cursor2 = start2; + var dest = start1; + array[dest++] = array[cursor2++]; + if (--length2 === 0) { + for (i = 0; i < length1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + return; + } + if (length1 === 1) { + for (i = 0; i < length2; i++) { + array[dest + i] = array[cursor2 + i]; + } + array[dest + length2] = tmp[cursor1]; + return; + } + var _minGallop = minGallop; + var count1; + var count2; + var exit; + while (1) { + count1 = 0; + count2 = 0; + exit = false; + do { + if (compare(array[cursor2], tmp[cursor1]) < 0) { + array[dest++] = array[cursor2++]; + count2++; + count1 = 0; + if (--length2 === 0) { + exit = true; + break; + } + } + else { + array[dest++] = tmp[cursor1++]; + count1++; + count2 = 0; + if (--length1 === 1) { + exit = true; + break; + } + } + } while ((count1 | count2) < _minGallop); + if (exit) { + break; + } + do { + count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare); + if (count1 !== 0) { + for (i = 0; i < count1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + dest += count1; + cursor1 += count1; + length1 -= count1; + if (length1 <= 1) { + exit = true; + break; + } + } + array[dest++] = array[cursor2++]; + if (--length2 === 0) { + exit = true; + break; + } + count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare); + if (count2 !== 0) { + for (i = 0; i < count2; i++) { + array[dest + i] = array[cursor2 + i]; + } + dest += count2; + cursor2 += count2; + length2 -= count2; + if (length2 === 0) { + exit = true; + break; + } + } + array[dest++] = tmp[cursor1++]; + if (--length1 === 1) { + exit = true; + break; + } + _minGallop--; + } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); + if (exit) { + break; + } + if (_minGallop < 0) { + _minGallop = 0; + } + _minGallop += 2; + } + minGallop = _minGallop; + minGallop < 1 && (minGallop = 1); + if (length1 === 1) { + for (i = 0; i < length2; i++) { + array[dest + i] = array[cursor2 + i]; + } + array[dest + length2] = tmp[cursor1]; + } + else if (length1 === 0) { + throw new Error(); + } + else { + for (i = 0; i < length1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + } + } + function mergeHigh(start1, length1, start2, length2) { + var i = 0; + for (i = 0; i < length2; i++) { + tmp[i] = array[start2 + i]; + } + var cursor1 = start1 + length1 - 1; + var cursor2 = length2 - 1; + var dest = start2 + length2 - 1; + var customCursor = 0; + var customDest = 0; + array[dest--] = array[cursor1--]; + if (--length1 === 0) { + customCursor = dest - (length2 - 1); + for (i = 0; i < length2; i++) { + array[customCursor + i] = tmp[i]; + } + return; + } + if (length2 === 1) { + dest -= length1; + cursor1 -= length1; + customDest = dest + 1; + customCursor = cursor1 + 1; + for (i = length1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + array[dest] = tmp[cursor2]; + return; + } + var _minGallop = minGallop; + while (true) { + var count1 = 0; + var count2 = 0; + var exit = false; + do { + if (compare(tmp[cursor2], array[cursor1]) < 0) { + array[dest--] = array[cursor1--]; + count1++; + count2 = 0; + if (--length1 === 0) { + exit = true; + break; + } + } + else { + array[dest--] = tmp[cursor2--]; + count2++; + count1 = 0; + if (--length2 === 1) { + exit = true; + break; + } + } + } while ((count1 | count2) < _minGallop); + if (exit) { + break; + } + do { + count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare); + if (count1 !== 0) { + dest -= count1; + cursor1 -= count1; + length1 -= count1; + customDest = dest + 1; + customCursor = cursor1 + 1; + for (i = count1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + if (length1 === 0) { + exit = true; + break; + } + } + array[dest--] = tmp[cursor2--]; + if (--length2 === 1) { + exit = true; + break; + } + count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare); + if (count2 !== 0) { + dest -= count2; + cursor2 -= count2; + length2 -= count2; + customDest = dest + 1; + customCursor = cursor2 + 1; + for (i = 0; i < count2; i++) { + array[customDest + i] = tmp[customCursor + i]; + } + if (length2 <= 1) { + exit = true; + break; + } + } + array[dest--] = array[cursor1--]; + if (--length1 === 0) { + exit = true; + break; + } + _minGallop--; + } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); + if (exit) { + break; + } + if (_minGallop < 0) { + _minGallop = 0; + } + _minGallop += 2; + } + minGallop = _minGallop; + if (minGallop < 1) { + minGallop = 1; + } + if (length2 === 1) { + dest -= length1; + cursor1 -= length1; + customDest = dest + 1; + customCursor = cursor1 + 1; + for (i = length1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + array[dest] = tmp[cursor2]; + } + else if (length2 === 0) { + throw new Error(); + } + else { + customCursor = dest - (length2 - 1); + for (i = 0; i < length2; i++) { + array[customCursor + i] = tmp[i]; + } + } + } + return { + mergeRuns: mergeRuns, + forceMergeRuns: forceMergeRuns, + pushRun: pushRun + }; + } + function sort(array, compare, lo, hi) { + if (!lo) { + lo = 0; + } + if (!hi) { + hi = array.length; + } + var remaining = hi - lo; + if (remaining < 2) { + return; + } + var runLength = 0; + if (remaining < DEFAULT_MIN_MERGE) { + runLength = makeAscendingRun(array, lo, hi, compare); + binaryInsertionSort(array, lo, hi, lo + runLength, compare); + return; + } + var ts = TimSort(array, compare); + var minRun = minRunLength(remaining); + do { + runLength = makeAscendingRun(array, lo, hi, compare); + if (runLength < minRun) { + var force = remaining; + if (force > minRun) { + force = minRun; + } + binaryInsertionSort(array, lo, lo + force, lo + runLength, compare); + runLength = force; + } + ts.pushRun(lo, runLength); + ts.mergeRuns(); + remaining -= runLength; + lo += runLength; + } while (remaining !== 0); + ts.forceMergeRuns(); + } + + var REDRAW_BIT = 1; + var STYLE_CHANGED_BIT = 2; + var SHAPE_CHANGED_BIT = 4; + + var invalidZErrorLogged = false; + function logInvalidZError() { + if (invalidZErrorLogged) { + return; + } + invalidZErrorLogged = true; + console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors'); + } + function shapeCompareFunc(a, b) { + if (a.zlevel === b.zlevel) { + if (a.z === b.z) { + return a.z2 - b.z2; + } + return a.z - b.z; + } + return a.zlevel - b.zlevel; + } + var Storage = (function () { + function Storage() { + this._roots = []; + this._displayList = []; + this._displayListLen = 0; + this.displayableSortFunc = shapeCompareFunc; + } + Storage.prototype.traverse = function (cb, context) { + for (var i = 0; i < this._roots.length; i++) { + this._roots[i].traverse(cb, context); + } + }; + Storage.prototype.getDisplayList = function (update, includeIgnore) { + includeIgnore = includeIgnore || false; + var displayList = this._displayList; + if (update || !displayList.length) { + this.updateDisplayList(includeIgnore); + } + return displayList; + }; + Storage.prototype.updateDisplayList = function (includeIgnore) { + this._displayListLen = 0; + var roots = this._roots; + var displayList = this._displayList; + for (var i = 0, len = roots.length; i < len; i++) { + this._updateAndAddDisplayable(roots[i], null, includeIgnore); + } + displayList.length = this._displayListLen; + sort(displayList, shapeCompareFunc); + }; + Storage.prototype._updateAndAddDisplayable = function (el, clipPaths, includeIgnore) { + if (el.ignore && !includeIgnore) { + return; + } + el.beforeUpdate(); + el.update(); + el.afterUpdate(); + var userSetClipPath = el.getClipPath(); + if (el.ignoreClip) { + clipPaths = null; + } + else if (userSetClipPath) { + if (clipPaths) { + clipPaths = clipPaths.slice(); + } + else { + clipPaths = []; + } + var currentClipPath = userSetClipPath; + var parentClipPath = el; + while (currentClipPath) { + currentClipPath.parent = parentClipPath; + currentClipPath.updateTransform(); + clipPaths.push(currentClipPath); + parentClipPath = currentClipPath; + currentClipPath = currentClipPath.getClipPath(); + } + } + if (el.childrenRef) { + var children = el.childrenRef(); + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (el.__dirty) { + child.__dirty |= REDRAW_BIT; + } + this._updateAndAddDisplayable(child, clipPaths, includeIgnore); + } + el.__dirty = 0; + } + else { + var disp = el; + if (clipPaths && clipPaths.length) { + disp.__clipPaths = clipPaths; + } + else if (disp.__clipPaths && disp.__clipPaths.length > 0) { + disp.__clipPaths = []; + } + if (isNaN(disp.z)) { + logInvalidZError(); + disp.z = 0; + } + if (isNaN(disp.z2)) { + logInvalidZError(); + disp.z2 = 0; + } + if (isNaN(disp.zlevel)) { + logInvalidZError(); + disp.zlevel = 0; + } + this._displayList[this._displayListLen++] = disp; + } + var decalEl = el.getDecalElement && el.getDecalElement(); + if (decalEl) { + this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore); + } + var textGuide = el.getTextGuideLine(); + if (textGuide) { + this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore); + } + var textEl = el.getTextContent(); + if (textEl) { + this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore); + } + }; + Storage.prototype.addRoot = function (el) { + if (el.__zr && el.__zr.storage === this) { + return; + } + this._roots.push(el); + }; + Storage.prototype.delRoot = function (el) { + if (el instanceof Array) { + for (var i = 0, l = el.length; i < l; i++) { + this.delRoot(el[i]); + } + return; + } + var idx = indexOf(this._roots, el); + if (idx >= 0) { + this._roots.splice(idx, 1); + } + }; + Storage.prototype.delAllRoots = function () { + this._roots = []; + this._displayList = []; + this._displayListLen = 0; + return; + }; + Storage.prototype.getRoots = function () { + return this._roots; + }; + Storage.prototype.dispose = function () { + this._displayList = null; + this._roots = null; + }; + return Storage; + }()); + + var requestAnimationFrame; + requestAnimationFrame = (env.hasGlobalWindow + && ((window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) + || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window)) + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame)) || function (func) { + return setTimeout(func, 16); + }; + var requestAnimationFrame$1 = requestAnimationFrame; + + var easingFuncs = { + linear: function (k) { + return k; + }, + quadraticIn: function (k) { + return k * k; + }, + quadraticOut: function (k) { + return k * (2 - k); + }, + quadraticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k; + } + return -0.5 * (--k * (k - 2) - 1); + }, + cubicIn: function (k) { + return k * k * k; + }, + cubicOut: function (k) { + return --k * k * k + 1; + }, + cubicInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k; + } + return 0.5 * ((k -= 2) * k * k + 2); + }, + quarticIn: function (k) { + return k * k * k * k; + }, + quarticOut: function (k) { + return 1 - (--k * k * k * k); + }, + quarticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k; + } + return -0.5 * ((k -= 2) * k * k * k - 2); + }, + quinticIn: function (k) { + return k * k * k * k * k; + }, + quinticOut: function (k) { + return --k * k * k * k * k + 1; + }, + quinticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k * k; + } + return 0.5 * ((k -= 2) * k * k * k * k + 2); + }, + sinusoidalIn: function (k) { + return 1 - Math.cos(k * Math.PI / 2); + }, + sinusoidalOut: function (k) { + return Math.sin(k * Math.PI / 2); + }, + sinusoidalInOut: function (k) { + return 0.5 * (1 - Math.cos(Math.PI * k)); + }, + exponentialIn: function (k) { + return k === 0 ? 0 : Math.pow(1024, k - 1); + }, + exponentialOut: function (k) { + return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); + }, + exponentialInOut: function (k) { + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if ((k *= 2) < 1) { + return 0.5 * Math.pow(1024, k - 1); + } + return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2); + }, + circularIn: function (k) { + return 1 - Math.sqrt(1 - k * k); + }, + circularOut: function (k) { + return Math.sqrt(1 - (--k * k)); + }, + circularInOut: function (k) { + if ((k *= 2) < 1) { + return -0.5 * (Math.sqrt(1 - k * k) - 1); + } + return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); + }, + elasticIn: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; + s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return -(a * Math.pow(2, 10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p)); + }, + elasticOut: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; + s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return (a * Math.pow(2, -10 * k) + * Math.sin((k - s) * (2 * Math.PI) / p) + 1); + }, + elasticInOut: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; + s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + if ((k *= 2) < 1) { + return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; + }, + backIn: function (k) { + var s = 1.70158; + return k * k * ((s + 1) * k - s); + }, + backOut: function (k) { + var s = 1.70158; + return --k * k * ((s + 1) * k + s) + 1; + }, + backInOut: function (k) { + var s = 1.70158 * 1.525; + if ((k *= 2) < 1) { + return 0.5 * (k * k * ((s + 1) * k - s)); + } + return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); + }, + bounceIn: function (k) { + return 1 - easingFuncs.bounceOut(1 - k); + }, + bounceOut: function (k) { + if (k < (1 / 2.75)) { + return 7.5625 * k * k; + } + else if (k < (2 / 2.75)) { + return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; + } + else if (k < (2.5 / 2.75)) { + return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; + } + else { + return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; + } + }, + bounceInOut: function (k) { + if (k < 0.5) { + return easingFuncs.bounceIn(k * 2) * 0.5; + } + return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5; + } + }; + + var mathPow = Math.pow; + var mathSqrt = Math.sqrt; + var EPSILON = 1e-8; + var EPSILON_NUMERIC = 1e-4; + var THREE_SQRT = mathSqrt(3); + var ONE_THIRD = 1 / 3; + var _v0 = create(); + var _v1 = create(); + var _v2 = create(); + function isAroundZero(val) { + return val > -EPSILON && val < EPSILON; + } + function isNotAroundZero(val) { + return val > EPSILON || val < -EPSILON; + } + function cubicAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return onet * onet * (onet * p0 + 3 * t * p1) + + t * t * (t * p3 + 3 * onet * p2); + } + function cubicDerivativeAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return 3 * (((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet + + (p3 - p2) * t * t); + } + function cubicRootAt(p0, p1, p2, p3, val, roots) { + var a = p3 + 3 * (p1 - p2) - p0; + var b = 3 * (p2 - p1 * 2 + p0); + var c = 3 * (p1 - p0); + var d = p0 - val; + var A = b * b - 3 * a * c; + var B = b * c - 9 * a * d; + var C = c * c - 3 * b * d; + var n = 0; + if (isAroundZero(A) && isAroundZero(B)) { + if (isAroundZero(b)) { + roots[0] = 0; + } + else { + var t1 = -c / b; + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + } + else { + var disc = B * B - 4 * A * C; + if (isAroundZero(disc)) { + var K = B / A; + var t1 = -b / a + K; + var t2 = -K / 2; + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var Y1 = A * b + 1.5 * a * (-B + discSqrt); + var Y2 = A * b + 1.5 * a * (-B - discSqrt); + if (Y1 < 0) { + Y1 = -mathPow(-Y1, ONE_THIRD); + } + else { + Y1 = mathPow(Y1, ONE_THIRD); + } + if (Y2 < 0) { + Y2 = -mathPow(-Y2, ONE_THIRD); + } + else { + Y2 = mathPow(Y2, ONE_THIRD); + } + var t1 = (-b - (Y1 + Y2)) / (3 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + else { + var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A)); + var theta = Math.acos(T) / 3; + var ASqrt = mathSqrt(A); + var tmp = Math.cos(theta); + var t1 = (-b - 2 * ASqrt * tmp) / (3 * a); + var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a); + var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + if (t3 >= 0 && t3 <= 1) { + roots[n++] = t3; + } + } + } + return n; + } + function cubicExtrema(p0, p1, p2, p3, extrema) { + var b = 6 * p2 - 12 * p1 + 6 * p0; + var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2; + var c = 3 * p1 - 3 * p0; + var n = 0; + if (isAroundZero(a)) { + if (isNotAroundZero(b)) { + var t1 = -c / b; + if (t1 >= 0 && t1 <= 1) { + extrema[n++] = t1; + } + } + } + else { + var disc = b * b - 4 * a * c; + if (isAroundZero(disc)) { + extrema[0] = -b / (2 * a); + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var t1 = (-b + discSqrt) / (2 * a); + var t2 = (-b - discSqrt) / (2 * a); + if (t1 >= 0 && t1 <= 1) { + extrema[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + extrema[n++] = t2; + } + } + } + return n; + } + function cubicSubdivide(p0, p1, p2, p3, t, out) { + var p01 = (p1 - p0) * t + p0; + var p12 = (p2 - p1) * t + p1; + var p23 = (p3 - p2) * t + p2; + var p012 = (p12 - p01) * t + p01; + var p123 = (p23 - p12) * t + p12; + var p0123 = (p123 - p012) * t + p012; + out[0] = p0; + out[1] = p01; + out[2] = p012; + out[3] = p0123; + out[4] = p0123; + out[5] = p123; + out[6] = p23; + out[7] = p3; + } + function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) { + var t; + var interval = 0.005; + var d = Infinity; + var prev; + var next; + var d1; + var d2; + _v0[0] = x; + _v0[1] = y; + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = cubicAt(x0, x1, x2, x3, _t); + _v1[1] = cubicAt(y0, y1, y2, y3, _t); + d1 = distSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + prev = t - interval; + next = t + interval; + _v1[0] = cubicAt(x0, x1, x2, x3, prev); + _v1[1] = cubicAt(y0, y1, y2, y3, prev); + d1 = distSquare(_v1, _v0); + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } + else { + _v2[0] = cubicAt(x0, x1, x2, x3, next); + _v2[1] = cubicAt(y0, y1, y2, y3, next); + d2 = distSquare(_v2, _v0); + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } + else { + interval *= 0.5; + } + } + } + if (out) { + out[0] = cubicAt(x0, x1, x2, x3, t); + out[1] = cubicAt(y0, y1, y2, y3, t); + } + return mathSqrt(d); + } + function cubicLength(x0, y0, x1, y1, x2, y2, x3, y3, iteration) { + var px = x0; + var py = y0; + var d = 0; + var step = 1 / iteration; + for (var i = 1; i <= iteration; i++) { + var t = i * step; + var x = cubicAt(x0, x1, x2, x3, t); + var y = cubicAt(y0, y1, y2, y3, t); + var dx = x - px; + var dy = y - py; + d += Math.sqrt(dx * dx + dy * dy); + px = x; + py = y; + } + return d; + } + function quadraticAt(p0, p1, p2, t) { + var onet = 1 - t; + return onet * (onet * p0 + 2 * t * p1) + t * t * p2; + } + function quadraticDerivativeAt(p0, p1, p2, t) { + return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1)); + } + function quadraticRootAt(p0, p1, p2, val, roots) { + var a = p0 - 2 * p1 + p2; + var b = 2 * (p1 - p0); + var c = p0 - val; + var n = 0; + if (isAroundZero(a)) { + if (isNotAroundZero(b)) { + var t1 = -c / b; + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + } + else { + var disc = b * b - 4 * a * c; + if (isAroundZero(disc)) { + var t1 = -b / (2 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var t1 = (-b + discSqrt) / (2 * a); + var t2 = (-b - discSqrt) / (2 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + } + } + return n; + } + function quadraticExtremum(p0, p1, p2) { + var divider = p0 + p2 - 2 * p1; + if (divider === 0) { + return 0.5; + } + else { + return (p0 - p1) / divider; + } + } + function quadraticSubdivide(p0, p1, p2, t, out) { + var p01 = (p1 - p0) * t + p0; + var p12 = (p2 - p1) * t + p1; + var p012 = (p12 - p01) * t + p01; + out[0] = p0; + out[1] = p01; + out[2] = p012; + out[3] = p012; + out[4] = p12; + out[5] = p2; + } + function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) { + var t; + var interval = 0.005; + var d = Infinity; + _v0[0] = x; + _v0[1] = y; + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = quadraticAt(x0, x1, x2, _t); + _v1[1] = quadraticAt(y0, y1, y2, _t); + var d1 = distSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + var prev = t - interval; + var next = t + interval; + _v1[0] = quadraticAt(x0, x1, x2, prev); + _v1[1] = quadraticAt(y0, y1, y2, prev); + var d1 = distSquare(_v1, _v0); + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } + else { + _v2[0] = quadraticAt(x0, x1, x2, next); + _v2[1] = quadraticAt(y0, y1, y2, next); + var d2 = distSquare(_v2, _v0); + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } + else { + interval *= 0.5; + } + } + } + if (out) { + out[0] = quadraticAt(x0, x1, x2, t); + out[1] = quadraticAt(y0, y1, y2, t); + } + return mathSqrt(d); + } + function quadraticLength(x0, y0, x1, y1, x2, y2, iteration) { + var px = x0; + var py = y0; + var d = 0; + var step = 1 / iteration; + for (var i = 1; i <= iteration; i++) { + var t = i * step; + var x = quadraticAt(x0, x1, x2, t); + var y = quadraticAt(y0, y1, y2, t); + var dx = x - px; + var dy = y - py; + d += Math.sqrt(dx * dx + dy * dy); + px = x; + py = y; + } + return d; + } + + var regexp = /cubic-bezier\(([0-9,\.e ]+)\)/; + function createCubicEasingFunc(cubicEasingStr) { + var cubic = cubicEasingStr && regexp.exec(cubicEasingStr); + if (cubic) { + var points = cubic[1].split(','); + var a_1 = +trim(points[0]); + var b_1 = +trim(points[1]); + var c_1 = +trim(points[2]); + var d_1 = +trim(points[3]); + if (isNaN(a_1 + b_1 + c_1 + d_1)) { + return; + } + var roots_1 = []; + return function (p) { + return p <= 0 + ? 0 : p >= 1 + ? 1 + : cubicRootAt(0, a_1, c_1, 1, p, roots_1) && cubicAt(0, b_1, d_1, 1, roots_1[0]); + }; + } + } + + var Clip = (function () { + function Clip(opts) { + this._inited = false; + this._startTime = 0; + this._pausedTime = 0; + this._paused = false; + this._life = opts.life || 1000; + this._delay = opts.delay || 0; + this.loop = opts.loop || false; + this.onframe = opts.onframe || noop; + this.ondestroy = opts.ondestroy || noop; + this.onrestart = opts.onrestart || noop; + opts.easing && this.setEasing(opts.easing); + } + Clip.prototype.step = function (globalTime, deltaTime) { + if (!this._inited) { + this._startTime = globalTime + this._delay; + this._inited = true; + } + if (this._paused) { + this._pausedTime += deltaTime; + return; + } + var life = this._life; + var elapsedTime = globalTime - this._startTime - this._pausedTime; + var percent = elapsedTime / life; + if (percent < 0) { + percent = 0; + } + percent = Math.min(percent, 1); + var easingFunc = this.easingFunc; + var schedule = easingFunc ? easingFunc(percent) : percent; + this.onframe(schedule); + if (percent === 1) { + if (this.loop) { + var remainder = elapsedTime % life; + this._startTime = globalTime - remainder; + this._pausedTime = 0; + this.onrestart(); + } + else { + return true; + } + } + return false; + }; + Clip.prototype.pause = function () { + this._paused = true; + }; + Clip.prototype.resume = function () { + this._paused = false; + }; + Clip.prototype.setEasing = function (easing) { + this.easing = easing; + this.easingFunc = isFunction(easing) + ? easing + : easingFuncs[easing] || createCubicEasingFunc(easing); + }; + return Clip; + }()); + + var Entry = (function () { + function Entry(val) { + this.value = val; + } + return Entry; + }()); + var LinkedList = (function () { + function LinkedList() { + this._len = 0; + } + LinkedList.prototype.insert = function (val) { + var entry = new Entry(val); + this.insertEntry(entry); + return entry; + }; + LinkedList.prototype.insertEntry = function (entry) { + if (!this.head) { + this.head = this.tail = entry; + } + else { + this.tail.next = entry; + entry.prev = this.tail; + entry.next = null; + this.tail = entry; + } + this._len++; + }; + LinkedList.prototype.remove = function (entry) { + var prev = entry.prev; + var next = entry.next; + if (prev) { + prev.next = next; + } + else { + this.head = next; + } + if (next) { + next.prev = prev; + } + else { + this.tail = prev; + } + entry.next = entry.prev = null; + this._len--; + }; + LinkedList.prototype.len = function () { + return this._len; + }; + LinkedList.prototype.clear = function () { + this.head = this.tail = null; + this._len = 0; + }; + return LinkedList; + }()); + var LRU = (function () { + function LRU(maxSize) { + this._list = new LinkedList(); + this._maxSize = 10; + this._map = {}; + this._maxSize = maxSize; + } + LRU.prototype.put = function (key, value) { + var list = this._list; + var map = this._map; + var removed = null; + if (map[key] == null) { + var len = list.len(); + var entry = this._lastRemovedEntry; + if (len >= this._maxSize && len > 0) { + var leastUsedEntry = list.head; + list.remove(leastUsedEntry); + delete map[leastUsedEntry.key]; + removed = leastUsedEntry.value; + this._lastRemovedEntry = leastUsedEntry; + } + if (entry) { + entry.value = value; + } + else { + entry = new Entry(value); + } + entry.key = key; + list.insertEntry(entry); + map[key] = entry; + } + return removed; + }; + LRU.prototype.get = function (key) { + var entry = this._map[key]; + var list = this._list; + if (entry != null) { + if (entry !== list.tail) { + list.remove(entry); + list.insertEntry(entry); + } + return entry.value; + } + }; + LRU.prototype.clear = function () { + this._list.clear(); + this._map = {}; + }; + LRU.prototype.len = function () { + return this._list.len(); + }; + return LRU; + }()); + + var kCSSColorTable = { + 'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1], + 'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1], + 'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1], + 'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1], + 'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1], + 'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1], + 'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1], + 'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1], + 'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1], + 'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1], + 'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1], + 'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1], + 'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1], + 'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1], + 'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1], + 'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1], + 'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1], + 'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1], + 'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1], + 'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1], + 'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1], + 'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1], + 'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1], + 'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1], + 'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1], + 'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1], + 'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1], + 'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1], + 'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1], + 'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1], + 'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1], + 'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1], + 'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1], + 'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1], + 'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1], + 'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1], + 'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1], + 'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1], + 'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1], + 'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1], + 'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1], + 'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1], + 'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1], + 'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1], + 'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1], + 'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1], + 'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1], + 'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1], + 'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1], + 'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1], + 'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1], + 'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1], + 'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1], + 'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1], + 'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1], + 'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1], + 'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1], + 'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1], + 'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1], + 'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1], + 'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1], + 'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1], + 'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1], + 'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1], + 'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1], + 'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1], + 'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1], + 'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1], + 'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1], + 'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1], + 'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1], + 'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1], + 'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1], + 'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1] + }; + function clampCssByte(i) { + i = Math.round(i); + return i < 0 ? 0 : i > 255 ? 255 : i; + } + function clampCssAngle(i) { + i = Math.round(i); + return i < 0 ? 0 : i > 360 ? 360 : i; + } + function clampCssFloat(f) { + return f < 0 ? 0 : f > 1 ? 1 : f; + } + function parseCssInt(val) { + var str = val; + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssByte(parseFloat(str) / 100 * 255); + } + return clampCssByte(parseInt(str, 10)); + } + function parseCssFloat(val) { + var str = val; + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssFloat(parseFloat(str) / 100); + } + return clampCssFloat(parseFloat(str)); + } + function cssHueToRgb(m1, m2, h) { + if (h < 0) { + h += 1; + } + else if (h > 1) { + h -= 1; + } + if (h * 6 < 1) { + return m1 + (m2 - m1) * h * 6; + } + if (h * 2 < 1) { + return m2; + } + if (h * 3 < 2) { + return m1 + (m2 - m1) * (2 / 3 - h) * 6; + } + return m1; + } + function lerpNumber(a, b, p) { + return a + (b - a) * p; + } + function setRgba(out, r, g, b, a) { + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = a; + return out; + } + function copyRgba(out, a) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + return out; + } + var colorCache = new LRU(20); + var lastRemovedArr = null; + function putToCache(colorStr, rgbaArr) { + if (lastRemovedArr) { + copyRgba(lastRemovedArr, rgbaArr); + } + lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); + } + function parse(colorStr, rgbaArr) { + if (!colorStr) { + return; + } + rgbaArr = rgbaArr || []; + var cached = colorCache.get(colorStr); + if (cached) { + return copyRgba(rgbaArr, cached); + } + colorStr = colorStr + ''; + var str = colorStr.replace(/ /g, '').toLowerCase(); + if (str in kCSSColorTable) { + copyRgba(rgbaArr, kCSSColorTable[str]); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + var strLen = str.length; + if (str.charAt(0) === '#') { + if (strLen === 4 || strLen === 5) { + var iv = parseInt(str.slice(1, 4), 16); + if (!(iv >= 0 && iv <= 0xfff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + setRgba(rgbaArr, ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), (iv & 0xf0) | ((iv & 0xf0) >> 4), (iv & 0xf) | ((iv & 0xf) << 4), strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + else if (strLen === 7 || strLen === 9) { + var iv = parseInt(str.slice(1, 7), 16); + if (!(iv >= 0 && iv <= 0xffffff)) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + return; + } + var op = str.indexOf('('); + var ep = str.indexOf(')'); + if (op !== -1 && ep + 1 === strLen) { + var fname = str.substr(0, op); + var params = str.substr(op + 1, ep - (op + 1)).split(','); + var alpha = 1; + switch (fname) { + case 'rgba': + if (params.length !== 4) { + return params.length === 3 + ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1) + : setRgba(rgbaArr, 0, 0, 0, 1); + } + alpha = parseCssFloat(params.pop()); + case 'rgb': + if (params.length >= 3) { + setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), params.length === 3 ? alpha : parseCssFloat(params[3])); + putToCache(colorStr, rgbaArr); + return rgbaArr; + } + else { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + case 'hsla': + if (params.length !== 4) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + params[3] = parseCssFloat(params[3]); + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + case 'hsl': + if (params.length !== 3) { + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + hsla2rgba(params, rgbaArr); + putToCache(colorStr, rgbaArr); + return rgbaArr; + default: + return; + } + } + setRgba(rgbaArr, 0, 0, 0, 1); + return; + } + function hsla2rgba(hsla, rgba) { + var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; + var s = parseCssFloat(hsla[1]); + var l = parseCssFloat(hsla[2]); + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + rgba = rgba || []; + setRgba(rgba, clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), clampCssByte(cssHueToRgb(m1, m2, h) * 255), clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255), 1); + if (hsla.length === 4) { + rgba[3] = hsla[3]; + } + return rgba; + } + function rgba2hsla(rgba) { + if (!rgba) { + return; + } + var R = rgba[0] / 255; + var G = rgba[1] / 255; + var B = rgba[2] / 255; + var vMin = Math.min(R, G, B); + var vMax = Math.max(R, G, B); + var delta = vMax - vMin; + var L = (vMax + vMin) / 2; + var H; + var S; + if (delta === 0) { + H = 0; + S = 0; + } + else { + if (L < 0.5) { + S = delta / (vMax + vMin); + } + else { + S = delta / (2 - vMax - vMin); + } + var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; + var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; + var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + if (R === vMax) { + H = deltaB - deltaG; + } + else if (G === vMax) { + H = (1 / 3) + deltaR - deltaB; + } + else if (B === vMax) { + H = (2 / 3) + deltaG - deltaR; + } + if (H < 0) { + H += 1; + } + if (H > 1) { + H -= 1; + } + } + var hsla = [H * 360, S, L]; + if (rgba[3] != null) { + hsla.push(rgba[3]); + } + return hsla; + } + function lift(color, level) { + var colorArr = parse(color); + if (colorArr) { + for (var i = 0; i < 3; i++) { + if (level < 0) { + colorArr[i] = colorArr[i] * (1 - level) | 0; + } + else { + colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; + } + if (colorArr[i] > 255) { + colorArr[i] = 255; + } + else if (colorArr[i] < 0) { + colorArr[i] = 0; + } + } + return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); + } + } + function toHex(color) { + var colorArr = parse(color); + if (colorArr) { + return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); + } + } + function fastLerp(normalizedValue, colors, out) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1)) { + return; + } + out = out || []; + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colors[leftIndex]; + var rightColor = colors[rightIndex]; + var dv = value - leftIndex; + out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)); + out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)); + out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)); + out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)); + return out; + } + var fastMapToColor = fastLerp; + function lerp$1(normalizedValue, colors, fullOutput) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1)) { + return; + } + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = parse(colors[leftIndex]); + var rightColor = parse(colors[rightIndex]); + var dv = value - leftIndex; + var color = stringify([ + clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)), + clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)), + clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)), + clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv)) + ], 'rgba'); + return fullOutput + ? { + color: color, + leftIndex: leftIndex, + rightIndex: rightIndex, + value: value + } + : color; + } + var mapToColor = lerp$1; + function modifyHSL(color, h, s, l) { + var colorArr = parse(color); + if (color) { + colorArr = rgba2hsla(colorArr); + h != null && (colorArr[0] = clampCssAngle(h)); + s != null && (colorArr[1] = parseCssFloat(s)); + l != null && (colorArr[2] = parseCssFloat(l)); + return stringify(hsla2rgba(colorArr), 'rgba'); + } + } + function modifyAlpha(color, alpha) { + var colorArr = parse(color); + if (colorArr && alpha != null) { + colorArr[3] = clampCssFloat(alpha); + return stringify(colorArr, 'rgba'); + } + } + function stringify(arrColor, type) { + if (!arrColor || !arrColor.length) { + return; + } + var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; + if (type === 'rgba' || type === 'hsva' || type === 'hsla') { + colorStr += ',' + arrColor[3]; + } + return type + '(' + colorStr + ')'; + } + function lum(color, backgroundLum) { + var arr = parse(color); + return arr + ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255 + + (1 - arr[3]) * backgroundLum + : 0; + } + function random() { + return stringify([ + Math.round(Math.random() * 255), + Math.round(Math.random() * 255), + Math.round(Math.random() * 255) + ], 'rgb'); + } + var liftedColorCache = new LRU(100); + function liftColor(color) { + if (isString(color)) { + var liftedColor = liftedColorCache.get(color); + if (!liftedColor) { + liftedColor = lift(color, -0.1); + liftedColorCache.put(color, liftedColor); + } + return liftedColor; + } + else if (isGradientObject(color)) { + var ret = extend({}, color); + ret.colorStops = map(color.colorStops, function (stop) { return ({ + offset: stop.offset, + color: lift(stop.color, -0.1) + }); }); + return ret; + } + return color; + } + + var color = /*#__PURE__*/Object.freeze({ + __proto__: null, + parse: parse, + lift: lift, + toHex: toHex, + fastLerp: fastLerp, + fastMapToColor: fastMapToColor, + lerp: lerp$1, + mapToColor: mapToColor, + modifyHSL: modifyHSL, + modifyAlpha: modifyAlpha, + stringify: stringify, + lum: lum, + random: random, + liftColor: liftColor + }); + + var mathRound = Math.round; + function normalizeColor(color) { + var opacity; + if (!color || color === 'transparent') { + color = 'none'; + } + else if (typeof color === 'string' && color.indexOf('rgba') > -1) { + var arr = parse(color); + if (arr) { + color = 'rgb(' + arr[0] + ',' + arr[1] + ',' + arr[2] + ')'; + opacity = arr[3]; + } + } + return { + color: color, + opacity: opacity == null ? 1 : opacity + }; + } + var EPSILON$1 = 1e-4; + function isAroundZero$1(transform) { + return transform < EPSILON$1 && transform > -EPSILON$1; + } + function round3(transform) { + return mathRound(transform * 1e3) / 1e3; + } + function round4(transform) { + return mathRound(transform * 1e4) / 1e4; + } + function getMatrixStr(m) { + return 'matrix(' + + round3(m[0]) + ',' + + round3(m[1]) + ',' + + round3(m[2]) + ',' + + round3(m[3]) + ',' + + round4(m[4]) + ',' + + round4(m[5]) + + ')'; + } + var TEXT_ALIGN_TO_ANCHOR = { + left: 'start', + right: 'end', + center: 'middle', + middle: 'middle' + }; + function adjustTextY(y, lineHeight, textBaseline) { + if (textBaseline === 'top') { + y += lineHeight / 2; + } + else if (textBaseline === 'bottom') { + y -= lineHeight / 2; + } + return y; + } + function hasShadow(style) { + return style + && (style.shadowBlur || style.shadowOffsetX || style.shadowOffsetY); + } + function getShadowKey(displayable) { + var style = displayable.style; + var globalScale = displayable.getGlobalScale(); + return [ + style.shadowColor, + (style.shadowBlur || 0).toFixed(2), + (style.shadowOffsetX || 0).toFixed(2), + (style.shadowOffsetY || 0).toFixed(2), + globalScale[0], + globalScale[1] + ].join(','); + } + function isImagePattern(val) { + return val && (!!val.image); + } + function isSVGPattern(val) { + return val && (!!val.svgElement); + } + function isPattern(val) { + return isImagePattern(val) || isSVGPattern(val); + } + function isLinearGradient(val) { + return val.type === 'linear'; + } + function isRadialGradient(val) { + return val.type === 'radial'; + } + function isGradient(val) { + return val && (val.type === 'linear' + || val.type === 'radial'); + } + function getIdURL(id) { + return "url(#" + id + ")"; + } + function getPathPrecision(el) { + var scale = el.getGlobalScale(); + var size = Math.max(scale[0], scale[1]); + return Math.max(Math.ceil(Math.log(size) / Math.log(10)), 1); + } + function getSRTTransformString(transform) { + var x = transform.x || 0; + var y = transform.y || 0; + var rotation = (transform.rotation || 0) * RADIAN_TO_DEGREE; + var scaleX = retrieve2(transform.scaleX, 1); + var scaleY = retrieve2(transform.scaleY, 1); + var skewX = transform.skewX || 0; + var skewY = transform.skewY || 0; + var res = []; + if (x || y) { + res.push("translate(" + x + "px," + y + "px)"); + } + if (rotation) { + res.push("rotate(" + rotation + ")"); + } + if (scaleX !== 1 || scaleY !== 1) { + res.push("scale(" + scaleX + "," + scaleY + ")"); + } + if (skewX || skewY) { + res.push("skew(" + mathRound(skewX * RADIAN_TO_DEGREE) + "deg, " + mathRound(skewY * RADIAN_TO_DEGREE) + "deg)"); + } + return res.join(' '); + } + var encodeBase64 = (function () { + if (env.hasGlobalWindow && isFunction(window.btoa)) { + return function (str) { + return window.btoa(unescape(encodeURIComponent(str))); + }; + } + if (typeof Buffer !== 'undefined') { + return function (str) { + return Buffer.from(str).toString('base64'); + }; + } + return function (str) { + if ("development" !== 'production') { + logError('Base64 isn\'t natively supported in the current environment.'); + } + return null; + }; + })(); + + var arraySlice = Array.prototype.slice; + function interpolateNumber(p0, p1, percent) { + return (p1 - p0) * percent + p0; + } + function interpolate1DArray(out, p0, p1, percent) { + var len = p0.length; + for (var i = 0; i < len; i++) { + out[i] = interpolateNumber(p0[i], p1[i], percent); + } + return out; + } + function interpolate2DArray(out, p0, p1, percent) { + var len = p0.length; + var len2 = len && p0[0].length; + for (var i = 0; i < len; i++) { + if (!out[i]) { + out[i] = []; + } + for (var j = 0; j < len2; j++) { + out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent); + } + } + return out; + } + function add1DArray(out, p0, p1, sign) { + var len = p0.length; + for (var i = 0; i < len; i++) { + out[i] = p0[i] + p1[i] * sign; + } + return out; + } + function add2DArray(out, p0, p1, sign) { + var len = p0.length; + var len2 = len && p0[0].length; + for (var i = 0; i < len; i++) { + if (!out[i]) { + out[i] = []; + } + for (var j = 0; j < len2; j++) { + out[i][j] = p0[i][j] + p1[i][j] * sign; + } + } + return out; + } + function fillColorStops(val0, val1) { + var len0 = val0.length; + var len1 = val1.length; + var shorterArr = len0 > len1 ? val1 : val0; + var shorterLen = Math.min(len0, len1); + var last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 }; + for (var i = shorterLen; i < Math.max(len0, len1); i++) { + shorterArr.push({ + offset: last.offset, + color: last.color.slice() + }); + } + } + function fillArray(val0, val1, arrDim) { + var arr0 = val0; + var arr1 = val1; + if (!arr0.push || !arr1.push) { + return; + } + var arr0Len = arr0.length; + var arr1Len = arr1.length; + if (arr0Len !== arr1Len) { + var isPreviousLarger = arr0Len > arr1Len; + if (isPreviousLarger) { + arr0.length = arr1Len; + } + else { + for (var i = arr0Len; i < arr1Len; i++) { + arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])); + } + } + } + var len2 = arr0[0] && arr0[0].length; + for (var i = 0; i < arr0.length; i++) { + if (arrDim === 1) { + if (isNaN(arr0[i])) { + arr0[i] = arr1[i]; + } + } + else { + for (var j = 0; j < len2; j++) { + if (isNaN(arr0[i][j])) { + arr0[i][j] = arr1[i][j]; + } + } + } + } + } + function cloneValue(value) { + if (isArrayLike(value)) { + var len = value.length; + if (isArrayLike(value[0])) { + var ret = []; + for (var i = 0; i < len; i++) { + ret.push(arraySlice.call(value[i])); + } + return ret; + } + return arraySlice.call(value); + } + return value; + } + function rgba2String(rgba) { + rgba[0] = Math.floor(rgba[0]) || 0; + rgba[1] = Math.floor(rgba[1]) || 0; + rgba[2] = Math.floor(rgba[2]) || 0; + rgba[3] = rgba[3] == null ? 1 : rgba[3]; + return 'rgba(' + rgba.join(',') + ')'; + } + function guessArrayDim(value) { + return isArrayLike(value && value[0]) ? 2 : 1; + } + var VALUE_TYPE_NUMBER = 0; + var VALUE_TYPE_1D_ARRAY = 1; + var VALUE_TYPE_2D_ARRAY = 2; + var VALUE_TYPE_COLOR = 3; + var VALUE_TYPE_LINEAR_GRADIENT = 4; + var VALUE_TYPE_RADIAL_GRADIENT = 5; + var VALUE_TYPE_UNKOWN = 6; + function isGradientValueType(valType) { + return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT; + } + function isArrayValueType(valType) { + return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY; + } + var tmpRgba = [0, 0, 0, 0]; + var Track = (function () { + function Track(propName) { + this.keyframes = []; + this.discrete = false; + this._invalid = false; + this._needsSort = false; + this._lastFr = 0; + this._lastFrP = 0; + this.propName = propName; + } + Track.prototype.isFinished = function () { + return this._finished; + }; + Track.prototype.setFinished = function () { + this._finished = true; + if (this._additiveTrack) { + this._additiveTrack.setFinished(); + } + }; + Track.prototype.needsAnimate = function () { + return this.keyframes.length >= 1; + }; + Track.prototype.getAdditiveTrack = function () { + return this._additiveTrack; + }; + Track.prototype.addKeyframe = function (time, rawValue, easing) { + this._needsSort = true; + var keyframes = this.keyframes; + var len = keyframes.length; + var discrete = false; + var valType = VALUE_TYPE_UNKOWN; + var value = rawValue; + if (isArrayLike(rawValue)) { + var arrayDim = guessArrayDim(rawValue); + valType = arrayDim; + if (arrayDim === 1 && !isNumber(rawValue[0]) + || arrayDim === 2 && !isNumber(rawValue[0][0])) { + discrete = true; + } + } + else { + if (isNumber(rawValue) && !eqNaN(rawValue)) { + valType = VALUE_TYPE_NUMBER; + } + else if (isString(rawValue)) { + if (!isNaN(+rawValue)) { + valType = VALUE_TYPE_NUMBER; + } + else { + var colorArray = parse(rawValue); + if (colorArray) { + value = colorArray; + valType = VALUE_TYPE_COLOR; + } + } + } + else if (isGradientObject(rawValue)) { + var parsedGradient = extend({}, value); + parsedGradient.colorStops = map(rawValue.colorStops, function (colorStop) { return ({ + offset: colorStop.offset, + color: parse(colorStop.color) + }); }); + if (isLinearGradient(rawValue)) { + valType = VALUE_TYPE_LINEAR_GRADIENT; + } + else if (isRadialGradient(rawValue)) { + valType = VALUE_TYPE_RADIAL_GRADIENT; + } + value = parsedGradient; + } + } + if (len === 0) { + this.valType = valType; + } + else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) { + discrete = true; + } + this.discrete = this.discrete || discrete; + var kf = { + time: time, + value: value, + rawValue: rawValue, + percent: 0 + }; + if (easing) { + kf.easing = easing; + kf.easingFunc = isFunction(easing) + ? easing + : easingFuncs[easing] || createCubicEasingFunc(easing); + } + keyframes.push(kf); + return kf; + }; + Track.prototype.prepare = function (maxTime, additiveTrack) { + var kfs = this.keyframes; + if (this._needsSort) { + kfs.sort(function (a, b) { + return a.time - b.time; + }); + } + var valType = this.valType; + var kfsLen = kfs.length; + var lastKf = kfs[kfsLen - 1]; + var isDiscrete = this.discrete; + var isArr = isArrayValueType(valType); + var isGradient = isGradientValueType(valType); + for (var i = 0; i < kfsLen; i++) { + var kf = kfs[i]; + var value = kf.value; + var lastValue = lastKf.value; + kf.percent = kf.time / maxTime; + if (!isDiscrete) { + if (isArr && i !== kfsLen - 1) { + fillArray(value, lastValue, valType); + } + else if (isGradient) { + fillColorStops(value.colorStops, lastValue.colorStops); + } + } + } + if (!isDiscrete + && valType !== VALUE_TYPE_RADIAL_GRADIENT + && additiveTrack + && this.needsAnimate() + && additiveTrack.needsAnimate() + && valType === additiveTrack.valType + && !additiveTrack._finished) { + this._additiveTrack = additiveTrack; + var startValue = kfs[0].value; + for (var i = 0; i < kfsLen; i++) { + if (valType === VALUE_TYPE_NUMBER) { + kfs[i].additiveValue = kfs[i].value - startValue; + } + else if (valType === VALUE_TYPE_COLOR) { + kfs[i].additiveValue = + add1DArray([], kfs[i].value, startValue, -1); + } + else if (isArrayValueType(valType)) { + kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY + ? add1DArray([], kfs[i].value, startValue, -1) + : add2DArray([], kfs[i].value, startValue, -1); + } + } + } + }; + Track.prototype.step = function (target, percent) { + if (this._finished) { + return; + } + if (this._additiveTrack && this._additiveTrack._finished) { + this._additiveTrack = null; + } + var isAdditive = this._additiveTrack != null; + var valueKey = isAdditive ? 'additiveValue' : 'value'; + var valType = this.valType; + var keyframes = this.keyframes; + var kfsNum = keyframes.length; + var propName = this.propName; + var isValueColor = valType === VALUE_TYPE_COLOR; + var frameIdx; + var lastFrame = this._lastFr; + var mathMin = Math.min; + var frame; + var nextFrame; + if (kfsNum === 1) { + frame = nextFrame = keyframes[0]; + } + else { + if (percent < 0) { + frameIdx = 0; + } + else if (percent < this._lastFrP) { + var start = mathMin(lastFrame + 1, kfsNum - 1); + for (frameIdx = start; frameIdx >= 0; frameIdx--) { + if (keyframes[frameIdx].percent <= percent) { + break; + } + } + frameIdx = mathMin(frameIdx, kfsNum - 2); + } + else { + for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) { + if (keyframes[frameIdx].percent > percent) { + break; + } + } + frameIdx = mathMin(frameIdx - 1, kfsNum - 2); + } + nextFrame = keyframes[frameIdx + 1]; + frame = keyframes[frameIdx]; + } + if (!(frame && nextFrame)) { + return; + } + this._lastFr = frameIdx; + this._lastFrP = percent; + var interval = (nextFrame.percent - frame.percent); + var w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1); + if (nextFrame.easingFunc) { + w = nextFrame.easingFunc(w); + } + var targetArr = isAdditive ? this._additiveValue + : (isValueColor ? tmpRgba : target[propName]); + if ((isArrayValueType(valType) || isValueColor) && !targetArr) { + targetArr = this._additiveValue = []; + } + if (this.discrete) { + target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue; + } + else if (isArrayValueType(valType)) { + valType === VALUE_TYPE_1D_ARRAY + ? interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w) + : interpolate2DArray(targetArr, frame[valueKey], nextFrame[valueKey], w); + } + else if (isGradientValueType(valType)) { + var val = frame[valueKey]; + var nextVal_1 = nextFrame[valueKey]; + var isLinearGradient_1 = valType === VALUE_TYPE_LINEAR_GRADIENT; + target[propName] = { + type: isLinearGradient_1 ? 'linear' : 'radial', + x: interpolateNumber(val.x, nextVal_1.x, w), + y: interpolateNumber(val.y, nextVal_1.y, w), + colorStops: map(val.colorStops, function (colorStop, idx) { + var nextColorStop = nextVal_1.colorStops[idx]; + return { + offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w), + color: rgba2String(interpolate1DArray([], colorStop.color, nextColorStop.color, w)) + }; + }), + global: nextVal_1.global + }; + if (isLinearGradient_1) { + target[propName].x2 = interpolateNumber(val.x2, nextVal_1.x2, w); + target[propName].y2 = interpolateNumber(val.y2, nextVal_1.y2, w); + } + else { + target[propName].r = interpolateNumber(val.r, nextVal_1.r, w); + } + } + else if (isValueColor) { + interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w); + if (!isAdditive) { + target[propName] = rgba2String(targetArr); + } + } + else { + var value = interpolateNumber(frame[valueKey], nextFrame[valueKey], w); + if (isAdditive) { + this._additiveValue = value; + } + else { + target[propName] = value; + } + } + if (isAdditive) { + this._addToTarget(target); + } + }; + Track.prototype._addToTarget = function (target) { + var valType = this.valType; + var propName = this.propName; + var additiveValue = this._additiveValue; + if (valType === VALUE_TYPE_NUMBER) { + target[propName] = target[propName] + additiveValue; + } + else if (valType === VALUE_TYPE_COLOR) { + parse(target[propName], tmpRgba); + add1DArray(tmpRgba, tmpRgba, additiveValue, 1); + target[propName] = rgba2String(tmpRgba); + } + else if (valType === VALUE_TYPE_1D_ARRAY) { + add1DArray(target[propName], target[propName], additiveValue, 1); + } + else if (valType === VALUE_TYPE_2D_ARRAY) { + add2DArray(target[propName], target[propName], additiveValue, 1); + } + }; + return Track; + }()); + var Animator = (function () { + function Animator(target, loop, allowDiscreteAnimation, additiveTo) { + this._tracks = {}; + this._trackKeys = []; + this._maxTime = 0; + this._started = 0; + this._clip = null; + this._target = target; + this._loop = loop; + if (loop && additiveTo) { + logError('Can\' use additive animation on looped animation.'); + return; + } + this._additiveAnimators = additiveTo; + this._allowDiscrete = allowDiscreteAnimation; + } + Animator.prototype.getMaxTime = function () { + return this._maxTime; + }; + Animator.prototype.getDelay = function () { + return this._delay; + }; + Animator.prototype.getLoop = function () { + return this._loop; + }; + Animator.prototype.getTarget = function () { + return this._target; + }; + Animator.prototype.changeTarget = function (target) { + this._target = target; + }; + Animator.prototype.when = function (time, props, easing) { + return this.whenWithKeys(time, props, keys(props), easing); + }; + Animator.prototype.whenWithKeys = function (time, props, propNames, easing) { + var tracks = this._tracks; + for (var i = 0; i < propNames.length; i++) { + var propName = propNames[i]; + var track = tracks[propName]; + if (!track) { + track = tracks[propName] = new Track(propName); + var initialValue = void 0; + var additiveTrack = this._getAdditiveTrack(propName); + if (additiveTrack) { + var addtiveTrackKfs = additiveTrack.keyframes; + var lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1]; + initialValue = lastFinalKf && lastFinalKf.value; + if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) { + initialValue = rgba2String(initialValue); + } + } + else { + initialValue = this._target[propName]; + } + if (initialValue == null) { + continue; + } + if (time > 0) { + track.addKeyframe(0, cloneValue(initialValue), easing); + } + this._trackKeys.push(propName); + } + track.addKeyframe(time, cloneValue(props[propName]), easing); + } + this._maxTime = Math.max(this._maxTime, time); + return this; + }; + Animator.prototype.pause = function () { + this._clip.pause(); + this._paused = true; + }; + Animator.prototype.resume = function () { + this._clip.resume(); + this._paused = false; + }; + Animator.prototype.isPaused = function () { + return !!this._paused; + }; + Animator.prototype.duration = function (duration) { + this._maxTime = duration; + this._force = true; + return this; + }; + Animator.prototype._doneCallback = function () { + this._setTracksFinished(); + this._clip = null; + var doneList = this._doneCbs; + if (doneList) { + var len = doneList.length; + for (var i = 0; i < len; i++) { + doneList[i].call(this); + } + } + }; + Animator.prototype._abortedCallback = function () { + this._setTracksFinished(); + var animation = this.animation; + var abortedList = this._abortedCbs; + if (animation) { + animation.removeClip(this._clip); + } + this._clip = null; + if (abortedList) { + for (var i = 0; i < abortedList.length; i++) { + abortedList[i].call(this); + } + } + }; + Animator.prototype._setTracksFinished = function () { + var tracks = this._tracks; + var tracksKeys = this._trackKeys; + for (var i = 0; i < tracksKeys.length; i++) { + tracks[tracksKeys[i]].setFinished(); + } + }; + Animator.prototype._getAdditiveTrack = function (trackName) { + var additiveTrack; + var additiveAnimators = this._additiveAnimators; + if (additiveAnimators) { + for (var i = 0; i < additiveAnimators.length; i++) { + var track = additiveAnimators[i].getTrack(trackName); + if (track) { + additiveTrack = track; + } + } + } + return additiveTrack; + }; + Animator.prototype.start = function (easing) { + if (this._started > 0) { + return; + } + this._started = 1; + var self = this; + var tracks = []; + var maxTime = this._maxTime || 0; + for (var i = 0; i < this._trackKeys.length; i++) { + var propName = this._trackKeys[i]; + var track = this._tracks[propName]; + var additiveTrack = this._getAdditiveTrack(propName); + var kfs = track.keyframes; + var kfsNum = kfs.length; + track.prepare(maxTime, additiveTrack); + if (track.needsAnimate()) { + if (!this._allowDiscrete && track.discrete) { + var lastKf = kfs[kfsNum - 1]; + if (lastKf) { + self._target[track.propName] = lastKf.rawValue; + } + track.setFinished(); + } + else { + tracks.push(track); + } + } + } + if (tracks.length || this._force) { + var clip = new Clip({ + life: maxTime, + loop: this._loop, + delay: this._delay || 0, + onframe: function (percent) { + self._started = 2; + var additiveAnimators = self._additiveAnimators; + if (additiveAnimators) { + var stillHasAdditiveAnimator = false; + for (var i = 0; i < additiveAnimators.length; i++) { + if (additiveAnimators[i]._clip) { + stillHasAdditiveAnimator = true; + break; + } + } + if (!stillHasAdditiveAnimator) { + self._additiveAnimators = null; + } + } + for (var i = 0; i < tracks.length; i++) { + tracks[i].step(self._target, percent); + } + var onframeList = self._onframeCbs; + if (onframeList) { + for (var i = 0; i < onframeList.length; i++) { + onframeList[i](self._target, percent); + } + } + }, + ondestroy: function () { + self._doneCallback(); + } + }); + this._clip = clip; + if (this.animation) { + this.animation.addClip(clip); + } + if (easing) { + clip.setEasing(easing); + } + } + else { + this._doneCallback(); + } + return this; + }; + Animator.prototype.stop = function (forwardToLast) { + if (!this._clip) { + return; + } + var clip = this._clip; + if (forwardToLast) { + clip.onframe(1); + } + this._abortedCallback(); + }; + Animator.prototype.delay = function (time) { + this._delay = time; + return this; + }; + Animator.prototype.during = function (cb) { + if (cb) { + if (!this._onframeCbs) { + this._onframeCbs = []; + } + this._onframeCbs.push(cb); + } + return this; + }; + Animator.prototype.done = function (cb) { + if (cb) { + if (!this._doneCbs) { + this._doneCbs = []; + } + this._doneCbs.push(cb); + } + return this; + }; + Animator.prototype.aborted = function (cb) { + if (cb) { + if (!this._abortedCbs) { + this._abortedCbs = []; + } + this._abortedCbs.push(cb); + } + return this; + }; + Animator.prototype.getClip = function () { + return this._clip; + }; + Animator.prototype.getTrack = function (propName) { + return this._tracks[propName]; + }; + Animator.prototype.getTracks = function () { + var _this = this; + return map(this._trackKeys, function (key) { return _this._tracks[key]; }); + }; + Animator.prototype.stopTracks = function (propNames, forwardToLast) { + if (!propNames.length || !this._clip) { + return true; + } + var tracks = this._tracks; + var tracksKeys = this._trackKeys; + for (var i = 0; i < propNames.length; i++) { + var track = tracks[propNames[i]]; + if (track && !track.isFinished()) { + if (forwardToLast) { + track.step(this._target, 1); + } + else if (this._started === 1) { + track.step(this._target, 0); + } + track.setFinished(); + } + } + var allAborted = true; + for (var i = 0; i < tracksKeys.length; i++) { + if (!tracks[tracksKeys[i]].isFinished()) { + allAborted = false; + break; + } + } + if (allAborted) { + this._abortedCallback(); + } + return allAborted; + }; + Animator.prototype.saveTo = function (target, trackKeys, firstOrLast) { + if (!target) { + return; + } + trackKeys = trackKeys || this._trackKeys; + for (var i = 0; i < trackKeys.length; i++) { + var propName = trackKeys[i]; + var track = this._tracks[propName]; + if (!track || track.isFinished()) { + continue; + } + var kfs = track.keyframes; + var kf = kfs[firstOrLast ? 0 : kfs.length - 1]; + if (kf) { + target[propName] = cloneValue(kf.rawValue); + } + } + }; + Animator.prototype.__changeFinalValue = function (finalProps, trackKeys) { + trackKeys = trackKeys || keys(finalProps); + for (var i = 0; i < trackKeys.length; i++) { + var propName = trackKeys[i]; + var track = this._tracks[propName]; + if (!track) { + continue; + } + var kfs = track.keyframes; + if (kfs.length > 1) { + var lastKf = kfs.pop(); + track.addKeyframe(lastKf.time, finalProps[propName]); + track.prepare(this._maxTime, track.getAdditiveTrack()); + } + } + }; + return Animator; + }()); + + function getTime() { + return new Date().getTime(); + } + var Animation = (function (_super) { + __extends(Animation, _super); + function Animation(opts) { + var _this = _super.call(this) || this; + _this._running = false; + _this._time = 0; + _this._pausedTime = 0; + _this._pauseStart = 0; + _this._paused = false; + opts = opts || {}; + _this.stage = opts.stage || {}; + return _this; + } + Animation.prototype.addClip = function (clip) { + if (clip.animation) { + this.removeClip(clip); + } + if (!this._head) { + this._head = this._tail = clip; + } + else { + this._tail.next = clip; + clip.prev = this._tail; + clip.next = null; + this._tail = clip; + } + clip.animation = this; + }; + Animation.prototype.addAnimator = function (animator) { + animator.animation = this; + var clip = animator.getClip(); + if (clip) { + this.addClip(clip); + } + }; + Animation.prototype.removeClip = function (clip) { + if (!clip.animation) { + return; + } + var prev = clip.prev; + var next = clip.next; + if (prev) { + prev.next = next; + } + else { + this._head = next; + } + if (next) { + next.prev = prev; + } + else { + this._tail = prev; + } + clip.next = clip.prev = clip.animation = null; + }; + Animation.prototype.removeAnimator = function (animator) { + var clip = animator.getClip(); + if (clip) { + this.removeClip(clip); + } + animator.animation = null; + }; + Animation.prototype.update = function (notTriggerFrameAndStageUpdate) { + var time = getTime() - this._pausedTime; + var delta = time - this._time; + var clip = this._head; + while (clip) { + var nextClip = clip.next; + var finished = clip.step(time, delta); + if (finished) { + clip.ondestroy(); + this.removeClip(clip); + clip = nextClip; + } + else { + clip = nextClip; + } + } + this._time = time; + if (!notTriggerFrameAndStageUpdate) { + this.trigger('frame', delta); + this.stage.update && this.stage.update(); + } + }; + Animation.prototype._startLoop = function () { + var self = this; + this._running = true; + function step() { + if (self._running) { + requestAnimationFrame$1(step); + !self._paused && self.update(); + } + } + requestAnimationFrame$1(step); + }; + Animation.prototype.start = function () { + if (this._running) { + return; + } + this._time = getTime(); + this._pausedTime = 0; + this._startLoop(); + }; + Animation.prototype.stop = function () { + this._running = false; + }; + Animation.prototype.pause = function () { + if (!this._paused) { + this._pauseStart = getTime(); + this._paused = true; + } + }; + Animation.prototype.resume = function () { + if (this._paused) { + this._pausedTime += getTime() - this._pauseStart; + this._paused = false; + } + }; + Animation.prototype.clear = function () { + var clip = this._head; + while (clip) { + var nextClip = clip.next; + clip.prev = clip.next = clip.animation = null; + clip = nextClip; + } + this._head = this._tail = null; + }; + Animation.prototype.isFinished = function () { + return this._head == null; + }; + Animation.prototype.animate = function (target, options) { + options = options || {}; + this.start(); + var animator = new Animator(target, options.loop); + this.addAnimator(animator); + return animator; + }; + return Animation; + }(Eventful)); + + var TOUCH_CLICK_DELAY = 300; + var globalEventSupported = env.domSupported; + var localNativeListenerNames = (function () { + var mouseHandlerNames = [ + 'click', 'dblclick', 'mousewheel', 'wheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove', 'contextmenu' + ]; + var touchHandlerNames = [ + 'touchstart', 'touchend', 'touchmove' + ]; + var pointerEventNameMap = { + pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1 + }; + var pointerHandlerNames = map(mouseHandlerNames, function (name) { + var nm = name.replace('mouse', 'pointer'); + return pointerEventNameMap.hasOwnProperty(nm) ? nm : name; + }); + return { + mouse: mouseHandlerNames, + touch: touchHandlerNames, + pointer: pointerHandlerNames + }; + })(); + var globalNativeListenerNames = { + mouse: ['mousemove', 'mouseup'], + pointer: ['pointermove', 'pointerup'] + }; + var wheelEventSupported = false; + function isPointerFromTouch(event) { + var pointerType = event.pointerType; + return pointerType === 'pen' || pointerType === 'touch'; + } + function setTouchTimer(scope) { + scope.touching = true; + if (scope.touchTimer != null) { + clearTimeout(scope.touchTimer); + scope.touchTimer = null; + } + scope.touchTimer = setTimeout(function () { + scope.touching = false; + scope.touchTimer = null; + }, 700); + } + function markTouch(event) { + event && (event.zrByTouch = true); + } + function normalizeGlobalEvent(instance, event) { + return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true); + } + function isLocalEl(instance, el) { + var elTmp = el; + var isLocal = false; + while (elTmp && elTmp.nodeType !== 9 + && !(isLocal = elTmp.domBelongToZr + || (elTmp !== el && elTmp === instance.painterRoot))) { + elTmp = elTmp.parentNode; + } + return isLocal; + } + var FakeGlobalEvent = (function () { + function FakeGlobalEvent(instance, event) { + this.stopPropagation = noop; + this.stopImmediatePropagation = noop; + this.preventDefault = noop; + this.type = event.type; + this.target = this.currentTarget = instance.dom; + this.pointerType = event.pointerType; + this.clientX = event.clientX; + this.clientY = event.clientY; + } + return FakeGlobalEvent; + }()); + var localDOMHandlers = { + mousedown: function (event) { + event = normalizeEvent(this.dom, event); + this.__mayPointerCapture = [event.zrX, event.zrY]; + this.trigger('mousedown', event); + }, + mousemove: function (event) { + event = normalizeEvent(this.dom, event); + var downPoint = this.__mayPointerCapture; + if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) { + this.__togglePointerCapture(true); + } + this.trigger('mousemove', event); + }, + mouseup: function (event) { + event = normalizeEvent(this.dom, event); + this.__togglePointerCapture(false); + this.trigger('mouseup', event); + }, + mouseout: function (event) { + event = normalizeEvent(this.dom, event); + var element = event.toElement || event.relatedTarget; + if (!isLocalEl(this, element)) { + if (this.__pointerCapturing) { + event.zrEventControl = 'no_globalout'; + } + this.trigger('mouseout', event); + } + }, + wheel: function (event) { + wheelEventSupported = true; + event = normalizeEvent(this.dom, event); + this.trigger('mousewheel', event); + }, + mousewheel: function (event) { + if (wheelEventSupported) { + return; + } + event = normalizeEvent(this.dom, event); + this.trigger('mousewheel', event); + }, + touchstart: function (event) { + event = normalizeEvent(this.dom, event); + markTouch(event); + this.__lastTouchMoment = new Date(); + this.handler.processGesture(event, 'start'); + localDOMHandlers.mousemove.call(this, event); + localDOMHandlers.mousedown.call(this, event); + }, + touchmove: function (event) { + event = normalizeEvent(this.dom, event); + markTouch(event); + this.handler.processGesture(event, 'change'); + localDOMHandlers.mousemove.call(this, event); + }, + touchend: function (event) { + event = normalizeEvent(this.dom, event); + markTouch(event); + this.handler.processGesture(event, 'end'); + localDOMHandlers.mouseup.call(this, event); + if (+new Date() - (+this.__lastTouchMoment) < TOUCH_CLICK_DELAY) { + localDOMHandlers.click.call(this, event); + } + }, + pointerdown: function (event) { + localDOMHandlers.mousedown.call(this, event); + }, + pointermove: function (event) { + if (!isPointerFromTouch(event)) { + localDOMHandlers.mousemove.call(this, event); + } + }, + pointerup: function (event) { + localDOMHandlers.mouseup.call(this, event); + }, + pointerout: function (event) { + if (!isPointerFromTouch(event)) { + localDOMHandlers.mouseout.call(this, event); + } + } + }; + each(['click', 'dblclick', 'contextmenu'], function (name) { + localDOMHandlers[name] = function (event) { + event = normalizeEvent(this.dom, event); + this.trigger(name, event); + }; + }); + var globalDOMHandlers = { + pointermove: function (event) { + if (!isPointerFromTouch(event)) { + globalDOMHandlers.mousemove.call(this, event); + } + }, + pointerup: function (event) { + globalDOMHandlers.mouseup.call(this, event); + }, + mousemove: function (event) { + this.trigger('mousemove', event); + }, + mouseup: function (event) { + var pointerCaptureReleasing = this.__pointerCapturing; + this.__togglePointerCapture(false); + this.trigger('mouseup', event); + if (pointerCaptureReleasing) { + event.zrEventControl = 'only_globalout'; + this.trigger('mouseout', event); + } + } + }; + function mountLocalDOMEventListeners(instance, scope) { + var domHandlers = scope.domHandlers; + if (env.pointerEventsSupported) { + each(localNativeListenerNames.pointer, function (nativeEventName) { + mountSingleDOMEventListener(scope, nativeEventName, function (event) { + domHandlers[nativeEventName].call(instance, event); + }); + }); + } + else { + if (env.touchEventsSupported) { + each(localNativeListenerNames.touch, function (nativeEventName) { + mountSingleDOMEventListener(scope, nativeEventName, function (event) { + domHandlers[nativeEventName].call(instance, event); + setTouchTimer(scope); + }); + }); + } + each(localNativeListenerNames.mouse, function (nativeEventName) { + mountSingleDOMEventListener(scope, nativeEventName, function (event) { + event = getNativeEvent(event); + if (!scope.touching) { + domHandlers[nativeEventName].call(instance, event); + } + }); + }); + } + } + function mountGlobalDOMEventListeners(instance, scope) { + if (env.pointerEventsSupported) { + each(globalNativeListenerNames.pointer, mount); + } + else if (!env.touchEventsSupported) { + each(globalNativeListenerNames.mouse, mount); + } + function mount(nativeEventName) { + function nativeEventListener(event) { + event = getNativeEvent(event); + if (!isLocalEl(instance, event.target)) { + event = normalizeGlobalEvent(instance, event); + scope.domHandlers[nativeEventName].call(instance, event); + } + } + mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { capture: true }); + } + } + function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) { + scope.mounted[nativeEventName] = listener; + scope.listenerOpts[nativeEventName] = opt; + addEventListener(scope.domTarget, nativeEventName, listener, opt); + } + function unmountDOMEventListeners(scope) { + var mounted = scope.mounted; + for (var nativeEventName in mounted) { + if (mounted.hasOwnProperty(nativeEventName)) { + removeEventListener(scope.domTarget, nativeEventName, mounted[nativeEventName], scope.listenerOpts[nativeEventName]); + } + } + scope.mounted = {}; + } + var DOMHandlerScope = (function () { + function DOMHandlerScope(domTarget, domHandlers) { + this.mounted = {}; + this.listenerOpts = {}; + this.touching = false; + this.domTarget = domTarget; + this.domHandlers = domHandlers; + } + return DOMHandlerScope; + }()); + var HandlerDomProxy = (function (_super) { + __extends(HandlerDomProxy, _super); + function HandlerDomProxy(dom, painterRoot) { + var _this = _super.call(this) || this; + _this.__pointerCapturing = false; + _this.dom = dom; + _this.painterRoot = painterRoot; + _this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers); + if (globalEventSupported) { + _this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers); + } + mountLocalDOMEventListeners(_this, _this._localHandlerScope); + return _this; + } + HandlerDomProxy.prototype.dispose = function () { + unmountDOMEventListeners(this._localHandlerScope); + if (globalEventSupported) { + unmountDOMEventListeners(this._globalHandlerScope); + } + }; + HandlerDomProxy.prototype.setCursor = function (cursorStyle) { + this.dom.style && (this.dom.style.cursor = cursorStyle || 'default'); + }; + HandlerDomProxy.prototype.__togglePointerCapture = function (isPointerCapturing) { + this.__mayPointerCapture = null; + if (globalEventSupported + && ((+this.__pointerCapturing) ^ (+isPointerCapturing))) { + this.__pointerCapturing = isPointerCapturing; + var globalHandlerScope = this._globalHandlerScope; + isPointerCapturing + ? mountGlobalDOMEventListeners(this, globalHandlerScope) + : unmountDOMEventListeners(globalHandlerScope); + } + }; + return HandlerDomProxy; + }(Eventful)); + + var dpr = 1; + if (env.hasGlobalWindow) { + dpr = Math.max(window.devicePixelRatio + || (window.screen && window.screen.deviceXDPI / window.screen.logicalXDPI) + || 1, 1); + } + var devicePixelRatio = dpr; + var DARK_MODE_THRESHOLD = 0.4; + var DARK_LABEL_COLOR = '#333'; + var LIGHT_LABEL_COLOR = '#ccc'; + var LIGHTER_LABEL_COLOR = '#eee'; + + var mIdentity = identity; + var EPSILON$2 = 5e-5; + function isNotAroundZero$1(val) { + return val > EPSILON$2 || val < -EPSILON$2; + } + var scaleTmp = []; + var tmpTransform = []; + var originTransform = create$1(); + var abs = Math.abs; + var Transformable = (function () { + function Transformable() { + } + Transformable.prototype.getLocalTransform = function (m) { + return Transformable.getLocalTransform(this, m); + }; + Transformable.prototype.setPosition = function (arr) { + this.x = arr[0]; + this.y = arr[1]; + }; + Transformable.prototype.setScale = function (arr) { + this.scaleX = arr[0]; + this.scaleY = arr[1]; + }; + Transformable.prototype.setSkew = function (arr) { + this.skewX = arr[0]; + this.skewY = arr[1]; + }; + Transformable.prototype.setOrigin = function (arr) { + this.originX = arr[0]; + this.originY = arr[1]; + }; + Transformable.prototype.needLocalTransform = function () { + return isNotAroundZero$1(this.rotation) + || isNotAroundZero$1(this.x) + || isNotAroundZero$1(this.y) + || isNotAroundZero$1(this.scaleX - 1) + || isNotAroundZero$1(this.scaleY - 1) + || isNotAroundZero$1(this.skewX) + || isNotAroundZero$1(this.skewY); + }; + Transformable.prototype.updateTransform = function () { + var parentTransform = this.parent && this.parent.transform; + var needLocalTransform = this.needLocalTransform(); + var m = this.transform; + if (!(needLocalTransform || parentTransform)) { + if (m) { + mIdentity(m); + this.invTransform = null; + } + return; + } + m = m || create$1(); + if (needLocalTransform) { + this.getLocalTransform(m); + } + else { + mIdentity(m); + } + if (parentTransform) { + if (needLocalTransform) { + mul$1(m, parentTransform, m); + } + else { + copy$1(m, parentTransform); + } + } + this.transform = m; + this._resolveGlobalScaleRatio(m); + }; + Transformable.prototype._resolveGlobalScaleRatio = function (m) { + var globalScaleRatio = this.globalScaleRatio; + if (globalScaleRatio != null && globalScaleRatio !== 1) { + this.getGlobalScale(scaleTmp); + var relX = scaleTmp[0] < 0 ? -1 : 1; + var relY = scaleTmp[1] < 0 ? -1 : 1; + var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0; + var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0; + m[0] *= sx; + m[1] *= sx; + m[2] *= sy; + m[3] *= sy; + } + this.invTransform = this.invTransform || create$1(); + invert(this.invTransform, m); + }; + Transformable.prototype.getComputedTransform = function () { + var transformNode = this; + var ancestors = []; + while (transformNode) { + ancestors.push(transformNode); + transformNode = transformNode.parent; + } + while (transformNode = ancestors.pop()) { + transformNode.updateTransform(); + } + return this.transform; + }; + Transformable.prototype.setLocalTransform = function (m) { + if (!m) { + return; + } + var sx = m[0] * m[0] + m[1] * m[1]; + var sy = m[2] * m[2] + m[3] * m[3]; + var rotation = Math.atan2(m[1], m[0]); + var shearX = Math.PI / 2 + rotation - Math.atan2(m[3], m[2]); + sy = Math.sqrt(sy) * Math.cos(shearX); + sx = Math.sqrt(sx); + this.skewX = shearX; + this.skewY = 0; + this.rotation = -rotation; + this.x = +m[4]; + this.y = +m[5]; + this.scaleX = sx; + this.scaleY = sy; + this.originX = 0; + this.originY = 0; + }; + Transformable.prototype.decomposeTransform = function () { + if (!this.transform) { + return; + } + var parent = this.parent; + var m = this.transform; + if (parent && parent.transform) { + parent.invTransform = parent.invTransform || create$1(); + mul$1(tmpTransform, parent.invTransform, m); + m = tmpTransform; + } + var ox = this.originX; + var oy = this.originY; + if (ox || oy) { + originTransform[4] = ox; + originTransform[5] = oy; + mul$1(tmpTransform, m, originTransform); + tmpTransform[4] -= ox; + tmpTransform[5] -= oy; + m = tmpTransform; + } + this.setLocalTransform(m); + }; + Transformable.prototype.getGlobalScale = function (out) { + var m = this.transform; + out = out || []; + if (!m) { + out[0] = 1; + out[1] = 1; + return out; + } + out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]); + out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]); + if (m[0] < 0) { + out[0] = -out[0]; + } + if (m[3] < 0) { + out[1] = -out[1]; + } + return out; + }; + Transformable.prototype.transformCoordToLocal = function (x, y) { + var v2 = [x, y]; + var invTransform = this.invTransform; + if (invTransform) { + applyTransform(v2, v2, invTransform); + } + return v2; + }; + Transformable.prototype.transformCoordToGlobal = function (x, y) { + var v2 = [x, y]; + var transform = this.transform; + if (transform) { + applyTransform(v2, v2, transform); + } + return v2; + }; + Transformable.prototype.getLineScale = function () { + var m = this.transform; + return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10 + ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1])) + : 1; + }; + Transformable.prototype.copyTransform = function (source) { + copyTransform(this, source); + }; + Transformable.getLocalTransform = function (target, m) { + m = m || []; + var ox = target.originX || 0; + var oy = target.originY || 0; + var sx = target.scaleX; + var sy = target.scaleY; + var ax = target.anchorX; + var ay = target.anchorY; + var rotation = target.rotation || 0; + var x = target.x; + var y = target.y; + var skewX = target.skewX ? Math.tan(target.skewX) : 0; + var skewY = target.skewY ? Math.tan(-target.skewY) : 0; + if (ox || oy || ax || ay) { + var dx = ox + ax; + var dy = oy + ay; + m[4] = -dx * sx - skewX * dy * sy; + m[5] = -dy * sy - skewY * dx * sx; + } + else { + m[4] = m[5] = 0; + } + m[0] = sx; + m[3] = sy; + m[1] = skewY * sx; + m[2] = skewX * sy; + rotation && rotate(m, m, rotation); + m[4] += ox + x; + m[5] += oy + y; + return m; + }; + Transformable.initDefaultProps = (function () { + var proto = Transformable.prototype; + proto.scaleX = + proto.scaleY = + proto.globalScaleRatio = 1; + proto.x = + proto.y = + proto.originX = + proto.originY = + proto.skewX = + proto.skewY = + proto.rotation = + proto.anchorX = + proto.anchorY = 0; + })(); + return Transformable; + }()); + var TRANSFORMABLE_PROPS = [ + 'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY' + ]; + function copyTransform(target, source) { + for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) { + var propName = TRANSFORMABLE_PROPS[i]; + target[propName] = source[propName]; + } + } + + var textWidthCache = {}; + function getWidth(text, font) { + font = font || DEFAULT_FONT; + var cacheOfFont = textWidthCache[font]; + if (!cacheOfFont) { + cacheOfFont = textWidthCache[font] = new LRU(500); + } + var width = cacheOfFont.get(text); + if (width == null) { + width = platformApi.measureText(text, font).width; + cacheOfFont.put(text, width); + } + return width; + } + function innerGetBoundingRect(text, font, textAlign, textBaseline) { + var width = getWidth(text, font); + var height = getLineHeight(font); + var x = adjustTextX(0, width, textAlign); + var y = adjustTextY$1(0, height, textBaseline); + var rect = new BoundingRect(x, y, width, height); + return rect; + } + function getBoundingRect(text, font, textAlign, textBaseline) { + var textLines = ((text || '') + '').split('\n'); + var len = textLines.length; + if (len === 1) { + return innerGetBoundingRect(textLines[0], font, textAlign, textBaseline); + } + else { + var uniondRect = new BoundingRect(0, 0, 0, 0); + for (var i = 0; i < textLines.length; i++) { + var rect = innerGetBoundingRect(textLines[i], font, textAlign, textBaseline); + i === 0 ? uniondRect.copy(rect) : uniondRect.union(rect); + } + return uniondRect; + } + } + function adjustTextX(x, width, textAlign) { + if (textAlign === 'right') { + x -= width; + } + else if (textAlign === 'center') { + x -= width / 2; + } + return x; + } + function adjustTextY$1(y, height, verticalAlign) { + if (verticalAlign === 'middle') { + y -= height / 2; + } + else if (verticalAlign === 'bottom') { + y -= height; + } + return y; + } + function getLineHeight(font) { + return getWidth('国', font); + } + function parsePercent(value, maxValue) { + if (typeof value === 'string') { + if (value.lastIndexOf('%') >= 0) { + return parseFloat(value) / 100 * maxValue; + } + return parseFloat(value); + } + return value; + } + function calculateTextPosition(out, opts, rect) { + var textPosition = opts.position || 'inside'; + var distance = opts.distance != null ? opts.distance : 5; + var height = rect.height; + var width = rect.width; + var halfHeight = height / 2; + var x = rect.x; + var y = rect.y; + var textAlign = 'left'; + var textVerticalAlign = 'top'; + if (textPosition instanceof Array) { + x += parsePercent(textPosition[0], rect.width); + y += parsePercent(textPosition[1], rect.height); + textAlign = null; + textVerticalAlign = null; + } + else { + switch (textPosition) { + case 'left': + x -= distance; + y += halfHeight; + textAlign = 'right'; + textVerticalAlign = 'middle'; + break; + case 'right': + x += distance + width; + y += halfHeight; + textVerticalAlign = 'middle'; + break; + case 'top': + x += width / 2; + y -= distance; + textAlign = 'center'; + textVerticalAlign = 'bottom'; + break; + case 'bottom': + x += width / 2; + y += height + distance; + textAlign = 'center'; + break; + case 'inside': + x += width / 2; + y += halfHeight; + textAlign = 'center'; + textVerticalAlign = 'middle'; + break; + case 'insideLeft': + x += distance; + y += halfHeight; + textVerticalAlign = 'middle'; + break; + case 'insideRight': + x += width - distance; + y += halfHeight; + textAlign = 'right'; + textVerticalAlign = 'middle'; + break; + case 'insideTop': + x += width / 2; + y += distance; + textAlign = 'center'; + break; + case 'insideBottom': + x += width / 2; + y += height - distance; + textAlign = 'center'; + textVerticalAlign = 'bottom'; + break; + case 'insideTopLeft': + x += distance; + y += distance; + break; + case 'insideTopRight': + x += width - distance; + y += distance; + textAlign = 'right'; + break; + case 'insideBottomLeft': + x += distance; + y += height - distance; + textVerticalAlign = 'bottom'; + break; + case 'insideBottomRight': + x += width - distance; + y += height - distance; + textAlign = 'right'; + textVerticalAlign = 'bottom'; + break; + } + } + out = out || {}; + out.x = x; + out.y = y; + out.align = textAlign; + out.verticalAlign = textVerticalAlign; + return out; + } + + var PRESERVED_NORMAL_STATE = '__zr_normal__'; + var PRIMARY_STATES_KEYS = TRANSFORMABLE_PROPS.concat(['ignore']); + var DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) { + obj[key] = true; + return obj; + }, { ignore: false }); + var tmpTextPosCalcRes = {}; + var tmpBoundingRect = new BoundingRect(0, 0, 0, 0); + var Element = (function () { + function Element(props) { + this.id = guid(); + this.animators = []; + this.currentStates = []; + this.states = {}; + this._init(props); + } + Element.prototype._init = function (props) { + this.attr(props); + }; + Element.prototype.drift = function (dx, dy, e) { + switch (this.draggable) { + case 'horizontal': + dy = 0; + break; + case 'vertical': + dx = 0; + break; + } + var m = this.transform; + if (!m) { + m = this.transform = [1, 0, 0, 1, 0, 0]; + } + m[4] += dx; + m[5] += dy; + this.decomposeTransform(); + this.markRedraw(); + }; + Element.prototype.beforeUpdate = function () { }; + Element.prototype.afterUpdate = function () { }; + Element.prototype.update = function () { + this.updateTransform(); + if (this.__dirty) { + this.updateInnerText(); + } + }; + Element.prototype.updateInnerText = function (forceUpdate) { + var textEl = this._textContent; + if (textEl && (!textEl.ignore || forceUpdate)) { + if (!this.textConfig) { + this.textConfig = {}; + } + var textConfig = this.textConfig; + var isLocal = textConfig.local; + var innerTransformable = textEl.innerTransformable; + var textAlign = void 0; + var textVerticalAlign = void 0; + var textStyleChanged = false; + innerTransformable.parent = isLocal ? this : null; + var innerOrigin = false; + innerTransformable.copyTransform(textEl); + if (textConfig.position != null) { + var layoutRect = tmpBoundingRect; + if (textConfig.layoutRect) { + layoutRect.copy(textConfig.layoutRect); + } + else { + layoutRect.copy(this.getBoundingRect()); + } + if (!isLocal) { + layoutRect.applyTransform(this.transform); + } + if (this.calculateTextPosition) { + this.calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect); + } + else { + calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect); + } + innerTransformable.x = tmpTextPosCalcRes.x; + innerTransformable.y = tmpTextPosCalcRes.y; + textAlign = tmpTextPosCalcRes.align; + textVerticalAlign = tmpTextPosCalcRes.verticalAlign; + var textOrigin = textConfig.origin; + if (textOrigin && textConfig.rotation != null) { + var relOriginX = void 0; + var relOriginY = void 0; + if (textOrigin === 'center') { + relOriginX = layoutRect.width * 0.5; + relOriginY = layoutRect.height * 0.5; + } + else { + relOriginX = parsePercent(textOrigin[0], layoutRect.width); + relOriginY = parsePercent(textOrigin[1], layoutRect.height); + } + innerOrigin = true; + innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x); + innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y); + } + } + if (textConfig.rotation != null) { + innerTransformable.rotation = textConfig.rotation; + } + var textOffset = textConfig.offset; + if (textOffset) { + innerTransformable.x += textOffset[0]; + innerTransformable.y += textOffset[1]; + if (!innerOrigin) { + innerTransformable.originX = -textOffset[0]; + innerTransformable.originY = -textOffset[1]; + } + } + var isInside = textConfig.inside == null + ? (typeof textConfig.position === 'string' && textConfig.position.indexOf('inside') >= 0) + : textConfig.inside; + var innerTextDefaultStyle = this._innerTextDefaultStyle || (this._innerTextDefaultStyle = {}); + var textFill = void 0; + var textStroke = void 0; + var autoStroke = void 0; + if (isInside && this.canBeInsideText()) { + textFill = textConfig.insideFill; + textStroke = textConfig.insideStroke; + if (textFill == null || textFill === 'auto') { + textFill = this.getInsideTextFill(); + } + if (textStroke == null || textStroke === 'auto') { + textStroke = this.getInsideTextStroke(textFill); + autoStroke = true; + } + } + else { + textFill = textConfig.outsideFill; + textStroke = textConfig.outsideStroke; + if (textFill == null || textFill === 'auto') { + textFill = this.getOutsideFill(); + } + if (textStroke == null || textStroke === 'auto') { + textStroke = this.getOutsideStroke(textFill); + autoStroke = true; + } + } + textFill = textFill || '#000'; + if (textFill !== innerTextDefaultStyle.fill + || textStroke !== innerTextDefaultStyle.stroke + || autoStroke !== innerTextDefaultStyle.autoStroke + || textAlign !== innerTextDefaultStyle.align + || textVerticalAlign !== innerTextDefaultStyle.verticalAlign) { + textStyleChanged = true; + innerTextDefaultStyle.fill = textFill; + innerTextDefaultStyle.stroke = textStroke; + innerTextDefaultStyle.autoStroke = autoStroke; + innerTextDefaultStyle.align = textAlign; + innerTextDefaultStyle.verticalAlign = textVerticalAlign; + textEl.setDefaultTextStyle(innerTextDefaultStyle); + } + textEl.__dirty |= REDRAW_BIT; + if (textStyleChanged) { + textEl.dirtyStyle(true); + } + } + }; + Element.prototype.canBeInsideText = function () { + return true; + }; + Element.prototype.getInsideTextFill = function () { + return '#fff'; + }; + Element.prototype.getInsideTextStroke = function (textFill) { + return '#000'; + }; + Element.prototype.getOutsideFill = function () { + return this.__zr && this.__zr.isDarkMode() ? LIGHT_LABEL_COLOR : DARK_LABEL_COLOR; + }; + Element.prototype.getOutsideStroke = function (textFill) { + var backgroundColor = this.__zr && this.__zr.getBackgroundColor(); + var colorArr = typeof backgroundColor === 'string' && parse(backgroundColor); + if (!colorArr) { + colorArr = [255, 255, 255, 1]; + } + var alpha = colorArr[3]; + var isDark = this.__zr.isDarkMode(); + for (var i = 0; i < 3; i++) { + colorArr[i] = colorArr[i] * alpha + (isDark ? 0 : 255) * (1 - alpha); + } + colorArr[3] = 1; + return stringify(colorArr, 'rgba'); + }; + Element.prototype.traverse = function (cb, context) { }; + Element.prototype.attrKV = function (key, value) { + if (key === 'textConfig') { + this.setTextConfig(value); + } + else if (key === 'textContent') { + this.setTextContent(value); + } + else if (key === 'clipPath') { + this.setClipPath(value); + } + else if (key === 'extra') { + this.extra = this.extra || {}; + extend(this.extra, value); + } + else { + this[key] = value; + } + }; + Element.prototype.hide = function () { + this.ignore = true; + this.markRedraw(); + }; + Element.prototype.show = function () { + this.ignore = false; + this.markRedraw(); + }; + Element.prototype.attr = function (keyOrObj, value) { + if (typeof keyOrObj === 'string') { + this.attrKV(keyOrObj, value); + } + else if (isObject(keyOrObj)) { + var obj = keyOrObj; + var keysArr = keys(obj); + for (var i = 0; i < keysArr.length; i++) { + var key = keysArr[i]; + this.attrKV(key, keyOrObj[key]); + } + } + this.markRedraw(); + return this; + }; + Element.prototype.saveCurrentToNormalState = function (toState) { + this._innerSaveToNormal(toState); + var normalState = this._normalState; + for (var i = 0; i < this.animators.length; i++) { + var animator = this.animators[i]; + var fromStateTransition = animator.__fromStateTransition; + if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) { + continue; + } + var targetName = animator.targetName; + var target = targetName + ? normalState[targetName] : normalState; + animator.saveTo(target); + } + }; + Element.prototype._innerSaveToNormal = function (toState) { + var normalState = this._normalState; + if (!normalState) { + normalState = this._normalState = {}; + } + if (toState.textConfig && !normalState.textConfig) { + normalState.textConfig = this.textConfig; + } + this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS); + }; + Element.prototype._savePrimaryToNormal = function (toState, normalState, primaryKeys) { + for (var i = 0; i < primaryKeys.length; i++) { + var key = primaryKeys[i]; + if (toState[key] != null && !(key in normalState)) { + normalState[key] = this[key]; + } + } + }; + Element.prototype.hasState = function () { + return this.currentStates.length > 0; + }; + Element.prototype.getState = function (name) { + return this.states[name]; + }; + Element.prototype.ensureState = function (name) { + var states = this.states; + if (!states[name]) { + states[name] = {}; + } + return states[name]; + }; + Element.prototype.clearStates = function (noAnimation) { + this.useState(PRESERVED_NORMAL_STATE, false, noAnimation); + }; + Element.prototype.useState = function (stateName, keepCurrentStates, noAnimation, forceUseHoverLayer) { + var toNormalState = stateName === PRESERVED_NORMAL_STATE; + var hasStates = this.hasState(); + if (!hasStates && toNormalState) { + return; + } + var currentStates = this.currentStates; + var animationCfg = this.stateTransition; + if (indexOf(currentStates, stateName) >= 0 && (keepCurrentStates || currentStates.length === 1)) { + return; + } + var state; + if (this.stateProxy && !toNormalState) { + state = this.stateProxy(stateName); + } + if (!state) { + state = (this.states && this.states[stateName]); + } + if (!state && !toNormalState) { + logError("State " + stateName + " not exists."); + return; + } + if (!toNormalState) { + this.saveCurrentToNormalState(state); + } + var useHoverLayer = !!((state && state.hoverLayer) || forceUseHoverLayer); + if (useHoverLayer) { + this._toggleHoverLayerFlag(true); + } + this._applyStateObj(stateName, state, this._normalState, keepCurrentStates, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg); + var textContent = this._textContent; + var textGuide = this._textGuide; + if (textContent) { + textContent.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer); + } + if (textGuide) { + textGuide.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer); + } + if (toNormalState) { + this.currentStates = []; + this._normalState = {}; + } + else { + if (!keepCurrentStates) { + this.currentStates = [stateName]; + } + else { + this.currentStates.push(stateName); + } + } + this._updateAnimationTargets(); + this.markRedraw(); + if (!useHoverLayer && this.__inHover) { + this._toggleHoverLayerFlag(false); + this.__dirty &= ~REDRAW_BIT; + } + return state; + }; + Element.prototype.useStates = function (states, noAnimation, forceUseHoverLayer) { + if (!states.length) { + this.clearStates(); + } + else { + var stateObjects = []; + var currentStates = this.currentStates; + var len = states.length; + var notChange = len === currentStates.length; + if (notChange) { + for (var i = 0; i < len; i++) { + if (states[i] !== currentStates[i]) { + notChange = false; + break; + } + } + } + if (notChange) { + return; + } + for (var i = 0; i < len; i++) { + var stateName = states[i]; + var stateObj = void 0; + if (this.stateProxy) { + stateObj = this.stateProxy(stateName, states); + } + if (!stateObj) { + stateObj = this.states[stateName]; + } + if (stateObj) { + stateObjects.push(stateObj); + } + } + var lastStateObj = stateObjects[len - 1]; + var useHoverLayer = !!((lastStateObj && lastStateObj.hoverLayer) || forceUseHoverLayer); + if (useHoverLayer) { + this._toggleHoverLayerFlag(true); + } + var mergedState = this._mergeStates(stateObjects); + var animationCfg = this.stateTransition; + this.saveCurrentToNormalState(mergedState); + this._applyStateObj(states.join(','), mergedState, this._normalState, false, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg); + var textContent = this._textContent; + var textGuide = this._textGuide; + if (textContent) { + textContent.useStates(states, noAnimation, useHoverLayer); + } + if (textGuide) { + textGuide.useStates(states, noAnimation, useHoverLayer); + } + this._updateAnimationTargets(); + this.currentStates = states.slice(); + this.markRedraw(); + if (!useHoverLayer && this.__inHover) { + this._toggleHoverLayerFlag(false); + this.__dirty &= ~REDRAW_BIT; + } + } + }; + Element.prototype.isSilent = function () { + var isSilent = this.silent; + var ancestor = this.parent; + while (!isSilent && ancestor) { + if (ancestor.silent) { + isSilent = true; + break; + } + ancestor = ancestor.parent; + } + return isSilent; + }; + Element.prototype._updateAnimationTargets = function () { + for (var i = 0; i < this.animators.length; i++) { + var animator = this.animators[i]; + if (animator.targetName) { + animator.changeTarget(this[animator.targetName]); + } + } + }; + Element.prototype.removeState = function (state) { + var idx = indexOf(this.currentStates, state); + if (idx >= 0) { + var currentStates = this.currentStates.slice(); + currentStates.splice(idx, 1); + this.useStates(currentStates); + } + }; + Element.prototype.replaceState = function (oldState, newState, forceAdd) { + var currentStates = this.currentStates.slice(); + var idx = indexOf(currentStates, oldState); + var newStateExists = indexOf(currentStates, newState) >= 0; + if (idx >= 0) { + if (!newStateExists) { + currentStates[idx] = newState; + } + else { + currentStates.splice(idx, 1); + } + } + else if (forceAdd && !newStateExists) { + currentStates.push(newState); + } + this.useStates(currentStates); + }; + Element.prototype.toggleState = function (state, enable) { + if (enable) { + this.useState(state, true); + } + else { + this.removeState(state); + } + }; + Element.prototype._mergeStates = function (states) { + var mergedState = {}; + var mergedTextConfig; + for (var i = 0; i < states.length; i++) { + var state = states[i]; + extend(mergedState, state); + if (state.textConfig) { + mergedTextConfig = mergedTextConfig || {}; + extend(mergedTextConfig, state.textConfig); + } + } + if (mergedTextConfig) { + mergedState.textConfig = mergedTextConfig; + } + return mergedState; + }; + Element.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) { + var needsRestoreToNormal = !(state && keepCurrentStates); + if (state && state.textConfig) { + this.textConfig = extend({}, keepCurrentStates ? this.textConfig : normalState.textConfig); + extend(this.textConfig, state.textConfig); + } + else if (needsRestoreToNormal) { + if (normalState.textConfig) { + this.textConfig = normalState.textConfig; + } + } + var transitionTarget = {}; + var hasTransition = false; + for (var i = 0; i < PRIMARY_STATES_KEYS.length; i++) { + var key = PRIMARY_STATES_KEYS[i]; + var propNeedsTransition = transition && DEFAULT_ANIMATABLE_MAP[key]; + if (state && state[key] != null) { + if (propNeedsTransition) { + hasTransition = true; + transitionTarget[key] = state[key]; + } + else { + this[key] = state[key]; + } + } + else if (needsRestoreToNormal) { + if (normalState[key] != null) { + if (propNeedsTransition) { + hasTransition = true; + transitionTarget[key] = normalState[key]; + } + else { + this[key] = normalState[key]; + } + } + } + } + if (!transition) { + for (var i = 0; i < this.animators.length; i++) { + var animator = this.animators[i]; + var targetName = animator.targetName; + if (!animator.getLoop()) { + animator.__changeFinalValue(targetName + ? (state || normalState)[targetName] + : (state || normalState)); + } + } + } + if (hasTransition) { + this._transitionState(stateName, transitionTarget, animationCfg); + } + }; + Element.prototype._attachComponent = function (componentEl) { + if (componentEl.__zr && !componentEl.__hostTarget) { + if ("development" !== 'production') { + throw new Error('Text element has been added to zrender.'); + } + return; + } + if (componentEl === this) { + if ("development" !== 'production') { + throw new Error('Recursive component attachment.'); + } + return; + } + var zr = this.__zr; + if (zr) { + componentEl.addSelfToZr(zr); + } + componentEl.__zr = zr; + componentEl.__hostTarget = this; + }; + Element.prototype._detachComponent = function (componentEl) { + if (componentEl.__zr) { + componentEl.removeSelfFromZr(componentEl.__zr); + } + componentEl.__zr = null; + componentEl.__hostTarget = null; + }; + Element.prototype.getClipPath = function () { + return this._clipPath; + }; + Element.prototype.setClipPath = function (clipPath) { + if (this._clipPath && this._clipPath !== clipPath) { + this.removeClipPath(); + } + this._attachComponent(clipPath); + this._clipPath = clipPath; + this.markRedraw(); + }; + Element.prototype.removeClipPath = function () { + var clipPath = this._clipPath; + if (clipPath) { + this._detachComponent(clipPath); + this._clipPath = null; + this.markRedraw(); + } + }; + Element.prototype.getTextContent = function () { + return this._textContent; + }; + Element.prototype.setTextContent = function (textEl) { + var previousTextContent = this._textContent; + if (previousTextContent === textEl) { + return; + } + if (previousTextContent && previousTextContent !== textEl) { + this.removeTextContent(); + } + if ("development" !== 'production') { + if (textEl.__zr && !textEl.__hostTarget) { + throw new Error('Text element has been added to zrender.'); + } + } + textEl.innerTransformable = new Transformable(); + this._attachComponent(textEl); + this._textContent = textEl; + this.markRedraw(); + }; + Element.prototype.setTextConfig = function (cfg) { + if (!this.textConfig) { + this.textConfig = {}; + } + extend(this.textConfig, cfg); + this.markRedraw(); + }; + Element.prototype.removeTextConfig = function () { + this.textConfig = null; + this.markRedraw(); + }; + Element.prototype.removeTextContent = function () { + var textEl = this._textContent; + if (textEl) { + textEl.innerTransformable = null; + this._detachComponent(textEl); + this._textContent = null; + this._innerTextDefaultStyle = null; + this.markRedraw(); + } + }; + Element.prototype.getTextGuideLine = function () { + return this._textGuide; + }; + Element.prototype.setTextGuideLine = function (guideLine) { + if (this._textGuide && this._textGuide !== guideLine) { + this.removeTextGuideLine(); + } + this._attachComponent(guideLine); + this._textGuide = guideLine; + this.markRedraw(); + }; + Element.prototype.removeTextGuideLine = function () { + var textGuide = this._textGuide; + if (textGuide) { + this._detachComponent(textGuide); + this._textGuide = null; + this.markRedraw(); + } + }; + Element.prototype.markRedraw = function () { + this.__dirty |= REDRAW_BIT; + var zr = this.__zr; + if (zr) { + if (this.__inHover) { + zr.refreshHover(); + } + else { + zr.refresh(); + } + } + if (this.__hostTarget) { + this.__hostTarget.markRedraw(); + } + }; + Element.prototype.dirty = function () { + this.markRedraw(); + }; + Element.prototype._toggleHoverLayerFlag = function (inHover) { + this.__inHover = inHover; + var textContent = this._textContent; + var textGuide = this._textGuide; + if (textContent) { + textContent.__inHover = inHover; + } + if (textGuide) { + textGuide.__inHover = inHover; + } + }; + Element.prototype.addSelfToZr = function (zr) { + if (this.__zr === zr) { + return; + } + this.__zr = zr; + var animators = this.animators; + if (animators) { + for (var i = 0; i < animators.length; i++) { + zr.animation.addAnimator(animators[i]); + } + } + if (this._clipPath) { + this._clipPath.addSelfToZr(zr); + } + if (this._textContent) { + this._textContent.addSelfToZr(zr); + } + if (this._textGuide) { + this._textGuide.addSelfToZr(zr); + } + }; + Element.prototype.removeSelfFromZr = function (zr) { + if (!this.__zr) { + return; + } + this.__zr = null; + var animators = this.animators; + if (animators) { + for (var i = 0; i < animators.length; i++) { + zr.animation.removeAnimator(animators[i]); + } + } + if (this._clipPath) { + this._clipPath.removeSelfFromZr(zr); + } + if (this._textContent) { + this._textContent.removeSelfFromZr(zr); + } + if (this._textGuide) { + this._textGuide.removeSelfFromZr(zr); + } + }; + Element.prototype.animate = function (key, loop, allowDiscreteAnimation) { + var target = key ? this[key] : this; + if ("development" !== 'production') { + if (!target) { + logError('Property "' + + key + + '" is not existed in element ' + + this.id); + return; + } + } + var animator = new Animator(target, loop, allowDiscreteAnimation); + key && (animator.targetName = key); + this.addAnimator(animator, key); + return animator; + }; + Element.prototype.addAnimator = function (animator, key) { + var zr = this.__zr; + var el = this; + animator.during(function () { + el.updateDuringAnimation(key); + }).done(function () { + var animators = el.animators; + var idx = indexOf(animators, animator); + if (idx >= 0) { + animators.splice(idx, 1); + } + }); + this.animators.push(animator); + if (zr) { + zr.animation.addAnimator(animator); + } + zr && zr.wakeUp(); + }; + Element.prototype.updateDuringAnimation = function (key) { + this.markRedraw(); + }; + Element.prototype.stopAnimation = function (scope, forwardToLast) { + var animators = this.animators; + var len = animators.length; + var leftAnimators = []; + for (var i = 0; i < len; i++) { + var animator = animators[i]; + if (!scope || scope === animator.scope) { + animator.stop(forwardToLast); + } + else { + leftAnimators.push(animator); + } + } + this.animators = leftAnimators; + return this; + }; + Element.prototype.animateTo = function (target, cfg, animationProps) { + animateTo(this, target, cfg, animationProps); + }; + Element.prototype.animateFrom = function (target, cfg, animationProps) { + animateTo(this, target, cfg, animationProps, true); + }; + Element.prototype._transitionState = function (stateName, target, cfg, animationProps) { + var animators = animateTo(this, target, cfg, animationProps); + for (var i = 0; i < animators.length; i++) { + animators[i].__fromStateTransition = stateName; + } + }; + Element.prototype.getBoundingRect = function () { + return null; + }; + Element.prototype.getPaintRect = function () { + return null; + }; + Element.initDefaultProps = (function () { + var elProto = Element.prototype; + elProto.type = 'element'; + elProto.name = ''; + elProto.ignore = + elProto.silent = + elProto.isGroup = + elProto.draggable = + elProto.dragging = + elProto.ignoreClip = + elProto.__inHover = false; + elProto.__dirty = REDRAW_BIT; + var logs = {}; + function logDeprecatedError(key, xKey, yKey) { + if (!logs[key + xKey + yKey]) { + console.warn("DEPRECATED: '" + key + "' has been deprecated. use '" + xKey + "', '" + yKey + "' instead"); + logs[key + xKey + yKey] = true; + } + } + function createLegacyProperty(key, privateKey, xKey, yKey) { + Object.defineProperty(elProto, key, { + get: function () { + if ("development" !== 'production') { + logDeprecatedError(key, xKey, yKey); + } + if (!this[privateKey]) { + var pos = this[privateKey] = []; + enhanceArray(this, pos); + } + return this[privateKey]; + }, + set: function (pos) { + if ("development" !== 'production') { + logDeprecatedError(key, xKey, yKey); + } + this[xKey] = pos[0]; + this[yKey] = pos[1]; + this[privateKey] = pos; + enhanceArray(this, pos); + } + }); + function enhanceArray(self, pos) { + Object.defineProperty(pos, 0, { + get: function () { + return self[xKey]; + }, + set: function (val) { + self[xKey] = val; + } + }); + Object.defineProperty(pos, 1, { + get: function () { + return self[yKey]; + }, + set: function (val) { + self[yKey] = val; + } + }); + } + } + if (Object.defineProperty) { + createLegacyProperty('position', '_legacyPos', 'x', 'y'); + createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY'); + createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY'); + } + })(); + return Element; + }()); + mixin(Element, Eventful); + mixin(Element, Transformable); + function animateTo(animatable, target, cfg, animationProps, reverse) { + cfg = cfg || {}; + var animators = []; + animateToShallow(animatable, '', animatable, target, cfg, animationProps, animators, reverse); + var finishCount = animators.length; + var doneHappened = false; + var cfgDone = cfg.done; + var cfgAborted = cfg.aborted; + var doneCb = function () { + doneHappened = true; + finishCount--; + if (finishCount <= 0) { + doneHappened + ? (cfgDone && cfgDone()) + : (cfgAborted && cfgAborted()); + } + }; + var abortedCb = function () { + finishCount--; + if (finishCount <= 0) { + doneHappened + ? (cfgDone && cfgDone()) + : (cfgAborted && cfgAborted()); + } + }; + if (!finishCount) { + cfgDone && cfgDone(); + } + if (animators.length > 0 && cfg.during) { + animators[0].during(function (target, percent) { + cfg.during(percent); + }); + } + for (var i = 0; i < animators.length; i++) { + var animator = animators[i]; + if (doneCb) { + animator.done(doneCb); + } + if (abortedCb) { + animator.aborted(abortedCb); + } + if (cfg.force) { + animator.duration(cfg.duration); + } + animator.start(cfg.easing); + } + return animators; + } + function copyArrShallow(source, target, len) { + for (var i = 0; i < len; i++) { + source[i] = target[i]; + } + } + function is2DArray(value) { + return isArrayLike(value[0]); + } + function copyValue(target, source, key) { + if (isArrayLike(source[key])) { + if (!isArrayLike(target[key])) { + target[key] = []; + } + if (isTypedArray(source[key])) { + var len = source[key].length; + if (target[key].length !== len) { + target[key] = new (source[key].constructor)(len); + copyArrShallow(target[key], source[key], len); + } + } + else { + var sourceArr = source[key]; + var targetArr = target[key]; + var len0 = sourceArr.length; + if (is2DArray(sourceArr)) { + var len1 = sourceArr[0].length; + for (var i = 0; i < len0; i++) { + if (!targetArr[i]) { + targetArr[i] = Array.prototype.slice.call(sourceArr[i]); + } + else { + copyArrShallow(targetArr[i], sourceArr[i], len1); + } + } + } + else { + copyArrShallow(targetArr, sourceArr, len0); + } + targetArr.length = sourceArr.length; + } + } + else { + target[key] = source[key]; + } + } + function isValueSame(val1, val2) { + return val1 === val2 + || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2); + } + function is1DArraySame(arr0, arr1) { + var len = arr0.length; + if (len !== arr1.length) { + return false; + } + for (var i = 0; i < len; i++) { + if (arr0[i] !== arr1[i]) { + return false; + } + } + return true; + } + function animateToShallow(animatable, topKey, animateObj, target, cfg, animationProps, animators, reverse) { + var targetKeys = keys(target); + var duration = cfg.duration; + var delay = cfg.delay; + var additive = cfg.additive; + var setToFinal = cfg.setToFinal; + var animateAll = !isObject(animationProps); + var existsAnimators = animatable.animators; + var animationKeys = []; + for (var k = 0; k < targetKeys.length; k++) { + var innerKey = targetKeys[k]; + var targetVal = target[innerKey]; + if (targetVal != null && animateObj[innerKey] != null + && (animateAll || animationProps[innerKey])) { + if (isObject(targetVal) + && !isArrayLike(targetVal) + && !isGradientObject(targetVal)) { + if (topKey) { + if (!reverse) { + animateObj[innerKey] = targetVal; + animatable.updateDuringAnimation(topKey); + } + continue; + } + animateToShallow(animatable, innerKey, animateObj[innerKey], targetVal, cfg, animationProps && animationProps[innerKey], animators, reverse); + } + else { + animationKeys.push(innerKey); + } + } + else if (!reverse) { + animateObj[innerKey] = targetVal; + animatable.updateDuringAnimation(topKey); + animationKeys.push(innerKey); + } + } + var keyLen = animationKeys.length; + if (!additive && keyLen) { + for (var i = 0; i < existsAnimators.length; i++) { + var animator = existsAnimators[i]; + if (animator.targetName === topKey) { + var allAborted = animator.stopTracks(animationKeys); + if (allAborted) { + var idx = indexOf(existsAnimators, animator); + existsAnimators.splice(idx, 1); + } + } + } + } + if (!cfg.force) { + animationKeys = filter(animationKeys, function (key) { return !isValueSame(target[key], animateObj[key]); }); + keyLen = animationKeys.length; + } + if (keyLen > 0 + || (cfg.force && !animators.length)) { + var revertedSource = void 0; + var reversedTarget = void 0; + var sourceClone = void 0; + if (reverse) { + reversedTarget = {}; + if (setToFinal) { + revertedSource = {}; + } + for (var i = 0; i < keyLen; i++) { + var innerKey = animationKeys[i]; + reversedTarget[innerKey] = animateObj[innerKey]; + if (setToFinal) { + revertedSource[innerKey] = target[innerKey]; + } + else { + animateObj[innerKey] = target[innerKey]; + } + } + } + else if (setToFinal) { + sourceClone = {}; + for (var i = 0; i < keyLen; i++) { + var innerKey = animationKeys[i]; + sourceClone[innerKey] = cloneValue(animateObj[innerKey]); + copyValue(animateObj, target, innerKey); + } + } + var animator = new Animator(animateObj, false, false, additive ? filter(existsAnimators, function (animator) { return animator.targetName === topKey; }) : null); + animator.targetName = topKey; + if (cfg.scope) { + animator.scope = cfg.scope; + } + if (setToFinal && revertedSource) { + animator.whenWithKeys(0, revertedSource, animationKeys); + } + if (sourceClone) { + animator.whenWithKeys(0, sourceClone, animationKeys); + } + animator.whenWithKeys(duration == null ? 500 : duration, reverse ? reversedTarget : target, animationKeys).delay(delay || 0); + animatable.addAnimator(animator, topKey); + animators.push(animator); + } + } + + var Group = (function (_super) { + __extends(Group, _super); + function Group(opts) { + var _this = _super.call(this) || this; + _this.isGroup = true; + _this._children = []; + _this.attr(opts); + return _this; + } + Group.prototype.childrenRef = function () { + return this._children; + }; + Group.prototype.children = function () { + return this._children.slice(); + }; + Group.prototype.childAt = function (idx) { + return this._children[idx]; + }; + Group.prototype.childOfName = function (name) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + if (children[i].name === name) { + return children[i]; + } + } + }; + Group.prototype.childCount = function () { + return this._children.length; + }; + Group.prototype.add = function (child) { + if (child) { + if (child !== this && child.parent !== this) { + this._children.push(child); + this._doAdd(child); + } + if ("development" !== 'production') { + if (child.__hostTarget) { + throw 'This elemenet has been used as an attachment'; + } + } + } + return this; + }; + Group.prototype.addBefore = function (child, nextSibling) { + if (child && child !== this && child.parent !== this + && nextSibling && nextSibling.parent === this) { + var children = this._children; + var idx = children.indexOf(nextSibling); + if (idx >= 0) { + children.splice(idx, 0, child); + this._doAdd(child); + } + } + return this; + }; + Group.prototype.replace = function (oldChild, newChild) { + var idx = indexOf(this._children, oldChild); + if (idx >= 0) { + this.replaceAt(newChild, idx); + } + return this; + }; + Group.prototype.replaceAt = function (child, index) { + var children = this._children; + var old = children[index]; + if (child && child !== this && child.parent !== this && child !== old) { + children[index] = child; + old.parent = null; + var zr = this.__zr; + if (zr) { + old.removeSelfFromZr(zr); + } + this._doAdd(child); + } + return this; + }; + Group.prototype._doAdd = function (child) { + if (child.parent) { + child.parent.remove(child); + } + child.parent = this; + var zr = this.__zr; + if (zr && zr !== child.__zr) { + child.addSelfToZr(zr); + } + zr && zr.refresh(); + }; + Group.prototype.remove = function (child) { + var zr = this.__zr; + var children = this._children; + var idx = indexOf(children, child); + if (idx < 0) { + return this; + } + children.splice(idx, 1); + child.parent = null; + if (zr) { + child.removeSelfFromZr(zr); + } + zr && zr.refresh(); + return this; + }; + Group.prototype.removeAll = function () { + var children = this._children; + var zr = this.__zr; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (zr) { + child.removeSelfFromZr(zr); + } + child.parent = null; + } + children.length = 0; + return this; + }; + Group.prototype.eachChild = function (cb, context) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + cb.call(context, child, i); + } + return this; + }; + Group.prototype.traverse = function (cb, context) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + var stopped = cb.call(context, child); + if (child.isGroup && !stopped) { + child.traverse(cb, context); + } + } + return this; + }; + Group.prototype.addSelfToZr = function (zr) { + _super.prototype.addSelfToZr.call(this, zr); + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + child.addSelfToZr(zr); + } + }; + Group.prototype.removeSelfFromZr = function (zr) { + _super.prototype.removeSelfFromZr.call(this, zr); + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + child.removeSelfFromZr(zr); + } + }; + Group.prototype.getBoundingRect = function (includeChildren) { + var tmpRect = new BoundingRect(0, 0, 0, 0); + var children = includeChildren || this._children; + var tmpMat = []; + var rect = null; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.ignore || child.invisible) { + continue; + } + var childRect = child.getBoundingRect(); + var transform = child.getLocalTransform(tmpMat); + if (transform) { + BoundingRect.applyTransform(tmpRect, childRect, transform); + rect = rect || tmpRect.clone(); + rect.union(tmpRect); + } + else { + rect = rect || childRect.clone(); + rect.union(childRect); + } + } + return rect || tmpRect; + }; + return Group; + }(Element)); + Group.prototype.type = 'group'; + + /*! + * ZRender, a high performance 2d drawing library. + * + * Copyright (c) 2013, Baidu Inc. + * All rights reserved. + * + * LICENSE + * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt + */ + var painterCtors = {}; + var instances = {}; + function delInstance(id) { + delete instances[id]; + } + function isDarkMode(backgroundColor) { + if (!backgroundColor) { + return false; + } + if (typeof backgroundColor === 'string') { + return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD; + } + else if (backgroundColor.colorStops) { + var colorStops = backgroundColor.colorStops; + var totalLum = 0; + var len = colorStops.length; + for (var i = 0; i < len; i++) { + totalLum += lum(colorStops[i].color, 1); + } + totalLum /= len; + return totalLum < DARK_MODE_THRESHOLD; + } + return false; + } + var ZRender = (function () { + function ZRender(id, dom, opts) { + var _this = this; + this._sleepAfterStill = 10; + this._stillFrameAccum = 0; + this._needsRefresh = true; + this._needsRefreshHover = true; + this._darkMode = false; + opts = opts || {}; + this.dom = dom; + this.id = id; + var storage = new Storage(); + var rendererType = opts.renderer || 'canvas'; + if (!painterCtors[rendererType]) { + rendererType = keys(painterCtors)[0]; + } + if ("development" !== 'production') { + if (!painterCtors[rendererType]) { + throw new Error("Renderer '" + rendererType + "' is not imported. Please import it first."); + } + } + opts.useDirtyRect = opts.useDirtyRect == null + ? false + : opts.useDirtyRect; + var painter = new painterCtors[rendererType](dom, storage, opts, id); + var ssrMode = opts.ssr || painter.ssrOnly; + this.storage = storage; + this.painter = painter; + var handlerProxy = (!env.node && !env.worker && !ssrMode) + ? new HandlerDomProxy(painter.getViewportRoot(), painter.root) + : null; + var useCoarsePointer = opts.useCoarsePointer; + var usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto') + ? env.touchEventsSupported + : !!useCoarsePointer; + var defaultPointerSize = 44; + var pointerSize; + if (usePointerSize) { + pointerSize = retrieve2(opts.pointerSize, defaultPointerSize); + } + this.handler = new Handler(storage, painter, handlerProxy, painter.root, pointerSize); + this.animation = new Animation({ + stage: { + update: ssrMode ? null : function () { return _this._flush(true); } + } + }); + if (!ssrMode) { + this.animation.start(); + } + } + ZRender.prototype.add = function (el) { + if (this._disposed || !el) { + return; + } + this.storage.addRoot(el); + el.addSelfToZr(this); + this.refresh(); + }; + ZRender.prototype.remove = function (el) { + if (this._disposed || !el) { + return; + } + this.storage.delRoot(el); + el.removeSelfFromZr(this); + this.refresh(); + }; + ZRender.prototype.configLayer = function (zLevel, config) { + if (this._disposed) { + return; + } + if (this.painter.configLayer) { + this.painter.configLayer(zLevel, config); + } + this.refresh(); + }; + ZRender.prototype.setBackgroundColor = function (backgroundColor) { + if (this._disposed) { + return; + } + if (this.painter.setBackgroundColor) { + this.painter.setBackgroundColor(backgroundColor); + } + this.refresh(); + this._backgroundColor = backgroundColor; + this._darkMode = isDarkMode(backgroundColor); + }; + ZRender.prototype.getBackgroundColor = function () { + return this._backgroundColor; + }; + ZRender.prototype.setDarkMode = function (darkMode) { + this._darkMode = darkMode; + }; + ZRender.prototype.isDarkMode = function () { + return this._darkMode; + }; + ZRender.prototype.refreshImmediately = function (fromInside) { + if (this._disposed) { + return; + } + if (!fromInside) { + this.animation.update(true); + } + this._needsRefresh = false; + this.painter.refresh(); + this._needsRefresh = false; + }; + ZRender.prototype.refresh = function () { + if (this._disposed) { + return; + } + this._needsRefresh = true; + this.animation.start(); + }; + ZRender.prototype.flush = function () { + if (this._disposed) { + return; + } + this._flush(false); + }; + ZRender.prototype._flush = function (fromInside) { + var triggerRendered; + var start = getTime(); + if (this._needsRefresh) { + triggerRendered = true; + this.refreshImmediately(fromInside); + } + if (this._needsRefreshHover) { + triggerRendered = true; + this.refreshHoverImmediately(); + } + var end = getTime(); + if (triggerRendered) { + this._stillFrameAccum = 0; + this.trigger('rendered', { + elapsedTime: end - start + }); + } + else if (this._sleepAfterStill > 0) { + this._stillFrameAccum++; + if (this._stillFrameAccum > this._sleepAfterStill) { + this.animation.stop(); + } + } + }; + ZRender.prototype.setSleepAfterStill = function (stillFramesCount) { + this._sleepAfterStill = stillFramesCount; + }; + ZRender.prototype.wakeUp = function () { + if (this._disposed) { + return; + } + this.animation.start(); + this._stillFrameAccum = 0; + }; + ZRender.prototype.refreshHover = function () { + this._needsRefreshHover = true; + }; + ZRender.prototype.refreshHoverImmediately = function () { + if (this._disposed) { + return; + } + this._needsRefreshHover = false; + if (this.painter.refreshHover && this.painter.getType() === 'canvas') { + this.painter.refreshHover(); + } + }; + ZRender.prototype.resize = function (opts) { + if (this._disposed) { + return; + } + opts = opts || {}; + this.painter.resize(opts.width, opts.height); + this.handler.resize(); + }; + ZRender.prototype.clearAnimation = function () { + if (this._disposed) { + return; + } + this.animation.clear(); + }; + ZRender.prototype.getWidth = function () { + if (this._disposed) { + return; + } + return this.painter.getWidth(); + }; + ZRender.prototype.getHeight = function () { + if (this._disposed) { + return; + } + return this.painter.getHeight(); + }; + ZRender.prototype.setCursorStyle = function (cursorStyle) { + if (this._disposed) { + return; + } + this.handler.setCursorStyle(cursorStyle); + }; + ZRender.prototype.findHover = function (x, y) { + if (this._disposed) { + return; + } + return this.handler.findHover(x, y); + }; + ZRender.prototype.on = function (eventName, eventHandler, context) { + if (!this._disposed) { + this.handler.on(eventName, eventHandler, context); + } + return this; + }; + ZRender.prototype.off = function (eventName, eventHandler) { + if (this._disposed) { + return; + } + this.handler.off(eventName, eventHandler); + }; + ZRender.prototype.trigger = function (eventName, event) { + if (this._disposed) { + return; + } + this.handler.trigger(eventName, event); + }; + ZRender.prototype.clear = function () { + if (this._disposed) { + return; + } + var roots = this.storage.getRoots(); + for (var i = 0; i < roots.length; i++) { + if (roots[i] instanceof Group) { + roots[i].removeSelfFromZr(this); + } + } + this.storage.delAllRoots(); + this.painter.clear(); + }; + ZRender.prototype.dispose = function () { + if (this._disposed) { + return; + } + this.animation.stop(); + this.clear(); + this.storage.dispose(); + this.painter.dispose(); + this.handler.dispose(); + this.animation = + this.storage = + this.painter = + this.handler = null; + this._disposed = true; + delInstance(this.id); + }; + return ZRender; + }()); + function init(dom, opts) { + var zr = new ZRender(guid(), dom, opts); + instances[zr.id] = zr; + return zr; + } + function dispose(zr) { + zr.dispose(); + } + function disposeAll() { + for (var key in instances) { + if (instances.hasOwnProperty(key)) { + instances[key].dispose(); + } + } + instances = {}; + } + function getInstance(id) { + return instances[id]; + } + function registerPainter(name, Ctor) { + painterCtors[name] = Ctor; + } + var ssrDataGetter; + function getElementSSRData(el) { + if (typeof ssrDataGetter === 'function') { + return ssrDataGetter(el); + } + } + function registerSSRDataGetter(getter) { + ssrDataGetter = getter; + } + var version = '5.5.0'; + + var zrender = /*#__PURE__*/Object.freeze({ + __proto__: null, + init: init, + dispose: dispose, + disposeAll: disposeAll, + getInstance: getInstance, + registerPainter: registerPainter, + getElementSSRData: getElementSSRData, + registerSSRDataGetter: registerSSRDataGetter, + version: version + }); + + var RADIAN_EPSILON = 1e-4; + // Although chrome already enlarge this number to 100 for `toFixed`, but + // we sill follow the spec for compatibility. + var ROUND_SUPPORTED_PRECISION_MAX = 20; + function _trim(str) { + return str.replace(/^\s+|\s+$/g, ''); + } + /** + * Linear mapping a value from domain to range + * @param val + * @param domain Domain extent domain[0] can be bigger than domain[1] + * @param range Range extent range[0] can be bigger than range[1] + * @param clamp Default to be false + */ + function linearMap(val, domain, range, clamp) { + var d0 = domain[0]; + var d1 = domain[1]; + var r0 = range[0]; + var r1 = range[1]; + var subDomain = d1 - d0; + var subRange = r1 - r0; + if (subDomain === 0) { + return subRange === 0 ? r0 : (r0 + r1) / 2; + } + // Avoid accuracy problem in edge, such as + // 146.39 - 62.83 === 83.55999999999999. + // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError + // It is a little verbose for efficiency considering this method + // is a hotspot. + if (clamp) { + if (subDomain > 0) { + if (val <= d0) { + return r0; + } else if (val >= d1) { + return r1; + } + } else { + if (val >= d0) { + return r0; + } else if (val <= d1) { + return r1; + } + } + } else { + if (val === d0) { + return r0; + } + if (val === d1) { + return r1; + } + } + return (val - d0) / subDomain * subRange + r0; + } + /** + * Convert a percent string to absolute number. + * Returns NaN if percent is not a valid string or number + */ + function parsePercent$1(percent, all) { + switch (percent) { + case 'center': + case 'middle': + percent = '50%'; + break; + case 'left': + case 'top': + percent = '0%'; + break; + case 'right': + case 'bottom': + percent = '100%'; + break; + } + if (isString(percent)) { + if (_trim(percent).match(/%$/)) { + return parseFloat(percent) / 100 * all; + } + return parseFloat(percent); + } + return percent == null ? NaN : +percent; + } + function round(x, precision, returnStr) { + if (precision == null) { + precision = 10; + } + // Avoid range error + precision = Math.min(Math.max(0, precision), ROUND_SUPPORTED_PRECISION_MAX); + // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01' + x = (+x).toFixed(precision); + return returnStr ? x : +x; + } + /** + * Inplacd asc sort arr. + * The input arr will be modified. + */ + function asc(arr) { + arr.sort(function (a, b) { + return a - b; + }); + return arr; + } + /** + * Get precision. + */ + function getPrecision(val) { + val = +val; + if (isNaN(val)) { + return 0; + } + // It is much faster than methods converting number to string as follows + // let tmp = val.toString(); + // return tmp.length - 1 - tmp.indexOf('.'); + // especially when precision is low + // Notice: + // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`. + // (see https://jsbench.me/2vkpcekkvw/1) + // (2) If the val is less than for example 1e-15, the result may be incorrect. + // (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`) + if (val > 1e-14) { + var e = 1; + for (var i = 0; i < 15; i++, e *= 10) { + if (Math.round(val * e) / e === val) { + return i; + } + } + } + return getPrecisionSafe(val); + } + /** + * Get precision with slow but safe method + */ + function getPrecisionSafe(val) { + // toLowerCase for: '3.4E-12' + var str = val.toString().toLowerCase(); + // Consider scientific notation: '3.4e-12' '3.4e+12' + var eIndex = str.indexOf('e'); + var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0; + var significandPartLen = eIndex > 0 ? eIndex : str.length; + var dotIndex = str.indexOf('.'); + var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex; + return Math.max(0, decimalPartLen - exp); + } + /** + * Minimal dicernible data precisioin according to a single pixel. + */ + function getPixelPrecision(dataExtent, pixelExtent) { + var log = Math.log; + var LN10 = Math.LN10; + var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10); + var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); + // toFixed() digits argument must be between 0 and 20. + var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20); + return !isFinite(precision) ? 20 : precision; + } + /** + * Get a data of given precision, assuring the sum of percentages + * in valueList is 1. + * The largest remainder method is used. + * https://en.wikipedia.org/wiki/Largest_remainder_method + * + * @param valueList a list of all data + * @param idx index of the data to be processed in valueList + * @param precision integer number showing digits of precision + * @return percent ranging from 0 to 100 + */ + function getPercentWithPrecision(valueList, idx, precision) { + if (!valueList[idx]) { + return 0; + } + var seats = getPercentSeats(valueList, precision); + return seats[idx] || 0; + } + /** + * Get a data of given precision, assuring the sum of percentages + * in valueList is 1. + * The largest remainder method is used. + * https://en.wikipedia.org/wiki/Largest_remainder_method + * + * @param valueList a list of all data + * @param precision integer number showing digits of precision + * @return {Array} + */ + function getPercentSeats(valueList, precision) { + var sum = reduce(valueList, function (acc, val) { + return acc + (isNaN(val) ? 0 : val); + }, 0); + if (sum === 0) { + return []; + } + var digits = Math.pow(10, precision); + var votesPerQuota = map(valueList, function (val) { + return (isNaN(val) ? 0 : val) / sum * digits * 100; + }); + var targetSeats = digits * 100; + var seats = map(votesPerQuota, function (votes) { + // Assign automatic seats. + return Math.floor(votes); + }); + var currentSum = reduce(seats, function (acc, val) { + return acc + val; + }, 0); + var remainder = map(votesPerQuota, function (votes, idx) { + return votes - seats[idx]; + }); + // Has remainding votes. + while (currentSum < targetSeats) { + // Find next largest remainder. + var max = Number.NEGATIVE_INFINITY; + var maxId = null; + for (var i = 0, len = remainder.length; i < len; ++i) { + if (remainder[i] > max) { + max = remainder[i]; + maxId = i; + } + } + // Add a vote to max remainder. + ++seats[maxId]; + remainder[maxId] = 0; + ++currentSum; + } + return map(seats, function (seat) { + return seat / digits; + }); + } + /** + * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004 + * See + */ + function addSafe(val0, val1) { + var maxPrecision = Math.max(getPrecision(val0), getPrecision(val1)); + // const multiplier = Math.pow(10, maxPrecision); + // return (Math.round(val0 * multiplier) + Math.round(val1 * multiplier)) / multiplier; + var sum = val0 + val1; + // // PENDING: support more? + return maxPrecision > ROUND_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision); + } + // Number.MAX_SAFE_INTEGER, ie do not support. + var MAX_SAFE_INTEGER = 9007199254740991; + /** + * To 0 - 2 * PI, considering negative radian. + */ + function remRadian(radian) { + var pi2 = Math.PI * 2; + return (radian % pi2 + pi2) % pi2; + } + /** + * @param {type} radian + * @return {boolean} + */ + function isRadianAroundZero(val) { + return val > -RADIAN_EPSILON && val < RADIAN_EPSILON; + } + // eslint-disable-next-line + var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line + /** + * @param value valid type: number | string | Date, otherwise return `new Date(NaN)` + * These values can be accepted: + * + An instance of Date, represent a time in its own time zone. + * + Or string in a subset of ISO 8601, only including: + * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06', + * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123', + * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00', + * all of which will be treated as local time if time zone is not specified + * (see ). + * + Or other string format, including (all of which will be treated as local time): + * '2012', '2012-3-1', '2012/3/1', '2012/03/01', + * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123' + * + a timestamp, which represent a time in UTC. + * @return date Never be null/undefined. If invalid, return `new Date(NaN)`. + */ + function parseDate(value) { + if (value instanceof Date) { + return value; + } else if (isString(value)) { + // Different browsers parse date in different way, so we parse it manually. + // Some other issues: + // new Date('1970-01-01') is UTC, + // new Date('1970/01/01') and new Date('1970-1-01') is local. + // See issue #3623 + var match = TIME_REG.exec(value); + if (!match) { + // return Invalid Date. + return new Date(NaN); + } + // Use local time when no timezone offset is specified. + if (!match[8]) { + // match[n] can only be string or undefined. + // But take care of '12' + 1 => '121'. + return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0); + } + // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time, + // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment). + // For example, system timezone is set as "Time Zone: America/Toronto", + // then these code will get different result: + // `new Date(1478411999999).getTimezoneOffset(); // get 240` + // `new Date(1478412000000).getTimezoneOffset(); // get 300` + // So we should not use `new Date`, but use `Date.UTC`. + else { + var hour = +match[4] || 0; + if (match[8].toUpperCase() !== 'Z') { + hour -= +match[8].slice(0, 3); + } + return new Date(Date.UTC(+match[1], +(match[2] || 1) - 1, +match[3] || 1, hour, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0)); + } + } else if (value == null) { + return new Date(NaN); + } + return new Date(Math.round(value)); + } + /** + * Quantity of a number. e.g. 0.1, 1, 10, 100 + * + * @param val + * @return + */ + function quantity(val) { + return Math.pow(10, quantityExponent(val)); + } + /** + * Exponent of the quantity of a number + * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3 + * + * @param val non-negative value + * @return + */ + function quantityExponent(val) { + if (val === 0) { + return 0; + } + var exp = Math.floor(Math.log(val) / Math.LN10); + /** + * exp is expected to be the rounded-down result of the base-10 log of val. + * But due to the precision loss with Math.log(val), we need to restore it + * using 10^exp to make sure we can get val back from exp. #11249 + */ + if (val / Math.pow(10, exp) >= 10) { + exp++; + } + return exp; + } + /** + * find a “nice” number approximately equal to x. Round the number if round = true, + * take ceiling if round = false. The primary observation is that the “nicest” + * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers. + * + * See "Nice Numbers for Graph Labels" of Graphic Gems. + * + * @param val Non-negative value. + * @param round + * @return Niced number + */ + function nice(val, round) { + var exponent = quantityExponent(val); + var exp10 = Math.pow(10, exponent); + var f = val / exp10; // 1 <= f < 10 + var nf; + if (round) { + if (f < 1.5) { + nf = 1; + } else if (f < 2.5) { + nf = 2; + } else if (f < 4) { + nf = 3; + } else if (f < 7) { + nf = 5; + } else { + nf = 10; + } + } else { + if (f < 1) { + nf = 1; + } else if (f < 2) { + nf = 2; + } else if (f < 3) { + nf = 3; + } else if (f < 5) { + nf = 5; + } else { + nf = 10; + } + } + val = nf * exp10; + // Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754). + // 20 is the uppper bound of toFixed. + return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val; + } + /** + * This code was copied from "d3.js" + * . + * See the license statement at the head of this file. + * @param ascArr + */ + function quantile(ascArr, p) { + var H = (ascArr.length - 1) * p + 1; + var h = Math.floor(H); + var v = +ascArr[h - 1]; + var e = H - h; + return e ? v + e * (ascArr[h] - v) : v; + } + /** + * Order intervals asc, and split them when overlap. + * expect(numberUtil.reformIntervals([ + * {interval: [18, 62], close: [1, 1]}, + * {interval: [-Infinity, -70], close: [0, 0]}, + * {interval: [-70, -26], close: [1, 1]}, + * {interval: [-26, 18], close: [1, 1]}, + * {interval: [62, 150], close: [1, 1]}, + * {interval: [106, 150], close: [1, 1]}, + * {interval: [150, Infinity], close: [0, 0]} + * ])).toEqual([ + * {interval: [-Infinity, -70], close: [0, 0]}, + * {interval: [-70, -26], close: [1, 1]}, + * {interval: [-26, 18], close: [0, 1]}, + * {interval: [18, 62], close: [0, 1]}, + * {interval: [62, 150], close: [0, 1]}, + * {interval: [150, Infinity], close: [0, 0]} + * ]); + * @param list, where `close` mean open or close + * of the interval, and Infinity can be used. + * @return The origin list, which has been reformed. + */ + function reformIntervals(list) { + list.sort(function (a, b) { + return littleThan(a, b, 0) ? -1 : 1; + }); + var curr = -Infinity; + var currClose = 1; + for (var i = 0; i < list.length;) { + var interval = list[i].interval; + var close_1 = list[i].close; + for (var lg = 0; lg < 2; lg++) { + if (interval[lg] <= curr) { + interval[lg] = curr; + close_1[lg] = !lg ? 1 - currClose : 1; + } + curr = interval[lg]; + currClose = close_1[lg]; + } + if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) { + list.splice(i, 1); + } else { + i++; + } + } + return list; + function littleThan(a, b, lg) { + return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1)); + } + } + /** + * [Numeric is defined as]: + * `parseFloat(val) == val` + * For example: + * numeric: + * typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity, + * and they rounded by white-spaces or line-terminal like ' -123 \n ' (see es spec) + * not-numeric: + * null, undefined, [], {}, true, false, 'NaN', NaN, '123ab', + * empty string, string with only white-spaces or line-terminal (see es spec), + * 0x12, '0x12', '-0x12', 012, '012', '-012', + * non-string, ... + * + * @test See full test cases in `test/ut/spec/util/number.js`. + * @return Must be a typeof number. If not numeric, return NaN. + */ + function numericToNumber(val) { + var valFloat = parseFloat(val); + return valFloat == val // eslint-disable-line eqeqeq + && (valFloat !== 0 || !isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '. + ? valFloat : NaN; + } + /** + * Definition of "numeric": see `numericToNumber`. + */ + function isNumeric(val) { + return !isNaN(numericToNumber(val)); + } + /** + * Use random base to prevent users hard code depending on + * this auto generated marker id. + * @return An positive integer. + */ + function getRandomIdBase() { + return Math.round(Math.random() * 9); + } + /** + * Get the greatest common divisor. + * + * @param {number} a one number + * @param {number} b the other number + */ + function getGreatestCommonDividor(a, b) { + if (b === 0) { + return a; + } + return getGreatestCommonDividor(b, a % b); + } + /** + * Get the least common multiple. + * + * @param {number} a one number + * @param {number} b the other number + */ + function getLeastCommonMultiple(a, b) { + if (a == null) { + return b; + } + if (b == null) { + return a; + } + return a * b / getGreatestCommonDividor(a, b); + } + + var ECHARTS_PREFIX = '[ECharts] '; + var storedLogs = {}; + var hasConsole = typeof console !== 'undefined' + // eslint-disable-next-line + && console.warn && console.log; + function outputLog(type, str, onlyOnce) { + if (hasConsole) { + if (onlyOnce) { + if (storedLogs[str]) { + return; + } + storedLogs[str] = true; + } + // eslint-disable-next-line + console[type](ECHARTS_PREFIX + str); + } + } + function log(str, onlyOnce) { + outputLog('log', str, onlyOnce); + } + function warn(str, onlyOnce) { + outputLog('warn', str, onlyOnce); + } + function error(str, onlyOnce) { + outputLog('error', str, onlyOnce); + } + function deprecateLog(str) { + if ("development" !== 'production') { + // Not display duplicate message. + outputLog('warn', 'DEPRECATED: ' + str, true); + } + } + function deprecateReplaceLog(oldOpt, newOpt, scope) { + if ("development" !== 'production') { + deprecateLog((scope ? "[" + scope + "]" : '') + (oldOpt + " is deprecated, use " + newOpt + " instead.")); + } + } + /** + * If in __DEV__ environment, get console printable message for users hint. + * Parameters are separated by ' '. + * @usage + * makePrintable('This is an error on', someVar, someObj); + * + * @param hintInfo anything about the current execution context to hint users. + * @throws Error + */ + function makePrintable() { + var hintInfo = []; + for (var _i = 0; _i < arguments.length; _i++) { + hintInfo[_i] = arguments[_i]; + } + var msg = ''; + if ("development" !== 'production') { + // Fuzzy stringify for print. + // This code only exist in dev environment. + var makePrintableStringIfPossible_1 = function (val) { + return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null; + }; + msg = map(hintInfo, function (arg) { + if (isString(arg)) { + // Print without quotation mark for some statement. + return arg; + } else { + var printableStr = makePrintableStringIfPossible_1(arg); + if (printableStr != null) { + return printableStr; + } else if (typeof JSON !== 'undefined' && JSON.stringify) { + try { + return JSON.stringify(arg, function (n, val) { + var printableStr = makePrintableStringIfPossible_1(val); + return printableStr == null ? val : printableStr; + }); + // In most cases the info object is small, so do not line break. + } catch (err) { + return '?'; + } + } else { + return '?'; + } + } + }).join(' '); + } + return msg; + } + /** + * @throws Error + */ + function throwError(msg) { + throw new Error(msg); + } + + function interpolateNumber$1(p0, p1, percent) { + return (p1 - p0) * percent + p0; + } + /** + * Make the name displayable. But we should + * make sure it is not duplicated with user + * specified name, so use '\0'; + */ + var DUMMY_COMPONENT_NAME_PREFIX = 'series\0'; + var INTERNAL_COMPONENT_ID_PREFIX = '\0_ec_\0'; + /** + * If value is not array, then translate it to array. + * @param {*} value + * @return {Array} [value] or value + */ + function normalizeToArray(value) { + return value instanceof Array ? value : value == null ? [] : [value]; + } + /** + * Sync default option between normal and emphasis like `position` and `show` + * In case some one will write code like + * label: { + * show: false, + * position: 'outside', + * fontSize: 18 + * }, + * emphasis: { + * label: { show: true } + * } + */ + function defaultEmphasis(opt, key, subOpts) { + // Caution: performance sensitive. + if (opt) { + opt[key] = opt[key] || {}; + opt.emphasis = opt.emphasis || {}; + opt.emphasis[key] = opt.emphasis[key] || {}; + // Default emphasis option from normal + for (var i = 0, len = subOpts.length; i < len; i++) { + var subOptName = subOpts[i]; + if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) { + opt.emphasis[key][subOptName] = opt[key][subOptName]; + } + } + } + } + var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; + // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([ + // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter', + // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', + // // FIXME: deprecated, check and remove it. + // 'textStyle' + // ]); + /** + * The method does not ensure performance. + * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] + * This helper method retrieves value from data. + */ + function getDataItemValue(dataItem) { + return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem; + } + /** + * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] + * This helper method determine if dataItem has extra option besides value + */ + function isDataItemOption(dataItem) { + return isObject(dataItem) && !(dataItem instanceof Array); + // // markLine data can be array + // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); + } + /** + * Mapping to existings for merge. + * + * Mode "normalMege": + * The mapping result (merge result) will keep the order of the existing + * component, rather than the order of new option. Because we should ensure + * some specified index reference (like xAxisIndex) keep work. + * And in most cases, "merge option" is used to update partial option but not + * be expected to change the order. + * + * Mode "replaceMege": + * (1) Only the id mapped components will be merged. + * (2) Other existing components (except internal components) will be removed. + * (3) Other new options will be used to create new component. + * (4) The index of the existing components will not be modified. + * That means their might be "hole" after the removal. + * The new components are created first at those available index. + * + * Mode "replaceAll": + * This mode try to support that reproduce an echarts instance from another + * echarts instance (via `getOption`) in some simple cases. + * In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`, + * which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is, + * the "hole" in `newCmptOptions` will also be kept. + * On the contrary, other modes try best to eliminate holes. + * PENDING: This is an experimental mode yet. + * + * @return See the comment of . + */ + function mappingToExists(existings, newCmptOptions, mode) { + var isNormalMergeMode = mode === 'normalMerge'; + var isReplaceMergeMode = mode === 'replaceMerge'; + var isReplaceAllMode = mode === 'replaceAll'; + existings = existings || []; + newCmptOptions = (newCmptOptions || []).slice(); + var existingIdIdxMap = createHashMap(); + // Validate id and name on user input option. + each(newCmptOptions, function (cmptOption, index) { + if (!isObject(cmptOption)) { + newCmptOptions[index] = null; + return; + } + if ("development" !== 'production') { + // There is some legacy case that name is set as `false`. + // But should work normally rather than throw error. + if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) { + warnInvalidateIdOrName(cmptOption.id); + } + if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) { + warnInvalidateIdOrName(cmptOption.name); + } + } + }); + var result = prepareResult(existings, existingIdIdxMap, mode); + if (isNormalMergeMode || isReplaceMergeMode) { + mappingById(result, existings, existingIdIdxMap, newCmptOptions); + } + if (isNormalMergeMode) { + mappingByName(result, newCmptOptions); + } + if (isNormalMergeMode || isReplaceMergeMode) { + mappingByIndex(result, newCmptOptions, isReplaceMergeMode); + } else if (isReplaceAllMode) { + mappingInReplaceAllMode(result, newCmptOptions); + } + makeIdAndName(result); + // The array `result` MUST NOT contain elided items, otherwise the + // forEach will omit those items and result in incorrect result. + return result; + } + function prepareResult(existings, existingIdIdxMap, mode) { + var result = []; + if (mode === 'replaceAll') { + return result; + } + // Do not use native `map` to in case that the array `existings` + // contains elided items, which will be omitted. + for (var index = 0; index < existings.length; index++) { + var existing = existings[index]; + // Because of replaceMerge, `existing` may be null/undefined. + if (existing && existing.id != null) { + existingIdIdxMap.set(existing.id, index); + } + // For non-internal-componnets: + // Mode "normalMerge": all existings kept. + // Mode "replaceMerge": all existing removed unless mapped by id. + // For internal-components: + // go with "replaceMerge" approach in both mode. + result.push({ + existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing, + newOption: null, + keyInfo: null, + brandNew: null + }); + } + return result; + } + function mappingById(result, existings, existingIdIdxMap, newCmptOptions) { + // Mapping by id if specified. + each(newCmptOptions, function (cmptOption, index) { + if (!cmptOption || cmptOption.id == null) { + return; + } + var optionId = makeComparableKey(cmptOption.id); + var existingIdx = existingIdIdxMap.get(optionId); + if (existingIdx != null) { + var resultItem = result[existingIdx]; + assert(!resultItem.newOption, 'Duplicated option on id "' + optionId + '".'); + resultItem.newOption = cmptOption; + // In both mode, if id matched, new option will be merged to + // the existings rather than creating new component model. + resultItem.existing = existings[existingIdx]; + newCmptOptions[index] = null; + } + }); + } + function mappingByName(result, newCmptOptions) { + // Mapping by name if specified. + each(newCmptOptions, function (cmptOption, index) { + if (!cmptOption || cmptOption.name == null) { + return; + } + for (var i = 0; i < result.length; i++) { + var existing = result[i].existing; + if (!result[i].newOption // Consider name: two map to one. + // Can not match when both ids existing but different. + && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) { + result[i].newOption = cmptOption; + newCmptOptions[index] = null; + return; + } + } + }); + } + function mappingByIndex(result, newCmptOptions, brandNew) { + each(newCmptOptions, function (cmptOption) { + if (!cmptOption) { + return; + } + // Find the first place that not mapped by id and not internal component (consider the "hole"). + var resultItem; + var nextIdx = 0; + while ( + // Be `!resultItem` only when `nextIdx >= result.length`. + (resultItem = result[nextIdx] + // (1) Existing models that already have id should be able to mapped to. Because + // after mapping performed, model will always be assigned with an id if user not given. + // After that all models have id. + // (2) If new option has id, it can only set to a hole or append to the last. It should + // not be merged to the existings with different id. Because id should not be overwritten. + // (3) Name can be overwritten, because axis use name as 'show label text'. + ) && (resultItem.newOption || isComponentIdInternal(resultItem.existing) || + // In mode "replaceMerge", here no not-mapped-non-internal-existing. + resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) { + nextIdx++; + } + if (resultItem) { + resultItem.newOption = cmptOption; + resultItem.brandNew = brandNew; + } else { + result.push({ + newOption: cmptOption, + brandNew: brandNew, + existing: null, + keyInfo: null + }); + } + nextIdx++; + }); + } + function mappingInReplaceAllMode(result, newCmptOptions) { + each(newCmptOptions, function (cmptOption) { + // The feature "reproduce" requires "hole" will also reproduced + // in case that component index referring are broken. + result.push({ + newOption: cmptOption, + brandNew: true, + existing: null, + keyInfo: null + }); + }); + } + /** + * Make id and name for mapping result (result of mappingToExists) + * into `keyInfo` field. + */ + function makeIdAndName(mapResult) { + // We use this id to hash component models and view instances + // in echarts. id can be specified by user, or auto generated. + // The id generation rule ensures new view instance are able + // to mapped to old instance when setOption are called in + // no-merge mode. So we generate model id by name and plus + // type in view id. + // name can be duplicated among components, which is convenient + // to specify multi components (like series) by one name. + // Ensure that each id is distinct. + var idMap = createHashMap(); + each(mapResult, function (item) { + var existing = item.existing; + existing && idMap.set(existing.id, item); + }); + each(mapResult, function (item) { + var opt = item.newOption; + // Force ensure id not duplicated. + assert(!opt || opt.id == null || !idMap.get(opt.id) || idMap.get(opt.id) === item, 'id duplicates: ' + (opt && opt.id)); + opt && opt.id != null && idMap.set(opt.id, item); + !item.keyInfo && (item.keyInfo = {}); + }); + // Make name and id. + each(mapResult, function (item, index) { + var existing = item.existing; + var opt = item.newOption; + var keyInfo = item.keyInfo; + if (!isObject(opt)) { + return; + } + // Name can be overwritten. Consider case: axis.name = '20km'. + // But id generated by name will not be changed, which affect + // only in that case: setOption with 'not merge mode' and view + // instance will be recreated, which can be accepted. + keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name + // Avoid that different series has the same name, + // because name may be used like in color pallet. + : DUMMY_COMPONENT_NAME_PREFIX + index; + if (existing) { + keyInfo.id = makeComparableKey(existing.id); + } else if (opt.id != null) { + keyInfo.id = makeComparableKey(opt.id); + } else { + // Consider this situatoin: + // optionA: [{name: 'a'}, {name: 'a'}, {..}] + // optionB [{..}, {name: 'a'}, {name: 'a'}] + // Series with the same name between optionA and optionB + // should be mapped. + var idNum = 0; + do { + keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++; + } while (idMap.get(keyInfo.id)); + } + idMap.set(keyInfo.id, item); + }); + } + function keyExistAndEqual(attr, obj1, obj2) { + var key1 = convertOptionIdName(obj1[attr], null); + var key2 = convertOptionIdName(obj2[attr], null); + // See `MappingExistingItem`. `id` and `name` trade string equals to number. + return key1 != null && key2 != null && key1 === key2; + } + /** + * @return return null if not exist. + */ + function makeComparableKey(val) { + if ("development" !== 'production') { + if (val == null) { + throw new Error(); + } + } + return convertOptionIdName(val, ''); + } + function convertOptionIdName(idOrName, defaultValue) { + if (idOrName == null) { + return defaultValue; + } + return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue; + } + function warnInvalidateIdOrName(idOrName) { + if ("development" !== 'production') { + warn('`' + idOrName + '` is invalid id or name. Must be a string or number.'); + } + } + function isValidIdOrName(idOrName) { + return isStringSafe(idOrName) || isNumeric(idOrName); + } + function isNameSpecified(componentModel) { + var name = componentModel.name; + // Is specified when `indexOf` get -1 or > 0. + return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX)); + } + /** + * @public + * @param {Object} cmptOption + * @return {boolean} + */ + function isComponentIdInternal(cmptOption) { + return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0; + } + function makeInternalComponentId(idSuffix) { + return INTERNAL_COMPONENT_ID_PREFIX + idSuffix; + } + function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) { + // Set mainType and complete subType. + each(mappingResult, function (item) { + var newOption = item.newOption; + if (isObject(newOption)) { + item.keyInfo.mainType = mainType; + item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor); + } + }); + } + function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) { + var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType + // Use determineSubType only when there is no existComponent. + : componentModelCtor.determineSubType(mainType, newCmptOption); + // tooltip, markline, markpoint may always has no subType + return subType; + } + /** + * A helper for removing duplicate items between batchA and batchB, + * and in themselves, and categorize by series. + * + * @param batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] + * @param batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] + * @return result: [resultBatchA, resultBatchB] + */ + function compressBatches(batchA, batchB) { + var mapA = {}; + var mapB = {}; + makeMap(batchA || [], mapA); + makeMap(batchB || [], mapB, mapA); + return [mapToArray(mapA), mapToArray(mapB)]; + function makeMap(sourceBatch, map, otherMap) { + for (var i = 0, len = sourceBatch.length; i < len; i++) { + var seriesId = convertOptionIdName(sourceBatch[i].seriesId, null); + if (seriesId == null) { + return; + } + var dataIndices = normalizeToArray(sourceBatch[i].dataIndex); + var otherDataIndices = otherMap && otherMap[seriesId]; + for (var j = 0, lenj = dataIndices.length; j < lenj; j++) { + var dataIndex = dataIndices[j]; + if (otherDataIndices && otherDataIndices[dataIndex]) { + otherDataIndices[dataIndex] = null; + } else { + (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1; + } + } + } + } + function mapToArray(map, isData) { + var result = []; + for (var i in map) { + if (map.hasOwnProperty(i) && map[i] != null) { + if (isData) { + result.push(+i); + } else { + var dataIndices = mapToArray(map[i], true); + dataIndices.length && result.push({ + seriesId: i, + dataIndex: dataIndices + }); + } + } + } + return result; + } + } + /** + * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name + * each of which can be Array or primary type. + * @return dataIndex If not found, return undefined/null. + */ + function queryDataIndex(data, payload) { + if (payload.dataIndexInside != null) { + return payload.dataIndexInside; + } else if (payload.dataIndex != null) { + return isArray(payload.dataIndex) ? map(payload.dataIndex, function (value) { + return data.indexOfRawIndex(value); + }) : data.indexOfRawIndex(payload.dataIndex); + } else if (payload.name != null) { + return isArray(payload.name) ? map(payload.name, function (value) { + return data.indexOfName(value); + }) : data.indexOfName(payload.name); + } + } + /** + * Enable property storage to any host object. + * Notice: Serialization is not supported. + * + * For example: + * let inner = zrUitl.makeInner(); + * + * function some1(hostObj) { + * inner(hostObj).someProperty = 1212; + * ... + * } + * function some2() { + * let fields = inner(this); + * fields.someProperty1 = 1212; + * fields.someProperty2 = 'xx'; + * ... + * } + * + * @return {Function} + */ + function makeInner() { + var key = '__ec_inner_' + innerUniqueIndex++; + return function (hostObj) { + return hostObj[key] || (hostObj[key] = {}); + }; + } + var innerUniqueIndex = getRandomIdBase(); + /** + * The same behavior as `component.getReferringComponents`. + */ + function parseFinder(ecModel, finderInput, opt) { + var _a = preParseFinder(finderInput, opt), + mainTypeSpecified = _a.mainTypeSpecified, + queryOptionMap = _a.queryOptionMap, + others = _a.others; + var result = others; + var defaultMainType = opt ? opt.defaultMainType : null; + if (!mainTypeSpecified && defaultMainType) { + queryOptionMap.set(defaultMainType, {}); + } + queryOptionMap.each(function (queryOption, mainType) { + var queryResult = queryReferringComponents(ecModel, mainType, queryOption, { + useDefault: defaultMainType === mainType, + enableAll: opt && opt.enableAll != null ? opt.enableAll : true, + enableNone: opt && opt.enableNone != null ? opt.enableNone : true + }); + result[mainType + 'Models'] = queryResult.models; + result[mainType + 'Model'] = queryResult.models[0]; + }); + return result; + } + function preParseFinder(finderInput, opt) { + var finder; + if (isString(finderInput)) { + var obj = {}; + obj[finderInput + 'Index'] = 0; + finder = obj; + } else { + finder = finderInput; + } + var queryOptionMap = createHashMap(); + var others = {}; + var mainTypeSpecified = false; + each(finder, function (value, key) { + // Exclude 'dataIndex' and other illgal keys. + if (key === 'dataIndex' || key === 'dataIndexInside') { + others[key] = value; + return; + } + var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || []; + var mainType = parsedKey[1]; + var queryType = (parsedKey[2] || '').toLowerCase(); + if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) { + return; + } + mainTypeSpecified = mainTypeSpecified || !!mainType; + var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {}); + queryOption[queryType] = value; + }); + return { + mainTypeSpecified: mainTypeSpecified, + queryOptionMap: queryOptionMap, + others: others + }; + } + var SINGLE_REFERRING = { + useDefault: true, + enableAll: false, + enableNone: false + }; + var MULTIPLE_REFERRING = { + useDefault: false, + enableAll: true, + enableNone: true + }; + function queryReferringComponents(ecModel, mainType, userOption, opt) { + opt = opt || SINGLE_REFERRING; + var indexOption = userOption.index; + var idOption = userOption.id; + var nameOption = userOption.name; + var result = { + models: null, + specified: indexOption != null || idOption != null || nameOption != null + }; + if (!result.specified) { + // Use the first as default if `useDefault`. + var firstCmpt = void 0; + result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : []; + return result; + } + if (indexOption === 'none' || indexOption === false) { + assert(opt.enableNone, '`"none"` or `false` is not a valid value on index option.'); + result.models = []; + return result; + } + // `queryComponents` will return all components if + // both all of index/id/name are null/undefined. + if (indexOption === 'all') { + assert(opt.enableAll, '`"all"` is not a valid value on index option.'); + indexOption = idOption = nameOption = null; + } + result.models = ecModel.queryComponents({ + mainType: mainType, + index: indexOption, + id: idOption, + name: nameOption + }); + return result; + } + function setAttribute(dom, key, value) { + dom.setAttribute ? dom.setAttribute(key, value) : dom[key] = value; + } + function getAttribute(dom, key) { + return dom.getAttribute ? dom.getAttribute(key) : dom[key]; + } + function getTooltipRenderMode(renderModeOption) { + if (renderModeOption === 'auto') { + // Using html when `document` exists, use richText otherwise + return env.domSupported ? 'html' : 'richText'; + } else { + return renderModeOption || 'html'; + } + } + /** + * Group a list by key. + */ + function groupData(array, getKey // return key + ) { + var buckets = createHashMap(); + var keys = []; + each(array, function (item) { + var key = getKey(item); + (buckets.get(key) || (keys.push(key), buckets.set(key, []))).push(item); + }); + return { + keys: keys, + buckets: buckets + }; + } + /** + * Interpolate raw values of a series with percent + * + * @param data data + * @param labelModel label model of the text element + * @param sourceValue start value. May be null/undefined when init. + * @param targetValue end value + * @param percent 0~1 percentage; 0 uses start value while 1 uses end value + * @return interpolated values + * If `sourceValue` and `targetValue` are `number`, return `number`. + * If `sourceValue` and `targetValue` are `string`, return `string`. + * If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`. + * Other cases do not supported. + */ + function interpolateRawValues(data, precision, sourceValue, targetValue, percent) { + var isAutoPrecision = precision == null || precision === 'auto'; + if (targetValue == null) { + return targetValue; + } + if (isNumber(targetValue)) { + var value = interpolateNumber$1(sourceValue || 0, targetValue, percent); + return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision); + } else if (isString(targetValue)) { + return percent < 1 ? sourceValue : targetValue; + } else { + var interpolated = []; + var leftArr = sourceValue; + var rightArr = targetValue; + var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length); + for (var i = 0; i < length_1; ++i) { + var info = data.getDimensionInfo(i); + // Don't interpolate ordinal dims + if (info && info.type === 'ordinal') { + // In init, there is no `sourceValue`, but should better not to get undefined result. + interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i]; + } else { + var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0; + var rightVal = rightArr[i]; + var value = interpolateNumber$1(leftVal, rightVal, percent); + interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision); + } + } + return interpolated; + } + } + + var TYPE_DELIMITER = '.'; + var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; + var IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___'; + /** + * Notice, parseClassType('') should returns {main: '', sub: ''} + * @public + */ + function parseClassType(componentType) { + var ret = { + main: '', + sub: '' + }; + if (componentType) { + var typeArr = componentType.split(TYPE_DELIMITER); + ret.main = typeArr[0] || ''; + ret.sub = typeArr[1] || ''; + } + return ret; + } + /** + * @public + */ + function checkClassType(componentType) { + assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType "' + componentType + '" illegal'); + } + function isExtendedClass(clz) { + return !!(clz && clz[IS_EXTENDED_CLASS]); + } + /** + * Implements `ExtendableConstructor` for `rootClz`. + * + * @usage + * ```ts + * class Xxx {} + * type XxxConstructor = typeof Xxx & ExtendableConstructor + * enableClassExtend(Xxx as XxxConstructor); + * ``` + */ + function enableClassExtend(rootClz, mandatoryMethods) { + rootClz.$constructor = rootClz; // FIXME: not necessary? + rootClz.extend = function (proto) { + if ("development" !== 'production') { + each(mandatoryMethods, function (method) { + if (!proto[method]) { + console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.'); + } + }); + } + var superClass = this; + var ExtendedClass; + if (isESClass(superClass)) { + ExtendedClass = /** @class */function (_super) { + __extends(class_1, _super); + function class_1() { + return _super.apply(this, arguments) || this; + } + return class_1; + }(superClass); + } else { + // For backward compat, we both support ts class inheritance and this + // "extend" approach. + // The constructor should keep the same behavior as ts class inheritance: + // If this constructor/$constructor is not declared, auto invoke the super + // constructor. + // If this constructor/$constructor is declared, it is responsible for + // calling the super constructor. + ExtendedClass = function () { + (proto.$constructor || superClass).apply(this, arguments); + }; + inherits(ExtendedClass, this); + } + extend(ExtendedClass.prototype, proto); + ExtendedClass[IS_EXTENDED_CLASS] = true; + ExtendedClass.extend = this.extend; + ExtendedClass.superCall = superCall; + ExtendedClass.superApply = superApply; + ExtendedClass.superClass = superClass; + return ExtendedClass; + }; + } + function isESClass(fn) { + return isFunction(fn) && /^class\s/.test(Function.prototype.toString.call(fn)); + } + /** + * A work around to both support ts extend and this extend mechanism. + * on sub-class. + * @usage + * ```ts + * class Component { ... } + * classUtil.enableClassExtend(Component); + * classUtil.enableClassManagement(Component, {registerWhenExtend: true}); + * + * class Series extends Component { ... } + * // Without calling `markExtend`, `registerWhenExtend` will not work. + * Component.markExtend(Series); + * ``` + */ + function mountExtend(SubClz, SupperClz) { + SubClz.extend = SupperClz.extend; + } + // A random offset. + var classBase = Math.round(Math.random() * 10); + /** + * Implements `CheckableConstructor` for `target`. + * Can not use instanceof, consider different scope by + * cross domain or es module import in ec extensions. + * Mount a method "isInstance()" to Clz. + * + * @usage + * ```ts + * class Xxx {} + * type XxxConstructor = typeof Xxx & CheckableConstructor; + * enableClassCheck(Xxx as XxxConstructor) + * ``` + */ + function enableClassCheck(target) { + var classAttr = ['__\0is_clz', classBase++].join('_'); + target.prototype[classAttr] = true; + if ("development" !== 'production') { + assert(!target.isInstance, 'The method "is" can not be defined.'); + } + target.isInstance = function (obj) { + return !!(obj && obj[classAttr]); + }; + } + // superCall should have class info, which can not be fetched from 'this'. + // Consider this case: + // class A has method f, + // class B inherits class A, overrides method f, f call superApply('f'), + // class C inherits class B, does not override method f, + // then when method of class C is called, dead loop occurred. + function superCall(context, methodName) { + var args = []; + for (var _i = 2; _i < arguments.length; _i++) { + args[_i - 2] = arguments[_i]; + } + return this.superClass.prototype[methodName].apply(context, args); + } + function superApply(context, methodName, args) { + return this.superClass.prototype[methodName].apply(context, args); + } + /** + * Implements `ClassManager` for `target` + * + * @usage + * ```ts + * class Xxx {} + * type XxxConstructor = typeof Xxx & ClassManager + * enableClassManagement(Xxx as XxxConstructor); + * ``` + */ + function enableClassManagement(target) { + /** + * Component model classes + * key: componentType, + * value: + * componentClass, when componentType is 'a' + * or Object., when componentType is 'a.b' + */ + var storage = {}; + target.registerClass = function (clz) { + // `type` should not be a "instance member". + // If using TS class, should better declared as `static type = 'series.pie'`. + // otherwise users have to mount `type` on prototype manually. + // For backward compat and enable instance visit type via `this.type`, + // we still support fetch `type` from prototype. + var componentFullType = clz.type || clz.prototype.type; + if (componentFullType) { + checkClassType(componentFullType); + // If only static type declared, we assign it to prototype mandatorily. + clz.prototype.type = componentFullType; + var componentTypeInfo = parseClassType(componentFullType); + if (!componentTypeInfo.sub) { + if ("development" !== 'production') { + if (storage[componentTypeInfo.main]) { + console.warn(componentTypeInfo.main + ' exists.'); + } + } + storage[componentTypeInfo.main] = clz; + } else if (componentTypeInfo.sub !== IS_CONTAINER) { + var container = makeContainer(componentTypeInfo); + container[componentTypeInfo.sub] = clz; + } + } + return clz; + }; + target.getClass = function (mainType, subType, throwWhenNotFound) { + var clz = storage[mainType]; + if (clz && clz[IS_CONTAINER]) { + clz = subType ? clz[subType] : null; + } + if (throwWhenNotFound && !clz) { + throw new Error(!subType ? mainType + '.' + 'type should be specified.' : 'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.'); + } + return clz; + }; + target.getClassesByMainType = function (componentType) { + var componentTypeInfo = parseClassType(componentType); + var result = []; + var obj = storage[componentTypeInfo.main]; + if (obj && obj[IS_CONTAINER]) { + each(obj, function (o, type) { + type !== IS_CONTAINER && result.push(o); + }); + } else { + result.push(obj); + } + return result; + }; + target.hasClass = function (componentType) { + // Just consider componentType.main. + var componentTypeInfo = parseClassType(componentType); + return !!storage[componentTypeInfo.main]; + }; + /** + * @return Like ['aa', 'bb'], but can not be ['aa.xx'] + */ + target.getAllClassMainTypes = function () { + var types = []; + each(storage, function (obj, type) { + types.push(type); + }); + return types; + }; + /** + * If a main type is container and has sub types + */ + target.hasSubTypes = function (componentType) { + var componentTypeInfo = parseClassType(componentType); + var obj = storage[componentTypeInfo.main]; + return obj && obj[IS_CONTAINER]; + }; + function makeContainer(componentTypeInfo) { + var container = storage[componentTypeInfo.main]; + if (!container || !container[IS_CONTAINER]) { + container = storage[componentTypeInfo.main] = {}; + container[IS_CONTAINER] = true; + } + return container; + } + } + // /** + // * @param {string|Array.} properties + // */ + // export function setReadOnly(obj, properties) { + // FIXME It seems broken in IE8 simulation of IE11 + // if (!zrUtil.isArray(properties)) { + // properties = properties != null ? [properties] : []; + // } + // zrUtil.each(properties, function (prop) { + // let value = obj[prop]; + // Object.defineProperty + // && Object.defineProperty(obj, prop, { + // value: value, writable: false + // }); + // zrUtil.isArray(obj[prop]) + // && Object.freeze + // && Object.freeze(obj[prop]); + // }); + // } + + function makeStyleMapper(properties, ignoreParent) { + // Normalize + for (var i = 0; i < properties.length; i++) { + if (!properties[i][1]) { + properties[i][1] = properties[i][0]; + } + } + ignoreParent = ignoreParent || false; + return function (model, excludes, includes) { + var style = {}; + for (var i = 0; i < properties.length; i++) { + var propName = properties[i][1]; + if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) { + continue; + } + var val = model.getShallow(propName, ignoreParent); + if (val != null) { + style[properties[i][0]] = val; + } + } + // TODO Text or image? + return style; + }; + } + + var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] + // Option decal is in `DecalObject` but style.decal is in `PatternObject`. + // So do not transfer decal directly. + ]; + + var getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP); + var AreaStyleMixin = /** @class */function () { + function AreaStyleMixin() {} + AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) { + return getAreaStyle(this, excludes, includes); + }; + return AreaStyleMixin; + }(); + + var globalImageCache = new LRU(50); + function findExistImage(newImageOrSrc) { + if (typeof newImageOrSrc === 'string') { + var cachedImgObj = globalImageCache.get(newImageOrSrc); + return cachedImgObj && cachedImgObj.image; + } + else { + return newImageOrSrc; + } + } + function createOrUpdateImage(newImageOrSrc, image, hostEl, onload, cbPayload) { + if (!newImageOrSrc) { + return image; + } + else if (typeof newImageOrSrc === 'string') { + if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) { + return image; + } + var cachedImgObj = globalImageCache.get(newImageOrSrc); + var pendingWrap = { hostEl: hostEl, cb: onload, cbPayload: cbPayload }; + if (cachedImgObj) { + image = cachedImgObj.image; + !isImageReady(image) && cachedImgObj.pending.push(pendingWrap); + } + else { + image = platformApi.loadImage(newImageOrSrc, imageOnLoad, imageOnLoad); + image.__zrImageSrc = newImageOrSrc; + globalImageCache.put(newImageOrSrc, image.__cachedImgObj = { + image: image, + pending: [pendingWrap] + }); + } + return image; + } + else { + return newImageOrSrc; + } + } + function imageOnLoad() { + var cachedImgObj = this.__cachedImgObj; + this.onload = this.onerror = this.__cachedImgObj = null; + for (var i = 0; i < cachedImgObj.pending.length; i++) { + var pendingWrap = cachedImgObj.pending[i]; + var cb = pendingWrap.cb; + cb && cb(this, pendingWrap.cbPayload); + pendingWrap.hostEl.dirty(); + } + cachedImgObj.pending.length = 0; + } + function isImageReady(image) { + return image && image.width && image.height; + } + + var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g; + function truncateText(text, containerWidth, font, ellipsis, options) { + if (!containerWidth) { + return ''; + } + var textLines = (text + '').split('\n'); + options = prepareTruncateOptions(containerWidth, font, ellipsis, options); + for (var i = 0, len = textLines.length; i < len; i++) { + textLines[i] = truncateSingleLine(textLines[i], options); + } + return textLines.join('\n'); + } + function prepareTruncateOptions(containerWidth, font, ellipsis, options) { + options = options || {}; + var preparedOpts = extend({}, options); + preparedOpts.font = font; + ellipsis = retrieve2(ellipsis, '...'); + preparedOpts.maxIterations = retrieve2(options.maxIterations, 2); + var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0); + preparedOpts.cnCharWidth = getWidth('国', font); + var ascCharWidth = preparedOpts.ascCharWidth = getWidth('a', font); + preparedOpts.placeholder = retrieve2(options.placeholder, ''); + var contentWidth = containerWidth = Math.max(0, containerWidth - 1); + for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) { + contentWidth -= ascCharWidth; + } + var ellipsisWidth = getWidth(ellipsis, font); + if (ellipsisWidth > contentWidth) { + ellipsis = ''; + ellipsisWidth = 0; + } + contentWidth = containerWidth - ellipsisWidth; + preparedOpts.ellipsis = ellipsis; + preparedOpts.ellipsisWidth = ellipsisWidth; + preparedOpts.contentWidth = contentWidth; + preparedOpts.containerWidth = containerWidth; + return preparedOpts; + } + function truncateSingleLine(textLine, options) { + var containerWidth = options.containerWidth; + var font = options.font; + var contentWidth = options.contentWidth; + if (!containerWidth) { + return ''; + } + var lineWidth = getWidth(textLine, font); + if (lineWidth <= containerWidth) { + return textLine; + } + for (var j = 0;; j++) { + if (lineWidth <= contentWidth || j >= options.maxIterations) { + textLine += options.ellipsis; + break; + } + var subLength = j === 0 + ? estimateLength(textLine, contentWidth, options.ascCharWidth, options.cnCharWidth) + : lineWidth > 0 + ? Math.floor(textLine.length * contentWidth / lineWidth) + : 0; + textLine = textLine.substr(0, subLength); + lineWidth = getWidth(textLine, font); + } + if (textLine === '') { + textLine = options.placeholder; + } + return textLine; + } + function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) { + var width = 0; + var i = 0; + for (var len = text.length; i < len && width < contentWidth; i++) { + var charCode = text.charCodeAt(i); + width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth; + } + return i; + } + function parsePlainText(text, style) { + text != null && (text += ''); + var overflow = style.overflow; + var padding = style.padding; + var font = style.font; + var truncate = overflow === 'truncate'; + var calculatedLineHeight = getLineHeight(font); + var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight); + var bgColorDrawn = !!(style.backgroundColor); + var truncateLineOverflow = style.lineOverflow === 'truncate'; + var width = style.width; + var lines; + if (width != null && (overflow === 'break' || overflow === 'breakAll')) { + lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : []; + } + else { + lines = text ? text.split('\n') : []; + } + var contentHeight = lines.length * lineHeight; + var height = retrieve2(style.height, contentHeight); + if (contentHeight > height && truncateLineOverflow) { + var lineCount = Math.floor(height / lineHeight); + lines = lines.slice(0, lineCount); + } + if (text && truncate && width != null) { + var options = prepareTruncateOptions(width, font, style.ellipsis, { + minChar: style.truncateMinChar, + placeholder: style.placeholder + }); + for (var i = 0; i < lines.length; i++) { + lines[i] = truncateSingleLine(lines[i], options); + } + } + var outerHeight = height; + var contentWidth = 0; + for (var i = 0; i < lines.length; i++) { + contentWidth = Math.max(getWidth(lines[i], font), contentWidth); + } + if (width == null) { + width = contentWidth; + } + var outerWidth = contentWidth; + if (padding) { + outerHeight += padding[0] + padding[2]; + outerWidth += padding[1] + padding[3]; + width += padding[1] + padding[3]; + } + if (bgColorDrawn) { + outerWidth = width; + } + return { + lines: lines, + height: height, + outerWidth: outerWidth, + outerHeight: outerHeight, + lineHeight: lineHeight, + calculatedLineHeight: calculatedLineHeight, + contentWidth: contentWidth, + contentHeight: contentHeight, + width: width + }; + } + var RichTextToken = (function () { + function RichTextToken() { + } + return RichTextToken; + }()); + var RichTextLine = (function () { + function RichTextLine(tokens) { + this.tokens = []; + if (tokens) { + this.tokens = tokens; + } + } + return RichTextLine; + }()); + var RichTextContentBlock = (function () { + function RichTextContentBlock() { + this.width = 0; + this.height = 0; + this.contentWidth = 0; + this.contentHeight = 0; + this.outerWidth = 0; + this.outerHeight = 0; + this.lines = []; + } + return RichTextContentBlock; + }()); + function parseRichText(text, style) { + var contentBlock = new RichTextContentBlock(); + text != null && (text += ''); + if (!text) { + return contentBlock; + } + var topWidth = style.width; + var topHeight = style.height; + var overflow = style.overflow; + var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null + ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' } + : null; + var lastIndex = STYLE_REG.lastIndex = 0; + var result; + while ((result = STYLE_REG.exec(text)) != null) { + var matchedIndex = result.index; + if (matchedIndex > lastIndex) { + pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo); + } + pushTokens(contentBlock, result[2], style, wrapInfo, result[1]); + lastIndex = STYLE_REG.lastIndex; + } + if (lastIndex < text.length) { + pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo); + } + var pendingList = []; + var calculatedHeight = 0; + var calculatedWidth = 0; + var stlPadding = style.padding; + var truncate = overflow === 'truncate'; + var truncateLine = style.lineOverflow === 'truncate'; + function finishLine(line, lineWidth, lineHeight) { + line.width = lineWidth; + line.lineHeight = lineHeight; + calculatedHeight += lineHeight; + calculatedWidth = Math.max(calculatedWidth, lineWidth); + } + outer: for (var i = 0; i < contentBlock.lines.length; i++) { + var line = contentBlock.lines[i]; + var lineHeight = 0; + var lineWidth = 0; + for (var j = 0; j < line.tokens.length; j++) { + var token = line.tokens[j]; + var tokenStyle = token.styleName && style.rich[token.styleName] || {}; + var textPadding = token.textPadding = tokenStyle.padding; + var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0; + var font = token.font = tokenStyle.font || style.font; + token.contentHeight = getLineHeight(font); + var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight); + token.innerHeight = tokenHeight; + textPadding && (tokenHeight += textPadding[0] + textPadding[2]); + token.height = tokenHeight; + token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight); + token.align = tokenStyle && tokenStyle.align || style.align; + token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle'; + if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) { + if (j > 0) { + line.tokens = line.tokens.slice(0, j); + finishLine(line, lineWidth, lineHeight); + contentBlock.lines = contentBlock.lines.slice(0, i + 1); + } + else { + contentBlock.lines = contentBlock.lines.slice(0, i); + } + break outer; + } + var styleTokenWidth = tokenStyle.width; + var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto'; + if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') { + token.percentWidth = styleTokenWidth; + pendingList.push(token); + token.contentWidth = getWidth(token.text, font); + } + else { + if (tokenWidthNotSpecified) { + var textBackgroundColor = tokenStyle.backgroundColor; + var bgImg = textBackgroundColor && textBackgroundColor.image; + if (bgImg) { + bgImg = findExistImage(bgImg); + if (isImageReady(bgImg)) { + token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height); + } + } + } + var remainTruncWidth = truncate && topWidth != null + ? topWidth - lineWidth : null; + if (remainTruncWidth != null && remainTruncWidth < token.width) { + if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) { + token.text = ''; + token.width = token.contentWidth = 0; + } + else { + token.text = truncateText(token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar }); + token.width = token.contentWidth = getWidth(token.text, font); + } + } + else { + token.contentWidth = getWidth(token.text, font); + } + } + token.width += paddingH; + lineWidth += token.width; + tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight)); + } + finishLine(line, lineWidth, lineHeight); + } + contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth); + contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight); + contentBlock.contentHeight = calculatedHeight; + contentBlock.contentWidth = calculatedWidth; + if (stlPadding) { + contentBlock.outerWidth += stlPadding[1] + stlPadding[3]; + contentBlock.outerHeight += stlPadding[0] + stlPadding[2]; + } + for (var i = 0; i < pendingList.length; i++) { + var token = pendingList[i]; + var percentWidth = token.percentWidth; + token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width; + } + return contentBlock; + } + function pushTokens(block, str, style, wrapInfo, styleName) { + var isEmptyStr = str === ''; + var tokenStyle = styleName && style.rich[styleName] || {}; + var lines = block.lines; + var font = tokenStyle.font || style.font; + var newLine = false; + var strLines; + var linesWidths; + if (wrapInfo) { + var tokenPadding = tokenStyle.padding; + var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0; + if (tokenStyle.width != null && tokenStyle.width !== 'auto') { + var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH; + if (lines.length > 0) { + if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) { + strLines = str.split('\n'); + newLine = true; + } + } + wrapInfo.accumWidth = outerWidth_1; + } + else { + var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth); + wrapInfo.accumWidth = res.accumWidth + tokenPaddingH; + linesWidths = res.linesWidths; + strLines = res.lines; + } + } + else { + strLines = str.split('\n'); + } + for (var i = 0; i < strLines.length; i++) { + var text = strLines[i]; + var token = new RichTextToken(); + token.styleName = styleName; + token.text = text; + token.isLineHolder = !text && !isEmptyStr; + if (typeof tokenStyle.width === 'number') { + token.width = tokenStyle.width; + } + else { + token.width = linesWidths + ? linesWidths[i] + : getWidth(text, font); + } + if (!i && !newLine) { + var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens; + var tokensLen = tokens.length; + (tokensLen === 1 && tokens[0].isLineHolder) + ? (tokens[0] = token) + : ((text || !tokensLen || isEmptyStr) && tokens.push(token)); + } + else { + lines.push(new RichTextLine([token])); + } + } + } + function isAlphabeticLetter(ch) { + var code = ch.charCodeAt(0); + return code >= 0x20 && code <= 0x24F + || code >= 0x370 && code <= 0x10FF + || code >= 0x1200 && code <= 0x13FF + || code >= 0x1E00 && code <= 0x206F; + } + var breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) { + obj[ch] = true; + return obj; + }, {}); + function isWordBreakChar(ch) { + if (isAlphabeticLetter(ch)) { + if (breakCharMap[ch]) { + return true; + } + return false; + } + return true; + } + function wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) { + var lines = []; + var linesWidths = []; + var line = ''; + var currentWord = ''; + var currentWordWidth = 0; + var accumWidth = 0; + for (var i = 0; i < text.length; i++) { + var ch = text.charAt(i); + if (ch === '\n') { + if (currentWord) { + line += currentWord; + accumWidth += currentWordWidth; + } + lines.push(line); + linesWidths.push(accumWidth); + line = ''; + currentWord = ''; + currentWordWidth = 0; + accumWidth = 0; + continue; + } + var chWidth = getWidth(ch, font); + var inWord = isBreakAll ? false : !isWordBreakChar(ch); + if (!lines.length + ? lastAccumWidth + accumWidth + chWidth > lineWidth + : accumWidth + chWidth > lineWidth) { + if (!accumWidth) { + if (inWord) { + lines.push(currentWord); + linesWidths.push(currentWordWidth); + currentWord = ch; + currentWordWidth = chWidth; + } + else { + lines.push(ch); + linesWidths.push(chWidth); + } + } + else if (line || currentWord) { + if (inWord) { + if (!line) { + line = currentWord; + currentWord = ''; + currentWordWidth = 0; + accumWidth = currentWordWidth; + } + lines.push(line); + linesWidths.push(accumWidth - currentWordWidth); + currentWord += ch; + currentWordWidth += chWidth; + line = ''; + accumWidth = currentWordWidth; + } + else { + if (currentWord) { + line += currentWord; + currentWord = ''; + currentWordWidth = 0; + } + lines.push(line); + linesWidths.push(accumWidth); + line = ch; + accumWidth = chWidth; + } + } + continue; + } + accumWidth += chWidth; + if (inWord) { + currentWord += ch; + currentWordWidth += chWidth; + } + else { + if (currentWord) { + line += currentWord; + currentWord = ''; + currentWordWidth = 0; + } + line += ch; + } + } + if (!lines.length && !line) { + line = text; + currentWord = ''; + currentWordWidth = 0; + } + if (currentWord) { + line += currentWord; + } + if (line) { + lines.push(line); + linesWidths.push(accumWidth); + } + if (lines.length === 1) { + accumWidth += lastAccumWidth; + } + return { + accumWidth: accumWidth, + lines: lines, + linesWidths: linesWidths + }; + } + + var STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10)); + var DEFAULT_COMMON_STYLE = { + shadowBlur: 0, + shadowOffsetX: 0, + shadowOffsetY: 0, + shadowColor: '#000', + opacity: 1, + blend: 'source-over' + }; + var DEFAULT_COMMON_ANIMATION_PROPS = { + style: { + shadowBlur: true, + shadowOffsetX: true, + shadowOffsetY: true, + shadowColor: true, + opacity: true + } + }; + DEFAULT_COMMON_STYLE[STYLE_MAGIC_KEY] = true; + var PRIMARY_STATES_KEYS$1 = ['z', 'z2', 'invisible']; + var PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible']; + var Displayable = (function (_super) { + __extends(Displayable, _super); + function Displayable(props) { + return _super.call(this, props) || this; + } + Displayable.prototype._init = function (props) { + var keysArr = keys(props); + for (var i = 0; i < keysArr.length; i++) { + var key = keysArr[i]; + if (key === 'style') { + this.useStyle(props[key]); + } + else { + _super.prototype.attrKV.call(this, key, props[key]); + } + } + if (!this.style) { + this.useStyle({}); + } + }; + Displayable.prototype.beforeBrush = function () { }; + Displayable.prototype.afterBrush = function () { }; + Displayable.prototype.innerBeforeBrush = function () { }; + Displayable.prototype.innerAfterBrush = function () { }; + Displayable.prototype.shouldBePainted = function (viewWidth, viewHeight, considerClipPath, considerAncestors) { + var m = this.transform; + if (this.ignore + || this.invisible + || this.style.opacity === 0 + || (this.culling + && isDisplayableCulled(this, viewWidth, viewHeight)) + || (m && !m[0] && !m[3])) { + return false; + } + if (considerClipPath && this.__clipPaths) { + for (var i = 0; i < this.__clipPaths.length; ++i) { + if (this.__clipPaths[i].isZeroArea()) { + return false; + } + } + } + if (considerAncestors && this.parent) { + var parent_1 = this.parent; + while (parent_1) { + if (parent_1.ignore) { + return false; + } + parent_1 = parent_1.parent; + } + } + return true; + }; + Displayable.prototype.contain = function (x, y) { + return this.rectContain(x, y); + }; + Displayable.prototype.traverse = function (cb, context) { + cb.call(context, this); + }; + Displayable.prototype.rectContain = function (x, y) { + var coord = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + return rect.contain(coord[0], coord[1]); + }; + Displayable.prototype.getPaintRect = function () { + var rect = this._paintRect; + if (!this._paintRect || this.__dirty) { + var transform = this.transform; + var elRect = this.getBoundingRect(); + var style = this.style; + var shadowSize = style.shadowBlur || 0; + var shadowOffsetX = style.shadowOffsetX || 0; + var shadowOffsetY = style.shadowOffsetY || 0; + rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0)); + if (transform) { + BoundingRect.applyTransform(rect, elRect, transform); + } + else { + rect.copy(elRect); + } + if (shadowSize || shadowOffsetX || shadowOffsetY) { + rect.width += shadowSize * 2 + Math.abs(shadowOffsetX); + rect.height += shadowSize * 2 + Math.abs(shadowOffsetY); + rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize); + rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize); + } + var tolerance = this.dirtyRectTolerance; + if (!rect.isZero()) { + rect.x = Math.floor(rect.x - tolerance); + rect.y = Math.floor(rect.y - tolerance); + rect.width = Math.ceil(rect.width + 1 + tolerance * 2); + rect.height = Math.ceil(rect.height + 1 + tolerance * 2); + } + } + return rect; + }; + Displayable.prototype.setPrevPaintRect = function (paintRect) { + if (paintRect) { + this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0); + this._prevPaintRect.copy(paintRect); + } + else { + this._prevPaintRect = null; + } + }; + Displayable.prototype.getPrevPaintRect = function () { + return this._prevPaintRect; + }; + Displayable.prototype.animateStyle = function (loop) { + return this.animate('style', loop); + }; + Displayable.prototype.updateDuringAnimation = function (targetKey) { + if (targetKey === 'style') { + this.dirtyStyle(); + } + else { + this.markRedraw(); + } + }; + Displayable.prototype.attrKV = function (key, value) { + if (key !== 'style') { + _super.prototype.attrKV.call(this, key, value); + } + else { + if (!this.style) { + this.useStyle(value); + } + else { + this.setStyle(value); + } + } + }; + Displayable.prototype.setStyle = function (keyOrObj, value) { + if (typeof keyOrObj === 'string') { + this.style[keyOrObj] = value; + } + else { + extend(this.style, keyOrObj); + } + this.dirtyStyle(); + return this; + }; + Displayable.prototype.dirtyStyle = function (notRedraw) { + if (!notRedraw) { + this.markRedraw(); + } + this.__dirty |= STYLE_CHANGED_BIT; + if (this._rect) { + this._rect = null; + } + }; + Displayable.prototype.dirty = function () { + this.dirtyStyle(); + }; + Displayable.prototype.styleChanged = function () { + return !!(this.__dirty & STYLE_CHANGED_BIT); + }; + Displayable.prototype.styleUpdated = function () { + this.__dirty &= ~STYLE_CHANGED_BIT; + }; + Displayable.prototype.createStyle = function (obj) { + return createObject(DEFAULT_COMMON_STYLE, obj); + }; + Displayable.prototype.useStyle = function (obj) { + if (!obj[STYLE_MAGIC_KEY]) { + obj = this.createStyle(obj); + } + if (this.__inHover) { + this.__hoverStyle = obj; + } + else { + this.style = obj; + } + this.dirtyStyle(); + }; + Displayable.prototype.isStyleObject = function (obj) { + return obj[STYLE_MAGIC_KEY]; + }; + Displayable.prototype._innerSaveToNormal = function (toState) { + _super.prototype._innerSaveToNormal.call(this, toState); + var normalState = this._normalState; + if (toState.style && !normalState.style) { + normalState.style = this._mergeStyle(this.createStyle(), this.style); + } + this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS$1); + }; + Displayable.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) { + _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg); + var needsRestoreToNormal = !(state && keepCurrentStates); + var targetStyle; + if (state && state.style) { + if (transition) { + if (keepCurrentStates) { + targetStyle = state.style; + } + else { + targetStyle = this._mergeStyle(this.createStyle(), normalState.style); + this._mergeStyle(targetStyle, state.style); + } + } + else { + targetStyle = this._mergeStyle(this.createStyle(), keepCurrentStates ? this.style : normalState.style); + this._mergeStyle(targetStyle, state.style); + } + } + else if (needsRestoreToNormal) { + targetStyle = normalState.style; + } + if (targetStyle) { + if (transition) { + var sourceStyle = this.style; + this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle); + if (needsRestoreToNormal) { + var changedKeys = keys(sourceStyle); + for (var i = 0; i < changedKeys.length; i++) { + var key = changedKeys[i]; + if (key in targetStyle) { + targetStyle[key] = targetStyle[key]; + this.style[key] = sourceStyle[key]; + } + } + } + var targetKeys = keys(targetStyle); + for (var i = 0; i < targetKeys.length; i++) { + var key = targetKeys[i]; + this.style[key] = this.style[key]; + } + this._transitionState(stateName, { + style: targetStyle + }, animationCfg, this.getAnimationStyleProps()); + } + else { + this.useStyle(targetStyle); + } + } + var statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS$1; + for (var i = 0; i < statesKeys.length; i++) { + var key = statesKeys[i]; + if (state && state[key] != null) { + this[key] = state[key]; + } + else if (needsRestoreToNormal) { + if (normalState[key] != null) { + this[key] = normalState[key]; + } + } + } + }; + Displayable.prototype._mergeStates = function (states) { + var mergedState = _super.prototype._mergeStates.call(this, states); + var mergedStyle; + for (var i = 0; i < states.length; i++) { + var state = states[i]; + if (state.style) { + mergedStyle = mergedStyle || {}; + this._mergeStyle(mergedStyle, state.style); + } + } + if (mergedStyle) { + mergedState.style = mergedStyle; + } + return mergedState; + }; + Displayable.prototype._mergeStyle = function (targetStyle, sourceStyle) { + extend(targetStyle, sourceStyle); + return targetStyle; + }; + Displayable.prototype.getAnimationStyleProps = function () { + return DEFAULT_COMMON_ANIMATION_PROPS; + }; + Displayable.initDefaultProps = (function () { + var dispProto = Displayable.prototype; + dispProto.type = 'displayable'; + dispProto.invisible = false; + dispProto.z = 0; + dispProto.z2 = 0; + dispProto.zlevel = 0; + dispProto.culling = false; + dispProto.cursor = 'pointer'; + dispProto.rectHover = false; + dispProto.incremental = false; + dispProto._rect = null; + dispProto.dirtyRectTolerance = 0; + dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT; + })(); + return Displayable; + }(Element)); + var tmpRect$1 = new BoundingRect(0, 0, 0, 0); + var viewRect = new BoundingRect(0, 0, 0, 0); + function isDisplayableCulled(el, width, height) { + tmpRect$1.copy(el.getBoundingRect()); + if (el.transform) { + tmpRect$1.applyTransform(el.transform); + } + viewRect.width = width; + viewRect.height = height; + return !tmpRect$1.intersect(viewRect); + } + + var mathMin$1 = Math.min; + var mathMax$1 = Math.max; + var mathSin = Math.sin; + var mathCos = Math.cos; + var PI2 = Math.PI * 2; + var start = create(); + var end = create(); + var extremity = create(); + function fromPoints(points, min, max) { + if (points.length === 0) { + return; + } + var p = points[0]; + var left = p[0]; + var right = p[0]; + var top = p[1]; + var bottom = p[1]; + for (var i = 1; i < points.length; i++) { + p = points[i]; + left = mathMin$1(left, p[0]); + right = mathMax$1(right, p[0]); + top = mathMin$1(top, p[1]); + bottom = mathMax$1(bottom, p[1]); + } + min[0] = left; + min[1] = top; + max[0] = right; + max[1] = bottom; + } + function fromLine(x0, y0, x1, y1, min, max) { + min[0] = mathMin$1(x0, x1); + min[1] = mathMin$1(y0, y1); + max[0] = mathMax$1(x0, x1); + max[1] = mathMax$1(y0, y1); + } + var xDim = []; + var yDim = []; + function fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min, max) { + var cubicExtrema$1 = cubicExtrema; + var cubicAt$1 = cubicAt; + var n = cubicExtrema$1(x0, x1, x2, x3, xDim); + min[0] = Infinity; + min[1] = Infinity; + max[0] = -Infinity; + max[1] = -Infinity; + for (var i = 0; i < n; i++) { + var x = cubicAt$1(x0, x1, x2, x3, xDim[i]); + min[0] = mathMin$1(x, min[0]); + max[0] = mathMax$1(x, max[0]); + } + n = cubicExtrema$1(y0, y1, y2, y3, yDim); + for (var i = 0; i < n; i++) { + var y = cubicAt$1(y0, y1, y2, y3, yDim[i]); + min[1] = mathMin$1(y, min[1]); + max[1] = mathMax$1(y, max[1]); + } + min[0] = mathMin$1(x0, min[0]); + max[0] = mathMax$1(x0, max[0]); + min[0] = mathMin$1(x3, min[0]); + max[0] = mathMax$1(x3, max[0]); + min[1] = mathMin$1(y0, min[1]); + max[1] = mathMax$1(y0, max[1]); + min[1] = mathMin$1(y3, min[1]); + max[1] = mathMax$1(y3, max[1]); + } + function fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) { + var quadraticExtremum$1 = quadraticExtremum; + var quadraticAt$1 = quadraticAt; + var tx = mathMax$1(mathMin$1(quadraticExtremum$1(x0, x1, x2), 1), 0); + var ty = mathMax$1(mathMin$1(quadraticExtremum$1(y0, y1, y2), 1), 0); + var x = quadraticAt$1(x0, x1, x2, tx); + var y = quadraticAt$1(y0, y1, y2, ty); + min[0] = mathMin$1(x0, x2, x); + min[1] = mathMin$1(y0, y2, y); + max[0] = mathMax$1(x0, x2, x); + max[1] = mathMax$1(y0, y2, y); + } + function fromArc(x, y, rx, ry, startAngle, endAngle, anticlockwise, min$1, max$1) { + var vec2Min = min; + var vec2Max = max; + var diff = Math.abs(startAngle - endAngle); + if (diff % PI2 < 1e-4 && diff > 1e-4) { + min$1[0] = x - rx; + min$1[1] = y - ry; + max$1[0] = x + rx; + max$1[1] = y + ry; + return; + } + start[0] = mathCos(startAngle) * rx + x; + start[1] = mathSin(startAngle) * ry + y; + end[0] = mathCos(endAngle) * rx + x; + end[1] = mathSin(endAngle) * ry + y; + vec2Min(min$1, start, end); + vec2Max(max$1, start, end); + startAngle = startAngle % (PI2); + if (startAngle < 0) { + startAngle = startAngle + PI2; + } + endAngle = endAngle % (PI2); + if (endAngle < 0) { + endAngle = endAngle + PI2; + } + if (startAngle > endAngle && !anticlockwise) { + endAngle += PI2; + } + else if (startAngle < endAngle && anticlockwise) { + startAngle += PI2; + } + if (anticlockwise) { + var tmp = endAngle; + endAngle = startAngle; + startAngle = tmp; + } + for (var angle = 0; angle < endAngle; angle += Math.PI / 2) { + if (angle > startAngle) { + extremity[0] = mathCos(angle) * rx + x; + extremity[1] = mathSin(angle) * ry + y; + vec2Min(min$1, extremity, min$1); + vec2Max(max$1, extremity, max$1); + } + } + } + + var CMD = { + M: 1, + L: 2, + C: 3, + Q: 4, + A: 5, + Z: 6, + R: 7 + }; + var tmpOutX = []; + var tmpOutY = []; + var min$1 = []; + var max$1 = []; + var min2 = []; + var max2 = []; + var mathMin$2 = Math.min; + var mathMax$2 = Math.max; + var mathCos$1 = Math.cos; + var mathSin$1 = Math.sin; + var mathAbs = Math.abs; + var PI = Math.PI; + var PI2$1 = PI * 2; + var hasTypedArray = typeof Float32Array !== 'undefined'; + var tmpAngles = []; + function modPI2(radian) { + var n = Math.round(radian / PI * 1e8) / 1e8; + return (n % 2) * PI; + } + function normalizeArcAngles(angles, anticlockwise) { + var newStartAngle = modPI2(angles[0]); + if (newStartAngle < 0) { + newStartAngle += PI2$1; + } + var delta = newStartAngle - angles[0]; + var newEndAngle = angles[1]; + newEndAngle += delta; + if (!anticlockwise && newEndAngle - newStartAngle >= PI2$1) { + newEndAngle = newStartAngle + PI2$1; + } + else if (anticlockwise && newStartAngle - newEndAngle >= PI2$1) { + newEndAngle = newStartAngle - PI2$1; + } + else if (!anticlockwise && newStartAngle > newEndAngle) { + newEndAngle = newStartAngle + (PI2$1 - modPI2(newStartAngle - newEndAngle)); + } + else if (anticlockwise && newStartAngle < newEndAngle) { + newEndAngle = newStartAngle - (PI2$1 - modPI2(newEndAngle - newStartAngle)); + } + angles[0] = newStartAngle; + angles[1] = newEndAngle; + } + var PathProxy = (function () { + function PathProxy(notSaveData) { + this.dpr = 1; + this._xi = 0; + this._yi = 0; + this._x0 = 0; + this._y0 = 0; + this._len = 0; + if (notSaveData) { + this._saveData = false; + } + if (this._saveData) { + this.data = []; + } + } + PathProxy.prototype.increaseVersion = function () { + this._version++; + }; + PathProxy.prototype.getVersion = function () { + return this._version; + }; + PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) { + segmentIgnoreThreshold = segmentIgnoreThreshold || 0; + if (segmentIgnoreThreshold > 0) { + this._ux = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sx) || 0; + this._uy = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sy) || 0; + } + }; + PathProxy.prototype.setDPR = function (dpr) { + this.dpr = dpr; + }; + PathProxy.prototype.setContext = function (ctx) { + this._ctx = ctx; + }; + PathProxy.prototype.getContext = function () { + return this._ctx; + }; + PathProxy.prototype.beginPath = function () { + this._ctx && this._ctx.beginPath(); + this.reset(); + return this; + }; + PathProxy.prototype.reset = function () { + if (this._saveData) { + this._len = 0; + } + if (this._pathSegLen) { + this._pathSegLen = null; + this._pathLen = 0; + } + this._version++; + }; + PathProxy.prototype.moveTo = function (x, y) { + this._drawPendingPt(); + this.addData(CMD.M, x, y); + this._ctx && this._ctx.moveTo(x, y); + this._x0 = x; + this._y0 = y; + this._xi = x; + this._yi = y; + return this; + }; + PathProxy.prototype.lineTo = function (x, y) { + var dx = mathAbs(x - this._xi); + var dy = mathAbs(y - this._yi); + var exceedUnit = dx > this._ux || dy > this._uy; + this.addData(CMD.L, x, y); + if (this._ctx && exceedUnit) { + this._ctx.lineTo(x, y); + } + if (exceedUnit) { + this._xi = x; + this._yi = y; + this._pendingPtDist = 0; + } + else { + var d2 = dx * dx + dy * dy; + if (d2 > this._pendingPtDist) { + this._pendingPtX = x; + this._pendingPtY = y; + this._pendingPtDist = d2; + } + } + return this; + }; + PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) { + this._drawPendingPt(); + this.addData(CMD.C, x1, y1, x2, y2, x3, y3); + if (this._ctx) { + this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); + } + this._xi = x3; + this._yi = y3; + return this; + }; + PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) { + this._drawPendingPt(); + this.addData(CMD.Q, x1, y1, x2, y2); + if (this._ctx) { + this._ctx.quadraticCurveTo(x1, y1, x2, y2); + } + this._xi = x2; + this._yi = y2; + return this; + }; + PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) { + this._drawPendingPt(); + tmpAngles[0] = startAngle; + tmpAngles[1] = endAngle; + normalizeArcAngles(tmpAngles, anticlockwise); + startAngle = tmpAngles[0]; + endAngle = tmpAngles[1]; + var delta = endAngle - startAngle; + this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 0, anticlockwise ? 0 : 1); + this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise); + this._xi = mathCos$1(endAngle) * r + cx; + this._yi = mathSin$1(endAngle) * r + cy; + return this; + }; + PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) { + this._drawPendingPt(); + if (this._ctx) { + this._ctx.arcTo(x1, y1, x2, y2, radius); + } + return this; + }; + PathProxy.prototype.rect = function (x, y, w, h) { + this._drawPendingPt(); + this._ctx && this._ctx.rect(x, y, w, h); + this.addData(CMD.R, x, y, w, h); + return this; + }; + PathProxy.prototype.closePath = function () { + this._drawPendingPt(); + this.addData(CMD.Z); + var ctx = this._ctx; + var x0 = this._x0; + var y0 = this._y0; + if (ctx) { + ctx.closePath(); + } + this._xi = x0; + this._yi = y0; + return this; + }; + PathProxy.prototype.fill = function (ctx) { + ctx && ctx.fill(); + this.toStatic(); + }; + PathProxy.prototype.stroke = function (ctx) { + ctx && ctx.stroke(); + this.toStatic(); + }; + PathProxy.prototype.len = function () { + return this._len; + }; + PathProxy.prototype.setData = function (data) { + var len = data.length; + if (!(this.data && this.data.length === len) && hasTypedArray) { + this.data = new Float32Array(len); + } + for (var i = 0; i < len; i++) { + this.data[i] = data[i]; + } + this._len = len; + }; + PathProxy.prototype.appendPath = function (path) { + if (!(path instanceof Array)) { + path = [path]; + } + var len = path.length; + var appendSize = 0; + var offset = this._len; + for (var i = 0; i < len; i++) { + appendSize += path[i].len(); + } + if (hasTypedArray && (this.data instanceof Float32Array)) { + this.data = new Float32Array(offset + appendSize); + } + for (var i = 0; i < len; i++) { + var appendPathData = path[i].data; + for (var k = 0; k < appendPathData.length; k++) { + this.data[offset++] = appendPathData[k]; + } + } + this._len = offset; + }; + PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) { + if (!this._saveData) { + return; + } + var data = this.data; + if (this._len + arguments.length > data.length) { + this._expandData(); + data = this.data; + } + for (var i = 0; i < arguments.length; i++) { + data[this._len++] = arguments[i]; + } + }; + PathProxy.prototype._drawPendingPt = function () { + if (this._pendingPtDist > 0) { + this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY); + this._pendingPtDist = 0; + } + }; + PathProxy.prototype._expandData = function () { + if (!(this.data instanceof Array)) { + var newData = []; + for (var i = 0; i < this._len; i++) { + newData[i] = this.data[i]; + } + this.data = newData; + } + }; + PathProxy.prototype.toStatic = function () { + if (!this._saveData) { + return; + } + this._drawPendingPt(); + var data = this.data; + if (data instanceof Array) { + data.length = this._len; + if (hasTypedArray && this._len > 11) { + this.data = new Float32Array(data); + } + } + }; + PathProxy.prototype.getBoundingRect = function () { + min$1[0] = min$1[1] = min2[0] = min2[1] = Number.MAX_VALUE; + max$1[0] = max$1[1] = max2[0] = max2[1] = -Number.MAX_VALUE; + var data = this.data; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + var i; + for (i = 0; i < this._len;) { + var cmd = data[i++]; + var isFirst = i === 1; + if (isFirst) { + xi = data[i]; + yi = data[i + 1]; + x0 = xi; + y0 = yi; + } + switch (cmd) { + case CMD.M: + xi = x0 = data[i++]; + yi = y0 = data[i++]; + min2[0] = x0; + min2[1] = y0; + max2[0] = x0; + max2[1] = y0; + break; + case CMD.L: + fromLine(xi, yi, data[i], data[i + 1], min2, max2); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.C: + fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.Q: + fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.A: + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++]; + var endAngle = data[i++] + startAngle; + i += 1; + var anticlockwise = !data[i++]; + if (isFirst) { + x0 = mathCos$1(startAngle) * rx + cx; + y0 = mathSin$1(startAngle) * ry + cy; + } + fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2); + xi = mathCos$1(endAngle) * rx + cx; + yi = mathSin$1(endAngle) * ry + cy; + break; + case CMD.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + fromLine(x0, y0, x0 + width, y0 + height, min2, max2); + break; + case CMD.Z: + xi = x0; + yi = y0; + break; + } + min(min$1, min$1, min2); + max(max$1, max$1, max2); + } + if (i === 0) { + min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0; + } + return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]); + }; + PathProxy.prototype._calculateLength = function () { + var data = this.data; + var len = this._len; + var ux = this._ux; + var uy = this._uy; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + if (!this._pathSegLen) { + this._pathSegLen = []; + } + var pathSegLen = this._pathSegLen; + var pathTotalLen = 0; + var segCount = 0; + for (var i = 0; i < len;) { + var cmd = data[i++]; + var isFirst = i === 1; + if (isFirst) { + xi = data[i]; + yi = data[i + 1]; + x0 = xi; + y0 = yi; + } + var l = -1; + switch (cmd) { + case CMD.M: + xi = x0 = data[i++]; + yi = y0 = data[i++]; + break; + case CMD.L: { + var x2 = data[i++]; + var y2 = data[i++]; + var dx = x2 - xi; + var dy = y2 - yi; + if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) { + l = Math.sqrt(dx * dx + dy * dy); + xi = x2; + yi = y2; + } + break; + } + case CMD.C: { + var x1 = data[i++]; + var y1 = data[i++]; + var x2 = data[i++]; + var y2 = data[i++]; + var x3 = data[i++]; + var y3 = data[i++]; + l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10); + xi = x3; + yi = y3; + break; + } + case CMD.Q: { + var x1 = data[i++]; + var y1 = data[i++]; + var x2 = data[i++]; + var y2 = data[i++]; + l = quadraticLength(xi, yi, x1, y1, x2, y2, 10); + xi = x2; + yi = y2; + break; + } + case CMD.A: + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++]; + var delta = data[i++]; + var endAngle = delta + startAngle; + i += 1; + if (isFirst) { + x0 = mathCos$1(startAngle) * rx + cx; + y0 = mathSin$1(startAngle) * ry + cy; + } + l = mathMax$2(rx, ry) * mathMin$2(PI2$1, Math.abs(delta)); + xi = mathCos$1(endAngle) * rx + cx; + yi = mathSin$1(endAngle) * ry + cy; + break; + case CMD.R: { + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + l = width * 2 + height * 2; + break; + } + case CMD.Z: { + var dx = x0 - xi; + var dy = y0 - yi; + l = Math.sqrt(dx * dx + dy * dy); + xi = x0; + yi = y0; + break; + } + } + if (l >= 0) { + pathSegLen[segCount++] = l; + pathTotalLen += l; + } + } + this._pathLen = pathTotalLen; + return pathTotalLen; + }; + PathProxy.prototype.rebuildPath = function (ctx, percent) { + var d = this.data; + var ux = this._ux; + var uy = this._uy; + var len = this._len; + var x0; + var y0; + var xi; + var yi; + var x; + var y; + var drawPart = percent < 1; + var pathSegLen; + var pathTotalLen; + var accumLength = 0; + var segCount = 0; + var displayedLength; + var pendingPtDist = 0; + var pendingPtX; + var pendingPtY; + if (drawPart) { + if (!this._pathSegLen) { + this._calculateLength(); + } + pathSegLen = this._pathSegLen; + pathTotalLen = this._pathLen; + displayedLength = percent * pathTotalLen; + if (!displayedLength) { + return; + } + } + lo: for (var i = 0; i < len;) { + var cmd = d[i++]; + var isFirst = i === 1; + if (isFirst) { + xi = d[i]; + yi = d[i + 1]; + x0 = xi; + y0 = yi; + } + if (cmd !== CMD.L && pendingPtDist > 0) { + ctx.lineTo(pendingPtX, pendingPtY); + pendingPtDist = 0; + } + switch (cmd) { + case CMD.M: + x0 = xi = d[i++]; + y0 = yi = d[i++]; + ctx.moveTo(xi, yi); + break; + case CMD.L: { + x = d[i++]; + y = d[i++]; + var dx = mathAbs(x - xi); + var dy = mathAbs(y - yi); + if (dx > ux || dy > uy) { + if (drawPart) { + var l = pathSegLen[segCount++]; + if (accumLength + l > displayedLength) { + var t = (displayedLength - accumLength) / l; + ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t); + break lo; + } + accumLength += l; + } + ctx.lineTo(x, y); + xi = x; + yi = y; + pendingPtDist = 0; + } + else { + var d2 = dx * dx + dy * dy; + if (d2 > pendingPtDist) { + pendingPtX = x; + pendingPtY = y; + pendingPtDist = d2; + } + } + break; + } + case CMD.C: { + var x1 = d[i++]; + var y1 = d[i++]; + var x2 = d[i++]; + var y2 = d[i++]; + var x3 = d[i++]; + var y3 = d[i++]; + if (drawPart) { + var l = pathSegLen[segCount++]; + if (accumLength + l > displayedLength) { + var t = (displayedLength - accumLength) / l; + cubicSubdivide(xi, x1, x2, x3, t, tmpOutX); + cubicSubdivide(yi, y1, y2, y3, t, tmpOutY); + ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]); + break lo; + } + accumLength += l; + } + ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); + xi = x3; + yi = y3; + break; + } + case CMD.Q: { + var x1 = d[i++]; + var y1 = d[i++]; + var x2 = d[i++]; + var y2 = d[i++]; + if (drawPart) { + var l = pathSegLen[segCount++]; + if (accumLength + l > displayedLength) { + var t = (displayedLength - accumLength) / l; + quadraticSubdivide(xi, x1, x2, t, tmpOutX); + quadraticSubdivide(yi, y1, y2, t, tmpOutY); + ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]); + break lo; + } + accumLength += l; + } + ctx.quadraticCurveTo(x1, y1, x2, y2); + xi = x2; + yi = y2; + break; + } + case CMD.A: + var cx = d[i++]; + var cy = d[i++]; + var rx = d[i++]; + var ry = d[i++]; + var startAngle = d[i++]; + var delta = d[i++]; + var psi = d[i++]; + var anticlockwise = !d[i++]; + var r = (rx > ry) ? rx : ry; + var isEllipse = mathAbs(rx - ry) > 1e-3; + var endAngle = startAngle + delta; + var breakBuild = false; + if (drawPart) { + var l = pathSegLen[segCount++]; + if (accumLength + l > displayedLength) { + endAngle = startAngle + delta * (displayedLength - accumLength) / l; + breakBuild = true; + } + accumLength += l; + } + if (isEllipse && ctx.ellipse) { + ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise); + } + else { + ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise); + } + if (breakBuild) { + break lo; + } + if (isFirst) { + x0 = mathCos$1(startAngle) * rx + cx; + y0 = mathSin$1(startAngle) * ry + cy; + } + xi = mathCos$1(endAngle) * rx + cx; + yi = mathSin$1(endAngle) * ry + cy; + break; + case CMD.R: + x0 = xi = d[i]; + y0 = yi = d[i + 1]; + x = d[i++]; + y = d[i++]; + var width = d[i++]; + var height = d[i++]; + if (drawPart) { + var l = pathSegLen[segCount++]; + if (accumLength + l > displayedLength) { + var d_1 = displayedLength - accumLength; + ctx.moveTo(x, y); + ctx.lineTo(x + mathMin$2(d_1, width), y); + d_1 -= width; + if (d_1 > 0) { + ctx.lineTo(x + width, y + mathMin$2(d_1, height)); + } + d_1 -= height; + if (d_1 > 0) { + ctx.lineTo(x + mathMax$2(width - d_1, 0), y + height); + } + d_1 -= width; + if (d_1 > 0) { + ctx.lineTo(x, y + mathMax$2(height - d_1, 0)); + } + break lo; + } + accumLength += l; + } + ctx.rect(x, y, width, height); + break; + case CMD.Z: + if (drawPart) { + var l = pathSegLen[segCount++]; + if (accumLength + l > displayedLength) { + var t = (displayedLength - accumLength) / l; + ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t); + break lo; + } + accumLength += l; + } + ctx.closePath(); + xi = x0; + yi = y0; + } + } + }; + PathProxy.prototype.clone = function () { + var newProxy = new PathProxy(); + var data = this.data; + newProxy.data = data.slice ? data.slice() + : Array.prototype.slice.call(data); + newProxy._len = this._len; + return newProxy; + }; + PathProxy.CMD = CMD; + PathProxy.initDefaultProps = (function () { + var proto = PathProxy.prototype; + proto._saveData = true; + proto._ux = 0; + proto._uy = 0; + proto._pendingPtDist = 0; + proto._version = 0; + })(); + return PathProxy; + }()); + + function containStroke(x0, y0, x1, y1, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + var _a = 0; + var _b = x0; + if ((y > y0 + _l && y > y1 + _l) + || (y < y0 - _l && y < y1 - _l) + || (x > x0 + _l && x > x1 + _l) + || (x < x0 - _l && x < x1 - _l)) { + return false; + } + if (x0 !== x1) { + _a = (y0 - y1) / (x0 - x1); + _b = (x0 * y1 - x1 * y0) / (x0 - x1); + } + else { + return Math.abs(x - x0) <= _l / 2; + } + var tmp = _a * x - y + _b; + var _s = tmp * tmp / (_a * _a + 1); + return _s <= _l / 2 * _l / 2; + } + + function containStroke$1(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + if ((y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l) + || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l) + || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l) + || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)) { + return false; + } + var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null); + return d <= _l / 2; + } + + function containStroke$2(x0, y0, x1, y1, x2, y2, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + if ((y > y0 + _l && y > y1 + _l && y > y2 + _l) + || (y < y0 - _l && y < y1 - _l && y < y2 - _l) + || (x > x0 + _l && x > x1 + _l && x > x2 + _l) + || (x < x0 - _l && x < x1 - _l && x < x2 - _l)) { + return false; + } + var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null); + return d <= _l / 2; + } + + var PI2$2 = Math.PI * 2; + function normalizeRadian(angle) { + angle %= PI2$2; + if (angle < 0) { + angle += PI2$2; + } + return angle; + } + + var PI2$3 = Math.PI * 2; + function containStroke$3(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + x -= cx; + y -= cy; + var d = Math.sqrt(x * x + y * y); + if ((d - _l > r) || (d + _l < r)) { + return false; + } + if (Math.abs(startAngle - endAngle) % PI2$3 < 1e-4) { + return true; + } + if (anticlockwise) { + var tmp = startAngle; + startAngle = normalizeRadian(endAngle); + endAngle = normalizeRadian(tmp); + } + else { + startAngle = normalizeRadian(startAngle); + endAngle = normalizeRadian(endAngle); + } + if (startAngle > endAngle) { + endAngle += PI2$3; + } + var angle = Math.atan2(y, x); + if (angle < 0) { + angle += PI2$3; + } + return (angle >= startAngle && angle <= endAngle) + || (angle + PI2$3 >= startAngle && angle + PI2$3 <= endAngle); + } + + function windingLine(x0, y0, x1, y1, x, y) { + if ((y > y0 && y > y1) || (y < y0 && y < y1)) { + return 0; + } + if (y1 === y0) { + return 0; + } + var t = (y - y0) / (y1 - y0); + var dir = y1 < y0 ? 1 : -1; + if (t === 1 || t === 0) { + dir = y1 < y0 ? 0.5 : -0.5; + } + var x_ = t * (x1 - x0) + x0; + return x_ === x ? Infinity : x_ > x ? dir : 0; + } + + var CMD$1 = PathProxy.CMD; + var PI2$4 = Math.PI * 2; + var EPSILON$3 = 1e-4; + function isAroundEqual(a, b) { + return Math.abs(a - b) < EPSILON$3; + } + var roots = [-1, -1, -1]; + var extrema = [-1, -1]; + function swapExtrema() { + var tmp = extrema[0]; + extrema[0] = extrema[1]; + extrema[1] = tmp; + } + function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) { + if ((y > y0 && y > y1 && y > y2 && y > y3) + || (y < y0 && y < y1 && y < y2 && y < y3)) { + return 0; + } + var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var w = 0; + var nExtrema = -1; + var y0_ = void 0; + var y1_ = void 0; + for (var i = 0; i < nRoots; i++) { + var t = roots[i]; + var unit = (t === 0 || t === 1) ? 0.5 : 1; + var x_ = cubicAt(x0, x1, x2, x3, t); + if (x_ < x) { + continue; + } + if (nExtrema < 0) { + nExtrema = cubicExtrema(y0, y1, y2, y3, extrema); + if (extrema[1] < extrema[0] && nExtrema > 1) { + swapExtrema(); + } + y0_ = cubicAt(y0, y1, y2, y3, extrema[0]); + if (nExtrema > 1) { + y1_ = cubicAt(y0, y1, y2, y3, extrema[1]); + } + } + if (nExtrema === 2) { + if (t < extrema[0]) { + w += y0_ < y0 ? unit : -unit; + } + else if (t < extrema[1]) { + w += y1_ < y0_ ? unit : -unit; + } + else { + w += y3 < y1_ ? unit : -unit; + } + } + else { + if (t < extrema[0]) { + w += y0_ < y0 ? unit : -unit; + } + else { + w += y3 < y0_ ? unit : -unit; + } + } + } + return w; + } + } + function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) { + if ((y > y0 && y > y1 && y > y2) + || (y < y0 && y < y1 && y < y2)) { + return 0; + } + var nRoots = quadraticRootAt(y0, y1, y2, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var t = quadraticExtremum(y0, y1, y2); + if (t >= 0 && t <= 1) { + var w = 0; + var y_ = quadraticAt(y0, y1, y2, t); + for (var i = 0; i < nRoots; i++) { + var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1; + var x_ = quadraticAt(x0, x1, x2, roots[i]); + if (x_ < x) { + continue; + } + if (roots[i] < t) { + w += y_ < y0 ? unit : -unit; + } + else { + w += y2 < y_ ? unit : -unit; + } + } + return w; + } + else { + var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1; + var x_ = quadraticAt(x0, x1, x2, roots[0]); + if (x_ < x) { + return 0; + } + return y2 < y0 ? unit : -unit; + } + } + } + function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) { + y -= cy; + if (y > r || y < -r) { + return 0; + } + var tmp = Math.sqrt(r * r - y * y); + roots[0] = -tmp; + roots[1] = tmp; + var dTheta = Math.abs(startAngle - endAngle); + if (dTheta < 1e-4) { + return 0; + } + if (dTheta >= PI2$4 - 1e-4) { + startAngle = 0; + endAngle = PI2$4; + var dir = anticlockwise ? 1 : -1; + if (x >= roots[0] + cx && x <= roots[1] + cx) { + return dir; + } + else { + return 0; + } + } + if (startAngle > endAngle) { + var tmp_1 = startAngle; + startAngle = endAngle; + endAngle = tmp_1; + } + if (startAngle < 0) { + startAngle += PI2$4; + endAngle += PI2$4; + } + var w = 0; + for (var i = 0; i < 2; i++) { + var x_ = roots[i]; + if (x_ + cx > x) { + var angle = Math.atan2(y, x_); + var dir = anticlockwise ? 1 : -1; + if (angle < 0) { + angle = PI2$4 + angle; + } + if ((angle >= startAngle && angle <= endAngle) + || (angle + PI2$4 >= startAngle && angle + PI2$4 <= endAngle)) { + if (angle > Math.PI / 2 && angle < Math.PI * 1.5) { + dir = -dir; + } + w += dir; + } + } + } + return w; + } + function containPath(path, lineWidth, isStroke, x, y) { + var data = path.data; + var len = path.len(); + var w = 0; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + var x1; + var y1; + for (var i = 0; i < len;) { + var cmd = data[i++]; + var isFirst = i === 1; + if (cmd === CMD$1.M && i > 1) { + if (!isStroke) { + w += windingLine(xi, yi, x0, y0, x, y); + } + } + if (isFirst) { + xi = data[i]; + yi = data[i + 1]; + x0 = xi; + y0 = yi; + } + switch (cmd) { + case CMD$1.M: + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + break; + case CMD$1.L: + if (isStroke) { + if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) { + return true; + } + } + else { + w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD$1.C: + if (isStroke) { + if (containStroke$1(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) { + return true; + } + } + else { + w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD$1.Q: + if (isStroke) { + if (containStroke$2(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) { + return true; + } + } + else { + w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD$1.A: + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var theta = data[i++]; + var dTheta = data[i++]; + i += 1; + var anticlockwise = !!(1 - data[i++]); + x1 = Math.cos(theta) * rx + cx; + y1 = Math.sin(theta) * ry + cy; + if (!isFirst) { + w += windingLine(xi, yi, x1, y1, x, y); + } + else { + x0 = x1; + y0 = y1; + } + var _x = (x - cx) * ry / rx + cx; + if (isStroke) { + if (containStroke$3(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) { + return true; + } + } + else { + w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y); + } + xi = Math.cos(theta + dTheta) * rx + cx; + yi = Math.sin(theta + dTheta) * ry + cy; + break; + case CMD$1.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + x1 = x0 + width; + y1 = y0 + height; + if (isStroke) { + if (containStroke(x0, y0, x1, y0, lineWidth, x, y) + || containStroke(x1, y0, x1, y1, lineWidth, x, y) + || containStroke(x1, y1, x0, y1, lineWidth, x, y) + || containStroke(x0, y1, x0, y0, lineWidth, x, y)) { + return true; + } + } + else { + w += windingLine(x1, y0, x1, y1, x, y); + w += windingLine(x0, y1, x0, y0, x, y); + } + break; + case CMD$1.Z: + if (isStroke) { + if (containStroke(xi, yi, x0, y0, lineWidth, x, y)) { + return true; + } + } + else { + w += windingLine(xi, yi, x0, y0, x, y); + } + xi = x0; + yi = y0; + break; + } + } + if (!isStroke && !isAroundEqual(yi, y0)) { + w += windingLine(xi, yi, x0, y0, x, y) || 0; + } + return w !== 0; + } + function contain(pathProxy, x, y) { + return containPath(pathProxy, 0, false, x, y); + } + function containStroke$4(pathProxy, lineWidth, x, y) { + return containPath(pathProxy, lineWidth, true, x, y); + } + + var DEFAULT_PATH_STYLE = defaults({ + fill: '#000', + stroke: null, + strokePercent: 1, + fillOpacity: 1, + strokeOpacity: 1, + lineDashOffset: 0, + lineWidth: 1, + lineCap: 'butt', + miterLimit: 10, + strokeNoScale: false, + strokeFirst: false + }, DEFAULT_COMMON_STYLE); + var DEFAULT_PATH_ANIMATION_PROPS = { + style: defaults({ + fill: true, + stroke: true, + strokePercent: true, + fillOpacity: true, + strokeOpacity: true, + lineDashOffset: true, + lineWidth: true, + miterLimit: true + }, DEFAULT_COMMON_ANIMATION_PROPS.style) + }; + var pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible', + 'culling', 'z', 'z2', 'zlevel', 'parent' + ]); + var Path = (function (_super) { + __extends(Path, _super); + function Path(opts) { + return _super.call(this, opts) || this; + } + Path.prototype.update = function () { + var _this = this; + _super.prototype.update.call(this); + var style = this.style; + if (style.decal) { + var decalEl = this._decalEl = this._decalEl || new Path(); + if (decalEl.buildPath === Path.prototype.buildPath) { + decalEl.buildPath = function (ctx) { + _this.buildPath(ctx, _this.shape); + }; + } + decalEl.silent = true; + var decalElStyle = decalEl.style; + for (var key in style) { + if (decalElStyle[key] !== style[key]) { + decalElStyle[key] = style[key]; + } + } + decalElStyle.fill = style.fill ? style.decal : null; + decalElStyle.decal = null; + decalElStyle.shadowColor = null; + style.strokeFirst && (decalElStyle.stroke = null); + for (var i = 0; i < pathCopyParams.length; ++i) { + decalEl[pathCopyParams[i]] = this[pathCopyParams[i]]; + } + decalEl.__dirty |= REDRAW_BIT; + } + else if (this._decalEl) { + this._decalEl = null; + } + }; + Path.prototype.getDecalElement = function () { + return this._decalEl; + }; + Path.prototype._init = function (props) { + var keysArr = keys(props); + this.shape = this.getDefaultShape(); + var defaultStyle = this.getDefaultStyle(); + if (defaultStyle) { + this.useStyle(defaultStyle); + } + for (var i = 0; i < keysArr.length; i++) { + var key = keysArr[i]; + var value = props[key]; + if (key === 'style') { + if (!this.style) { + this.useStyle(value); + } + else { + extend(this.style, value); + } + } + else if (key === 'shape') { + extend(this.shape, value); + } + else { + _super.prototype.attrKV.call(this, key, value); + } + } + if (!this.style) { + this.useStyle({}); + } + }; + Path.prototype.getDefaultStyle = function () { + return null; + }; + Path.prototype.getDefaultShape = function () { + return {}; + }; + Path.prototype.canBeInsideText = function () { + return this.hasFill(); + }; + Path.prototype.getInsideTextFill = function () { + var pathFill = this.style.fill; + if (pathFill !== 'none') { + if (isString(pathFill)) { + var fillLum = lum(pathFill, 0); + if (fillLum > 0.5) { + return DARK_LABEL_COLOR; + } + else if (fillLum > 0.2) { + return LIGHTER_LABEL_COLOR; + } + return LIGHT_LABEL_COLOR; + } + else if (pathFill) { + return LIGHT_LABEL_COLOR; + } + } + return DARK_LABEL_COLOR; + }; + Path.prototype.getInsideTextStroke = function (textFill) { + var pathFill = this.style.fill; + if (isString(pathFill)) { + var zr = this.__zr; + var isDarkMode = !!(zr && zr.isDarkMode()); + var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD; + if (isDarkMode === isDarkLabel) { + return pathFill; + } + } + }; + Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { }; + Path.prototype.pathUpdated = function () { + this.__dirty &= ~SHAPE_CHANGED_BIT; + }; + Path.prototype.getUpdatedPathProxy = function (inBatch) { + !this.path && this.createPathProxy(); + this.path.beginPath(); + this.buildPath(this.path, this.shape, inBatch); + return this.path; + }; + Path.prototype.createPathProxy = function () { + this.path = new PathProxy(false); + }; + Path.prototype.hasStroke = function () { + var style = this.style; + var stroke = style.stroke; + return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0)); + }; + Path.prototype.hasFill = function () { + var style = this.style; + var fill = style.fill; + return fill != null && fill !== 'none'; + }; + Path.prototype.getBoundingRect = function () { + var rect = this._rect; + var style = this.style; + var needsUpdateRect = !rect; + if (needsUpdateRect) { + var firstInvoke = false; + if (!this.path) { + firstInvoke = true; + this.createPathProxy(); + } + var path = this.path; + if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) { + path.beginPath(); + this.buildPath(path, this.shape, false); + this.pathUpdated(); + } + rect = path.getBoundingRect(); + } + this._rect = rect; + if (this.hasStroke() && this.path && this.path.len() > 0) { + var rectStroke = this._rectStroke || (this._rectStroke = rect.clone()); + if (this.__dirty || needsUpdateRect) { + rectStroke.copy(rect); + var lineScale = style.strokeNoScale ? this.getLineScale() : 1; + var w = style.lineWidth; + if (!this.hasFill()) { + var strokeContainThreshold = this.strokeContainThreshold; + w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold); + } + if (lineScale > 1e-10) { + rectStroke.width += w / lineScale; + rectStroke.height += w / lineScale; + rectStroke.x -= w / lineScale / 2; + rectStroke.y -= w / lineScale / 2; + } + } + return rectStroke; + } + return rect; + }; + Path.prototype.contain = function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + var style = this.style; + x = localPos[0]; + y = localPos[1]; + if (rect.contain(x, y)) { + var pathProxy = this.path; + if (this.hasStroke()) { + var lineWidth = style.lineWidth; + var lineScale = style.strokeNoScale ? this.getLineScale() : 1; + if (lineScale > 1e-10) { + if (!this.hasFill()) { + lineWidth = Math.max(lineWidth, this.strokeContainThreshold); + } + if (containStroke$4(pathProxy, lineWidth / lineScale, x, y)) { + return true; + } + } + } + if (this.hasFill()) { + return contain(pathProxy, x, y); + } + } + return false; + }; + Path.prototype.dirtyShape = function () { + this.__dirty |= SHAPE_CHANGED_BIT; + if (this._rect) { + this._rect = null; + } + if (this._decalEl) { + this._decalEl.dirtyShape(); + } + this.markRedraw(); + }; + Path.prototype.dirty = function () { + this.dirtyStyle(); + this.dirtyShape(); + }; + Path.prototype.animateShape = function (loop) { + return this.animate('shape', loop); + }; + Path.prototype.updateDuringAnimation = function (targetKey) { + if (targetKey === 'style') { + this.dirtyStyle(); + } + else if (targetKey === 'shape') { + this.dirtyShape(); + } + else { + this.markRedraw(); + } + }; + Path.prototype.attrKV = function (key, value) { + if (key === 'shape') { + this.setShape(value); + } + else { + _super.prototype.attrKV.call(this, key, value); + } + }; + Path.prototype.setShape = function (keyOrObj, value) { + var shape = this.shape; + if (!shape) { + shape = this.shape = {}; + } + if (typeof keyOrObj === 'string') { + shape[keyOrObj] = value; + } + else { + extend(shape, keyOrObj); + } + this.dirtyShape(); + return this; + }; + Path.prototype.shapeChanged = function () { + return !!(this.__dirty & SHAPE_CHANGED_BIT); + }; + Path.prototype.createStyle = function (obj) { + return createObject(DEFAULT_PATH_STYLE, obj); + }; + Path.prototype._innerSaveToNormal = function (toState) { + _super.prototype._innerSaveToNormal.call(this, toState); + var normalState = this._normalState; + if (toState.shape && !normalState.shape) { + normalState.shape = extend({}, this.shape); + } + }; + Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) { + _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg); + var needsRestoreToNormal = !(state && keepCurrentStates); + var targetShape; + if (state && state.shape) { + if (transition) { + if (keepCurrentStates) { + targetShape = state.shape; + } + else { + targetShape = extend({}, normalState.shape); + extend(targetShape, state.shape); + } + } + else { + targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape); + extend(targetShape, state.shape); + } + } + else if (needsRestoreToNormal) { + targetShape = normalState.shape; + } + if (targetShape) { + if (transition) { + this.shape = extend({}, this.shape); + var targetShapePrimaryProps = {}; + var shapeKeys = keys(targetShape); + for (var i = 0; i < shapeKeys.length; i++) { + var key = shapeKeys[i]; + if (typeof targetShape[key] === 'object') { + this.shape[key] = targetShape[key]; + } + else { + targetShapePrimaryProps[key] = targetShape[key]; + } + } + this._transitionState(stateName, { + shape: targetShapePrimaryProps + }, animationCfg); + } + else { + this.shape = targetShape; + this.dirtyShape(); + } + } + }; + Path.prototype._mergeStates = function (states) { + var mergedState = _super.prototype._mergeStates.call(this, states); + var mergedShape; + for (var i = 0; i < states.length; i++) { + var state = states[i]; + if (state.shape) { + mergedShape = mergedShape || {}; + this._mergeStyle(mergedShape, state.shape); + } + } + if (mergedShape) { + mergedState.shape = mergedShape; + } + return mergedState; + }; + Path.prototype.getAnimationStyleProps = function () { + return DEFAULT_PATH_ANIMATION_PROPS; + }; + Path.prototype.isZeroArea = function () { + return false; + }; + Path.extend = function (defaultProps) { + var Sub = (function (_super) { + __extends(Sub, _super); + function Sub(opts) { + var _this = _super.call(this, opts) || this; + defaultProps.init && defaultProps.init.call(_this, opts); + return _this; + } + Sub.prototype.getDefaultStyle = function () { + return clone(defaultProps.style); + }; + Sub.prototype.getDefaultShape = function () { + return clone(defaultProps.shape); + }; + return Sub; + }(Path)); + for (var key in defaultProps) { + if (typeof defaultProps[key] === 'function') { + Sub.prototype[key] = defaultProps[key]; + } + } + return Sub; + }; + Path.initDefaultProps = (function () { + var pathProto = Path.prototype; + pathProto.type = 'path'; + pathProto.strokeContainThreshold = 5; + pathProto.segmentIgnoreThreshold = 0; + pathProto.subPixelOptimize = false; + pathProto.autoBatch = false; + pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT; + })(); + return Path; + }(Displayable)); + + var DEFAULT_TSPAN_STYLE = defaults({ + strokeFirst: true, + font: DEFAULT_FONT, + x: 0, + y: 0, + textAlign: 'left', + textBaseline: 'top', + miterLimit: 2 + }, DEFAULT_PATH_STYLE); + var TSpan = (function (_super) { + __extends(TSpan, _super); + function TSpan() { + return _super !== null && _super.apply(this, arguments) || this; + } + TSpan.prototype.hasStroke = function () { + var style = this.style; + var stroke = style.stroke; + return stroke != null && stroke !== 'none' && style.lineWidth > 0; + }; + TSpan.prototype.hasFill = function () { + var style = this.style; + var fill = style.fill; + return fill != null && fill !== 'none'; + }; + TSpan.prototype.createStyle = function (obj) { + return createObject(DEFAULT_TSPAN_STYLE, obj); + }; + TSpan.prototype.setBoundingRect = function (rect) { + this._rect = rect; + }; + TSpan.prototype.getBoundingRect = function () { + var style = this.style; + if (!this._rect) { + var text = style.text; + text != null ? (text += '') : (text = ''); + var rect = getBoundingRect(text, style.font, style.textAlign, style.textBaseline); + rect.x += style.x || 0; + rect.y += style.y || 0; + if (this.hasStroke()) { + var w = style.lineWidth; + rect.x -= w / 2; + rect.y -= w / 2; + rect.width += w; + rect.height += w; + } + this._rect = rect; + } + return this._rect; + }; + TSpan.initDefaultProps = (function () { + var tspanProto = TSpan.prototype; + tspanProto.dirtyRectTolerance = 10; + })(); + return TSpan; + }(Displayable)); + TSpan.prototype.type = 'tspan'; + + var DEFAULT_IMAGE_STYLE = defaults({ + x: 0, + y: 0 + }, DEFAULT_COMMON_STYLE); + var DEFAULT_IMAGE_ANIMATION_PROPS = { + style: defaults({ + x: true, + y: true, + width: true, + height: true, + sx: true, + sy: true, + sWidth: true, + sHeight: true + }, DEFAULT_COMMON_ANIMATION_PROPS.style) + }; + function isImageLike(source) { + return !!(source + && typeof source !== 'string' + && source.width && source.height); + } + var ZRImage = (function (_super) { + __extends(ZRImage, _super); + function ZRImage() { + return _super !== null && _super.apply(this, arguments) || this; + } + ZRImage.prototype.createStyle = function (obj) { + return createObject(DEFAULT_IMAGE_STYLE, obj); + }; + ZRImage.prototype._getSize = function (dim) { + var style = this.style; + var size = style[dim]; + if (size != null) { + return size; + } + var imageSource = isImageLike(style.image) + ? style.image : this.__image; + if (!imageSource) { + return 0; + } + var otherDim = dim === 'width' ? 'height' : 'width'; + var otherDimSize = style[otherDim]; + if (otherDimSize == null) { + return imageSource[dim]; + } + else { + return imageSource[dim] / imageSource[otherDim] * otherDimSize; + } + }; + ZRImage.prototype.getWidth = function () { + return this._getSize('width'); + }; + ZRImage.prototype.getHeight = function () { + return this._getSize('height'); + }; + ZRImage.prototype.getAnimationStyleProps = function () { + return DEFAULT_IMAGE_ANIMATION_PROPS; + }; + ZRImage.prototype.getBoundingRect = function () { + var style = this.style; + if (!this._rect) { + this._rect = new BoundingRect(style.x || 0, style.y || 0, this.getWidth(), this.getHeight()); + } + return this._rect; + }; + return ZRImage; + }(Displayable)); + ZRImage.prototype.type = 'image'; + + function buildPath(ctx, shape) { + var x = shape.x; + var y = shape.y; + var width = shape.width; + var height = shape.height; + var r = shape.r; + var r1; + var r2; + var r3; + var r4; + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + if (typeof r === 'number') { + r1 = r2 = r3 = r4 = r; + } + else if (r instanceof Array) { + if (r.length === 1) { + r1 = r2 = r3 = r4 = r[0]; + } + else if (r.length === 2) { + r1 = r3 = r[0]; + r2 = r4 = r[1]; + } + else if (r.length === 3) { + r1 = r[0]; + r2 = r4 = r[1]; + r3 = r[2]; + } + else { + r1 = r[0]; + r2 = r[1]; + r3 = r[2]; + r4 = r[3]; + } + } + else { + r1 = r2 = r3 = r4 = 0; + } + var total; + if (r1 + r2 > width) { + total = r1 + r2; + r1 *= width / total; + r2 *= width / total; + } + if (r3 + r4 > width) { + total = r3 + r4; + r3 *= width / total; + r4 *= width / total; + } + if (r2 + r3 > height) { + total = r2 + r3; + r2 *= height / total; + r3 *= height / total; + } + if (r1 + r4 > height) { + total = r1 + r4; + r1 *= height / total; + r4 *= height / total; + } + ctx.moveTo(x + r1, y); + ctx.lineTo(x + width - r2, y); + r2 !== 0 && ctx.arc(x + width - r2, y + r2, r2, -Math.PI / 2, 0); + ctx.lineTo(x + width, y + height - r3); + r3 !== 0 && ctx.arc(x + width - r3, y + height - r3, r3, 0, Math.PI / 2); + ctx.lineTo(x + r4, y + height); + r4 !== 0 && ctx.arc(x + r4, y + height - r4, r4, Math.PI / 2, Math.PI); + ctx.lineTo(x, y + r1); + r1 !== 0 && ctx.arc(x + r1, y + r1, r1, Math.PI, Math.PI * 1.5); + } + + var round$1 = Math.round; + function subPixelOptimizeLine(outputShape, inputShape, style) { + if (!inputShape) { + return; + } + var x1 = inputShape.x1; + var x2 = inputShape.x2; + var y1 = inputShape.y1; + var y2 = inputShape.y2; + outputShape.x1 = x1; + outputShape.x2 = x2; + outputShape.y1 = y1; + outputShape.y2 = y2; + var lineWidth = style && style.lineWidth; + if (!lineWidth) { + return outputShape; + } + if (round$1(x1 * 2) === round$1(x2 * 2)) { + outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true); + } + if (round$1(y1 * 2) === round$1(y2 * 2)) { + outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true); + } + return outputShape; + } + function subPixelOptimizeRect(outputShape, inputShape, style) { + if (!inputShape) { + return; + } + var originX = inputShape.x; + var originY = inputShape.y; + var originWidth = inputShape.width; + var originHeight = inputShape.height; + outputShape.x = originX; + outputShape.y = originY; + outputShape.width = originWidth; + outputShape.height = originHeight; + var lineWidth = style && style.lineWidth; + if (!lineWidth) { + return outputShape; + } + outputShape.x = subPixelOptimize(originX, lineWidth, true); + outputShape.y = subPixelOptimize(originY, lineWidth, true); + outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1); + outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1); + return outputShape; + } + function subPixelOptimize(position, lineWidth, positiveOrNegative) { + if (!lineWidth) { + return position; + } + var doubledPosition = round$1(position * 2); + return (doubledPosition + round$1(lineWidth)) % 2 === 0 + ? doubledPosition / 2 + : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2; + } + + var RectShape = (function () { + function RectShape() { + this.x = 0; + this.y = 0; + this.width = 0; + this.height = 0; + } + return RectShape; + }()); + var subPixelOptimizeOutputShape = {}; + var Rect = (function (_super) { + __extends(Rect, _super); + function Rect(opts) { + return _super.call(this, opts) || this; + } + Rect.prototype.getDefaultShape = function () { + return new RectShape(); + }; + Rect.prototype.buildPath = function (ctx, shape) { + var x; + var y; + var width; + var height; + if (this.subPixelOptimize) { + var optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style); + x = optimizedShape.x; + y = optimizedShape.y; + width = optimizedShape.width; + height = optimizedShape.height; + optimizedShape.r = shape.r; + shape = optimizedShape; + } + else { + x = shape.x; + y = shape.y; + width = shape.width; + height = shape.height; + } + if (!shape.r) { + ctx.rect(x, y, width, height); + } + else { + buildPath(ctx, shape); + } + }; + Rect.prototype.isZeroArea = function () { + return !this.shape.width || !this.shape.height; + }; + return Rect; + }(Path)); + Rect.prototype.type = 'rect'; + + var DEFAULT_RICH_TEXT_COLOR = { + fill: '#000' + }; + var DEFAULT_STROKE_LINE_WIDTH = 2; + var DEFAULT_TEXT_ANIMATION_PROPS = { + style: defaults({ + fill: true, + stroke: true, + fillOpacity: true, + strokeOpacity: true, + lineWidth: true, + fontSize: true, + lineHeight: true, + width: true, + height: true, + textShadowColor: true, + textShadowBlur: true, + textShadowOffsetX: true, + textShadowOffsetY: true, + backgroundColor: true, + padding: true, + borderColor: true, + borderWidth: true, + borderRadius: true + }, DEFAULT_COMMON_ANIMATION_PROPS.style) + }; + var ZRText = (function (_super) { + __extends(ZRText, _super); + function ZRText(opts) { + var _this = _super.call(this) || this; + _this.type = 'text'; + _this._children = []; + _this._defaultStyle = DEFAULT_RICH_TEXT_COLOR; + _this.attr(opts); + return _this; + } + ZRText.prototype.childrenRef = function () { + return this._children; + }; + ZRText.prototype.update = function () { + _super.prototype.update.call(this); + if (this.styleChanged()) { + this._updateSubTexts(); + } + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + child.zlevel = this.zlevel; + child.z = this.z; + child.z2 = this.z2; + child.culling = this.culling; + child.cursor = this.cursor; + child.invisible = this.invisible; + } + }; + ZRText.prototype.updateTransform = function () { + var innerTransformable = this.innerTransformable; + if (innerTransformable) { + innerTransformable.updateTransform(); + if (innerTransformable.transform) { + this.transform = innerTransformable.transform; + } + } + else { + _super.prototype.updateTransform.call(this); + } + }; + ZRText.prototype.getLocalTransform = function (m) { + var innerTransformable = this.innerTransformable; + return innerTransformable + ? innerTransformable.getLocalTransform(m) + : _super.prototype.getLocalTransform.call(this, m); + }; + ZRText.prototype.getComputedTransform = function () { + if (this.__hostTarget) { + this.__hostTarget.getComputedTransform(); + this.__hostTarget.updateInnerText(true); + } + return _super.prototype.getComputedTransform.call(this); + }; + ZRText.prototype._updateSubTexts = function () { + this._childCursor = 0; + normalizeTextStyle(this.style); + this.style.rich + ? this._updateRichTexts() + : this._updatePlainTexts(); + this._children.length = this._childCursor; + this.styleUpdated(); + }; + ZRText.prototype.addSelfToZr = function (zr) { + _super.prototype.addSelfToZr.call(this, zr); + for (var i = 0; i < this._children.length; i++) { + this._children[i].__zr = zr; + } + }; + ZRText.prototype.removeSelfFromZr = function (zr) { + _super.prototype.removeSelfFromZr.call(this, zr); + for (var i = 0; i < this._children.length; i++) { + this._children[i].__zr = null; + } + }; + ZRText.prototype.getBoundingRect = function () { + if (this.styleChanged()) { + this._updateSubTexts(); + } + if (!this._rect) { + var tmpRect = new BoundingRect(0, 0, 0, 0); + var children = this._children; + var tmpMat = []; + var rect = null; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + var childRect = child.getBoundingRect(); + var transform = child.getLocalTransform(tmpMat); + if (transform) { + tmpRect.copy(childRect); + tmpRect.applyTransform(transform); + rect = rect || tmpRect.clone(); + rect.union(tmpRect); + } + else { + rect = rect || childRect.clone(); + rect.union(childRect); + } + } + this._rect = rect || tmpRect; + } + return this._rect; + }; + ZRText.prototype.setDefaultTextStyle = function (defaultTextStyle) { + this._defaultStyle = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR; + }; + ZRText.prototype.setTextContent = function (textContent) { + if ("development" !== 'production') { + throw new Error('Can\'t attach text on another text'); + } + }; + ZRText.prototype._mergeStyle = function (targetStyle, sourceStyle) { + if (!sourceStyle) { + return targetStyle; + } + var sourceRich = sourceStyle.rich; + var targetRich = targetStyle.rich || (sourceRich && {}); + extend(targetStyle, sourceStyle); + if (sourceRich && targetRich) { + this._mergeRich(targetRich, sourceRich); + targetStyle.rich = targetRich; + } + else if (targetRich) { + targetStyle.rich = targetRich; + } + return targetStyle; + }; + ZRText.prototype._mergeRich = function (targetRich, sourceRich) { + var richNames = keys(sourceRich); + for (var i = 0; i < richNames.length; i++) { + var richName = richNames[i]; + targetRich[richName] = targetRich[richName] || {}; + extend(targetRich[richName], sourceRich[richName]); + } + }; + ZRText.prototype.getAnimationStyleProps = function () { + return DEFAULT_TEXT_ANIMATION_PROPS; + }; + ZRText.prototype._getOrCreateChild = function (Ctor) { + var child = this._children[this._childCursor]; + if (!child || !(child instanceof Ctor)) { + child = new Ctor(); + } + this._children[this._childCursor++] = child; + child.__zr = this.__zr; + child.parent = this; + return child; + }; + ZRText.prototype._updatePlainTexts = function () { + var style = this.style; + var textFont = style.font || DEFAULT_FONT; + var textPadding = style.padding; + var text = getStyleText(style); + var contentBlock = parsePlainText(text, style); + var needDrawBg = needDrawBackground(style); + var bgColorDrawn = !!(style.backgroundColor); + var outerHeight = contentBlock.outerHeight; + var outerWidth = contentBlock.outerWidth; + var contentWidth = contentBlock.contentWidth; + var textLines = contentBlock.lines; + var lineHeight = contentBlock.lineHeight; + var defaultStyle = this._defaultStyle; + var baseX = style.x || 0; + var baseY = style.y || 0; + var textAlign = style.align || defaultStyle.align || 'left'; + var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign || 'top'; + var textX = baseX; + var textY = adjustTextY$1(baseY, contentBlock.contentHeight, verticalAlign); + if (needDrawBg || textPadding) { + var boxX = adjustTextX(baseX, outerWidth, textAlign); + var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign); + needDrawBg && this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight); + } + textY += lineHeight / 2; + if (textPadding) { + textX = getTextXForPadding(baseX, textAlign, textPadding); + if (verticalAlign === 'top') { + textY += textPadding[0]; + } + else if (verticalAlign === 'bottom') { + textY -= textPadding[2]; + } + } + var defaultLineWidth = 0; + var useDefaultFill = false; + var textFill = getFill('fill' in style + ? style.fill + : (useDefaultFill = true, defaultStyle.fill)); + var textStroke = getStroke('stroke' in style + ? style.stroke + : (!bgColorDrawn + && (!defaultStyle.autoStroke || useDefaultFill)) + ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke) + : null); + var hasShadow = style.textShadowBlur > 0; + var fixedBoundingRect = style.width != null + && (style.overflow === 'truncate' || style.overflow === 'break' || style.overflow === 'breakAll'); + var calculatedLineHeight = contentBlock.calculatedLineHeight; + for (var i = 0; i < textLines.length; i++) { + var el = this._getOrCreateChild(TSpan); + var subElStyle = el.createStyle(); + el.useStyle(subElStyle); + subElStyle.text = textLines[i]; + subElStyle.x = textX; + subElStyle.y = textY; + if (textAlign) { + subElStyle.textAlign = textAlign; + } + subElStyle.textBaseline = 'middle'; + subElStyle.opacity = style.opacity; + subElStyle.strokeFirst = true; + if (hasShadow) { + subElStyle.shadowBlur = style.textShadowBlur || 0; + subElStyle.shadowColor = style.textShadowColor || 'transparent'; + subElStyle.shadowOffsetX = style.textShadowOffsetX || 0; + subElStyle.shadowOffsetY = style.textShadowOffsetY || 0; + } + subElStyle.stroke = textStroke; + subElStyle.fill = textFill; + if (textStroke) { + subElStyle.lineWidth = style.lineWidth || defaultLineWidth; + subElStyle.lineDash = style.lineDash; + subElStyle.lineDashOffset = style.lineDashOffset || 0; + } + subElStyle.font = textFont; + setSeparateFont(subElStyle, style); + textY += lineHeight; + if (fixedBoundingRect) { + el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, style.width, subElStyle.textAlign), adjustTextY$1(subElStyle.y, calculatedLineHeight, subElStyle.textBaseline), contentWidth, calculatedLineHeight)); + } + } + }; + ZRText.prototype._updateRichTexts = function () { + var style = this.style; + var text = getStyleText(style); + var contentBlock = parseRichText(text, style); + var contentWidth = contentBlock.width; + var outerWidth = contentBlock.outerWidth; + var outerHeight = contentBlock.outerHeight; + var textPadding = style.padding; + var baseX = style.x || 0; + var baseY = style.y || 0; + var defaultStyle = this._defaultStyle; + var textAlign = style.align || defaultStyle.align; + var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign; + var boxX = adjustTextX(baseX, outerWidth, textAlign); + var boxY = adjustTextY$1(baseY, outerHeight, verticalAlign); + var xLeft = boxX; + var lineTop = boxY; + if (textPadding) { + xLeft += textPadding[3]; + lineTop += textPadding[0]; + } + var xRight = xLeft + contentWidth; + if (needDrawBackground(style)) { + this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight); + } + var bgColorDrawn = !!(style.backgroundColor); + for (var i = 0; i < contentBlock.lines.length; i++) { + var line = contentBlock.lines[i]; + var tokens = line.tokens; + var tokenCount = tokens.length; + var lineHeight = line.lineHeight; + var remainedWidth = line.width; + var leftIndex = 0; + var lineXLeft = xLeft; + var lineXRight = xRight; + var rightIndex = tokenCount - 1; + var token = void 0; + while (leftIndex < tokenCount + && (token = tokens[leftIndex], !token.align || token.align === 'left')) { + this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left', bgColorDrawn); + remainedWidth -= token.width; + lineXLeft += token.width; + leftIndex++; + } + while (rightIndex >= 0 + && (token = tokens[rightIndex], token.align === 'right')) { + this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right', bgColorDrawn); + remainedWidth -= token.width; + lineXRight -= token.width; + rightIndex--; + } + lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - remainedWidth) / 2; + while (leftIndex <= rightIndex) { + token = tokens[leftIndex]; + this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center', bgColorDrawn); + lineXLeft += token.width; + leftIndex++; + } + lineTop += lineHeight; + } + }; + ZRText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign, parentBgColorDrawn) { + var tokenStyle = style.rich[token.styleName] || {}; + tokenStyle.text = token.text; + var verticalAlign = token.verticalAlign; + var y = lineTop + lineHeight / 2; + if (verticalAlign === 'top') { + y = lineTop + token.height / 2; + } + else if (verticalAlign === 'bottom') { + y = lineTop + lineHeight - token.height / 2; + } + var needDrawBg = !token.isLineHolder && needDrawBackground(tokenStyle); + needDrawBg && this._renderBackground(tokenStyle, style, textAlign === 'right' + ? x - token.width + : textAlign === 'center' + ? x - token.width / 2 + : x, y - token.height / 2, token.width, token.height); + var bgColorDrawn = !!tokenStyle.backgroundColor; + var textPadding = token.textPadding; + if (textPadding) { + x = getTextXForPadding(x, textAlign, textPadding); + y -= token.height / 2 - textPadding[0] - token.innerHeight / 2; + } + var el = this._getOrCreateChild(TSpan); + var subElStyle = el.createStyle(); + el.useStyle(subElStyle); + var defaultStyle = this._defaultStyle; + var useDefaultFill = false; + var defaultLineWidth = 0; + var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill + : 'fill' in style ? style.fill + : (useDefaultFill = true, defaultStyle.fill)); + var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke + : 'stroke' in style ? style.stroke + : (!bgColorDrawn + && !parentBgColorDrawn + && (!defaultStyle.autoStroke || useDefaultFill)) ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke) + : null); + var hasShadow = tokenStyle.textShadowBlur > 0 + || style.textShadowBlur > 0; + subElStyle.text = token.text; + subElStyle.x = x; + subElStyle.y = y; + if (hasShadow) { + subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0; + subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent'; + subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0; + subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0; + } + subElStyle.textAlign = textAlign; + subElStyle.textBaseline = 'middle'; + subElStyle.font = token.font || DEFAULT_FONT; + subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1); + setSeparateFont(subElStyle, tokenStyle); + if (textStroke) { + subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth); + subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash); + subElStyle.lineDashOffset = style.lineDashOffset || 0; + subElStyle.stroke = textStroke; + } + if (textFill) { + subElStyle.fill = textFill; + } + var textWidth = token.contentWidth; + var textHeight = token.contentHeight; + el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, textWidth, subElStyle.textAlign), adjustTextY$1(subElStyle.y, textHeight, subElStyle.textBaseline), textWidth, textHeight)); + }; + ZRText.prototype._renderBackground = function (style, topStyle, x, y, width, height) { + var textBackgroundColor = style.backgroundColor; + var textBorderWidth = style.borderWidth; + var textBorderColor = style.borderColor; + var isImageBg = textBackgroundColor && textBackgroundColor.image; + var isPlainOrGradientBg = textBackgroundColor && !isImageBg; + var textBorderRadius = style.borderRadius; + var self = this; + var rectEl; + var imgEl; + if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) { + rectEl = this._getOrCreateChild(Rect); + rectEl.useStyle(rectEl.createStyle()); + rectEl.style.fill = null; + var rectShape = rectEl.shape; + rectShape.x = x; + rectShape.y = y; + rectShape.width = width; + rectShape.height = height; + rectShape.r = textBorderRadius; + rectEl.dirtyShape(); + } + if (isPlainOrGradientBg) { + var rectStyle = rectEl.style; + rectStyle.fill = textBackgroundColor || null; + rectStyle.fillOpacity = retrieve2(style.fillOpacity, 1); + } + else if (isImageBg) { + imgEl = this._getOrCreateChild(ZRImage); + imgEl.onload = function () { + self.dirtyStyle(); + }; + var imgStyle = imgEl.style; + imgStyle.image = textBackgroundColor.image; + imgStyle.x = x; + imgStyle.y = y; + imgStyle.width = width; + imgStyle.height = height; + } + if (textBorderWidth && textBorderColor) { + var rectStyle = rectEl.style; + rectStyle.lineWidth = textBorderWidth; + rectStyle.stroke = textBorderColor; + rectStyle.strokeOpacity = retrieve2(style.strokeOpacity, 1); + rectStyle.lineDash = style.borderDash; + rectStyle.lineDashOffset = style.borderDashOffset || 0; + rectEl.strokeContainThreshold = 0; + if (rectEl.hasFill() && rectEl.hasStroke()) { + rectStyle.strokeFirst = true; + rectStyle.lineWidth *= 2; + } + } + var commonStyle = (rectEl || imgEl).style; + commonStyle.shadowBlur = style.shadowBlur || 0; + commonStyle.shadowColor = style.shadowColor || 'transparent'; + commonStyle.shadowOffsetX = style.shadowOffsetX || 0; + commonStyle.shadowOffsetY = style.shadowOffsetY || 0; + commonStyle.opacity = retrieve3(style.opacity, topStyle.opacity, 1); + }; + ZRText.makeFont = function (style) { + var font = ''; + if (hasSeparateFont(style)) { + font = [ + style.fontStyle, + style.fontWeight, + parseFontSize(style.fontSize), + style.fontFamily || 'sans-serif' + ].join(' '); + } + return font && trim(font) || style.textFont || style.font; + }; + return ZRText; + }(Displayable)); + var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 }; + var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 }; + var FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily']; + function parseFontSize(fontSize) { + if (typeof fontSize === 'string' + && (fontSize.indexOf('px') !== -1 + || fontSize.indexOf('rem') !== -1 + || fontSize.indexOf('em') !== -1)) { + return fontSize; + } + else if (!isNaN(+fontSize)) { + return fontSize + 'px'; + } + else { + return DEFAULT_FONT_SIZE + 'px'; + } + } + function setSeparateFont(targetStyle, sourceStyle) { + for (var i = 0; i < FONT_PARTS.length; i++) { + var fontProp = FONT_PARTS[i]; + var val = sourceStyle[fontProp]; + if (val != null) { + targetStyle[fontProp] = val; + } + } + } + function hasSeparateFont(style) { + return style.fontSize != null || style.fontFamily || style.fontWeight; + } + function normalizeTextStyle(style) { + normalizeStyle(style); + each(style.rich, normalizeStyle); + return style; + } + function normalizeStyle(style) { + if (style) { + style.font = ZRText.makeFont(style); + var textAlign = style.align; + textAlign === 'middle' && (textAlign = 'center'); + style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left'; + var verticalAlign = style.verticalAlign; + verticalAlign === 'center' && (verticalAlign = 'middle'); + style.verticalAlign = (verticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[verticalAlign]) ? verticalAlign : 'top'; + var textPadding = style.padding; + if (textPadding) { + style.padding = normalizeCssArray(style.padding); + } + } + } + function getStroke(stroke, lineWidth) { + return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none') + ? null + : (stroke.image || stroke.colorStops) + ? '#000' + : stroke; + } + function getFill(fill) { + return (fill == null || fill === 'none') + ? null + : (fill.image || fill.colorStops) + ? '#000' + : fill; + } + function getTextXForPadding(x, textAlign, textPadding) { + return textAlign === 'right' + ? (x - textPadding[1]) + : textAlign === 'center' + ? (x + textPadding[3] / 2 - textPadding[1] / 2) + : (x + textPadding[3]); + } + function getStyleText(style) { + var text = style.text; + text != null && (text += ''); + return text; + } + function needDrawBackground(style) { + return !!(style.backgroundColor + || style.lineHeight + || (style.borderWidth && style.borderColor)); + } + + var getECData = makeInner(); + var setCommonECData = function (seriesIndex, dataType, dataIdx, el) { + if (el) { + var ecData = getECData(el); + // Add data index and series index for indexing the data by element + // Useful in tooltip + ecData.dataIndex = dataIdx; + ecData.dataType = dataType; + ecData.seriesIndex = seriesIndex; + ecData.ssrType = 'chart'; + // TODO: not store dataIndex on children. + if (el.type === 'group') { + el.traverse(function (child) { + var childECData = getECData(child); + childECData.seriesIndex = seriesIndex; + childECData.dataIndex = dataIdx; + childECData.dataType = dataType; + childECData.ssrType = 'chart'; + }); + } + } + }; + + // Reserve 0 as default. + var _highlightNextDigit = 1; + var _highlightKeyMap = {}; + var getSavedStates = makeInner(); + var getComponentStates = makeInner(); + var HOVER_STATE_NORMAL = 0; + var HOVER_STATE_BLUR = 1; + var HOVER_STATE_EMPHASIS = 2; + var SPECIAL_STATES = ['emphasis', 'blur', 'select']; + var DISPLAY_STATES = ['normal', 'emphasis', 'blur', 'select']; + var Z2_EMPHASIS_LIFT = 10; + var Z2_SELECT_LIFT = 9; + var HIGHLIGHT_ACTION_TYPE = 'highlight'; + var DOWNPLAY_ACTION_TYPE = 'downplay'; + var SELECT_ACTION_TYPE = 'select'; + var UNSELECT_ACTION_TYPE = 'unselect'; + var TOGGLE_SELECT_ACTION_TYPE = 'toggleSelect'; + function hasFillOrStroke(fillOrStroke) { + return fillOrStroke != null && fillOrStroke !== 'none'; + } + function doChangeHoverState(el, stateName, hoverStateEnum) { + if (el.onHoverStateChange && (el.hoverState || 0) !== hoverStateEnum) { + el.onHoverStateChange(stateName); + } + el.hoverState = hoverStateEnum; + } + function singleEnterEmphasis(el) { + // Only mark the flag. + // States will be applied in the echarts.ts in next frame. + doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS); + } + function singleLeaveEmphasis(el) { + // Only mark the flag. + // States will be applied in the echarts.ts in next frame. + if (el.hoverState === HOVER_STATE_EMPHASIS) { + doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL); + } + } + function singleEnterBlur(el) { + doChangeHoverState(el, 'blur', HOVER_STATE_BLUR); + } + function singleLeaveBlur(el) { + if (el.hoverState === HOVER_STATE_BLUR) { + doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL); + } + } + function singleEnterSelect(el) { + el.selected = true; + } + function singleLeaveSelect(el) { + el.selected = false; + } + function updateElementState(el, updater, commonParam) { + updater(el, commonParam); + } + function traverseUpdateState(el, updater, commonParam) { + updateElementState(el, updater, commonParam); + el.isGroup && el.traverse(function (child) { + updateElementState(child, updater, commonParam); + }); + } + function setStatesFlag(el, stateName) { + switch (stateName) { + case 'emphasis': + el.hoverState = HOVER_STATE_EMPHASIS; + break; + case 'normal': + el.hoverState = HOVER_STATE_NORMAL; + break; + case 'blur': + el.hoverState = HOVER_STATE_BLUR; + break; + case 'select': + el.selected = true; + } + } + function getFromStateStyle(el, props, toStateName, defaultValue) { + var style = el.style; + var fromState = {}; + for (var i = 0; i < props.length; i++) { + var propName = props[i]; + var val = style[propName]; + fromState[propName] = val == null ? defaultValue && defaultValue[propName] : val; + } + for (var i = 0; i < el.animators.length; i++) { + var animator = el.animators[i]; + if (animator.__fromStateTransition + // Don't consider the animation to emphasis state. + && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') { + animator.saveTo(fromState, props); + } + } + return fromState; + } + function createEmphasisDefaultState(el, stateName, targetStates, state) { + var hasSelect = targetStates && indexOf(targetStates, 'select') >= 0; + var cloned = false; + if (el instanceof Path) { + var store = getSavedStates(el); + var fromFill = hasSelect ? store.selectFill || store.normalFill : store.normalFill; + var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke; + if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) { + state = state || {}; + var emphasisStyle = state.style || {}; + // inherit case + if (emphasisStyle.fill === 'inherit') { + cloned = true; + state = extend({}, state); + emphasisStyle = extend({}, emphasisStyle); + emphasisStyle.fill = fromFill; + } + // Apply default color lift + else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) { + cloned = true; + // Not modify the original value. + state = extend({}, state); + emphasisStyle = extend({}, emphasisStyle); + // Already being applied 'emphasis'. DON'T lift color multiple times. + emphasisStyle.fill = liftColor(fromFill); + } + // Not highlight stroke if fill has been highlighted. + else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) { + if (!cloned) { + state = extend({}, state); + emphasisStyle = extend({}, emphasisStyle); + } + emphasisStyle.stroke = liftColor(fromStroke); + } + state.style = emphasisStyle; + } + } + if (state) { + // TODO Share with textContent? + if (state.z2 == null) { + if (!cloned) { + state = extend({}, state); + } + var z2EmphasisLift = el.z2EmphasisLift; + state.z2 = el.z2 + (z2EmphasisLift != null ? z2EmphasisLift : Z2_EMPHASIS_LIFT); + } + } + return state; + } + function createSelectDefaultState(el, stateName, state) { + // const hasSelect = indexOf(el.currentStates, stateName) >= 0; + if (state) { + // TODO Share with textContent? + if (state.z2 == null) { + state = extend({}, state); + var z2SelectLift = el.z2SelectLift; + state.z2 = el.z2 + (z2SelectLift != null ? z2SelectLift : Z2_SELECT_LIFT); + } + } + return state; + } + function createBlurDefaultState(el, stateName, state) { + var hasBlur = indexOf(el.currentStates, stateName) >= 0; + var currentOpacity = el.style.opacity; + var fromState = !hasBlur ? getFromStateStyle(el, ['opacity'], stateName, { + opacity: 1 + }) : null; + state = state || {}; + var blurStyle = state.style || {}; + if (blurStyle.opacity == null) { + // clone state + state = extend({}, state); + blurStyle = extend({ + // Already being applied 'emphasis'. DON'T mul opacity multiple times. + opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1 + }, blurStyle); + state.style = blurStyle; + } + return state; + } + function elementStateProxy(stateName, targetStates) { + var state = this.states[stateName]; + if (this.style) { + if (stateName === 'emphasis') { + return createEmphasisDefaultState(this, stateName, targetStates, state); + } else if (stateName === 'blur') { + return createBlurDefaultState(this, stateName, state); + } else if (stateName === 'select') { + return createSelectDefaultState(this, stateName, state); + } + } + return state; + } + /** + * Set hover style (namely "emphasis style") of element. + * @param el Should not be `zrender/graphic/Group`. + * @param focus 'self' | 'selfInSeries' | 'series' + */ + function setDefaultStateProxy(el) { + el.stateProxy = elementStateProxy; + var textContent = el.getTextContent(); + var textGuide = el.getTextGuideLine(); + if (textContent) { + textContent.stateProxy = elementStateProxy; + } + if (textGuide) { + textGuide.stateProxy = elementStateProxy; + } + } + function enterEmphasisWhenMouseOver(el, e) { + !shouldSilent(el, e) + // "emphasis" event highlight has higher priority than mouse highlight. + && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis); + } + function leaveEmphasisWhenMouseOut(el, e) { + !shouldSilent(el, e) + // "emphasis" event highlight has higher priority than mouse highlight. + && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis); + } + function enterEmphasis(el, highlightDigit) { + el.__highByOuter |= 1 << (highlightDigit || 0); + traverseUpdateState(el, singleEnterEmphasis); + } + function leaveEmphasis(el, highlightDigit) { + !(el.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdateState(el, singleLeaveEmphasis); + } + function enterBlur(el) { + traverseUpdateState(el, singleEnterBlur); + } + function leaveBlur(el) { + traverseUpdateState(el, singleLeaveBlur); + } + function enterSelect(el) { + traverseUpdateState(el, singleEnterSelect); + } + function leaveSelect(el) { + traverseUpdateState(el, singleLeaveSelect); + } + function shouldSilent(el, e) { + return el.__highDownSilentOnTouch && e.zrByTouch; + } + function allLeaveBlur(api) { + var model = api.getModel(); + var leaveBlurredSeries = []; + var allComponentViews = []; + model.eachComponent(function (componentType, componentModel) { + var componentStates = getComponentStates(componentModel); + var isSeries = componentType === 'series'; + var view = isSeries ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel); + !isSeries && allComponentViews.push(view); + if (componentStates.isBlured) { + // Leave blur anyway + view.group.traverse(function (child) { + singleLeaveBlur(child); + }); + isSeries && leaveBlurredSeries.push(componentModel); + } + componentStates.isBlured = false; + }); + each(allComponentViews, function (view) { + if (view && view.toggleBlurSeries) { + view.toggleBlurSeries(leaveBlurredSeries, false, model); + } + }); + } + function blurSeries(targetSeriesIndex, focus, blurScope, api) { + var ecModel = api.getModel(); + blurScope = blurScope || 'coordinateSystem'; + function leaveBlurOfIndices(data, dataIndices) { + for (var i = 0; i < dataIndices.length; i++) { + var itemEl = data.getItemGraphicEl(dataIndices[i]); + itemEl && leaveBlur(itemEl); + } + } + if (targetSeriesIndex == null) { + return; + } + if (!focus || focus === 'none') { + return; + } + var targetSeriesModel = ecModel.getSeriesByIndex(targetSeriesIndex); + var targetCoordSys = targetSeriesModel.coordinateSystem; + if (targetCoordSys && targetCoordSys.master) { + targetCoordSys = targetCoordSys.master; + } + var blurredSeries = []; + ecModel.eachSeries(function (seriesModel) { + var sameSeries = targetSeriesModel === seriesModel; + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.master) { + coordSys = coordSys.master; + } + var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead. + if (!( + // Not blur other series if blurScope series + blurScope === 'series' && !sameSeries + // Not blur other coordinate system if blurScope is coordinateSystem + || blurScope === 'coordinateSystem' && !sameCoordSys + // Not blur self series if focus is series. + || focus === 'series' && sameSeries + // TODO blurScope: coordinate system + )) { + var view = api.getViewOfSeriesModel(seriesModel); + view.group.traverse(function (child) { + // For the elements that have been triggered by other components, + // and are still required to be highlighted, + // because the current is directly forced to blur the element, + // it will cause the focus self to be unable to highlight, so skip the blur of this element. + if (child.__highByOuter && sameSeries && focus === 'self') { + return; + } + singleEnterBlur(child); + }); + if (isArrayLike(focus)) { + leaveBlurOfIndices(seriesModel.getData(), focus); + } else if (isObject(focus)) { + var dataTypes = keys(focus); + for (var d = 0; d < dataTypes.length; d++) { + leaveBlurOfIndices(seriesModel.getData(dataTypes[d]), focus[dataTypes[d]]); + } + } + blurredSeries.push(seriesModel); + getComponentStates(seriesModel).isBlured = true; + } + }); + ecModel.eachComponent(function (componentType, componentModel) { + if (componentType === 'series') { + return; + } + var view = api.getViewOfComponentModel(componentModel); + if (view && view.toggleBlurSeries) { + view.toggleBlurSeries(blurredSeries, true, ecModel); + } + }); + } + function blurComponent(componentMainType, componentIndex, api) { + if (componentMainType == null || componentIndex == null) { + return; + } + var componentModel = api.getModel().getComponent(componentMainType, componentIndex); + if (!componentModel) { + return; + } + getComponentStates(componentModel).isBlured = true; + var view = api.getViewOfComponentModel(componentModel); + if (!view || !view.focusBlurEnabled) { + return; + } + view.group.traverse(function (child) { + singleEnterBlur(child); + }); + } + function blurSeriesFromHighlightPayload(seriesModel, payload, api) { + var seriesIndex = seriesModel.seriesIndex; + var data = seriesModel.getData(payload.dataType); + if (!data) { + if ("development" !== 'production') { + error("Unknown dataType " + payload.dataType); + } + return; + } + var dataIndex = queryDataIndex(data, payload); + // Pick the first one if there is multiple/none exists. + dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0; + var el = data.getItemGraphicEl(dataIndex); + if (!el) { + var count = data.count(); + var current = 0; + // If data on dataIndex is NaN. + while (!el && current < count) { + el = data.getItemGraphicEl(current++); + } + } + if (el) { + var ecData = getECData(el); + blurSeries(seriesIndex, ecData.focus, ecData.blurScope, api); + } else { + // If there is no element put on the data. Try getting it from raw option + // TODO Should put it on seriesModel? + var focus_1 = seriesModel.get(['emphasis', 'focus']); + var blurScope = seriesModel.get(['emphasis', 'blurScope']); + if (focus_1 != null) { + blurSeries(seriesIndex, focus_1, blurScope, api); + } + } + } + function findComponentHighDownDispatchers(componentMainType, componentIndex, name, api) { + var ret = { + focusSelf: false, + dispatchers: null + }; + if (componentMainType == null || componentMainType === 'series' || componentIndex == null || name == null) { + return ret; + } + var componentModel = api.getModel().getComponent(componentMainType, componentIndex); + if (!componentModel) { + return ret; + } + var view = api.getViewOfComponentModel(componentModel); + if (!view || !view.findHighDownDispatchers) { + return ret; + } + var dispatchers = view.findHighDownDispatchers(name); + // At presnet, the component (like Geo) only blur inside itself. + // So we do not use `blurScope` in component. + var focusSelf; + for (var i = 0; i < dispatchers.length; i++) { + if ("development" !== 'production' && !isHighDownDispatcher(dispatchers[i])) { + error('param should be highDownDispatcher'); + } + if (getECData(dispatchers[i]).focus === 'self') { + focusSelf = true; + break; + } + } + return { + focusSelf: focusSelf, + dispatchers: dispatchers + }; + } + function handleGlobalMouseOverForHighDown(dispatcher, e, api) { + if ("development" !== 'production' && !isHighDownDispatcher(dispatcher)) { + error('param should be highDownDispatcher'); + } + var ecData = getECData(dispatcher); + var _a = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api), + dispatchers = _a.dispatchers, + focusSelf = _a.focusSelf; + // If `findHighDownDispatchers` is supported on the component, + // highlight/downplay elements with the same name. + if (dispatchers) { + if (focusSelf) { + blurComponent(ecData.componentMainType, ecData.componentIndex, api); + } + each(dispatchers, function (dispatcher) { + return enterEmphasisWhenMouseOver(dispatcher, e); + }); + } else { + // Try blur all in the related series. Then emphasis the hoverred. + // TODO. progressive mode. + blurSeries(ecData.seriesIndex, ecData.focus, ecData.blurScope, api); + if (ecData.focus === 'self') { + blurComponent(ecData.componentMainType, ecData.componentIndex, api); + } + // Other than series, component that not support `findHighDownDispatcher` will + // also use it. But in this case, highlight/downplay are only supported in + // mouse hover but not in dispatchAction. + enterEmphasisWhenMouseOver(dispatcher, e); + } + } + function handleGlobalMouseOutForHighDown(dispatcher, e, api) { + if ("development" !== 'production' && !isHighDownDispatcher(dispatcher)) { + error('param should be highDownDispatcher'); + } + allLeaveBlur(api); + var ecData = getECData(dispatcher); + var dispatchers = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api).dispatchers; + if (dispatchers) { + each(dispatchers, function (dispatcher) { + return leaveEmphasisWhenMouseOut(dispatcher, e); + }); + } else { + leaveEmphasisWhenMouseOut(dispatcher, e); + } + } + function toggleSelectionFromPayload(seriesModel, payload, api) { + if (!isSelectChangePayload(payload)) { + return; + } + var dataType = payload.dataType; + var data = seriesModel.getData(dataType); + var dataIndex = queryDataIndex(data, payload); + if (!isArray(dataIndex)) { + dataIndex = [dataIndex]; + } + seriesModel[payload.type === TOGGLE_SELECT_ACTION_TYPE ? 'toggleSelect' : payload.type === SELECT_ACTION_TYPE ? 'select' : 'unselect'](dataIndex, dataType); + } + function updateSeriesElementSelection(seriesModel) { + var allData = seriesModel.getAllData(); + each(allData, function (_a) { + var data = _a.data, + type = _a.type; + data.eachItemGraphicEl(function (el, idx) { + seriesModel.isSelected(idx, type) ? enterSelect(el) : leaveSelect(el); + }); + }); + } + function getAllSelectedIndices(ecModel) { + var ret = []; + ecModel.eachSeries(function (seriesModel) { + var allData = seriesModel.getAllData(); + each(allData, function (_a) { + var data = _a.data, + type = _a.type; + var dataIndices = seriesModel.getSelectedDataIndices(); + if (dataIndices.length > 0) { + var item = { + dataIndex: dataIndices, + seriesIndex: seriesModel.seriesIndex + }; + if (type != null) { + item.dataType = type; + } + ret.push(item); + } + }); + }); + return ret; + } + /** + * Enable the function that mouseover will trigger the emphasis state. + * + * NOTE: + * This function should be used on the element with dataIndex, seriesIndex. + * + */ + function enableHoverEmphasis(el, focus, blurScope) { + setAsHighDownDispatcher(el, true); + traverseUpdateState(el, setDefaultStateProxy); + enableHoverFocus(el, focus, blurScope); + } + function disableHoverEmphasis(el) { + setAsHighDownDispatcher(el, false); + } + function toggleHoverEmphasis(el, focus, blurScope, isDisabled) { + isDisabled ? disableHoverEmphasis(el) : enableHoverEmphasis(el, focus, blurScope); + } + function enableHoverFocus(el, focus, blurScope) { + var ecData = getECData(el); + if (focus != null) { + // TODO dataIndex may be set after this function. This check is not useful. + // if (ecData.dataIndex == null) { + // if (__DEV__) { + // console.warn('focus can only been set on element with dataIndex'); + // } + // } + // else { + ecData.focus = focus; + ecData.blurScope = blurScope; + // } + } else if (ecData.focus) { + ecData.focus = null; + } + } + var OTHER_STATES = ['emphasis', 'blur', 'select']; + var defaultStyleGetterMap = { + itemStyle: 'getItemStyle', + lineStyle: 'getLineStyle', + areaStyle: 'getAreaStyle' + }; + /** + * Set emphasis/blur/selected states of element. + */ + function setStatesStylesFromModel(el, itemModel, styleType, + // default itemStyle + getter) { + styleType = styleType || 'itemStyle'; + for (var i = 0; i < OTHER_STATES.length; i++) { + var stateName = OTHER_STATES[i]; + var model = itemModel.getModel([stateName, styleType]); + var state = el.ensureState(stateName); + // Let it throw error if getterType is not found. + state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]](); + } + } + /** + * + * Set element as highlight / downplay dispatcher. + * It will be checked when element received mouseover event or from highlight action. + * It's in change of all highlight/downplay behavior of it's children. + * + * @param el + * @param el.highDownSilentOnTouch + * In touch device, mouseover event will be trigger on touchstart event + * (see module:zrender/dom/HandlerProxy). By this mechanism, we can + * conveniently use hoverStyle when tap on touch screen without additional + * code for compatibility. + * But if the chart/component has select feature, which usually also use + * hoverStyle, there might be conflict between 'select-highlight' and + * 'hover-highlight' especially when roam is enabled (see geo for example). + * In this case, `highDownSilentOnTouch` should be used to disable + * hover-highlight on touch device. + * @param asDispatcher If `false`, do not set as "highDownDispatcher". + */ + function setAsHighDownDispatcher(el, asDispatcher) { + var disable = asDispatcher === false; + var extendedEl = el; + // Make `highDownSilentOnTouch` and `onStateChange` only work after + // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly. + if (el.highDownSilentOnTouch) { + extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch; + } + // Simple optimize, since this method might be + // called for each elements of a group in some cases. + if (!disable || extendedEl.__highDownDispatcher) { + // Emphasis, normal can be triggered manually by API or other components like hover link. + // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent); + // Also keep previous record. + extendedEl.__highByOuter = extendedEl.__highByOuter || 0; + extendedEl.__highDownDispatcher = !disable; + } + } + function isHighDownDispatcher(el) { + return !!(el && el.__highDownDispatcher); + } + /** + * Enable component highlight/downplay features: + * + hover link (within the same name) + * + focus blur in component + */ + function enableComponentHighDownFeatures(el, componentModel, componentHighDownName) { + var ecData = getECData(el); + ecData.componentMainType = componentModel.mainType; + ecData.componentIndex = componentModel.componentIndex; + ecData.componentHighDownName = componentHighDownName; + } + /** + * Support highlight/downplay record on each elements. + * For the case: hover highlight/downplay (legend, visualMap, ...) and + * user triggered highlight/downplay should not conflict. + * Only all of the highlightDigit cleared, return to normal. + * @param {string} highlightKey + * @return {number} highlightDigit + */ + function getHighlightDigit(highlightKey) { + var highlightDigit = _highlightKeyMap[highlightKey]; + if (highlightDigit == null && _highlightNextDigit <= 32) { + highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++; + } + return highlightDigit; + } + function isSelectChangePayload(payload) { + var payloadType = payload.type; + return payloadType === SELECT_ACTION_TYPE || payloadType === UNSELECT_ACTION_TYPE || payloadType === TOGGLE_SELECT_ACTION_TYPE; + } + function isHighDownPayload(payload) { + var payloadType = payload.type; + return payloadType === HIGHLIGHT_ACTION_TYPE || payloadType === DOWNPLAY_ACTION_TYPE; + } + function savePathStates(el) { + var store = getSavedStates(el); + store.normalFill = el.style.fill; + store.normalStroke = el.style.stroke; + var selectState = el.states.select || {}; + store.selectFill = selectState.style && selectState.style.fill || null; + store.selectStroke = selectState.style && selectState.style.stroke || null; + } + + var CMD$2 = PathProxy.CMD; + var points = [[], [], []]; + var mathSqrt$1 = Math.sqrt; + var mathAtan2 = Math.atan2; + function transformPath(path, m) { + if (!m) { + return; + } + var data = path.data; + var len = path.len(); + var cmd; + var nPoint; + var i; + var j; + var k; + var p; + var M = CMD$2.M; + var C = CMD$2.C; + var L = CMD$2.L; + var R = CMD$2.R; + var A = CMD$2.A; + var Q = CMD$2.Q; + for (i = 0, j = 0; i < len;) { + cmd = data[i++]; + j = i; + nPoint = 0; + switch (cmd) { + case M: + nPoint = 1; + break; + case L: + nPoint = 1; + break; + case C: + nPoint = 3; + break; + case Q: + nPoint = 2; + break; + case A: + var x = m[4]; + var y = m[5]; + var sx = mathSqrt$1(m[0] * m[0] + m[1] * m[1]); + var sy = mathSqrt$1(m[2] * m[2] + m[3] * m[3]); + var angle = mathAtan2(-m[1] / sy, m[0] / sx); + data[i] *= sx; + data[i++] += x; + data[i] *= sy; + data[i++] += y; + data[i++] *= sx; + data[i++] *= sy; + data[i++] += angle; + data[i++] += angle; + i += 2; + j = i; + break; + case R: + p[0] = data[i++]; + p[1] = data[i++]; + applyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + p[0] += data[i++]; + p[1] += data[i++]; + applyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + } + for (k = 0; k < nPoint; k++) { + var p_1 = points[k]; + p_1[0] = data[i++]; + p_1[1] = data[i++]; + applyTransform(p_1, p_1, m); + data[j++] = p_1[0]; + data[j++] = p_1[1]; + } + } + path.increaseVersion(); + } + + var mathSqrt$2 = Math.sqrt; + var mathSin$2 = Math.sin; + var mathCos$2 = Math.cos; + var PI$1 = Math.PI; + function vMag(v) { + return Math.sqrt(v[0] * v[0] + v[1] * v[1]); + } + function vRatio(u, v) { + return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v)); + } + function vAngle(u, v) { + return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) + * Math.acos(vRatio(u, v)); + } + function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) { + var psi = psiDeg * (PI$1 / 180.0); + var xp = mathCos$2(psi) * (x1 - x2) / 2.0 + + mathSin$2(psi) * (y1 - y2) / 2.0; + var yp = -1 * mathSin$2(psi) * (x1 - x2) / 2.0 + + mathCos$2(psi) * (y1 - y2) / 2.0; + var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry); + if (lambda > 1) { + rx *= mathSqrt$2(lambda); + ry *= mathSqrt$2(lambda); + } + var f = (fa === fs ? -1 : 1) + * mathSqrt$2((((rx * rx) * (ry * ry)) + - ((rx * rx) * (yp * yp)) + - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + + (ry * ry) * (xp * xp))) || 0; + var cxp = f * rx * yp / ry; + var cyp = f * -ry * xp / rx; + var cx = (x1 + x2) / 2.0 + + mathCos$2(psi) * cxp + - mathSin$2(psi) * cyp; + var cy = (y1 + y2) / 2.0 + + mathSin$2(psi) * cxp + + mathCos$2(psi) * cyp; + var theta = vAngle([1, 0], [(xp - cxp) / rx, (yp - cyp) / ry]); + var u = [(xp - cxp) / rx, (yp - cyp) / ry]; + var v = [(-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry]; + var dTheta = vAngle(u, v); + if (vRatio(u, v) <= -1) { + dTheta = PI$1; + } + if (vRatio(u, v) >= 1) { + dTheta = 0; + } + if (dTheta < 0) { + var n = Math.round(dTheta / PI$1 * 1e6) / 1e6; + dTheta = PI$1 * 2 + (n % 2) * PI$1; + } + path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs); + } + var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig; + var numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g; + function createPathProxyFromString(data) { + var path = new PathProxy(); + if (!data) { + return path; + } + var cpx = 0; + var cpy = 0; + var subpathX = cpx; + var subpathY = cpy; + var prevCmd; + var CMD = PathProxy.CMD; + var cmdList = data.match(commandReg); + if (!cmdList) { + return path; + } + for (var l = 0; l < cmdList.length; l++) { + var cmdText = cmdList[l]; + var cmdStr = cmdText.charAt(0); + var cmd = void 0; + var p = cmdText.match(numberReg) || []; + var pLen = p.length; + for (var i = 0; i < pLen; i++) { + p[i] = parseFloat(p[i]); + } + var off = 0; + while (off < pLen) { + var ctlPtx = void 0; + var ctlPty = void 0; + var rx = void 0; + var ry = void 0; + var psi = void 0; + var fa = void 0; + var fs = void 0; + var x1 = cpx; + var y1 = cpy; + var len = void 0; + var pathData = void 0; + switch (cmdStr) { + case 'l': + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'L': + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'm': + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.M; + path.addData(cmd, cpx, cpy); + subpathX = cpx; + subpathY = cpy; + cmdStr = 'l'; + break; + case 'M': + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.M; + path.addData(cmd, cpx, cpy); + subpathX = cpx; + subpathY = cpy; + cmdStr = 'L'; + break; + case 'h': + cpx += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'H': + cpx = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'v': + cpy += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'V': + cpy = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'C': + cmd = CMD.C; + path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]); + cpx = p[off - 2]; + cpy = p[off - 1]; + break; + case 'c': + cmd = CMD.C; + path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy); + cpx += p[off - 2]; + cpy += p[off - 1]; + break; + case 'S': + ctlPtx = cpx; + ctlPty = cpy; + len = path.len(); + pathData = path.data; + if (prevCmd === CMD.C) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cmd = CMD.C; + x1 = p[off++]; + y1 = p[off++]; + cpx = p[off++]; + cpy = p[off++]; + path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); + break; + case 's': + ctlPtx = cpx; + ctlPty = cpy; + len = path.len(); + pathData = path.data; + if (prevCmd === CMD.C) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cmd = CMD.C; + x1 = cpx + p[off++]; + y1 = cpy + p[off++]; + cpx += p[off++]; + cpy += p[off++]; + path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); + break; + case 'Q': + x1 = p[off++]; + y1 = p[off++]; + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.Q; + path.addData(cmd, x1, y1, cpx, cpy); + break; + case 'q': + x1 = p[off++] + cpx; + y1 = p[off++] + cpy; + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.Q; + path.addData(cmd, x1, y1, cpx, cpy); + break; + case 'T': + ctlPtx = cpx; + ctlPty = cpy; + len = path.len(); + pathData = path.data; + if (prevCmd === CMD.Q) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.Q; + path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); + break; + case 't': + ctlPtx = cpx; + ctlPty = cpy; + len = path.len(); + pathData = path.data; + if (prevCmd === CMD.Q) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.Q; + path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); + break; + case 'A': + rx = p[off++]; + ry = p[off++]; + psi = p[off++]; + fa = p[off++]; + fs = p[off++]; + x1 = cpx, y1 = cpy; + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.A; + processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path); + break; + case 'a': + rx = p[off++]; + ry = p[off++]; + psi = p[off++]; + fa = p[off++]; + fs = p[off++]; + x1 = cpx, y1 = cpy; + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.A; + processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path); + break; + } + } + if (cmdStr === 'z' || cmdStr === 'Z') { + cmd = CMD.Z; + path.addData(cmd); + cpx = subpathX; + cpy = subpathY; + } + prevCmd = cmd; + } + path.toStatic(); + return path; + } + var SVGPath = (function (_super) { + __extends(SVGPath, _super); + function SVGPath() { + return _super !== null && _super.apply(this, arguments) || this; + } + SVGPath.prototype.applyTransform = function (m) { }; + return SVGPath; + }(Path)); + function isPathProxy(path) { + return path.setData != null; + } + function createPathOptions(str, opts) { + var pathProxy = createPathProxyFromString(str); + var innerOpts = extend({}, opts); + innerOpts.buildPath = function (path) { + if (isPathProxy(path)) { + path.setData(pathProxy.data); + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx, 1); + } + } + else { + var ctx = path; + pathProxy.rebuildPath(ctx, 1); + } + }; + innerOpts.applyTransform = function (m) { + transformPath(pathProxy, m); + this.dirtyShape(); + }; + return innerOpts; + } + function createFromString(str, opts) { + return new SVGPath(createPathOptions(str, opts)); + } + function extendFromString(str, defaultOpts) { + var innerOpts = createPathOptions(str, defaultOpts); + var Sub = (function (_super) { + __extends(Sub, _super); + function Sub(opts) { + var _this = _super.call(this, opts) || this; + _this.applyTransform = innerOpts.applyTransform; + _this.buildPath = innerOpts.buildPath; + return _this; + } + return Sub; + }(SVGPath)); + return Sub; + } + function mergePath(pathEls, opts) { + var pathList = []; + var len = pathEls.length; + for (var i = 0; i < len; i++) { + var pathEl = pathEls[i]; + pathList.push(pathEl.getUpdatedPathProxy(true)); + } + var pathBundle = new Path(opts); + pathBundle.createPathProxy(); + pathBundle.buildPath = function (path) { + if (isPathProxy(path)) { + path.appendPath(pathList); + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx, 1); + } + } + }; + return pathBundle; + } + function clonePath(sourcePath, opts) { + opts = opts || {}; + var path = new Path(); + if (sourcePath.shape) { + path.setShape(sourcePath.shape); + } + path.setStyle(sourcePath.style); + if (opts.bakeTransform) { + transformPath(path.path, sourcePath.getComputedTransform()); + } + else { + if (opts.toLocal) { + path.setLocalTransform(sourcePath.getComputedTransform()); + } + else { + path.copyTransform(sourcePath); + } + } + path.buildPath = sourcePath.buildPath; + path.applyTransform = path.applyTransform; + path.z = sourcePath.z; + path.z2 = sourcePath.z2; + path.zlevel = sourcePath.zlevel; + return path; + } + + var CircleShape = (function () { + function CircleShape() { + this.cx = 0; + this.cy = 0; + this.r = 0; + } + return CircleShape; + }()); + var Circle = (function (_super) { + __extends(Circle, _super); + function Circle(opts) { + return _super.call(this, opts) || this; + } + Circle.prototype.getDefaultShape = function () { + return new CircleShape(); + }; + Circle.prototype.buildPath = function (ctx, shape) { + ctx.moveTo(shape.cx + shape.r, shape.cy); + ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2); + }; + return Circle; + }(Path)); + Circle.prototype.type = 'circle'; + + var EllipseShape = (function () { + function EllipseShape() { + this.cx = 0; + this.cy = 0; + this.rx = 0; + this.ry = 0; + } + return EllipseShape; + }()); + var Ellipse = (function (_super) { + __extends(Ellipse, _super); + function Ellipse(opts) { + return _super.call(this, opts) || this; + } + Ellipse.prototype.getDefaultShape = function () { + return new EllipseShape(); + }; + Ellipse.prototype.buildPath = function (ctx, shape) { + var k = 0.5522848; + var x = shape.cx; + var y = shape.cy; + var a = shape.rx; + var b = shape.ry; + var ox = a * k; + var oy = b * k; + ctx.moveTo(x - a, y); + ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b); + ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y); + ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b); + ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y); + ctx.closePath(); + }; + return Ellipse; + }(Path)); + Ellipse.prototype.type = 'ellipse'; + + var PI$2 = Math.PI; + var PI2$5 = PI$2 * 2; + var mathSin$3 = Math.sin; + var mathCos$3 = Math.cos; + var mathACos = Math.acos; + var mathATan2 = Math.atan2; + var mathAbs$1 = Math.abs; + var mathSqrt$3 = Math.sqrt; + var mathMax$3 = Math.max; + var mathMin$3 = Math.min; + var e = 1e-4; + function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { + var dx10 = x1 - x0; + var dy10 = y1 - y0; + var dx32 = x3 - x2; + var dy32 = y3 - y2; + var t = dy32 * dx10 - dx32 * dy10; + if (t * t < e) { + return; + } + t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t; + return [x0 + t * dx10, y0 + t * dy10]; + } + function computeCornerTangents(x0, y0, x1, y1, radius, cr, clockwise) { + var x01 = x0 - x1; + var y01 = y0 - y1; + var lo = (clockwise ? cr : -cr) / mathSqrt$3(x01 * x01 + y01 * y01); + var ox = lo * y01; + var oy = -lo * x01; + var x11 = x0 + ox; + var y11 = y0 + oy; + var x10 = x1 + ox; + var y10 = y1 + oy; + var x00 = (x11 + x10) / 2; + var y00 = (y11 + y10) / 2; + var dx = x10 - x11; + var dy = y10 - y11; + var d2 = dx * dx + dy * dy; + var r = radius - cr; + var s = x11 * y10 - x10 * y11; + var d = (dy < 0 ? -1 : 1) * mathSqrt$3(mathMax$3(0, r * r * d2 - s * s)); + var cx0 = (s * dy - dx * d) / d2; + var cy0 = (-s * dx - dy * d) / d2; + var cx1 = (s * dy + dx * d) / d2; + var cy1 = (-s * dx + dy * d) / d2; + var dx0 = cx0 - x00; + var dy0 = cy0 - y00; + var dx1 = cx1 - x00; + var dy1 = cy1 - y00; + if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) { + cx0 = cx1; + cy0 = cy1; + } + return { + cx: cx0, + cy: cy0, + x0: -ox, + y0: -oy, + x1: cx0 * (radius / r - 1), + y1: cy0 * (radius / r - 1) + }; + } + function normalizeCornerRadius(cr) { + var arr; + if (isArray(cr)) { + var len = cr.length; + if (!len) { + return cr; + } + if (len === 1) { + arr = [cr[0], cr[0], 0, 0]; + } + else if (len === 2) { + arr = [cr[0], cr[0], cr[1], cr[1]]; + } + else if (len === 3) { + arr = cr.concat(cr[2]); + } + else { + arr = cr; + } + } + else { + arr = [cr, cr, cr, cr]; + } + return arr; + } + function buildPath$1(ctx, shape) { + var _a; + var radius = mathMax$3(shape.r, 0); + var innerRadius = mathMax$3(shape.r0 || 0, 0); + var hasRadius = radius > 0; + var hasInnerRadius = innerRadius > 0; + if (!hasRadius && !hasInnerRadius) { + return; + } + if (!hasRadius) { + radius = innerRadius; + innerRadius = 0; + } + if (innerRadius > radius) { + var tmp = radius; + radius = innerRadius; + innerRadius = tmp; + } + var startAngle = shape.startAngle, endAngle = shape.endAngle; + if (isNaN(startAngle) || isNaN(endAngle)) { + return; + } + var cx = shape.cx, cy = shape.cy; + var clockwise = !!shape.clockwise; + var arc = mathAbs$1(endAngle - startAngle); + var mod = arc > PI2$5 && arc % PI2$5; + mod > e && (arc = mod); + if (!(radius > e)) { + ctx.moveTo(cx, cy); + } + else if (arc > PI2$5 - e) { + ctx.moveTo(cx + radius * mathCos$3(startAngle), cy + radius * mathSin$3(startAngle)); + ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); + if (innerRadius > e) { + ctx.moveTo(cx + innerRadius * mathCos$3(endAngle), cy + innerRadius * mathSin$3(endAngle)); + ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); + } + } + else { + var icrStart = void 0; + var icrEnd = void 0; + var ocrStart = void 0; + var ocrEnd = void 0; + var ocrs = void 0; + var ocre = void 0; + var icrs = void 0; + var icre = void 0; + var ocrMax = void 0; + var icrMax = void 0; + var limitedOcrMax = void 0; + var limitedIcrMax = void 0; + var xre = void 0; + var yre = void 0; + var xirs = void 0; + var yirs = void 0; + var xrs = radius * mathCos$3(startAngle); + var yrs = radius * mathSin$3(startAngle); + var xire = innerRadius * mathCos$3(endAngle); + var yire = innerRadius * mathSin$3(endAngle); + var hasArc = arc > e; + if (hasArc) { + var cornerRadius = shape.cornerRadius; + if (cornerRadius) { + _a = normalizeCornerRadius(cornerRadius), icrStart = _a[0], icrEnd = _a[1], ocrStart = _a[2], ocrEnd = _a[3]; + } + var halfRd = mathAbs$1(radius - innerRadius) / 2; + ocrs = mathMin$3(halfRd, ocrStart); + ocre = mathMin$3(halfRd, ocrEnd); + icrs = mathMin$3(halfRd, icrStart); + icre = mathMin$3(halfRd, icrEnd); + limitedOcrMax = ocrMax = mathMax$3(ocrs, ocre); + limitedIcrMax = icrMax = mathMax$3(icrs, icre); + if (ocrMax > e || icrMax > e) { + xre = radius * mathCos$3(endAngle); + yre = radius * mathSin$3(endAngle); + xirs = innerRadius * mathCos$3(startAngle); + yirs = innerRadius * mathSin$3(startAngle); + if (arc < PI$2) { + var it_1 = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire); + if (it_1) { + var x0 = xrs - it_1[0]; + var y0 = yrs - it_1[1]; + var x1 = xre - it_1[0]; + var y1 = yre - it_1[1]; + var a = 1 / mathSin$3(mathACos((x0 * x1 + y0 * y1) / (mathSqrt$3(x0 * x0 + y0 * y0) * mathSqrt$3(x1 * x1 + y1 * y1))) / 2); + var b = mathSqrt$3(it_1[0] * it_1[0] + it_1[1] * it_1[1]); + limitedOcrMax = mathMin$3(ocrMax, (radius - b) / (a + 1)); + limitedIcrMax = mathMin$3(icrMax, (innerRadius - b) / (a - 1)); + } + } + } + } + if (!hasArc) { + ctx.moveTo(cx + xrs, cy + yrs); + } + else if (limitedOcrMax > e) { + var crStart = mathMin$3(ocrStart, limitedOcrMax); + var crEnd = mathMin$3(ocrEnd, limitedOcrMax); + var ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise); + var ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise); + ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); + if (limitedOcrMax < ocrMax && crStart === crEnd) { + ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); + } + else { + crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); + ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise); + crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); + } + } + else { + ctx.moveTo(cx + xrs, cy + yrs); + ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); + } + if (!(innerRadius > e) || !hasArc) { + ctx.lineTo(cx + xire, cy + yire); + } + else if (limitedIcrMax > e) { + var crStart = mathMin$3(icrStart, limitedIcrMax); + var crEnd = mathMin$3(icrEnd, limitedIcrMax); + var ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise); + var ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise); + ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); + if (limitedIcrMax < icrMax && crStart === crEnd) { + ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); + } + else { + crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); + ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise); + crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); + } + } + else { + ctx.lineTo(cx + xire, cy + yire); + ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); + } + } + ctx.closePath(); + } + + var SectorShape = (function () { + function SectorShape() { + this.cx = 0; + this.cy = 0; + this.r0 = 0; + this.r = 0; + this.startAngle = 0; + this.endAngle = Math.PI * 2; + this.clockwise = true; + this.cornerRadius = 0; + } + return SectorShape; + }()); + var Sector = (function (_super) { + __extends(Sector, _super); + function Sector(opts) { + return _super.call(this, opts) || this; + } + Sector.prototype.getDefaultShape = function () { + return new SectorShape(); + }; + Sector.prototype.buildPath = function (ctx, shape) { + buildPath$1(ctx, shape); + }; + Sector.prototype.isZeroArea = function () { + return this.shape.startAngle === this.shape.endAngle + || this.shape.r === this.shape.r0; + }; + return Sector; + }(Path)); + Sector.prototype.type = 'sector'; + + var RingShape = (function () { + function RingShape() { + this.cx = 0; + this.cy = 0; + this.r = 0; + this.r0 = 0; + } + return RingShape; + }()); + var Ring = (function (_super) { + __extends(Ring, _super); + function Ring(opts) { + return _super.call(this, opts) || this; + } + Ring.prototype.getDefaultShape = function () { + return new RingShape(); + }; + Ring.prototype.buildPath = function (ctx, shape) { + var x = shape.cx; + var y = shape.cy; + var PI2 = Math.PI * 2; + ctx.moveTo(x + shape.r, y); + ctx.arc(x, y, shape.r, 0, PI2, false); + ctx.moveTo(x + shape.r0, y); + ctx.arc(x, y, shape.r0, 0, PI2, true); + }; + return Ring; + }(Path)); + Ring.prototype.type = 'ring'; + + function smoothBezier(points, smooth, isLoop, constraint) { + var cps = []; + var v = []; + var v1 = []; + var v2 = []; + var prevPoint; + var nextPoint; + var min$1; + var max$1; + if (constraint) { + min$1 = [Infinity, Infinity]; + max$1 = [-Infinity, -Infinity]; + for (var i = 0, len = points.length; i < len; i++) { + min(min$1, min$1, points[i]); + max(max$1, max$1, points[i]); + } + min(min$1, min$1, constraint[0]); + max(max$1, max$1, constraint[1]); + } + for (var i = 0, len = points.length; i < len; i++) { + var point = points[i]; + if (isLoop) { + prevPoint = points[i ? i - 1 : len - 1]; + nextPoint = points[(i + 1) % len]; + } + else { + if (i === 0 || i === len - 1) { + cps.push(clone$1(points[i])); + continue; + } + else { + prevPoint = points[i - 1]; + nextPoint = points[i + 1]; + } + } + sub(v, nextPoint, prevPoint); + scale(v, v, smooth); + var d0 = distance(point, prevPoint); + var d1 = distance(point, nextPoint); + var sum = d0 + d1; + if (sum !== 0) { + d0 /= sum; + d1 /= sum; + } + scale(v1, v, -d0); + scale(v2, v, d1); + var cp0 = add([], point, v1); + var cp1 = add([], point, v2); + if (constraint) { + max(cp0, cp0, min$1); + min(cp0, cp0, max$1); + max(cp1, cp1, min$1); + min(cp1, cp1, max$1); + } + cps.push(cp0); + cps.push(cp1); + } + if (isLoop) { + cps.push(cps.shift()); + } + return cps; + } + + function buildPath$2(ctx, shape, closePath) { + var smooth = shape.smooth; + var points = shape.points; + if (points && points.length >= 2) { + if (smooth) { + var controlPoints = smoothBezier(points, smooth, closePath, shape.smoothConstraint); + ctx.moveTo(points[0][0], points[0][1]); + var len = points.length; + for (var i = 0; i < (closePath ? len : len - 1); i++) { + var cp1 = controlPoints[i * 2]; + var cp2 = controlPoints[i * 2 + 1]; + var p = points[(i + 1) % len]; + ctx.bezierCurveTo(cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1]); + } + } + else { + ctx.moveTo(points[0][0], points[0][1]); + for (var i = 1, l = points.length; i < l; i++) { + ctx.lineTo(points[i][0], points[i][1]); + } + } + closePath && ctx.closePath(); + } + } + + var PolygonShape = (function () { + function PolygonShape() { + this.points = null; + this.smooth = 0; + this.smoothConstraint = null; + } + return PolygonShape; + }()); + var Polygon = (function (_super) { + __extends(Polygon, _super); + function Polygon(opts) { + return _super.call(this, opts) || this; + } + Polygon.prototype.getDefaultShape = function () { + return new PolygonShape(); + }; + Polygon.prototype.buildPath = function (ctx, shape) { + buildPath$2(ctx, shape, true); + }; + return Polygon; + }(Path)); + Polygon.prototype.type = 'polygon'; + + var PolylineShape = (function () { + function PolylineShape() { + this.points = null; + this.percent = 1; + this.smooth = 0; + this.smoothConstraint = null; + } + return PolylineShape; + }()); + var Polyline = (function (_super) { + __extends(Polyline, _super); + function Polyline(opts) { + return _super.call(this, opts) || this; + } + Polyline.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + Polyline.prototype.getDefaultShape = function () { + return new PolylineShape(); + }; + Polyline.prototype.buildPath = function (ctx, shape) { + buildPath$2(ctx, shape, false); + }; + return Polyline; + }(Path)); + Polyline.prototype.type = 'polyline'; + + var subPixelOptimizeOutputShape$1 = {}; + var LineShape = (function () { + function LineShape() { + this.x1 = 0; + this.y1 = 0; + this.x2 = 0; + this.y2 = 0; + this.percent = 1; + } + return LineShape; + }()); + var Line = (function (_super) { + __extends(Line, _super); + function Line(opts) { + return _super.call(this, opts) || this; + } + Line.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + Line.prototype.getDefaultShape = function () { + return new LineShape(); + }; + Line.prototype.buildPath = function (ctx, shape) { + var x1; + var y1; + var x2; + var y2; + if (this.subPixelOptimize) { + var optimizedShape = subPixelOptimizeLine(subPixelOptimizeOutputShape$1, shape, this.style); + x1 = optimizedShape.x1; + y1 = optimizedShape.y1; + x2 = optimizedShape.x2; + y2 = optimizedShape.y2; + } + else { + x1 = shape.x1; + y1 = shape.y1; + x2 = shape.x2; + y2 = shape.y2; + } + var percent = shape.percent; + if (percent === 0) { + return; + } + ctx.moveTo(x1, y1); + if (percent < 1) { + x2 = x1 * (1 - percent) + x2 * percent; + y2 = y1 * (1 - percent) + y2 * percent; + } + ctx.lineTo(x2, y2); + }; + Line.prototype.pointAt = function (p) { + var shape = this.shape; + return [ + shape.x1 * (1 - p) + shape.x2 * p, + shape.y1 * (1 - p) + shape.y2 * p + ]; + }; + return Line; + }(Path)); + Line.prototype.type = 'line'; + + var out = []; + var BezierCurveShape = (function () { + function BezierCurveShape() { + this.x1 = 0; + this.y1 = 0; + this.x2 = 0; + this.y2 = 0; + this.cpx1 = 0; + this.cpy1 = 0; + this.percent = 1; + } + return BezierCurveShape; + }()); + function someVectorAt(shape, t, isTangent) { + var cpx2 = shape.cpx2; + var cpy2 = shape.cpy2; + if (cpx2 != null || cpy2 != null) { + return [ + (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t), + (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t) + ]; + } + else { + return [ + (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t), + (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t) + ]; + } + } + var BezierCurve = (function (_super) { + __extends(BezierCurve, _super); + function BezierCurve(opts) { + return _super.call(this, opts) || this; + } + BezierCurve.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + BezierCurve.prototype.getDefaultShape = function () { + return new BezierCurveShape(); + }; + BezierCurve.prototype.buildPath = function (ctx, shape) { + var x1 = shape.x1; + var y1 = shape.y1; + var x2 = shape.x2; + var y2 = shape.y2; + var cpx1 = shape.cpx1; + var cpy1 = shape.cpy1; + var cpx2 = shape.cpx2; + var cpy2 = shape.cpy2; + var percent = shape.percent; + if (percent === 0) { + return; + } + ctx.moveTo(x1, y1); + if (cpx2 == null || cpy2 == null) { + if (percent < 1) { + quadraticSubdivide(x1, cpx1, x2, percent, out); + cpx1 = out[1]; + x2 = out[2]; + quadraticSubdivide(y1, cpy1, y2, percent, out); + cpy1 = out[1]; + y2 = out[2]; + } + ctx.quadraticCurveTo(cpx1, cpy1, x2, y2); + } + else { + if (percent < 1) { + cubicSubdivide(x1, cpx1, cpx2, x2, percent, out); + cpx1 = out[1]; + cpx2 = out[2]; + x2 = out[3]; + cubicSubdivide(y1, cpy1, cpy2, y2, percent, out); + cpy1 = out[1]; + cpy2 = out[2]; + y2 = out[3]; + } + ctx.bezierCurveTo(cpx1, cpy1, cpx2, cpy2, x2, y2); + } + }; + BezierCurve.prototype.pointAt = function (t) { + return someVectorAt(this.shape, t, false); + }; + BezierCurve.prototype.tangentAt = function (t) { + var p = someVectorAt(this.shape, t, true); + return normalize(p, p); + }; + return BezierCurve; + }(Path)); + BezierCurve.prototype.type = 'bezier-curve'; + + var ArcShape = (function () { + function ArcShape() { + this.cx = 0; + this.cy = 0; + this.r = 0; + this.startAngle = 0; + this.endAngle = Math.PI * 2; + this.clockwise = true; + } + return ArcShape; + }()); + var Arc = (function (_super) { + __extends(Arc, _super); + function Arc(opts) { + return _super.call(this, opts) || this; + } + Arc.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + Arc.prototype.getDefaultShape = function () { + return new ArcShape(); + }; + Arc.prototype.buildPath = function (ctx, shape) { + var x = shape.cx; + var y = shape.cy; + var r = Math.max(shape.r, 0); + var startAngle = shape.startAngle; + var endAngle = shape.endAngle; + var clockwise = shape.clockwise; + var unitX = Math.cos(startAngle); + var unitY = Math.sin(startAngle); + ctx.moveTo(unitX * r + x, unitY * r + y); + ctx.arc(x, y, r, startAngle, endAngle, !clockwise); + }; + return Arc; + }(Path)); + Arc.prototype.type = 'arc'; + + var CompoundPath = (function (_super) { + __extends(CompoundPath, _super); + function CompoundPath() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'compound'; + return _this; + } + CompoundPath.prototype._updatePathDirty = function () { + var paths = this.shape.paths; + var dirtyPath = this.shapeChanged(); + for (var i = 0; i < paths.length; i++) { + dirtyPath = dirtyPath || paths[i].shapeChanged(); + } + if (dirtyPath) { + this.dirtyShape(); + } + }; + CompoundPath.prototype.beforeBrush = function () { + this._updatePathDirty(); + var paths = this.shape.paths || []; + var scale = this.getGlobalScale(); + for (var i = 0; i < paths.length; i++) { + if (!paths[i].path) { + paths[i].createPathProxy(); + } + paths[i].path.setScale(scale[0], scale[1], paths[i].segmentIgnoreThreshold); + } + }; + CompoundPath.prototype.buildPath = function (ctx, shape) { + var paths = shape.paths || []; + for (var i = 0; i < paths.length; i++) { + paths[i].buildPath(ctx, paths[i].shape, true); + } + }; + CompoundPath.prototype.afterBrush = function () { + var paths = this.shape.paths || []; + for (var i = 0; i < paths.length; i++) { + paths[i].pathUpdated(); + } + }; + CompoundPath.prototype.getBoundingRect = function () { + this._updatePathDirty.call(this); + return Path.prototype.getBoundingRect.call(this); + }; + return CompoundPath; + }(Path)); + + var Gradient = (function () { + function Gradient(colorStops) { + this.colorStops = colorStops || []; + } + Gradient.prototype.addColorStop = function (offset, color) { + this.colorStops.push({ + offset: offset, + color: color + }); + }; + return Gradient; + }()); + + var LinearGradient = (function (_super) { + __extends(LinearGradient, _super); + function LinearGradient(x, y, x2, y2, colorStops, globalCoord) { + var _this = _super.call(this, colorStops) || this; + _this.x = x == null ? 0 : x; + _this.y = y == null ? 0 : y; + _this.x2 = x2 == null ? 1 : x2; + _this.y2 = y2 == null ? 0 : y2; + _this.type = 'linear'; + _this.global = globalCoord || false; + return _this; + } + return LinearGradient; + }(Gradient)); + + var RadialGradient = (function (_super) { + __extends(RadialGradient, _super); + function RadialGradient(x, y, r, colorStops, globalCoord) { + var _this = _super.call(this, colorStops) || this; + _this.x = x == null ? 0.5 : x; + _this.y = y == null ? 0.5 : y; + _this.r = r == null ? 0.5 : r; + _this.type = 'radial'; + _this.global = globalCoord || false; + return _this; + } + return RadialGradient; + }(Gradient)); + + var extent = [0, 0]; + var extent2 = [0, 0]; + var minTv$1 = new Point(); + var maxTv$1 = new Point(); + var OrientedBoundingRect = (function () { + function OrientedBoundingRect(rect, transform) { + this._corners = []; + this._axes = []; + this._origin = [0, 0]; + for (var i = 0; i < 4; i++) { + this._corners[i] = new Point(); + } + for (var i = 0; i < 2; i++) { + this._axes[i] = new Point(); + } + if (rect) { + this.fromBoundingRect(rect, transform); + } + } + OrientedBoundingRect.prototype.fromBoundingRect = function (rect, transform) { + var corners = this._corners; + var axes = this._axes; + var x = rect.x; + var y = rect.y; + var x2 = x + rect.width; + var y2 = y + rect.height; + corners[0].set(x, y); + corners[1].set(x2, y); + corners[2].set(x2, y2); + corners[3].set(x, y2); + if (transform) { + for (var i = 0; i < 4; i++) { + corners[i].transform(transform); + } + } + Point.sub(axes[0], corners[1], corners[0]); + Point.sub(axes[1], corners[3], corners[0]); + axes[0].normalize(); + axes[1].normalize(); + for (var i = 0; i < 2; i++) { + this._origin[i] = axes[i].dot(corners[0]); + } + }; + OrientedBoundingRect.prototype.intersect = function (other, mtv) { + var overlapped = true; + var noMtv = !mtv; + minTv$1.set(Infinity, Infinity); + maxTv$1.set(0, 0); + if (!this._intersectCheckOneSide(this, other, minTv$1, maxTv$1, noMtv, 1)) { + overlapped = false; + if (noMtv) { + return overlapped; + } + } + if (!this._intersectCheckOneSide(other, this, minTv$1, maxTv$1, noMtv, -1)) { + overlapped = false; + if (noMtv) { + return overlapped; + } + } + if (!noMtv) { + Point.copy(mtv, overlapped ? minTv$1 : maxTv$1); + } + return overlapped; + }; + OrientedBoundingRect.prototype._intersectCheckOneSide = function (self, other, minTv, maxTv, noMtv, inverse) { + var overlapped = true; + for (var i = 0; i < 2; i++) { + var axis = this._axes[i]; + this._getProjMinMaxOnAxis(i, self._corners, extent); + this._getProjMinMaxOnAxis(i, other._corners, extent2); + if (extent[1] < extent2[0] || extent[0] > extent2[1]) { + overlapped = false; + if (noMtv) { + return overlapped; + } + var dist0 = Math.abs(extent2[0] - extent[1]); + var dist1 = Math.abs(extent[0] - extent2[1]); + if (Math.min(dist0, dist1) > maxTv.len()) { + if (dist0 < dist1) { + Point.scale(maxTv, axis, -dist0 * inverse); + } + else { + Point.scale(maxTv, axis, dist1 * inverse); + } + } + } + else if (minTv) { + var dist0 = Math.abs(extent2[0] - extent[1]); + var dist1 = Math.abs(extent[0] - extent2[1]); + if (Math.min(dist0, dist1) < minTv.len()) { + if (dist0 < dist1) { + Point.scale(minTv, axis, dist0 * inverse); + } + else { + Point.scale(minTv, axis, -dist1 * inverse); + } + } + } + } + return overlapped; + }; + OrientedBoundingRect.prototype._getProjMinMaxOnAxis = function (dim, corners, out) { + var axis = this._axes[dim]; + var origin = this._origin; + var proj = corners[0].dot(axis) + origin[dim]; + var min = proj; + var max = proj; + for (var i = 1; i < corners.length; i++) { + var proj_1 = corners[i].dot(axis) + origin[dim]; + min = Math.min(proj_1, min); + max = Math.max(proj_1, max); + } + out[0] = min; + out[1] = max; + }; + return OrientedBoundingRect; + }()); + + var m = []; + var IncrementalDisplayable = (function (_super) { + __extends(IncrementalDisplayable, _super); + function IncrementalDisplayable() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.notClear = true; + _this.incremental = true; + _this._displayables = []; + _this._temporaryDisplayables = []; + _this._cursor = 0; + return _this; + } + IncrementalDisplayable.prototype.traverse = function (cb, context) { + cb.call(context, this); + }; + IncrementalDisplayable.prototype.useStyle = function () { + this.style = {}; + }; + IncrementalDisplayable.prototype.getCursor = function () { + return this._cursor; + }; + IncrementalDisplayable.prototype.innerAfterBrush = function () { + this._cursor = this._displayables.length; + }; + IncrementalDisplayable.prototype.clearDisplaybles = function () { + this._displayables = []; + this._temporaryDisplayables = []; + this._cursor = 0; + this.markRedraw(); + this.notClear = false; + }; + IncrementalDisplayable.prototype.clearTemporalDisplayables = function () { + this._temporaryDisplayables = []; + }; + IncrementalDisplayable.prototype.addDisplayable = function (displayable, notPersistent) { + if (notPersistent) { + this._temporaryDisplayables.push(displayable); + } + else { + this._displayables.push(displayable); + } + this.markRedraw(); + }; + IncrementalDisplayable.prototype.addDisplayables = function (displayables, notPersistent) { + notPersistent = notPersistent || false; + for (var i = 0; i < displayables.length; i++) { + this.addDisplayable(displayables[i], notPersistent); + } + }; + IncrementalDisplayable.prototype.getDisplayables = function () { + return this._displayables; + }; + IncrementalDisplayable.prototype.getTemporalDisplayables = function () { + return this._temporaryDisplayables; + }; + IncrementalDisplayable.prototype.eachPendingDisplayable = function (cb) { + for (var i = this._cursor; i < this._displayables.length; i++) { + cb && cb(this._displayables[i]); + } + for (var i = 0; i < this._temporaryDisplayables.length; i++) { + cb && cb(this._temporaryDisplayables[i]); + } + }; + IncrementalDisplayable.prototype.update = function () { + this.updateTransform(); + for (var i = this._cursor; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + displayable.parent = this; + displayable.update(); + displayable.parent = null; + } + for (var i = 0; i < this._temporaryDisplayables.length; i++) { + var displayable = this._temporaryDisplayables[i]; + displayable.parent = this; + displayable.update(); + displayable.parent = null; + } + }; + IncrementalDisplayable.prototype.getBoundingRect = function () { + if (!this._rect) { + var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity); + for (var i = 0; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + var childRect = displayable.getBoundingRect().clone(); + if (displayable.needLocalTransform()) { + childRect.applyTransform(displayable.getLocalTransform(m)); + } + rect.union(childRect); + } + this._rect = rect; + } + return this._rect; + }; + IncrementalDisplayable.prototype.contain = function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + if (rect.contain(localPos[0], localPos[1])) { + for (var i = 0; i < this._displayables.length; i++) { + var displayable = this._displayables[i]; + if (displayable.contain(x, y)) { + return true; + } + } + } + return false; + }; + return IncrementalDisplayable; + }(Displayable)); + + // Stored properties for further transition. + var transitionStore = makeInner(); + /** + * Return null if animation is disabled. + */ + function getAnimationConfig(animationType, animatableModel, dataIndex, + // Extra opts can override the option in animatable model. + extraOpts, + // TODO It's only for pictorial bar now. + extraDelayParams) { + var animationPayload; + // Check if there is global animation configuration from dataZoom/resize can override the config in option. + // If animation is enabled. Will use this animation config in payload. + // If animation is disabled. Just ignore it. + if (animatableModel && animatableModel.ecModel) { + var updatePayload = animatableModel.ecModel.getUpdatePayload(); + animationPayload = updatePayload && updatePayload.animation; + } + var animationEnabled = animatableModel && animatableModel.isAnimationEnabled(); + var isUpdate = animationType === 'update'; + if (animationEnabled) { + var duration = void 0; + var easing = void 0; + var delay = void 0; + if (extraOpts) { + duration = retrieve2(extraOpts.duration, 200); + easing = retrieve2(extraOpts.easing, 'cubicOut'); + delay = 0; + } else { + duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration'); + easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing'); + delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay'); + } + // animation from payload has highest priority. + if (animationPayload) { + animationPayload.duration != null && (duration = animationPayload.duration); + animationPayload.easing != null && (easing = animationPayload.easing); + animationPayload.delay != null && (delay = animationPayload.delay); + } + if (isFunction(delay)) { + delay = delay(dataIndex, extraDelayParams); + } + if (isFunction(duration)) { + duration = duration(dataIndex); + } + var config = { + duration: duration || 0, + delay: delay, + easing: easing + }; + return config; + } else { + return null; + } + } + function animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) { + var isFrom = false; + var removeOpt; + if (isFunction(dataIndex)) { + during = cb; + cb = dataIndex; + dataIndex = null; + } else if (isObject(dataIndex)) { + cb = dataIndex.cb; + during = dataIndex.during; + isFrom = dataIndex.isFrom; + removeOpt = dataIndex.removeOpt; + dataIndex = dataIndex.dataIndex; + } + var isRemove = animationType === 'leave'; + if (!isRemove) { + // Must stop the remove animation. + el.stopAnimation('leave'); + } + var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null); + if (animationConfig && animationConfig.duration > 0) { + var duration = animationConfig.duration; + var animationDelay = animationConfig.delay; + var animationEasing = animationConfig.easing; + var animateConfig = { + duration: duration, + delay: animationDelay || 0, + easing: animationEasing, + done: cb, + force: !!cb || !!during, + // Set to final state in update/init animation. + // So the post processing based on the path shape can be done correctly. + setToFinal: !isRemove, + scope: animationType, + during: during + }; + isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig); + } else { + el.stopAnimation(); + // If `isFrom`, the props is the "from" props. + !isFrom && el.attr(props); + // Call during at least once. + during && during(1); + cb && cb(); + } + } + /** + * Update graphic element properties with or without animation according to the + * configuration in series. + * + * Caution: this method will stop previous animation. + * So do not use this method to one element twice before + * animation starts, unless you know what you are doing. + * @example + * graphic.updateProps(el, { + * position: [100, 100] + * }, seriesModel, dataIndex, function () { console.log('Animation done!'); }); + * // Or + * graphic.updateProps(el, { + * position: [100, 100] + * }, seriesModel, function () { console.log('Animation done!'); }); + */ + function updateProps(el, props, + // TODO: TYPE AnimatableModel + animatableModel, dataIndex, cb, during) { + animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during); + } + /** + * Init graphic element properties with or without animation according to the + * configuration in series. + * + * Caution: this method will stop previous animation. + * So do not use this method to one element twice before + * animation starts, unless you know what you are doing. + */ + function initProps(el, props, animatableModel, dataIndex, cb, during) { + animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during); + } + /** + * If element is removed. + * It can determine if element is having remove animation. + */ + function isElementRemoved(el) { + if (!el.__zr) { + return true; + } + for (var i = 0; i < el.animators.length; i++) { + var animator = el.animators[i]; + if (animator.scope === 'leave') { + return true; + } + } + return false; + } + /** + * Remove graphic element + */ + function removeElement(el, props, animatableModel, dataIndex, cb, during) { + // Don't do remove animation twice. + if (isElementRemoved(el)) { + return; + } + animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during); + } + function fadeOutDisplayable(el, animatableModel, dataIndex, done) { + el.removeTextContent(); + el.removeTextGuideLine(); + removeElement(el, { + style: { + opacity: 0 + } + }, animatableModel, dataIndex, done); + } + function removeElementWithFadeOut(el, animatableModel, dataIndex) { + function doRemove() { + el.parent && el.parent.remove(el); + } + // Hide label and labelLine first + // TODO Also use fade out animation? + if (!el.isGroup) { + fadeOutDisplayable(el, animatableModel, dataIndex, doRemove); + } else { + el.traverse(function (disp) { + if (!disp.isGroup) { + // Can invoke doRemove multiple times. + fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove); + } + }); + } + } + /** + * Save old style for style transition in universalTransition module. + * It's used when element will be reused in each render. + * For chart like map, heatmap, which will always create new element. + * We don't need to save this because universalTransition can get old style from the old element + */ + function saveOldStyle(el) { + transitionStore(el).oldStyle = el.style; + } + function getOldStyle(el) { + return transitionStore(el).oldStyle; + } + + var mathMax$4 = Math.max; + var mathMin$4 = Math.min; + var _customShapeMap = {}; + /** + * Extend shape with parameters + */ + function extendShape(opts) { + return Path.extend(opts); + } + var extendPathFromString = extendFromString; + /** + * Extend path + */ + function extendPath(pathData, opts) { + return extendPathFromString(pathData, opts); + } + /** + * Register a user defined shape. + * The shape class can be fetched by `getShapeClass` + * This method will overwrite the registered shapes, including + * the registered built-in shapes, if using the same `name`. + * The shape can be used in `custom series` and + * `graphic component` by declaring `{type: name}`. + * + * @param name + * @param ShapeClass Can be generated by `extendShape`. + */ + function registerShape(name, ShapeClass) { + _customShapeMap[name] = ShapeClass; + } + /** + * Find shape class registered by `registerShape`. Usually used in + * fetching user defined shape. + * + * [Caution]: + * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared + * to use user registered shapes. + * Because the built-in shape (see `getBuiltInShape`) will be registered by + * `registerShape` by default. That enables users to get both built-in + * shapes as well as the shapes belonging to themsleves. But users can overwrite + * the built-in shapes by using names like 'circle', 'rect' via calling + * `registerShape`. So the echarts inner featrues should not fetch shapes from here + * in case that it is overwritten by users, except that some features, like + * `custom series`, `graphic component`, do it deliberately. + * + * (2) In the features like `custom series`, `graphic component`, the user input + * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic + * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names + * are reserved names, that is, if some user registers a shape named `'image'`, + * the shape will not be used. If we intending to add some more reserved names + * in feature, that might bring break changes (disable some existing user shape + * names). But that case probably rarely happens. So we don't make more mechanism + * to resolve this issue here. + * + * @param name + * @return The shape class. If not found, return nothing. + */ + function getShapeClass(name) { + if (_customShapeMap.hasOwnProperty(name)) { + return _customShapeMap[name]; + } + } + /** + * Create a path element from path data string + * @param pathData + * @param opts + * @param rect + * @param layout 'center' or 'cover' default to be cover + */ + function makePath(pathData, opts, rect, layout) { + var path = createFromString(pathData, opts); + if (rect) { + if (layout === 'center') { + rect = centerGraphic(rect, path.getBoundingRect()); + } + resizePath(path, rect); + } + return path; + } + /** + * Create a image element from image url + * @param imageUrl image url + * @param opts options + * @param rect constrain rect + * @param layout 'center' or 'cover'. Default to be 'cover' + */ + function makeImage(imageUrl, rect, layout) { + var zrImg = new ZRImage({ + style: { + image: imageUrl, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + onload: function (img) { + if (layout === 'center') { + var boundingRect = { + width: img.width, + height: img.height + }; + zrImg.setStyle(centerGraphic(rect, boundingRect)); + } + } + }); + return zrImg; + } + /** + * Get position of centered element in bounding box. + * + * @param rect element local bounding box + * @param boundingRect constraint bounding box + * @return element position containing x, y, width, and height + */ + function centerGraphic(rect, boundingRect) { + // Set rect to center, keep width / height ratio. + var aspect = boundingRect.width / boundingRect.height; + var width = rect.height * aspect; + var height; + if (width <= rect.width) { + height = rect.height; + } else { + width = rect.width; + height = width / aspect; + } + var cx = rect.x + rect.width / 2; + var cy = rect.y + rect.height / 2; + return { + x: cx - width / 2, + y: cy - height / 2, + width: width, + height: height + }; + } + var mergePath$1 = mergePath; + /** + * Resize a path to fit the rect + * @param path + * @param rect + */ + function resizePath(path, rect) { + if (!path.applyTransform) { + return; + } + var pathRect = path.getBoundingRect(); + var m = pathRect.calculateTransform(rect); + path.applyTransform(m); + } + /** + * Sub pixel optimize line for canvas + */ + function subPixelOptimizeLine$1(shape, lineWidth) { + subPixelOptimizeLine(shape, shape, { + lineWidth: lineWidth + }); + return shape; + } + /** + * Sub pixel optimize rect for canvas + */ + function subPixelOptimizeRect$1(param) { + subPixelOptimizeRect(param.shape, param.shape, param.style); + return param; + } + /** + * Sub pixel optimize for canvas + * + * @param position Coordinate, such as x, y + * @param lineWidth Should be nonnegative integer. + * @param positiveOrNegative Default false (negative). + * @return Optimized position. + */ + var subPixelOptimize$1 = subPixelOptimize; + /** + * Get transform matrix of target (param target), + * in coordinate of its ancestor (param ancestor) + * + * @param target + * @param [ancestor] + */ + function getTransform(target, ancestor) { + var mat = identity([]); + while (target && target !== ancestor) { + mul$1(mat, target.getLocalTransform(), mat); + target = target.parent; + } + return mat; + } + /** + * Apply transform to an vertex. + * @param target [x, y] + * @param transform Can be: + * + Transform matrix: like [1, 0, 0, 1, 0, 0] + * + {position, rotation, scale}, the same as `zrender/Transformable`. + * @param invert Whether use invert matrix. + * @return [x, y] + */ + function applyTransform$1(target, transform, invert$1) { + if (transform && !isArrayLike(transform)) { + transform = Transformable.getLocalTransform(transform); + } + if (invert$1) { + transform = invert([], transform); + } + return applyTransform([], target, transform); + } + /** + * @param direction 'left' 'right' 'top' 'bottom' + * @param transform Transform matrix: like [1, 0, 0, 1, 0, 0] + * @param invert Whether use invert matrix. + * @return Transformed direction. 'left' 'right' 'top' 'bottom' + */ + function transformDirection(direction, transform, invert) { + // Pick a base, ensure that transform result will not be (0, 0). + var hBase = transform[4] === 0 || transform[5] === 0 || transform[0] === 0 ? 1 : Math.abs(2 * transform[4] / transform[0]); + var vBase = transform[4] === 0 || transform[5] === 0 || transform[2] === 0 ? 1 : Math.abs(2 * transform[4] / transform[2]); + var vertex = [direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0]; + vertex = applyTransform$1(vertex, transform, invert); + return Math.abs(vertex[0]) > Math.abs(vertex[1]) ? vertex[0] > 0 ? 'right' : 'left' : vertex[1] > 0 ? 'bottom' : 'top'; + } + function isNotGroup(el) { + return !el.isGroup; + } + function isPath(el) { + return el.shape != null; + } + /** + * Apply group transition animation from g1 to g2. + * If no animatableModel, no animation. + */ + function groupTransition(g1, g2, animatableModel) { + if (!g1 || !g2) { + return; + } + function getElMap(g) { + var elMap = {}; + g.traverse(function (el) { + if (isNotGroup(el) && el.anid) { + elMap[el.anid] = el; + } + }); + return elMap; + } + function getAnimatableProps(el) { + var obj = { + x: el.x, + y: el.y, + rotation: el.rotation + }; + if (isPath(el)) { + obj.shape = extend({}, el.shape); + } + return obj; + } + var elMap1 = getElMap(g1); + g2.traverse(function (el) { + if (isNotGroup(el) && el.anid) { + var oldEl = elMap1[el.anid]; + if (oldEl) { + var newProp = getAnimatableProps(el); + el.attr(getAnimatableProps(oldEl)); + updateProps(el, newProp, animatableModel, getECData(el).dataIndex); + } + } + }); + } + function clipPointsByRect(points, rect) { + // FIXME: This way might be incorrect when graphic clipped by a corner + // and when element has a border. + return map(points, function (point) { + var x = point[0]; + x = mathMax$4(x, rect.x); + x = mathMin$4(x, rect.x + rect.width); + var y = point[1]; + y = mathMax$4(y, rect.y); + y = mathMin$4(y, rect.y + rect.height); + return [x, y]; + }); + } + /** + * Return a new clipped rect. If rect size are negative, return undefined. + */ + function clipRectByRect(targetRect, rect) { + var x = mathMax$4(targetRect.x, rect.x); + var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width); + var y = mathMax$4(targetRect.y, rect.y); + var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); + // If the total rect is cliped, nothing, including the border, + // should be painted. So return undefined. + if (x2 >= x && y2 >= y) { + return { + x: x, + y: y, + width: x2 - x, + height: y2 - y + }; + } + } + function createIcon(iconStr, + // Support 'image://' or 'path://' or direct svg path. + opt, rect) { + var innerOpts = extend({ + rectHover: true + }, opt); + var style = innerOpts.style = { + strokeNoScale: true + }; + rect = rect || { + x: -1, + y: -1, + width: 2, + height: 2 + }; + if (iconStr) { + return iconStr.indexOf('image://') === 0 ? (style.image = iconStr.slice(8), defaults(style, rect), new ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center'); + } + } + /** + * Return `true` if the given line (line `a`) and the given polygon + * are intersect. + * Note that we do not count colinear as intersect here because no + * requirement for that. We could do that if required in future. + */ + function linePolygonIntersect(a1x, a1y, a2x, a2y, points) { + for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) { + var p = points[i]; + if (lineLineIntersect(a1x, a1y, a2x, a2y, p[0], p[1], p2[0], p2[1])) { + return true; + } + p2 = p; + } + } + /** + * Return `true` if the given two lines (line `a` and line `b`) + * are intersect. + * Note that we do not count colinear as intersect here because no + * requirement for that. We could do that if required in future. + */ + function lineLineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) { + // let `vec_m` to be `vec_a2 - vec_a1` and `vec_n` to be `vec_b2 - vec_b1`. + var mx = a2x - a1x; + var my = a2y - a1y; + var nx = b2x - b1x; + var ny = b2y - b1y; + // `vec_m` and `vec_n` are parallel iff + // existing `k` such that `vec_m = k · vec_n`, equivalent to `vec_m X vec_n = 0`. + var nmCrossProduct = crossProduct2d(nx, ny, mx, my); + if (nearZero(nmCrossProduct)) { + return false; + } + // `vec_m` and `vec_n` are intersect iff + // existing `p` and `q` in [0, 1] such that `vec_a1 + p * vec_m = vec_b1 + q * vec_n`, + // such that `q = ((vec_a1 - vec_b1) X vec_m) / (vec_n X vec_m)` + // and `p = ((vec_a1 - vec_b1) X vec_n) / (vec_n X vec_m)`. + var b1a1x = a1x - b1x; + var b1a1y = a1y - b1y; + var q = crossProduct2d(b1a1x, b1a1y, mx, my) / nmCrossProduct; + if (q < 0 || q > 1) { + return false; + } + var p = crossProduct2d(b1a1x, b1a1y, nx, ny) / nmCrossProduct; + if (p < 0 || p > 1) { + return false; + } + return true; + } + /** + * Cross product of 2-dimension vector. + */ + function crossProduct2d(x1, y1, x2, y2) { + return x1 * y2 - x2 * y1; + } + function nearZero(val) { + return val <= 1e-6 && val >= -1e-6; + } + function setTooltipConfig(opt) { + var itemTooltipOption = opt.itemTooltipOption; + var componentModel = opt.componentModel; + var itemName = opt.itemName; + var itemTooltipOptionObj = isString(itemTooltipOption) ? { + formatter: itemTooltipOption + } : itemTooltipOption; + var mainType = componentModel.mainType; + var componentIndex = componentModel.componentIndex; + var formatterParams = { + componentType: mainType, + name: itemName, + $vars: ['name'] + }; + formatterParams[mainType + 'Index'] = componentIndex; + var formatterParamsExtra = opt.formatterParamsExtra; + if (formatterParamsExtra) { + each(keys(formatterParamsExtra), function (key) { + if (!hasOwn(formatterParams, key)) { + formatterParams[key] = formatterParamsExtra[key]; + formatterParams.$vars.push(key); + } + }); + } + var ecData = getECData(opt.el); + ecData.componentMainType = mainType; + ecData.componentIndex = componentIndex; + ecData.tooltipConfig = { + name: itemName, + option: defaults({ + content: itemName, + formatterParams: formatterParams + }, itemTooltipOptionObj) + }; + } + function traverseElement(el, cb) { + var stopped; + // TODO + // Polyfill for fixing zrender group traverse don't visit it's root issue. + if (el.isGroup) { + stopped = cb(el); + } + if (!stopped) { + el.traverse(cb); + } + } + function traverseElements(els, cb) { + if (els) { + if (isArray(els)) { + for (var i = 0; i < els.length; i++) { + traverseElement(els[i], cb); + } + } else { + traverseElement(els, cb); + } + } + } + // Register built-in shapes. These shapes might be overwritten + // by users, although we do not recommend that. + registerShape('circle', Circle); + registerShape('ellipse', Ellipse); + registerShape('sector', Sector); + registerShape('ring', Ring); + registerShape('polygon', Polygon); + registerShape('polyline', Polyline); + registerShape('rect', Rect); + registerShape('line', Line); + registerShape('bezierCurve', BezierCurve); + registerShape('arc', Arc); + + var graphic = /*#__PURE__*/Object.freeze({ + __proto__: null, + updateProps: updateProps, + initProps: initProps, + removeElement: removeElement, + removeElementWithFadeOut: removeElementWithFadeOut, + isElementRemoved: isElementRemoved, + extendShape: extendShape, + extendPath: extendPath, + registerShape: registerShape, + getShapeClass: getShapeClass, + makePath: makePath, + makeImage: makeImage, + mergePath: mergePath$1, + resizePath: resizePath, + subPixelOptimizeLine: subPixelOptimizeLine$1, + subPixelOptimizeRect: subPixelOptimizeRect$1, + subPixelOptimize: subPixelOptimize$1, + getTransform: getTransform, + applyTransform: applyTransform$1, + transformDirection: transformDirection, + groupTransition: groupTransition, + clipPointsByRect: clipPointsByRect, + clipRectByRect: clipRectByRect, + createIcon: createIcon, + linePolygonIntersect: linePolygonIntersect, + lineLineIntersect: lineLineIntersect, + setTooltipConfig: setTooltipConfig, + traverseElements: traverseElements, + Group: Group, + Image: ZRImage, + Text: ZRText, + Circle: Circle, + Ellipse: Ellipse, + Sector: Sector, + Ring: Ring, + Polygon: Polygon, + Polyline: Polyline, + Rect: Rect, + Line: Line, + BezierCurve: BezierCurve, + Arc: Arc, + IncrementalDisplayable: IncrementalDisplayable, + CompoundPath: CompoundPath, + LinearGradient: LinearGradient, + RadialGradient: RadialGradient, + BoundingRect: BoundingRect, + OrientedBoundingRect: OrientedBoundingRect, + Point: Point, + Path: Path + }); + + var EMPTY_OBJ = {}; + function setLabelText(label, labelTexts) { + for (var i = 0; i < SPECIAL_STATES.length; i++) { + var stateName = SPECIAL_STATES[i]; + var text = labelTexts[stateName]; + var state = label.ensureState(stateName); + state.style = state.style || {}; + state.style.text = text; + } + var oldStates = label.currentStates.slice(); + label.clearStates(true); + label.setStyle({ + text: labelTexts.normal + }); + label.useStates(oldStates, true); + } + function getLabelText(opt, stateModels, interpolatedValue) { + var labelFetcher = opt.labelFetcher; + var labelDataIndex = opt.labelDataIndex; + var labelDimIndex = opt.labelDimIndex; + var normalModel = stateModels.normal; + var baseText; + if (labelFetcher) { + baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, normalModel && normalModel.get('formatter'), interpolatedValue != null ? { + interpolatedValue: interpolatedValue + } : null); + } + if (baseText == null) { + baseText = isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt, interpolatedValue) : opt.defaultText; + } + var statesText = { + normal: baseText + }; + for (var i = 0; i < SPECIAL_STATES.length; i++) { + var stateName = SPECIAL_STATES[i]; + var stateModel = stateModels[stateName]; + statesText[stateName] = retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, stateName, null, labelDimIndex, stateModel && stateModel.get('formatter')) : null, baseText); + } + return statesText; + } + function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified + // TODO specified position? + ) { + opt = opt || EMPTY_OBJ; + var isSetOnText = targetEl instanceof ZRText; + var needsCreateText = false; + for (var i = 0; i < DISPLAY_STATES.length; i++) { + var stateModel = labelStatesModels[DISPLAY_STATES[i]]; + if (stateModel && stateModel.getShallow('show')) { + needsCreateText = true; + break; + } + } + var textContent = isSetOnText ? targetEl : targetEl.getTextContent(); + if (needsCreateText) { + if (!isSetOnText) { + // Reuse the previous + if (!textContent) { + textContent = new ZRText(); + targetEl.setTextContent(textContent); + } + // Use same state proxy + if (targetEl.stateProxy) { + textContent.stateProxy = targetEl.stateProxy; + } + } + var labelStatesTexts = getLabelText(opt, labelStatesModels); + var normalModel = labelStatesModels.normal; + var showNormal = !!normalModel.getShallow('show'); + var normalStyle = createTextStyle(normalModel, stateSpecified && stateSpecified.normal, opt, false, !isSetOnText); + normalStyle.text = labelStatesTexts.normal; + if (!isSetOnText) { + // Always create new + targetEl.setTextConfig(createTextConfig(normalModel, opt, false)); + } + for (var i = 0; i < SPECIAL_STATES.length; i++) { + var stateName = SPECIAL_STATES[i]; + var stateModel = labelStatesModels[stateName]; + if (stateModel) { + var stateObj = textContent.ensureState(stateName); + var stateShow = !!retrieve2(stateModel.getShallow('show'), showNormal); + if (stateShow !== showNormal) { + stateObj.ignore = !stateShow; + } + stateObj.style = createTextStyle(stateModel, stateSpecified && stateSpecified[stateName], opt, true, !isSetOnText); + stateObj.style.text = labelStatesTexts[stateName]; + if (!isSetOnText) { + var targetElEmphasisState = targetEl.ensureState(stateName); + targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true); + } + } + } + // PENDING: if there is many requirements that emphasis position + // need to be different from normal position, we might consider + // auto silent is those cases. + textContent.silent = !!normalModel.getShallow('silent'); + // Keep x and y + if (textContent.style.x != null) { + normalStyle.x = textContent.style.x; + } + if (textContent.style.y != null) { + normalStyle.y = textContent.style.y; + } + textContent.ignore = !showNormal; + // Always create new style. + textContent.useStyle(normalStyle); + textContent.dirty(); + if (opt.enableTextSetter) { + labelInner(textContent).setLabelText = function (interpolatedValue) { + var labelStatesTexts = getLabelText(opt, labelStatesModels, interpolatedValue); + setLabelText(textContent, labelStatesTexts); + }; + } + } else if (textContent) { + // Not display rich text. + textContent.ignore = true; + } + targetEl.dirty(); + } + function getLabelStatesModels(itemModel, labelName) { + labelName = labelName || 'label'; + var statesModels = { + normal: itemModel.getModel(labelName) + }; + for (var i = 0; i < SPECIAL_STATES.length; i++) { + var stateName = SPECIAL_STATES[i]; + statesModels[stateName] = itemModel.getModel([stateName, labelName]); + } + return statesModels; + } + /** + * Set basic textStyle properties. + */ + function createTextStyle(textStyleModel, specifiedTextStyle, + // Fixed style in the code. Can't be set by model. + opt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender. + ) { + var textStyle = {}; + setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached); + specifiedTextStyle && extend(textStyle, specifiedTextStyle); + // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); + return textStyle; + } + function createTextConfig(textStyleModel, opt, isNotNormal) { + opt = opt || {}; + var textConfig = {}; + var labelPosition; + var labelRotate = textStyleModel.getShallow('rotate'); + var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5); + var labelOffset = textStyleModel.getShallow('offset'); + labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); + // 'outside' is not a valid zr textPostion value, but used + // in bar series, and magric type should be considered. + labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top'); + if (labelPosition != null) { + textConfig.position = labelPosition; + } + if (labelOffset != null) { + textConfig.offset = labelOffset; + } + if (labelRotate != null) { + labelRotate *= Math.PI / 180; + textConfig.rotation = labelRotate; + } + if (labelDistance != null) { + textConfig.distance = labelDistance; + } + // fill and auto is determined by the color of path fill if it's not specified by developers. + textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto'; + return textConfig; + } + /** + * The uniform entry of set text style, that is, retrieve style definitions + * from `model` and set to `textStyle` object. + * + * Never in merge mode, but in overwrite mode, that is, all of the text style + * properties will be set. (Consider the states of normal and emphasis and + * default value can be adopted, merge would make the logic too complicated + * to manage.) + */ + function setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) { + // Consider there will be abnormal when merge hover style to normal style if given default value. + opt = opt || EMPTY_OBJ; + var ecModel = textStyleModel.ecModel; + var globalTextStyle = ecModel && ecModel.option.textStyle; + // Consider case: + // { + // data: [{ + // value: 12, + // label: { + // rich: { + // // no 'a' here but using parent 'a'. + // } + // } + // }], + // rich: { + // a: { ... } + // } + // } + var richItemNames = getRichItemNames(textStyleModel); + var richResult; + if (richItemNames) { + richResult = {}; + for (var name_1 in richItemNames) { + if (richItemNames.hasOwnProperty(name_1)) { + // Cascade is supported in rich. + var richTextStyle = textStyleModel.getModel(['rich', name_1]); + // In rich, never `disableBox`. + // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`, + // the default color `'blue'` will not be adopted if no color declared in `rich`. + // That might confuses users. So probably we should put `textStyleModel` as the + // root ancestor of the `richTextStyle`. But that would be a break change. + setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true); + } + } + } + if (richResult) { + textStyle.rich = richResult; + } + var overflow = textStyleModel.get('overflow'); + if (overflow) { + textStyle.overflow = overflow; + } + var margin = textStyleModel.get('minMargin'); + if (margin != null) { + textStyle.margin = margin; + } + setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false); + } + // Consider case: + // { + // data: [{ + // value: 12, + // label: { + // rich: { + // // no 'a' here but using parent 'a'. + // } + // } + // }], + // rich: { + // a: { ... } + // } + // } + // TODO TextStyleModel + function getRichItemNames(textStyleModel) { + // Use object to remove duplicated names. + var richItemNameMap; + while (textStyleModel && textStyleModel !== textStyleModel.ecModel) { + var rich = (textStyleModel.option || EMPTY_OBJ).rich; + if (rich) { + richItemNameMap = richItemNameMap || {}; + var richKeys = keys(rich); + for (var i = 0; i < richKeys.length; i++) { + var richKey = richKeys[i]; + richItemNameMap[richKey] = 1; + } + } + textStyleModel = textStyleModel.parentModel; + } + return richItemNameMap; + } + var TEXT_PROPS_WITH_GLOBAL = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY']; + var TEXT_PROPS_SELF = ['align', 'lineHeight', 'width', 'height', 'tag', 'verticalAlign', 'ellipsis']; + var TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; + function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) { + // In merge mode, default value should not be given. + globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ; + var inheritColor = opt && opt.inheritColor; + var fillColor = textStyleModel.getShallow('color'); + var strokeColor = textStyleModel.getShallow('textBorderColor'); + var opacity = retrieve2(textStyleModel.getShallow('opacity'), globalTextStyle.opacity); + if (fillColor === 'inherit' || fillColor === 'auto') { + if ("development" !== 'production') { + if (fillColor === 'auto') { + deprecateReplaceLog('color: \'auto\'', 'color: \'inherit\''); + } + } + if (inheritColor) { + fillColor = inheritColor; + } else { + fillColor = null; + } + } + if (strokeColor === 'inherit' || strokeColor === 'auto') { + if ("development" !== 'production') { + if (strokeColor === 'auto') { + deprecateReplaceLog('color: \'auto\'', 'color: \'inherit\''); + } + } + if (inheritColor) { + strokeColor = inheritColor; + } else { + strokeColor = null; + } + } + if (!isAttached) { + // Only use default global textStyle.color if text is individual. + // Otherwise it will use the strategy of attached text color because text may be on a path. + fillColor = fillColor || globalTextStyle.color; + strokeColor = strokeColor || globalTextStyle.textBorderColor; + } + if (fillColor != null) { + textStyle.fill = fillColor; + } + if (strokeColor != null) { + textStyle.stroke = strokeColor; + } + var textBorderWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth); + if (textBorderWidth != null) { + textStyle.lineWidth = textBorderWidth; + } + var textBorderType = retrieve2(textStyleModel.getShallow('textBorderType'), globalTextStyle.textBorderType); + if (textBorderType != null) { + textStyle.lineDash = textBorderType; + } + var textBorderDashOffset = retrieve2(textStyleModel.getShallow('textBorderDashOffset'), globalTextStyle.textBorderDashOffset); + if (textBorderDashOffset != null) { + textStyle.lineDashOffset = textBorderDashOffset; + } + if (!isNotNormal && opacity == null && !inRich) { + opacity = opt && opt.defaultOpacity; + } + if (opacity != null) { + textStyle.opacity = opacity; + } + // TODO + if (!isNotNormal && !isAttached) { + // Set default finally. + if (textStyle.fill == null && opt.inheritColor) { + textStyle.fill = opt.inheritColor; + } + } + // Do not use `getFont` here, because merge should be supported, where + // part of these properties may be changed in emphasis style, and the + // others should remain their original value got from normal style. + for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) { + var key = TEXT_PROPS_WITH_GLOBAL[i]; + var val = retrieve2(textStyleModel.getShallow(key), globalTextStyle[key]); + if (val != null) { + textStyle[key] = val; + } + } + for (var i = 0; i < TEXT_PROPS_SELF.length; i++) { + var key = TEXT_PROPS_SELF[i]; + var val = textStyleModel.getShallow(key); + if (val != null) { + textStyle[key] = val; + } + } + if (textStyle.verticalAlign == null) { + var baseline = textStyleModel.getShallow('baseline'); + if (baseline != null) { + textStyle.verticalAlign = baseline; + } + } + if (!isBlock || !opt.disableBox) { + for (var i = 0; i < TEXT_PROPS_BOX.length; i++) { + var key = TEXT_PROPS_BOX[i]; + var val = textStyleModel.getShallow(key); + if (val != null) { + textStyle[key] = val; + } + } + var borderType = textStyleModel.getShallow('borderType'); + if (borderType != null) { + textStyle.borderDash = borderType; + } + if ((textStyle.backgroundColor === 'auto' || textStyle.backgroundColor === 'inherit') && inheritColor) { + if ("development" !== 'production') { + if (textStyle.backgroundColor === 'auto') { + deprecateReplaceLog('backgroundColor: \'auto\'', 'backgroundColor: \'inherit\''); + } + } + textStyle.backgroundColor = inheritColor; + } + if ((textStyle.borderColor === 'auto' || textStyle.borderColor === 'inherit') && inheritColor) { + if ("development" !== 'production') { + if (textStyle.borderColor === 'auto') { + deprecateReplaceLog('borderColor: \'auto\'', 'borderColor: \'inherit\''); + } + } + textStyle.borderColor = inheritColor; + } + } + } + function getFont(opt, ecModel) { + var gTextStyleModel = ecModel && ecModel.getModel('textStyle'); + return trim([ + // FIXME in node-canvas fontWeight is before fontStyle + opt.fontStyle || gTextStyleModel && gTextStyleModel.getShallow('fontStyle') || '', opt.fontWeight || gTextStyleModel && gTextStyleModel.getShallow('fontWeight') || '', (opt.fontSize || gTextStyleModel && gTextStyleModel.getShallow('fontSize') || 12) + 'px', opt.fontFamily || gTextStyleModel && gTextStyleModel.getShallow('fontFamily') || 'sans-serif'].join(' ')); + } + var labelInner = makeInner(); + function setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) { + if (!label) { + return; + } + var obj = labelInner(label); + obj.prevValue = obj.value; + obj.value = value; + var normalLabelModel = labelStatesModels.normal; + obj.valueAnimation = normalLabelModel.get('valueAnimation'); + if (obj.valueAnimation) { + obj.precision = normalLabelModel.get('precision'); + obj.defaultInterpolatedText = getDefaultText; + obj.statesModels = labelStatesModels; + } + } + function animateLabelValue(textEl, dataIndex, data, animatableModel, labelFetcher) { + var labelInnerStore = labelInner(textEl); + if (!labelInnerStore.valueAnimation || labelInnerStore.prevValue === labelInnerStore.value) { + // Value not changed, no new label animation + return; + } + var defaultInterpolatedText = labelInnerStore.defaultInterpolatedText; + // Consider the case that being animating, do not use the `obj.value`, + // Otherwise it will jump to the `obj.value` when this new animation started. + var currValue = retrieve2(labelInnerStore.interpolatedValue, labelInnerStore.prevValue); + var targetValue = labelInnerStore.value; + function during(percent) { + var interpolated = interpolateRawValues(data, labelInnerStore.precision, currValue, targetValue, percent); + labelInnerStore.interpolatedValue = percent === 1 ? null : interpolated; + var labelText = getLabelText({ + labelDataIndex: dataIndex, + labelFetcher: labelFetcher, + defaultText: defaultInterpolatedText ? defaultInterpolatedText(interpolated) : interpolated + '' + }, labelInnerStore.statesModels, interpolated); + setLabelText(textEl, labelText); + } + textEl.percent = 0; + (labelInnerStore.prevValue == null ? initProps : updateProps)(textEl, { + // percent is used to prevent animation from being aborted #15916 + percent: 1 + }, animatableModel, dataIndex, null, during); + } + + var PATH_COLOR = ['textStyle', 'color']; + var textStyleParams = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'padding', 'lineHeight', 'rich', 'width', 'height', 'overflow']; + // TODO Performance improvement? + var tmpText = new ZRText(); + var TextStyleMixin = /** @class */function () { + function TextStyleMixin() {} + /** + * Get color property or get color from option.textStyle.color + */ + // TODO Callback + TextStyleMixin.prototype.getTextColor = function (isEmphasis) { + var ecModel = this.ecModel; + return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null); + }; + /** + * Create font string from fontStyle, fontWeight, fontSize, fontFamily + * @return {string} + */ + TextStyleMixin.prototype.getFont = function () { + return getFont({ + fontStyle: this.getShallow('fontStyle'), + fontWeight: this.getShallow('fontWeight'), + fontSize: this.getShallow('fontSize'), + fontFamily: this.getShallow('fontFamily') + }, this.ecModel); + }; + TextStyleMixin.prototype.getTextRect = function (text) { + var style = { + text: text, + verticalAlign: this.getShallow('verticalAlign') || this.getShallow('baseline') + }; + for (var i = 0; i < textStyleParams.length; i++) { + style[textStyleParams[i]] = this.getShallow(textStyleParams[i]); + } + tmpText.useStyle(style); + tmpText.update(); + return tmpText.getBoundingRect(); + }; + return TextStyleMixin; + }(); + + var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] + // Option decal is in `DecalObject` but style.decal is in `PatternObject`. + // So do not transfer decal directly. + ]; + + var getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP); + var LineStyleMixin = /** @class */function () { + function LineStyleMixin() {} + LineStyleMixin.prototype.getLineStyle = function (excludes) { + return getLineStyle(this, excludes); + }; + return LineStyleMixin; + }(); + + var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] + // Option decal is in `DecalObject` but style.decal is in `PatternObject`. + // So do not transfer decal directly. + ]; + + var getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP); + var ItemStyleMixin = /** @class */function () { + function ItemStyleMixin() {} + ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) { + return getItemStyle(this, excludes, includes); + }; + return ItemStyleMixin; + }(); + + var Model = /** @class */function () { + function Model(option, parentModel, ecModel) { + this.parentModel = parentModel; + this.ecModel = ecModel; + this.option = option; + // Simple optimization + // if (this.init) { + // if (arguments.length <= 4) { + // this.init(option, parentModel, ecModel, extraOpt); + // } + // else { + // this.init.apply(this, arguments); + // } + // } + } + + Model.prototype.init = function (option, parentModel, ecModel) { + var rest = []; + for (var _i = 3; _i < arguments.length; _i++) { + rest[_i - 3] = arguments[_i]; + } + }; + /** + * Merge the input option to me. + */ + Model.prototype.mergeOption = function (option, ecModel) { + merge(this.option, option, true); + }; + // `path` can be 'a.b.c', so the return value type have to be `ModelOption` + // TODO: TYPE strict key check? + // get(path: string | string[], ignoreParent?: boolean): ModelOption; + Model.prototype.get = function (path, ignoreParent) { + if (path == null) { + return this.option; + } + return this._doGet(this.parsePath(path), !ignoreParent && this.parentModel); + }; + Model.prototype.getShallow = function (key, ignoreParent) { + var option = this.option; + var val = option == null ? option : option[key]; + if (val == null && !ignoreParent) { + var parentModel = this.parentModel; + if (parentModel) { + // FIXME:TS do not know how to make it works + val = parentModel.getShallow(key); + } + } + return val; + }; + // `path` can be 'a.b.c', so the return value type have to be `Model` + // getModel(path: string | string[], parentModel?: Model): Model; + // TODO 'a.b.c' is deprecated + Model.prototype.getModel = function (path, parentModel) { + var hasPath = path != null; + var pathFinal = hasPath ? this.parsePath(path) : null; + var obj = hasPath ? this._doGet(pathFinal) : this.option; + parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal)); + return new Model(obj, parentModel, this.ecModel); + }; + /** + * If model has option + */ + Model.prototype.isEmpty = function () { + return this.option == null; + }; + Model.prototype.restoreData = function () {}; + // Pending + Model.prototype.clone = function () { + var Ctor = this.constructor; + return new Ctor(clone(this.option)); + }; + // setReadOnly(properties): void { + // clazzUtil.setReadOnly(this, properties); + // } + // If path is null/undefined, return null/undefined. + Model.prototype.parsePath = function (path) { + if (typeof path === 'string') { + return path.split('.'); + } + return path; + }; + // Resolve path for parent. Perhaps useful when parent use a different property. + // Default to be a identity resolver. + // Can be modified to a different resolver. + Model.prototype.resolveParentPath = function (path) { + return path; + }; + // FIXME:TS check whether put this method here + Model.prototype.isAnimationEnabled = function () { + if (!env.node && this.option) { + if (this.option.animation != null) { + return !!this.option.animation; + } else if (this.parentModel) { + return this.parentModel.isAnimationEnabled(); + } + } + }; + Model.prototype._doGet = function (pathArr, parentModel) { + var obj = this.option; + if (!pathArr) { + return obj; + } + for (var i = 0; i < pathArr.length; i++) { + // Ignore empty + if (!pathArr[i]) { + continue; + } + // obj could be number/string/... (like 0) + obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null; + if (obj == null) { + break; + } + } + if (obj == null && parentModel) { + obj = parentModel._doGet(this.resolveParentPath(pathArr), parentModel.parentModel); + } + return obj; + }; + return Model; + }(); + // Enable Model.extend. + enableClassExtend(Model); + enableClassCheck(Model); + mixin(Model, LineStyleMixin); + mixin(Model, ItemStyleMixin); + mixin(Model, AreaStyleMixin); + mixin(Model, TextStyleMixin); + + // A random offset + var base = Math.round(Math.random() * 10); + /** + * @public + * @param {string} type + * @return {string} + */ + function getUID(type) { + // Considering the case of crossing js context, + // use Math.random to make id as unique as possible. + return [type || '', base++].join('_'); + } + /** + * Implements `SubTypeDefaulterManager` for `target`. + */ + function enableSubTypeDefaulter(target) { + var subTypeDefaulters = {}; + target.registerSubTypeDefaulter = function (componentType, defaulter) { + var componentTypeInfo = parseClassType(componentType); + subTypeDefaulters[componentTypeInfo.main] = defaulter; + }; + target.determineSubType = function (componentType, option) { + var type = option.type; + if (!type) { + var componentTypeMain = parseClassType(componentType).main; + if (target.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) { + type = subTypeDefaulters[componentTypeMain](option); + } + } + return type; + }; + } + /** + * Implements `TopologicalTravelable` for `entity`. + * + * Topological travel on Activity Network (Activity On Vertices). + * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis']. + * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology. + * If there is circular dependencey, Error will be thrown. + */ + function enableTopologicalTravel(entity, dependencyGetter) { + /** + * @param targetNameList Target Component type list. + * Can be ['aa', 'bb', 'aa.xx'] + * @param fullNameList By which we can build dependency graph. + * @param callback Params: componentType, dependencies. + * @param context Scope of callback. + */ + entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) { + if (!targetNameList.length) { + return; + } + var result = makeDepndencyGraph(fullNameList); + var graph = result.graph; + var noEntryList = result.noEntryList; + var targetNameSet = {}; + each(targetNameList, function (name) { + targetNameSet[name] = true; + }); + while (noEntryList.length) { + var currComponentType = noEntryList.pop(); + var currVertex = graph[currComponentType]; + var isInTargetNameSet = !!targetNameSet[currComponentType]; + if (isInTargetNameSet) { + callback.call(context, currComponentType, currVertex.originalDeps.slice()); + delete targetNameSet[currComponentType]; + } + each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge); + } + each(targetNameSet, function () { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList); + } + throw new Error(errMsg); + }); + function removeEdge(succComponentType) { + graph[succComponentType].entryCount--; + if (graph[succComponentType].entryCount === 0) { + noEntryList.push(succComponentType); + } + } + // Consider this case: legend depends on series, and we call + // chart.setOption({series: [...]}), where only series is in option. + // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will + // not be called, but only sereis.mergeOption is called. Thus legend + // have no chance to update its local record about series (like which + // name of series is available in legend). + function removeEdgeAndAdd(succComponentType) { + targetNameSet[succComponentType] = true; + removeEdge(succComponentType); + } + }; + function makeDepndencyGraph(fullNameList) { + var graph = {}; + var noEntryList = []; + each(fullNameList, function (name) { + var thisItem = createDependencyGraphItem(graph, name); + var originalDeps = thisItem.originalDeps = dependencyGetter(name); + var availableDeps = getAvailableDependencies(originalDeps, fullNameList); + thisItem.entryCount = availableDeps.length; + if (thisItem.entryCount === 0) { + noEntryList.push(name); + } + each(availableDeps, function (dependentName) { + if (indexOf(thisItem.predecessor, dependentName) < 0) { + thisItem.predecessor.push(dependentName); + } + var thatItem = createDependencyGraphItem(graph, dependentName); + if (indexOf(thatItem.successor, dependentName) < 0) { + thatItem.successor.push(name); + } + }); + }); + return { + graph: graph, + noEntryList: noEntryList + }; + } + function createDependencyGraphItem(graph, name) { + if (!graph[name]) { + graph[name] = { + predecessor: [], + successor: [] + }; + } + return graph[name]; + } + function getAvailableDependencies(originalDeps, fullNameList) { + var availableDeps = []; + each(originalDeps, function (dep) { + indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep); + }); + return availableDeps; + } + } + function inheritDefaultOption(superOption, subOption) { + // See also `model/Component.ts#getDefaultOption` + return merge(merge({}, superOption, true), subOption, true); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /** + * Language: English. + */ + var langEN = { + time: { + month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], + monthAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], + dayOfWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], + dayOfWeekAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] + }, + legend: { + selector: { + all: 'All', + inverse: 'Inv' + } + }, + toolbox: { + brush: { + title: { + rect: 'Box Select', + polygon: 'Lasso Select', + lineX: 'Horizontally Select', + lineY: 'Vertically Select', + keep: 'Keep Selections', + clear: 'Clear Selections' + } + }, + dataView: { + title: 'Data View', + lang: ['Data View', 'Close', 'Refresh'] + }, + dataZoom: { + title: { + zoom: 'Zoom', + back: 'Zoom Reset' + } + }, + magicType: { + title: { + line: 'Switch to Line Chart', + bar: 'Switch to Bar Chart', + stack: 'Stack', + tiled: 'Tile' + } + }, + restore: { + title: 'Restore' + }, + saveAsImage: { + title: 'Save as Image', + lang: ['Right Click to Save Image'] + } + }, + series: { + typeNames: { + pie: 'Pie chart', + bar: 'Bar chart', + line: 'Line chart', + scatter: 'Scatter plot', + effectScatter: 'Ripple scatter plot', + radar: 'Radar chart', + tree: 'Tree', + treemap: 'Treemap', + boxplot: 'Boxplot', + candlestick: 'Candlestick', + k: 'K line chart', + heatmap: 'Heat map', + map: 'Map', + parallel: 'Parallel coordinate map', + lines: 'Line graph', + graph: 'Relationship graph', + sankey: 'Sankey diagram', + funnel: 'Funnel chart', + gauge: 'Gauge', + pictorialBar: 'Pictorial bar', + themeRiver: 'Theme River Map', + sunburst: 'Sunburst', + custom: 'Custom chart', + chart: 'Chart' + } + }, + aria: { + general: { + withTitle: 'This is a chart about "{title}"', + withoutTitle: 'This is a chart' + }, + series: { + single: { + prefix: '', + withName: ' with type {seriesType} named {seriesName}.', + withoutName: ' with type {seriesType}.' + }, + multiple: { + prefix: '. It consists of {seriesCount} series count.', + withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.', + withoutName: ' The {seriesId} series is a {seriesType}.', + separator: { + middle: '', + end: '' + } + } + }, + data: { + allData: 'The data is as follows: ', + partialData: 'The first {displayCnt} items are: ', + withName: 'the data for {name} is {value}', + withoutName: '{value}', + separator: { + middle: ', ', + end: '. ' + } + } + } + }; + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var langZH = { + time: { + month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + monthAbbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + dayOfWeek: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + dayOfWeekAbbr: ['日', '一', '二', '三', '四', '五', '六'] + }, + legend: { + selector: { + all: '全选', + inverse: '反选' + } + }, + toolbox: { + brush: { + title: { + rect: '矩形选择', + polygon: '圈选', + lineX: '横向选择', + lineY: '纵向选择', + keep: '保持选择', + clear: '清除选择' + } + }, + dataView: { + title: '数据视图', + lang: ['数据视图', '关闭', '刷新'] + }, + dataZoom: { + title: { + zoom: '区域缩放', + back: '区域缩放还原' + } + }, + magicType: { + title: { + line: '切换为折线图', + bar: '切换为柱状图', + stack: '切换为堆叠', + tiled: '切换为平铺' + } + }, + restore: { + title: '还原' + }, + saveAsImage: { + title: '保存为图片', + lang: ['右键另存为图片'] + } + }, + series: { + typeNames: { + pie: '饼图', + bar: '柱状图', + line: '折线图', + scatter: '散点图', + effectScatter: '涟漪散点图', + radar: '雷达图', + tree: '树图', + treemap: '矩形树图', + boxplot: '箱型图', + candlestick: 'K线图', + k: 'K线图', + heatmap: '热力图', + map: '地图', + parallel: '平行坐标图', + lines: '线图', + graph: '关系图', + sankey: '桑基图', + funnel: '漏斗图', + gauge: '仪表盘图', + pictorialBar: '象形柱图', + themeRiver: '主题河流图', + sunburst: '旭日图', + custom: '自定义图表', + chart: '图表' + } + }, + aria: { + general: { + withTitle: '这是一个关于“{title}”的图表。', + withoutTitle: '这是一个图表,' + }, + series: { + single: { + prefix: '', + withName: '图表类型是{seriesType},表示{seriesName}。', + withoutName: '图表类型是{seriesType}。' + }, + multiple: { + prefix: '它由{seriesCount}个图表系列组成。', + withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType},', + withoutName: '第{seriesId}个系列是一个{seriesType},', + separator: { + middle: ';', + end: '。' + } + } + }, + data: { + allData: '其数据是——', + partialData: '其中,前{displayCnt}项是——', + withName: '{name}的数据是{value}', + withoutName: '{value}', + separator: { + middle: ',', + end: '' + } + } + } + }; + + var LOCALE_ZH = 'ZH'; + var LOCALE_EN = 'EN'; + var DEFAULT_LOCALE = LOCALE_EN; + var localeStorage = {}; + var localeModels = {}; + var SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () { + var langStr = ( /* eslint-disable-next-line */ + document.documentElement.lang || navigator.language || navigator.browserLanguage || DEFAULT_LOCALE).toUpperCase(); + return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE; + }(); + function registerLocale(locale, localeObj) { + locale = locale.toUpperCase(); + localeModels[locale] = new Model(localeObj); + localeStorage[locale] = localeObj; + } + // export function getLocale(locale: string) { + // return localeStorage[locale]; + // } + function createLocaleObject(locale) { + if (isString(locale)) { + var localeObj = localeStorage[locale.toUpperCase()] || {}; + if (locale === LOCALE_ZH || locale === LOCALE_EN) { + return clone(localeObj); + } else { + return merge(clone(localeObj), clone(localeStorage[DEFAULT_LOCALE]), false); + } + } else { + return merge(clone(locale), clone(localeStorage[DEFAULT_LOCALE]), false); + } + } + function getLocaleModel(lang) { + return localeModels[lang]; + } + function getDefaultLocaleModel() { + return localeModels[DEFAULT_LOCALE]; + } + // Default locale + registerLocale(LOCALE_EN, langEN); + registerLocale(LOCALE_ZH, langZH); + + var ONE_SECOND = 1000; + var ONE_MINUTE = ONE_SECOND * 60; + var ONE_HOUR = ONE_MINUTE * 60; + var ONE_DAY = ONE_HOUR * 24; + var ONE_YEAR = ONE_DAY * 365; + var defaultLeveledFormatter = { + year: '{yyyy}', + month: '{MMM}', + day: '{d}', + hour: '{HH}:{mm}', + minute: '{HH}:{mm}', + second: '{HH}:{mm}:{ss}', + millisecond: '{HH}:{mm}:{ss} {SSS}', + none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}' + }; + var fullDayFormatter = '{yyyy}-{MM}-{dd}'; + var fullLeveledFormatter = { + year: '{yyyy}', + month: '{yyyy}-{MM}', + day: fullDayFormatter, + hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour, + minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute, + second: fullDayFormatter + ' ' + defaultLeveledFormatter.second, + millisecond: defaultLeveledFormatter.none + }; + var primaryTimeUnits = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond']; + var timeUnits = ['year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond']; + function pad(str, len) { + str += ''; + return '0000'.substr(0, len - str.length) + str; + } + function getPrimaryTimeUnit(timeUnit) { + switch (timeUnit) { + case 'half-year': + case 'quarter': + return 'month'; + case 'week': + case 'half-week': + return 'day'; + case 'half-day': + case 'quarter-day': + return 'hour'; + default: + // year, minutes, second, milliseconds + return timeUnit; + } + } + function isPrimaryTimeUnit(timeUnit) { + return timeUnit === getPrimaryTimeUnit(timeUnit); + } + function getDefaultFormatPrecisionOfInterval(timeUnit) { + switch (timeUnit) { + case 'year': + case 'month': + return 'day'; + case 'millisecond': + return 'millisecond'; + default: + // Also for day, hour, minute, second + return 'second'; + } + } + function format( + // Note: The result based on `isUTC` are totally different, which can not be just simply + // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory. + time, template, isUTC, lang) { + var date = parseDate(time); + var y = date[fullYearGetterName(isUTC)](); + var M = date[monthGetterName(isUTC)]() + 1; + var q = Math.floor((M - 1) / 3) + 1; + var d = date[dateGetterName(isUTC)](); + var e = date['get' + (isUTC ? 'UTC' : '') + 'Day'](); + var H = date[hoursGetterName(isUTC)](); + var h = (H - 1) % 12 + 1; + var m = date[minutesGetterName(isUTC)](); + var s = date[secondsGetterName(isUTC)](); + var S = date[millisecondsGetterName(isUTC)](); + var localeModel = lang instanceof Model ? lang : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel(); + var timeModel = localeModel.getModel('time'); + var month = timeModel.get('month'); + var monthAbbr = timeModel.get('monthAbbr'); + var dayOfWeek = timeModel.get('dayOfWeek'); + var dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr'); + return (template || '').replace(/{yyyy}/g, y + '').replace(/{yy}/g, pad(y % 100 + '', 2)).replace(/{Q}/g, q + '').replace(/{MMMM}/g, month[M - 1]).replace(/{MMM}/g, monthAbbr[M - 1]).replace(/{MM}/g, pad(M, 2)).replace(/{M}/g, M + '').replace(/{dd}/g, pad(d, 2)).replace(/{d}/g, d + '').replace(/{eeee}/g, dayOfWeek[e]).replace(/{ee}/g, dayOfWeekAbbr[e]).replace(/{e}/g, e + '').replace(/{HH}/g, pad(H, 2)).replace(/{H}/g, H + '').replace(/{hh}/g, pad(h + '', 2)).replace(/{h}/g, h + '').replace(/{mm}/g, pad(m, 2)).replace(/{m}/g, m + '').replace(/{ss}/g, pad(s, 2)).replace(/{s}/g, s + '').replace(/{SSS}/g, pad(S, 3)).replace(/{S}/g, S + ''); + } + function leveledFormat(tick, idx, formatter, lang, isUTC) { + var template = null; + if (isString(formatter)) { + // Single formatter for all units at all levels + template = formatter; + } else if (isFunction(formatter)) { + // Callback formatter + template = formatter(tick.value, idx, { + level: tick.level + }); + } else { + var defaults$1 = extend({}, defaultLeveledFormatter); + if (tick.level > 0) { + for (var i = 0; i < primaryTimeUnits.length; ++i) { + defaults$1[primaryTimeUnits[i]] = "{primary|" + defaults$1[primaryTimeUnits[i]] + "}"; + } + } + var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units + : defaults(formatter, defaults$1) : defaults$1; + var unit = getUnitFromValue(tick.value, isUTC); + if (mergedFormatter[unit]) { + template = mergedFormatter[unit]; + } else if (mergedFormatter.inherit) { + // Unit formatter is not defined and should inherit from bigger units + var targetId = timeUnits.indexOf(unit); + for (var i = targetId - 1; i >= 0; --i) { + if (mergedFormatter[unit]) { + template = mergedFormatter[unit]; + break; + } + } + template = template || defaults$1.none; + } + if (isArray(template)) { + var levelId = tick.level == null ? 0 : tick.level >= 0 ? tick.level : template.length + tick.level; + levelId = Math.min(levelId, template.length - 1); + template = template[levelId]; + } + } + return format(new Date(tick.value), template, isUTC, lang); + } + function getUnitFromValue(value, isUTC) { + var date = parseDate(value); + var M = date[monthGetterName(isUTC)]() + 1; + var d = date[dateGetterName(isUTC)](); + var h = date[hoursGetterName(isUTC)](); + var m = date[minutesGetterName(isUTC)](); + var s = date[secondsGetterName(isUTC)](); + var S = date[millisecondsGetterName(isUTC)](); + var isSecond = S === 0; + var isMinute = isSecond && s === 0; + var isHour = isMinute && m === 0; + var isDay = isHour && h === 0; + var isMonth = isDay && d === 1; + var isYear = isMonth && M === 1; + if (isYear) { + return 'year'; + } else if (isMonth) { + return 'month'; + } else if (isDay) { + return 'day'; + } else if (isHour) { + return 'hour'; + } else if (isMinute) { + return 'minute'; + } else if (isSecond) { + return 'second'; + } else { + return 'millisecond'; + } + } + function getUnitValue(value, unit, isUTC) { + var date = isNumber(value) ? parseDate(value) : value; + unit = unit || getUnitFromValue(value, isUTC); + switch (unit) { + case 'year': + return date[fullYearGetterName(isUTC)](); + case 'half-year': + return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0; + case 'quarter': + return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4); + case 'month': + return date[monthGetterName(isUTC)](); + case 'day': + return date[dateGetterName(isUTC)](); + case 'half-day': + return date[hoursGetterName(isUTC)]() / 24; + case 'hour': + return date[hoursGetterName(isUTC)](); + case 'minute': + return date[minutesGetterName(isUTC)](); + case 'second': + return date[secondsGetterName(isUTC)](); + case 'millisecond': + return date[millisecondsGetterName(isUTC)](); + } + } + function fullYearGetterName(isUTC) { + return isUTC ? 'getUTCFullYear' : 'getFullYear'; + } + function monthGetterName(isUTC) { + return isUTC ? 'getUTCMonth' : 'getMonth'; + } + function dateGetterName(isUTC) { + return isUTC ? 'getUTCDate' : 'getDate'; + } + function hoursGetterName(isUTC) { + return isUTC ? 'getUTCHours' : 'getHours'; + } + function minutesGetterName(isUTC) { + return isUTC ? 'getUTCMinutes' : 'getMinutes'; + } + function secondsGetterName(isUTC) { + return isUTC ? 'getUTCSeconds' : 'getSeconds'; + } + function millisecondsGetterName(isUTC) { + return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds'; + } + function fullYearSetterName(isUTC) { + return isUTC ? 'setUTCFullYear' : 'setFullYear'; + } + function monthSetterName(isUTC) { + return isUTC ? 'setUTCMonth' : 'setMonth'; + } + function dateSetterName(isUTC) { + return isUTC ? 'setUTCDate' : 'setDate'; + } + function hoursSetterName(isUTC) { + return isUTC ? 'setUTCHours' : 'setHours'; + } + function minutesSetterName(isUTC) { + return isUTC ? 'setUTCMinutes' : 'setMinutes'; + } + function secondsSetterName(isUTC) { + return isUTC ? 'setUTCSeconds' : 'setSeconds'; + } + function millisecondsSetterName(isUTC) { + return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds'; + } + + function getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) { + var textEl = new ZRText({ + style: { + text: text, + font: font, + align: align, + verticalAlign: verticalAlign, + padding: padding, + rich: rich, + overflow: truncate ? 'truncate' : null, + lineHeight: lineHeight + } + }); + return textEl.getBoundingRect(); + } + + /** + * Add a comma each three digit. + */ + function addCommas(x) { + if (!isNumeric(x)) { + return isString(x) ? x : '-'; + } + var parts = (x + '').split('.'); + return parts[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,') + (parts.length > 1 ? '.' + parts[1] : ''); + } + function toCamelCase(str, upperCaseFirst) { + str = (str || '').toLowerCase().replace(/-(.)/g, function (match, group1) { + return group1.toUpperCase(); + }); + if (upperCaseFirst && str) { + str = str.charAt(0).toUpperCase() + str.slice(1); + } + return str; + } + var normalizeCssArray$1 = normalizeCssArray; + /** + * Make value user readable for tooltip and label. + * "User readable": + * Try to not print programmer-specific text like NaN, Infinity, null, undefined. + * Avoid to display an empty string, which users can not recognize there is + * a value and it might look like a bug. + */ + function makeValueReadable(value, valueType, useUTC) { + var USER_READABLE_DEFUALT_TIME_PATTERN = '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss}'; + function stringToUserReadable(str) { + return str && trim(str) ? str : '-'; + } + function isNumberUserReadable(num) { + return !!(num != null && !isNaN(num) && isFinite(num)); + } + var isTypeTime = valueType === 'time'; + var isValueDate = value instanceof Date; + if (isTypeTime || isValueDate) { + var date = isTypeTime ? parseDate(value) : value; + if (!isNaN(+date)) { + return format(date, USER_READABLE_DEFUALT_TIME_PATTERN, useUTC); + } else if (isValueDate) { + return '-'; + } + // In other cases, continue to try to display the value in the following code. + } + + if (valueType === 'ordinal') { + return isStringSafe(value) ? stringToUserReadable(value) : isNumber(value) ? isNumberUserReadable(value) ? value + '' : '-' : '-'; + } + // By default. + var numericResult = numericToNumber(value); + return isNumberUserReadable(numericResult) ? addCommas(numericResult) : isStringSafe(value) ? stringToUserReadable(value) : typeof value === 'boolean' ? value + '' : '-'; + } + var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; + var wrapVar = function (varName, seriesIdx) { + return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}'; + }; + /** + * Template formatter + * @param {Array.|Object} paramsList + */ + function formatTpl(tpl, paramsList, encode) { + if (!isArray(paramsList)) { + paramsList = [paramsList]; + } + var seriesLen = paramsList.length; + if (!seriesLen) { + return ''; + } + var $vars = paramsList[0].$vars || []; + for (var i = 0; i < $vars.length; i++) { + var alias = TPL_VAR_ALIAS[i]; + tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0)); + } + for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) { + for (var k = 0; k < $vars.length; k++) { + var val = paramsList[seriesIdx][$vars[k]]; + tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val); + } + } + return tpl; + } + /** + * simple Template formatter + */ + function formatTplSimple(tpl, param, encode) { + each(param, function (value, key) { + tpl = tpl.replace('{' + key + '}', encode ? encodeHTML(value) : value); + }); + return tpl; + } + function getTooltipMarker(inOpt, extraCssText) { + var opt = isString(inOpt) ? { + color: inOpt, + extraCssText: extraCssText + } : inOpt || {}; + var color = opt.color; + var type = opt.type; + extraCssText = opt.extraCssText; + var renderMode = opt.renderMode || 'html'; + if (!color) { + return ''; + } + if (renderMode === 'html') { + return type === 'subItem' ? '' : ''; + } else { + // Should better not to auto generate style name by auto-increment number here. + // Because this util is usually called in tooltip formatter, which is probably + // called repeatedly when mouse move and the auto-increment number increases fast. + // Users can make their own style name by theirselves, make it unique and readable. + var markerId = opt.markerId || 'markerX'; + return { + renderMode: renderMode, + content: '{' + markerId + '|} ', + style: type === 'subItem' ? { + width: 4, + height: 4, + borderRadius: 2, + backgroundColor: color + } : { + width: 10, + height: 10, + borderRadius: 5, + backgroundColor: color + } + }; + } + } + /** + * @deprecated Use `time/format` instead. + * ISO Date format + * @param {string} tpl + * @param {number} value + * @param {boolean} [isUTC=false] Default in local time. + * see `module:echarts/scale/Time` + * and `module:echarts/util/number#parseDate`. + * @inner + */ + function formatTime(tpl, value, isUTC) { + if ("development" !== 'production') { + deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format'); + } + if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') { + tpl = 'MM-dd\nyyyy'; + } + var date = parseDate(value); + var getUTC = isUTC ? 'getUTC' : 'get'; + var y = date[getUTC + 'FullYear'](); + var M = date[getUTC + 'Month']() + 1; + var d = date[getUTC + 'Date'](); + var h = date[getUTC + 'Hours'](); + var m = date[getUTC + 'Minutes'](); + var s = date[getUTC + 'Seconds'](); + var S = date[getUTC + 'Milliseconds'](); + tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', pad(y % 100 + '', 2)).replace('dd', pad(d, 2)).replace('d', d).replace('hh', pad(h, 2)).replace('h', h).replace('mm', pad(m, 2)).replace('m', m).replace('ss', pad(s, 2)).replace('s', s).replace('SSS', pad(S, 3)); + return tpl; + } + /** + * Capital first + * @param {string} str + * @return {string} + */ + function capitalFirst(str) { + return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; + } + /** + * @return Never be null/undefined. + */ + function convertToColorString(color, defaultColor) { + defaultColor = defaultColor || 'transparent'; + return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor; + } + /** + * open new tab + * @param link url + * @param target blank or self + */ + function windowOpen(link, target) { + /* global window */ + if (target === '_blank' || target === 'blank') { + var blank = window.open(); + blank.opener = null; + blank.location.href = link; + } else { + window.open(link, target); + } + } + + var each$1 = each; + /** + * @public + */ + var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height']; + /** + * @public + */ + var HV_NAMES = [['width', 'left', 'right'], ['height', 'top', 'bottom']]; + function boxLayout(orient, group, gap, maxWidth, maxHeight) { + var x = 0; + var y = 0; + if (maxWidth == null) { + maxWidth = Infinity; + } + if (maxHeight == null) { + maxHeight = Infinity; + } + var currentLineMaxSize = 0; + group.eachChild(function (child, idx) { + var rect = child.getBoundingRect(); + var nextChild = group.childAt(idx + 1); + var nextChildRect = nextChild && nextChild.getBoundingRect(); + var nextX; + var nextY; + if (orient === 'horizontal') { + var moveX = rect.width + (nextChildRect ? -nextChildRect.x + rect.x : 0); + nextX = x + moveX; + // Wrap when width exceeds maxWidth or meet a `newline` group + // FIXME compare before adding gap? + if (nextX > maxWidth || child.newline) { + x = 0; + nextX = moveX; + y += currentLineMaxSize + gap; + currentLineMaxSize = rect.height; + } else { + // FIXME: consider rect.y is not `0`? + currentLineMaxSize = Math.max(currentLineMaxSize, rect.height); + } + } else { + var moveY = rect.height + (nextChildRect ? -nextChildRect.y + rect.y : 0); + nextY = y + moveY; + // Wrap when width exceeds maxHeight or meet a `newline` group + if (nextY > maxHeight || child.newline) { + x += currentLineMaxSize + gap; + y = 0; + nextY = moveY; + currentLineMaxSize = rect.width; + } else { + currentLineMaxSize = Math.max(currentLineMaxSize, rect.width); + } + } + if (child.newline) { + return; + } + child.x = x; + child.y = y; + child.markRedraw(); + orient === 'horizontal' ? x = nextX + gap : y = nextY + gap; + }); + } + /** + * VBox or HBox layouting + * @param {string} orient + * @param {module:zrender/graphic/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + var box = boxLayout; + /** + * VBox layouting + * @param {module:zrender/graphic/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + var vbox = curry(boxLayout, 'vertical'); + /** + * HBox layouting + * @param {module:zrender/graphic/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + var hbox = curry(boxLayout, 'horizontal'); + /** + * If x or x2 is not specified or 'center' 'left' 'right', + * the width would be as long as possible. + * If y or y2 is not specified or 'middle' 'top' 'bottom', + * the height would be as long as possible. + */ + function getAvailableSize(positionInfo, containerRect, margin) { + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + var x = parsePercent$1(positionInfo.left, containerWidth); + var y = parsePercent$1(positionInfo.top, containerHeight); + var x2 = parsePercent$1(positionInfo.right, containerWidth); + var y2 = parsePercent$1(positionInfo.bottom, containerHeight); + (isNaN(x) || isNaN(parseFloat(positionInfo.left))) && (x = 0); + (isNaN(x2) || isNaN(parseFloat(positionInfo.right))) && (x2 = containerWidth); + (isNaN(y) || isNaN(parseFloat(positionInfo.top))) && (y = 0); + (isNaN(y2) || isNaN(parseFloat(positionInfo.bottom))) && (y2 = containerHeight); + margin = normalizeCssArray$1(margin || 0); + return { + width: Math.max(x2 - x - margin[1] - margin[3], 0), + height: Math.max(y2 - y - margin[0] - margin[2], 0) + }; + } + /** + * Parse position info. + */ + function getLayoutRect(positionInfo, containerRect, margin) { + margin = normalizeCssArray$1(margin || 0); + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + var left = parsePercent$1(positionInfo.left, containerWidth); + var top = parsePercent$1(positionInfo.top, containerHeight); + var right = parsePercent$1(positionInfo.right, containerWidth); + var bottom = parsePercent$1(positionInfo.bottom, containerHeight); + var width = parsePercent$1(positionInfo.width, containerWidth); + var height = parsePercent$1(positionInfo.height, containerHeight); + var verticalMargin = margin[2] + margin[0]; + var horizontalMargin = margin[1] + margin[3]; + var aspect = positionInfo.aspect; + // If width is not specified, calculate width from left and right + if (isNaN(width)) { + width = containerWidth - right - horizontalMargin - left; + } + if (isNaN(height)) { + height = containerHeight - bottom - verticalMargin - top; + } + if (aspect != null) { + // If width and height are not given + // 1. Graph should not exceeds the container + // 2. Aspect must be keeped + // 3. Graph should take the space as more as possible + // FIXME + // Margin is not considered, because there is no case that both + // using margin and aspect so far. + if (isNaN(width) && isNaN(height)) { + if (aspect > containerWidth / containerHeight) { + width = containerWidth * 0.8; + } else { + height = containerHeight * 0.8; + } + } + // Calculate width or height with given aspect + if (isNaN(width)) { + width = aspect * height; + } + if (isNaN(height)) { + height = width / aspect; + } + } + // If left is not specified, calculate left from right and width + if (isNaN(left)) { + left = containerWidth - right - width - horizontalMargin; + } + if (isNaN(top)) { + top = containerHeight - bottom - height - verticalMargin; + } + // Align left and top + switch (positionInfo.left || positionInfo.right) { + case 'center': + left = containerWidth / 2 - width / 2 - margin[3]; + break; + case 'right': + left = containerWidth - width - horizontalMargin; + break; + } + switch (positionInfo.top || positionInfo.bottom) { + case 'middle': + case 'center': + top = containerHeight / 2 - height / 2 - margin[0]; + break; + case 'bottom': + top = containerHeight - height - verticalMargin; + break; + } + // If something is wrong and left, top, width, height are calculated as NaN + left = left || 0; + top = top || 0; + if (isNaN(width)) { + // Width may be NaN if only one value is given except width + width = containerWidth - horizontalMargin - left - (right || 0); + } + if (isNaN(height)) { + // Height may be NaN if only one value is given except height + height = containerHeight - verticalMargin - top - (bottom || 0); + } + var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); + rect.margin = margin; + return rect; + } + /** + * Position a zr element in viewport + * Group position is specified by either + * {left, top}, {right, bottom} + * If all properties exists, right and bottom will be igonred. + * + * Logic: + * 1. Scale (against origin point in parent coord) + * 2. Rotate (against origin point in parent coord) + * 3. Translate (with el.position by this method) + * So this method only fixes the last step 'Translate', which does not affect + * scaling and rotating. + * + * If be called repeatedly with the same input el, the same result will be gotten. + * + * Return true if the layout happened. + * + * @param el Should have `getBoundingRect` method. + * @param positionInfo + * @param positionInfo.left + * @param positionInfo.top + * @param positionInfo.right + * @param positionInfo.bottom + * @param positionInfo.width Only for opt.boundingModel: 'raw' + * @param positionInfo.height Only for opt.boundingModel: 'raw' + * @param containerRect + * @param margin + * @param opt + * @param opt.hv Only horizontal or only vertical. Default to be [1, 1] + * @param opt.boundingMode + * Specify how to calculate boundingRect when locating. + * 'all': Position the boundingRect that is transformed and uioned + * both itself and its descendants. + * This mode simplies confine the elements in the bounding + * of their container (e.g., using 'right: 0'). + * 'raw': Position the boundingRect that is not transformed and only itself. + * This mode is useful when you want a element can overflow its + * container. (Consider a rotated circle needs to be located in a corner.) + * In this mode positionInfo.width/height can only be number. + */ + function positionElement(el, positionInfo, containerRect, margin, opt, out) { + var h = !opt || !opt.hv || opt.hv[0]; + var v = !opt || !opt.hv || opt.hv[1]; + var boundingMode = opt && opt.boundingMode || 'all'; + out = out || el; + out.x = el.x; + out.y = el.y; + if (!h && !v) { + return false; + } + var rect; + if (boundingMode === 'raw') { + rect = el.type === 'group' ? new BoundingRect(0, 0, +positionInfo.width || 0, +positionInfo.height || 0) : el.getBoundingRect(); + } else { + rect = el.getBoundingRect(); + if (el.needLocalTransform()) { + var transform = el.getLocalTransform(); + // Notice: raw rect may be inner object of el, + // which should not be modified. + rect = rect.clone(); + rect.applyTransform(transform); + } + } + // The real width and height can not be specified but calculated by the given el. + var layoutRect = getLayoutRect(defaults({ + width: rect.width, + height: rect.height + }, positionInfo), containerRect, margin); + // Because 'tranlate' is the last step in transform + // (see zrender/core/Transformable#getLocalTransform), + // we can just only modify el.position to get final result. + var dx = h ? layoutRect.x - rect.x : 0; + var dy = v ? layoutRect.y - rect.y : 0; + if (boundingMode === 'raw') { + out.x = dx; + out.y = dy; + } else { + out.x += dx; + out.y += dy; + } + if (out === el) { + el.markRedraw(); + } + return true; + } + /** + * @param option Contains some of the properties in HV_NAMES. + * @param hvIdx 0: horizontal; 1: vertical. + */ + function sizeCalculable(option, hvIdx) { + return option[HV_NAMES[hvIdx][0]] != null || option[HV_NAMES[hvIdx][1]] != null && option[HV_NAMES[hvIdx][2]] != null; + } + function fetchLayoutMode(ins) { + var layoutMode = ins.layoutMode || ins.constructor.layoutMode; + return isObject(layoutMode) ? layoutMode : layoutMode ? { + type: layoutMode + } : null; + } + /** + * Consider Case: + * When default option has {left: 0, width: 100}, and we set {right: 0} + * through setOption or media query, using normal zrUtil.merge will cause + * {right: 0} does not take effect. + * + * @example + * ComponentModel.extend({ + * init: function () { + * ... + * let inputPositionParams = layout.getLayoutParams(option); + * this.mergeOption(inputPositionParams); + * }, + * mergeOption: function (newOption) { + * newOption && zrUtil.merge(thisOption, newOption, true); + * layout.mergeLayoutParam(thisOption, newOption); + * } + * }); + * + * @param targetOption + * @param newOption + * @param opt + */ + function mergeLayoutParam(targetOption, newOption, opt) { + var ignoreSize = opt && opt.ignoreSize; + !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]); + var hResult = merge(HV_NAMES[0], 0); + var vResult = merge(HV_NAMES[1], 1); + copy(HV_NAMES[0], targetOption, hResult); + copy(HV_NAMES[1], targetOption, vResult); + function merge(names, hvIdx) { + var newParams = {}; + var newValueCount = 0; + var merged = {}; + var mergedValueCount = 0; + var enoughParamNumber = 2; + each$1(names, function (name) { + merged[name] = targetOption[name]; + }); + each$1(names, function (name) { + // Consider case: newOption.width is null, which is + // set by user for removing width setting. + hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]); + hasValue(newParams, name) && newValueCount++; + hasValue(merged, name) && mergedValueCount++; + }); + if (ignoreSize[hvIdx]) { + // Only one of left/right is premitted to exist. + if (hasValue(newOption, names[1])) { + merged[names[2]] = null; + } else if (hasValue(newOption, names[2])) { + merged[names[1]] = null; + } + return merged; + } + // Case: newOption: {width: ..., right: ...}, + // or targetOption: {right: ...} and newOption: {width: ...}, + // There is no conflict when merged only has params count + // little than enoughParamNumber. + if (mergedValueCount === enoughParamNumber || !newValueCount) { + return merged; + } + // Case: newOption: {width: ..., right: ...}, + // Than we can make sure user only want those two, and ignore + // all origin params in targetOption. + else if (newValueCount >= enoughParamNumber) { + return newParams; + } else { + // Chose another param from targetOption by priority. + for (var i = 0; i < names.length; i++) { + var name_1 = names[i]; + if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) { + newParams[name_1] = targetOption[name_1]; + break; + } + } + return newParams; + } + } + function hasProp(obj, name) { + return obj.hasOwnProperty(name); + } + function hasValue(obj, name) { + return obj[name] != null && obj[name] !== 'auto'; + } + function copy(names, target, source) { + each$1(names, function (name) { + target[name] = source[name]; + }); + } + } + /** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + */ + function getLayoutParams(source) { + return copyLayoutParams({}, source); + } + /** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + * @param {Object} source + * @return {Object} Result contains those props. + */ + function copyLayoutParams(target, source) { + source && target && each$1(LOCATION_PARAMS, function (name) { + source.hasOwnProperty(name) && (target[name] = source[name]); + }); + return target; + } + + var inner = makeInner(); + var ComponentModel = /** @class */function (_super) { + __extends(ComponentModel, _super); + function ComponentModel(option, parentModel, ecModel) { + var _this = _super.call(this, option, parentModel, ecModel) || this; + _this.uid = getUID('ec_cpt_model'); + return _this; + } + ComponentModel.prototype.init = function (option, parentModel, ecModel) { + this.mergeDefaultAndTheme(option, ecModel); + }; + ComponentModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { + var layoutMode = fetchLayoutMode(this); + var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; + var themeModel = ecModel.getTheme(); + merge(option, themeModel.get(this.mainType)); + merge(option, this.getDefaultOption()); + if (layoutMode) { + mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }; + ComponentModel.prototype.mergeOption = function (option, ecModel) { + merge(this.option, option, true); + var layoutMode = fetchLayoutMode(this); + if (layoutMode) { + mergeLayoutParam(this.option, option, layoutMode); + } + }; + /** + * Called immediately after `init` or `mergeOption` of this instance called. + */ + ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {}; + /** + * [How to declare defaultOption]: + * + * (A) If using class declaration in typescript (since echarts 5): + * ```ts + * import {ComponentOption} from '../model/option.js'; + * export interface XxxOption extends ComponentOption { + * aaa: number + * } + * export class XxxModel extends Component { + * static type = 'xxx'; + * static defaultOption: XxxOption = { + * aaa: 123 + * } + * } + * Component.registerClass(XxxModel); + * ``` + * ```ts + * import {inheritDefaultOption} from '../util/component.js'; + * import {XxxModel, XxxOption} from './XxxModel.js'; + * export interface XxxSubOption extends XxxOption { + * bbb: number + * } + * class XxxSubModel extends XxxModel { + * static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, { + * bbb: 456 + * }) + * fn() { + * let opt = this.getDefaultOption(); + * // opt is {aaa: 123, bbb: 456} + * } + * } + * ``` + * + * (B) If using class extend (previous approach in echarts 3 & 4): + * ```js + * let XxxComponent = Component.extend({ + * defaultOption: { + * xx: 123 + * } + * }) + * ``` + * ```js + * let XxxSubComponent = XxxComponent.extend({ + * defaultOption: { + * yy: 456 + * }, + * fn: function () { + * let opt = this.getDefaultOption(); + * // opt is {xx: 123, yy: 456} + * } + * }) + * ``` + */ + ComponentModel.prototype.getDefaultOption = function () { + var ctor = this.constructor; + // If using class declaration, it is different to travel super class + // in legacy env and auto merge defaultOption. So if using class + // declaration, defaultOption should be merged manually. + if (!isExtendedClass(ctor)) { + // When using ts class, defaultOption must be declared as static. + return ctor.defaultOption; + } + // FIXME: remove this approach? + var fields = inner(this); + if (!fields.defaultOption) { + var optList = []; + var clz = ctor; + while (clz) { + var opt = clz.prototype.defaultOption; + opt && optList.push(opt); + clz = clz.superClass; + } + var defaultOption = {}; + for (var i = optList.length - 1; i >= 0; i--) { + defaultOption = merge(defaultOption, optList[i], true); + } + fields.defaultOption = defaultOption; + } + return fields.defaultOption; + }; + /** + * Notice: always force to input param `useDefault` in case that forget to consider it. + * The same behavior as `modelUtil.parseFinder`. + * + * @param useDefault In many cases like series refer axis and axis refer grid, + * If axis index / axis id not specified, use the first target as default. + * In other cases like dataZoom refer axis, if not specified, measn no refer. + */ + ComponentModel.prototype.getReferringComponents = function (mainType, opt) { + var indexKey = mainType + 'Index'; + var idKey = mainType + 'Id'; + return queryReferringComponents(this.ecModel, mainType, { + index: this.get(indexKey, true), + id: this.get(idKey, true) + }, opt); + }; + ComponentModel.prototype.getBoxLayoutParams = function () { + // Consider itself having box layout configs. + var boxLayoutModel = this; + return { + left: boxLayoutModel.get('left'), + top: boxLayoutModel.get('top'), + right: boxLayoutModel.get('right'), + bottom: boxLayoutModel.get('bottom'), + width: boxLayoutModel.get('width'), + height: boxLayoutModel.get('height') + }; + }; + /** + * Get key for zlevel. + * If developers don't configure zlevel. We will assign zlevel to series based on the key. + * For example, lines with trail effect and progressive series will in an individual zlevel. + */ + ComponentModel.prototype.getZLevelKey = function () { + return ''; + }; + ComponentModel.prototype.setZLevel = function (zlevel) { + this.option.zlevel = zlevel; + }; + ComponentModel.protoInitialize = function () { + var proto = ComponentModel.prototype; + proto.type = 'component'; + proto.id = ''; + proto.name = ''; + proto.mainType = ''; + proto.subType = ''; + proto.componentIndex = 0; + }(); + return ComponentModel; + }(Model); + mountExtend(ComponentModel, Model); + enableClassManagement(ComponentModel); + enableSubTypeDefaulter(ComponentModel); + enableTopologicalTravel(ComponentModel, getDependencies); + function getDependencies(componentType) { + var deps = []; + each(ComponentModel.getClassesByMainType(componentType), function (clz) { + deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []); + }); + // Ensure main type. + deps = map(deps, function (type) { + return parseClassType(type).main; + }); + // Hack dataset for convenience. + if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) { + deps.unshift('dataset'); + } + return deps; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var platform = ''; + // Navigator not exists in node + if (typeof navigator !== 'undefined') { + /* global navigator */ + platform = navigator.platform || ''; + } + var decalColor = 'rgba(0, 0, 0, 0.2)'; + var globalDefault = { + darkMode: 'auto', + // backgroundColor: 'rgba(0,0,0,0)', + colorBy: 'series', + color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + gradientColor: ['#f6efa6', '#d88273', '#bf444c'], + aria: { + decal: { + decals: [{ + color: decalColor, + dashArrayX: [1, 0], + dashArrayY: [2, 5], + symbolSize: 1, + rotation: Math.PI / 6 + }, { + color: decalColor, + symbol: 'circle', + dashArrayX: [[8, 8], [0, 8, 8, 0]], + dashArrayY: [6, 0], + symbolSize: 0.8 + }, { + color: decalColor, + dashArrayX: [1, 0], + dashArrayY: [4, 3], + rotation: -Math.PI / 4 + }, { + color: decalColor, + dashArrayX: [[6, 6], [0, 6, 6, 0]], + dashArrayY: [6, 0] + }, { + color: decalColor, + dashArrayX: [[1, 0], [1, 6]], + dashArrayY: [1, 0, 6, 0], + rotation: Math.PI / 4 + }, { + color: decalColor, + symbol: 'triangle', + dashArrayX: [[9, 9], [0, 9, 9, 0]], + dashArrayY: [7, 2], + symbolSize: 0.75 + }] + } + }, + // If xAxis and yAxis declared, grid is created by default. + // grid: {}, + textStyle: { + // color: '#000', + // decoration: 'none', + // PENDING + fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif', + // fontFamily: 'Arial, Verdana, sans-serif', + fontSize: 12, + fontStyle: 'normal', + fontWeight: 'normal' + }, + // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/ + // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + // Default is source-over + blendMode: null, + stateAnimation: { + duration: 300, + easing: 'cubicOut' + }, + animation: 'auto', + animationDuration: 1000, + animationDurationUpdate: 500, + animationEasing: 'cubicInOut', + animationEasingUpdate: 'cubicInOut', + animationThreshold: 2000, + // Configuration for progressive/incremental rendering + progressiveThreshold: 3000, + progressive: 400, + // Threshold of if use single hover layer to optimize. + // It is recommended that `hoverLayerThreshold` is equivalent to or less than + // `progressiveThreshold`, otherwise hover will cause restart of progressive, + // which is unexpected. + // see example . + hoverLayerThreshold: 3000, + // See: module:echarts/scale/Time + useUTC: false + }; + + var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'itemChildGroupId', 'seriesName']); + var SOURCE_FORMAT_ORIGINAL = 'original'; + var SOURCE_FORMAT_ARRAY_ROWS = 'arrayRows'; + var SOURCE_FORMAT_OBJECT_ROWS = 'objectRows'; + var SOURCE_FORMAT_KEYED_COLUMNS = 'keyedColumns'; + var SOURCE_FORMAT_TYPED_ARRAY = 'typedArray'; + var SOURCE_FORMAT_UNKNOWN = 'unknown'; + var SERIES_LAYOUT_BY_COLUMN = 'column'; + var SERIES_LAYOUT_BY_ROW = 'row'; + + // The result of `guessOrdinal`. + var BE_ORDINAL = { + Must: 1, + Might: 2, + Not: 3 // Other cases + }; + + var innerGlobalModel = makeInner(); + /** + * MUST be called before mergeOption of all series. + */ + function resetSourceDefaulter(ecModel) { + // `datasetMap` is used to make default encode. + innerGlobalModel(ecModel).datasetMap = createHashMap(); + } + /** + * [The strategy of the arrengment of data dimensions for dataset]: + * "value way": all axes are non-category axes. So series one by one take + * several (the number is coordSysDims.length) dimensions from dataset. + * The result of data arrengment of data dimensions like: + * | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y | + * "category way": at least one axis is category axis. So the the first data + * dimension is always mapped to the first category axis and shared by + * all of the series. The other data dimensions are taken by series like + * "value way" does. + * The result of data arrengment of data dimensions like: + * | ser_shared_x | ser0_y | ser1_y | ser2_y | + * + * @return encode Never be `null/undefined`. + */ + function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) { + var encode = {}; + var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); + // Currently only make default when using dataset, util more reqirements occur. + if (!datasetModel || !coordDimensions) { + return encode; + } + var encodeItemName = []; + var encodeSeriesName = []; + var ecModel = seriesModel.ecModel; + var datasetMap = innerGlobalModel(ecModel).datasetMap; + var key = datasetModel.uid + '_' + source.seriesLayoutBy; + var baseCategoryDimIndex; + var categoryWayValueDimStart; + coordDimensions = coordDimensions.slice(); + each(coordDimensions, function (coordDimInfoLoose, coordDimIdx) { + var coordDimInfo = isObject(coordDimInfoLoose) ? coordDimInfoLoose : coordDimensions[coordDimIdx] = { + name: coordDimInfoLoose + }; + if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) { + baseCategoryDimIndex = coordDimIdx; + categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimInfo); + } + encode[coordDimInfo.name] = []; + }); + var datasetRecord = datasetMap.get(key) || datasetMap.set(key, { + categoryWayDim: categoryWayValueDimStart, + valueWayDim: 0 + }); + // TODO + // Auto detect first time axis and do arrangement. + each(coordDimensions, function (coordDimInfo, coordDimIdx) { + var coordDimName = coordDimInfo.name; + var count = getDataDimCountOnCoordDim(coordDimInfo); + // In value way. + if (baseCategoryDimIndex == null) { + var start = datasetRecord.valueWayDim; + pushDim(encode[coordDimName], start, count); + pushDim(encodeSeriesName, start, count); + datasetRecord.valueWayDim += count; + // ??? TODO give a better default series name rule? + // especially when encode x y specified. + // consider: when multiple series share one dimension + // category axis, series name should better use + // the other dimension name. On the other hand, use + // both dimensions name. + } + // In category way, the first category axis. + else if (baseCategoryDimIndex === coordDimIdx) { + pushDim(encode[coordDimName], 0, count); + pushDim(encodeItemName, 0, count); + } + // In category way, the other axis. + else { + var start = datasetRecord.categoryWayDim; + pushDim(encode[coordDimName], start, count); + pushDim(encodeSeriesName, start, count); + datasetRecord.categoryWayDim += count; + } + }); + function pushDim(dimIdxArr, idxFrom, idxCount) { + for (var i = 0; i < idxCount; i++) { + dimIdxArr.push(idxFrom + i); + } + } + function getDataDimCountOnCoordDim(coordDimInfo) { + var dimsDef = coordDimInfo.dimsDef; + return dimsDef ? dimsDef.length : 1; + } + encodeItemName.length && (encode.itemName = encodeItemName); + encodeSeriesName.length && (encode.seriesName = encodeSeriesName); + return encode; + } + /** + * Work for data like [{name: ..., value: ...}, ...]. + * + * @return encode Never be `null/undefined`. + */ + function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) { + var encode = {}; + var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); + // Currently only make default when using dataset, util more reqirements occur. + if (!datasetModel) { + return encode; + } + var sourceFormat = source.sourceFormat; + var dimensionsDefine = source.dimensionsDefine; + var potentialNameDimIndex; + if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { + each(dimensionsDefine, function (dim, idx) { + if ((isObject(dim) ? dim.name : dim) === 'name') { + potentialNameDimIndex = idx; + } + }); + } + var idxResult = function () { + var idxRes0 = {}; + var idxRes1 = {}; + var guessRecords = []; + // 5 is an experience value. + for (var i = 0, len = Math.min(5, dimCount); i < len; i++) { + var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i); + guessRecords.push(guessResult); + var isPureNumber = guessResult === BE_ORDINAL.Not; + // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim, + // and then find a name dim with the priority: + // "BE_ORDINAL.Might|BE_ORDINAL.Must" > "other dim" > "the value dim itself". + if (isPureNumber && idxRes0.v == null && i !== potentialNameDimIndex) { + idxRes0.v = i; + } + if (idxRes0.n == null || idxRes0.n === idxRes0.v || !isPureNumber && guessRecords[idxRes0.n] === BE_ORDINAL.Not) { + idxRes0.n = i; + } + if (fulfilled(idxRes0) && guessRecords[idxRes0.n] !== BE_ORDINAL.Not) { + return idxRes0; + } + // [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not), + // find the first BE_ORDINAL.Might as the value dim, + // and then find a name dim with the priority: + // "other dim" > "the value dim itself". + // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be + // treated as number. + if (!isPureNumber) { + if (guessResult === BE_ORDINAL.Might && idxRes1.v == null && i !== potentialNameDimIndex) { + idxRes1.v = i; + } + if (idxRes1.n == null || idxRes1.n === idxRes1.v) { + idxRes1.n = i; + } + } + } + function fulfilled(idxResult) { + return idxResult.v != null && idxResult.n != null; + } + return fulfilled(idxRes0) ? idxRes0 : fulfilled(idxRes1) ? idxRes1 : null; + }(); + if (idxResult) { + encode.value = [idxResult.v]; + // `potentialNameDimIndex` has highest priority. + var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; + // By default, label uses itemName in charts. + // So we don't set encodeLabel here. + encode.itemName = [nameDimIndex]; + encode.seriesName = [nameDimIndex]; + } + return encode; + } + /** + * @return If return null/undefined, indicate that should not use datasetModel. + */ + function querySeriesUpstreamDatasetModel(seriesModel) { + // Caution: consider the scenario: + // A dataset is declared and a series is not expected to use the dataset, + // and at the beginning `setOption({series: { noData })` (just prepare other + // option but no data), then `setOption({series: {data: [...]}); In this case, + // the user should set an empty array to avoid that dataset is used by default. + var thisData = seriesModel.get('data', true); + if (!thisData) { + return queryReferringComponents(seriesModel.ecModel, 'dataset', { + index: seriesModel.get('datasetIndex', true), + id: seriesModel.get('datasetId', true) + }, SINGLE_REFERRING).models[0]; + } + } + /** + * @return Always return an array event empty. + */ + function queryDatasetUpstreamDatasetModels(datasetModel) { + // Only these attributes declared, we by default reference to `datasetIndex: 0`. + // Otherwise, no reference. + if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) { + return []; + } + return queryReferringComponents(datasetModel.ecModel, 'dataset', { + index: datasetModel.get('fromDatasetIndex', true), + id: datasetModel.get('fromDatasetId', true) + }, SINGLE_REFERRING).models; + } + /** + * The rule should not be complex, otherwise user might not + * be able to known where the data is wrong. + * The code is ugly, but how to make it neat? + */ + function guessOrdinal(source, dimIndex) { + return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex); + } + // dimIndex may be overflow source data. + // return {BE_ORDINAL} + function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) { + var result; + // Experience value. + var maxLoop = 5; + if (isTypedArray(data)) { + return BE_ORDINAL.Not; + } + // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine + // always exists in source. + var dimName; + var dimType; + if (dimensionsDefine) { + var dimDefItem = dimensionsDefine[dimIndex]; + if (isObject(dimDefItem)) { + dimName = dimDefItem.name; + dimType = dimDefItem.type; + } else if (isString(dimDefItem)) { + dimName = dimDefItem; + } + } + if (dimType != null) { + return dimType === 'ordinal' ? BE_ORDINAL.Must : BE_ORDINAL.Not; + } + if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { + var dataArrayRows = data; + if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { + var sample = dataArrayRows[dimIndex]; + for (var i = 0; i < (sample || []).length && i < maxLoop; i++) { + if ((result = detectValue(sample[startIndex + i])) != null) { + return result; + } + } + } else { + for (var i = 0; i < dataArrayRows.length && i < maxLoop; i++) { + var row = dataArrayRows[startIndex + i]; + if (row && (result = detectValue(row[dimIndex])) != null) { + return result; + } + } + } + } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { + var dataObjectRows = data; + if (!dimName) { + return BE_ORDINAL.Not; + } + for (var i = 0; i < dataObjectRows.length && i < maxLoop; i++) { + var item = dataObjectRows[i]; + if (item && (result = detectValue(item[dimName])) != null) { + return result; + } + } + } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { + var dataKeyedColumns = data; + if (!dimName) { + return BE_ORDINAL.Not; + } + var sample = dataKeyedColumns[dimName]; + if (!sample || isTypedArray(sample)) { + return BE_ORDINAL.Not; + } + for (var i = 0; i < sample.length && i < maxLoop; i++) { + if ((result = detectValue(sample[i])) != null) { + return result; + } + } + } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { + var dataOriginal = data; + for (var i = 0; i < dataOriginal.length && i < maxLoop; i++) { + var item = dataOriginal[i]; + var val = getDataItemValue(item); + if (!isArray(val)) { + return BE_ORDINAL.Not; + } + if ((result = detectValue(val[dimIndex])) != null) { + return result; + } + } + } + function detectValue(val) { + var beStr = isString(val); + // Consider usage convenience, '1', '2' will be treated as "number". + // `isFinit('')` get `true`. + if (val != null && isFinite(val) && val !== '') { + return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not; + } else if (beStr && val !== '-') { + return BE_ORDINAL.Must; + } + } + return BE_ORDINAL.Not; + } + + var internalOptionCreatorMap = createHashMap(); + function registerInternalOptionCreator(mainType, creator) { + assert(internalOptionCreatorMap.get(mainType) == null && creator); + internalOptionCreatorMap.set(mainType, creator); + } + function concatInternalOptions(ecModel, mainType, newCmptOptionList) { + var internalOptionCreator = internalOptionCreatorMap.get(mainType); + if (!internalOptionCreator) { + return newCmptOptionList; + } + var internalOptions = internalOptionCreator(ecModel); + if (!internalOptions) { + return newCmptOptionList; + } + if ("development" !== 'production') { + for (var i = 0; i < internalOptions.length; i++) { + assert(isComponentIdInternal(internalOptions[i])); + } + } + return newCmptOptionList.concat(internalOptions); + } + + var innerColor = makeInner(); + var innerDecal = makeInner(); + var PaletteMixin = /** @class */function () { + function PaletteMixin() {} + PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) { + var defaultPalette = normalizeToArray(this.get('color', true)); + var layeredPalette = this.get('colorLayer', true); + return getFromPalette(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum); + }; + PaletteMixin.prototype.clearColorPalette = function () { + clearPalette(this, innerColor); + }; + return PaletteMixin; + }(); + function getDecalFromPalette(ecModel, name, scope, requestNum) { + var defaultDecals = normalizeToArray(ecModel.get(['aria', 'decal', 'decals'])); + return getFromPalette(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum); + } + function getNearestPalette(palettes, requestColorNum) { + var paletteNum = palettes.length; + // TODO palettes must be in order + for (var i = 0; i < paletteNum; i++) { + if (palettes[i].length > requestColorNum) { + return palettes[i]; + } + } + return palettes[paletteNum - 1]; + } + /** + * @param name MUST NOT be null/undefined. Otherwise call this function + * twise with the same parameters will get different result. + * @param scope default this. + * @return Can be null/undefined + */ + function getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) { + scope = scope || that; + var scopeFields = inner(scope); + var paletteIdx = scopeFields.paletteIdx || 0; + var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; + // Use `hasOwnProperty` to avoid conflict with Object.prototype. + if (paletteNameMap.hasOwnProperty(name)) { + return paletteNameMap[name]; + } + var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); + // In case can't find in layered color palette. + palette = palette || defaultPalette; + if (!palette || !palette.length) { + return; + } + var pickedPaletteItem = palette[paletteIdx]; + if (name) { + paletteNameMap[name] = pickedPaletteItem; + } + scopeFields.paletteIdx = (paletteIdx + 1) % palette.length; + return pickedPaletteItem; + } + function clearPalette(that, inner) { + inner(that).paletteIdx = 0; + inner(that).paletteNameMap = {}; + } + + // ----------------------- + // Internal method names: + // ----------------------- + var reCreateSeriesIndices; + var assertSeriesInitialized; + var initBase; + var OPTION_INNER_KEY = '\0_ec_inner'; + var OPTION_INNER_VALUE = 1; + var BUITIN_COMPONENTS_MAP = { + grid: 'GridComponent', + polar: 'PolarComponent', + geo: 'GeoComponent', + singleAxis: 'SingleAxisComponent', + parallel: 'ParallelComponent', + calendar: 'CalendarComponent', + graphic: 'GraphicComponent', + toolbox: 'ToolboxComponent', + tooltip: 'TooltipComponent', + axisPointer: 'AxisPointerComponent', + brush: 'BrushComponent', + title: 'TitleComponent', + timeline: 'TimelineComponent', + markPoint: 'MarkPointComponent', + markLine: 'MarkLineComponent', + markArea: 'MarkAreaComponent', + legend: 'LegendComponent', + dataZoom: 'DataZoomComponent', + visualMap: 'VisualMapComponent', + // aria: 'AriaComponent', + // dataset: 'DatasetComponent', + // Dependencies + xAxis: 'GridComponent', + yAxis: 'GridComponent', + angleAxis: 'PolarComponent', + radiusAxis: 'PolarComponent' + }; + var BUILTIN_CHARTS_MAP = { + line: 'LineChart', + bar: 'BarChart', + pie: 'PieChart', + scatter: 'ScatterChart', + radar: 'RadarChart', + map: 'MapChart', + tree: 'TreeChart', + treemap: 'TreemapChart', + graph: 'GraphChart', + gauge: 'GaugeChart', + funnel: 'FunnelChart', + parallel: 'ParallelChart', + sankey: 'SankeyChart', + boxplot: 'BoxplotChart', + candlestick: 'CandlestickChart', + effectScatter: 'EffectScatterChart', + lines: 'LinesChart', + heatmap: 'HeatmapChart', + pictorialBar: 'PictorialBarChart', + themeRiver: 'ThemeRiverChart', + sunburst: 'SunburstChart', + custom: 'CustomChart' + }; + var componetsMissingLogPrinted = {}; + function checkMissingComponents(option) { + each(option, function (componentOption, mainType) { + if (!ComponentModel.hasClass(mainType)) { + var componentImportName = BUITIN_COMPONENTS_MAP[mainType]; + if (componentImportName && !componetsMissingLogPrinted[componentImportName]) { + error("Component " + mainType + " is used but not imported.\nimport { " + componentImportName + " } from 'echarts/components';\necharts.use([" + componentImportName + "]);"); + componetsMissingLogPrinted[componentImportName] = true; + } + } + }); + } + var GlobalModel = /** @class */function (_super) { + __extends(GlobalModel, _super); + function GlobalModel() { + return _super !== null && _super.apply(this, arguments) || this; + } + GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) { + theme = theme || {}; + this.option = null; // Mark as not initialized. + this._theme = new Model(theme); + this._locale = new Model(locale); + this._optionManager = optionManager; + }; + GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) { + if ("development" !== 'production') { + assert(option != null, 'option is null/undefined'); + assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()'); + } + var innerOpt = normalizeSetOptionInput(opts); + this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt); + this._resetOption(null, innerOpt); + }; + /** + * @param type null/undefined: reset all. + * 'recreate': force recreate all. + * 'timeline': only reset timeline option + * 'media': only reset media query option + * @return Whether option changed. + */ + GlobalModel.prototype.resetOption = function (type, opt) { + return this._resetOption(type, normalizeSetOptionInput(opt)); + }; + GlobalModel.prototype._resetOption = function (type, opt) { + var optionChanged = false; + var optionManager = this._optionManager; + if (!type || type === 'recreate') { + var baseOption = optionManager.mountOption(type === 'recreate'); + if ("development" !== 'production') { + checkMissingComponents(baseOption); + } + if (!this.option || type === 'recreate') { + initBase(this, baseOption); + } else { + this.restoreData(); + this._mergeOption(baseOption, opt); + } + optionChanged = true; + } + if (type === 'timeline' || type === 'media') { + this.restoreData(); + } + // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`, + // it should better not have the same props with `MediaUnit['option']`. + // Because either `option2` or `MediaUnit['option']` will be always merged to "current option" + // rather than original "baseOption". If they both override a prop, the result might be + // unexpected when media state changed after `setOption` called. + // If we really need to modify a props in each `MediaUnit['option']`, use the full version + // (`{baseOption, media}`) in `setOption`. + // For `timeline`, the case is the same. + if (!type || type === 'recreate' || type === 'timeline') { + var timelineOption = optionManager.getTimelineOption(this); + if (timelineOption) { + optionChanged = true; + this._mergeOption(timelineOption, opt); + } + } + if (!type || type === 'recreate' || type === 'media') { + var mediaOptions = optionManager.getMediaOption(this); + if (mediaOptions.length) { + each(mediaOptions, function (mediaOption) { + optionChanged = true; + this._mergeOption(mediaOption, opt); + }, this); + } + } + return optionChanged; + }; + GlobalModel.prototype.mergeOption = function (option) { + this._mergeOption(option, null); + }; + GlobalModel.prototype._mergeOption = function (newOption, opt) { + var option = this.option; + var componentsMap = this._componentsMap; + var componentsCount = this._componentsCount; + var newCmptTypes = []; + var newCmptTypeMap = createHashMap(); + var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap; + resetSourceDefaulter(this); + // If no component class, merge directly. + // For example: color, animaiton options, etc. + each(newOption, function (componentOption, mainType) { + if (componentOption == null) { + return; + } + if (!ComponentModel.hasClass(mainType)) { + // globalSettingTask.dirty(); + option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true); + } else if (mainType) { + newCmptTypes.push(mainType); + newCmptTypeMap.set(mainType, true); + } + }); + if (replaceMergeMainTypeMap) { + // If there is a mainType `xxx` in `replaceMerge` but not declared in option, + // we trade it as it is declared in option as `{xxx: []}`. Because: + // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`. + // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`. + replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) { + if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) { + newCmptTypes.push(mainTypeInReplaceMerge); + newCmptTypeMap.set(mainTypeInReplaceMerge, true); + } + }); + } + ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this); + function visitComponent(mainType) { + var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType])); + var oldCmptList = componentsMap.get(mainType); + var mergeMode = + // `!oldCmptList` means init. See the comment in `mappingToExists` + !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge'; + var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); + // Set mainType and complete subType. + setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); + // Empty it before the travel, in order to prevent `this._componentsMap` + // from being used in the `init`/`mergeOption`/`optionUpdated` of some + // components, which is probably incorrect logic. + option[mainType] = null; + componentsMap.set(mainType, null); + componentsCount.set(mainType, 0); + var optionsByMainType = []; + var cmptsByMainType = []; + var cmptsCountByMainType = 0; + var tooltipExists; + var tooltipWarningLogged; + each(mappingResult, function (resultItem, index) { + var componentModel = resultItem.existing; + var newCmptOption = resultItem.newOption; + if (!newCmptOption) { + if (componentModel) { + // Consider where is no new option and should be merged using {}, + // see removeEdgeAndAdd in topologicalTravel and + // ComponentModel.getAllClassMainTypes. + componentModel.mergeOption({}, this); + componentModel.optionUpdated({}, false); + } + // If no both `resultItem.exist` and `resultItem.option`, + // either it is in `replaceMerge` and not matched by any id, + // or it has been removed in previous `replaceMerge` and left a "hole" in this component index. + } else { + var isSeriesType = mainType === 'series'; + var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists + ); + + if (!ComponentModelClass) { + if ("development" !== 'production') { + var subType = resultItem.keyInfo.subType; + var seriesImportName = BUILTIN_CHARTS_MAP[subType]; + if (!componetsMissingLogPrinted[subType]) { + componetsMissingLogPrinted[subType] = true; + if (seriesImportName) { + error("Series " + subType + " is used but not imported.\nimport { " + seriesImportName + " } from 'echarts/charts';\necharts.use([" + seriesImportName + "]);"); + } else { + error("Unknown series " + subType); + } + } + } + return; + } + // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception. + if (mainType === 'tooltip') { + if (tooltipExists) { + if ("development" !== 'production') { + if (!tooltipWarningLogged) { + warn('Currently only one tooltip component is allowed.'); + tooltipWarningLogged = true; + } + } + return; + } + tooltipExists = true; + } + if (componentModel && componentModel.constructor === ComponentModelClass) { + componentModel.name = resultItem.keyInfo.name; + // componentModel.settingTask && componentModel.settingTask.dirty(); + componentModel.mergeOption(newCmptOption, this); + componentModel.optionUpdated(newCmptOption, false); + } else { + // PENDING Global as parent ? + var extraOpt = extend({ + componentIndex: index + }, resultItem.keyInfo); + componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); + // Assign `keyInfo` + extend(componentModel, extraOpt); + if (resultItem.brandNew) { + componentModel.__requireNewView = true; + } + componentModel.init(newCmptOption, this, this); + // Call optionUpdated after init. + // newCmptOption has been used as componentModel.option + // and may be merged with theme and default, so pass null + // to avoid confusion. + componentModel.optionUpdated(null, true); + } + } + if (componentModel) { + optionsByMainType.push(componentModel.option); + cmptsByMainType.push(componentModel); + cmptsCountByMainType++; + } else { + // Always do assign to avoid elided item in array. + optionsByMainType.push(void 0); + cmptsByMainType.push(void 0); + } + }, this); + option[mainType] = optionsByMainType; + componentsMap.set(mainType, cmptsByMainType); + componentsCount.set(mainType, cmptsCountByMainType); + // Backup series for filtering. + if (mainType === 'series') { + reCreateSeriesIndices(this); + } + } + // If no series declared, ensure `_seriesIndices` initialized. + if (!this._seriesIndices) { + reCreateSeriesIndices(this); + } + }; + /** + * Get option for output (cloned option and inner info removed) + */ + GlobalModel.prototype.getOption = function () { + var option = clone(this.option); + each(option, function (optInMainType, mainType) { + if (ComponentModel.hasClass(mainType)) { + var opts = normalizeToArray(optInMainType); + // Inner cmpts need to be removed. + // Inner cmpts might not be at last since ec5.0, but still + // compatible for users: if inner cmpt at last, splice the returned array. + var realLen = opts.length; + var metNonInner = false; + for (var i = realLen - 1; i >= 0; i--) { + // Remove options with inner id. + if (opts[i] && !isComponentIdInternal(opts[i])) { + metNonInner = true; + } else { + opts[i] = null; + !metNonInner && realLen--; + } + } + opts.length = realLen; + option[mainType] = opts; + } + }); + delete option[OPTION_INNER_KEY]; + return option; + }; + GlobalModel.prototype.getTheme = function () { + return this._theme; + }; + GlobalModel.prototype.getLocaleModel = function () { + return this._locale; + }; + GlobalModel.prototype.setUpdatePayload = function (payload) { + this._payload = payload; + }; + GlobalModel.prototype.getUpdatePayload = function () { + return this._payload; + }; + /** + * @param idx If not specified, return the first one. + */ + GlobalModel.prototype.getComponent = function (mainType, idx) { + var list = this._componentsMap.get(mainType); + if (list) { + var cmpt = list[idx || 0]; + if (cmpt) { + return cmpt; + } else if (idx == null) { + for (var i = 0; i < list.length; i++) { + if (list[i]) { + return list[i]; + } + } + } + } + }; + /** + * @return Never be null/undefined. + */ + GlobalModel.prototype.queryComponents = function (condition) { + var mainType = condition.mainType; + if (!mainType) { + return []; + } + var index = condition.index; + var id = condition.id; + var name = condition.name; + var cmpts = this._componentsMap.get(mainType); + if (!cmpts || !cmpts.length) { + return []; + } + var result; + if (index != null) { + result = []; + each(normalizeToArray(index), function (idx) { + cmpts[idx] && result.push(cmpts[idx]); + }); + } else if (id != null) { + result = queryByIdOrName('id', id, cmpts); + } else if (name != null) { + result = queryByIdOrName('name', name, cmpts); + } else { + // Return all non-empty components in that mainType + result = filter(cmpts, function (cmpt) { + return !!cmpt; + }); + } + return filterBySubType(result, condition); + }; + /** + * The interface is different from queryComponents, + * which is convenient for inner usage. + * + * @usage + * let result = findComponents( + * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}} + * ); + * let result = findComponents( + * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}} + * ); + * let result = findComponents( + * {mainType: 'series', + * filter: function (model, index) {...}} + * ); + * // result like [component0, componnet1, ...] + */ + GlobalModel.prototype.findComponents = function (condition) { + var query = condition.query; + var mainType = condition.mainType; + var queryCond = getQueryCond(query); + var result = queryCond ? this.queryComponents(queryCond) + // Retrieve all non-empty components. + : filter(this._componentsMap.get(mainType), function (cmpt) { + return !!cmpt; + }); + return doFilter(filterBySubType(result, condition)); + function getQueryCond(q) { + var indexAttr = mainType + 'Index'; + var idAttr = mainType + 'Id'; + var nameAttr = mainType + 'Name'; + return q && (q[indexAttr] != null || q[idAttr] != null || q[nameAttr] != null) ? { + mainType: mainType, + // subType will be filtered finally. + index: q[indexAttr], + id: q[idAttr], + name: q[nameAttr] + } : null; + } + function doFilter(res) { + return condition.filter ? filter(res, condition.filter) : res; + } + }; + GlobalModel.prototype.eachComponent = function (mainType, cb, context) { + var componentsMap = this._componentsMap; + if (isFunction(mainType)) { + var ctxForAll_1 = cb; + var cbForAll_1 = mainType; + componentsMap.each(function (cmpts, componentType) { + for (var i = 0; cmpts && i < cmpts.length; i++) { + var cmpt = cmpts[i]; + cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex); + } + }); + } else { + var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null; + for (var i = 0; cmpts && i < cmpts.length; i++) { + var cmpt = cmpts[i]; + cmpt && cb.call(context, cmpt, cmpt.componentIndex); + } + } + }; + /** + * Get series list before filtered by name. + */ + GlobalModel.prototype.getSeriesByName = function (name) { + var nameStr = convertOptionIdName(name, null); + return filter(this._componentsMap.get('series'), function (oneSeries) { + return !!oneSeries && nameStr != null && oneSeries.name === nameStr; + }); + }; + /** + * Get series list before filtered by index. + */ + GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) { + return this._componentsMap.get('series')[seriesIndex]; + }; + /** + * Get series list before filtered by type. + * FIXME: rename to getRawSeriesByType? + */ + GlobalModel.prototype.getSeriesByType = function (subType) { + return filter(this._componentsMap.get('series'), function (oneSeries) { + return !!oneSeries && oneSeries.subType === subType; + }); + }; + /** + * Get all series before filtered. + */ + GlobalModel.prototype.getSeries = function () { + return filter(this._componentsMap.get('series'), function (oneSeries) { + return !!oneSeries; + }); + }; + /** + * Count series before filtered. + */ + GlobalModel.prototype.getSeriesCount = function () { + return this._componentsCount.get('series'); + }; + /** + * After filtering, series may be different + * from raw series. + */ + GlobalModel.prototype.eachSeries = function (cb, context) { + assertSeriesInitialized(this); + each(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.get('series')[rawSeriesIndex]; + cb.call(context, series, rawSeriesIndex); + }, this); + }; + /** + * Iterate raw series before filtered. + * + * @param {Function} cb + * @param {*} context + */ + GlobalModel.prototype.eachRawSeries = function (cb, context) { + each(this._componentsMap.get('series'), function (series) { + series && cb.call(context, series, series.componentIndex); + }); + }; + /** + * After filtering, series may be different. + * from raw series. + */ + GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) { + assertSeriesInitialized(this); + each(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.get('series')[rawSeriesIndex]; + if (series.subType === subType) { + cb.call(context, series, rawSeriesIndex); + } + }, this); + }; + /** + * Iterate raw series before filtered of given type. + */ + GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) { + return each(this.getSeriesByType(subType), cb, context); + }; + GlobalModel.prototype.isSeriesFiltered = function (seriesModel) { + assertSeriesInitialized(this); + return this._seriesIndicesMap.get(seriesModel.componentIndex) == null; + }; + GlobalModel.prototype.getCurrentSeriesIndices = function () { + return (this._seriesIndices || []).slice(); + }; + GlobalModel.prototype.filterSeries = function (cb, context) { + assertSeriesInitialized(this); + var newSeriesIndices = []; + each(this._seriesIndices, function (seriesRawIdx) { + var series = this._componentsMap.get('series')[seriesRawIdx]; + cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx); + }, this); + this._seriesIndices = newSeriesIndices; + this._seriesIndicesMap = createHashMap(newSeriesIndices); + }; + GlobalModel.prototype.restoreData = function (payload) { + reCreateSeriesIndices(this); + var componentsMap = this._componentsMap; + var componentTypes = []; + componentsMap.each(function (components, componentType) { + if (ComponentModel.hasClass(componentType)) { + componentTypes.push(componentType); + } + }); + ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) { + each(componentsMap.get(componentType), function (component) { + if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) { + component.restoreData(); + } + }); + }); + }; + GlobalModel.internalField = function () { + reCreateSeriesIndices = function (ecModel) { + var seriesIndices = ecModel._seriesIndices = []; + each(ecModel._componentsMap.get('series'), function (series) { + // series may have been removed by `replaceMerge`. + series && seriesIndices.push(series.componentIndex); + }); + ecModel._seriesIndicesMap = createHashMap(seriesIndices); + }; + assertSeriesInitialized = function (ecModel) { + // Components that use _seriesIndices should depends on series component, + // which make sure that their initialization is after series. + if ("development" !== 'production') { + if (!ecModel._seriesIndices) { + throw new Error('Option should contains series.'); + } + } + }; + initBase = function (ecModel, baseOption) { + // Using OPTION_INNER_KEY to mark that this option cannot be used outside, + // i.e. `chart.setOption(chart.getModel().option);` is forbidden. + ecModel.option = {}; + ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; + // Init with series: [], in case of calling findSeries method + // before series initialized. + ecModel._componentsMap = createHashMap({ + series: [] + }); + ecModel._componentsCount = createHashMap(); + // If user spefied `option.aria`, aria will be enable. This detection should be + // performed before theme and globalDefault merge. + var airaOption = baseOption.aria; + if (isObject(airaOption) && airaOption.enabled == null) { + airaOption.enabled = true; + } + mergeTheme(baseOption, ecModel._theme.option); + // TODO Needs clone when merging to the unexisted property + merge(baseOption, globalDefault, false); + ecModel._mergeOption(baseOption, null); + }; + }(); + return GlobalModel; + }(Model); + function isNotTargetSeries(seriesModel, payload) { + if (payload) { + var index = payload.seriesIndex; + var id = payload.seriesId; + var name_1 = payload.seriesName; + return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1; + } + } + function mergeTheme(option, theme) { + // PENDING + // NOT use `colorLayer` in theme if option has `color` + var notMergeColorLayer = option.color && !option.colorLayer; + each(theme, function (themeItem, name) { + if (name === 'colorLayer' && notMergeColorLayer) { + return; + } + // If it is component model mainType, the model handles that merge later. + // otherwise, merge them here. + if (!ComponentModel.hasClass(name)) { + if (typeof themeItem === 'object') { + option[name] = !option[name] ? clone(themeItem) : merge(option[name], themeItem, false); + } else { + if (option[name] == null) { + option[name] = themeItem; + } + } + } + }); + } + function queryByIdOrName(attr, idOrName, cmpts) { + // Here is a break from echarts4: string and number are + // treated as equal. + if (isArray(idOrName)) { + var keyMap_1 = createHashMap(); + each(idOrName, function (idOrNameItem) { + if (idOrNameItem != null) { + var idName = convertOptionIdName(idOrNameItem, null); + idName != null && keyMap_1.set(idOrNameItem, true); + } + }); + return filter(cmpts, function (cmpt) { + return cmpt && keyMap_1.get(cmpt[attr]); + }); + } else { + var idName_1 = convertOptionIdName(idOrName, null); + return filter(cmpts, function (cmpt) { + return cmpt && idName_1 != null && cmpt[attr] === idName_1; + }); + } + } + function filterBySubType(components, condition) { + // Using hasOwnProperty for restrict. Consider + // subType is undefined in user payload. + return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) { + return cmpt && cmpt.subType === condition.subType; + }) : components; + } + function normalizeSetOptionInput(opts) { + var replaceMergeMainTypeMap = createHashMap(); + opts && each(normalizeToArray(opts.replaceMerge), function (mainType) { + if ("development" !== 'production') { + assert(ComponentModel.hasClass(mainType), '"' + mainType + '" is not valid component main type in "replaceMerge"'); + } + replaceMergeMainTypeMap.set(mainType, true); + }); + return { + replaceMergeMainTypeMap: replaceMergeMainTypeMap + }; + } + mixin(GlobalModel, PaletteMixin); + + var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isSSR', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', + // 'getModel', + 'getOption', + // 'getViewOfComponentModel', + // 'getViewOfSeriesModel', + 'getId', 'updateLabelLayout']; + var ExtensionAPI = /** @class */function () { + function ExtensionAPI(ecInstance) { + each(availableMethods, function (methodName) { + this[methodName] = bind(ecInstance[methodName], ecInstance); + }, this); + } + return ExtensionAPI; + }(); + + var coordinateSystemCreators = {}; + var CoordinateSystemManager = /** @class */function () { + function CoordinateSystemManager() { + this._coordinateSystems = []; + } + CoordinateSystemManager.prototype.create = function (ecModel, api) { + var coordinateSystems = []; + each(coordinateSystemCreators, function (creator, type) { + var list = creator.create(ecModel, api); + coordinateSystems = coordinateSystems.concat(list || []); + }); + this._coordinateSystems = coordinateSystems; + }; + CoordinateSystemManager.prototype.update = function (ecModel, api) { + each(this._coordinateSystems, function (coordSys) { + coordSys.update && coordSys.update(ecModel, api); + }); + }; + CoordinateSystemManager.prototype.getCoordinateSystems = function () { + return this._coordinateSystems.slice(); + }; + CoordinateSystemManager.register = function (type, creator) { + coordinateSystemCreators[type] = creator; + }; + CoordinateSystemManager.get = function (type) { + return coordinateSystemCreators[type]; + }; + return CoordinateSystemManager; + }(); + + var QUERY_REG = /^(min|max)?(.+)$/; + // Key: mainType + // type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>; + /** + * TERM EXPLANATIONS: + * See `ECOption` and `ECUnitOption` in `src/util/types.ts`. + */ + var OptionManager = /** @class */function () { + // timeline.notMerge is not supported in ec3. Firstly there is rearly + // case that notMerge is needed. Secondly supporting 'notMerge' requires + // rawOption cloned and backuped when timeline changed, which does no + // good to performance. What's more, that both timeline and setOption + // method supply 'notMerge' brings complex and some problems. + // Consider this case: + // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false); + // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false); + function OptionManager(api) { + this._timelineOptions = []; + this._mediaList = []; + /** + * -1, means default. + * empty means no media. + */ + this._currentMediaIndices = []; + this._api = api; + } + OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) { + if (rawOption) { + // That set dat primitive is dangerous if user reuse the data when setOption again. + each(normalizeToArray(rawOption.series), function (series) { + series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data); + }); + each(normalizeToArray(rawOption.dataset), function (dataset) { + dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source); + }); + } + // Caution: some series modify option data, if do not clone, + // it should ensure that the repeat modify correctly + // (create a new object when modify itself). + rawOption = clone(rawOption); + // FIXME + // If some property is set in timeline options or media option but + // not set in baseOption, a warning should be given. + var optionBackup = this._optionBackup; + var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup); + this._newBaseOption = newParsedOption.baseOption; + // For setOption at second time (using merge mode); + if (optionBackup) { + // FIXME + // the restore merge solution is essentially incorrect. + // the mapping can not be 100% consistent with ecModel, which probably brings + // potential bug! + // The first merge is delayed, because in most cases, users do not call `setOption` twice. + // let fakeCmptsMap = this._fakeCmptsMap; + // if (!fakeCmptsMap) { + // fakeCmptsMap = this._fakeCmptsMap = createHashMap(); + // mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null); + // } + // mergeToBackupOption( + // fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt + // ); + // For simplicity, timeline options and media options do not support merge, + // that is, if you `setOption` twice and both has timeline options, the latter + // timeline options will not be merged to the former, but just substitute them. + if (newParsedOption.timelineOptions.length) { + optionBackup.timelineOptions = newParsedOption.timelineOptions; + } + if (newParsedOption.mediaList.length) { + optionBackup.mediaList = newParsedOption.mediaList; + } + if (newParsedOption.mediaDefault) { + optionBackup.mediaDefault = newParsedOption.mediaDefault; + } + } else { + this._optionBackup = newParsedOption; + } + }; + OptionManager.prototype.mountOption = function (isRecreate) { + var optionBackup = this._optionBackup; + this._timelineOptions = optionBackup.timelineOptions; + this._mediaList = optionBackup.mediaList; + this._mediaDefault = optionBackup.mediaDefault; + this._currentMediaIndices = []; + return clone(isRecreate + // this._optionBackup.baseOption, which is created at the first `setOption` + // called, and is merged into every new option by inner method `mergeToBackupOption` + // each time `setOption` called, can be only used in `isRecreate`, because + // its reliability is under suspicion. In other cases option merge is + // performed by `model.mergeOption`. + ? optionBackup.baseOption : this._newBaseOption); + }; + OptionManager.prototype.getTimelineOption = function (ecModel) { + var option; + var timelineOptions = this._timelineOptions; + if (timelineOptions.length) { + // getTimelineOption can only be called after ecModel inited, + // so we can get currentIndex from timelineModel. + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel) { + option = clone( + // FIXME:TS as TimelineModel or quivlant interface + timelineOptions[timelineModel.getCurrentIndex()]); + } + } + return option; + }; + OptionManager.prototype.getMediaOption = function (ecModel) { + var ecWidth = this._api.getWidth(); + var ecHeight = this._api.getHeight(); + var mediaList = this._mediaList; + var mediaDefault = this._mediaDefault; + var indices = []; + var result = []; + // No media defined. + if (!mediaList.length && !mediaDefault) { + return result; + } + // Multi media may be applied, the latter defined media has higher priority. + for (var i = 0, len = mediaList.length; i < len; i++) { + if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) { + indices.push(i); + } + } + // FIXME + // Whether mediaDefault should force users to provide? Otherwise + // the change by media query can not be recorvered. + if (!indices.length && mediaDefault) { + indices = [-1]; + } + if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) { + result = map(indices, function (index) { + return clone(index === -1 ? mediaDefault.option : mediaList[index].option); + }); + } + // Otherwise return nothing. + this._currentMediaIndices = indices; + return result; + }; + return OptionManager; + }(); + /** + * [RAW_OPTION_PATTERNS] + * (Note: "series: []" represents all other props in `ECUnitOption`) + * + * (1) No prop "baseOption" declared: + * Root option is used as "baseOption" (except prop "options" and "media"). + * ```js + * option = { + * series: [], + * timeline: {}, + * options: [], + * }; + * option = { + * series: [], + * media: {}, + * }; + * option = { + * series: [], + * timeline: {}, + * options: [], + * media: {}, + * } + * ``` + * + * (2) Prop "baseOption" declared: + * If "baseOption" declared, `ECUnitOption` props can only be declared + * inside "baseOption" except prop "timeline" (compat ec2). + * ```js + * option = { + * baseOption: { + * timeline: {}, + * series: [], + * }, + * options: [] + * }; + * option = { + * baseOption: { + * series: [], + * }, + * media: [] + * }; + * option = { + * baseOption: { + * timeline: {}, + * series: [], + * }, + * options: [] + * media: [] + * }; + * option = { + * // ec3 compat ec2: allow (only) `timeline` declared + * // outside baseOption. Keep this setting for compat. + * timeline: {}, + * baseOption: { + * series: [], + * }, + * options: [], + * media: [] + * }; + * ``` + */ + function parseRawOption( + // `rawOption` May be modified + rawOption, optionPreprocessorFuncs, isNew) { + var mediaList = []; + var mediaDefault; + var baseOption; + var declaredBaseOption = rawOption.baseOption; + // Compatible with ec2, [RAW_OPTION_PATTERNS] above. + var timelineOnRoot = rawOption.timeline; + var timelineOptionsOnRoot = rawOption.options; + var mediaOnRoot = rawOption.media; + var hasMedia = !!rawOption.media; + var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline); + if (declaredBaseOption) { + baseOption = declaredBaseOption; + // For merge option. + if (!baseOption.timeline) { + baseOption.timeline = timelineOnRoot; + } + } + // For convenience, enable to use the root option as the `baseOption`: + // `{ ...normalOptionProps, media: [{ ... }, { ... }] }` + else { + if (hasTimeline || hasMedia) { + rawOption.options = rawOption.media = null; + } + baseOption = rawOption; + } + if (hasMedia) { + if (isArray(mediaOnRoot)) { + each(mediaOnRoot, function (singleMedia) { + if ("development" !== 'production') { + // Real case of wrong config. + if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) { + error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }'); + } + } + if (singleMedia && singleMedia.option) { + if (singleMedia.query) { + mediaList.push(singleMedia); + } else if (!mediaDefault) { + // Use the first media default. + mediaDefault = singleMedia; + } + } + }); + } else { + if ("development" !== 'production') { + // Real case of wrong config. + error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }'); + } + } + } + doPreprocess(baseOption); + each(timelineOptionsOnRoot, function (option) { + return doPreprocess(option); + }); + each(mediaList, function (media) { + return doPreprocess(media.option); + }); + function doPreprocess(option) { + each(optionPreprocessorFuncs, function (preProcess) { + preProcess(option, isNew); + }); + } + return { + baseOption: baseOption, + timelineOptions: timelineOptionsOnRoot || [], + mediaDefault: mediaDefault, + mediaList: mediaList + }; + } + /** + * @see + * Support: width, height, aspectRatio + * Can use max or min as prefix. + */ + function applyMediaQuery(query, ecWidth, ecHeight) { + var realMap = { + width: ecWidth, + height: ecHeight, + aspectratio: ecWidth / ecHeight // lower case for convenience. + }; + + var applicable = true; + each(query, function (value, attr) { + var matched = attr.match(QUERY_REG); + if (!matched || !matched[1] || !matched[2]) { + return; + } + var operator = matched[1]; + var realAttr = matched[2].toLowerCase(); + if (!compare(realMap[realAttr], value, operator)) { + applicable = false; + } + }); + return applicable; + } + function compare(real, expect, operator) { + if (operator === 'min') { + return real >= expect; + } else if (operator === 'max') { + return real <= expect; + } else { + // Equals + return real === expect; + } + } + function indicesEquals(indices1, indices2) { + // indices is always order by asc and has only finite number. + return indices1.join(',') === indices2.join(','); + } + + var each$2 = each; + var isObject$1 = isObject; + var POSSIBLE_STYLES = ['areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', 'chordStyle', 'label', 'labelLine']; + function compatEC2ItemStyle(opt) { + var itemStyleOpt = opt && opt.itemStyle; + if (!itemStyleOpt) { + return; + } + for (var i = 0, len = POSSIBLE_STYLES.length; i < len; i++) { + var styleName = POSSIBLE_STYLES[i]; + var normalItemStyleOpt = itemStyleOpt.normal; + var emphasisItemStyleOpt = itemStyleOpt.emphasis; + if (normalItemStyleOpt && normalItemStyleOpt[styleName]) { + if ("development" !== 'production') { + deprecateReplaceLog("itemStyle.normal." + styleName, styleName); + } + opt[styleName] = opt[styleName] || {}; + if (!opt[styleName].normal) { + opt[styleName].normal = normalItemStyleOpt[styleName]; + } else { + merge(opt[styleName].normal, normalItemStyleOpt[styleName]); + } + normalItemStyleOpt[styleName] = null; + } + if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) { + if ("development" !== 'production') { + deprecateReplaceLog("itemStyle.emphasis." + styleName, "emphasis." + styleName); + } + opt[styleName] = opt[styleName] || {}; + if (!opt[styleName].emphasis) { + opt[styleName].emphasis = emphasisItemStyleOpt[styleName]; + } else { + merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]); + } + emphasisItemStyleOpt[styleName] = null; + } + } + } + function convertNormalEmphasis(opt, optType, useExtend) { + if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) { + var normalOpt = opt[optType].normal; + var emphasisOpt = opt[optType].emphasis; + if (normalOpt) { + if ("development" !== 'production') { + // eslint-disable-next-line max-len + deprecateLog("'normal' hierarchy in " + optType + " has been removed since 4.0. All style properties are configured in " + optType + " directly now."); + } + // Timeline controlStyle has other properties besides normal and emphasis + if (useExtend) { + opt[optType].normal = opt[optType].emphasis = null; + defaults(opt[optType], normalOpt); + } else { + opt[optType] = normalOpt; + } + } + if (emphasisOpt) { + if ("development" !== 'production') { + deprecateLog(optType + ".emphasis has been changed to emphasis." + optType + " since 4.0"); + } + opt.emphasis = opt.emphasis || {}; + opt.emphasis[optType] = emphasisOpt; + // Also compat the case user mix the style and focus together in ec3 style + // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } } + if (emphasisOpt.focus) { + opt.emphasis.focus = emphasisOpt.focus; + } + if (emphasisOpt.blurScope) { + opt.emphasis.blurScope = emphasisOpt.blurScope; + } + } + } + } + function removeEC3NormalStatus(opt) { + convertNormalEmphasis(opt, 'itemStyle'); + convertNormalEmphasis(opt, 'lineStyle'); + convertNormalEmphasis(opt, 'areaStyle'); + convertNormalEmphasis(opt, 'label'); + convertNormalEmphasis(opt, 'labelLine'); + // treemap + convertNormalEmphasis(opt, 'upperLabel'); + // graph + convertNormalEmphasis(opt, 'edgeLabel'); + } + function compatTextStyle(opt, propName) { + // Check whether is not object (string\null\undefined ...) + var labelOptSingle = isObject$1(opt) && opt[propName]; + var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle; + if (textStyle) { + if ("development" !== 'production') { + // eslint-disable-next-line max-len + deprecateLog("textStyle hierarchy in " + propName + " has been removed since 4.0. All textStyle properties are configured in " + propName + " directly now."); + } + for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) { + var textPropName = TEXT_STYLE_OPTIONS[i]; + if (textStyle.hasOwnProperty(textPropName)) { + labelOptSingle[textPropName] = textStyle[textPropName]; + } + } + } + } + function compatEC3CommonStyles(opt) { + if (opt) { + removeEC3NormalStatus(opt); + compatTextStyle(opt, 'label'); + opt.emphasis && compatTextStyle(opt.emphasis, 'label'); + } + } + function processSeries(seriesOpt) { + if (!isObject$1(seriesOpt)) { + return; + } + compatEC2ItemStyle(seriesOpt); + removeEC3NormalStatus(seriesOpt); + compatTextStyle(seriesOpt, 'label'); + // treemap + compatTextStyle(seriesOpt, 'upperLabel'); + // graph + compatTextStyle(seriesOpt, 'edgeLabel'); + if (seriesOpt.emphasis) { + compatTextStyle(seriesOpt.emphasis, 'label'); + // treemap + compatTextStyle(seriesOpt.emphasis, 'upperLabel'); + // graph + compatTextStyle(seriesOpt.emphasis, 'edgeLabel'); + } + var markPoint = seriesOpt.markPoint; + if (markPoint) { + compatEC2ItemStyle(markPoint); + compatEC3CommonStyles(markPoint); + } + var markLine = seriesOpt.markLine; + if (markLine) { + compatEC2ItemStyle(markLine); + compatEC3CommonStyles(markLine); + } + var markArea = seriesOpt.markArea; + if (markArea) { + compatEC3CommonStyles(markArea); + } + var data = seriesOpt.data; + // Break with ec3: if `setOption` again, there may be no `type` in option, + // then the backward compat based on option type will not be performed. + if (seriesOpt.type === 'graph') { + data = data || seriesOpt.nodes; + var edgeData = seriesOpt.links || seriesOpt.edges; + if (edgeData && !isTypedArray(edgeData)) { + for (var i = 0; i < edgeData.length; i++) { + compatEC3CommonStyles(edgeData[i]); + } + } + each(seriesOpt.categories, function (opt) { + removeEC3NormalStatus(opt); + }); + } + if (data && !isTypedArray(data)) { + for (var i = 0; i < data.length; i++) { + compatEC3CommonStyles(data[i]); + } + } + // mark point data + markPoint = seriesOpt.markPoint; + if (markPoint && markPoint.data) { + var mpData = markPoint.data; + for (var i = 0; i < mpData.length; i++) { + compatEC3CommonStyles(mpData[i]); + } + } + // mark line data + markLine = seriesOpt.markLine; + if (markLine && markLine.data) { + var mlData = markLine.data; + for (var i = 0; i < mlData.length; i++) { + if (isArray(mlData[i])) { + compatEC3CommonStyles(mlData[i][0]); + compatEC3CommonStyles(mlData[i][1]); + } else { + compatEC3CommonStyles(mlData[i]); + } + } + } + // Series + if (seriesOpt.type === 'gauge') { + compatTextStyle(seriesOpt, 'axisLabel'); + compatTextStyle(seriesOpt, 'title'); + compatTextStyle(seriesOpt, 'detail'); + } else if (seriesOpt.type === 'treemap') { + convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle'); + each(seriesOpt.levels, function (opt) { + removeEC3NormalStatus(opt); + }); + } else if (seriesOpt.type === 'tree') { + removeEC3NormalStatus(seriesOpt.leaves); + } + // sunburst starts from ec4, so it does not need to compat levels. + } + + function toArr(o) { + return isArray(o) ? o : o ? [o] : []; + } + function toObj(o) { + return (isArray(o) ? o[0] : o) || {}; + } + function globalCompatStyle(option, isTheme) { + each$2(toArr(option.series), function (seriesOpt) { + isObject$1(seriesOpt) && processSeries(seriesOpt); + }); + var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar']; + isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis'); + each$2(axes, function (axisName) { + each$2(toArr(option[axisName]), function (axisOpt) { + if (axisOpt) { + compatTextStyle(axisOpt, 'axisLabel'); + compatTextStyle(axisOpt.axisPointer, 'label'); + } + }); + }); + each$2(toArr(option.parallel), function (parallelOpt) { + var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault; + compatTextStyle(parallelAxisDefault, 'axisLabel'); + compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label'); + }); + each$2(toArr(option.calendar), function (calendarOpt) { + convertNormalEmphasis(calendarOpt, 'itemStyle'); + compatTextStyle(calendarOpt, 'dayLabel'); + compatTextStyle(calendarOpt, 'monthLabel'); + compatTextStyle(calendarOpt, 'yearLabel'); + }); + // radar.name.textStyle + each$2(toArr(option.radar), function (radarOpt) { + compatTextStyle(radarOpt, 'name'); + // Use axisName instead of name because component has name property + if (radarOpt.name && radarOpt.axisName == null) { + radarOpt.axisName = radarOpt.name; + delete radarOpt.name; + if ("development" !== 'production') { + deprecateLog('name property in radar component has been changed to axisName'); + } + } + if (radarOpt.nameGap != null && radarOpt.axisNameGap == null) { + radarOpt.axisNameGap = radarOpt.nameGap; + delete radarOpt.nameGap; + if ("development" !== 'production') { + deprecateLog('nameGap property in radar component has been changed to axisNameGap'); + } + } + if ("development" !== 'production') { + each$2(radarOpt.indicator, function (indicatorOpt) { + if (indicatorOpt.text) { + deprecateReplaceLog('text', 'name', 'radar.indicator'); + } + }); + } + }); + each$2(toArr(option.geo), function (geoOpt) { + if (isObject$1(geoOpt)) { + compatEC3CommonStyles(geoOpt); + each$2(toArr(geoOpt.regions), function (regionObj) { + compatEC3CommonStyles(regionObj); + }); + } + }); + each$2(toArr(option.timeline), function (timelineOpt) { + compatEC3CommonStyles(timelineOpt); + convertNormalEmphasis(timelineOpt, 'label'); + convertNormalEmphasis(timelineOpt, 'itemStyle'); + convertNormalEmphasis(timelineOpt, 'controlStyle', true); + var data = timelineOpt.data; + isArray(data) && each(data, function (item) { + if (isObject(item)) { + convertNormalEmphasis(item, 'label'); + convertNormalEmphasis(item, 'itemStyle'); + } + }); + }); + each$2(toArr(option.toolbox), function (toolboxOpt) { + convertNormalEmphasis(toolboxOpt, 'iconStyle'); + each$2(toolboxOpt.feature, function (featureOpt) { + convertNormalEmphasis(featureOpt, 'iconStyle'); + }); + }); + compatTextStyle(toObj(option.axisPointer), 'label'); + compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); + // Clean logs + // storedLogs = {}; + } + + function get(opt, path) { + var pathArr = path.split(','); + var obj = opt; + for (var i = 0; i < pathArr.length; i++) { + obj = obj && obj[pathArr[i]]; + if (obj == null) { + break; + } + } + return obj; + } + function set$1(opt, path, val, overwrite) { + var pathArr = path.split(','); + var obj = opt; + var key; + var i = 0; + for (; i < pathArr.length - 1; i++) { + key = pathArr[i]; + if (obj[key] == null) { + obj[key] = {}; + } + obj = obj[key]; + } + if (overwrite || obj[pathArr[i]] == null) { + obj[pathArr[i]] = val; + } + } + function compatLayoutProperties(option) { + option && each(LAYOUT_PROPERTIES, function (prop) { + if (prop[0] in option && !(prop[1] in option)) { + option[prop[1]] = option[prop[0]]; + } + }); + } + var LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']]; + var COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline']; + var BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']]; + function compatBarItemStyle(option) { + var itemStyle = option && option.itemStyle; + if (itemStyle) { + for (var i = 0; i < BAR_ITEM_STYLE_MAP.length; i++) { + var oldName = BAR_ITEM_STYLE_MAP[i][1]; + var newName = BAR_ITEM_STYLE_MAP[i][0]; + if (itemStyle[oldName] != null) { + itemStyle[newName] = itemStyle[oldName]; + if ("development" !== 'production') { + deprecateReplaceLog(oldName, newName); + } + } + } + } + } + function compatPieLabel(option) { + if (!option) { + return; + } + if (option.alignTo === 'edge' && option.margin != null && option.edgeDistance == null) { + if ("development" !== 'production') { + deprecateReplaceLog('label.margin', 'label.edgeDistance', 'pie'); + } + option.edgeDistance = option.margin; + } + } + function compatSunburstState(option) { + if (!option) { + return; + } + if (option.downplay && !option.blur) { + option.blur = option.downplay; + if ("development" !== 'production') { + deprecateReplaceLog('downplay', 'blur', 'sunburst'); + } + } + } + function compatGraphFocus(option) { + if (!option) { + return; + } + if (option.focusNodeAdjacency != null) { + option.emphasis = option.emphasis || {}; + if (option.emphasis.focus == null) { + if ("development" !== 'production') { + deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \'adjacency\'}', 'graph/sankey'); + } + option.emphasis.focus = 'adjacency'; + } + } + } + function traverseTree(data, cb) { + if (data) { + for (var i = 0; i < data.length; i++) { + cb(data[i]); + data[i] && traverseTree(data[i].children, cb); + } + } + } + function globalBackwardCompat(option, isTheme) { + globalCompatStyle(option, isTheme); + // Make sure series array for model initialization. + option.series = normalizeToArray(option.series); + each(option.series, function (seriesOpt) { + if (!isObject(seriesOpt)) { + return; + } + var seriesType = seriesOpt.type; + if (seriesType === 'line') { + if (seriesOpt.clipOverflow != null) { + seriesOpt.clip = seriesOpt.clipOverflow; + if ("development" !== 'production') { + deprecateReplaceLog('clipOverflow', 'clip', 'line'); + } + } + } else if (seriesType === 'pie' || seriesType === 'gauge') { + if (seriesOpt.clockWise != null) { + seriesOpt.clockwise = seriesOpt.clockWise; + if ("development" !== 'production') { + deprecateReplaceLog('clockWise', 'clockwise'); + } + } + compatPieLabel(seriesOpt.label); + var data = seriesOpt.data; + if (data && !isTypedArray(data)) { + for (var i = 0; i < data.length; i++) { + compatPieLabel(data[i]); + } + } + if (seriesOpt.hoverOffset != null) { + seriesOpt.emphasis = seriesOpt.emphasis || {}; + if (seriesOpt.emphasis.scaleSize = null) { + if ("development" !== 'production') { + deprecateReplaceLog('hoverOffset', 'emphasis.scaleSize'); + } + seriesOpt.emphasis.scaleSize = seriesOpt.hoverOffset; + } + } + } else if (seriesType === 'gauge') { + var pointerColor = get(seriesOpt, 'pointer.color'); + pointerColor != null && set$1(seriesOpt, 'itemStyle.color', pointerColor); + } else if (seriesType === 'bar') { + compatBarItemStyle(seriesOpt); + compatBarItemStyle(seriesOpt.backgroundStyle); + compatBarItemStyle(seriesOpt.emphasis); + var data = seriesOpt.data; + if (data && !isTypedArray(data)) { + for (var i = 0; i < data.length; i++) { + if (typeof data[i] === 'object') { + compatBarItemStyle(data[i]); + compatBarItemStyle(data[i] && data[i].emphasis); + } + } + } + } else if (seriesType === 'sunburst') { + var highlightPolicy = seriesOpt.highlightPolicy; + if (highlightPolicy) { + seriesOpt.emphasis = seriesOpt.emphasis || {}; + if (!seriesOpt.emphasis.focus) { + seriesOpt.emphasis.focus = highlightPolicy; + if ("development" !== 'production') { + deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst'); + } + } + } + compatSunburstState(seriesOpt); + traverseTree(seriesOpt.data, compatSunburstState); + } else if (seriesType === 'graph' || seriesType === 'sankey') { + compatGraphFocus(seriesOpt); + // TODO nodes, edges? + } else if (seriesType === 'map') { + if (seriesOpt.mapType && !seriesOpt.map) { + if ("development" !== 'production') { + deprecateReplaceLog('mapType', 'map', 'map'); + } + seriesOpt.map = seriesOpt.mapType; + } + if (seriesOpt.mapLocation) { + if ("development" !== 'production') { + deprecateLog('`mapLocation` is not used anymore.'); + } + defaults(seriesOpt, seriesOpt.mapLocation); + } + } + if (seriesOpt.hoverAnimation != null) { + seriesOpt.emphasis = seriesOpt.emphasis || {}; + if (seriesOpt.emphasis && seriesOpt.emphasis.scale == null) { + if ("development" !== 'production') { + deprecateReplaceLog('hoverAnimation', 'emphasis.scale'); + } + seriesOpt.emphasis.scale = seriesOpt.hoverAnimation; + } + } + compatLayoutProperties(seriesOpt); + }); + // dataRange has changed to visualMap + if (option.dataRange) { + option.visualMap = option.dataRange; + } + each(COMPATITABLE_COMPONENTS, function (componentName) { + var options = option[componentName]; + if (options) { + if (!isArray(options)) { + options = [options]; + } + each(options, function (option) { + compatLayoutProperties(option); + }); + } + }); + } + + // (1) [Caution]: the logic is correct based on the premises: + // data processing stage is blocked in stream. + // See + // (2) Only register once when import repeatedly. + // Should be executed after series is filtered and before stack calculation. + function dataStack(ecModel) { + var stackInfoMap = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + var stack = seriesModel.get('stack'); + // Compatible: when `stack` is set as '', do not stack. + if (stack) { + var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []); + var data = seriesModel.getData(); + var stackInfo = { + // Used for calculate axis extent automatically. + // TODO: Type getCalculationInfo return more specific type? + stackResultDimension: data.getCalculationInfo('stackResultDimension'), + stackedOverDimension: data.getCalculationInfo('stackedOverDimension'), + stackedDimension: data.getCalculationInfo('stackedDimension'), + stackedByDimension: data.getCalculationInfo('stackedByDimension'), + isStackedByIndex: data.getCalculationInfo('isStackedByIndex'), + data: data, + seriesModel: seriesModel + }; + // If stacked on axis that do not support data stack. + if (!stackInfo.stackedDimension || !(stackInfo.isStackedByIndex || stackInfo.stackedByDimension)) { + return; + } + stackInfoList.length && data.setCalculationInfo('stackedOnSeries', stackInfoList[stackInfoList.length - 1].seriesModel); + stackInfoList.push(stackInfo); + } + }); + stackInfoMap.each(calculateStack); + } + function calculateStack(stackInfoList) { + each(stackInfoList, function (targetStackInfo, idxInStack) { + var resultVal = []; + var resultNaN = [NaN, NaN]; + var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension]; + var targetData = targetStackInfo.data; + var isStackedByIndex = targetStackInfo.isStackedByIndex; + var stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; + // Should not write on raw data, because stack series model list changes + // depending on legend selection. + targetData.modify(dims, function (v0, v1, dataIndex) { + var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); + // Consider `connectNulls` of line area, if value is NaN, stackedOver + // should also be NaN, to draw a appropriate belt area. + if (isNaN(sum)) { + return resultNaN; + } + var byValue; + var stackedDataRawIndex; + if (isStackedByIndex) { + stackedDataRawIndex = targetData.getRawIndex(dataIndex); + } else { + byValue = targetData.get(targetStackInfo.stackedByDimension, dataIndex); + } + // If stackOver is NaN, chart view will render point on value start. + var stackedOver = NaN; + for (var j = idxInStack - 1; j >= 0; j--) { + var stackInfo = stackInfoList[j]; + // Has been optimized by inverted indices on `stackedByDimension`. + if (!isStackedByIndex) { + stackedDataRawIndex = stackInfo.data.rawIndexOf(stackInfo.stackedByDimension, byValue); + } + if (stackedDataRawIndex >= 0) { + var val = stackInfo.data.getByRawIndex(stackInfo.stackResultDimension, stackedDataRawIndex); + // Considering positive stack, negative stack and empty data + if (stackStrategy === 'all' // single stack group + || stackStrategy === 'positive' && val > 0 || stackStrategy === 'negative' && val < 0 || stackStrategy === 'samesign' && sum >= 0 && val > 0 // All positive stack + || stackStrategy === 'samesign' && sum <= 0 && val < 0 // All negative stack + ) { + // The sum has to be very small to be affected by the + // floating arithmetic problem. An incorrect result will probably + // cause axis min/max to be filtered incorrectly. + sum = addSafe(sum, val); + stackedOver = val; + break; + } + } + } + resultVal[0] = sum; + resultVal[1] = stackedOver; + return resultVal; + }); + }); + } + + // @inner + var SourceImpl = /** @class */function () { + function SourceImpl(fields) { + this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []); + this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; + // Visit config + this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN; + this.startIndex = fields.startIndex || 0; + this.dimensionsDetectedCount = fields.dimensionsDetectedCount; + this.metaRawOption = fields.metaRawOption; + var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine; + if (dimensionsDefine) { + for (var i = 0; i < dimensionsDefine.length; i++) { + var dim = dimensionsDefine[i]; + if (dim.type == null) { + if (guessOrdinal(this, i) === BE_ORDINAL.Must) { + dim.type = 'ordinal'; + } + } + } + } + } + return SourceImpl; + }(); + function isSourceInstance(val) { + return val instanceof SourceImpl; + } + /** + * Create a source from option. + * NOTE: Created source is immutable. Don't change any properties in it. + */ + function createSource(sourceData, thisMetaRawOption, + // can be null. If not provided, auto detect it from `sourceData`. + sourceFormat) { + sourceFormat = sourceFormat || detectSourceFormat(sourceData); + var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy; + var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions); + var source = new SourceImpl({ + data: sourceData, + sourceFormat: sourceFormat, + seriesLayoutBy: seriesLayoutBy, + dimensionsDefine: determined.dimensionsDefine, + startIndex: determined.startIndex, + dimensionsDetectedCount: determined.dimensionsDetectedCount, + metaRawOption: clone(thisMetaRawOption) + }); + return source; + } + /** + * Wrap original series data for some compatibility cases. + */ + function createSourceFromSeriesDataOption(data) { + return new SourceImpl({ + data: data, + sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL + }); + } + /** + * Clone source but excludes source data. + */ + function cloneSourceShallow(source) { + return new SourceImpl({ + data: source.data, + sourceFormat: source.sourceFormat, + seriesLayoutBy: source.seriesLayoutBy, + dimensionsDefine: clone(source.dimensionsDefine), + startIndex: source.startIndex, + dimensionsDetectedCount: source.dimensionsDetectedCount + }); + } + /** + * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`. + */ + function detectSourceFormat(data) { + var sourceFormat = SOURCE_FORMAT_UNKNOWN; + if (isTypedArray(data)) { + sourceFormat = SOURCE_FORMAT_TYPED_ARRAY; + } else if (isArray(data)) { + // FIXME Whether tolerate null in top level array? + if (data.length === 0) { + sourceFormat = SOURCE_FORMAT_ARRAY_ROWS; + } + for (var i = 0, len = data.length; i < len; i++) { + var item = data[i]; + if (item == null) { + continue; + } else if (isArray(item) || isTypedArray(item)) { + sourceFormat = SOURCE_FORMAT_ARRAY_ROWS; + break; + } else if (isObject(item)) { + sourceFormat = SOURCE_FORMAT_OBJECT_ROWS; + break; + } + } + } else if (isObject(data)) { + for (var key in data) { + if (hasOwn(data, key) && isArrayLike(data[key])) { + sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS; + break; + } + } + } + return sourceFormat; + } + /** + * Determine the source definitions from data standalone dimensions definitions + * are not specified. + */ + function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, + // standalone raw dimensions definition, like: + // { + // dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }] + // } + // in `dataset` or `series` + dimensionsDefine) { + var dimensionsDetectedCount; + var startIndex; + // PENDING: Could data be null/undefined here? + // currently, if `dataset.source` not specified, error thrown. + // if `series.data` not specified, nothing rendered without error thrown. + // Should test these cases. + if (!data) { + return { + dimensionsDefine: normalizeDimensionsOption(dimensionsDefine), + startIndex: startIndex, + dimensionsDetectedCount: dimensionsDetectedCount + }; + } + if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { + var dataArrayRows = data; + // Rule: Most of the first line are string: it is header. + // Caution: consider a line with 5 string and 1 number, + // it still can not be sure it is a head, because the + // 5 string may be 5 values of category columns. + if (sourceHeader === 'auto' || sourceHeader == null) { + arrayRowsTravelFirst(function (val) { + // '-' is regarded as null/undefined. + if (val != null && val !== '-') { + if (isString(val)) { + startIndex == null && (startIndex = 1); + } else { + startIndex = 0; + } + } + // 10 is an experience number, avoid long loop. + }, seriesLayoutBy, dataArrayRows, 10); + } else { + startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0; + } + if (!dimensionsDefine && startIndex === 1) { + dimensionsDefine = []; + arrayRowsTravelFirst(function (val, index) { + dimensionsDefine[index] = val != null ? val + '' : ''; + }, seriesLayoutBy, dataArrayRows, Infinity); + } + dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? dataArrayRows.length : dataArrayRows[0] ? dataArrayRows[0].length : null; + } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { + if (!dimensionsDefine) { + dimensionsDefine = objectRowsCollectDimensions(data); + } + } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { + if (!dimensionsDefine) { + dimensionsDefine = []; + each(data, function (colArr, key) { + dimensionsDefine.push(key); + }); + } + } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { + var value0 = getDataItemValue(data[0]); + dimensionsDetectedCount = isArray(value0) && value0.length || 1; + } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { + if ("development" !== 'production') { + assert(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.'); + } + } + return { + startIndex: startIndex, + dimensionsDefine: normalizeDimensionsOption(dimensionsDefine), + dimensionsDetectedCount: dimensionsDetectedCount + }; + } + function objectRowsCollectDimensions(data) { + var firstIndex = 0; + var obj; + while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line + if (obj) { + return keys(obj); + } + } + // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'], + // which is reasonable. But dimension name is duplicated. + // Returns undefined or an array contains only object without null/undefined or string. + function normalizeDimensionsOption(dimensionsDefine) { + if (!dimensionsDefine) { + // The meaning of null/undefined is different from empty array. + return; + } + var nameMap = createHashMap(); + return map(dimensionsDefine, function (rawItem, index) { + rawItem = isObject(rawItem) ? rawItem : { + name: rawItem + }; + // Other fields will be discarded. + var item = { + name: rawItem.name, + displayName: rawItem.displayName, + type: rawItem.type + }; + // User can set null in dimensions. + // We don't auto specify name, otherwise a given name may + // cause it to be referred unexpectedly. + if (item.name == null) { + return item; + } + // Also consider number form like 2012. + item.name += ''; + // User may also specify displayName. + // displayName will always exists except user not + // specified or dim name is not specified or detected. + // (A auto generated dim name will not be used as + // displayName). + if (item.displayName == null) { + item.displayName = item.name; + } + var exist = nameMap.get(item.name); + if (!exist) { + nameMap.set(item.name, { + count: 1 + }); + } else { + item.name += '-' + exist.count++; + } + return item; + }); + } + function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) { + if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { + for (var i = 0; i < data.length && i < maxLoop; i++) { + cb(data[i] ? data[i][0] : null, i); + } + } else { + var value0 = data[0] || []; + for (var i = 0; i < value0.length && i < maxLoop; i++) { + cb(value0[i], i); + } + } + } + function shouldRetrieveDataByName(source) { + var sourceFormat = source.sourceFormat; + return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var _a, _b, _c; + var providerMethods; + var mountMethods; + /** + * If normal array used, mutable chunk size is supported. + * If typed array used, chunk size must be fixed. + */ + var DefaultDataProvider = /** @class */function () { + function DefaultDataProvider(sourceParam, dimSize) { + // let source: Source; + var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; + // declare source is Source; + this._source = source; + var data = this._data = source.data; + // Typed array. TODO IE10+? + if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { + if ("development" !== 'production') { + if (dimSize == null) { + throw new Error('Typed array data must specify dimension size'); + } + } + this._offset = 0; + this._dimSize = dimSize; + this._data = data; + } + mountMethods(this, data, source); + } + DefaultDataProvider.prototype.getSource = function () { + return this._source; + }; + DefaultDataProvider.prototype.count = function () { + return 0; + }; + DefaultDataProvider.prototype.getItem = function (idx, out) { + return; + }; + DefaultDataProvider.prototype.appendData = function (newData) {}; + DefaultDataProvider.prototype.clean = function () {}; + DefaultDataProvider.protoInitialize = function () { + // PENDING: To avoid potential incompat (e.g., prototype + // is visited somewhere), still init them on prototype. + var proto = DefaultDataProvider.prototype; + proto.pure = false; + proto.persistent = true; + }(); + DefaultDataProvider.internalField = function () { + var _a; + mountMethods = function (provider, data, source) { + var sourceFormat = source.sourceFormat; + var seriesLayoutBy = source.seriesLayoutBy; + var startIndex = source.startIndex; + var dimsDef = source.dimensionsDefine; + var methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)]; + if ("development" !== 'production') { + assert(methods, 'Invalide sourceFormat: ' + sourceFormat); + } + extend(provider, methods); + if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { + provider.getItem = getItemForTypedArray; + provider.count = countForTypedArray; + provider.fillStorage = fillStorageForTypedArray; + } else { + var rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy); + provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef); + var rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy); + provider.count = bind(rawCounter, null, data, startIndex, dimsDef); + } + }; + var getItemForTypedArray = function (idx, out) { + idx = idx - this._offset; + out = out || []; + var data = this._data; + var dimSize = this._dimSize; + var offset = dimSize * idx; + for (var i = 0; i < dimSize; i++) { + out[i] = data[offset + i]; + } + return out; + }; + var fillStorageForTypedArray = function (start, end, storage, extent) { + var data = this._data; + var dimSize = this._dimSize; + for (var dim = 0; dim < dimSize; dim++) { + var dimExtent = extent[dim]; + var min = dimExtent[0] == null ? Infinity : dimExtent[0]; + var max = dimExtent[1] == null ? -Infinity : dimExtent[1]; + var count = end - start; + var arr = storage[dim]; + for (var i = 0; i < count; i++) { + // appendData with TypedArray will always do replace in provider. + var val = data[i * dimSize + dim]; + arr[start + i] = val; + val < min && (min = val); + val > max && (max = val); + } + dimExtent[0] = min; + dimExtent[1] = max; + } + }; + var countForTypedArray = function () { + return this._data ? this._data.length / this._dimSize : 0; + }; + providerMethods = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = { + pure: true, + appendData: appendDataSimply + }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = { + pure: true, + appendData: function () { + throw new Error('Do not support appendData when set seriesLayoutBy: "row".'); + } + }, _a[SOURCE_FORMAT_OBJECT_ROWS] = { + pure: true, + appendData: appendDataSimply + }, _a[SOURCE_FORMAT_KEYED_COLUMNS] = { + pure: true, + appendData: function (newData) { + var data = this._data; + each(newData, function (newCol, key) { + var oldCol = data[key] || (data[key] = []); + for (var i = 0; i < (newCol || []).length; i++) { + oldCol.push(newCol[i]); + } + }); + } + }, _a[SOURCE_FORMAT_ORIGINAL] = { + appendData: appendDataSimply + }, _a[SOURCE_FORMAT_TYPED_ARRAY] = { + persistent: false, + pure: true, + appendData: function (newData) { + if ("development" !== 'production') { + assert(isTypedArray(newData), 'Added data must be TypedArray if data in initialization is TypedArray'); + } + this._data = newData; + }, + // Clean self if data is already used. + clean: function () { + // PENDING + this._offset += this.count(); + this._data = null; + } + }, _a); + function appendDataSimply(newData) { + for (var i = 0; i < newData.length; i++) { + this._data.push(newData[i]); + } + } + }(); + return DefaultDataProvider; + }(); + var getItemSimply = function (rawData, startIndex, dimsDef, idx) { + return rawData[idx]; + }; + var rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) { + return rawData[idx + startIndex]; + }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) { + idx += startIndex; + var item = out || []; + var data = rawData; + for (var i = 0; i < data.length; i++) { + var row = data[i]; + item[i] = row ? row[idx] : null; + } + return item; + }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) { + var item = out || []; + for (var i = 0; i < dimsDef.length; i++) { + var dimName = dimsDef[i].name; + if ("development" !== 'production') { + if (dimName == null) { + throw new Error(); + } + } + var col = rawData[dimName]; + item[i] = col ? col[idx] : null; + } + return item; + }, _a[SOURCE_FORMAT_ORIGINAL] = getItemSimply, _a); + function getRawSourceItemGetter(sourceFormat, seriesLayoutBy) { + var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)]; + if ("development" !== 'production') { + assert(method, 'Do not support get item on "' + sourceFormat + '", "' + seriesLayoutBy + '".'); + } + return method; + } + var countSimply = function (rawData, startIndex, dimsDef) { + return rawData.length; + }; + var rawSourceDataCounterMap = (_b = {}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef) { + return Math.max(0, rawData.length - startIndex); + }, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef) { + var row = rawData[0]; + return row ? Math.max(0, row.length - startIndex) : 0; + }, _b[SOURCE_FORMAT_OBJECT_ROWS] = countSimply, _b[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef) { + var dimName = dimsDef[0].name; + if ("development" !== 'production') { + if (dimName == null) { + throw new Error(); + } + } + var col = rawData[dimName]; + return col ? col.length : 0; + }, _b[SOURCE_FORMAT_ORIGINAL] = countSimply, _b); + function getRawSourceDataCounter(sourceFormat, seriesLayoutBy) { + var method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)]; + if ("development" !== 'production') { + assert(method, 'Do not support count on "' + sourceFormat + '", "' + seriesLayoutBy + '".'); + } + return method; + } + var getRawValueSimply = function (dataItem, dimIndex, property) { + return dataItem[dimIndex]; + }; + var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) { + return dataItem[property]; + }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) { + // FIXME: In some case (markpoint in geo (geo-map.html)), + // dataItem is {coord: [...]} + var value = getDataItemValue(dataItem); + return !(value instanceof Array) ? value : value[dimIndex]; + }, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c); + function getRawSourceValueGetter(sourceFormat) { + var method = rawSourceValueGetterMap[sourceFormat]; + if ("development" !== 'production') { + assert(method, 'Do not support get value on "' + sourceFormat + '".'); + } + return method; + } + function getMethodMapKey(sourceFormat, seriesLayoutBy) { + return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat; + } + // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem, + // Consider persistent. + // Caution: why use raw value to display on label or tooltip? + // A reason is to avoid format. For example time value we do not know + // how to format is expected. More over, if stack is used, calculated + // value may be 0.91000000001, which have brings trouble to display. + // TODO: consider how to treat null/undefined/NaN when display? + function retrieveRawValue(data, dataIndex, + // If dimIndex is null/undefined, return OptionDataItem. + // Otherwise, return OptionDataValue. + dim) { + if (!data) { + return; + } + // Consider data may be not persistent. + var dataItem = data.getRawDataItem(dataIndex); + if (dataItem == null) { + return; + } + var store = data.getStore(); + var sourceFormat = store.getSource().sourceFormat; + if (dim != null) { + var dimIndex = data.getDimensionIndex(dim); + var property = store.getDimensionProperty(dimIndex); + return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property); + } else { + var result = dataItem; + if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { + result = getDataItemValue(dataItem); + } + return result; + } + } + + var DIMENSION_LABEL_REG = /\{@(.+?)\}/g; + var DataFormatMixin = /** @class */function () { + function DataFormatMixin() {} + /** + * Get params for formatter + */ + DataFormatMixin.prototype.getDataParams = function (dataIndex, dataType) { + var data = this.getData(dataType); + var rawValue = this.getRawValue(dataIndex, dataType); + var rawDataIndex = data.getRawIndex(dataIndex); + var name = data.getName(dataIndex); + var itemOpt = data.getRawDataItem(dataIndex); + var style = data.getItemVisual(dataIndex, 'style'); + var color = style && style[data.getItemVisual(dataIndex, 'drawType') || 'fill']; + var borderColor = style && style.stroke; + var mainType = this.mainType; + var isSeries = mainType === 'series'; + var userOutput = data.userOutput && data.userOutput.get(); + return { + componentType: mainType, + componentSubType: this.subType, + componentIndex: this.componentIndex, + seriesType: isSeries ? this.subType : null, + seriesIndex: this.seriesIndex, + seriesId: isSeries ? this.id : null, + seriesName: isSeries ? this.name : null, + name: name, + dataIndex: rawDataIndex, + data: itemOpt, + dataType: dataType, + value: rawValue, + color: color, + borderColor: borderColor, + dimensionNames: userOutput ? userOutput.fullDimensions : null, + encode: userOutput ? userOutput.encode : null, + // Param name list for mapping `a`, `b`, `c`, `d`, `e` + $vars: ['seriesName', 'name', 'value'] + }; + }; + /** + * Format label + * @param dataIndex + * @param status 'normal' by default + * @param dataType + * @param labelDimIndex Only used in some chart that + * use formatter in different dimensions, like radar. + * @param formatter Formatter given outside. + * @return return null/undefined if no formatter + */ + DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) { + status = status || 'normal'; + var data = this.getData(dataType); + var params = this.getDataParams(dataIndex, dataType); + if (extendParams) { + params.value = extendParams.interpolatedValue; + } + if (labelDimIndex != null && isArray(params.value)) { + params.value = params.value[labelDimIndex]; + } + if (!formatter) { + var itemModel = data.getItemModel(dataIndex); + // @ts-ignore + formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']); + } + if (isFunction(formatter)) { + params.status = status; + params.dimensionIndex = labelDimIndex; + return formatter(params); + } else if (isString(formatter)) { + var str = formatTpl(formatter, params); + // Support 'aaa{@[3]}bbb{@product}ccc'. + // Do not support '}' in dim name util have to. + return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) { + var len = dimStr.length; + var dimLoose = dimStr; + if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') { + dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0 + if ("development" !== 'production') { + if (isNaN(dimLoose)) { + error("Invalide label formatter: @" + dimStr + ", only support @[0], @[1], @[2], ..."); + } + } + } + var val = retrieveRawValue(data, dataIndex, dimLoose); + if (extendParams && isArray(extendParams.interpolatedValue)) { + var dimIndex = data.getDimensionIndex(dimLoose); + if (dimIndex >= 0) { + val = extendParams.interpolatedValue[dimIndex]; + } + } + return val != null ? val + '' : ''; + }); + } + }; + /** + * Get raw value in option + */ + DataFormatMixin.prototype.getRawValue = function (idx, dataType) { + return retrieveRawValue(this.getData(dataType), idx); + }; + /** + * Should be implemented. + * @param {number} dataIndex + * @param {boolean} [multipleSeries=false] + * @param {string} [dataType] + */ + DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + // Empty function + return; + }; + return DataFormatMixin; + }(); + // PENDING: previously we accept this type when calling `formatTooltip`, + // but guess little chance has been used outside. Do we need to backward + // compat it? + // type TooltipFormatResultLegacyObject = { + // // `html` means the markup language text, either in 'html' or 'richText'. + // // The name `html` is not appropriate because in 'richText' it is not a HTML + // // string. But still support it for backward compatibility. + // html: string; + // markers: Dictionary; + // }; + /** + * For backward compat, normalize the return from `formatTooltip`. + */ + function normalizeTooltipFormatResult(result) { + var markupText; + // let markers: Dictionary; + var markupFragment; + if (isObject(result)) { + if (result.type) { + markupFragment = result; + } else { + if ("development" !== 'production') { + console.warn('The return type of `formatTooltip` is not supported: ' + makePrintable(result)); + } + } + // else { + // markupText = (result as TooltipFormatResultLegacyObject).html; + // markers = (result as TooltipFormatResultLegacyObject).markers; + // if (markersExisting) { + // markers = zrUtil.merge(markersExisting, markers); + // } + // } + } else { + markupText = result; + } + return { + text: markupText, + // markers: markers || markersExisting, + frag: markupFragment + }; + } + + /** + * @param {Object} define + * @return See the return of `createTask`. + */ + function createTask(define) { + return new Task(define); + } + var Task = /** @class */function () { + function Task(define) { + define = define || {}; + this._reset = define.reset; + this._plan = define.plan; + this._count = define.count; + this._onDirty = define.onDirty; + this._dirty = true; + } + /** + * @param step Specified step. + * @param skip Skip customer perform call. + * @param modBy Sampling window size. + * @param modDataCount Sampling count. + * @return whether unfinished. + */ + Task.prototype.perform = function (performArgs) { + var upTask = this._upstream; + var skip = performArgs && performArgs.skip; + // TODO some refactor. + // Pull data. Must pull data each time, because context.data + // may be updated by Series.setData. + if (this._dirty && upTask) { + var context = this.context; + context.data = context.outputData = upTask.context.outputData; + } + if (this.__pipeline) { + this.__pipeline.currentTask = this; + } + var planResult; + if (this._plan && !skip) { + planResult = this._plan(this.context); + } + // Support sharding by mod, which changes the render sequence and makes the rendered graphic + // elements uniformed distributed when progress, especially when moving or zooming. + var lastModBy = normalizeModBy(this._modBy); + var lastModDataCount = this._modDataCount || 0; + var modBy = normalizeModBy(performArgs && performArgs.modBy); + var modDataCount = performArgs && performArgs.modDataCount || 0; + if (lastModBy !== modBy || lastModDataCount !== modDataCount) { + planResult = 'reset'; + } + function normalizeModBy(val) { + !(val >= 1) && (val = 1); // jshint ignore:line + return val; + } + var forceFirstProgress; + if (this._dirty || planResult === 'reset') { + this._dirty = false; + forceFirstProgress = this._doReset(skip); + } + this._modBy = modBy; + this._modDataCount = modDataCount; + var step = performArgs && performArgs.step; + if (upTask) { + if ("development" !== 'production') { + assert(upTask._outputDueEnd != null); + } + this._dueEnd = upTask._outputDueEnd; + } + // DataTask or overallTask + else { + if ("development" !== 'production') { + assert(!this._progress || this._count); + } + this._dueEnd = this._count ? this._count(this.context) : Infinity; + } + // Note: Stubs, that its host overall task let it has progress, has progress. + // If no progress, pass index from upstream to downstream each time plan called. + if (this._progress) { + var start = this._dueIndex; + var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd); + if (!skip && (forceFirstProgress || start < end)) { + var progress = this._progress; + if (isArray(progress)) { + for (var i = 0; i < progress.length; i++) { + this._doProgress(progress[i], start, end, modBy, modDataCount); + } + } else { + this._doProgress(progress, start, end, modBy, modDataCount); + } + } + this._dueIndex = end; + // If no `outputDueEnd`, assume that output data and + // input data is the same, so use `dueIndex` as `outputDueEnd`. + var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end; + if ("development" !== 'production') { + // ??? Can not rollback. + assert(outputDueEnd >= this._outputDueEnd); + } + this._outputDueEnd = outputDueEnd; + } else { + // (1) Some overall task has no progress. + // (2) Stubs, that its host overall task do not let it has progress, has no progress. + // This should always be performed so it can be passed to downstream. + this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd; + } + return this.unfinished(); + }; + Task.prototype.dirty = function () { + this._dirty = true; + this._onDirty && this._onDirty(this.context); + }; + Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) { + iterator.reset(start, end, modBy, modDataCount); + this._callingProgress = progress; + this._callingProgress({ + start: start, + end: end, + count: end - start, + next: iterator.next + }, this.context); + }; + Task.prototype._doReset = function (skip) { + this._dueIndex = this._outputDueEnd = this._dueEnd = 0; + this._settedOutputEnd = null; + var progress; + var forceFirstProgress; + if (!skip && this._reset) { + progress = this._reset(this.context); + if (progress && progress.progress) { + forceFirstProgress = progress.forceFirstProgress; + progress = progress.progress; + } + // To simplify no progress checking, array must has item. + if (isArray(progress) && !progress.length) { + progress = null; + } + } + this._progress = progress; + this._modBy = this._modDataCount = null; + var downstream = this._downstream; + downstream && downstream.dirty(); + return forceFirstProgress; + }; + Task.prototype.unfinished = function () { + return this._progress && this._dueIndex < this._dueEnd; + }; + /** + * @param downTask The downstream task. + * @return The downstream task. + */ + Task.prototype.pipe = function (downTask) { + if ("development" !== 'production') { + assert(downTask && !downTask._disposed && downTask !== this); + } + // If already downstream, do not dirty downTask. + if (this._downstream !== downTask || this._dirty) { + this._downstream = downTask; + downTask._upstream = this; + downTask.dirty(); + } + }; + Task.prototype.dispose = function () { + if (this._disposed) { + return; + } + this._upstream && (this._upstream._downstream = null); + this._downstream && (this._downstream._upstream = null); + this._dirty = false; + this._disposed = true; + }; + Task.prototype.getUpstream = function () { + return this._upstream; + }; + Task.prototype.getDownstream = function () { + return this._downstream; + }; + Task.prototype.setOutputEnd = function (end) { + // This only happens in dataTask, dataZoom, map, currently. + // where dataZoom do not set end each time, but only set + // when reset. So we should record the set end, in case + // that the stub of dataZoom perform again and earse the + // set end by upstream. + this._outputDueEnd = this._settedOutputEnd = end; + }; + return Task; + }(); + var iterator = function () { + var end; + var current; + var modBy; + var modDataCount; + var winCount; + var it = { + reset: function (s, e, sStep, sCount) { + current = s; + end = e; + modBy = sStep; + modDataCount = sCount; + winCount = Math.ceil(modDataCount / modBy); + it.next = modBy > 1 && modDataCount > 0 ? modNext : sequentialNext; + } + }; + return it; + function sequentialNext() { + return current < end ? current++ : null; + } + function modNext() { + var dataIndex = current % winCount * modBy + Math.ceil(current / winCount); + var result = current >= end ? null : dataIndex < modDataCount ? dataIndex + // If modDataCount is smaller than data.count() (consider `appendData` case), + // Use normal linear rendering mode. + : current; + current++; + return result; + } + }(); + // ----------------------------------------------------------------------------- + // For stream debug (Should be commented out after used!) + // @usage: printTask(this, 'begin'); + // @usage: printTask(this, null, {someExtraProp}); + // @usage: Use `__idxInPipeline` as conditional breakpiont. + // + // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void { + // window.ecTaskUID == null && (window.ecTaskUID = 0); + // task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`); + // task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`); + // let props = []; + // if (task.__pipeline) { + // let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`; + // props.push({text: '__idxInPipeline/total', value: val}); + // } else { + // let stubCount = 0; + // task.agentStubMap.each(() => stubCount++); + // props.push({text: 'idx', value: `overall (stubs: ${stubCount})`}); + // } + // props.push({text: 'uid', value: task.uidDebug}); + // if (task.__pipeline) { + // props.push({text: 'pipelineId', value: task.__pipeline.id}); + // task.agent && props.push( + // {text: 'stubFor', value: task.agent.uidDebug} + // ); + // } + // props.push( + // {text: 'dirty', value: task._dirty}, + // {text: 'dueIndex', value: task._dueIndex}, + // {text: 'dueEnd', value: task._dueEnd}, + // {text: 'outputDueEnd', value: task._outputDueEnd} + // ); + // if (extra) { + // Object.keys(extra).forEach(key => { + // props.push({text: key, value: extra[key]}); + // }); + // } + // let args = ['color: blue']; + // let msg = `%c[${prefix || 'T'}] %c` + props.map(item => ( + // args.push('color: green', 'color: red'), + // `${item.text}: %c${item.value}` + // )).join('%c, '); + // console.log.apply(console, [msg].concat(args)); + // // console.log(this); + // }; + // window.printPipeline = function (task: any, prefix: string) { + // const pipeline = task.__pipeline; + // let currTask = pipeline.head; + // while (currTask) { + // window.printTask(currTask, prefix); + // currTask = currTask._downstream; + // } + // }; + // window.showChain = function (chainHeadTask) { + // var chain = []; + // var task = chainHeadTask; + // while (task) { + // chain.push({ + // task: task, + // up: task._upstream, + // down: task._downstream, + // idxInPipeline: task.__idxInPipeline + // }); + // task = task._downstream; + // } + // return chain; + // }; + // window.findTaskInChain = function (task, chainHeadTask) { + // let chain = window.showChain(chainHeadTask); + // let result = []; + // for (let i = 0; i < chain.length; i++) { + // let chainItem = chain[i]; + // if (chainItem.task === task) { + // result.push(i); + // } + // } + // return result; + // }; + // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) { + // let chainA = window.showChain(chainHeadTaskA); + // for (let i = 0; i < chainA.length; i++) { + // console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB)); + // } + // }; + + /** + * Convert raw the value in to inner value in List. + * + * [Performance sensitive] + * + * [Caution]: this is the key logic of user value parser. + * For backward compatibility, do not modify it until you have to! + */ + function parseDataValue(value, + // For high performance, do not omit the second param. + opt) { + // Performance sensitive. + var dimType = opt && opt.type; + if (dimType === 'ordinal') { + // If given value is a category string + return value; + } + if (dimType === 'time' + // spead up when using timestamp + && !isNumber(value) && value != null && value !== '-') { + value = +parseDate(value); + } + // dimType defaults 'number'. + // If dimType is not ordinal and value is null or undefined or NaN or '-', + // parse to NaN. + // number-like string (like ' 123 ') can be converted to a number. + // where null/undefined or other string will be converted to NaN. + return value == null || value === '' ? NaN + // If string (like '-'), using '+' parse to NaN + // If object, also parse to NaN + : +value; + } + var valueParserMap = createHashMap({ + 'number': function (val) { + // Do not use `numericToNumber` here. We have `numericToNumber` by default. + // Here the number parser can have loose rule: + // enable to cut suffix: "120px" => 120, "14%" => 14. + return parseFloat(val); + }, + 'time': function (val) { + // return timestamp. + return +parseDate(val); + }, + 'trim': function (val) { + return isString(val) ? trim(val) : val; + } + }); + function getRawValueParser(type) { + return valueParserMap.get(type); + } + var ORDER_COMPARISON_OP_MAP = { + lt: function (lval, rval) { + return lval < rval; + }, + lte: function (lval, rval) { + return lval <= rval; + }, + gt: function (lval, rval) { + return lval > rval; + }, + gte: function (lval, rval) { + return lval >= rval; + } + }; + var FilterOrderComparator = /** @class */function () { + function FilterOrderComparator(op, rval) { + if (!isNumber(rval)) { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = 'rvalue of "<", ">", "<=", ">=" can only be number in filter.'; + } + throwError(errMsg); + } + this._opFn = ORDER_COMPARISON_OP_MAP[op]; + this._rvalFloat = numericToNumber(rval); + } + // Performance sensitive. + FilterOrderComparator.prototype.evaluate = function (lval) { + // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat. + return isNumber(lval) ? this._opFn(lval, this._rvalFloat) : this._opFn(numericToNumber(lval), this._rvalFloat); + }; + return FilterOrderComparator; + }(); + var SortOrderComparator = /** @class */function () { + /** + * @param order by default: 'asc' + * @param incomparable by default: Always on the tail. + * That is, if 'asc' => 'max', if 'desc' => 'min' + * See the definition of "incomparable" in [SORT_COMPARISON_RULE]. + */ + function SortOrderComparator(order, incomparable) { + var isDesc = order === 'desc'; + this._resultLT = isDesc ? 1 : -1; + if (incomparable == null) { + incomparable = isDesc ? 'min' : 'max'; + } + this._incomparable = incomparable === 'min' ? -Infinity : Infinity; + } + // See [SORT_COMPARISON_RULE]. + // Performance sensitive. + SortOrderComparator.prototype.evaluate = function (lval, rval) { + // Most cases is 'number', and typeof maybe 10 times faseter than parseFloat. + var lvalFloat = isNumber(lval) ? lval : numericToNumber(lval); + var rvalFloat = isNumber(rval) ? rval : numericToNumber(rval); + var lvalNotNumeric = isNaN(lvalFloat); + var rvalNotNumeric = isNaN(rvalFloat); + if (lvalNotNumeric) { + lvalFloat = this._incomparable; + } + if (rvalNotNumeric) { + rvalFloat = this._incomparable; + } + if (lvalNotNumeric && rvalNotNumeric) { + var lvalIsStr = isString(lval); + var rvalIsStr = isString(rval); + if (lvalIsStr) { + lvalFloat = rvalIsStr ? lval : 0; + } + if (rvalIsStr) { + rvalFloat = lvalIsStr ? rval : 0; + } + } + return lvalFloat < rvalFloat ? this._resultLT : lvalFloat > rvalFloat ? -this._resultLT : 0; + }; + return SortOrderComparator; + }(); + var FilterEqualityComparator = /** @class */function () { + function FilterEqualityComparator(isEq, rval) { + this._rval = rval; + this._isEQ = isEq; + this._rvalTypeof = typeof rval; + this._rvalFloat = numericToNumber(rval); + } + // Performance sensitive. + FilterEqualityComparator.prototype.evaluate = function (lval) { + var eqResult = lval === this._rval; + if (!eqResult) { + var lvalTypeof = typeof lval; + if (lvalTypeof !== this._rvalTypeof && (lvalTypeof === 'number' || this._rvalTypeof === 'number')) { + eqResult = numericToNumber(lval) === this._rvalFloat; + } + } + return this._isEQ ? eqResult : !eqResult; + }; + return FilterEqualityComparator; + }(); + /** + * [FILTER_COMPARISON_RULE] + * `lt`|`lte`|`gt`|`gte`: + * + rval must be a number. And lval will be converted to number (`numericToNumber`) to compare. + * `eq`: + * + If same type, compare with `===`. + * + If there is one number, convert to number (`numericToNumber`) to compare. + * + Else return `false`. + * `ne`: + * + Not `eq`. + * + * + * [SORT_COMPARISON_RULE] + * All the values are grouped into three categories: + * + "numeric" (number and numeric string) + * + "non-numeric-string" (string that excluding numeric string) + * + "others" + * "numeric" vs "numeric": values are ordered by number order. + * "non-numeric-string" vs "non-numeric-string": values are ordered by ES spec (#sec-abstract-relational-comparison). + * "others" vs "others": do not change order (always return 0). + * "numeric" vs "non-numeric-string": "non-numeric-string" is treated as "incomparable". + * "number" vs "others": "others" is treated as "incomparable". + * "non-numeric-string" vs "others": "others" is treated as "incomparable". + * "incomparable" will be seen as -Infinity or Infinity (depends on the settings). + * MEMO: + * Non-numeric string sort makes sense when we need to put the items with the same tag together. + * But if we support string sort, we still need to avoid the misleading like `'2' > '12'`, + * So we treat "numeric-string" sorted by number order rather than string comparison. + * + * + * [CHECK_LIST_OF_THE_RULE_DESIGN] + * + Do not support string comparison until required. And also need to + * avoid the misleading of "2" > "12". + * + Should avoid the misleading case: + * `" 22 " gte "22"` is `true` but `" 22 " eq "22"` is `false`. + * + JS bad case should be avoided: null <= 0, [] <= 0, ' ' <= 0, ... + * + Only "numeric" can be converted to comparable number, otherwise converted to NaN. + * See `util/number.ts#numericToNumber`. + * + * @return If `op` is not `RelationalOperator`, return null; + */ + function createFilterComparator(op, rval) { + return op === 'eq' || op === 'ne' ? new FilterEqualityComparator(op === 'eq', rval) : hasOwn(ORDER_COMPARISON_OP_MAP, op) ? new FilterOrderComparator(op, rval) : null; + } + + /** + * TODO: disable writable. + * This structure will be exposed to users. + */ + var ExternalSource = /** @class */function () { + function ExternalSource() {} + ExternalSource.prototype.getRawData = function () { + // Only built-in transform available. + throw new Error('not supported'); + }; + ExternalSource.prototype.getRawDataItem = function (dataIndex) { + // Only built-in transform available. + throw new Error('not supported'); + }; + ExternalSource.prototype.cloneRawData = function () { + return; + }; + /** + * @return If dimension not found, return null/undefined. + */ + ExternalSource.prototype.getDimensionInfo = function (dim) { + return; + }; + /** + * dimensions defined if and only if either: + * (a) dataset.dimensions are declared. + * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`). + * If dimensions are defined, `dimensionInfoAll` is corresponding to + * the defined dimensions. + * Otherwise, `dimensionInfoAll` is determined by data columns. + * @return Always return an array (even empty array). + */ + ExternalSource.prototype.cloneAllDimensionInfo = function () { + return; + }; + ExternalSource.prototype.count = function () { + return; + }; + /** + * Only support by dimension index. + * No need to support by dimension name in transform function, + * because transform function is not case-specific, no need to use name literally. + */ + ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) { + return; + }; + ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) { + return; + }; + ExternalSource.prototype.convertValue = function (rawVal, dimInfo) { + return parseDataValue(rawVal, dimInfo); + }; + return ExternalSource; + }(); + function createExternalSource(internalSource, externalTransform) { + var extSource = new ExternalSource(); + var data = internalSource.data; + var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat; + var sourceHeaderCount = internalSource.startIndex; + var errMsg = ''; + if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) { + // For the logic simplicity in transformer, only 'culumn' is + // supported in data transform. Otherwise, the `dimensionsDefine` + // might be detected by 'row', which probably confuses users. + if ("development" !== 'production') { + errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.'; + } + throwError(errMsg); + } + // [MEMO] + // Create a new dimensions structure for exposing. + // Do not expose all dimension info to users directly. + // Because the dimension is probably auto detected from data and not might reliable. + // Should not lead the transformers to think that is reliable and return it. + // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`. + var dimensions = []; + var dimsByName = {}; + var dimsDef = internalSource.dimensionsDefine; + if (dimsDef) { + each(dimsDef, function (dimDef, idx) { + var name = dimDef.name; + var dimDefExt = { + index: idx, + name: name, + displayName: dimDef.displayName + }; + dimensions.push(dimDefExt); + // Users probably do not specify dimension name. For simplicity, data transform + // does not generate dimension name. + if (name != null) { + // Dimension name should not be duplicated. + // For simplicity, data transform forbids name duplication, do not generate + // new name like module `completeDimensions.ts` did, but just tell users. + var errMsg_1 = ''; + if (hasOwn(dimsByName, name)) { + if ("development" !== 'production') { + errMsg_1 = 'dimension name "' + name + '" duplicated.'; + } + throwError(errMsg_1); + } + dimsByName[name] = dimDefExt; + } + }); + } + // If dimension definitions are not defined and can not be detected. + // e.g., pure data `[[11, 22], ...]`. + else { + for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) { + // Do not generete name or anything others. The consequence process in + // `transform` or `series` probably have there own name generation strategry. + dimensions.push({ + index: i + }); + } + } + // Implement public methods: + var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); + if (externalTransform.__isBuiltIn) { + extSource.getRawDataItem = function (dataIndex) { + return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex); + }; + extSource.getRawData = bind(getRawData, null, internalSource); + } + extSource.cloneRawData = bind(cloneRawData, null, internalSource); + var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); + extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions); + var rawValueGetter = getRawSourceValueGetter(sourceFormat); + extSource.retrieveValue = function (dataIndex, dimIndex) { + var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex); + return retrieveValueFromItem(rawItem, dimIndex); + }; + var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) { + if (dataItem == null) { + return; + } + var dimDef = dimensions[dimIndex]; + // When `dimIndex` is `null`, `rawValueGetter` return the whole item. + if (dimDef) { + return rawValueGetter(dataItem, dimIndex, dimDef.name); + } + }; + extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName); + extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions); + return extSource; + } + function getRawData(upstream) { + var sourceFormat = upstream.sourceFormat; + if (!isSupportedSourceFormat(sourceFormat)) { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = '`getRawData` is not supported in source format ' + sourceFormat; + } + throwError(errMsg); + } + return upstream.data; + } + function cloneRawData(upstream) { + var sourceFormat = upstream.sourceFormat; + var data = upstream.data; + if (!isSupportedSourceFormat(sourceFormat)) { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat; + } + throwError(errMsg); + } + if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { + var result = []; + for (var i = 0, len = data.length; i < len; i++) { + // Not strictly clone for performance + result.push(data[i].slice()); + } + return result; + } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { + var result = []; + for (var i = 0, len = data.length; i < len; i++) { + // Not strictly clone for performance + result.push(extend({}, data[i])); + } + return result; + } + } + function getDimensionInfo(dimensions, dimsByName, dim) { + if (dim == null) { + return; + } + // Keep the same logic as `List::getDimension` did. + if (isNumber(dim) + // If being a number-like string but not being defined a dimension name. + || !isNaN(dim) && !hasOwn(dimsByName, dim)) { + return dimensions[dim]; + } else if (hasOwn(dimsByName, dim)) { + return dimsByName[dim]; + } + } + function cloneAllDimensionInfo(dimensions) { + return clone(dimensions); + } + var externalTransformMap = createHashMap(); + function registerExternalTransform(externalTransform) { + externalTransform = clone(externalTransform); + var type = externalTransform.type; + var errMsg = ''; + if (!type) { + if ("development" !== 'production') { + errMsg = 'Must have a `type` when `registerTransform`.'; + } + throwError(errMsg); + } + var typeParsed = type.split(':'); + if (typeParsed.length !== 2) { + if ("development" !== 'production') { + errMsg = 'Name must include namespace like "ns:regression".'; + } + throwError(errMsg); + } + // Namespace 'echarts:xxx' is official namespace, where the transforms should + // be called directly via 'xxx' rather than 'echarts:xxx'. + var isBuiltIn = false; + if (typeParsed[0] === 'echarts') { + type = typeParsed[1]; + isBuiltIn = true; + } + externalTransform.__isBuiltIn = isBuiltIn; + externalTransformMap.set(type, externalTransform); + } + function applyDataTransform(rawTransOption, sourceList, infoForPrint) { + var pipedTransOption = normalizeToArray(rawTransOption); + var pipeLen = pipedTransOption.length; + var errMsg = ''; + if (!pipeLen) { + if ("development" !== 'production') { + errMsg = 'If `transform` declared, it should at least contain one transform.'; + } + throwError(errMsg); + } + for (var i = 0, len = pipeLen; i < len; i++) { + var transOption = pipedTransOption[i]; + sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); + // piped transform only support single input, except the fist one. + // piped transform only support single output, except the last one. + if (i !== len - 1) { + sourceList.length = Math.max(sourceList.length, 1); + } + } + return sourceList; + } + function applySingleDataTransform(transOption, upSourceList, infoForPrint, + // If `pipeIndex` is null/undefined, no piped transform. + pipeIndex) { + var errMsg = ''; + if (!upSourceList.length) { + if ("development" !== 'production') { + errMsg = 'Must have at least one upstream dataset.'; + } + throwError(errMsg); + } + if (!isObject(transOption)) { + if ("development" !== 'production') { + errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.'; + } + throwError(errMsg); + } + var transType = transOption.type; + var externalTransform = externalTransformMap.get(transType); + if (!externalTransform) { + if ("development" !== 'production') { + errMsg = 'Can not find transform on type "' + transType + '".'; + } + throwError(errMsg); + } + // Prepare source + var extUpSourceList = map(upSourceList, function (upSource) { + return createExternalSource(upSource, externalTransform); + }); + var resultList = normalizeToArray(externalTransform.transform({ + upstream: extUpSourceList[0], + upstreamList: extUpSourceList, + config: clone(transOption.config) + })); + if ("development" !== 'production') { + if (transOption.print) { + var printStrArr = map(resultList, function (extSource) { + var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : ''; + return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\n'); + }).join('\n'); + log(printStrArr); + } + } + return map(resultList, function (result, resultIndex) { + var errMsg = ''; + if (!isObject(result)) { + if ("development" !== 'production') { + errMsg = 'A transform should not return some empty results.'; + } + throwError(errMsg); + } + if (!result.data) { + if ("development" !== 'production') { + errMsg = 'Transform result data should be not be null or undefined'; + } + throwError(errMsg); + } + var sourceFormat = detectSourceFormat(result.data); + if (!isSupportedSourceFormat(sourceFormat)) { + if ("development" !== 'production') { + errMsg = 'Transform result data should be array rows or object rows.'; + } + throwError(errMsg); + } + var resultMetaRawOption; + var firstUpSource = upSourceList[0]; + /** + * Intuitively, the end users known the content of the original `dataset.source`, + * calucating the transform result in mind. + * Suppose the original `dataset.source` is: + * ```js + * [ + * ['product', '2012', '2013', '2014', '2015'], + * ['AAA', 41.1, 30.4, 65.1, 53.3], + * ['BBB', 86.5, 92.1, 85.7, 83.1], + * ['CCC', 24.1, 67.2, 79.5, 86.4] + * ] + * ``` + * The dimension info have to be detected from the source data. + * Some of the transformers (like filter, sort) will follow the dimension info + * of upstream, while others use new dimensions (like aggregate). + * Transformer can output a field `dimensions` to define the its own output dimensions. + * We also allow transformers to ignore the output `dimensions` field, and + * inherit the upstream dimensions definition. It can reduce the burden of handling + * dimensions in transformers. + * + * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`. + */ + if (firstUpSource && resultIndex === 0 + // If transformer returns `dimensions`, it means that the transformer has different + // dimensions definitions. We do not inherit anything from upstream. + && !result.dimensions) { + var startIndex = firstUpSource.startIndex; + // We copy the header of upstream to the result, because: + // (1) The returned data always does not contain header line and can not be used + // as dimension-detection. In this case we can not use "detected dimensions" of + // upstream directly, because it might be detected based on different `seriesLayoutBy`. + // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`. + // So the original detected header should be add to the result, otherwise they can not be read. + if (startIndex) { + result.data = firstUpSource.data.slice(0, startIndex).concat(result.data); + } + resultMetaRawOption = { + seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, + sourceHeader: startIndex, + dimensions: firstUpSource.metaRawOption.dimensions + }; + } else { + resultMetaRawOption = { + seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, + sourceHeader: 0, + dimensions: result.dimensions + }; + } + return createSource(result.data, resultMetaRawOption, null); + }); + } + function isSupportedSourceFormat(sourceFormat) { + return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS; + } + + var UNDEFINED = 'undefined'; + /* global Float64Array, Int32Array, Uint32Array, Uint16Array */ + // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is + // different from the Ctor of typed array. + var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array; + var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array; + var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array; + var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array; + /** + * Multi dimensional data store + */ + var dataCtors = { + 'float': CtorFloat64Array, + 'int': CtorInt32Array, + // Ordinal data type can be string or int + 'ordinal': Array, + 'number': Array, + 'time': CtorFloat64Array + }; + var defaultDimValueGetters; + function getIndicesCtor(rawCount) { + // The possible max value in this._indicies is always this._rawCount despite of filtering. + return rawCount > 65535 ? CtorUint32Array : CtorUint16Array; + } + function getInitialExtent() { + return [Infinity, -Infinity]; + } + function cloneChunk(originalChunk) { + var Ctor = originalChunk.constructor; + // Only shallow clone is enough when Array. + return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk); + } + function prepareStore(store, dimIdx, dimType, end, append) { + var DataCtor = dataCtors[dimType || 'float']; + if (append) { + var oldStore = store[dimIdx]; + var oldLen = oldStore && oldStore.length; + if (!(oldLen === end)) { + var newStore = new DataCtor(end); + // The cost of the copy is probably inconsiderable + // within the initial chunkSize. + for (var j = 0; j < oldLen; j++) { + newStore[j] = oldStore[j]; + } + store[dimIdx] = newStore; + } + } else { + store[dimIdx] = new DataCtor(end); + } + } + /** + * Basically, DataStore API keep immutable. + */ + var DataStore = /** @class */function () { + function DataStore() { + this._chunks = []; + // It will not be calculated until needed. + this._rawExtent = []; + this._extent = []; + this._count = 0; + this._rawCount = 0; + this._calcDimNameToIdx = createHashMap(); + } + /** + * Initialize from data + */ + DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) { + if ("development" !== 'production') { + assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.'); + } + this._provider = provider; + // Clear + this._chunks = []; + this._indices = null; + this.getRawIndex = this._getRawIdxIdentity; + var source = provider.getSource(); + var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; + // Default dim value getter + this._dimValueGetter = dimValueGetter || defaultGetter; + // Reset raw extent. + this._rawExtent = []; + var willRetrieveDataByName = shouldRetrieveDataByName(source); + this._dimensions = map(inputDimensions, function (dim) { + if ("development" !== 'production') { + if (willRetrieveDataByName) { + assert(dim.property != null); + } + } + return { + // Only pick these two props. Not leak other properties like orderMeta. + type: dim.type, + property: dim.property + }; + }); + this._initDataFromProvider(0, provider.count()); + }; + DataStore.prototype.getProvider = function () { + return this._provider; + }; + /** + * Caution: even when a `source` instance owned by a series, the created data store + * may still be shared by different sereis (the source hash does not use all `source` + * props, see `sourceManager`). In this case, the `source` props that are not used in + * hash (like `source.dimensionDefine`) probably only belongs to a certain series and + * thus should not be fetch here. + */ + DataStore.prototype.getSource = function () { + return this._provider.getSource(); + }; + /** + * @caution Only used in dataStack. + */ + DataStore.prototype.ensureCalculationDimension = function (dimName, type) { + var calcDimNameToIdx = this._calcDimNameToIdx; + var dimensions = this._dimensions; + var calcDimIdx = calcDimNameToIdx.get(dimName); + if (calcDimIdx != null) { + if (dimensions[calcDimIdx].type === type) { + return calcDimIdx; + } + } else { + calcDimIdx = dimensions.length; + } + dimensions[calcDimIdx] = { + type: type + }; + calcDimNameToIdx.set(dimName, calcDimIdx); + this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount); + this._rawExtent[calcDimIdx] = getInitialExtent(); + return calcDimIdx; + }; + DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) { + var chunk = this._chunks[dimIdx]; + var dim = this._dimensions[dimIdx]; + var rawExtents = this._rawExtent; + var offset = dim.ordinalOffset || 0; + var len = chunk.length; + if (offset === 0) { + // We need to reset the rawExtent if collect is from start. + // Because this dimension may be guessed as number and calcuating a wrong extent. + rawExtents[dimIdx] = getInitialExtent(); + } + var dimRawExtent = rawExtents[dimIdx]; + // Parse from previous data offset. len may be changed after appendData + for (var i = offset; i < len; i++) { + var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]); + if (!isNaN(val)) { + dimRawExtent[0] = Math.min(val, dimRawExtent[0]); + dimRawExtent[1] = Math.max(val, dimRawExtent[1]); + } + } + dim.ordinalMeta = ordinalMeta; + dim.ordinalOffset = len; + dim.type = 'ordinal'; // Force to be ordinal + }; + + DataStore.prototype.getOrdinalMeta = function (dimIdx) { + var dimInfo = this._dimensions[dimIdx]; + var ordinalMeta = dimInfo.ordinalMeta; + return ordinalMeta; + }; + DataStore.prototype.getDimensionProperty = function (dimIndex) { + var item = this._dimensions[dimIndex]; + return item && item.property; + }; + /** + * Caution: Can be only called on raw data (before `this._indices` created). + */ + DataStore.prototype.appendData = function (data) { + if ("development" !== 'production') { + assert(!this._indices, 'appendData can only be called on raw data.'); + } + var provider = this._provider; + var start = this.count(); + provider.appendData(data); + var end = provider.count(); + if (!provider.persistent) { + end += start; + } + if (start < end) { + this._initDataFromProvider(start, end, true); + } + return [start, end]; + }; + DataStore.prototype.appendValues = function (values, minFillLen) { + var chunks = this._chunks; + var dimensions = this._dimensions; + var dimLen = dimensions.length; + var rawExtent = this._rawExtent; + var start = this.count(); + var end = start + Math.max(values.length, minFillLen || 0); + for (var i = 0; i < dimLen; i++) { + var dim = dimensions[i]; + prepareStore(chunks, i, dim.type, end, true); + } + var emptyDataItem = []; + for (var idx = start; idx < end; idx++) { + var sourceIdx = idx - start; + // Store the data by dimensions + for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) { + var dim = dimensions[dimIdx]; + var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx); + chunks[dimIdx][idx] = val; + var dimRawExtent = rawExtent[dimIdx]; + val < dimRawExtent[0] && (dimRawExtent[0] = val); + val > dimRawExtent[1] && (dimRawExtent[1] = val); + } + } + this._rawCount = this._count = end; + return { + start: start, + end: end + }; + }; + DataStore.prototype._initDataFromProvider = function (start, end, append) { + var provider = this._provider; + var chunks = this._chunks; + var dimensions = this._dimensions; + var dimLen = dimensions.length; + var rawExtent = this._rawExtent; + var dimNames = map(dimensions, function (dim) { + return dim.property; + }); + for (var i = 0; i < dimLen; i++) { + var dim = dimensions[i]; + if (!rawExtent[i]) { + rawExtent[i] = getInitialExtent(); + } + prepareStore(chunks, i, dim.type, end, append); + } + if (provider.fillStorage) { + provider.fillStorage(start, end, chunks, rawExtent); + } else { + var dataItem = []; + for (var idx = start; idx < end; idx++) { + // NOTICE: Try not to write things into dataItem + dataItem = provider.getItem(idx, dataItem); + // Each data item is value + // [1, 2] + // 2 + // Bar chart, line chart which uses category axis + // only gives the 'y' value. 'x' value is the indices of category + // Use a tempValue to normalize the value to be a (x, y) value + // Store the data by dimensions + for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) { + var dimStorage = chunks[dimIdx]; + // PENDING NULL is empty or zero + var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx); + dimStorage[idx] = val; + var dimRawExtent = rawExtent[dimIdx]; + val < dimRawExtent[0] && (dimRawExtent[0] = val); + val > dimRawExtent[1] && (dimRawExtent[1] = val); + } + } + } + if (!provider.persistent && provider.clean) { + // Clean unused data if data source is typed array. + provider.clean(); + } + this._rawCount = this._count = end; + // Reset data extent + this._extent = []; + }; + DataStore.prototype.count = function () { + return this._count; + }; + /** + * Get value. Return NaN if idx is out of range. + */ + DataStore.prototype.get = function (dim, idx) { + if (!(idx >= 0 && idx < this._count)) { + return NaN; + } + var dimStore = this._chunks[dim]; + return dimStore ? dimStore[this.getRawIndex(idx)] : NaN; + }; + DataStore.prototype.getValues = function (dimensions, idx) { + var values = []; + var dimArr = []; + if (idx == null) { + idx = dimensions; + // TODO get all from store? + dimensions = []; + // All dimensions + for (var i = 0; i < this._dimensions.length; i++) { + dimArr.push(i); + } + } else { + dimArr = dimensions; + } + for (var i = 0, len = dimArr.length; i < len; i++) { + values.push(this.get(dimArr[i], idx)); + } + return values; + }; + /** + * @param dim concrete dim + */ + DataStore.prototype.getByRawIndex = function (dim, rawIdx) { + if (!(rawIdx >= 0 && rawIdx < this._rawCount)) { + return NaN; + } + var dimStore = this._chunks[dim]; + return dimStore ? dimStore[rawIdx] : NaN; + }; + /** + * Get sum of data in one dimension + */ + DataStore.prototype.getSum = function (dim) { + var dimData = this._chunks[dim]; + var sum = 0; + if (dimData) { + for (var i = 0, len = this.count(); i < len; i++) { + var value = this.get(dim, i); + if (!isNaN(value)) { + sum += value; + } + } + } + return sum; + }; + /** + * Get median of data in one dimension + */ + DataStore.prototype.getMedian = function (dim) { + var dimDataArray = []; + // map all data of one dimension + this.each([dim], function (val) { + if (!isNaN(val)) { + dimDataArray.push(val); + } + }); + // TODO + // Use quick select? + var sortedDimDataArray = dimDataArray.sort(function (a, b) { + return a - b; + }); + var len = this.count(); + // calculate median + return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2; + }; + /** + * Retrieve the index with given raw data index. + */ + DataStore.prototype.indexOfRawIndex = function (rawIndex) { + if (rawIndex >= this._rawCount || rawIndex < 0) { + return -1; + } + if (!this._indices) { + return rawIndex; + } + // Indices are ascending + var indices = this._indices; + // If rawIndex === dataIndex + var rawDataIndex = indices[rawIndex]; + if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) { + return rawIndex; + } + var left = 0; + var right = this._count - 1; + while (left <= right) { + var mid = (left + right) / 2 | 0; + if (indices[mid] < rawIndex) { + left = mid + 1; + } else if (indices[mid] > rawIndex) { + right = mid - 1; + } else { + return mid; + } + } + return -1; + }; + /** + * Retrieve the index of nearest value. + * @param dim + * @param value + * @param [maxDistance=Infinity] + * @return If and only if multiple indices have + * the same value, they are put to the result. + */ + DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) { + var chunks = this._chunks; + var dimData = chunks[dim]; + var nearestIndices = []; + if (!dimData) { + return nearestIndices; + } + if (maxDistance == null) { + maxDistance = Infinity; + } + var minDist = Infinity; + var minDiff = -1; + var nearestIndicesLen = 0; + // Check the test case of `test/ut/spec/data/SeriesData.js`. + for (var i = 0, len = this.count(); i < len; i++) { + var dataIndex = this.getRawIndex(i); + var diff = value - dimData[dataIndex]; + var dist = Math.abs(diff); + if (dist <= maxDistance) { + // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`, + // we'd better not push both of them to `nearestIndices`, otherwise it is easy to + // get more than one item in `nearestIndices` (more specifically, in `tooltip`). + // So we choose the one that `diff >= 0` in this case. + // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them + // should be push to `nearestIndices`. + if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) { + minDist = dist; + minDiff = diff; + nearestIndicesLen = 0; + } + if (diff === minDiff) { + nearestIndices[nearestIndicesLen++] = i; + } + } + } + nearestIndices.length = nearestIndicesLen; + return nearestIndices; + }; + DataStore.prototype.getIndices = function () { + var newIndices; + var indices = this._indices; + if (indices) { + var Ctor = indices.constructor; + var thisCount = this._count; + // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`. + if (Ctor === Array) { + newIndices = new Ctor(thisCount); + for (var i = 0; i < thisCount; i++) { + newIndices[i] = indices[i]; + } + } else { + newIndices = new Ctor(indices.buffer, 0, thisCount); + } + } else { + var Ctor = getIndicesCtor(this._rawCount); + newIndices = new Ctor(this.count()); + for (var i = 0; i < newIndices.length; i++) { + newIndices[i] = i; + } + } + return newIndices; + }; + /** + * Data filter. + */ + DataStore.prototype.filter = function (dims, cb) { + if (!this._count) { + return this; + } + var newStore = this.clone(); + var count = newStore.count(); + var Ctor = getIndicesCtor(newStore._rawCount); + var newIndices = new Ctor(count); + var value = []; + var dimSize = dims.length; + var offset = 0; + var dim0 = dims[0]; + var chunks = newStore._chunks; + for (var i = 0; i < count; i++) { + var keep = void 0; + var rawIdx = newStore.getRawIndex(i); + // Simple optimization + if (dimSize === 0) { + keep = cb(i); + } else if (dimSize === 1) { + var val = chunks[dim0][rawIdx]; + keep = cb(val, i); + } else { + var k = 0; + for (; k < dimSize; k++) { + value[k] = chunks[dims[k]][rawIdx]; + } + value[k] = i; + keep = cb.apply(null, value); + } + if (keep) { + newIndices[offset++] = rawIdx; + } + } + // Set indices after filtered. + if (offset < count) { + newStore._indices = newIndices; + } + newStore._count = offset; + // Reset data extent + newStore._extent = []; + newStore._updateGetRawIdx(); + return newStore; + }; + /** + * Select data in range. (For optimization of filter) + * (Manually inline code, support 5 million data filtering in data zoom.) + */ + DataStore.prototype.selectRange = function (range) { + var newStore = this.clone(); + var len = newStore._count; + if (!len) { + return this; + } + var dims = keys(range); + var dimSize = dims.length; + if (!dimSize) { + return this; + } + var originalCount = newStore.count(); + var Ctor = getIndicesCtor(newStore._rawCount); + var newIndices = new Ctor(originalCount); + var offset = 0; + var dim0 = dims[0]; + var min = range[dim0][0]; + var max = range[dim0][1]; + var storeArr = newStore._chunks; + var quickFinished = false; + if (!newStore._indices) { + // Extreme optimization for common case. About 2x faster in chrome. + var idx = 0; + if (dimSize === 1) { + var dimStorage = storeArr[dims[0]]; + for (var i = 0; i < len; i++) { + var val = dimStorage[i]; + // NaN will not be filtered. Consider the case, in line chart, empty + // value indicates the line should be broken. But for the case like + // scatter plot, a data item with empty value will not be rendered, + // but the axis extent may be effected if some other dim of the data + // item has value. Fortunately it is not a significant negative effect. + if (val >= min && val <= max || isNaN(val)) { + newIndices[offset++] = idx; + } + idx++; + } + quickFinished = true; + } else if (dimSize === 2) { + var dimStorage = storeArr[dims[0]]; + var dimStorage2 = storeArr[dims[1]]; + var min2 = range[dims[1]][0]; + var max2 = range[dims[1]][1]; + for (var i = 0; i < len; i++) { + var val = dimStorage[i]; + var val2 = dimStorage2[i]; + // Do not filter NaN, see comment above. + if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) { + newIndices[offset++] = idx; + } + idx++; + } + quickFinished = true; + } + } + if (!quickFinished) { + if (dimSize === 1) { + for (var i = 0; i < originalCount; i++) { + var rawIndex = newStore.getRawIndex(i); + var val = storeArr[dims[0]][rawIndex]; + // Do not filter NaN, see comment above. + if (val >= min && val <= max || isNaN(val)) { + newIndices[offset++] = rawIndex; + } + } + } else { + for (var i = 0; i < originalCount; i++) { + var keep = true; + var rawIndex = newStore.getRawIndex(i); + for (var k = 0; k < dimSize; k++) { + var dimk = dims[k]; + var val = storeArr[dimk][rawIndex]; + // Do not filter NaN, see comment above. + if (val < range[dimk][0] || val > range[dimk][1]) { + keep = false; + } + } + if (keep) { + newIndices[offset++] = newStore.getRawIndex(i); + } + } + } + } + // Set indices after filtered. + if (offset < originalCount) { + newStore._indices = newIndices; + } + newStore._count = offset; + // Reset data extent + newStore._extent = []; + newStore._updateGetRawIdx(); + return newStore; + }; + // /** + // * Data mapping to a plain array + // */ + // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] { + // const result: any[] = []; + // this.each(dims, function () { + // result.push(cb && (cb as MapArrayCb).apply(null, arguments)); + // }); + // return result; + // } + /** + * Data mapping to a new List with given dimensions + */ + DataStore.prototype.map = function (dims, cb) { + // TODO only clone picked chunks. + var target = this.clone(dims); + this._updateDims(target, dims, cb); + return target; + }; + /** + * @caution Danger!! Only used in dataStack. + */ + DataStore.prototype.modify = function (dims, cb) { + this._updateDims(this, dims, cb); + }; + DataStore.prototype._updateDims = function (target, dims, cb) { + var targetChunks = target._chunks; + var tmpRetValue = []; + var dimSize = dims.length; + var dataCount = target.count(); + var values = []; + var rawExtent = target._rawExtent; + for (var i = 0; i < dims.length; i++) { + rawExtent[dims[i]] = getInitialExtent(); + } + for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) { + var rawIndex = target.getRawIndex(dataIndex); + for (var k = 0; k < dimSize; k++) { + values[k] = targetChunks[dims[k]][rawIndex]; + } + values[dimSize] = dataIndex; + var retValue = cb && cb.apply(null, values); + if (retValue != null) { + // a number or string (in oridinal dimension)? + if (typeof retValue !== 'object') { + tmpRetValue[0] = retValue; + retValue = tmpRetValue; + } + for (var i = 0; i < retValue.length; i++) { + var dim = dims[i]; + var val = retValue[i]; + var rawExtentOnDim = rawExtent[dim]; + var dimStore = targetChunks[dim]; + if (dimStore) { + dimStore[rawIndex] = val; + } + if (val < rawExtentOnDim[0]) { + rawExtentOnDim[0] = val; + } + if (val > rawExtentOnDim[1]) { + rawExtentOnDim[1] = val; + } + } + } + } + }; + /** + * Large data down sampling using largest-triangle-three-buckets + * @param {string} valueDimension + * @param {number} targetCount + */ + DataStore.prototype.lttbDownSample = function (valueDimension, rate) { + var target = this.clone([valueDimension], true); + var targetStorage = target._chunks; + var dimStore = targetStorage[valueDimension]; + var len = this.count(); + var sampledIndex = 0; + var frameSize = Math.floor(1 / rate); + var currentRawIndex = this.getRawIndex(0); + var maxArea; + var area; + var nextRawIndex; + var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len)); + // First frame use the first data. + newIndices[sampledIndex++] = currentRawIndex; + for (var i = 1; i < len - 1; i += frameSize) { + var nextFrameStart = Math.min(i + frameSize, len - 1); + var nextFrameEnd = Math.min(i + frameSize * 2, len); + var avgX = (nextFrameEnd + nextFrameStart) / 2; + var avgY = 0; + for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) { + var rawIndex = this.getRawIndex(idx); + var y = dimStore[rawIndex]; + if (isNaN(y)) { + continue; + } + avgY += y; + } + avgY /= nextFrameEnd - nextFrameStart; + var frameStart = i; + var frameEnd = Math.min(i + frameSize, len); + var pointAX = i - 1; + var pointAY = dimStore[currentRawIndex]; + maxArea = -1; + nextRawIndex = frameStart; + var firstNaNIndex = -1; + var countNaN = 0; + // Find a point from current frame that construct a triangle with largest area with previous selected point + // And the average of next frame. + for (var idx = frameStart; idx < frameEnd; idx++) { + var rawIndex = this.getRawIndex(idx); + var y = dimStore[rawIndex]; + if (isNaN(y)) { + countNaN++; + if (firstNaNIndex < 0) { + firstNaNIndex = rawIndex; + } + continue; + } + // Calculate triangle area over three buckets + area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY)); + if (area > maxArea) { + maxArea = area; + nextRawIndex = rawIndex; // Next a is this b + } + } + + if (countNaN > 0 && countNaN < frameEnd - frameStart) { + // Append first NaN point in every bucket. + // It is necessary to ensure the correct order of indices. + newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex); + nextRawIndex = Math.max(firstNaNIndex, nextRawIndex); + } + newIndices[sampledIndex++] = nextRawIndex; + currentRawIndex = nextRawIndex; // This a is the next a (chosen b) + } + // First frame use the last data. + newIndices[sampledIndex++] = this.getRawIndex(len - 1); + target._count = sampledIndex; + target._indices = newIndices; + target.getRawIndex = this._getRawIdx; + return target; + }; + /** + * Large data down sampling on given dimension + * @param sampleIndex Sample index for name and id + */ + DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) { + var target = this.clone([dimension], true); + var targetStorage = target._chunks; + var frameValues = []; + var frameSize = Math.floor(1 / rate); + var dimStore = targetStorage[dimension]; + var len = this.count(); + var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent(); + var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize)); + var offset = 0; + for (var i = 0; i < len; i += frameSize) { + // Last frame + if (frameSize > len - i) { + frameSize = len - i; + frameValues.length = frameSize; + } + for (var k = 0; k < frameSize; k++) { + var dataIdx = this.getRawIndex(i + k); + frameValues[k] = dimStore[dataIdx]; + } + var value = sampleValue(frameValues); + var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); + // Only write value on the filtered data + dimStore[sampleFrameIdx] = value; + if (value < rawExtentOnDim[0]) { + rawExtentOnDim[0] = value; + } + if (value > rawExtentOnDim[1]) { + rawExtentOnDim[1] = value; + } + newIndices[offset++] = sampleFrameIdx; + } + target._count = offset; + target._indices = newIndices; + target._updateGetRawIdx(); + return target; + }; + /** + * Data iteration + * @param ctx default this + * @example + * list.each('x', function (x, idx) {}); + * list.each(['x', 'y'], function (x, y, idx) {}); + * list.each(function (idx) {}) + */ + DataStore.prototype.each = function (dims, cb) { + if (!this._count) { + return; + } + var dimSize = dims.length; + var chunks = this._chunks; + for (var i = 0, len = this.count(); i < len; i++) { + var rawIdx = this.getRawIndex(i); + // Simple optimization + switch (dimSize) { + case 0: + cb(i); + break; + case 1: + cb(chunks[dims[0]][rawIdx], i); + break; + case 2: + cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i); + break; + default: + var k = 0; + var value = []; + for (; k < dimSize; k++) { + value[k] = chunks[dims[k]][rawIdx]; + } + // Index + value[k] = i; + cb.apply(null, value); + } + } + }; + /** + * Get extent of data in one dimension + */ + DataStore.prototype.getDataExtent = function (dim) { + // Make sure use concrete dim as cache name. + var dimData = this._chunks[dim]; + var initialExtent = getInitialExtent(); + if (!dimData) { + return initialExtent; + } + // Make more strict checkings to ensure hitting cache. + var currEnd = this.count(); + // Consider the most cases when using data zoom, `getDataExtent` + // happened before filtering. We cache raw extent, which is not + // necessary to be cleared and recalculated when restore data. + var useRaw = !this._indices; + var dimExtent; + if (useRaw) { + return this._rawExtent[dim].slice(); + } + dimExtent = this._extent[dim]; + if (dimExtent) { + return dimExtent.slice(); + } + dimExtent = initialExtent; + var min = dimExtent[0]; + var max = dimExtent[1]; + for (var i = 0; i < currEnd; i++) { + var rawIdx = this.getRawIndex(i); + var value = dimData[rawIdx]; + value < min && (min = value); + value > max && (max = value); + } + dimExtent = [min, max]; + this._extent[dim] = dimExtent; + return dimExtent; + }; + /** + * Get raw data item + */ + DataStore.prototype.getRawDataItem = function (idx) { + var rawIdx = this.getRawIndex(idx); + if (!this._provider.persistent) { + var val = []; + var chunks = this._chunks; + for (var i = 0; i < chunks.length; i++) { + val.push(chunks[i][rawIdx]); + } + return val; + } else { + return this._provider.getItem(rawIdx); + } + }; + /** + * Clone shallow. + * + * @param clonedDims Determine which dims to clone. Will share the data if not specified. + */ + DataStore.prototype.clone = function (clonedDims, ignoreIndices) { + var target = new DataStore(); + var chunks = this._chunks; + var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) { + obj[dimIdx] = true; + return obj; + }, {}); + if (clonedDimsMap) { + for (var i = 0; i < chunks.length; i++) { + // Not clone if dim is not picked. + target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]); + } + } else { + target._chunks = chunks; + } + this._copyCommonProps(target); + if (!ignoreIndices) { + target._indices = this._cloneIndices(); + } + target._updateGetRawIdx(); + return target; + }; + DataStore.prototype._copyCommonProps = function (target) { + target._count = this._count; + target._rawCount = this._rawCount; + target._provider = this._provider; + target._dimensions = this._dimensions; + target._extent = clone(this._extent); + target._rawExtent = clone(this._rawExtent); + }; + DataStore.prototype._cloneIndices = function () { + if (this._indices) { + var Ctor = this._indices.constructor; + var indices = void 0; + if (Ctor === Array) { + var thisCount = this._indices.length; + indices = new Ctor(thisCount); + for (var i = 0; i < thisCount; i++) { + indices[i] = this._indices[i]; + } + } else { + indices = new Ctor(this._indices); + } + return indices; + } + return null; + }; + DataStore.prototype._getRawIdxIdentity = function (idx) { + return idx; + }; + DataStore.prototype._getRawIdx = function (idx) { + if (idx < this._count && idx >= 0) { + return this._indices[idx]; + } + return -1; + }; + DataStore.prototype._updateGetRawIdx = function () { + this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity; + }; + DataStore.internalField = function () { + function getDimValueSimply(dataItem, property, dataIndex, dimIndex) { + return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]); + } + defaultDimValueGetters = { + arrayRows: getDimValueSimply, + objectRows: function (dataItem, property, dataIndex, dimIndex) { + return parseDataValue(dataItem[property], this._dimensions[dimIndex]); + }, + keyedColumns: getDimValueSimply, + original: function (dataItem, property, dataIndex, dimIndex) { + // Performance sensitive, do not use modelUtil.getDataItemValue. + // If dataItem is an plain object with no value field, the let `value` + // will be assigned with the object, but it will be tread correctly + // in the `convertValue`. + var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); + return parseDataValue(value instanceof Array ? value[dimIndex] + // If value is a single number or something else not array. + : value, this._dimensions[dimIndex]); + }, + typedArray: function (dataItem, property, dataIndex, dimIndex) { + return dataItem[dimIndex]; + } + }; + }(); + return DataStore; + }(); + + /** + * [REQUIREMENT_MEMO]: + * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option. + * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and + * `root-dataset`. Them on `series` has higher priority. + * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might + * confuse users: whether those props indicate how to visit the upstream source or visit + * the transform result source, and some transforms has nothing to do with these props, + * and some transforms might have multiple upstream. + * (3) Transforms should specify `metaRawOption` in each output, just like they can be + * declared in `root-dataset`. + * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms. + * That is for reducing complexity in transforms. + * PENDING: Whether to provide transposition transform? + * + * [IMPLEMENTAION_MEMO]: + * "sourceVisitConfig" are calculated from `metaRawOption` and `data`. + * They will not be calculated until `source` is about to be visited (to prevent from + * duplicate calcuation). `source` is visited only in series and input to transforms. + * + * [DIMENSION_INHERIT_RULE]: + * By default the dimensions are inherited from ancestors, unless a transform return + * a new dimensions definition. + * Consider the case: + * ```js + * dataset: [{ + * source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...] + * }, { + * transform: { type: 'filter', ... } + * }] + * dataset: [{ + * dimension: ['Product', 'Sales', 'Prise'], + * source: [ ['Cookies', 321, 44.21], ...] + * }, { + * transform: { type: 'filter', ... } + * }] + * ``` + * The two types of option should have the same behavior after transform. + * + * + * [SCENARIO]: + * (1) Provide source data directly: + * ```js + * series: { + * encode: {...}, + * dimensions: [...] + * seriesLayoutBy: 'row', + * data: [[...]] + * } + * ``` + * (2) Series refer to dataset. + * ```js + * series: [{ + * encode: {...} + * // Ignore datasetIndex means `datasetIndex: 0` + * // and the dimensions defination in dataset is used + * }, { + * encode: {...}, + * seriesLayoutBy: 'column', + * datasetIndex: 1 + * }] + * ``` + * (3) dataset transform + * ```js + * dataset: [{ + * source: [...] + * }, { + * source: [...] + * }, { + * // By default from 0. + * transform: { type: 'filter', config: {...} } + * }, { + * // Piped. + * transform: [ + * { type: 'filter', config: {...} }, + * { type: 'sort', config: {...} } + * ] + * }, { + * id: 'regressionData', + * fromDatasetIndex: 1, + * // Third-party transform + * transform: { type: 'ecStat:regression', config: {...} } + * }, { + * // retrieve the extra result. + * id: 'regressionFormula', + * fromDatasetId: 'regressionData', + * fromTransformResult: 1 + * }] + * ``` + */ + var SourceManager = /** @class */function () { + function SourceManager(sourceHost) { + // Cached source. Do not repeat calculating if not dirty. + this._sourceList = []; + this._storeList = []; + // version sign of each upstream source manager. + this._upstreamSignList = []; + this._versionSignBase = 0; + this._dirty = true; + this._sourceHost = sourceHost; + } + /** + * Mark dirty. + */ + SourceManager.prototype.dirty = function () { + this._setLocalSource([], []); + this._storeList = []; + this._dirty = true; + }; + SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) { + this._sourceList = sourceList; + this._upstreamSignList = upstreamSignList; + this._versionSignBase++; + if (this._versionSignBase > 9e10) { + this._versionSignBase = 0; + } + }; + /** + * For detecting whether the upstream source is dirty, so that + * the local cached source (in `_sourceList`) should be discarded. + */ + SourceManager.prototype._getVersionSign = function () { + return this._sourceHost.uid + '_' + this._versionSignBase; + }; + /** + * Always return a source instance. Otherwise throw error. + */ + SourceManager.prototype.prepareSource = function () { + // For the case that call `setOption` multiple time but no data changed, + // cache the result source to prevent from repeating transform. + if (this._isDirty()) { + this._createSource(); + this._dirty = false; + } + }; + SourceManager.prototype._createSource = function () { + this._setLocalSource([], []); + var sourceHost = this._sourceHost; + var upSourceMgrList = this._getUpstreamSourceManagers(); + var hasUpstream = !!upSourceMgrList.length; + var resultSourceList; + var upstreamSignList; + if (isSeries(sourceHost)) { + var seriesModel = sourceHost; + var data = void 0; + var sourceFormat = void 0; + var upSource = void 0; + // Has upstream dataset + if (hasUpstream) { + var upSourceMgr = upSourceMgrList[0]; + upSourceMgr.prepareSource(); + upSource = upSourceMgr.getSource(); + data = upSource.data; + sourceFormat = upSource.sourceFormat; + upstreamSignList = [upSourceMgr._getVersionSign()]; + } + // Series data is from own. + else { + data = seriesModel.get('data', true); + sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL; + upstreamSignList = []; + } + // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root. + var newMetaRawOption = this._getSourceMetaRawOption() || {}; + var upMetaRawOption = upSource && upSource.metaRawOption || {}; + var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null; + var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); + // Note here we should not use `upSource.dimensionsDefine`. Consider the case: + // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`, + // but series need `seriesLayoutBy: 'row'`. + var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); + // We share source with dataset as much as possible + // to avoid extra memory cost of high dimensional data. + var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions; + resultSourceList = needsCreateSource ? [createSource(data, { + seriesLayoutBy: seriesLayoutBy, + sourceHeader: sourceHeader, + dimensions: dimensions + }, sourceFormat)] : []; + } else { + var datasetModel = sourceHost; + // Has upstream dataset. + if (hasUpstream) { + var result = this._applyTransform(upSourceMgrList); + resultSourceList = result.sourceList; + upstreamSignList = result.upstreamSignList; + } + // Is root dataset. + else { + var sourceData = datasetModel.get('source', true); + resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)]; + upstreamSignList = []; + } + } + if ("development" !== 'production') { + assert(resultSourceList && upstreamSignList); + } + this._setLocalSource(resultSourceList, upstreamSignList); + }; + SourceManager.prototype._applyTransform = function (upMgrList) { + var datasetModel = this._sourceHost; + var transformOption = datasetModel.get('transform', true); + var fromTransformResult = datasetModel.get('fromTransformResult', true); + if ("development" !== 'production') { + assert(fromTransformResult != null || transformOption != null); + } + if (fromTransformResult != null) { + var errMsg = ''; + if (upMgrList.length !== 1) { + if ("development" !== 'production') { + errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset'; + } + doThrow(errMsg); + } + } + var sourceList; + var upSourceList = []; + var upstreamSignList = []; + each(upMgrList, function (upMgr) { + upMgr.prepareSource(); + var upSource = upMgr.getSource(fromTransformResult || 0); + var errMsg = ''; + if (fromTransformResult != null && !upSource) { + if ("development" !== 'production') { + errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult; + } + doThrow(errMsg); + } + upSourceList.push(upSource); + upstreamSignList.push(upMgr._getVersionSign()); + }); + if (transformOption) { + sourceList = applyDataTransform(transformOption, upSourceList, { + datasetIndex: datasetModel.componentIndex + }); + } else if (fromTransformResult != null) { + sourceList = [cloneSourceShallow(upSourceList[0])]; + } + return { + sourceList: sourceList, + upstreamSignList: upstreamSignList + }; + }; + SourceManager.prototype._isDirty = function () { + if (this._dirty) { + return true; + } + // All sourceList is from the some upstream. + var upSourceMgrList = this._getUpstreamSourceManagers(); + for (var i = 0; i < upSourceMgrList.length; i++) { + var upSrcMgr = upSourceMgrList[i]; + if ( + // Consider the case that there is ancestor diry, call it recursively. + // The performance is probably not an issue because usually the chain is not long. + upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) { + return true; + } + } + }; + /** + * @param sourceIndex By default 0, means "main source". + * In most cases there is only one source. + */ + SourceManager.prototype.getSource = function (sourceIndex) { + sourceIndex = sourceIndex || 0; + var source = this._sourceList[sourceIndex]; + if (!source) { + // Series may share source instance with dataset. + var upSourceMgrList = this._getUpstreamSourceManagers(); + return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex); + } + return source; + }; + /** + * + * Get a data store which can be shared across series. + * Only available for series. + * + * @param seriesDimRequest Dimensions that are generated in series. + * Should have been sorted by `storeDimIndex` asc. + */ + SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) { + if ("development" !== 'production') { + assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.'); + } + var schema = seriesDimRequest.makeStoreSchema(); + return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash); + }; + SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) { + // TODO Can use other sourceIndex? + var sourceIndex = 0; + var storeList = this._storeList; + var cachedStoreMap = storeList[sourceIndex]; + if (!cachedStoreMap) { + cachedStoreMap = storeList[sourceIndex] = {}; + } + var cachedStore = cachedStoreMap[sourceReadKey]; + if (!cachedStore) { + var upSourceMgr = this._getUpstreamSourceManagers()[0]; + if (isSeries(this._sourceHost) && upSourceMgr) { + cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey); + } else { + cachedStore = new DataStore(); + // Always create store from source of series. + cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims); + } + cachedStoreMap[sourceReadKey] = cachedStore; + } + return cachedStore; + }; + /** + * PENDING: Is it fast enough? + * If no upstream, return empty array. + */ + SourceManager.prototype._getUpstreamSourceManagers = function () { + // Always get the relationship from the raw option. + // Do not cache the link of the dependency graph, so that + // there is no need to update them when change happens. + var sourceHost = this._sourceHost; + if (isSeries(sourceHost)) { + var datasetModel = querySeriesUpstreamDatasetModel(sourceHost); + return !datasetModel ? [] : [datasetModel.getSourceManager()]; + } else { + return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) { + return datasetModel.getSourceManager(); + }); + } + }; + SourceManager.prototype._getSourceMetaRawOption = function () { + var sourceHost = this._sourceHost; + var seriesLayoutBy; + var sourceHeader; + var dimensions; + if (isSeries(sourceHost)) { + seriesLayoutBy = sourceHost.get('seriesLayoutBy', true); + sourceHeader = sourceHost.get('sourceHeader', true); + dimensions = sourceHost.get('dimensions', true); + } + // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them. + else if (!this._getUpstreamSourceManagers().length) { + var model = sourceHost; + seriesLayoutBy = model.get('seriesLayoutBy', true); + sourceHeader = model.get('sourceHeader', true); + dimensions = model.get('dimensions', true); + } + return { + seriesLayoutBy: seriesLayoutBy, + sourceHeader: sourceHeader, + dimensions: dimensions + }; + }; + return SourceManager; + }(); + // Call this method after `super.init` and `super.mergeOption` to + // disable the transform merge, but do not disable transform clone from rawOption. + function disableTransformOptionMerge(datasetModel) { + var transformOption = datasetModel.option.transform; + transformOption && setAsPrimitive(datasetModel.option.transform); + } + function isSeries(sourceHost) { + // Avoid circular dependency with Series.ts + return sourceHost.mainType === 'series'; + } + function doThrow(errMsg) { + throw new Error(errMsg); + } + + var TOOLTIP_LINE_HEIGHT_CSS = 'line-height:1'; + // TODO: more textStyle option + function getTooltipTextStyle(textStyle, renderMode) { + var nameFontColor = textStyle.color || '#6e7079'; + var nameFontSize = textStyle.fontSize || 12; + var nameFontWeight = textStyle.fontWeight || '400'; + var valueFontColor = textStyle.color || '#464646'; + var valueFontSize = textStyle.fontSize || 14; + var valueFontWeight = textStyle.fontWeight || '900'; + if (renderMode === 'html') { + // `textStyle` is probably from user input, should be encoded to reduce security risk. + return { + // eslint-disable-next-line max-len + nameStyle: "font-size:" + encodeHTML(nameFontSize + '') + "px;color:" + encodeHTML(nameFontColor) + ";font-weight:" + encodeHTML(nameFontWeight + ''), + // eslint-disable-next-line max-len + valueStyle: "font-size:" + encodeHTML(valueFontSize + '') + "px;color:" + encodeHTML(valueFontColor) + ";font-weight:" + encodeHTML(valueFontWeight + '') + }; + } else { + return { + nameStyle: { + fontSize: nameFontSize, + fill: nameFontColor, + fontWeight: nameFontWeight + }, + valueStyle: { + fontSize: valueFontSize, + fill: valueFontColor, + fontWeight: valueFontWeight + } + }; + } + } + // See `TooltipMarkupLayoutIntent['innerGapLevel']`. + // (value from UI design) + var HTML_GAPS = [0, 10, 20, 30]; + var RICH_TEXT_GAPS = ['', '\n', '\n\n', '\n\n\n']; + // eslint-disable-next-line max-len + function createTooltipMarkup(type, option) { + option.type = type; + return option; + } + function isSectionFragment(frag) { + return frag.type === 'section'; + } + function getBuilder(frag) { + return isSectionFragment(frag) ? buildSection : buildNameValue; + } + function getBlockGapLevel(frag) { + if (isSectionFragment(frag)) { + var gapLevel_1 = 0; + var subBlockLen = frag.blocks.length; + var hasInnerGap_1 = subBlockLen > 1 || subBlockLen > 0 && !frag.noHeader; + each(frag.blocks, function (subBlock) { + var subGapLevel = getBlockGapLevel(subBlock); + // If the some of the sub-blocks have some gaps (like 10px) inside, this block + // should use a larger gap (like 20px) to distinguish those sub-blocks. + if (subGapLevel >= gapLevel_1) { + gapLevel_1 = subGapLevel + +(hasInnerGap_1 && ( + // 0 always can not be readable gap level. + !subGapLevel + // If no header, always keep the sub gap level. Otherwise + // look weird in case `multipleSeries`. + || isSectionFragment(subBlock) && !subBlock.noHeader)); + } + }); + return gapLevel_1; + } + return 0; + } + function buildSection(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) { + var noHeader = fragment.noHeader; + var gaps = getGap(getBlockGapLevel(fragment)); + var subMarkupTextList = []; + var subBlocks = fragment.blocks || []; + assert(!subBlocks || isArray(subBlocks)); + subBlocks = subBlocks || []; + var orderMode = ctx.orderMode; + if (fragment.sortBlocks && orderMode) { + subBlocks = subBlocks.slice(); + var orderMap = { + valueAsc: 'asc', + valueDesc: 'desc' + }; + if (hasOwn(orderMap, orderMode)) { + var comparator_1 = new SortOrderComparator(orderMap[orderMode], null); + subBlocks.sort(function (a, b) { + return comparator_1.evaluate(a.sortParam, b.sortParam); + }); + } + // FIXME 'seriesDesc' necessary? + else if (orderMode === 'seriesDesc') { + subBlocks.reverse(); + } + } + each(subBlocks, function (subBlock, idx) { + var valueFormatter = fragment.valueFormatter; + var subMarkupText = getBuilder(subBlock)( + // Inherit valueFormatter + valueFormatter ? extend(extend({}, ctx), { + valueFormatter: valueFormatter + }) : ctx, subBlock, idx > 0 ? gaps.html : 0, toolTipTextStyle); + subMarkupText != null && subMarkupTextList.push(subMarkupText); + }); + var subMarkupText = ctx.renderMode === 'richText' ? subMarkupTextList.join(gaps.richText) : wrapBlockHTML(subMarkupTextList.join(''), noHeader ? topMarginForOuterGap : gaps.html); + if (noHeader) { + return subMarkupText; + } + var displayableHeader = makeValueReadable(fragment.header, 'ordinal', ctx.useUTC); + var nameStyle = getTooltipTextStyle(toolTipTextStyle, ctx.renderMode).nameStyle; + if (ctx.renderMode === 'richText') { + return wrapInlineNameRichText(ctx, displayableHeader, nameStyle) + gaps.richText + subMarkupText; + } else { + return wrapBlockHTML("
" + encodeHTML(displayableHeader) + '
' + subMarkupText, topMarginForOuterGap); + } + } + function buildNameValue(ctx, fragment, topMarginForOuterGap, toolTipTextStyle) { + var renderMode = ctx.renderMode; + var noName = fragment.noName; + var noValue = fragment.noValue; + var noMarker = !fragment.markerType; + var name = fragment.name; + var useUTC = ctx.useUTC; + var valueFormatter = fragment.valueFormatter || ctx.valueFormatter || function (value) { + value = isArray(value) ? value : [value]; + return map(value, function (val, idx) { + return makeValueReadable(val, isArray(valueTypeOption) ? valueTypeOption[idx] : valueTypeOption, useUTC); + }); + }; + if (noName && noValue) { + return; + } + var markerStr = noMarker ? '' : ctx.markupStyleCreator.makeTooltipMarker(fragment.markerType, fragment.markerColor || '#333', renderMode); + var readableName = noName ? '' : makeValueReadable(name, 'ordinal', useUTC); + var valueTypeOption = fragment.valueType; + var readableValueList = noValue ? [] : valueFormatter(fragment.value, fragment.dataIndex); + var valueAlignRight = !noMarker || !noName; + // It little weird if only value next to marker but far from marker. + var valueCloseToMarker = !noMarker && noName; + var _a = getTooltipTextStyle(toolTipTextStyle, renderMode), + nameStyle = _a.nameStyle, + valueStyle = _a.valueStyle; + return renderMode === 'richText' ? (noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameRichText(ctx, readableName, nameStyle)) + // Value has commas inside, so use ' ' as delimiter for multiple values. + + (noValue ? '' : wrapInlineValueRichText(ctx, readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)) : wrapBlockHTML((noMarker ? '' : markerStr) + (noName ? '' : wrapInlineNameHTML(readableName, !noMarker, nameStyle)) + (noValue ? '' : wrapInlineValueHTML(readableValueList, valueAlignRight, valueCloseToMarker, valueStyle)), topMarginForOuterGap); + } + /** + * @return markupText. null/undefined means no content. + */ + function buildTooltipMarkup(fragment, markupStyleCreator, renderMode, orderMode, useUTC, toolTipTextStyle) { + if (!fragment) { + return; + } + var builder = getBuilder(fragment); + var ctx = { + useUTC: useUTC, + renderMode: renderMode, + orderMode: orderMode, + markupStyleCreator: markupStyleCreator, + valueFormatter: fragment.valueFormatter + }; + return builder(ctx, fragment, 0, toolTipTextStyle); + } + function getGap(gapLevel) { + return { + html: HTML_GAPS[gapLevel], + richText: RICH_TEXT_GAPS[gapLevel] + }; + } + function wrapBlockHTML(encodedContent, topGap) { + var clearfix = '
'; + var marginCSS = "margin: " + topGap + "px 0 0"; + return "
" + encodedContent + clearfix + '
'; + } + function wrapInlineNameHTML(name, leftHasMarker, style) { + var marginCss = leftHasMarker ? 'margin-left:2px' : ''; + return "" + encodeHTML(name) + ''; + } + function wrapInlineValueHTML(valueList, alignRight, valueCloseToMarker, style) { + // Do not too close to marker, considering there are multiple values separated by spaces. + var paddingStr = valueCloseToMarker ? '10px' : '20px'; + var alignCSS = alignRight ? "float:right;margin-left:" + paddingStr : ''; + valueList = isArray(valueList) ? valueList : [valueList]; + return "" + // Value has commas inside, so use ' ' as delimiter for multiple values. + + map(valueList, function (value) { + return encodeHTML(value); + }).join('  ') + ''; + } + function wrapInlineNameRichText(ctx, name, style) { + return ctx.markupStyleCreator.wrapRichTextStyle(name, style); + } + function wrapInlineValueRichText(ctx, values, alignRight, valueCloseToMarker, style) { + var styles = [style]; + var paddingLeft = valueCloseToMarker ? 10 : 20; + alignRight && styles.push({ + padding: [0, 0, 0, paddingLeft], + align: 'right' + }); + // Value has commas inside, so use ' ' as delimiter for multiple values. + return ctx.markupStyleCreator.wrapRichTextStyle(isArray(values) ? values.join(' ') : values, styles); + } + function retrieveVisualColorForTooltipMarker(series, dataIndex) { + var style = series.getData().getItemVisual(dataIndex, 'style'); + var color = style[series.visualDrawType]; + return convertToColorString(color); + } + function getPaddingFromTooltipModel(model, renderMode) { + var padding = model.get('padding'); + return padding != null ? padding + // We give slightly different to look pretty. + : renderMode === 'richText' ? [8, 10] : 10; + } + /** + * The major feature is generate styles for `renderMode: 'richText'`. + * But it also serves `renderMode: 'html'` to provide + * "renderMode-independent" API. + */ + var TooltipMarkupStyleCreator = /** @class */function () { + function TooltipMarkupStyleCreator() { + this.richTextStyles = {}; + // Notice that "generate a style name" usually happens repeatedly when mouse is moving and + // a tooltip is displayed. So we put the `_nextStyleNameId` as a member of each creator + // rather than static shared by all creators (which will cause it increase to fast). + this._nextStyleNameId = getRandomIdBase(); + } + TooltipMarkupStyleCreator.prototype._generateStyleName = function () { + return '__EC_aUTo_' + this._nextStyleNameId++; + }; + TooltipMarkupStyleCreator.prototype.makeTooltipMarker = function (markerType, colorStr, renderMode) { + var markerId = renderMode === 'richText' ? this._generateStyleName() : null; + var marker = getTooltipMarker({ + color: colorStr, + type: markerType, + renderMode: renderMode, + markerId: markerId + }); + if (isString(marker)) { + return marker; + } else { + if ("development" !== 'production') { + assert(markerId); + } + this.richTextStyles[markerId] = marker.style; + return marker.content; + } + }; + /** + * @usage + * ```ts + * const styledText = markupStyleCreator.wrapRichTextStyle([ + * // The styles will be auto merged. + * { + * fontSize: 12, + * color: 'blue' + * }, + * { + * padding: 20 + * } + * ]); + * ``` + */ + TooltipMarkupStyleCreator.prototype.wrapRichTextStyle = function (text, styles) { + var finalStl = {}; + if (isArray(styles)) { + each(styles, function (stl) { + return extend(finalStl, stl); + }); + } else { + extend(finalStl, styles); + } + var styleName = this._generateStyleName(); + this.richTextStyles[styleName] = finalStl; + return "{" + styleName + "|" + text + "}"; + }; + return TooltipMarkupStyleCreator; + }(); + + function defaultSeriesFormatTooltip(opt) { + var series = opt.series; + var dataIndex = opt.dataIndex; + var multipleSeries = opt.multipleSeries; + var data = series.getData(); + var tooltipDims = data.mapDimensionsAll('defaultedTooltip'); + var tooltipDimLen = tooltipDims.length; + var value = series.getRawValue(dataIndex); + var isValueArr = isArray(value); + var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); + // Complicated rule for pretty tooltip. + var inlineValue; + var inlineValueType; + var subBlocks; + var sortParam; + if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) { + var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor); + inlineValue = formatArrResult.inlineValues; + inlineValueType = formatArrResult.inlineValueTypes; + subBlocks = formatArrResult.blocks; + // Only support tooltip sort by the first inline value. It's enough in most cases. + sortParam = formatArrResult.inlineValues[0]; + } else if (tooltipDimLen) { + var dimInfo = data.getDimensionInfo(tooltipDims[0]); + sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]); + inlineValueType = dimInfo.type; + } else { + sortParam = inlineValue = isValueArr ? value[0] : value; + } + // Do not show generated series name. It might not be readable. + var seriesNameSpecified = isNameSpecified(series); + var seriesName = seriesNameSpecified && series.name || ''; + var itemName = data.getName(dataIndex); + var inlineName = multipleSeries ? seriesName : itemName; + return createTooltipMarkup('section', { + header: seriesName, + // When series name is not specified, do not show a header line with only '-'. + // This case always happens in tooltip.trigger: 'item'. + noHeader: multipleSeries || !seriesNameSpecified, + sortParam: sortParam, + blocks: [createTooltipMarkup('nameValue', { + markerType: 'item', + markerColor: markerColor, + // Do not mix display seriesName and itemName in one tooltip, + // which might confuses users. + name: inlineName, + // name dimension might be auto assigned, where the name might + // be not readable. So we check trim here. + noName: !trim(inlineName), + value: inlineValue, + valueType: inlineValueType, + dataIndex: dataIndex + })].concat(subBlocks || []) + }); + } + function formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) { + // check: category-no-encode-has-axis-data in dataset.html + var data = series.getData(); + var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) { + var dimItem = data.getDimensionInfo(idx); + return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null; + }, false); + var inlineValues = []; + var inlineValueTypes = []; + var blocks = []; + tooltipDims.length ? each(tooltipDims, function (dim) { + setEachItem(retrieveRawValue(data, dataIndex, dim), dim); + }) + // By default, all dims is used on tooltip. + : each(value, setEachItem); + function setEachItem(val, dim) { + var dimInfo = data.getDimensionInfo(dim); + // If `dimInfo.tooltip` is not set, show tooltip. + if (!dimInfo || dimInfo.otherDims.tooltip === false) { + return; + } + if (isValueMultipleLine) { + blocks.push(createTooltipMarkup('nameValue', { + markerType: 'subItem', + markerColor: colorStr, + name: dimInfo.displayName, + value: val, + valueType: dimInfo.type + })); + } else { + inlineValues.push(val); + inlineValueTypes.push(dimInfo.type); + } + } + return { + inlineValues: inlineValues, + inlineValueTypes: inlineValueTypes, + blocks: blocks + }; + } + + var inner$1 = makeInner(); + function getSelectionKey(data, dataIndex) { + return data.getName(dataIndex) || data.getId(dataIndex); + } + var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled'; + var SeriesModel = /** @class */function (_super) { + __extends(SeriesModel, _super); + function SeriesModel() { + // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`, + // the class members must not be initialized in constructor or declaration place. + // Otherwise there is bad case: + // class A {xxx = 1;} + // enableClassExtend(A); + // class B extends A {} + // var C = B.extend({xxx: 5}); + // var c = new C(); + // console.log(c.xxx); // expect 5 but always 1. + var _this = _super !== null && _super.apply(this, arguments) || this; + // --------------------------------------- + // Props about data selection + // --------------------------------------- + _this._selectedDataIndicesMap = {}; + return _this; + } + SeriesModel.prototype.init = function (option, parentModel, ecModel) { + this.seriesIndex = this.componentIndex; + this.dataTask = createTask({ + count: dataTaskCount, + reset: dataTaskReset + }); + this.dataTask.context = { + model: this + }; + this.mergeDefaultAndTheme(option, ecModel); + var sourceManager = inner$1(this).sourceManager = new SourceManager(this); + sourceManager.prepareSource(); + var data = this.getInitialData(option, ecModel); + wrapData(data, this); + this.dataTask.context.data = data; + if ("development" !== 'production') { + assert(data, 'getInitialData returned invalid data.'); + } + inner$1(this).dataBeforeProcessed = data; + // If we reverse the order (make data firstly, and then make + // dataBeforeProcessed by cloneShallow), cloneShallow will + // cause data.graph.data !== data when using + // module:echarts/data/Graph or module:echarts/data/Tree. + // See module:echarts/data/helper/linkSeriesData + // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model + // init or merge stage, because the data can be restored. So we do not `restoreData` + // and `setData` here, which forbids calling `seriesModel.getData()` in this stage. + // Call `seriesModel.getRawData()` instead. + // this.restoreData(); + autoSeriesName(this); + this._initSelectedMapFromData(data); + }; + /** + * Util for merge default and theme to option + */ + SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { + var layoutMode = fetchLayoutMode(this); + var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; + // Backward compat: using subType on theme. + // But if name duplicate between series subType + // (for example: parallel) add component mainType, + // add suffix 'Series'. + var themeSubType = this.subType; + if (ComponentModel.hasClass(themeSubType)) { + themeSubType += 'Series'; + } + merge(option, ecModel.getTheme().get(this.subType)); + merge(option, this.getDefaultOption()); + // Default label emphasis `show` + defaultEmphasis(option, 'label', ['show']); + this.fillDataTextStyle(option.data); + if (layoutMode) { + mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }; + SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) { + // this.settingTask.dirty(); + newSeriesOption = merge(this.option, newSeriesOption, true); + this.fillDataTextStyle(newSeriesOption.data); + var layoutMode = fetchLayoutMode(this); + if (layoutMode) { + mergeLayoutParam(this.option, newSeriesOption, layoutMode); + } + var sourceManager = inner$1(this).sourceManager; + sourceManager.dirty(); + sourceManager.prepareSource(); + var data = this.getInitialData(newSeriesOption, ecModel); + wrapData(data, this); + this.dataTask.dirty(); + this.dataTask.context.data = data; + inner$1(this).dataBeforeProcessed = data; + autoSeriesName(this); + this._initSelectedMapFromData(data); + }; + SeriesModel.prototype.fillDataTextStyle = function (data) { + // Default data label emphasis `show` + // FIXME Tree structure data ? + // FIXME Performance ? + if (data && !isTypedArray(data)) { + var props = ['show']; + for (var i = 0; i < data.length; i++) { + if (data[i] && data[i].label) { + defaultEmphasis(data[i], 'label', props); + } + } + } + }; + /** + * Init a data structure from data related option in series + * Must be overridden. + */ + SeriesModel.prototype.getInitialData = function (option, ecModel) { + return; + }; + /** + * Append data to list + */ + SeriesModel.prototype.appendData = function (params) { + // FIXME ??? + // (1) If data from dataset, forbidden append. + // (2) support append data of dataset. + var data = this.getRawData(); + data.appendData(params.data); + }; + /** + * Consider some method like `filter`, `map` need make new data, + * We should make sure that `seriesModel.getData()` get correct + * data in the stream procedure. So we fetch data from upstream + * each time `task.perform` called. + */ + SeriesModel.prototype.getData = function (dataType) { + var task = getCurrentTask(this); + if (task) { + var data = task.context.data; + return dataType == null ? data : data.getLinkedData(dataType); + } else { + // When series is not alive (that may happen when click toolbox + // restore or setOption with not merge mode), series data may + // be still need to judge animation or something when graphic + // elements want to know whether fade out. + return inner$1(this).data; + } + }; + SeriesModel.prototype.getAllData = function () { + var mainData = this.getData(); + return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{ + data: mainData + }]; + }; + SeriesModel.prototype.setData = function (data) { + var task = getCurrentTask(this); + if (task) { + var context = task.context; + // Consider case: filter, data sample. + // FIXME:TS never used, so comment it + // if (context.data !== data && task.modifyOutputEnd) { + // task.setOutputEnd(data.count()); + // } + context.outputData = data; + // Caution: setData should update context.data, + // Because getData may be called multiply in a + // single stage and expect to get the data just + // set. (For example, AxisProxy, x y both call + // getData and setDate sequentially). + // So the context.data should be fetched from + // upstream each time when a stage starts to be + // performed. + if (task !== this.dataTask) { + context.data = data; + } + } + inner$1(this).data = data; + }; + SeriesModel.prototype.getEncode = function () { + var encode = this.get('encode', true); + if (encode) { + return createHashMap(encode); + } + }; + SeriesModel.prototype.getSourceManager = function () { + return inner$1(this).sourceManager; + }; + SeriesModel.prototype.getSource = function () { + return this.getSourceManager().getSource(); + }; + /** + * Get data before processed + */ + SeriesModel.prototype.getRawData = function () { + return inner$1(this).dataBeforeProcessed; + }; + SeriesModel.prototype.getColorBy = function () { + var colorBy = this.get('colorBy'); + return colorBy || 'series'; + }; + SeriesModel.prototype.isColorBySeries = function () { + return this.getColorBy() === 'series'; + }; + /** + * Get base axis if has coordinate system and has axis. + * By default use coordSys.getBaseAxis(); + * Can be overridden for some chart. + * @return {type} description + */ + SeriesModel.prototype.getBaseAxis = function () { + var coordSys = this.coordinateSystem; + // @ts-ignore + return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); + }; + /** + * Default tooltip formatter + * + * @param dataIndex + * @param multipleSeries + * @param dataType + * @param renderMode valid values: 'html'(by default) and 'richText'. + * 'html' is used for rendering tooltip in extra DOM form, and the result + * string is used as DOM HTML content. + * 'richText' is used for rendering tooltip in rich text form, for those where + * DOM operation is not supported. + * @return formatted tooltip with `html` and `markers` + * Notice: The override method can also return string + */ + SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + return defaultSeriesFormatTooltip({ + series: this, + dataIndex: dataIndex, + multipleSeries: multipleSeries + }); + }; + SeriesModel.prototype.isAnimationEnabled = function () { + var ecModel = this.ecModel; + // Disable animation if using echarts in node but not give ssr flag. + // In ssr mode, renderToString will generate svg with css animation. + if (env.node && !(ecModel && ecModel.ssr)) { + return false; + } + var animationEnabled = this.getShallow('animation'); + if (animationEnabled) { + if (this.getData().count() > this.getShallow('animationThreshold')) { + animationEnabled = false; + } + } + return !!animationEnabled; + }; + SeriesModel.prototype.restoreData = function () { + this.dataTask.dirty(); + }; + SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) { + var ecModel = this.ecModel; + // PENDING + var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum); + if (!color) { + color = ecModel.getColorFromPalette(name, scope, requestColorNum); + } + return color; + }; + /** + * Use `data.mapDimensionsAll(coordDim)` instead. + * @deprecated + */ + SeriesModel.prototype.coordDimToDataDim = function (coordDim) { + return this.getRawData().mapDimensionsAll(coordDim); + }; + /** + * Get progressive rendering count each step + */ + SeriesModel.prototype.getProgressive = function () { + return this.get('progressive'); + }; + /** + * Get progressive rendering count each step + */ + SeriesModel.prototype.getProgressiveThreshold = function () { + return this.get('progressiveThreshold'); + }; + // PENGING If selectedMode is null ? + SeriesModel.prototype.select = function (innerDataIndices, dataType) { + this._innerSelect(this.getData(dataType), innerDataIndices); + }; + SeriesModel.prototype.unselect = function (innerDataIndices, dataType) { + var selectedMap = this.option.selectedMap; + if (!selectedMap) { + return; + } + var selectedMode = this.option.selectedMode; + var data = this.getData(dataType); + if (selectedMode === 'series' || selectedMap === 'all') { + this.option.selectedMap = {}; + this._selectedDataIndicesMap = {}; + return; + } + for (var i = 0; i < innerDataIndices.length; i++) { + var dataIndex = innerDataIndices[i]; + var nameOrId = getSelectionKey(data, dataIndex); + selectedMap[nameOrId] = false; + this._selectedDataIndicesMap[nameOrId] = -1; + } + }; + SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) { + var tmpArr = []; + for (var i = 0; i < innerDataIndices.length; i++) { + tmpArr[0] = innerDataIndices[i]; + this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType); + } + }; + SeriesModel.prototype.getSelectedDataIndices = function () { + if (this.option.selectedMap === 'all') { + return [].slice.call(this.getData().getIndices()); + } + var selectedDataIndicesMap = this._selectedDataIndicesMap; + var nameOrIds = keys(selectedDataIndicesMap); + var dataIndices = []; + for (var i = 0; i < nameOrIds.length; i++) { + var dataIndex = selectedDataIndicesMap[nameOrIds[i]]; + if (dataIndex >= 0) { + dataIndices.push(dataIndex); + } + } + return dataIndices; + }; + SeriesModel.prototype.isSelected = function (dataIndex, dataType) { + var selectedMap = this.option.selectedMap; + if (!selectedMap) { + return false; + } + var data = this.getData(dataType); + return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']); + }; + SeriesModel.prototype.isUniversalTransitionEnabled = function () { + if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) { + return true; + } + var universalTransitionOpt = this.option.universalTransition; + // Quick reject + if (!universalTransitionOpt) { + return false; + } + if (universalTransitionOpt === true) { + return true; + } + // Can be simply 'universalTransition: true' + return universalTransitionOpt && universalTransitionOpt.enabled; + }; + SeriesModel.prototype._innerSelect = function (data, innerDataIndices) { + var _a, _b; + var option = this.option; + var selectedMode = option.selectedMode; + var len = innerDataIndices.length; + if (!selectedMode || !len) { + return; + } + if (selectedMode === 'series') { + option.selectedMap = 'all'; + } else if (selectedMode === 'multiple') { + if (!isObject(option.selectedMap)) { + option.selectedMap = {}; + } + var selectedMap = option.selectedMap; + for (var i = 0; i < len; i++) { + var dataIndex = innerDataIndices[i]; + // TODO different types of data share same object. + var nameOrId = getSelectionKey(data, dataIndex); + selectedMap[nameOrId] = true; + this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex); + } + } else if (selectedMode === 'single' || selectedMode === true) { + var lastDataIndex = innerDataIndices[len - 1]; + var nameOrId = getSelectionKey(data, lastDataIndex); + option.selectedMap = (_a = {}, _a[nameOrId] = true, _a); + this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b); + } + }; + SeriesModel.prototype._initSelectedMapFromData = function (data) { + // Ignore select info in data if selectedMap exists. + // NOTE It's only for legacy usage. edge data is not supported. + if (this.option.selectedMap) { + return; + } + var dataIndices = []; + if (data.hasItemOption) { + data.each(function (idx) { + var rawItem = data.getRawDataItem(idx); + if (rawItem && rawItem.selected) { + dataIndices.push(idx); + } + }); + } + if (dataIndices.length > 0) { + this._innerSelect(data, dataIndices); + } + }; + // /** + // * @see {module:echarts/stream/Scheduler} + // */ + // abstract pipeTask: null + SeriesModel.registerClass = function (clz) { + return ComponentModel.registerClass(clz); + }; + SeriesModel.protoInitialize = function () { + var proto = SeriesModel.prototype; + proto.type = 'series.__base__'; + proto.seriesIndex = 0; + proto.ignoreStyleOnData = false; + proto.hasSymbolVisual = false; + proto.defaultSymbol = 'circle'; + // Make sure the values can be accessed! + proto.visualStyleAccessPath = 'itemStyle'; + proto.visualDrawType = 'fill'; + }(); + return SeriesModel; + }(ComponentModel); + mixin(SeriesModel, DataFormatMixin); + mixin(SeriesModel, PaletteMixin); + mountExtend(SeriesModel, ComponentModel); + /** + * MUST be called after `prepareSource` called + * Here we need to make auto series, especially for auto legend. But we + * do not modify series.name in option to avoid side effects. + */ + function autoSeriesName(seriesModel) { + // User specified name has higher priority, otherwise it may cause + // series can not be queried unexpectedly. + var name = seriesModel.name; + if (!isNameSpecified(seriesModel)) { + seriesModel.name = getSeriesAutoName(seriesModel) || name; + } + } + function getSeriesAutoName(seriesModel) { + var data = seriesModel.getRawData(); + var dataDims = data.mapDimensionsAll('seriesName'); + var nameArr = []; + each(dataDims, function (dataDim) { + var dimInfo = data.getDimensionInfo(dataDim); + dimInfo.displayName && nameArr.push(dimInfo.displayName); + }); + return nameArr.join(' '); + } + function dataTaskCount(context) { + return context.model.getRawData().count(); + } + function dataTaskReset(context) { + var seriesModel = context.model; + seriesModel.setData(seriesModel.getRawData().cloneShallow()); + return dataTaskProgress; + } + function dataTaskProgress(param, context) { + // Avoid repeat cloneShallow when data just created in reset. + if (context.outputData && param.end > context.outputData.count()) { + context.model.getRawData().cloneShallow(context.outputData); + } + } + // TODO refactor + function wrapData(data, seriesModel) { + each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) { + data.wrapMethod(methodName, curry(onDataChange, seriesModel)); + }); + } + function onDataChange(seriesModel, newList) { + var task = getCurrentTask(seriesModel); + if (task) { + // Consider case: filter, selectRange + task.setOutputEnd((newList || this).count()); + } + return newList; + } + function getCurrentTask(seriesModel) { + var scheduler = (seriesModel.ecModel || {}).scheduler; + var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid); + if (pipeline) { + // When pipline finished, the currrentTask keep the last + // task (renderTask). + var task = pipeline.currentTask; + if (task) { + var agentStubMap = task.agentStubMap; + if (agentStubMap) { + task = agentStubMap.get(seriesModel.uid); + } + } + return task; + } + } + + var ComponentView = /** @class */function () { + function ComponentView() { + this.group = new Group(); + this.uid = getUID('viewComponent'); + } + ComponentView.prototype.init = function (ecModel, api) {}; + ComponentView.prototype.render = function (model, ecModel, api, payload) {}; + ComponentView.prototype.dispose = function (ecModel, api) {}; + ComponentView.prototype.updateView = function (model, ecModel, api, payload) { + // Do nothing; + }; + ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) { + // Do nothing; + }; + ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) { + // Do nothing; + }; + /** + * Hook for toggle blur target series. + * Can be used in marker for blur or leave blur the markers + */ + ComponentView.prototype.toggleBlurSeries = function (seriesModels, isBlur, ecModel) { + // Do nothing; + }; + /** + * Traverse the new rendered elements. + * + * It will traverse the new added element in progressive rendering. + * And traverse all in normal rendering. + */ + ComponentView.prototype.eachRendered = function (cb) { + var group = this.group; + if (group) { + group.traverse(cb); + } + }; + return ComponentView; + }(); + enableClassExtend(ComponentView); + enableClassManagement(ComponentView); + + /** + * @return {string} If large mode changed, return string 'reset'; + */ + function createRenderPlanner() { + var inner = makeInner(); + return function (seriesModel) { + var fields = inner(seriesModel); + var pipelineContext = seriesModel.pipelineContext; + var originalLarge = !!fields.large; + var originalProgressive = !!fields.progressiveRender; + // FIXME: if the planner works on a filtered series, `pipelineContext` does not + // exists. See #11611 . Probably we need to modify this structure, see the comment + // on `performRawSeries` in `Schedular.js`. + var large = fields.large = !!(pipelineContext && pipelineContext.large); + var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender); + return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset'; + }; + } + + var inner$2 = makeInner(); + var renderPlanner = createRenderPlanner(); + var ChartView = /** @class */function () { + function ChartView() { + this.group = new Group(); + this.uid = getUID('viewChart'); + this.renderTask = createTask({ + plan: renderTaskPlan, + reset: renderTaskReset + }); + this.renderTask.context = { + view: this + }; + } + ChartView.prototype.init = function (ecModel, api) {}; + ChartView.prototype.render = function (seriesModel, ecModel, api, payload) { + if ("development" !== 'production') { + throw new Error('render method must been implemented'); + } + }; + /** + * Highlight series or specified data item. + */ + ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(payload && payload.dataType); + if (!data) { + if ("development" !== 'production') { + error("Unknown dataType " + payload.dataType); + } + return; + } + toggleHighlight(data, payload, 'emphasis'); + }; + /** + * Downplay series or specified data item. + */ + ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(payload && payload.dataType); + if (!data) { + if ("development" !== 'production') { + error("Unknown dataType " + payload.dataType); + } + return; + } + toggleHighlight(data, payload, 'normal'); + }; + /** + * Remove self. + */ + ChartView.prototype.remove = function (ecModel, api) { + this.group.removeAll(); + }; + /** + * Dispose self. + */ + ChartView.prototype.dispose = function (ecModel, api) {}; + ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) { + this.render(seriesModel, ecModel, api, payload); + }; + // FIXME never used? + ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) { + this.render(seriesModel, ecModel, api, payload); + }; + // FIXME never used? + ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) { + this.render(seriesModel, ecModel, api, payload); + }; + /** + * Traverse the new rendered elements. + * + * It will traverse the new added element in progressive rendering. + * And traverse all in normal rendering. + */ + ChartView.prototype.eachRendered = function (cb) { + traverseElements(this.group, cb); + }; + ChartView.markUpdateMethod = function (payload, methodName) { + inner$2(payload).updateMethod = methodName; + }; + ChartView.protoInitialize = function () { + var proto = ChartView.prototype; + proto.type = 'chart'; + }(); + return ChartView; + }(); + /** + * Set state of single element + */ + function elSetState(el, state, highlightDigit) { + if (el && isHighDownDispatcher(el)) { + (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit); + } + } + function toggleHighlight(data, payload, state) { + var dataIndex = queryDataIndex(data, payload); + var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null; + if (dataIndex != null) { + each(normalizeToArray(dataIndex), function (dataIdx) { + elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit); + }); + } else { + data.eachItemGraphicEl(function (el) { + elSetState(el, state, highlightDigit); + }); + } + } + enableClassExtend(ChartView, ['dispose']); + enableClassManagement(ChartView); + function renderTaskPlan(context) { + return renderPlanner(context.model); + } + function renderTaskReset(context) { + var seriesModel = context.model; + var ecModel = context.ecModel; + var api = context.api; + var payload = context.payload; + // FIXME: remove updateView updateVisual + var progressiveRender = seriesModel.pipelineContext.progressiveRender; + var view = context.view; + var updateMethod = payload && inner$2(payload).updateMethod; + var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod + // `appendData` is also supported when data amount + // is less than progressive threshold. + : 'render'; + if (methodName !== 'render') { + view[methodName](seriesModel, ecModel, api, payload); + } + return progressMethodMap[methodName]; + } + var progressMethodMap = { + incrementalPrepareRender: { + progress: function (params, context) { + context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload); + } + }, + render: { + // Put view.render in `progress` to support appendData. But in this case + // view.render should not be called in reset, otherwise it will be called + // twise. Use `forceFirstProgress` to make sure that view.render is called + // in any cases. + forceFirstProgress: true, + progress: function (params, context) { + context.view.render(context.model, context.ecModel, context.api, context.payload); + } + } + }; + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var ORIGIN_METHOD = '\0__throttleOriginMethod'; + var RATE = '\0__throttleRate'; + var THROTTLE_TYPE = '\0__throttleType'; + /** + * @public + * @param {(Function)} fn + * @param {number} [delay=0] Unit: ms. + * @param {boolean} [debounce=false] + * true: If call interval less than `delay`, only the last call works. + * false: If call interval less than `delay, call works on fixed rate. + * @return {(Function)} throttled fn. + */ + function throttle(fn, delay, debounce) { + var currCall; + var lastCall = 0; + var lastExec = 0; + var timer = null; + var diff; + var scope; + var args; + var debounceNextCall; + delay = delay || 0; + function exec() { + lastExec = new Date().getTime(); + timer = null; + fn.apply(scope, args || []); + } + var cb = function () { + var cbArgs = []; + for (var _i = 0; _i < arguments.length; _i++) { + cbArgs[_i] = arguments[_i]; + } + currCall = new Date().getTime(); + scope = this; + args = cbArgs; + var thisDelay = debounceNextCall || delay; + var thisDebounce = debounceNextCall || debounce; + debounceNextCall = null; + diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay; + clearTimeout(timer); + // Here we should make sure that: the `exec` SHOULD NOT be called later + // than a new call of `cb`, that is, preserving the command order. Consider + // calculating "scale rate" when roaming as an example. When a call of `cb` + // happens, either the `exec` is called dierectly, or the call is delayed. + // But the delayed call should never be later than next call of `cb`. Under + // this assurance, we can simply update view state each time `dispatchAction` + // triggered by user roaming, but not need to add extra code to avoid the + // state being "rolled-back". + if (thisDebounce) { + timer = setTimeout(exec, thisDelay); + } else { + if (diff >= 0) { + exec(); + } else { + timer = setTimeout(exec, -diff); + } + } + lastCall = currCall; + }; + /** + * Clear throttle. + * @public + */ + cb.clear = function () { + if (timer) { + clearTimeout(timer); + timer = null; + } + }; + /** + * Enable debounce once. + */ + cb.debounceNextCall = function (debounceDelay) { + debounceNextCall = debounceDelay; + }; + return cb; + } + /** + * Create throttle method or update throttle rate. + * + * @example + * ComponentView.prototype.render = function () { + * ... + * throttle.createOrUpdate( + * this, + * '_dispatchAction', + * this.model.get('throttle'), + * 'fixRate' + * ); + * }; + * ComponentView.prototype.remove = function () { + * throttle.clear(this, '_dispatchAction'); + * }; + * ComponentView.prototype.dispose = function () { + * throttle.clear(this, '_dispatchAction'); + * }; + * + */ + function createOrUpdate(obj, fnAttr, rate, throttleType) { + var fn = obj[fnAttr]; + if (!fn) { + return; + } + var originFn = fn[ORIGIN_METHOD] || fn; + var lastThrottleType = fn[THROTTLE_TYPE]; + var lastRate = fn[RATE]; + if (lastRate !== rate || lastThrottleType !== throttleType) { + if (rate == null || !throttleType) { + return obj[fnAttr] = originFn; + } + fn = obj[fnAttr] = throttle(originFn, rate, throttleType === 'debounce'); + fn[ORIGIN_METHOD] = originFn; + fn[THROTTLE_TYPE] = throttleType; + fn[RATE] = rate; + } + return fn; + } + /** + * Clear throttle. Example see throttle.createOrUpdate. + */ + function clear(obj, fnAttr) { + var fn = obj[fnAttr]; + if (fn && fn[ORIGIN_METHOD]) { + // Clear throttle + fn.clear && fn.clear(); + obj[fnAttr] = fn[ORIGIN_METHOD]; + } + } + + var inner$3 = makeInner(); + var defaultStyleMappers = { + itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true), + lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true) + }; + var defaultColorKey = { + lineStyle: 'stroke', + itemStyle: 'fill' + }; + function getStyleMapper(seriesModel, stylePath) { + var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath]; + if (!styleMapper) { + console.warn("Unknown style type '" + stylePath + "'."); + return defaultStyleMappers.itemStyle; + } + return styleMapper; + } + function getDefaultColorKey(seriesModel, stylePath) { + // return defaultColorKey[stylePath] || + var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath]; + if (!colorKey) { + console.warn("Unknown style type '" + stylePath + "'."); + return 'fill'; + } + return colorKey; + } + var seriesStyleTask = { + createOnAllSeries: true, + performRawSeries: true, + reset: function (seriesModel, ecModel) { + var data = seriesModel.getData(); + var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; + // Set in itemStyle + var styleModel = seriesModel.getModel(stylePath); + var getStyle = getStyleMapper(seriesModel, stylePath); + var globalStyle = getStyle(styleModel); + var decalOption = styleModel.getShallow('decal'); + if (decalOption) { + data.setVisual('decal', decalOption); + decalOption.dirty = true; + } + // TODO + var colorKey = getDefaultColorKey(seriesModel, stylePath); + var color = globalStyle[colorKey]; + // TODO style callback + var colorCallback = isFunction(color) ? color : null; + var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; + // Get from color palette by default. + if (!globalStyle[colorKey] || colorCallback || hasAutoColor) { + // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT + // make it effect palette. Because some scenarios users need to make some series + // transparent or as background, which should better not effect the palette. + var colorPalette = seriesModel.getColorFromPalette( + // TODO series count changed. + seriesModel.name, null, ecModel.getSeriesCount()); + if (!globalStyle[colorKey]) { + globalStyle[colorKey] = colorPalette; + data.setVisual('colorFromPalette', true); + } + globalStyle.fill = globalStyle.fill === 'auto' || isFunction(globalStyle.fill) ? colorPalette : globalStyle.fill; + globalStyle.stroke = globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke) ? colorPalette : globalStyle.stroke; + } + data.setVisual('style', globalStyle); + data.setVisual('drawType', colorKey); + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) { + data.setVisual('colorFromPalette', false); + return { + dataEach: function (data, idx) { + var dataParams = seriesModel.getDataParams(idx); + var itemStyle = extend({}, globalStyle); + itemStyle[colorKey] = colorCallback(dataParams); + data.setItemVisual(idx, 'style', itemStyle); + } + }; + } + } + }; + var sharedModel = new Model(); + var dataStyleTask = { + createOnAllSeries: true, + performRawSeries: true, + reset: function (seriesModel, ecModel) { + if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) { + return; + } + var data = seriesModel.getData(); + var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; + // Set in itemStyle + var getStyle = getStyleMapper(seriesModel, stylePath); + var colorKey = data.getVisual('drawType'); + return { + dataEach: data.hasItemOption ? function (data, idx) { + // Not use getItemModel for performance considuration + var rawItem = data.getRawDataItem(idx); + if (rawItem && rawItem[stylePath]) { + sharedModel.option = rawItem[stylePath]; + var style = getStyle(sharedModel); + var existsStyle = data.ensureUniqueItemVisual(idx, 'style'); + extend(existsStyle, style); + if (sharedModel.option.decal) { + data.setItemVisual(idx, 'decal', sharedModel.option.decal); + sharedModel.option.decal.dirty = true; + } + if (colorKey in style) { + data.setItemVisual(idx, 'colorFromPalette', false); + } + } + } : null + }; + } + }; + // Pick color from palette for the data which has not been set with color yet. + // Note: do not support stream rendering. No such cases yet. + var dataColorPaletteTask = { + performRawSeries: true, + overallReset: function (ecModel) { + // Each type of series uses one scope. + // Pie and funnel are using different scopes. + var paletteScopeGroupByType = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + var colorBy = seriesModel.getColorBy(); + if (seriesModel.isColorBySeries()) { + return; + } + var key = seriesModel.type + '-' + colorBy; + var colorScope = paletteScopeGroupByType.get(key); + if (!colorScope) { + colorScope = {}; + paletteScopeGroupByType.set(key, colorScope); + } + inner$3(seriesModel).scope = colorScope; + }); + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) { + return; + } + var dataAll = seriesModel.getRawData(); + var idxMap = {}; + var data = seriesModel.getData(); + var colorScope = inner$3(seriesModel).scope; + var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; + var colorKey = getDefaultColorKey(seriesModel, stylePath); + data.each(function (idx) { + var rawIdx = data.getRawIndex(idx); + idxMap[rawIdx] = idx; + }); + // Iterate on data before filtered. To make sure color from palette can be + // Consistent when toggling legend. + dataAll.each(function (rawIdx) { + var idx = idxMap[rawIdx]; + var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); + // Get color from palette for each data only when the color is inherited from series color, which is + // also picked from color palette. So following situation is not in the case: + // 1. series.itemStyle.color is set + // 2. color is encoded by visualMap + if (fromPalette) { + var itemStyle = data.ensureUniqueItemVisual(idx, 'style'); + var name_1 = dataAll.getName(rawIdx) || rawIdx + ''; + var dataCount = dataAll.count(); + itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount); + } + }); + }); + } + }; + + var PI$3 = Math.PI; + /** + * @param {module:echarts/ExtensionAPI} api + * @param {Object} [opts] + * @param {string} [opts.text] + * @param {string} [opts.color] + * @param {string} [opts.textColor] + * @return {module:zrender/Element} + */ + function defaultLoading(api, opts) { + opts = opts || {}; + defaults(opts, { + text: 'loading', + textColor: '#000', + fontSize: 12, + fontWeight: 'normal', + fontStyle: 'normal', + fontFamily: 'sans-serif', + maskColor: 'rgba(255, 255, 255, 0.8)', + showSpinner: true, + color: '#5470c6', + spinnerRadius: 10, + lineWidth: 5, + zlevel: 0 + }); + var group = new Group(); + var mask = new Rect({ + style: { + fill: opts.maskColor + }, + zlevel: opts.zlevel, + z: 10000 + }); + group.add(mask); + var textContent = new ZRText({ + style: { + text: opts.text, + fill: opts.textColor, + fontSize: opts.fontSize, + fontWeight: opts.fontWeight, + fontStyle: opts.fontStyle, + fontFamily: opts.fontFamily + }, + zlevel: opts.zlevel, + z: 10001 + }); + var labelRect = new Rect({ + style: { + fill: 'none' + }, + textContent: textContent, + textConfig: { + position: 'right', + distance: 10 + }, + zlevel: opts.zlevel, + z: 10001 + }); + group.add(labelRect); + var arc; + if (opts.showSpinner) { + arc = new Arc({ + shape: { + startAngle: -PI$3 / 2, + endAngle: -PI$3 / 2 + 0.1, + r: opts.spinnerRadius + }, + style: { + stroke: opts.color, + lineCap: 'round', + lineWidth: opts.lineWidth + }, + zlevel: opts.zlevel, + z: 10001 + }); + arc.animateShape(true).when(1000, { + endAngle: PI$3 * 3 / 2 + }).start('circularInOut'); + arc.animateShape(true).when(1000, { + startAngle: PI$3 * 3 / 2 + }).delay(300).start('circularInOut'); + group.add(arc); + } + // Inject resize + group.resize = function () { + var textWidth = textContent.getBoundingRect().width; + var r = opts.showSpinner ? opts.spinnerRadius : 0; + // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2 + // textDistance needs to be calculated when both animation and text exist + var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) + // only show the text + + (opts.showSpinner ? 0 : textWidth / 2) + // only show the spinner + + (textWidth ? 0 : r); + var cy = api.getHeight() / 2; + opts.showSpinner && arc.setShape({ + cx: cx, + cy: cy + }); + labelRect.setShape({ + x: cx - r, + y: cy - r, + width: r * 2, + height: r * 2 + }); + mask.setShape({ + x: 0, + y: 0, + width: api.getWidth(), + height: api.getHeight() + }); + }; + group.resize(); + return group; + } + + var Scheduler = /** @class */function () { + function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) { + // key: handlerUID + this._stageTaskMap = createHashMap(); + this.ecInstance = ecInstance; + this.api = api; + // Fix current processors in case that in some rear cases that + // processors might be registered after echarts instance created. + // Register processors incrementally for a echarts instance is + // not supported by this stream architecture. + dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice(); + visualHandlers = this._visualHandlers = visualHandlers.slice(); + this._allHandlers = dataProcessorHandlers.concat(visualHandlers); + } + Scheduler.prototype.restoreData = function (ecModel, payload) { + // TODO: Only restore needed series and components, but not all components. + // Currently `restoreData` of all of the series and component will be called. + // But some independent components like `title`, `legend`, `graphic`, `toolbox`, + // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`, + // and some components like coordinate system, axes, dataZoom, visualMap only + // need their target series refresh. + // (1) If we are implementing this feature some day, we should consider these cases: + // if a data processor depends on a component (e.g., dataZoomProcessor depends + // on the settings of `dataZoom`), it should be re-performed if the component + // is modified by `setOption`. + // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`, + // it should be re-performed when the result array of `getTargetSeries` changed. + // We use `dependencies` to cover these issues. + // (3) How to update target series when coordinate system related components modified. + // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty, + // and this case all of the tasks will be set as dirty. + ecModel.restoreData(payload); + // Theoretically an overall task not only depends on each of its target series, but also + // depends on all of the series. + // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks + // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure + // that the overall task is set as dirty and to be performed, otherwise it probably cause + // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it + // probably cause state chaos (consider `dataZoomProcessor`). + this._stageTaskMap.each(function (taskRecord) { + var overallTask = taskRecord.overallTask; + overallTask && overallTask.dirty(); + }); + }; + // If seriesModel provided, incremental threshold is check by series data. + Scheduler.prototype.getPerformArgs = function (task, isBlock) { + // For overall task + if (!task.__pipeline) { + return; + } + var pipeline = this._pipelineMap.get(task.__pipeline.id); + var pCtx = pipeline.context; + var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex; + var step = incremental ? pipeline.step : null; + var modDataCount = pCtx && pCtx.modDataCount; + var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null; + return { + step: step, + modBy: modBy, + modDataCount: modDataCount + }; + }; + Scheduler.prototype.getPipeline = function (pipelineId) { + return this._pipelineMap.get(pipelineId); + }; + /** + * Current, progressive rendering starts from visual and layout. + * Always detect render mode in the same stage, avoiding that incorrect + * detection caused by data filtering. + * Caution: + * `updateStreamModes` use `seriesModel.getData()`. + */ + Scheduler.prototype.updateStreamModes = function (seriesModel, view) { + var pipeline = this._pipelineMap.get(seriesModel.uid); + var data = seriesModel.getData(); + var dataLen = data.count(); + // `progressiveRender` means that can render progressively in each + // animation frame. Note that some types of series do not provide + // `view.incrementalPrepareRender` but support `chart.appendData`. We + // use the term `incremental` but not `progressive` to describe the + // case that `chart.appendData`. + var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold; + var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); + // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint. + // see `test/candlestick-large3.html` + var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null; + seriesModel.pipelineContext = pipeline.context = { + progressiveRender: progressiveRender, + modDataCount: modDataCount, + large: large + }; + }; + Scheduler.prototype.restorePipelines = function (ecModel) { + var scheduler = this; + var pipelineMap = scheduler._pipelineMap = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + var progressive = seriesModel.getProgressive(); + var pipelineId = seriesModel.uid; + pipelineMap.set(pipelineId, { + id: pipelineId, + head: null, + tail: null, + threshold: seriesModel.getProgressiveThreshold(), + progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()), + blockIndex: -1, + step: Math.round(progressive || 700), + count: 0 + }); + scheduler._pipe(seriesModel, seriesModel.dataTask); + }); + }; + Scheduler.prototype.prepareStageTasks = function () { + var stageTaskMap = this._stageTaskMap; + var ecModel = this.api.getModel(); + var api = this.api; + each(this._allHandlers, function (handler) { + var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {}); + var errMsg = ''; + if ("development" !== 'production') { + // Currently do not need to support to sepecify them both. + errMsg = '"reset" and "overallReset" must not be both specified.'; + } + assert(!(handler.reset && handler.overallReset), errMsg); + handler.reset && this._createSeriesStageTask(handler, record, ecModel, api); + handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api); + }, this); + }; + Scheduler.prototype.prepareView = function (view, model, ecModel, api) { + var renderTask = view.renderTask; + var context = renderTask.context; + context.model = model; + context.ecModel = ecModel; + context.api = api; + renderTask.__block = !view.incrementalPrepareRender; + this._pipe(model, renderTask); + }; + Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) { + // If we do not use `block` here, it should be considered when to update modes. + this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, { + block: true + }); + }; + Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) { + this._performStageTasks(this._visualHandlers, ecModel, payload, opt); + }; + Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) { + opt = opt || {}; + var unfinished = false; + var scheduler = this; + each(stageHandlers, function (stageHandler, idx) { + if (opt.visualType && opt.visualType !== stageHandler.visualType) { + return; + } + var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid); + var seriesTaskMap = stageHandlerRecord.seriesTaskMap; + var overallTask = stageHandlerRecord.overallTask; + if (overallTask) { + var overallNeedDirty_1; + var agentStubMap = overallTask.agentStubMap; + agentStubMap.each(function (stub) { + if (needSetDirty(opt, stub)) { + stub.dirty(); + overallNeedDirty_1 = true; + } + }); + overallNeedDirty_1 && overallTask.dirty(); + scheduler.updatePayload(overallTask, payload); + var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); + // Execute stubs firstly, which may set the overall task dirty, + // then execute the overall task. And stub will call seriesModel.setData, + // which ensures that in the overallTask seriesModel.getData() will not + // return incorrect data. + agentStubMap.each(function (stub) { + stub.perform(performArgs_1); + }); + if (overallTask.perform(performArgs_1)) { + unfinished = true; + } + } else if (seriesTaskMap) { + seriesTaskMap.each(function (task, pipelineId) { + if (needSetDirty(opt, task)) { + task.dirty(); + } + var performArgs = scheduler.getPerformArgs(task, opt.block); + // FIXME + // if intending to declare `performRawSeries` in handlers, only + // stream-independent (specifically, data item independent) operations can be + // performed. Because if a series is filtered, most of the tasks will not + // be performed. A stream-dependent operation probably cause wrong biz logic. + // Perhaps we should not provide a separate callback for this case instead + // of providing the config `performRawSeries`. The stream-dependent operations + // and stream-independent operations should better not be mixed. + performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model); + scheduler.updatePayload(task, payload); + if (task.perform(performArgs)) { + unfinished = true; + } + }); + } + }); + function needSetDirty(opt, task) { + return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id)); + } + this.unfinished = unfinished || this.unfinished; + }; + Scheduler.prototype.performSeriesTasks = function (ecModel) { + var unfinished; + ecModel.eachSeries(function (seriesModel) { + // Progress to the end for dataInit and dataRestore. + unfinished = seriesModel.dataTask.perform() || unfinished; + }); + this.unfinished = unfinished || this.unfinished; + }; + Scheduler.prototype.plan = function () { + // Travel pipelines, check block. + this._pipelineMap.each(function (pipeline) { + var task = pipeline.tail; + do { + if (task.__block) { + pipeline.blockIndex = task.__idxInPipeline; + break; + } + task = task.getUpstream(); + } while (task); + }); + }; + Scheduler.prototype.updatePayload = function (task, payload) { + payload !== 'remain' && (task.context.payload = payload); + }; + Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) { + var scheduler = this; + var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; + // The count of stages are totally about only several dozen, so + // do not need to reuse the map. + var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap(); + var seriesType = stageHandler.seriesType; + var getTargetSeries = stageHandler.getTargetSeries; + // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily, + // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`, + // it works but it may cause other irrelevant charts blocked. + if (stageHandler.createOnAllSeries) { + ecModel.eachRawSeries(create); + } else if (seriesType) { + ecModel.eachRawSeriesByType(seriesType, create); + } else if (getTargetSeries) { + getTargetSeries(ecModel, api).each(create); + } + function create(seriesModel) { + var pipelineId = seriesModel.uid; + // Init tasks for each seriesModel only once. + // Reuse original task instance. + var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({ + plan: seriesTaskPlan, + reset: seriesTaskReset, + count: seriesTaskCount + })); + task.context = { + model: seriesModel, + ecModel: ecModel, + api: api, + // PENDING: `useClearVisual` not used? + useClearVisual: stageHandler.isVisual && !stageHandler.isLayout, + plan: stageHandler.plan, + reset: stageHandler.reset, + scheduler: scheduler + }; + scheduler._pipe(seriesModel, task); + } + }; + Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) { + var scheduler = this; + var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask + // For overall task, the function only be called on reset stage. + || createTask({ + reset: overallTaskReset + }); + overallTask.context = { + ecModel: ecModel, + api: api, + overallReset: stageHandler.overallReset, + scheduler: scheduler + }; + var oldAgentStubMap = overallTask.agentStubMap; + // The count of stages are totally about only several dozen, so + // do not need to reuse the map. + var newAgentStubMap = overallTask.agentStubMap = createHashMap(); + var seriesType = stageHandler.seriesType; + var getTargetSeries = stageHandler.getTargetSeries; + var overallProgress = true; + var shouldOverallTaskDirty = false; + // FIXME:TS never used, so comment it + // let modifyOutputEnd = stageHandler.modifyOutputEnd; + // An overall task with seriesType detected or has `getTargetSeries`, we add + // stub in each pipelines, it will set the overall task dirty when the pipeline + // progress. Moreover, to avoid call the overall task each frame (too frequent), + // we set the pipeline block. + var errMsg = ''; + if ("development" !== 'production') { + errMsg = '"createOnAllSeries" is not supported for "overallReset", ' + 'because it will block all streams.'; + } + assert(!stageHandler.createOnAllSeries, errMsg); + if (seriesType) { + ecModel.eachRawSeriesByType(seriesType, createStub); + } else if (getTargetSeries) { + getTargetSeries(ecModel, api).each(createStub); + } + // Otherwise, (usually it is legacy case), the overall task will only be + // executed when upstream is dirty. Otherwise the progressive rendering of all + // pipelines will be disabled unexpectedly. But it still needs stubs to receive + // dirty info from upstream. + else { + overallProgress = false; + each(ecModel.getSeries(), createStub); + } + function createStub(seriesModel) { + var pipelineId = seriesModel.uid; + var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( + // When the result of `getTargetSeries` changed, the overallTask + // should be set as dirty and re-performed. + shouldOverallTaskDirty = true, createTask({ + reset: stubReset, + onDirty: stubOnDirty + }))); + stub.context = { + model: seriesModel, + overallProgress: overallProgress + // FIXME:TS never used, so comment it + // modifyOutputEnd: modifyOutputEnd + }; + + stub.agent = overallTask; + stub.__block = overallProgress; + scheduler._pipe(seriesModel, stub); + } + if (shouldOverallTaskDirty) { + overallTask.dirty(); + } + }; + Scheduler.prototype._pipe = function (seriesModel, task) { + var pipelineId = seriesModel.uid; + var pipeline = this._pipelineMap.get(pipelineId); + !pipeline.head && (pipeline.head = task); + pipeline.tail && pipeline.tail.pipe(task); + pipeline.tail = task; + task.__idxInPipeline = pipeline.count++; + task.__pipeline = pipeline; + }; + Scheduler.wrapStageHandler = function (stageHandler, visualType) { + if (isFunction(stageHandler)) { + stageHandler = { + overallReset: stageHandler, + seriesType: detectSeriseType(stageHandler) + }; + } + stageHandler.uid = getUID('stageHandler'); + visualType && (stageHandler.visualType = visualType); + return stageHandler; + }; + return Scheduler; + }(); + function overallTaskReset(context) { + context.overallReset(context.ecModel, context.api, context.payload); + } + function stubReset(context) { + return context.overallProgress && stubProgress; + } + function stubProgress() { + this.agent.dirty(); + this.getDownstream().dirty(); + } + function stubOnDirty() { + this.agent && this.agent.dirty(); + } + function seriesTaskPlan(context) { + return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null; + } + function seriesTaskReset(context) { + if (context.useClearVisual) { + context.data.clearAllVisual(); + } + var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload)); + return resetDefines.length > 1 ? map(resetDefines, function (v, idx) { + return makeSeriesTaskProgress(idx); + }) : singleSeriesTaskProgress; + } + var singleSeriesTaskProgress = makeSeriesTaskProgress(0); + function makeSeriesTaskProgress(resetDefineIdx) { + return function (params, context) { + var data = context.data; + var resetDefine = context.resetDefines[resetDefineIdx]; + if (resetDefine && resetDefine.dataEach) { + for (var i = params.start; i < params.end; i++) { + resetDefine.dataEach(data, i); + } + } else if (resetDefine && resetDefine.progress) { + resetDefine.progress(params, data); + } + }; + } + function seriesTaskCount(context) { + return context.data.count(); + } + /** + * Only some legacy stage handlers (usually in echarts extensions) are pure function. + * To ensure that they can work normally, they should work in block mode, that is, + * they should not be started util the previous tasks finished. So they cause the + * progressive rendering disabled. We try to detect the series type, to narrow down + * the block range to only the series type they concern, but not all series. + */ + function detectSeriseType(legacyFunc) { + seriesType = null; + try { + // Assume there is no async when calling `eachSeriesByType`. + legacyFunc(ecModelMock, apiMock); + } catch (e) {} + return seriesType; + } + var ecModelMock = {}; + var apiMock = {}; + var seriesType; + mockMethods(ecModelMock, GlobalModel); + mockMethods(apiMock, ExtensionAPI); + ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) { + seriesType = type; + }; + ecModelMock.eachComponent = function (cond) { + if (cond.mainType === 'series' && cond.subType) { + seriesType = cond.subType; + } + }; + function mockMethods(target, Clz) { + /* eslint-disable */ + for (var name_1 in Clz.prototype) { + // Do not use hasOwnProperty + target[name_1] = noop; + } + /* eslint-enable */ + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF']; + var lightTheme = { + color: colorAll, + colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll] + }; + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var contrastColor = '#B9B8CE'; + var backgroundColor = '#100C2A'; + var axisCommon = function () { + return { + axisLine: { + lineStyle: { + color: contrastColor + } + }, + splitLine: { + lineStyle: { + color: '#484753' + } + }, + splitArea: { + areaStyle: { + color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)'] + } + }, + minorSplitLine: { + lineStyle: { + color: '#20203B' + } + } + }; + }; + var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff']; + var theme = { + darkMode: true, + color: colorPalette, + backgroundColor: backgroundColor, + axisPointer: { + lineStyle: { + color: '#817f91' + }, + crossStyle: { + color: '#817f91' + }, + label: { + // TODO Contrast of label backgorundColor + color: '#fff' + } + }, + legend: { + textStyle: { + color: contrastColor + } + }, + textStyle: { + color: contrastColor + }, + title: { + textStyle: { + color: '#EEF1FA' + }, + subtextStyle: { + color: '#B9B8CE' + } + }, + toolbox: { + iconStyle: { + borderColor: contrastColor + } + }, + dataZoom: { + borderColor: '#71708A', + textStyle: { + color: contrastColor + }, + brushStyle: { + color: 'rgba(135,163,206,0.3)' + }, + handleStyle: { + color: '#353450', + borderColor: '#C5CBE3' + }, + moveHandleStyle: { + color: '#B0B6C3', + opacity: 0.3 + }, + fillerColor: 'rgba(135,163,206,0.2)', + emphasis: { + handleStyle: { + borderColor: '#91B7F2', + color: '#4D587D' + }, + moveHandleStyle: { + color: '#636D9A', + opacity: 0.7 + } + }, + dataBackground: { + lineStyle: { + color: '#71708A', + width: 1 + }, + areaStyle: { + color: '#71708A' + } + }, + selectedDataBackground: { + lineStyle: { + color: '#87A3CE' + }, + areaStyle: { + color: '#87A3CE' + } + } + }, + visualMap: { + textStyle: { + color: contrastColor + } + }, + timeline: { + lineStyle: { + color: contrastColor + }, + label: { + color: contrastColor + }, + controlStyle: { + color: contrastColor, + borderColor: contrastColor + } + }, + calendar: { + itemStyle: { + color: backgroundColor + }, + dayLabel: { + color: contrastColor + }, + monthLabel: { + color: contrastColor + }, + yearLabel: { + color: contrastColor + } + }, + timeAxis: axisCommon(), + logAxis: axisCommon(), + valueAxis: axisCommon(), + categoryAxis: axisCommon(), + line: { + symbol: 'circle' + }, + graph: { + color: colorPalette + }, + gauge: { + title: { + color: contrastColor + }, + axisLine: { + lineStyle: { + color: [[1, 'rgba(207,212,219,0.2)']] + } + }, + axisLabel: { + color: contrastColor + }, + detail: { + color: '#EEF1FA' + } + }, + candlestick: { + itemStyle: { + color: '#f64e56', + color0: '#54ea92', + borderColor: '#f64e56', + borderColor0: '#54ea92' + // borderColor: '#ca2824', + // borderColor0: '#09a443' + } + } + }; + + theme.categoryAxis.splitLine.show = false; + + /** + * Usage of query: + * `chart.on('click', query, handler);` + * The `query` can be: + * + The component type query string, only `mainType` or `mainType.subType`, + * like: 'xAxis', 'series', 'xAxis.category' or 'series.line'. + * + The component query object, like: + * `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`, + * `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`. + * + The data query object, like: + * `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`. + * + The other query object (cmponent customized query), like: + * `{element: 'some'}` (only available in custom series). + * + * Caveat: If a prop in the `query` object is `null/undefined`, it is the + * same as there is no such prop in the `query` object. + */ + var ECEventProcessor = /** @class */function () { + function ECEventProcessor() {} + ECEventProcessor.prototype.normalizeQuery = function (query) { + var cptQuery = {}; + var dataQuery = {}; + var otherQuery = {}; + // `query` is `mainType` or `mainType.subType` of component. + if (isString(query)) { + var condCptType = parseClassType(query); + // `.main` and `.sub` may be ''. + cptQuery.mainType = condCptType.main || null; + cptQuery.subType = condCptType.sub || null; + } + // `query` is an object, convert to {mainType, index, name, id}. + else { + // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved, + // can not be used in `compomentModel.filterForExposedEvent`. + var suffixes_1 = ['Index', 'Name', 'Id']; + var dataKeys_1 = { + name: 1, + dataIndex: 1, + dataType: 1 + }; + each(query, function (val, key) { + var reserved = false; + for (var i = 0; i < suffixes_1.length; i++) { + var propSuffix = suffixes_1[i]; + var suffixPos = key.lastIndexOf(propSuffix); + if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) { + var mainType = key.slice(0, suffixPos); + // Consider `dataIndex`. + if (mainType !== 'data') { + cptQuery.mainType = mainType; + cptQuery[propSuffix.toLowerCase()] = val; + reserved = true; + } + } + } + if (dataKeys_1.hasOwnProperty(key)) { + dataQuery[key] = val; + reserved = true; + } + if (!reserved) { + otherQuery[key] = val; + } + }); + } + return { + cptQuery: cptQuery, + dataQuery: dataQuery, + otherQuery: otherQuery + }; + }; + ECEventProcessor.prototype.filter = function (eventType, query) { + // They should be assigned before each trigger call. + var eventInfo = this.eventInfo; + if (!eventInfo) { + return true; + } + var targetEl = eventInfo.targetEl; + var packedEvent = eventInfo.packedEvent; + var model = eventInfo.model; + var view = eventInfo.view; + // For event like 'globalout'. + if (!model || !view) { + return true; + } + var cptQuery = query.cptQuery; + var dataQuery = query.dataQuery; + return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent)); + function check(query, host, prop, propOnHost) { + return query[prop] == null || host[propOnHost || prop] === query[prop]; + } + }; + ECEventProcessor.prototype.afterTrigger = function () { + // Make sure the eventInfo won't be used in next trigger. + this.eventInfo = null; + }; + return ECEventProcessor; + }(); + + var SYMBOL_PROPS_WITH_CB = ['symbol', 'symbolSize', 'symbolRotate', 'symbolOffset']; + var SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']); + // Encoding visual for all series include which is filtered for legend drawing + var seriesSymbolTask = { + createOnAllSeries: true, + // For legend. + performRawSeries: true, + reset: function (seriesModel, ecModel) { + var data = seriesModel.getData(); + if (seriesModel.legendIcon) { + data.setVisual('legendIcon', seriesModel.legendIcon); + } + if (!seriesModel.hasSymbolVisual) { + return; + } + var symbolOptions = {}; + var symbolOptionsCb = {}; + var hasCallback = false; + for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) { + var symbolPropName = SYMBOL_PROPS_WITH_CB[i]; + var val = seriesModel.get(symbolPropName); + if (isFunction(val)) { + hasCallback = true; + symbolOptionsCb[symbolPropName] = val; + } else { + symbolOptions[symbolPropName] = val; + } + } + symbolOptions.symbol = symbolOptions.symbol || seriesModel.defaultSymbol; + data.setVisual(extend({ + legendIcon: seriesModel.legendIcon || symbolOptions.symbol, + symbolKeepAspect: seriesModel.get('symbolKeepAspect') + }, symbolOptions)); + // Only visible series has each data be visual encoded + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + var symbolPropsCb = keys(symbolOptionsCb); + function dataEach(data, idx) { + var rawValue = seriesModel.getRawValue(idx); + var params = seriesModel.getDataParams(idx); + for (var i = 0; i < symbolPropsCb.length; i++) { + var symbolPropName = symbolPropsCb[i]; + data.setItemVisual(idx, symbolPropName, symbolOptionsCb[symbolPropName](rawValue, params)); + } + } + return { + dataEach: hasCallback ? dataEach : null + }; + } + }; + var dataSymbolTask = { + createOnAllSeries: true, + // For legend. + performRawSeries: true, + reset: function (seriesModel, ecModel) { + if (!seriesModel.hasSymbolVisual) { + return; + } + // Only visible series has each data be visual encoded + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + var data = seriesModel.getData(); + function dataEach(data, idx) { + var itemModel = data.getItemModel(idx); + for (var i = 0; i < SYMBOL_PROPS.length; i++) { + var symbolPropName = SYMBOL_PROPS[i]; + var val = itemModel.getShallow(symbolPropName, true); + if (val != null) { + data.setItemVisual(idx, symbolPropName, val); + } + } + } + return { + dataEach: data.hasItemOption ? dataEach : null + }; + } + }; + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function getItemVisualFromData(data, dataIndex, key) { + switch (key) { + case 'color': + var style = data.getItemVisual(dataIndex, 'style'); + return style[data.getVisual('drawType')]; + case 'opacity': + return data.getItemVisual(dataIndex, 'style').opacity; + case 'symbol': + case 'symbolSize': + case 'liftZ': + return data.getItemVisual(dataIndex, key); + default: + if ("development" !== 'production') { + console.warn("Unknown visual type " + key); + } + } + } + function getVisualFromData(data, key) { + switch (key) { + case 'color': + var style = data.getVisual('style'); + return style[data.getVisual('drawType')]; + case 'opacity': + return data.getVisual('style').opacity; + case 'symbol': + case 'symbolSize': + case 'liftZ': + return data.getVisual(key); + default: + if ("development" !== 'production') { + console.warn("Unknown visual type " + key); + } + } + } + function setItemVisualFromData(data, dataIndex, key, value) { + switch (key) { + case 'color': + // Make sure not sharing style object. + var style = data.ensureUniqueItemVisual(dataIndex, 'style'); + style[data.getVisual('drawType')] = value; + // Mark the color has been changed, not from palette anymore + data.setItemVisual(dataIndex, 'colorFromPalette', false); + break; + case 'opacity': + data.ensureUniqueItemVisual(dataIndex, 'style').opacity = value; + break; + case 'symbol': + case 'symbolSize': + case 'liftZ': + data.setItemVisual(dataIndex, key, value); + break; + default: + if ("development" !== 'production') { + console.warn("Unknown visual type " + key); + } + } + } + + // Legacy data selection action. + // Includes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect + function createLegacyDataSelectAction(seriesType, ecRegisterAction) { + function getSeriesIndices(ecModel, payload) { + var seriesIndices = []; + ecModel.eachComponent({ + mainType: 'series', + subType: seriesType, + query: payload + }, function (seriesModel) { + seriesIndices.push(seriesModel.seriesIndex); + }); + return seriesIndices; + } + each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) { + ecRegisterAction(eventsMap[0], function (payload, ecModel, api) { + payload = extend({}, payload); + if ("development" !== 'production') { + deprecateReplaceLog(payload.type, eventsMap[1]); + } + api.dispatchAction(extend(payload, { + type: eventsMap[1], + seriesIndex: getSeriesIndices(ecModel, payload) + })); + }); + }); + } + function handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) { + var legacyEventName = type + eventPostfix; + if (!ecIns.isSilent(legacyEventName)) { + if ("development" !== 'production') { + deprecateLog("event " + legacyEventName + " is deprecated."); + } + ecModel.eachComponent({ + mainType: 'series', + subType: 'pie' + }, function (seriesModel) { + var seriesIndex = seriesModel.seriesIndex; + var selectedMap = seriesModel.option.selectedMap; + var selected = payload.selected; + for (var i = 0; i < selected.length; i++) { + if (selected[i].seriesIndex === seriesIndex) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload.fromActionPayload); + ecIns.trigger(legacyEventName, { + type: legacyEventName, + seriesId: seriesModel.id, + name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex), + selected: isString(selectedMap) ? selectedMap : extend({}, selectedMap) + }); + } + } + }); + } + } + function handleLegacySelectEvents(messageCenter, ecIns, api) { + messageCenter.on('selectchanged', function (params) { + var ecModel = api.getModel(); + if (params.isFromClick) { + handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params); + handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params); + } else if (params.fromAction === 'select') { + handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params); + handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params); + } else if (params.fromAction === 'unselect') { + handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params); + handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params); + } + }); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function findEventDispatcher(target, det, returnFirstMatch) { + var found; + while (target) { + if (det(target)) { + found = target; + if (returnFirstMatch) { + break; + } + } + target = target.__hostTarget || target.parent; + } + return found; + } + + var wmUniqueIndex = Math.round(Math.random() * 9); + var supportDefineProperty = typeof Object.defineProperty === 'function'; + var WeakMap = (function () { + function WeakMap() { + this._id = '__ec_inner_' + wmUniqueIndex++; + } + WeakMap.prototype.get = function (key) { + return this._guard(key)[this._id]; + }; + WeakMap.prototype.set = function (key, value) { + var target = this._guard(key); + if (supportDefineProperty) { + Object.defineProperty(target, this._id, { + value: value, + enumerable: false, + configurable: true + }); + } + else { + target[this._id] = value; + } + return this; + }; + WeakMap.prototype["delete"] = function (key) { + if (this.has(key)) { + delete this._guard(key)[this._id]; + return true; + } + return false; + }; + WeakMap.prototype.has = function (key) { + return !!this._guard(key)[this._id]; + }; + WeakMap.prototype._guard = function (key) { + if (key !== Object(key)) { + throw TypeError('Value of WeakMap is not a non-null object.'); + } + return key; + }; + return WeakMap; + }()); + + /** + * Triangle shape + * @inner + */ + var Triangle = Path.extend({ + type: 'triangle', + shape: { + cx: 0, + cy: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var cx = shape.cx; + var cy = shape.cy; + var width = shape.width / 2; + var height = shape.height / 2; + path.moveTo(cx, cy - height); + path.lineTo(cx + width, cy + height); + path.lineTo(cx - width, cy + height); + path.closePath(); + } + }); + /** + * Diamond shape + * @inner + */ + var Diamond = Path.extend({ + type: 'diamond', + shape: { + cx: 0, + cy: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var cx = shape.cx; + var cy = shape.cy; + var width = shape.width / 2; + var height = shape.height / 2; + path.moveTo(cx, cy - height); + path.lineTo(cx + width, cy); + path.lineTo(cx, cy + height); + path.lineTo(cx - width, cy); + path.closePath(); + } + }); + /** + * Pin shape + * @inner + */ + var Pin = Path.extend({ + type: 'pin', + shape: { + // x, y on the cusp + x: 0, + y: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var x = shape.x; + var y = shape.y; + var w = shape.width / 5 * 3; + // Height must be larger than width + var h = Math.max(w, shape.height); + var r = w / 2; + // Dist on y with tangent point and circle center + var dy = r * r / (h - r); + var cy = y - h + r + dy; + var angle = Math.asin(dy / r); + // Dist on x with tangent point and circle center + var dx = Math.cos(angle) * r; + var tanX = Math.sin(angle); + var tanY = Math.cos(angle); + var cpLen = r * 0.6; + var cpLen2 = r * 0.7; + path.moveTo(x - dx, cy + dy); + path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle); + path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y); + path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy); + path.closePath(); + } + }); + /** + * Arrow shape + * @inner + */ + var Arrow = Path.extend({ + type: 'arrow', + shape: { + x: 0, + y: 0, + width: 0, + height: 0 + }, + buildPath: function (ctx, shape) { + var height = shape.height; + var width = shape.width; + var x = shape.x; + var y = shape.y; + var dx = width / 3 * 2; + ctx.moveTo(x, y); + ctx.lineTo(x + dx, y + height); + ctx.lineTo(x, y + height / 4 * 3); + ctx.lineTo(x - dx, y + height); + ctx.lineTo(x, y); + ctx.closePath(); + } + }); + /** + * Map of path constructors + */ + // TODO Use function to build symbol path. + var symbolCtors = { + line: Line, + rect: Rect, + roundRect: Rect, + square: Rect, + circle: Circle, + diamond: Diamond, + pin: Pin, + arrow: Arrow, + triangle: Triangle + }; + var symbolShapeMakers = { + line: function (x, y, w, h, shape) { + shape.x1 = x; + shape.y1 = y + h / 2; + shape.x2 = x + w; + shape.y2 = y + h / 2; + }, + rect: function (x, y, w, h, shape) { + shape.x = x; + shape.y = y; + shape.width = w; + shape.height = h; + }, + roundRect: function (x, y, w, h, shape) { + shape.x = x; + shape.y = y; + shape.width = w; + shape.height = h; + shape.r = Math.min(w, h) / 4; + }, + square: function (x, y, w, h, shape) { + var size = Math.min(w, h); + shape.x = x; + shape.y = y; + shape.width = size; + shape.height = size; + }, + circle: function (x, y, w, h, shape) { + // Put circle in the center of square + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.r = Math.min(w, h) / 2; + }, + diamond: function (x, y, w, h, shape) { + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.width = w; + shape.height = h; + }, + pin: function (x, y, w, h, shape) { + shape.x = x + w / 2; + shape.y = y + h / 2; + shape.width = w; + shape.height = h; + }, + arrow: function (x, y, w, h, shape) { + shape.x = x + w / 2; + shape.y = y + h / 2; + shape.width = w; + shape.height = h; + }, + triangle: function (x, y, w, h, shape) { + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.width = w; + shape.height = h; + } + }; + var symbolBuildProxies = {}; + each(symbolCtors, function (Ctor, name) { + symbolBuildProxies[name] = new Ctor(); + }); + var SymbolClz = Path.extend({ + type: 'symbol', + shape: { + symbolType: '', + x: 0, + y: 0, + width: 0, + height: 0 + }, + calculateTextPosition: function (out, config, rect) { + var res = calculateTextPosition(out, config, rect); + var shape = this.shape; + if (shape && shape.symbolType === 'pin' && config.position === 'inside') { + res.y = rect.y + rect.height * 0.4; + } + return res; + }, + buildPath: function (ctx, shape, inBundle) { + var symbolType = shape.symbolType; + if (symbolType !== 'none') { + var proxySymbol = symbolBuildProxies[symbolType]; + if (!proxySymbol) { + // Default rect + symbolType = 'rect'; + proxySymbol = symbolBuildProxies[symbolType]; + } + symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape); + proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle); + } + } + }); + // Provide setColor helper method to avoid determine if set the fill or stroke outside + function symbolPathSetColor(color, innerColor) { + if (this.type !== 'image') { + var symbolStyle = this.style; + if (this.__isEmptyBrush) { + symbolStyle.stroke = color; + symbolStyle.fill = innerColor || '#fff'; + // TODO Same width with lineStyle in LineView + symbolStyle.lineWidth = 2; + } else if (this.shape.symbolType === 'line') { + symbolStyle.stroke = color; + } else { + symbolStyle.fill = color; + } + this.markRedraw(); + } + } + /** + * Create a symbol element with given symbol configuration: shape, x, y, width, height, color + */ + function createSymbol(symbolType, x, y, w, h, color, + // whether to keep the ratio of w/h, + keepAspect) { + // TODO Support image object, DynamicImage. + var isEmpty = symbolType.indexOf('empty') === 0; + if (isEmpty) { + symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6); + } + var symbolPath; + if (symbolType.indexOf('image://') === 0) { + symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover'); + } else if (symbolType.indexOf('path://') === 0) { + symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover'); + } else { + symbolPath = new SymbolClz({ + shape: { + symbolType: symbolType, + x: x, + y: y, + width: w, + height: h + } + }); + } + symbolPath.__isEmptyBrush = isEmpty; + // TODO Should deprecate setColor + symbolPath.setColor = symbolPathSetColor; + if (color) { + symbolPath.setColor(color); + } + return symbolPath; + } + function normalizeSymbolSize(symbolSize) { + if (!isArray(symbolSize)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return [symbolSize[0] || 0, symbolSize[1] || 0]; + } + function normalizeSymbolOffset(symbolOffset, symbolSize) { + if (symbolOffset == null) { + return; + } + if (!isArray(symbolOffset)) { + symbolOffset = [symbolOffset, symbolOffset]; + } + return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0]; + } + + function isSafeNum(num) { + return isFinite(num); + } + function createLinearGradient(ctx, obj, rect) { + var x = obj.x == null ? 0 : obj.x; + var x2 = obj.x2 == null ? 1 : obj.x2; + var y = obj.y == null ? 0 : obj.y; + var y2 = obj.y2 == null ? 0 : obj.y2; + if (!obj.global) { + x = x * rect.width + rect.x; + x2 = x2 * rect.width + rect.x; + y = y * rect.height + rect.y; + y2 = y2 * rect.height + rect.y; + } + x = isSafeNum(x) ? x : 0; + x2 = isSafeNum(x2) ? x2 : 1; + y = isSafeNum(y) ? y : 0; + y2 = isSafeNum(y2) ? y2 : 0; + var canvasGradient = ctx.createLinearGradient(x, y, x2, y2); + return canvasGradient; + } + function createRadialGradient(ctx, obj, rect) { + var width = rect.width; + var height = rect.height; + var min = Math.min(width, height); + var x = obj.x == null ? 0.5 : obj.x; + var y = obj.y == null ? 0.5 : obj.y; + var r = obj.r == null ? 0.5 : obj.r; + if (!obj.global) { + x = x * width + rect.x; + y = y * height + rect.y; + r = r * min; + } + x = isSafeNum(x) ? x : 0.5; + y = isSafeNum(y) ? y : 0.5; + r = r >= 0 && isSafeNum(r) ? r : 0.5; + var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r); + return canvasGradient; + } + function getCanvasGradient(ctx, obj, rect) { + var canvasGradient = obj.type === 'radial' + ? createRadialGradient(ctx, obj, rect) + : createLinearGradient(ctx, obj, rect); + var colorStops = obj.colorStops; + for (var i = 0; i < colorStops.length; i++) { + canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color); + } + return canvasGradient; + } + function isClipPathChanged(clipPaths, prevClipPaths) { + if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) { + return false; + } + if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) { + return true; + } + for (var i = 0; i < clipPaths.length; i++) { + if (clipPaths[i] !== prevClipPaths[i]) { + return true; + } + } + return false; + } + function parseInt10(val) { + return parseInt(val, 10); + } + function getSize(root, whIdx, opts) { + var wh = ['width', 'height'][whIdx]; + var cwh = ['clientWidth', 'clientHeight'][whIdx]; + var plt = ['paddingLeft', 'paddingTop'][whIdx]; + var prb = ['paddingRight', 'paddingBottom'][whIdx]; + if (opts[wh] != null && opts[wh] !== 'auto') { + return parseFloat(opts[wh]); + } + var stl = document.defaultView.getComputedStyle(root); + return ((root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) + - (parseInt10(stl[plt]) || 0) + - (parseInt10(stl[prb]) || 0)) | 0; + } + + function normalizeLineDash(lineType, lineWidth) { + if (!lineType || lineType === 'solid' || !(lineWidth > 0)) { + return null; + } + return lineType === 'dashed' + ? [4 * lineWidth, 2 * lineWidth] + : lineType === 'dotted' + ? [lineWidth] + : isNumber(lineType) + ? [lineType] : isArray(lineType) ? lineType : null; + } + function getLineDash(el) { + var style = el.style; + var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth); + var lineDashOffset = style.lineDashOffset; + if (lineDash) { + var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1; + if (lineScale_1 && lineScale_1 !== 1) { + lineDash = map(lineDash, function (rawVal) { + return rawVal / lineScale_1; + }); + lineDashOffset /= lineScale_1; + } + } + return [lineDash, lineDashOffset]; + } + + var pathProxyForDraw = new PathProxy(true); + function styleHasStroke(style) { + var stroke = style.stroke; + return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0)); + } + function isValidStrokeFillStyle(strokeOrFill) { + return typeof strokeOrFill === 'string' && strokeOrFill !== 'none'; + } + function styleHasFill(style) { + var fill = style.fill; + return fill != null && fill !== 'none'; + } + function doFillPath(ctx, style) { + if (style.fillOpacity != null && style.fillOpacity !== 1) { + var originalGlobalAlpha = ctx.globalAlpha; + ctx.globalAlpha = style.fillOpacity * style.opacity; + ctx.fill(); + ctx.globalAlpha = originalGlobalAlpha; + } + else { + ctx.fill(); + } + } + function doStrokePath(ctx, style) { + if (style.strokeOpacity != null && style.strokeOpacity !== 1) { + var originalGlobalAlpha = ctx.globalAlpha; + ctx.globalAlpha = style.strokeOpacity * style.opacity; + ctx.stroke(); + ctx.globalAlpha = originalGlobalAlpha; + } + else { + ctx.stroke(); + } + } + function createCanvasPattern(ctx, pattern, el) { + var image = createOrUpdateImage(pattern.image, pattern.__image, el); + if (isImageReady(image)) { + var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat'); + if (typeof DOMMatrix === 'function' + && canvasPattern + && canvasPattern.setTransform) { + var matrix = new DOMMatrix(); + matrix.translateSelf((pattern.x || 0), (pattern.y || 0)); + matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE); + matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1)); + canvasPattern.setTransform(matrix); + } + return canvasPattern; + } + } + function brushPath(ctx, el, style, inBatch) { + var _a; + var hasStroke = styleHasStroke(style); + var hasFill = styleHasFill(style); + var strokePercent = style.strokePercent; + var strokePart = strokePercent < 1; + var firstDraw = !el.path; + if ((!el.silent || strokePart) && firstDraw) { + el.createPathProxy(); + } + var path = el.path || pathProxyForDraw; + var dirtyFlag = el.__dirty; + if (!inBatch) { + var fill = style.fill; + var stroke = style.stroke; + var hasFillGradient = hasFill && !!fill.colorStops; + var hasStrokeGradient = hasStroke && !!stroke.colorStops; + var hasFillPattern = hasFill && !!fill.image; + var hasStrokePattern = hasStroke && !!stroke.image; + var fillGradient = void 0; + var strokeGradient = void 0; + var fillPattern = void 0; + var strokePattern = void 0; + var rect = void 0; + if (hasFillGradient || hasStrokeGradient) { + rect = el.getBoundingRect(); + } + if (hasFillGradient) { + fillGradient = dirtyFlag + ? getCanvasGradient(ctx, fill, rect) + : el.__canvasFillGradient; + el.__canvasFillGradient = fillGradient; + } + if (hasStrokeGradient) { + strokeGradient = dirtyFlag + ? getCanvasGradient(ctx, stroke, rect) + : el.__canvasStrokeGradient; + el.__canvasStrokeGradient = strokeGradient; + } + if (hasFillPattern) { + fillPattern = (dirtyFlag || !el.__canvasFillPattern) + ? createCanvasPattern(ctx, fill, el) + : el.__canvasFillPattern; + el.__canvasFillPattern = fillPattern; + } + if (hasStrokePattern) { + strokePattern = (dirtyFlag || !el.__canvasStrokePattern) + ? createCanvasPattern(ctx, stroke, el) + : el.__canvasStrokePattern; + el.__canvasStrokePattern = fillPattern; + } + if (hasFillGradient) { + ctx.fillStyle = fillGradient; + } + else if (hasFillPattern) { + if (fillPattern) { + ctx.fillStyle = fillPattern; + } + else { + hasFill = false; + } + } + if (hasStrokeGradient) { + ctx.strokeStyle = strokeGradient; + } + else if (hasStrokePattern) { + if (strokePattern) { + ctx.strokeStyle = strokePattern; + } + else { + hasStroke = false; + } + } + } + var scale = el.getGlobalScale(); + path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold); + var lineDash; + var lineDashOffset; + if (ctx.setLineDash && style.lineDash) { + _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1]; + } + var needsRebuild = true; + if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) { + path.setDPR(ctx.dpr); + if (strokePart) { + path.setContext(null); + } + else { + path.setContext(ctx); + needsRebuild = false; + } + path.reset(); + el.buildPath(path, el.shape, inBatch); + path.toStatic(); + el.pathUpdated(); + } + if (needsRebuild) { + path.rebuildPath(ctx, strokePart ? strokePercent : 1); + } + if (lineDash) { + ctx.setLineDash(lineDash); + ctx.lineDashOffset = lineDashOffset; + } + if (!inBatch) { + if (style.strokeFirst) { + if (hasStroke) { + doStrokePath(ctx, style); + } + if (hasFill) { + doFillPath(ctx, style); + } + } + else { + if (hasFill) { + doFillPath(ctx, style); + } + if (hasStroke) { + doStrokePath(ctx, style); + } + } + } + if (lineDash) { + ctx.setLineDash([]); + } + } + function brushImage(ctx, el, style) { + var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload); + if (!image || !isImageReady(image)) { + return; + } + var x = style.x || 0; + var y = style.y || 0; + var width = el.getWidth(); + var height = el.getHeight(); + var aspect = image.width / image.height; + if (width == null && height != null) { + width = height * aspect; + } + else if (height == null && width != null) { + height = width / aspect; + } + else if (width == null && height == null) { + width = image.width; + height = image.height; + } + if (style.sWidth && style.sHeight) { + var sx = style.sx || 0; + var sy = style.sy || 0; + ctx.drawImage(image, sx, sy, style.sWidth, style.sHeight, x, y, width, height); + } + else if (style.sx && style.sy) { + var sx = style.sx; + var sy = style.sy; + var sWidth = width - sx; + var sHeight = height - sy; + ctx.drawImage(image, sx, sy, sWidth, sHeight, x, y, width, height); + } + else { + ctx.drawImage(image, x, y, width, height); + } + } + function brushText(ctx, el, style) { + var _a; + var text = style.text; + text != null && (text += ''); + if (text) { + ctx.font = style.font || DEFAULT_FONT; + ctx.textAlign = style.textAlign; + ctx.textBaseline = style.textBaseline; + var lineDash = void 0; + var lineDashOffset = void 0; + if (ctx.setLineDash && style.lineDash) { + _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1]; + } + if (lineDash) { + ctx.setLineDash(lineDash); + ctx.lineDashOffset = lineDashOffset; + } + if (style.strokeFirst) { + if (styleHasStroke(style)) { + ctx.strokeText(text, style.x, style.y); + } + if (styleHasFill(style)) { + ctx.fillText(text, style.x, style.y); + } + } + else { + if (styleHasFill(style)) { + ctx.fillText(text, style.x, style.y); + } + if (styleHasStroke(style)) { + ctx.strokeText(text, style.x, style.y); + } + } + if (lineDash) { + ctx.setLineDash([]); + } + } + } + var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; + var STROKE_PROPS = [ + ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10] + ]; + function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) { + var styleChanged = false; + if (!forceSetAll) { + prevStyle = prevStyle || {}; + if (style === prevStyle) { + return false; + } + } + if (forceSetAll || style.opacity !== prevStyle.opacity) { + flushPathDrawn(ctx, scope); + styleChanged = true; + var opacity = Math.max(Math.min(style.opacity, 1), 0); + ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity; + } + if (forceSetAll || style.blend !== prevStyle.blend) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend; + } + for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) { + var propName = SHADOW_NUMBER_PROPS[i]; + if (forceSetAll || style[propName] !== prevStyle[propName]) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + ctx[propName] = ctx.dpr * (style[propName] || 0); + } + } + if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor; + } + return styleChanged; + } + function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) { + var style = getStyle(el, scope.inHover); + var prevStyle = forceSetAll + ? null + : (prevEl && getStyle(prevEl, scope.inHover) || {}); + if (style === prevStyle) { + return false; + } + var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope); + if (forceSetAll || style.fill !== prevStyle.fill) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill); + } + if (forceSetAll || style.stroke !== prevStyle.stroke) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke); + } + if (forceSetAll || style.opacity !== prevStyle.opacity) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + ctx.globalAlpha = style.opacity == null ? 1 : style.opacity; + } + if (el.hasStroke()) { + var lineWidth = style.lineWidth; + var newLineWidth = lineWidth / ((style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1); + if (ctx.lineWidth !== newLineWidth) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + ctx.lineWidth = newLineWidth; + } + } + for (var i = 0; i < STROKE_PROPS.length; i++) { + var prop = STROKE_PROPS[i]; + var propName = prop[0]; + if (forceSetAll || style[propName] !== prevStyle[propName]) { + if (!styleChanged) { + flushPathDrawn(ctx, scope); + styleChanged = true; + } + ctx[propName] = style[propName] || prop[1]; + } + } + return styleChanged; + } + function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) { + return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope); + } + function setContextTransform(ctx, el) { + var m = el.transform; + var dpr = ctx.dpr || 1; + if (m) { + ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]); + } + else { + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + } + function updateClipStatus(clipPaths, ctx, scope) { + var allClipped = false; + for (var i = 0; i < clipPaths.length; i++) { + var clipPath = clipPaths[i]; + allClipped = allClipped || clipPath.isZeroArea(); + setContextTransform(ctx, clipPath); + ctx.beginPath(); + clipPath.buildPath(ctx, clipPath.shape); + ctx.clip(); + } + scope.allClipped = allClipped; + } + function isTransformChanged(m0, m1) { + if (m0 && m1) { + return m0[0] !== m1[0] + || m0[1] !== m1[1] + || m0[2] !== m1[2] + || m0[3] !== m1[3] + || m0[4] !== m1[4] + || m0[5] !== m1[5]; + } + else if (!m0 && !m1) { + return false; + } + return true; + } + var DRAW_TYPE_PATH = 1; + var DRAW_TYPE_IMAGE = 2; + var DRAW_TYPE_TEXT = 3; + var DRAW_TYPE_INCREMENTAL = 4; + function canPathBatch(style) { + var hasFill = styleHasFill(style); + var hasStroke = styleHasStroke(style); + return !(style.lineDash + || !(+hasFill ^ +hasStroke) + || (hasFill && typeof style.fill !== 'string') + || (hasStroke && typeof style.stroke !== 'string') + || style.strokePercent < 1 + || style.strokeOpacity < 1 + || style.fillOpacity < 1); + } + function flushPathDrawn(ctx, scope) { + scope.batchFill && ctx.fill(); + scope.batchStroke && ctx.stroke(); + scope.batchFill = ''; + scope.batchStroke = ''; + } + function getStyle(el, inHover) { + return inHover ? (el.__hoverStyle || el.style) : el.style; + } + function brushSingle(ctx, el) { + brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true); + } + function brush(ctx, el, scope, isLast) { + var m = el.transform; + if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) { + el.__dirty &= ~REDRAW_BIT; + el.__isRendered = false; + return; + } + var clipPaths = el.__clipPaths; + var prevElClipPaths = scope.prevElClipPaths; + var forceSetTransform = false; + var forceSetStyle = false; + if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) { + if (prevElClipPaths && prevElClipPaths.length) { + flushPathDrawn(ctx, scope); + ctx.restore(); + forceSetStyle = forceSetTransform = true; + scope.prevElClipPaths = null; + scope.allClipped = false; + scope.prevEl = null; + } + if (clipPaths && clipPaths.length) { + flushPathDrawn(ctx, scope); + ctx.save(); + updateClipStatus(clipPaths, ctx, scope); + forceSetTransform = true; + } + scope.prevElClipPaths = clipPaths; + } + if (scope.allClipped) { + el.__isRendered = false; + return; + } + el.beforeBrush && el.beforeBrush(); + el.innerBeforeBrush(); + var prevEl = scope.prevEl; + if (!prevEl) { + forceSetStyle = forceSetTransform = true; + } + var canBatchPath = el instanceof Path + && el.autoBatch + && canPathBatch(el.style); + if (forceSetTransform || isTransformChanged(m, prevEl.transform)) { + flushPathDrawn(ctx, scope); + setContextTransform(ctx, el); + } + else if (!canBatchPath) { + flushPathDrawn(ctx, scope); + } + var style = getStyle(el, scope.inHover); + if (el instanceof Path) { + if (scope.lastDrawType !== DRAW_TYPE_PATH) { + forceSetStyle = true; + scope.lastDrawType = DRAW_TYPE_PATH; + } + bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope); + if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) { + ctx.beginPath(); + } + brushPath(ctx, el, style, canBatchPath); + if (canBatchPath) { + scope.batchFill = style.fill || ''; + scope.batchStroke = style.stroke || ''; + } + } + else { + if (el instanceof TSpan) { + if (scope.lastDrawType !== DRAW_TYPE_TEXT) { + forceSetStyle = true; + scope.lastDrawType = DRAW_TYPE_TEXT; + } + bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope); + brushText(ctx, el, style); + } + else if (el instanceof ZRImage) { + if (scope.lastDrawType !== DRAW_TYPE_IMAGE) { + forceSetStyle = true; + scope.lastDrawType = DRAW_TYPE_IMAGE; + } + bindImageStyle(ctx, el, prevEl, forceSetStyle, scope); + brushImage(ctx, el, style); + } + else if (el.getTemporalDisplayables) { + if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) { + forceSetStyle = true; + scope.lastDrawType = DRAW_TYPE_INCREMENTAL; + } + brushIncremental(ctx, el, scope); + } + } + if (canBatchPath && isLast) { + flushPathDrawn(ctx, scope); + } + el.innerAfterBrush(); + el.afterBrush && el.afterBrush(); + scope.prevEl = el; + el.__dirty = 0; + el.__isRendered = true; + } + function brushIncremental(ctx, el, scope) { + var displayables = el.getDisplayables(); + var temporalDisplayables = el.getTemporalDisplayables(); + ctx.save(); + var innerScope = { + prevElClipPaths: null, + prevEl: null, + allClipped: false, + viewWidth: scope.viewWidth, + viewHeight: scope.viewHeight, + inHover: scope.inHover + }; + var i; + var len; + for (i = el.getCursor(), len = displayables.length; i < len; i++) { + var displayable = displayables[i]; + displayable.beforeBrush && displayable.beforeBrush(); + displayable.innerBeforeBrush(); + brush(ctx, displayable, innerScope, i === len - 1); + displayable.innerAfterBrush(); + displayable.afterBrush && displayable.afterBrush(); + innerScope.prevEl = displayable; + } + for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) { + var displayable = temporalDisplayables[i_1]; + displayable.beforeBrush && displayable.beforeBrush(); + displayable.innerBeforeBrush(); + brush(ctx, displayable, innerScope, i_1 === len_1 - 1); + displayable.innerAfterBrush(); + displayable.afterBrush && displayable.afterBrush(); + innerScope.prevEl = displayable; + } + el.clearTemporalDisplayables(); + el.notClear = true; + ctx.restore(); + } + + var decalMap = new WeakMap(); + var decalCache = new LRU(100); + var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight']; + /** + * Create or update pattern image from decal options + * + * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal + * @return {Pattern} pattern with generated image, null if no decal + */ + function createOrUpdatePatternFromDecal(decalObject, api) { + if (decalObject === 'none') { + return null; + } + var dpr = api.getDevicePixelRatio(); + var zr = api.getZr(); + var isSVG = zr.painter.type === 'svg'; + if (decalObject.dirty) { + decalMap["delete"](decalObject); + } + var oldPattern = decalMap.get(decalObject); + if (oldPattern) { + return oldPattern; + } + var decalOpt = defaults(decalObject, { + symbol: 'rect', + symbolSize: 1, + symbolKeepAspect: true, + color: 'rgba(0, 0, 0, 0.2)', + backgroundColor: null, + dashArrayX: 5, + dashArrayY: 5, + rotation: 0, + maxTileWidth: 512, + maxTileHeight: 512 + }); + if (decalOpt.backgroundColor === 'none') { + decalOpt.backgroundColor = null; + } + var pattern = { + repeat: 'repeat' + }; + setPatternnSource(pattern); + pattern.rotation = decalOpt.rotation; + pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr; + decalMap.set(decalObject, pattern); + decalObject.dirty = false; + return pattern; + function setPatternnSource(pattern) { + var keys = [dpr]; + var isValidKey = true; + for (var i = 0; i < decalKeys.length; ++i) { + var value = decalOpt[decalKeys[i]]; + if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') { + isValidKey = false; + break; + } + keys.push(value); + } + var cacheKey; + if (isValidKey) { + cacheKey = keys.join(',') + (isSVG ? '-svg' : ''); + var cache = decalCache.get(cacheKey); + if (cache) { + isSVG ? pattern.svgElement = cache : pattern.image = cache; + } + } + var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX); + var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY); + var symbolArray = normalizeSymbolArray(decalOpt.symbol); + var lineBlockLengthsX = getLineBlockLengthX(dashArrayX); + var lineBlockLengthY = getLineBlockLengthY(dashArrayY); + var canvas = !isSVG && platformApi.createCanvas(); + var svgRoot = isSVG && { + tag: 'g', + attrs: {}, + key: 'dcl', + children: [] + }; + var pSize = getPatternSize(); + var ctx; + if (canvas) { + canvas.width = pSize.width * dpr; + canvas.height = pSize.height * dpr; + ctx = canvas.getContext('2d'); + } + brushDecal(); + if (isValidKey) { + decalCache.put(cacheKey, canvas || svgRoot); + } + pattern.image = canvas; + pattern.svgElement = svgRoot; + pattern.svgWidth = pSize.width; + pattern.svgHeight = pSize.height; + /** + * Get minimum length that can make a repeatable pattern. + * + * @return {Object} pattern width and height + */ + function getPatternSize() { + /** + * For example, if dash is [[3, 2], [2, 1]] for X, it looks like + * |--- --- --- --- --- ... + * |-- -- -- -- -- -- -- -- ... + * |--- --- --- --- --- ... + * |-- -- -- -- -- -- -- -- ... + * So the minimum length of X is 15, + * which is the least common multiple of `3 + 2` and `2 + 1` + * |--- --- --- |--- --- ... + * |-- -- -- -- -- |-- -- -- ... + */ + var width = 1; + for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) { + width = getLeastCommonMultiple(width, lineBlockLengthsX[i]); + } + var symbolRepeats = 1; + for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) { + symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length); + } + width *= symbolRepeats; + var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length; + if ("development" !== 'production') { + var warn = function (attrName) { + /* eslint-disable-next-line */ + console.warn("Calculated decal size is greater than " + attrName + " due to decal option settings so " + attrName + " is used for the decal size. Please consider changing the decal option to make a smaller decal or set " + attrName + " to be larger to avoid incontinuity."); + }; + if (width > decalOpt.maxTileWidth) { + warn('maxTileWidth'); + } + if (height > decalOpt.maxTileHeight) { + warn('maxTileHeight'); + } + } + return { + width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)), + height: Math.max(1, Math.min(height, decalOpt.maxTileHeight)) + }; + } + function brushDecal() { + if (ctx) { + ctx.clearRect(0, 0, canvas.width, canvas.height); + if (decalOpt.backgroundColor) { + ctx.fillStyle = decalOpt.backgroundColor; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + } + var ySum = 0; + for (var i = 0; i < dashArrayY.length; ++i) { + ySum += dashArrayY[i]; + } + if (ySum <= 0) { + // dashArrayY is 0, draw nothing + return; + } + var y = -lineBlockLengthY; + var yId = 0; + var yIdTotal = 0; + var xId0 = 0; + while (y < pSize.height) { + if (yId % 2 === 0) { + var symbolYId = yIdTotal / 2 % symbolArray.length; + var x = 0; + var xId1 = 0; + var xId1Total = 0; + while (x < pSize.width * 2) { + var xSum = 0; + for (var i = 0; i < dashArrayX[xId0].length; ++i) { + xSum += dashArrayX[xId0][i]; + } + if (xSum <= 0) { + // Skip empty line + break; + } + // E.g., [15, 5, 20, 5] draws only for 15 and 20 + if (xId1 % 2 === 0) { + var size = (1 - decalOpt.symbolSize) * 0.5; + var left = x + dashArrayX[xId0][xId1] * size; + var top_1 = y + dashArrayY[yId] * size; + var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize; + var height = dashArrayY[yId] * decalOpt.symbolSize; + var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length; + brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]); + } + x += dashArrayX[xId0][xId1]; + ++xId1Total; + ++xId1; + if (xId1 === dashArrayX[xId0].length) { + xId1 = 0; + } + } + ++xId0; + if (xId0 === dashArrayX.length) { + xId0 = 0; + } + } + y += dashArrayY[yId]; + ++yIdTotal; + ++yId; + if (yId === dashArrayY.length) { + yId = 0; + } + } + function brushSymbol(x, y, width, height, symbolType) { + var scale = isSVG ? 1 : dpr; + var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect); + if (isSVG) { + var symbolVNode = zr.painter.renderOneToVNode(symbol); + if (symbolVNode) { + svgRoot.children.push(symbolVNode); + } + } else { + // Paint to canvas for all other renderers. + brushSingle(ctx, symbol); + } + } + } + } + } + /** + * Convert symbol array into normalized array + * + * @param {string | (string | string[])[]} symbol symbol input + * @return {string[][]} normolized symbol array + */ + function normalizeSymbolArray(symbol) { + if (!symbol || symbol.length === 0) { + return [['rect']]; + } + if (isString(symbol)) { + return [[symbol]]; + } + var isAllString = true; + for (var i = 0; i < symbol.length; ++i) { + if (!isString(symbol[i])) { + isAllString = false; + break; + } + } + if (isAllString) { + return normalizeSymbolArray([symbol]); + } + var result = []; + for (var i = 0; i < symbol.length; ++i) { + if (isString(symbol[i])) { + result.push([symbol[i]]); + } else { + result.push(symbol[i]); + } + } + return result; + } + /** + * Convert dash input into dashArray + * + * @param {DecalDashArrayX} dash dash input + * @return {number[][]} normolized dash array + */ + function normalizeDashArrayX(dash) { + if (!dash || dash.length === 0) { + return [[0, 0]]; + } + if (isNumber(dash)) { + var dashValue = Math.ceil(dash); + return [[dashValue, dashValue]]; + } + /** + * [20, 5] should be normalized into [[20, 5]], + * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]] + */ + var isAllNumber = true; + for (var i = 0; i < dash.length; ++i) { + if (!isNumber(dash[i])) { + isAllNumber = false; + break; + } + } + if (isAllNumber) { + return normalizeDashArrayX([dash]); + } + var result = []; + for (var i = 0; i < dash.length; ++i) { + if (isNumber(dash[i])) { + var dashValue = Math.ceil(dash[i]); + result.push([dashValue, dashValue]); + } else { + var dashValue = map(dash[i], function (n) { + return Math.ceil(n); + }); + if (dashValue.length % 2 === 1) { + // [4, 2, 1] means |---- - -- |---- - -- | + // so normalize it to be [4, 2, 1, 4, 2, 1] + result.push(dashValue.concat(dashValue)); + } else { + result.push(dashValue); + } + } + } + return result; + } + /** + * Convert dash input into dashArray + * + * @param {DecalDashArrayY} dash dash input + * @return {number[]} normolized dash array + */ + function normalizeDashArrayY(dash) { + if (!dash || typeof dash === 'object' && dash.length === 0) { + return [0, 0]; + } + if (isNumber(dash)) { + var dashValue_1 = Math.ceil(dash); + return [dashValue_1, dashValue_1]; + } + var dashValue = map(dash, function (n) { + return Math.ceil(n); + }); + return dash.length % 2 ? dashValue.concat(dashValue) : dashValue; + } + /** + * Get block length of each line. A block is the length of dash line and space. + * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after + * that, so the block length of this line is 5. + * + * @param {number[][]} dash dash array of X or Y + * @return {number[]} block length of each line + */ + function getLineBlockLengthX(dash) { + return map(dash, function (line) { + return getLineBlockLengthY(line); + }); + } + function getLineBlockLengthY(dash) { + var blockLength = 0; + for (var i = 0; i < dash.length; ++i) { + blockLength += dash[i]; + } + if (dash.length % 2 === 1) { + // [4, 2, 1] means |---- - -- |---- - -- | + // So total length is (4 + 2 + 1) * 2 + return blockLength * 2; + } + return blockLength; + } + + function decalVisual(ecModel, api) { + ecModel.eachRawSeries(function (seriesModel) { + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + var data = seriesModel.getData(); + if (data.hasItemVisual()) { + data.each(function (idx) { + var decal = data.getItemVisual(idx, 'decal'); + if (decal) { + var itemStyle = data.ensureUniqueItemVisual(idx, 'style'); + itemStyle.decal = createOrUpdatePatternFromDecal(decal, api); + } + }); + } + var decal = data.getVisual('decal'); + if (decal) { + var style = data.getVisual('style'); + style.decal = createOrUpdatePatternFromDecal(decal, api); + } + }); + } + + var lifecycle = new Eventful(); + + // Implementation of exported APIs. For example registerMap, getMap. + // The implementations will be registered when installing the component. + // Avoid these code being bundled to the core module. + var implsStore = {}; + // TODO Type + function registerImpl(name, impl) { + if ("development" !== 'production') { + if (implsStore[name]) { + error("Already has an implementation of " + name + "."); + } + } + implsStore[name] = impl; + } + function getImpl(name) { + if ("development" !== 'production') { + if (!implsStore[name]) { + error("Implementation of " + name + " doesn't exists."); + } + } + return implsStore[name]; + } + + var version$1 = '5.5.0'; + var dependencies = { + zrender: '5.5.0' + }; + var TEST_FRAME_REMAIN_TIME = 1; + var PRIORITY_PROCESSOR_SERIES_FILTER = 800; + // Some data processors depends on the stack result dimension (to calculate data extent). + // So data stack stage should be in front of data processing stage. + var PRIORITY_PROCESSOR_DATASTACK = 900; + // "Data filter" will block the stream, so it should be + // put at the beginning of data processing. + var PRIORITY_PROCESSOR_FILTER = 1000; + var PRIORITY_PROCESSOR_DEFAULT = 2000; + var PRIORITY_PROCESSOR_STATISTIC = 5000; + var PRIORITY_VISUAL_LAYOUT = 1000; + var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100; + var PRIORITY_VISUAL_GLOBAL = 2000; + var PRIORITY_VISUAL_CHART = 3000; + var PRIORITY_VISUAL_COMPONENT = 4000; + // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to + // overwrite the viusal result of component (like `visualMap`) + // using data item specific setting (like itemStyle.xxx on data item) + var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; + // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on + // visual result like `symbolSize`. + var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600; + var PRIORITY_VISUAL_BRUSH = 5000; + var PRIORITY_VISUAL_ARIA = 6000; + var PRIORITY_VISUAL_DECAL = 7000; + var PRIORITY = { + PROCESSOR: { + FILTER: PRIORITY_PROCESSOR_FILTER, + SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER, + STATISTIC: PRIORITY_PROCESSOR_STATISTIC + }, + VISUAL: { + LAYOUT: PRIORITY_VISUAL_LAYOUT, + PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT, + GLOBAL: PRIORITY_VISUAL_GLOBAL, + CHART: PRIORITY_VISUAL_CHART, + POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT, + COMPONENT: PRIORITY_VISUAL_COMPONENT, + BRUSH: PRIORITY_VISUAL_BRUSH, + CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM, + ARIA: PRIORITY_VISUAL_ARIA, + DECAL: PRIORITY_VISUAL_DECAL + } + }; + // Main process have three entries: `setOption`, `dispatchAction` and `resize`, + // where they must not be invoked nestedly, except the only case: invoke + // dispatchAction with updateMethod "none" in main process. + // This flag is used to carry out this rule. + // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]). + var IN_MAIN_PROCESS_KEY = '__flagInMainProcess'; + var PENDING_UPDATE = '__pendingUpdate'; + var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus'; + var ACTION_REG = /^[a-zA-Z0-9_]+$/; + var CONNECT_STATUS_KEY = '__connectUpdateStatus'; + var CONNECT_STATUS_PENDING = 0; + var CONNECT_STATUS_UPDATING = 1; + var CONNECT_STATUS_UPDATED = 2; + function createRegisterEventWithLowercaseECharts(method) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + if (this.isDisposed()) { + disposedWarning(this.id); + return; + } + return toLowercaseNameAndCallEventful(this, method, args); + }; + } + function createRegisterEventWithLowercaseMessageCenter(method) { + return function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return toLowercaseNameAndCallEventful(this, method, args); + }; + } + function toLowercaseNameAndCallEventful(host, method, args) { + // `args[0]` is event name. Event name is all lowercase. + args[0] = args[0] && args[0].toLowerCase(); + return Eventful.prototype[method].apply(host, args); + } + var MessageCenter = /** @class */function (_super) { + __extends(MessageCenter, _super); + function MessageCenter() { + return _super !== null && _super.apply(this, arguments) || this; + } + return MessageCenter; + }(Eventful); + var messageCenterProto = MessageCenter.prototype; + messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on'); + messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); + // --------------------------------------- + // Internal method names for class ECharts + // --------------------------------------- + var prepare; + var prepareView; + var updateDirectly; + var updateMethods; + var doConvertPixel; + var updateStreamModes; + var doDispatchAction; + var flushPendingActions; + var triggerUpdatedEvent; + var bindRenderedEvent; + var bindMouseEvent; + var render; + var renderComponents; + var renderSeries; + var createExtensionAPI; + var enableConnect; + var markStatusToUpdate; + var applyChangedStates; + var ECharts = /** @class */function (_super) { + __extends(ECharts, _super); + function ECharts(dom, + // Theme name or themeOption. + theme, opts) { + var _this = _super.call(this, new ECEventProcessor()) || this; + _this._chartsViews = []; + _this._chartsMap = {}; + _this._componentsViews = []; + _this._componentsMap = {}; + // Can't dispatch action during rendering procedure + _this._pendingActions = []; + opts = opts || {}; + // Get theme by name + if (isString(theme)) { + theme = themeStorage[theme]; + } + _this._dom = dom; + var defaultRenderer = 'canvas'; + var defaultCoarsePointer = 'auto'; + var defaultUseDirtyRect = false; + if ("development" !== 'production') { + var root = /* eslint-disable-next-line */ + env.hasGlobalWindow ? window : global; + if (root) { + defaultRenderer = retrieve2(root.__ECHARTS__DEFAULT__RENDERER__, defaultRenderer); + defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer); + defaultUseDirtyRect = retrieve2(root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__, defaultUseDirtyRect); + } + } + if (opts.ssr) { + registerSSRDataGetter(function (el) { + var ecData = getECData(el); + var dataIndex = ecData.dataIndex; + if (dataIndex == null) { + return; + } + var hashMap = createHashMap(); + hashMap.set('series_index', ecData.seriesIndex); + hashMap.set('data_index', dataIndex); + ecData.ssrType && hashMap.set('ssr_type', ecData.ssrType); + return hashMap; + }); + } + var zr = _this._zr = init(dom, { + renderer: opts.renderer || defaultRenderer, + devicePixelRatio: opts.devicePixelRatio, + width: opts.width, + height: opts.height, + ssr: opts.ssr, + useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect), + useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer), + pointerSize: opts.pointerSize + }); + _this._ssr = opts.ssr; + // Expect 60 fps. + _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17); + theme = clone(theme); + theme && globalBackwardCompat(theme, true); + _this._theme = theme; + _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG); + _this._coordSysMgr = new CoordinateSystemManager(); + var api = _this._api = createExtensionAPI(_this); + // Sort on demand + function prioritySortFunc(a, b) { + return a.__prio - b.__prio; + } + sort(visualFuncs, prioritySortFunc); + sort(dataProcessorFuncs, prioritySortFunc); + _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs); + _this._messageCenter = new MessageCenter(); + // Init mouse events + _this._initEvents(); + // In case some people write `window.onresize = chart.resize` + _this.resize = bind(_this.resize, _this); + zr.animation.on('frame', _this._onframe, _this); + bindRenderedEvent(zr, _this); + bindMouseEvent(zr, _this); + // ECharts instance can be used as value. + setAsPrimitive(_this); + return _this; + } + ECharts.prototype._onframe = function () { + if (this._disposed) { + return; + } + applyChangedStates(this); + var scheduler = this._scheduler; + // Lazy update + if (this[PENDING_UPDATE]) { + var silent = this[PENDING_UPDATE].silent; + this[IN_MAIN_PROCESS_KEY] = true; + try { + prepare(this); + updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams); + } catch (e) { + this[IN_MAIN_PROCESS_KEY] = false; + this[PENDING_UPDATE] = null; + throw e; + } + // At present, in each frame, zrender performs: + // (1) animation step forward. + // (2) trigger('frame') (where this `_onframe` is called) + // (3) zrender flush (render). + // If we do nothing here, since we use `setToFinal: true`, the step (3) above + // will render the final state of the elements before the real animation started. + this._zr.flush(); + this[IN_MAIN_PROCESS_KEY] = false; + this[PENDING_UPDATE] = null; + flushPendingActions.call(this, silent); + triggerUpdatedEvent.call(this, silent); + } + // Avoid do both lazy update and progress in one frame. + else if (scheduler.unfinished) { + // Stream progress. + var remainTime = TEST_FRAME_REMAIN_TIME; + var ecModel = this._model; + var api = this._api; + scheduler.unfinished = false; + do { + var startTime = +new Date(); + scheduler.performSeriesTasks(ecModel); + // Currently dataProcessorFuncs do not check threshold. + scheduler.performDataProcessorTasks(ecModel); + updateStreamModes(this, ecModel); + // Do not update coordinate system here. Because that coord system update in + // each frame is not a good user experience. So we follow the rule that + // the extent of the coordinate system is determined in the first frame (the + // frame is executed immediately after task reset. + // this._coordSysMgr.update(ecModel, api); + // console.log('--- ec frame visual ---', remainTime); + scheduler.performVisualTasks(ecModel); + renderSeries(this, this._model, api, 'remain', {}); + remainTime -= +new Date() - startTime; + } while (remainTime > 0 && scheduler.unfinished); + // Call flush explicitly for trigger finished event. + if (!scheduler.unfinished) { + this._zr.flush(); + } + // Else, zr flushing be ensue within the same frame, + // because zr flushing is after onframe event. + } + }; + + ECharts.prototype.getDom = function () { + return this._dom; + }; + ECharts.prototype.getId = function () { + return this.id; + }; + ECharts.prototype.getZr = function () { + return this._zr; + }; + ECharts.prototype.isSSR = function () { + return this._ssr; + }; + /* eslint-disable-next-line */ + ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) { + if (this[IN_MAIN_PROCESS_KEY]) { + if ("development" !== 'production') { + error('`setOption` should not be called during main process.'); + } + return; + } + if (this._disposed) { + disposedWarning(this.id); + return; + } + var silent; + var replaceMerge; + var transitionOpt; + if (isObject(notMerge)) { + lazyUpdate = notMerge.lazyUpdate; + silent = notMerge.silent; + replaceMerge = notMerge.replaceMerge; + transitionOpt = notMerge.transition; + notMerge = notMerge.notMerge; + } + this[IN_MAIN_PROCESS_KEY] = true; + if (!this._model || notMerge) { + var optionManager = new OptionManager(this._api); + var theme = this._theme; + var ecModel = this._model = new GlobalModel(); + ecModel.scheduler = this._scheduler; + ecModel.ssr = this._ssr; + ecModel.init(null, null, null, theme, this._locale, optionManager); + } + this._model.setOption(option, { + replaceMerge: replaceMerge + }, optionPreprocessorFuncs); + var updateParams = { + seriesTransition: transitionOpt, + optionChanged: true + }; + if (lazyUpdate) { + this[PENDING_UPDATE] = { + silent: silent, + updateParams: updateParams + }; + this[IN_MAIN_PROCESS_KEY] = false; + // `setOption(option, {lazyMode: true})` may be called when zrender has been slept. + // It should wake it up to make sure zrender start to render at the next frame. + this.getZr().wakeUp(); + } else { + try { + prepare(this); + updateMethods.update.call(this, null, updateParams); + } catch (e) { + this[PENDING_UPDATE] = null; + this[IN_MAIN_PROCESS_KEY] = false; + throw e; + } + // Ensure zr refresh sychronously, and then pixel in canvas can be + // fetched after `setOption`. + if (!this._ssr) { + // not use flush when using ssr mode. + this._zr.flush(); + } + this[PENDING_UPDATE] = null; + this[IN_MAIN_PROCESS_KEY] = false; + flushPendingActions.call(this, silent); + triggerUpdatedEvent.call(this, silent); + } + }; + /** + * @deprecated + */ + ECharts.prototype.setTheme = function () { + deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); + }; + // We don't want developers to use getModel directly. + ECharts.prototype.getModel = function () { + return this._model; + }; + ECharts.prototype.getOption = function () { + return this._model && this._model.getOption(); + }; + ECharts.prototype.getWidth = function () { + return this._zr.getWidth(); + }; + ECharts.prototype.getHeight = function () { + return this._zr.getHeight(); + }; + ECharts.prototype.getDevicePixelRatio = function () { + return this._zr.painter.dpr + /* eslint-disable-next-line */ || env.hasGlobalWindow && window.devicePixelRatio || 1; + }; + /** + * Get canvas which has all thing rendered + * @deprecated Use renderToCanvas instead. + */ + ECharts.prototype.getRenderedCanvas = function (opts) { + if ("development" !== 'production') { + deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas'); + } + return this.renderToCanvas(opts); + }; + ECharts.prototype.renderToCanvas = function (opts) { + opts = opts || {}; + var painter = this._zr.painter; + if ("development" !== 'production') { + if (painter.type !== 'canvas') { + throw new Error('renderToCanvas can only be used in the canvas renderer.'); + } + } + return painter.getRenderedCanvas({ + backgroundColor: opts.backgroundColor || this._model.get('backgroundColor'), + pixelRatio: opts.pixelRatio || this.getDevicePixelRatio() + }); + }; + ECharts.prototype.renderToSVGString = function (opts) { + opts = opts || {}; + var painter = this._zr.painter; + if ("development" !== 'production') { + if (painter.type !== 'svg') { + throw new Error('renderToSVGString can only be used in the svg renderer.'); + } + } + return painter.renderToString({ + useViewBox: opts.useViewBox + }); + }; + /** + * Get svg data url + */ + ECharts.prototype.getSvgDataURL = function () { + if (!env.svgSupported) { + return; + } + var zr = this._zr; + var list = zr.storage.getDisplayList(); + // Stop animations + each(list, function (el) { + el.stopAnimation(null, true); + }); + return zr.painter.toDataURL(); + }; + ECharts.prototype.getDataURL = function (opts) { + if (this._disposed) { + disposedWarning(this.id); + return; + } + opts = opts || {}; + var excludeComponents = opts.excludeComponents; + var ecModel = this._model; + var excludesComponentViews = []; + var self = this; + each(excludeComponents, function (componentType) { + ecModel.eachComponent({ + mainType: componentType + }, function (component) { + var view = self._componentsMap[component.__viewId]; + if (!view.group.ignore) { + excludesComponentViews.push(view); + view.group.ignore = true; + } + }); + }); + var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.renderToCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png')); + each(excludesComponentViews, function (view) { + view.group.ignore = false; + }); + return url; + }; + ECharts.prototype.getConnectedDataURL = function (opts) { + if (this._disposed) { + disposedWarning(this.id); + return; + } + var isSvg = opts.type === 'svg'; + var groupId = this.group; + var mathMin = Math.min; + var mathMax = Math.max; + var MAX_NUMBER = Infinity; + if (connectedGroups[groupId]) { + var left_1 = MAX_NUMBER; + var top_1 = MAX_NUMBER; + var right_1 = -MAX_NUMBER; + var bottom_1 = -MAX_NUMBER; + var canvasList_1 = []; + var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio(); + each(instances$1, function (chart, id) { + if (chart.group === groupId) { + var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.renderToCanvas(clone(opts)); + var boundingRect = chart.getDom().getBoundingClientRect(); + left_1 = mathMin(boundingRect.left, left_1); + top_1 = mathMin(boundingRect.top, top_1); + right_1 = mathMax(boundingRect.right, right_1); + bottom_1 = mathMax(boundingRect.bottom, bottom_1); + canvasList_1.push({ + dom: canvas, + left: boundingRect.left, + top: boundingRect.top + }); + } + }); + left_1 *= dpr_1; + top_1 *= dpr_1; + right_1 *= dpr_1; + bottom_1 *= dpr_1; + var width = right_1 - left_1; + var height = bottom_1 - top_1; + var targetCanvas = platformApi.createCanvas(); + var zr_1 = init(targetCanvas, { + renderer: isSvg ? 'svg' : 'canvas' + }); + zr_1.resize({ + width: width, + height: height + }); + if (isSvg) { + var content_1 = ''; + each(canvasList_1, function (item) { + var x = item.left - left_1; + var y = item.top - top_1; + content_1 += '' + item.dom + ''; + }); + zr_1.painter.getSvgRoot().innerHTML = content_1; + if (opts.connectedBackgroundColor) { + zr_1.painter.setBackgroundColor(opts.connectedBackgroundColor); + } + zr_1.refreshImmediately(); + return zr_1.painter.toDataURL(); + } else { + // Background between the charts + if (opts.connectedBackgroundColor) { + zr_1.add(new Rect({ + shape: { + x: 0, + y: 0, + width: width, + height: height + }, + style: { + fill: opts.connectedBackgroundColor + } + })); + } + each(canvasList_1, function (item) { + var img = new ZRImage({ + style: { + x: item.left * dpr_1 - left_1, + y: item.top * dpr_1 - top_1, + image: item.dom + } + }); + zr_1.add(img); + }); + zr_1.refreshImmediately(); + return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png')); + } + } else { + return this.getDataURL(opts); + } + }; + ECharts.prototype.convertToPixel = function (finder, value) { + return doConvertPixel(this, 'convertToPixel', finder, value); + }; + ECharts.prototype.convertFromPixel = function (finder, value) { + return doConvertPixel(this, 'convertFromPixel', finder, value); + }; + /** + * Is the specified coordinate systems or components contain the given pixel point. + * @param {Array|number} value + * @return {boolean} result + */ + ECharts.prototype.containPixel = function (finder, value) { + if (this._disposed) { + disposedWarning(this.id); + return; + } + var ecModel = this._model; + var result; + var findResult = parseFinder(ecModel, finder); + each(findResult, function (models, key) { + key.indexOf('Models') >= 0 && each(models, function (model) { + var coordSys = model.coordinateSystem; + if (coordSys && coordSys.containPoint) { + result = result || !!coordSys.containPoint(value); + } else if (key === 'seriesModels') { + var view = this._chartsMap[model.__viewId]; + if (view && view.containPoint) { + result = result || view.containPoint(value, model); + } else { + if ("development" !== 'production') { + warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.')); + } + } + } else { + if ("development" !== 'production') { + warn(key + ': containPoint is not supported'); + } + } + }, this); + }, this); + return !!result; + }; + /** + * Get visual from series or data. + * @param finder + * If string, e.g., 'series', means {seriesIndex: 0}. + * If Object, could contain some of these properties below: + * { + * seriesIndex / seriesId / seriesName, + * dataIndex / dataIndexInside + * } + * If dataIndex is not specified, series visual will be fetched, + * but not data item visual. + * If all of seriesIndex, seriesId, seriesName are not specified, + * visual will be fetched from first series. + * @param visualType 'color', 'symbol', 'symbolSize' + */ + ECharts.prototype.getVisual = function (finder, visualType) { + var ecModel = this._model; + var parsedFinder = parseFinder(ecModel, finder, { + defaultMainType: 'series' + }); + var seriesModel = parsedFinder.seriesModel; + if ("development" !== 'production') { + if (!seriesModel) { + warn('There is no specified series model'); + } + } + var data = seriesModel.getData(); + var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null; + return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType); + }; + /** + * Get view of corresponding component model + */ + ECharts.prototype.getViewOfComponentModel = function (componentModel) { + return this._componentsMap[componentModel.__viewId]; + }; + /** + * Get view of corresponding series model + */ + ECharts.prototype.getViewOfSeriesModel = function (seriesModel) { + return this._chartsMap[seriesModel.__viewId]; + }; + ECharts.prototype._initEvents = function () { + var _this = this; + each(MOUSE_EVENT_NAMES, function (eveName) { + var handler = function (e) { + var ecModel = _this.getModel(); + var el = e.target; + var params; + var isGlobalOut = eveName === 'globalout'; + // no e.target when 'globalout'. + if (isGlobalOut) { + params = {}; + } else { + el && findEventDispatcher(el, function (parent) { + var ecData = getECData(parent); + if (ecData && ecData.dataIndex != null) { + var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex); + params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType, el) || {}; + return true; + } + // If element has custom eventData of components + else if (ecData.eventData) { + params = extend({}, ecData.eventData); + return true; + } + }, true); + } + // Contract: if params prepared in mouse event, + // these properties must be specified: + // { + // componentType: string (component main type) + // componentIndex: number + // } + // Otherwise event query can not work. + if (params) { + var componentType = params.componentType; + var componentIndex = params.componentIndex; + // Special handling for historic reason: when trigger by + // markLine/markPoint/markArea, the componentType is + // 'markLine'/'markPoint'/'markArea', but we should better + // enable them to be queried by seriesIndex, since their + // option is set in each series. + if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') { + componentType = 'series'; + componentIndex = params.seriesIndex; + } + var model = componentType && componentIndex != null && ecModel.getComponent(componentType, componentIndex); + var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]; + if ("development" !== 'production') { + // `event.componentType` and `event[componentTpype + 'Index']` must not + // be missed, otherwise there is no way to distinguish source component. + // See `dataFormat.getDataParams`. + if (!isGlobalOut && !(model && view)) { + warn('model or view can not be found by params'); + } + } + params.event = e; + params.type = eveName; + _this._$eventProcessor.eventInfo = { + targetEl: el, + packedEvent: params, + model: model, + view: view + }; + _this.trigger(eveName, params); + } + }; + // Consider that some component (like tooltip, brush, ...) + // register zr event handler, but user event handler might + // do anything, such as call `setOption` or `dispatchAction`, + // which probably update any of the content and probably + // cause problem if it is called previous other inner handlers. + handler.zrEventfulCallAtLast = true; + _this._zr.on(eveName, handler, _this); + }); + each(eventActionMap, function (actionType, eventType) { + _this._messageCenter.on(eventType, function (event) { + this.trigger(eventType, event); + }, _this); + }); + // Extra events + // TODO register? + each(['selectchanged'], function (eventType) { + _this._messageCenter.on(eventType, function (event) { + this.trigger(eventType, event); + }, _this); + }); + handleLegacySelectEvents(this._messageCenter, this, this._api); + }; + ECharts.prototype.isDisposed = function () { + return this._disposed; + }; + ECharts.prototype.clear = function () { + if (this._disposed) { + disposedWarning(this.id); + return; + } + this.setOption({ + series: [] + }, true); + }; + ECharts.prototype.dispose = function () { + if (this._disposed) { + disposedWarning(this.id); + return; + } + this._disposed = true; + var dom = this.getDom(); + if (dom) { + setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, ''); + } + var chart = this; + var api = chart._api; + var ecModel = chart._model; + each(chart._componentsViews, function (component) { + component.dispose(ecModel, api); + }); + each(chart._chartsViews, function (chart) { + chart.dispose(ecModel, api); + }); + // Dispose after all views disposed + chart._zr.dispose(); + // Set properties to null. + // To reduce the memory cost in case the top code still holds this instance unexpectedly. + chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null; + delete instances$1[chart.id]; + }; + /** + * Resize the chart + */ + ECharts.prototype.resize = function (opts) { + if (this[IN_MAIN_PROCESS_KEY]) { + if ("development" !== 'production') { + error('`resize` should not be called during main process.'); + } + return; + } + if (this._disposed) { + disposedWarning(this.id); + return; + } + this._zr.resize(opts); + var ecModel = this._model; + // Resize loading effect + this._loadingFX && this._loadingFX.resize(); + if (!ecModel) { + return; + } + var needPrepare = ecModel.resetOption('media'); + var silent = opts && opts.silent; + // There is some real cases that: + // chart.setOption(option, { lazyUpdate: true }); + // chart.resize(); + if (this[PENDING_UPDATE]) { + if (silent == null) { + silent = this[PENDING_UPDATE].silent; + } + needPrepare = true; + this[PENDING_UPDATE] = null; + } + this[IN_MAIN_PROCESS_KEY] = true; + try { + needPrepare && prepare(this); + updateMethods.update.call(this, { + type: 'resize', + animation: extend({ + // Disable animation + duration: 0 + }, opts && opts.animation) + }); + } catch (e) { + this[IN_MAIN_PROCESS_KEY] = false; + throw e; + } + this[IN_MAIN_PROCESS_KEY] = false; + flushPendingActions.call(this, silent); + triggerUpdatedEvent.call(this, silent); + }; + ECharts.prototype.showLoading = function (name, cfg) { + if (this._disposed) { + disposedWarning(this.id); + return; + } + if (isObject(name)) { + cfg = name; + name = ''; + } + name = name || 'default'; + this.hideLoading(); + if (!loadingEffects[name]) { + if ("development" !== 'production') { + warn('Loading effects ' + name + ' not exists.'); + } + return; + } + var el = loadingEffects[name](this._api, cfg); + var zr = this._zr; + this._loadingFX = el; + zr.add(el); + }; + /** + * Hide loading effect + */ + ECharts.prototype.hideLoading = function () { + if (this._disposed) { + disposedWarning(this.id); + return; + } + this._loadingFX && this._zr.remove(this._loadingFX); + this._loadingFX = null; + }; + ECharts.prototype.makeActionFromEvent = function (eventObj) { + var payload = extend({}, eventObj); + payload.type = eventActionMap[eventObj.type]; + return payload; + }; + /** + * @param opt If pass boolean, means opt.silent + * @param opt.silent Default `false`. Whether trigger events. + * @param opt.flush Default `undefined`. + * true: Flush immediately, and then pixel in canvas can be fetched + * immediately. Caution: it might affect performance. + * false: Not flush. + * undefined: Auto decide whether perform flush. + */ + ECharts.prototype.dispatchAction = function (payload, opt) { + if (this._disposed) { + disposedWarning(this.id); + return; + } + if (!isObject(opt)) { + opt = { + silent: !!opt + }; + } + if (!actions[payload.type]) { + return; + } + // Avoid dispatch action before setOption. Especially in `connect`. + if (!this._model) { + return; + } + // May dispatchAction in rendering procedure + if (this[IN_MAIN_PROCESS_KEY]) { + this._pendingActions.push(payload); + return; + } + var silent = opt.silent; + doDispatchAction.call(this, payload, silent); + var flush = opt.flush; + if (flush) { + this._zr.flush(); + } else if (flush !== false && env.browser.weChat) { + // In WeChat embedded browser, `requestAnimationFrame` and `setInterval` + // hang when sliding page (on touch event), which cause that zr does not + // refresh until user interaction finished, which is not expected. + // But `dispatchAction` may be called too frequently when pan on touch + // screen, which impacts performance if do not throttle them. + this._throttledZrFlush(); + } + flushPendingActions.call(this, silent); + triggerUpdatedEvent.call(this, silent); + }; + ECharts.prototype.updateLabelLayout = function () { + lifecycle.trigger('series:layoutlabels', this._model, this._api, { + // Not adding series labels. + // TODO + updatedSeries: [] + }); + }; + ECharts.prototype.appendData = function (params) { + if (this._disposed) { + disposedWarning(this.id); + return; + } + var seriesIndex = params.seriesIndex; + var ecModel = this.getModel(); + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + if ("development" !== 'production') { + assert(params.data && seriesModel); + } + seriesModel.appendData(params); + // Note: `appendData` does not support that update extent of coordinate + // system, util some scenario require that. In the expected usage of + // `appendData`, the initial extent of coordinate system should better + // be fixed by axis `min`/`max` setting or initial data, otherwise if + // the extent changed while `appendData`, the location of the painted + // graphic elements have to be changed, which make the usage of + // `appendData` meaningless. + this._scheduler.unfinished = true; + this.getZr().wakeUp(); + }; + // A work around for no `internal` modifier in ts yet but + // need to strictly hide private methods to JS users. + ECharts.internalField = function () { + prepare = function (ecIns) { + var scheduler = ecIns._scheduler; + scheduler.restorePipelines(ecIns._model); + scheduler.prepareStageTasks(); + prepareView(ecIns, true); + prepareView(ecIns, false); + scheduler.plan(); + }; + /** + * Prepare view instances of charts and components + */ + prepareView = function (ecIns, isComponent) { + var ecModel = ecIns._model; + var scheduler = ecIns._scheduler; + var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews; + var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap; + var zr = ecIns._zr; + var api = ecIns._api; + for (var i = 0; i < viewList.length; i++) { + viewList[i].__alive = false; + } + isComponent ? ecModel.eachComponent(function (componentType, model) { + componentType !== 'series' && doPrepare(model); + }) : ecModel.eachSeries(doPrepare); + function doPrepare(model) { + // By default view will be reused if possible for the case that `setOption` with "notMerge" + // mode and need to enable transition animation. (Usually, when they have the same id, or + // especially no id but have the same type & name & index. See the `model.id` generation + // rule in `makeIdAndName` and `viewId` generation rule here). + // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that + // the new model has nothing to do with the old model. + var requireNewView = model.__requireNewView; + // This command should not work twice. + model.__requireNewView = false; + // Consider: id same and type changed. + var viewId = '_ec_' + model.id + '_' + model.type; + var view = !requireNewView && viewMap[viewId]; + if (!view) { + var classType = parseClassType(model.type); + var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : + // FIXME:TS + // (ChartView as ChartViewConstructor).getClass('series', classType.sub) + // For backward compat, still support a chart type declared as only subType + // like "liquidfill", but recommend "series.liquidfill" + // But need a base class to make a type series. + ChartView.getClass(classType.sub); + if ("development" !== 'production') { + assert(Clazz, classType.sub + ' does not exist.'); + } + view = new Clazz(); + view.init(ecModel, api); + viewMap[viewId] = view; + viewList.push(view); + zr.add(view.group); + } + model.__viewId = view.__id = viewId; + view.__alive = true; + view.__model = model; + view.group.__ecComponentInfo = { + mainType: model.mainType, + index: model.componentIndex + }; + !isComponent && scheduler.prepareView(view, model, ecModel, api); + } + for (var i = 0; i < viewList.length;) { + var view = viewList[i]; + if (!view.__alive) { + !isComponent && view.renderTask.dispose(); + zr.remove(view.group); + view.dispose(ecModel, api); + viewList.splice(i, 1); + if (viewMap[view.__id] === view) { + delete viewMap[view.__id]; + } + view.__id = view.group.__ecComponentInfo = null; + } else { + i++; + } + } + }; + updateDirectly = function (ecIns, method, payload, mainType, subType) { + var ecModel = ecIns._model; + ecModel.setUpdatePayload(payload); + // broadcast + if (!mainType) { + // FIXME + // Chart will not be update directly here, except set dirty. + // But there is no such scenario now. + each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView); + return; + } + var query = {}; + query[mainType + 'Id'] = payload[mainType + 'Id']; + query[mainType + 'Index'] = payload[mainType + 'Index']; + query[mainType + 'Name'] = payload[mainType + 'Name']; + var condition = { + mainType: mainType, + query: query + }; + subType && (condition.subType = subType); // subType may be '' by parseClassType; + var excludeSeriesId = payload.excludeSeriesId; + var excludeSeriesIdMap; + if (excludeSeriesId != null) { + excludeSeriesIdMap = createHashMap(); + each(normalizeToArray(excludeSeriesId), function (id) { + var modelId = convertOptionIdName(id, null); + if (modelId != null) { + excludeSeriesIdMap.set(modelId, true); + } + }); + } + // If dispatchAction before setOption, do nothing. + ecModel && ecModel.eachComponent(condition, function (model) { + var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null; + if (isExcluded) { + return; + } + if (isHighDownPayload(payload)) { + if (model instanceof SeriesModel) { + if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur && !model.get(['emphasis', 'disabled'])) { + blurSeriesFromHighlightPayload(model, payload, ecIns._api); + } + } else { + var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api), + focusSelf = _a.focusSelf, + dispatchers = _a.dispatchers; + if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) { + blurComponent(model.mainType, model.componentIndex, ecIns._api); + } + // PENDING: + // Whether to put this "enter emphasis" code in `ComponentView`, + // which will be the same as `ChartView` but might be not necessary + // and will be far from this logic. + if (dispatchers) { + each(dispatchers, function (dispatcher) { + payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher); + }); + } + } + } else if (isSelectChangePayload(payload)) { + // TODO geo + if (model instanceof SeriesModel) { + toggleSelectionFromPayload(model, payload, ecIns._api); + updateSeriesElementSelection(model); + markStatusToUpdate(ecIns); + } + } + }, ecIns); + ecModel && ecModel.eachComponent(condition, function (model) { + var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) != null; + if (isExcluded) { + return; + } + callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]); + }, ecIns); + function callView(view) { + view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload); + } + }; + updateMethods = { + prepareAndUpdate: function (payload) { + prepare(this); + updateMethods.update.call(this, payload, { + // Needs to mark option changed if newOption is given. + // It's from MagicType. + // TODO If use a separate flag optionChanged in payload? + optionChanged: payload.newOption != null + }); + }, + update: function (payload, updateParams) { + var ecModel = this._model; + var api = this._api; + var zr = this._zr; + var coordSysMgr = this._coordSysMgr; + var scheduler = this._scheduler; + // update before setOption + if (!ecModel) { + return; + } + ecModel.setUpdatePayload(payload); + scheduler.restoreData(ecModel, payload); + scheduler.performSeriesTasks(ecModel); + // TODO + // Save total ecModel here for undo/redo (after restoring data and before processing data). + // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call. + // Create new coordinate system each update + // In LineView may save the old coordinate system and use it to get the original point. + coordSysMgr.create(ecModel, api); + scheduler.performDataProcessorTasks(ecModel, payload); + // Current stream render is not supported in data process. So we can update + // stream modes after data processing, where the filtered data is used to + // determine whether to use progressive rendering. + updateStreamModes(this, ecModel); + // We update stream modes before coordinate system updated, then the modes info + // can be fetched when coord sys updating (consider the barGrid extent fix). But + // the drawback is the full coord info can not be fetched. Fortunately this full + // coord is not required in stream mode updater currently. + coordSysMgr.update(ecModel, api); + clearColorPalette(ecModel); + scheduler.performVisualTasks(ecModel, payload); + render(this, ecModel, api, payload, updateParams); + // Set background + var backgroundColor = ecModel.get('backgroundColor') || 'transparent'; + var darkMode = ecModel.get('darkMode'); + zr.setBackgroundColor(backgroundColor); + // Force set dark mode. + if (darkMode != null && darkMode !== 'auto') { + zr.setDarkMode(darkMode); + } + lifecycle.trigger('afterupdate', ecModel, api); + }, + updateTransform: function (payload) { + var _this = this; + var ecModel = this._model; + var api = this._api; + // update before setOption + if (!ecModel) { + return; + } + ecModel.setUpdatePayload(payload); + // ChartView.markUpdateMethod(payload, 'updateTransform'); + var componentDirtyList = []; + ecModel.eachComponent(function (componentType, componentModel) { + if (componentType === 'series') { + return; + } + var componentView = _this.getViewOfComponentModel(componentModel); + if (componentView && componentView.__alive) { + if (componentView.updateTransform) { + var result = componentView.updateTransform(componentModel, ecModel, api, payload); + result && result.update && componentDirtyList.push(componentView); + } else { + componentDirtyList.push(componentView); + } + } + }); + var seriesDirtyMap = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + var chartView = _this._chartsMap[seriesModel.__viewId]; + if (chartView.updateTransform) { + var result = chartView.updateTransform(seriesModel, ecModel, api, payload); + result && result.update && seriesDirtyMap.set(seriesModel.uid, 1); + } else { + seriesDirtyMap.set(seriesModel.uid, 1); + } + }); + clearColorPalette(ecModel); + // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); + this._scheduler.performVisualTasks(ecModel, payload, { + setDirty: true, + dirtyMap: seriesDirtyMap + }); + // Currently, not call render of components. Geo render cost a lot. + // renderComponents(ecIns, ecModel, api, payload, componentDirtyList); + renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap); + lifecycle.trigger('afterupdate', ecModel, api); + }, + updateView: function (payload) { + var ecModel = this._model; + // update before setOption + if (!ecModel) { + return; + } + ecModel.setUpdatePayload(payload); + ChartView.markUpdateMethod(payload, 'updateView'); + clearColorPalette(ecModel); + // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + this._scheduler.performVisualTasks(ecModel, payload, { + setDirty: true + }); + render(this, ecModel, this._api, payload, {}); + lifecycle.trigger('afterupdate', ecModel, this._api); + }, + updateVisual: function (payload) { + // updateMethods.update.call(this, payload); + var _this = this; + var ecModel = this._model; + // update before setOption + if (!ecModel) { + return; + } + ecModel.setUpdatePayload(payload); + // clear all visual + ecModel.eachSeries(function (seriesModel) { + seriesModel.getData().clearAllVisual(); + }); + // Perform visual + ChartView.markUpdateMethod(payload, 'updateVisual'); + clearColorPalette(ecModel); + // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. + this._scheduler.performVisualTasks(ecModel, payload, { + visualType: 'visual', + setDirty: true + }); + ecModel.eachComponent(function (componentType, componentModel) { + if (componentType !== 'series') { + var componentView = _this.getViewOfComponentModel(componentModel); + componentView && componentView.__alive && componentView.updateVisual(componentModel, ecModel, _this._api, payload); + } + }); + ecModel.eachSeries(function (seriesModel) { + var chartView = _this._chartsMap[seriesModel.__viewId]; + chartView.updateVisual(seriesModel, ecModel, _this._api, payload); + }); + lifecycle.trigger('afterupdate', ecModel, this._api); + }, + updateLayout: function (payload) { + updateMethods.update.call(this, payload); + } + }; + doConvertPixel = function (ecIns, methodName, finder, value) { + if (ecIns._disposed) { + disposedWarning(ecIns.id); + return; + } + var ecModel = ecIns._model; + var coordSysList = ecIns._coordSysMgr.getCoordinateSystems(); + var result; + var parsedFinder = parseFinder(ecModel, finder); + for (var i = 0; i < coordSysList.length; i++) { + var coordSys = coordSysList[i]; + if (coordSys[methodName] && (result = coordSys[methodName](ecModel, parsedFinder, value)) != null) { + return result; + } + } + if ("development" !== 'production') { + warn('No coordinate system that supports ' + methodName + ' found by the given finder.'); + } + }; + updateStreamModes = function (ecIns, ecModel) { + var chartsMap = ecIns._chartsMap; + var scheduler = ecIns._scheduler; + ecModel.eachSeries(function (seriesModel) { + scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]); + }); + }; + doDispatchAction = function (payload, silent) { + var _this = this; + var ecModel = this.getModel(); + var payloadType = payload.type; + var escapeConnect = payload.escapeConnect; + var actionWrap = actions[payloadType]; + var actionInfo = actionWrap.actionInfo; + var cptTypeTmp = (actionInfo.update || 'update').split(':'); + var updateMethod = cptTypeTmp.pop(); + var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]); + this[IN_MAIN_PROCESS_KEY] = true; + var payloads = [payload]; + var batched = false; + // Batch action + if (payload.batch) { + batched = true; + payloads = map(payload.batch, function (item) { + item = defaults(extend({}, item), payload); + item.batch = null; + return item; + }); + } + var eventObjBatch = []; + var eventObj; + var isSelectChange = isSelectChangePayload(payload); + var isHighDown = isHighDownPayload(payload); + // Only leave blur once if there are multiple batches. + if (isHighDown) { + allLeaveBlur(this._api); + } + each(payloads, function (batchItem) { + // Action can specify the event by return it. + eventObj = actionWrap.action(batchItem, _this._model, _this._api); + // Emit event outside + eventObj = eventObj || extend({}, batchItem); + // Convert type to eventType + eventObj.type = actionInfo.event || eventObj.type; + eventObjBatch.push(eventObj); + // light update does not perform data process, layout and visual. + if (isHighDown) { + var _a = preParseFinder(payload), + queryOptionMap = _a.queryOptionMap, + mainTypeSpecified = _a.mainTypeSpecified; + var componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series'; + updateDirectly(_this, updateMethod, batchItem, componentMainType); + markStatusToUpdate(_this); + } else if (isSelectChange) { + // At present `dispatchAction({ type: 'select', ... })` is not supported on components. + // geo still use 'geoselect'. + updateDirectly(_this, updateMethod, batchItem, 'series'); + markStatusToUpdate(_this); + } else if (cptType) { + updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub); + } + }); + if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) { + try { + // Still dirty + if (this[PENDING_UPDATE]) { + prepare(this); + updateMethods.update.call(this, payload); + this[PENDING_UPDATE] = null; + } else { + updateMethods[updateMethod].call(this, payload); + } + } catch (e) { + this[IN_MAIN_PROCESS_KEY] = false; + throw e; + } + } + // Follow the rule of action batch + if (batched) { + eventObj = { + type: actionInfo.event || payloadType, + escapeConnect: escapeConnect, + batch: eventObjBatch + }; + } else { + eventObj = eventObjBatch[0]; + } + this[IN_MAIN_PROCESS_KEY] = false; + if (!silent) { + var messageCenter = this._messageCenter; + messageCenter.trigger(eventObj.type, eventObj); + // Extra triggered 'selectchanged' event + if (isSelectChange) { + var newObj = { + type: 'selectchanged', + escapeConnect: escapeConnect, + selected: getAllSelectedIndices(ecModel), + isFromClick: payload.isFromClick || false, + fromAction: payload.type, + fromActionPayload: payload + }; + messageCenter.trigger(newObj.type, newObj); + } + } + }; + flushPendingActions = function (silent) { + var pendingActions = this._pendingActions; + while (pendingActions.length) { + var payload = pendingActions.shift(); + doDispatchAction.call(this, payload, silent); + } + }; + triggerUpdatedEvent = function (silent) { + !silent && this.trigger('updated'); + }; + /** + * Event `rendered` is triggered when zr + * rendered. It is useful for realtime + * snapshot (reflect animation). + * + * Event `finished` is triggered when: + * (1) zrender rendering finished. + * (2) initial animation finished. + * (3) progressive rendering finished. + * (4) no pending action. + * (5) no delayed setOption needs to be processed. + */ + bindRenderedEvent = function (zr, ecIns) { + zr.on('rendered', function (params) { + ecIns.trigger('rendered', params); + // The `finished` event should not be triggered repeatedly, + // so it should only be triggered when rendering indeed happens + // in zrender. (Consider the case that dipatchAction is keep + // triggering when mouse move). + if ( + // Although zr is dirty if initial animation is not finished + // and this checking is called on frame, we also check + // animation finished for robustness. + zr.animation.isFinished() && !ecIns[PENDING_UPDATE] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) { + ecIns.trigger('finished'); + } + }); + }; + bindMouseEvent = function (zr, ecIns) { + zr.on('mouseover', function (e) { + var el = e.target; + var dispatcher = findEventDispatcher(el, isHighDownDispatcher); + if (dispatcher) { + handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api); + markStatusToUpdate(ecIns); + } + }).on('mouseout', function (e) { + var el = e.target; + var dispatcher = findEventDispatcher(el, isHighDownDispatcher); + if (dispatcher) { + handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api); + markStatusToUpdate(ecIns); + } + }).on('click', function (e) { + var el = e.target; + var dispatcher = findEventDispatcher(el, function (target) { + return getECData(target).dataIndex != null; + }, true); + if (dispatcher) { + var actionType = dispatcher.selected ? 'unselect' : 'select'; + var ecData = getECData(dispatcher); + ecIns._api.dispatchAction({ + type: actionType, + dataType: ecData.dataType, + dataIndexInside: ecData.dataIndex, + seriesIndex: ecData.seriesIndex, + isFromClick: true + }); + } + }); + }; + function clearColorPalette(ecModel) { + ecModel.clearColorPalette(); + ecModel.eachSeries(function (seriesModel) { + seriesModel.clearColorPalette(); + }); + } + // Allocate zlevels for series and components + function allocateZlevels(ecModel) { + var componentZLevels = []; + var seriesZLevels = []; + var hasSeparateZLevel = false; + ecModel.eachComponent(function (componentType, componentModel) { + var zlevel = componentModel.get('zlevel') || 0; + var z = componentModel.get('z') || 0; + var zlevelKey = componentModel.getZLevelKey(); + hasSeparateZLevel = hasSeparateZLevel || !!zlevelKey; + (componentType === 'series' ? seriesZLevels : componentZLevels).push({ + zlevel: zlevel, + z: z, + idx: componentModel.componentIndex, + type: componentType, + key: zlevelKey + }); + }); + if (hasSeparateZLevel) { + // Series after component + var zLevels = componentZLevels.concat(seriesZLevels); + var lastSeriesZLevel_1; + var lastSeriesKey_1; + sort(zLevels, function (a, b) { + if (a.zlevel === b.zlevel) { + return a.z - b.z; + } + return a.zlevel - b.zlevel; + }); + each(zLevels, function (item) { + var componentModel = ecModel.getComponent(item.type, item.idx); + var zlevel = item.zlevel; + var key = item.key; + if (lastSeriesZLevel_1 != null) { + zlevel = Math.max(lastSeriesZLevel_1, zlevel); + } + if (key) { + if (zlevel === lastSeriesZLevel_1 && key !== lastSeriesKey_1) { + zlevel++; + } + lastSeriesKey_1 = key; + } else if (lastSeriesKey_1) { + if (zlevel === lastSeriesZLevel_1) { + zlevel++; + } + lastSeriesKey_1 = ''; + } + lastSeriesZLevel_1 = zlevel; + componentModel.setZLevel(zlevel); + }); + } + } + render = function (ecIns, ecModel, api, payload, updateParams) { + allocateZlevels(ecModel); + renderComponents(ecIns, ecModel, api, payload, updateParams); + each(ecIns._chartsViews, function (chart) { + chart.__alive = false; + }); + renderSeries(ecIns, ecModel, api, payload, updateParams); + // Remove groups of unrendered charts + each(ecIns._chartsViews, function (chart) { + if (!chart.__alive) { + chart.remove(ecModel, api); + } + }); + }; + renderComponents = function (ecIns, ecModel, api, payload, updateParams, dirtyList) { + each(dirtyList || ecIns._componentsViews, function (componentView) { + var componentModel = componentView.__model; + clearStates(componentModel, componentView); + componentView.render(componentModel, ecModel, api, payload); + updateZ(componentModel, componentView); + updateStates(componentModel, componentView); + }); + }; + /** + * Render each chart and component + */ + renderSeries = function (ecIns, ecModel, api, payload, updateParams, dirtyMap) { + // Render all charts + var scheduler = ecIns._scheduler; + updateParams = extend(updateParams || {}, { + updatedSeries: ecModel.getSeries() + }); + // TODO progressive? + lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams); + var unfinished = false; + ecModel.eachSeries(function (seriesModel) { + var chartView = ecIns._chartsMap[seriesModel.__viewId]; + chartView.__alive = true; + var renderTask = chartView.renderTask; + scheduler.updatePayload(renderTask, payload); + // TODO states on marker. + clearStates(seriesModel, chartView); + if (dirtyMap && dirtyMap.get(seriesModel.uid)) { + renderTask.dirty(); + } + if (renderTask.perform(scheduler.getPerformArgs(renderTask))) { + unfinished = true; + } + chartView.group.silent = !!seriesModel.get('silent'); + // Should not call markRedraw on group, because it will disable zrender + // incremental render (always render from the __startIndex each frame) + // chartView.group.markRedraw(); + updateBlend(seriesModel, chartView); + updateSeriesElementSelection(seriesModel); + }); + scheduler.unfinished = unfinished || scheduler.unfinished; + lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); + // transition after label is layouted. + lifecycle.trigger('series:transition', ecModel, api, updateParams); + ecModel.eachSeries(function (seriesModel) { + var chartView = ecIns._chartsMap[seriesModel.__viewId]; + // Update Z after labels updated. Before applying states. + updateZ(seriesModel, chartView); + // NOTE: Update states after label is updated. + // label should be in normal status when layouting. + updateStates(seriesModel, chartView); + }); + // If use hover layer + updateHoverLayerStatus(ecIns, ecModel); + lifecycle.trigger('series:afterupdate', ecModel, api, updateParams); + }; + markStatusToUpdate = function (ecIns) { + ecIns[STATUS_NEEDS_UPDATE_KEY] = true; + // Wake up zrender if it's sleep. Let it update states in the next frame. + ecIns.getZr().wakeUp(); + }; + applyChangedStates = function (ecIns) { + if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) { + return; + } + ecIns.getZr().storage.traverse(function (el) { + // Not applied on removed elements, it may still in fading. + if (isElementRemoved(el)) { + return; + } + applyElementStates(el); + }); + ecIns[STATUS_NEEDS_UPDATE_KEY] = false; + }; + function applyElementStates(el) { + var newStates = []; + var oldStates = el.currentStates; + // Keep other states. + for (var i = 0; i < oldStates.length; i++) { + var stateName = oldStates[i]; + if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) { + newStates.push(stateName); + } + } + // Only use states when it's exists. + if (el.selected && el.states.select) { + newStates.push('select'); + } + if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) { + newStates.push('emphasis'); + } else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) { + newStates.push('blur'); + } + el.useStates(newStates); + } + function updateHoverLayerStatus(ecIns, ecModel) { + var zr = ecIns._zr; + var storage = zr.storage; + var elCount = 0; + storage.traverse(function (el) { + if (!el.isGroup) { + elCount++; + } + }); + if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) { + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.preventUsingHoverLayer) { + return; + } + var chartView = ecIns._chartsMap[seriesModel.__viewId]; + if (chartView.__alive) { + chartView.eachRendered(function (el) { + if (el.states.emphasis) { + el.states.emphasis.hoverLayer = true; + } + }); + } + }); + } + } + /** + * Update chart and blend. + */ + function updateBlend(seriesModel, chartView) { + var blendMode = seriesModel.get('blendMode') || null; + chartView.eachRendered(function (el) { + // FIXME marker and other components + if (!el.isGroup) { + // DON'T mark the element dirty. In case element is incremental and don't want to rerender. + el.style.blend = blendMode; + } + }); + } + function updateZ(model, view) { + if (model.preventAutoZ) { + return; + } + var z = model.get('z') || 0; + var zlevel = model.get('zlevel') || 0; + // Set z and zlevel + view.eachRendered(function (el) { + doUpdateZ(el, z, zlevel, -Infinity); + // Don't traverse the children because it has been traversed in _updateZ. + return true; + }); + } + function doUpdateZ(el, z, zlevel, maxZ2) { + // Group may also have textContent + var label = el.getTextContent(); + var labelLine = el.getTextGuideLine(); + var isGroup = el.isGroup; + if (isGroup) { + // set z & zlevel of children elements of Group + var children = el.childrenRef(); + for (var i = 0; i < children.length; i++) { + maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2); + } + } else { + // not Group + el.z = z; + el.zlevel = zlevel; + maxZ2 = Math.max(el.z2, maxZ2); + } + // always set z and zlevel if label/labelLine exists + if (label) { + label.z = z; + label.zlevel = zlevel; + // lift z2 of text content + // TODO if el.emphasis.z2 is spcefied, what about textContent. + isFinite(maxZ2) && (label.z2 = maxZ2 + 2); + } + if (labelLine) { + var textGuideLineConfig = el.textGuideLineConfig; + labelLine.z = z; + labelLine.zlevel = zlevel; + isFinite(maxZ2) && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1)); + } + return maxZ2; + } + // Clear states without animation. + // TODO States on component. + function clearStates(model, view) { + view.eachRendered(function (el) { + // Not applied on removed elements, it may still in fading. + if (isElementRemoved(el)) { + return; + } + var textContent = el.getTextContent(); + var textGuide = el.getTextGuideLine(); + if (el.stateTransition) { + el.stateTransition = null; + } + if (textContent && textContent.stateTransition) { + textContent.stateTransition = null; + } + if (textGuide && textGuide.stateTransition) { + textGuide.stateTransition = null; + } + // TODO If el is incremental. + if (el.hasState()) { + el.prevStates = el.currentStates; + el.clearStates(); + } else if (el.prevStates) { + el.prevStates = null; + } + }); + } + function updateStates(model, view) { + var stateAnimationModel = model.getModel('stateAnimation'); + var enableAnimation = model.isAnimationEnabled(); + var duration = stateAnimationModel.get('duration'); + var stateTransition = duration > 0 ? { + duration: duration, + delay: stateAnimationModel.get('delay'), + easing: stateAnimationModel.get('easing') + // additive: stateAnimationModel.get('additive') + } : null; + view.eachRendered(function (el) { + if (el.states && el.states.emphasis) { + // Not applied on removed elements, it may still in fading. + if (isElementRemoved(el)) { + return; + } + if (el instanceof Path) { + savePathStates(el); + } + // Only updated on changed element. In case element is incremental and don't want to rerender. + // TODO, a more proper way? + if (el.__dirty) { + var prevStates = el.prevStates; + // Restore states without animation + if (prevStates) { + el.useStates(prevStates); + } + } + // Update state transition and enable animation again. + if (enableAnimation) { + el.stateTransition = stateTransition; + var textContent = el.getTextContent(); + var textGuide = el.getTextGuideLine(); + // TODO Is it necessary to animate label? + if (textContent) { + textContent.stateTransition = stateTransition; + } + if (textGuide) { + textGuide.stateTransition = stateTransition; + } + } + // Use highlighted and selected flag to toggle states. + if (el.__dirty) { + applyElementStates(el); + } + } + }); + } + createExtensionAPI = function (ecIns) { + return new ( /** @class */function (_super) { + __extends(class_1, _super); + function class_1() { + return _super !== null && _super.apply(this, arguments) || this; + } + class_1.prototype.getCoordinateSystems = function () { + return ecIns._coordSysMgr.getCoordinateSystems(); + }; + class_1.prototype.getComponentByElement = function (el) { + while (el) { + var modelInfo = el.__ecComponentInfo; + if (modelInfo != null) { + return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index); + } + el = el.parent; + } + }; + class_1.prototype.enterEmphasis = function (el, highlightDigit) { + enterEmphasis(el, highlightDigit); + markStatusToUpdate(ecIns); + }; + class_1.prototype.leaveEmphasis = function (el, highlightDigit) { + leaveEmphasis(el, highlightDigit); + markStatusToUpdate(ecIns); + }; + class_1.prototype.enterBlur = function (el) { + enterBlur(el); + markStatusToUpdate(ecIns); + }; + class_1.prototype.leaveBlur = function (el) { + leaveBlur(el); + markStatusToUpdate(ecIns); + }; + class_1.prototype.enterSelect = function (el) { + enterSelect(el); + markStatusToUpdate(ecIns); + }; + class_1.prototype.leaveSelect = function (el) { + leaveSelect(el); + markStatusToUpdate(ecIns); + }; + class_1.prototype.getModel = function () { + return ecIns.getModel(); + }; + class_1.prototype.getViewOfComponentModel = function (componentModel) { + return ecIns.getViewOfComponentModel(componentModel); + }; + class_1.prototype.getViewOfSeriesModel = function (seriesModel) { + return ecIns.getViewOfSeriesModel(seriesModel); + }; + return class_1; + }(ExtensionAPI))(ecIns); + }; + enableConnect = function (chart) { + function updateConnectedChartsStatus(charts, status) { + for (var i = 0; i < charts.length; i++) { + var otherChart = charts[i]; + otherChart[CONNECT_STATUS_KEY] = status; + } + } + each(eventActionMap, function (actionType, eventType) { + chart._messageCenter.on(eventType, function (event) { + if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) { + if (event && event.escapeConnect) { + return; + } + var action_1 = chart.makeActionFromEvent(event); + var otherCharts_1 = []; + each(instances$1, function (otherChart) { + if (otherChart !== chart && otherChart.group === chart.group) { + otherCharts_1.push(otherChart); + } + }); + updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_PENDING); + each(otherCharts_1, function (otherChart) { + if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) { + otherChart.dispatchAction(action_1); + } + }); + updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_UPDATED); + } + }); + }); + }; + }(); + return ECharts; + }(Eventful); + var echartsProto = ECharts.prototype; + echartsProto.on = createRegisterEventWithLowercaseECharts('on'); + echartsProto.off = createRegisterEventWithLowercaseECharts('off'); + /** + * @deprecated + */ + // @ts-ignore + echartsProto.one = function (eventName, cb, ctx) { + var self = this; + deprecateLog('ECharts#one is deprecated.'); + function wrapped() { + var args2 = []; + for (var _i = 0; _i < arguments.length; _i++) { + args2[_i] = arguments[_i]; + } + cb && cb.apply && cb.apply(this, args2); + // @ts-ignore + self.off(eventName, wrapped); + } + // @ts-ignore + this.on.call(this, eventName, wrapped, ctx); + }; + var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu']; + function disposedWarning(id) { + if ("development" !== 'production') { + warn('Instance ' + id + ' has been disposed'); + } + } + var actions = {}; + /** + * Map eventType to actionType + */ + var eventActionMap = {}; + var dataProcessorFuncs = []; + var optionPreprocessorFuncs = []; + var visualFuncs = []; + var themeStorage = {}; + var loadingEffects = {}; + var instances$1 = {}; + var connectedGroups = {}; + var idBase = +new Date() - 0; + var groupIdBase = +new Date() - 0; + var DOM_ATTRIBUTE_KEY = '_echarts_instance_'; + /** + * @param opts.devicePixelRatio Use window.devicePixelRatio by default + * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart. + * @param opts.width Use clientWidth of the input `dom` by default. + * Can be 'auto' (the same as null/undefined) + * @param opts.height Use clientHeight of the input `dom` by default. + * Can be 'auto' (the same as null/undefined) + * @param opts.locale Specify the locale. + * @param opts.useDirtyRect Enable dirty rectangle rendering or not. + */ + function init$1(dom, theme, opts) { + var isClient = !(opts && opts.ssr); + if (isClient) { + if ("development" !== 'production') { + if (!dom) { + throw new Error('Initialize failed: invalid dom.'); + } + } + var existInstance = getInstanceByDom(dom); + if (existInstance) { + if ("development" !== 'production') { + warn('There is a chart instance already initialized on the dom.'); + } + return existInstance; + } + if ("development" !== 'production') { + if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) { + warn('Can\'t get DOM width or height. Please check ' + 'dom.clientWidth and dom.clientHeight. They should not be 0.' + 'For example, you may need to call this in the callback ' + 'of window.onload.'); + } + } + } + var chart = new ECharts(dom, theme, opts); + chart.id = 'ec_' + idBase++; + instances$1[chart.id] = chart; + isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id); + enableConnect(chart); + lifecycle.trigger('afterinit', chart); + return chart; + } + /** + * @usage + * (A) + * ```js + * let chart1 = echarts.init(dom1); + * let chart2 = echarts.init(dom2); + * chart1.group = 'xxx'; + * chart2.group = 'xxx'; + * echarts.connect('xxx'); + * ``` + * (B) + * ```js + * let chart1 = echarts.init(dom1); + * let chart2 = echarts.init(dom2); + * echarts.connect('xxx', [chart1, chart2]); + * ``` + */ + function connect(groupId) { + // Is array of charts + if (isArray(groupId)) { + var charts = groupId; + groupId = null; + // If any chart has group + each(charts, function (chart) { + if (chart.group != null) { + groupId = chart.group; + } + }); + groupId = groupId || 'g_' + groupIdBase++; + each(charts, function (chart) { + chart.group = groupId; + }); + } + connectedGroups[groupId] = true; + return groupId; + } + function disconnect(groupId) { + connectedGroups[groupId] = false; + } + /** + * Alias and backward compatibility + * @deprecated + */ + var disConnect = disconnect; + /** + * Dispose a chart instance + */ + function dispose$1(chart) { + if (isString(chart)) { + chart = instances$1[chart]; + } else if (!(chart instanceof ECharts)) { + // Try to treat as dom + chart = getInstanceByDom(chart); + } + if (chart instanceof ECharts && !chart.isDisposed()) { + chart.dispose(); + } + } + function getInstanceByDom(dom) { + return instances$1[getAttribute(dom, DOM_ATTRIBUTE_KEY)]; + } + function getInstanceById(key) { + return instances$1[key]; + } + /** + * Register theme + */ + function registerTheme(name, theme) { + themeStorage[name] = theme; + } + /** + * Register option preprocessor + */ + function registerPreprocessor(preprocessorFunc) { + if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) { + optionPreprocessorFuncs.push(preprocessorFunc); + } + } + function registerProcessor(priority, processor) { + normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT); + } + /** + * Register postIniter + * @param {Function} postInitFunc + */ + function registerPostInit(postInitFunc) { + registerUpdateLifecycle('afterinit', postInitFunc); + } + /** + * Register postUpdater + * @param {Function} postUpdateFunc + */ + function registerPostUpdate(postUpdateFunc) { + registerUpdateLifecycle('afterupdate', postUpdateFunc); + } + function registerUpdateLifecycle(name, cb) { + lifecycle.on(name, cb); + } + function registerAction(actionInfo, eventName, action) { + if (isFunction(eventName)) { + action = eventName; + eventName = ''; + } + var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = { + event: eventName + }][0]; + // Event name is all lowercase + actionInfo.event = (actionInfo.event || actionType).toLowerCase(); + eventName = actionInfo.event; + if (eventActionMap[eventName]) { + // Already registered. + return; + } + // Validate action type and event name. + assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName)); + if (!actions[actionType]) { + actions[actionType] = { + action: action, + actionInfo: actionInfo + }; + } + eventActionMap[eventName] = actionType; + } + function registerCoordinateSystem(type, coordSysCreator) { + CoordinateSystemManager.register(type, coordSysCreator); + } + /** + * Get dimensions of specified coordinate system. + * @param {string} type + * @return {Array.} + */ + function getCoordinateSystemDimensions(type) { + var coordSysCreator = CoordinateSystemManager.get(type); + if (coordSysCreator) { + return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice(); + } + } + function registerLayout(priority, layoutTask) { + normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout'); + } + function registerVisual(priority, visualTask) { + normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual'); + } + var registeredTasks = []; + function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) { + if (isFunction(priority) || isObject(priority)) { + fn = priority; + priority = defaultPriority; + } + if ("development" !== 'production') { + if (isNaN(priority) || priority == null) { + throw new Error('Illegal priority'); + } + // Check duplicate + each(targetList, function (wrap) { + assert(wrap.__raw !== fn); + }); + } + // Already registered + if (indexOf(registeredTasks, fn) >= 0) { + return; + } + registeredTasks.push(fn); + var stageHandler = Scheduler.wrapStageHandler(fn, visualType); + stageHandler.__prio = priority; + stageHandler.__raw = fn; + targetList.push(stageHandler); + } + function registerLoading(name, loadingFx) { + loadingEffects[name] = loadingFx; + } + /** + * ZRender need a canvas context to do measureText. + * But in node environment canvas may be created by node-canvas. + * So we need to specify how to create a canvas instead of using document.createElement('canvas') + * + * + * @deprecated use setPlatformAPI({ createCanvas }) instead. + * + * @example + * let Canvas = require('canvas'); + * let echarts = require('echarts'); + * echarts.setCanvasCreator(function () { + * // Small size is enough. + * return new Canvas(32, 32); + * }); + */ + function setCanvasCreator(creator) { + if ("development" !== 'production') { + deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.'); + } + setPlatformAPI({ + createCanvas: creator + }); + } + /** + * The parameters and usage: see `geoSourceManager.registerMap`. + * Compatible with previous `echarts.registerMap`. + */ + function registerMap(mapName, geoJson, specialAreas) { + var registerMap = getImpl('registerMap'); + registerMap && registerMap(mapName, geoJson, specialAreas); + } + function getMap(mapName) { + var getMap = getImpl('getMap'); + return getMap && getMap(mapName); + } + var registerTransform = registerExternalTransform; + /** + * Globa dispatchAction to a specified chart instance. + */ + // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters[1]) { + // if (!payload || !payload.chartId) { + // // Must have chartId to find chart + // return; + // } + // const chart = instances[payload.chartId]; + // if (chart) { + // chart.dispatchAction(payload, opt); + // } + // } + // Builtin global visual + registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask); + registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask); + registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask); + registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask); + registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask); + registerVisual(PRIORITY_VISUAL_DECAL, decalVisual); + registerPreprocessor(globalBackwardCompat); + registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack); + registerLoading('default', defaultLoading); + // Default actions + registerAction({ + type: HIGHLIGHT_ACTION_TYPE, + event: HIGHLIGHT_ACTION_TYPE, + update: HIGHLIGHT_ACTION_TYPE + }, noop); + registerAction({ + type: DOWNPLAY_ACTION_TYPE, + event: DOWNPLAY_ACTION_TYPE, + update: DOWNPLAY_ACTION_TYPE + }, noop); + registerAction({ + type: SELECT_ACTION_TYPE, + event: SELECT_ACTION_TYPE, + update: SELECT_ACTION_TYPE + }, noop); + registerAction({ + type: UNSELECT_ACTION_TYPE, + event: UNSELECT_ACTION_TYPE, + update: UNSELECT_ACTION_TYPE + }, noop); + registerAction({ + type: TOGGLE_SELECT_ACTION_TYPE, + event: TOGGLE_SELECT_ACTION_TYPE, + update: TOGGLE_SELECT_ACTION_TYPE + }, noop); + // Default theme + registerTheme('light', lightTheme); + registerTheme('dark', theme); + // For backward compatibility, where the namespace `dataTool` will + // be mounted on `echarts` is the extension `dataTool` is imported. + var dataTool = {}; + + var extensions = []; + var extensionRegisters = { + registerPreprocessor: registerPreprocessor, + registerProcessor: registerProcessor, + registerPostInit: registerPostInit, + registerPostUpdate: registerPostUpdate, + registerUpdateLifecycle: registerUpdateLifecycle, + registerAction: registerAction, + registerCoordinateSystem: registerCoordinateSystem, + registerLayout: registerLayout, + registerVisual: registerVisual, + registerTransform: registerTransform, + registerLoading: registerLoading, + registerMap: registerMap, + registerImpl: registerImpl, + PRIORITY: PRIORITY, + ComponentModel: ComponentModel, + ComponentView: ComponentView, + SeriesModel: SeriesModel, + ChartView: ChartView, + // TODO Use ComponentModel and SeriesModel instead of Constructor + registerComponentModel: function (ComponentModelClass) { + ComponentModel.registerClass(ComponentModelClass); + }, + registerComponentView: function (ComponentViewClass) { + ComponentView.registerClass(ComponentViewClass); + }, + registerSeriesModel: function (SeriesModelClass) { + SeriesModel.registerClass(SeriesModelClass); + }, + registerChartView: function (ChartViewClass) { + ChartView.registerClass(ChartViewClass); + }, + registerSubTypeDefaulter: function (componentType, defaulter) { + ComponentModel.registerSubTypeDefaulter(componentType, defaulter); + }, + registerPainter: function (painterType, PainterCtor) { + registerPainter(painterType, PainterCtor); + } + }; + function use(ext) { + if (isArray(ext)) { + // use([ChartLine, ChartBar]); + each(ext, function (singleExt) { + use(singleExt); + }); + return; + } + if (indexOf(extensions, ext) >= 0) { + return; + } + extensions.push(ext); + if (isFunction(ext)) { + ext = { + install: ext + }; + } + ext.install(extensionRegisters); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) { + return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1; + } + function defaultKeyGetter(item) { + return item; + } + var DataDiffer = /** @class */function () { + /** + * @param context Can be visited by this.context in callback. + */ + function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, + // By default: 'oneToOne'. + diffMode) { + this._old = oldArr; + this._new = newArr; + this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; + this._newKeyGetter = newKeyGetter || defaultKeyGetter; + // Visible in callback via `this.context`; + this.context = context; + this._diffModeMultiple = diffMode === 'multiple'; + } + /** + * Callback function when add a data + */ + DataDiffer.prototype.add = function (func) { + this._add = func; + return this; + }; + /** + * Callback function when update a data + */ + DataDiffer.prototype.update = function (func) { + this._update = func; + return this; + }; + /** + * Callback function when update a data and only work in `cbMode: 'byKey'`. + */ + DataDiffer.prototype.updateManyToOne = function (func) { + this._updateManyToOne = func; + return this; + }; + /** + * Callback function when update a data and only work in `cbMode: 'byKey'`. + */ + DataDiffer.prototype.updateOneToMany = function (func) { + this._updateOneToMany = func; + return this; + }; + /** + * Callback function when update a data and only work in `cbMode: 'byKey'`. + */ + DataDiffer.prototype.updateManyToMany = function (func) { + this._updateManyToMany = func; + return this; + }; + /** + * Callback function when remove a data + */ + DataDiffer.prototype.remove = function (func) { + this._remove = func; + return this; + }; + DataDiffer.prototype.execute = function () { + this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne'](); + }; + DataDiffer.prototype._executeOneToOne = function () { + var oldArr = this._old; + var newArr = this._new; + var newDataIndexMap = {}; + var oldDataKeyArr = new Array(oldArr.length); + var newDataKeyArr = new Array(newArr.length); + this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter'); + this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter'); + for (var i = 0; i < oldArr.length; i++) { + var oldKey = oldDataKeyArr[i]; + var newIdxMapVal = newDataIndexMap[oldKey]; + var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); + // idx can never be empty array here. see 'set null' logic below. + if (newIdxMapValLen > 1) { + // Consider there is duplicate key (for example, use dataItem.name as key). + // We should make sure every item in newArr and oldArr can be visited. + var newIdx = newIdxMapVal.shift(); + if (newIdxMapVal.length === 1) { + newDataIndexMap[oldKey] = newIdxMapVal[0]; + } + this._update && this._update(newIdx, i); + } else if (newIdxMapValLen === 1) { + newDataIndexMap[oldKey] = null; + this._update && this._update(newIdxMapVal, i); + } else { + this._remove && this._remove(i); + } + } + this._performRestAdd(newDataKeyArr, newDataIndexMap); + }; + /** + * For example, consider the case: + * oldData: [o0, o1, o2, o3, o4, o5, o6, o7], + * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8], + * Where: + * o0, o1, n0 has key 'a' (many to one) + * o5, n4, n5, n6 has key 'b' (one to many) + * o2, n1 has key 'c' (one to one) + * n2, n3 has key 'd' (add) + * o3, o4 has key 'e' (remove) + * o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove) + * Then: + * (The order of the following directives are not ensured.) + * this._updateManyToOne(n0, [o0, o1]); + * this._updateOneToMany([n4, n5, n6], o5); + * this._update(n1, o2); + * this._remove(o3); + * this._remove(o4); + * this._remove(o6); + * this._remove(o7); + * this._add(n2); + * this._add(n3); + * this._add(n7); + * this._add(n8); + */ + DataDiffer.prototype._executeMultiple = function () { + var oldArr = this._old; + var newArr = this._new; + var oldDataIndexMap = {}; + var newDataIndexMap = {}; + var oldDataKeyArr = []; + var newDataKeyArr = []; + this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter'); + this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter'); + for (var i = 0; i < oldDataKeyArr.length; i++) { + var oldKey = oldDataKeyArr[i]; + var oldIdxMapVal = oldDataIndexMap[oldKey]; + var newIdxMapVal = newDataIndexMap[oldKey]; + var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal); + var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); + if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) { + this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal); + newDataIndexMap[oldKey] = null; + } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) { + this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal); + newDataIndexMap[oldKey] = null; + } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) { + this._update && this._update(newIdxMapVal, oldIdxMapVal); + newDataIndexMap[oldKey] = null; + } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) { + this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal); + newDataIndexMap[oldKey] = null; + } else if (oldIdxMapValLen > 1) { + for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) { + this._remove && this._remove(oldIdxMapVal[i_1]); + } + } else { + this._remove && this._remove(oldIdxMapVal); + } + } + this._performRestAdd(newDataKeyArr, newDataIndexMap); + }; + DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) { + for (var i = 0; i < newDataKeyArr.length; i++) { + var newKey = newDataKeyArr[i]; + var newIdxMapVal = newDataIndexMap[newKey]; + var idxMapValLen = dataIndexMapValueLength(newIdxMapVal); + if (idxMapValLen > 1) { + for (var j = 0; j < idxMapValLen; j++) { + this._add && this._add(newIdxMapVal[j]); + } + } else if (idxMapValLen === 1) { + this._add && this._add(newIdxMapVal); + } + // Support both `newDataKeyArr` are duplication removed or not removed. + newDataIndexMap[newKey] = null; + } + }; + DataDiffer.prototype._initIndexMap = function (arr, + // Can be null. + map, + // In 'byKey', the output `keyArr` is duplication removed. + // In 'byIndex', the output `keyArr` is not duplication removed and + // its indices are accurately corresponding to `arr`. + keyArr, keyGetterName) { + var cbModeMultiple = this._diffModeMultiple; + for (var i = 0; i < arr.length; i++) { + // Add prefix to avoid conflict with Object.prototype. + var key = '_ec_' + this[keyGetterName](arr[i], i); + if (!cbModeMultiple) { + keyArr[i] = key; + } + if (!map) { + continue; + } + var idxMapVal = map[key]; + var idxMapValLen = dataIndexMapValueLength(idxMapVal); + if (idxMapValLen === 0) { + // Simple optimize: in most cases, one index has one key, + // do not need array. + map[key] = i; + if (cbModeMultiple) { + keyArr.push(key); + } + } else if (idxMapValLen === 1) { + map[key] = [idxMapVal, i]; + } else { + idxMapVal.push(i); + } + } + }; + return DataDiffer; + }(); + + var DimensionUserOuput = /** @class */function () { + function DimensionUserOuput(encode, dimRequest) { + this._encode = encode; + this._schema = dimRequest; + } + DimensionUserOuput.prototype.get = function () { + return { + // Do not generate full dimension name until fist used. + fullDimensions: this._getFullDimensionNames(), + encode: this._encode + }; + }; + /** + * Get all data store dimension names. + * Theoretically a series data store is defined both by series and used dataset (if any). + * If some dimensions are omitted for performance reason in `this.dimensions`, + * the dimension name may not be auto-generated if user does not specify a dimension name. + * In this case, the dimension name is `null`/`undefined`. + */ + DimensionUserOuput.prototype._getFullDimensionNames = function () { + if (!this._cachedDimNames) { + this._cachedDimNames = this._schema ? this._schema.makeOutputDimensionNames() : []; + } + return this._cachedDimNames; + }; + return DimensionUserOuput; + }(); + function summarizeDimensions(data, schema) { + var summary = {}; + var encode = summary.encode = {}; + var notExtraCoordDimMap = createHashMap(); + var defaultedLabel = []; + var defaultedTooltip = []; + var userOutputEncode = {}; + each(data.dimensions, function (dimName) { + var dimItem = data.getDimensionInfo(dimName); + var coordDim = dimItem.coordDim; + if (coordDim) { + if ("development" !== 'production') { + assert(VISUAL_DIMENSIONS.get(coordDim) == null); + } + var coordDimIndex = dimItem.coordDimIndex; + getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName; + if (!dimItem.isExtraCoord) { + notExtraCoordDimMap.set(coordDim, 1); + // Use the last coord dim (and label friendly) as default label, + // because when dataset is used, it is hard to guess which dimension + // can be value dimension. If both show x, y on label is not look good, + // and conventionally y axis is focused more. + if (mayLabelDimType(dimItem.type)) { + defaultedLabel[0] = dimName; + } + // User output encode do not contain generated coords. + // And it only has index. User can use index to retrieve value from the raw item array. + getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = data.getDimensionIndex(dimItem.name); + } + if (dimItem.defaultTooltip) { + defaultedTooltip.push(dimName); + } + } + VISUAL_DIMENSIONS.each(function (v, otherDim) { + var encodeArr = getOrCreateEncodeArr(encode, otherDim); + var dimIndex = dimItem.otherDims[otherDim]; + if (dimIndex != null && dimIndex !== false) { + encodeArr[dimIndex] = dimItem.name; + } + }); + }); + var dataDimsOnCoord = []; + var encodeFirstDimNotExtra = {}; + notExtraCoordDimMap.each(function (v, coordDim) { + var dimArr = encode[coordDim]; + encodeFirstDimNotExtra[coordDim] = dimArr[0]; + // Not necessary to remove duplicate, because a data + // dim canot on more than one coordDim. + dataDimsOnCoord = dataDimsOnCoord.concat(dimArr); + }); + summary.dataDimsOnCoord = dataDimsOnCoord; + summary.dataDimIndicesOnCoord = map(dataDimsOnCoord, function (dimName) { + return data.getDimensionInfo(dimName).storeDimIndex; + }); + summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra; + var encodeLabel = encode.label; + // FIXME `encode.label` is not recommended, because formatter cannot be set + // in this way. Use label.formatter instead. Maybe remove this approach someday. + if (encodeLabel && encodeLabel.length) { + defaultedLabel = encodeLabel.slice(); + } + var encodeTooltip = encode.tooltip; + if (encodeTooltip && encodeTooltip.length) { + defaultedTooltip = encodeTooltip.slice(); + } else if (!defaultedTooltip.length) { + defaultedTooltip = defaultedLabel.slice(); + } + encode.defaultedLabel = defaultedLabel; + encode.defaultedTooltip = defaultedTooltip; + summary.userOutput = new DimensionUserOuput(userOutputEncode, schema); + return summary; + } + function getOrCreateEncodeArr(encode, dim) { + if (!encode.hasOwnProperty(dim)) { + encode[dim] = []; + } + return encode[dim]; + } + // FIXME:TS should be type `AxisType` + function getDimensionTypeByAxis(axisType) { + return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float'; + } + function mayLabelDimType(dimType) { + // In most cases, ordinal and time do not suitable for label. + // Ordinal info can be displayed on axis. Time is too long. + return !(dimType === 'ordinal' || dimType === 'time'); + } + // function findTheLastDimMayLabel(data) { + // // Get last value dim + // let dimensions = data.dimensions.slice(); + // let valueType; + // let valueDim; + // while (dimensions.length && ( + // valueDim = dimensions.pop(), + // valueType = data.getDimensionInfo(valueDim).type, + // valueType === 'ordinal' || valueType === 'time' + // )) {} // jshint ignore:line + // return valueDim; + // } + + var SeriesDimensionDefine = /** @class */function () { + /** + * @param opt All of the fields will be shallow copied. + */ + function SeriesDimensionDefine(opt) { + /** + * The format of `otherDims` is: + * ```js + * { + * tooltip?: number + * label?: number + * itemName?: number + * seriesName?: number + * } + * ``` + * + * A `series.encode` can specified these fields: + * ```js + * encode: { + * // "3, 1, 5" is the index of data dimension. + * tooltip: [3, 1, 5], + * label: [0, 3], + * ... + * } + * ``` + * `otherDims` is the parse result of the `series.encode` above, like: + * ```js + * // Suppose the index of this data dimension is `3`. + * this.otherDims = { + * // `3` is at the index `0` of the `encode.tooltip` + * tooltip: 0, + * // `3` is at the index `1` of the `encode.label` + * label: 1 + * }; + * ``` + * + * This prop should never be `null`/`undefined` after initialized. + */ + this.otherDims = {}; + if (opt != null) { + extend(this, opt); + } + } + return SeriesDimensionDefine; + }(); + + var inner$4 = makeInner(); + var dimTypeShort = { + float: 'f', + int: 'i', + ordinal: 'o', + number: 'n', + time: 't' + }; + /** + * Represents the dimension requirement of a series. + * + * NOTICE: + * When there are too many dimensions in dataset and many series, only the used dimensions + * (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`. + * But users may query data by other unused dimension names. + * In this case, users can only query data if and only if they have defined dimension names + * via ec option, so we provide `getDimensionIndexFromSource`, which only query them from + * `source` dimensions. + */ + var SeriesDataSchema = /** @class */function () { + function SeriesDataSchema(opt) { + this.dimensions = opt.dimensions; + this._dimOmitted = opt.dimensionOmitted; + this.source = opt.source; + this._fullDimCount = opt.fullDimensionCount; + this._updateDimOmitted(opt.dimensionOmitted); + } + SeriesDataSchema.prototype.isDimensionOmitted = function () { + return this._dimOmitted; + }; + SeriesDataSchema.prototype._updateDimOmitted = function (dimensionOmitted) { + this._dimOmitted = dimensionOmitted; + if (!dimensionOmitted) { + return; + } + if (!this._dimNameMap) { + this._dimNameMap = ensureSourceDimNameMap(this.source); + } + }; + /** + * @caution Can only be used when `dimensionOmitted: true`. + * + * Get index by user defined dimension name (i.e., not internal generate name). + * That is, get index from `dimensionsDefine`. + * If no `dimensionsDefine`, or no name get, return -1. + */ + SeriesDataSchema.prototype.getSourceDimensionIndex = function (dimName) { + return retrieve2(this._dimNameMap.get(dimName), -1); + }; + /** + * @caution Can only be used when `dimensionOmitted: true`. + * + * Notice: may return `null`/`undefined` if user not specify dimension names. + */ + SeriesDataSchema.prototype.getSourceDimension = function (dimIndex) { + var dimensionsDefine = this.source.dimensionsDefine; + if (dimensionsDefine) { + return dimensionsDefine[dimIndex]; + } + }; + SeriesDataSchema.prototype.makeStoreSchema = function () { + var dimCount = this._fullDimCount; + var willRetrieveDataByName = shouldRetrieveDataByName(this.source); + var makeHashStrict = !shouldOmitUnusedDimensions(dimCount); + // If source don't have dimensions or series don't omit unsed dimensions. + // Generate from seriesDimList directly + var dimHash = ''; + var dims = []; + for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < dimCount; fullDimIdx++) { + var property = void 0; + var type = void 0; + var ordinalMeta = void 0; + var seriesDimDef = this.dimensions[seriesDimIdx]; + // The list has been sorted by `storeDimIndex` asc. + if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) { + property = willRetrieveDataByName ? seriesDimDef.name : null; + type = seriesDimDef.type; + ordinalMeta = seriesDimDef.ordinalMeta; + seriesDimIdx++; + } else { + var sourceDimDef = this.getSourceDimension(fullDimIdx); + if (sourceDimDef) { + property = willRetrieveDataByName ? sourceDimDef.name : null; + type = sourceDimDef.type; + } + } + dims.push({ + property: property, + type: type, + ordinalMeta: ordinalMeta + }); + // If retrieving data by index, + // use to determine whether data can be shared. + // (Because in this case there might be no dimension name defined in dataset, but indices always exists). + // (Indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash). + // Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`), + // use in hash. + if (willRetrieveDataByName && property != null + // For data stack, we have make sure each series has its own dim on this store. + // So we do not add property to hash to make sure they can share this store. + && (!seriesDimDef || !seriesDimDef.isCalculationCoord)) { + dimHash += makeHashStrict + // Use escape character '`' in case that property name contains '$'. + ? property.replace(/\`/g, '`1').replace(/\$/g, '`2') + // For better performance, when there are large dimensions, tolerant this defects that hardly meet. + : property; + } + dimHash += '$'; + dimHash += dimTypeShort[type] || 'f'; + if (ordinalMeta) { + dimHash += ordinalMeta.uid; + } + dimHash += '$'; + } + // Source from endpoint(usually series) will be read differently + // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different. + // So we use this three props as key. + var source = this.source; + var hash = [source.seriesLayoutBy, source.startIndex, dimHash].join('$$'); + return { + dimensions: dims, + hash: hash + }; + }; + SeriesDataSchema.prototype.makeOutputDimensionNames = function () { + var result = []; + for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < this._fullDimCount; fullDimIdx++) { + var name_1 = void 0; + var seriesDimDef = this.dimensions[seriesDimIdx]; + // The list has been sorted by `storeDimIndex` asc. + if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) { + if (!seriesDimDef.isCalculationCoord) { + name_1 = seriesDimDef.name; + } + seriesDimIdx++; + } else { + var sourceDimDef = this.getSourceDimension(fullDimIdx); + if (sourceDimDef) { + name_1 = sourceDimDef.name; + } + } + result.push(name_1); + } + return result; + }; + SeriesDataSchema.prototype.appendCalculationDimension = function (dimDef) { + this.dimensions.push(dimDef); + dimDef.isCalculationCoord = true; + this._fullDimCount++; + // If append dimension on a data store, consider the store + // might be shared by different series, series dimensions not + // really map to store dimensions. + this._updateDimOmitted(true); + }; + return SeriesDataSchema; + }(); + function isSeriesDataSchema(schema) { + return schema instanceof SeriesDataSchema; + } + function createDimNameMap(dimsDef) { + var dataDimNameMap = createHashMap(); + for (var i = 0; i < (dimsDef || []).length; i++) { + var dimDefItemRaw = dimsDef[i]; + var userDimName = isObject(dimDefItemRaw) ? dimDefItemRaw.name : dimDefItemRaw; + if (userDimName != null && dataDimNameMap.get(userDimName) == null) { + dataDimNameMap.set(userDimName, i); + } + } + return dataDimNameMap; + } + function ensureSourceDimNameMap(source) { + var innerSource = inner$4(source); + return innerSource.dimNameMap || (innerSource.dimNameMap = createDimNameMap(source.dimensionsDefine)); + } + function shouldOmitUnusedDimensions(dimCount) { + return dimCount > 30; + } + + var isObject$2 = isObject; + var map$1 = map; + var CtorInt32Array$1 = typeof Int32Array === 'undefined' ? Array : Int32Array; + // Use prefix to avoid index to be the same as otherIdList[idx], + // which will cause weird update animation. + var ID_PREFIX = 'e\0\0'; + var INDEX_NOT_FOUND = -1; + // type SeriesDimensionIndex = DimensionIndex; + var TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_dimSummary', 'userOutput', '_rawData', '_dimValueGetter', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount']; + var CLONE_PROPERTIES = ['_approximateExtent']; + // ----------------------------- + // Internal method declarations: + // ----------------------------- + var prepareInvertedIndex; + var getId; + var getIdNameFromStore; + var normalizeDimensions; + var transferProperties; + var cloneListForMapAndSample; + var makeIdFromName; + var SeriesData = /** @class */function () { + /** + * @param dimensionsInput.dimensions + * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...]. + * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius + */ + function SeriesData(dimensionsInput, hostModel) { + this.type = 'list'; + this._dimOmitted = false; + this._nameList = []; + this._idList = []; + // Models of data option is stored sparse for optimizing memory cost + // Never used yet (not used yet). + // private _optionModels: Model[] = []; + // Global visual properties after visual coding + this._visual = {}; + // Global layout properties. + this._layout = {}; + // Item visual properties after visual coding + this._itemVisuals = []; + // Item layout properties after layout + this._itemLayouts = []; + // Graphic elements + this._graphicEls = []; + // key: dim, value: extent + this._approximateExtent = {}; + this._calculationInfo = {}; + // Having detected that there is data item is non primitive type + // (in type `OptionDataItemObject`). + // Like `data: [ { value: xx, itemStyle: {...} }, ...]` + // At present it only happen in `SOURCE_FORMAT_ORIGINAL`. + this.hasItemOption = false; + // Methods that create a new list based on this list should be listed here. + // Notice that those method should `RETURN` the new list. + this.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map']; + // Methods that change indices of this list should be listed here. + this.CHANGABLE_METHODS = ['filterSelf', 'selectRange']; + this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample']; + var dimensions; + var assignStoreDimIdx = false; + if (isSeriesDataSchema(dimensionsInput)) { + dimensions = dimensionsInput.dimensions; + this._dimOmitted = dimensionsInput.isDimensionOmitted(); + this._schema = dimensionsInput; + } else { + assignStoreDimIdx = true; + dimensions = dimensionsInput; + } + dimensions = dimensions || ['x', 'y']; + var dimensionInfos = {}; + var dimensionNames = []; + var invertedIndicesMap = {}; + var needsHasOwn = false; + var emptyObj = {}; + for (var i = 0; i < dimensions.length; i++) { + // Use the original dimensions[i], where other flag props may exists. + var dimInfoInput = dimensions[i]; + var dimensionInfo = isString(dimInfoInput) ? new SeriesDimensionDefine({ + name: dimInfoInput + }) : !(dimInfoInput instanceof SeriesDimensionDefine) ? new SeriesDimensionDefine(dimInfoInput) : dimInfoInput; + var dimensionName = dimensionInfo.name; + dimensionInfo.type = dimensionInfo.type || 'float'; + if (!dimensionInfo.coordDim) { + dimensionInfo.coordDim = dimensionName; + dimensionInfo.coordDimIndex = 0; + } + var otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {}; + dimensionNames.push(dimensionName); + dimensionInfos[dimensionName] = dimensionInfo; + if (emptyObj[dimensionName] != null) { + needsHasOwn = true; + } + if (dimensionInfo.createInvertedIndices) { + invertedIndicesMap[dimensionName] = []; + } + if (otherDims.itemName === 0) { + this._nameDimIdx = i; + } + if (otherDims.itemId === 0) { + this._idDimIdx = i; + } + if ("development" !== 'production') { + assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0); + } + if (assignStoreDimIdx) { + dimensionInfo.storeDimIndex = i; + } + } + this.dimensions = dimensionNames; + this._dimInfos = dimensionInfos; + this._initGetDimensionInfo(needsHasOwn); + this.hostModel = hostModel; + this._invertedIndicesMap = invertedIndicesMap; + if (this._dimOmitted) { + var dimIdxToName_1 = this._dimIdxToName = createHashMap(); + each(dimensionNames, function (dimName) { + dimIdxToName_1.set(dimensionInfos[dimName].storeDimIndex, dimName); + }); + } + } + /** + * + * Get concrete dimension name by dimension name or dimension index. + * If input a dimension name, do not validate whether the dimension name exits. + * + * @caution + * @param dim Must make sure the dimension is `SeriesDimensionLoose`. + * Because only those dimensions will have auto-generated dimension names if not + * have a user-specified name, and other dimensions will get a return of null/undefined. + * + * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples: + * ```js + * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx); + * ``` + * + * @return Concrete dim name. + */ + SeriesData.prototype.getDimension = function (dim) { + var dimIdx = this._recognizeDimIndex(dim); + if (dimIdx == null) { + return dim; + } + dimIdx = dim; + if (!this._dimOmitted) { + return this.dimensions[dimIdx]; + } + // Retrieve from series dimension definition because it probably contains + // generated dimension name (like 'x', 'y'). + var dimName = this._dimIdxToName.get(dimIdx); + if (dimName != null) { + return dimName; + } + var sourceDimDef = this._schema.getSourceDimension(dimIdx); + if (sourceDimDef) { + return sourceDimDef.name; + } + }; + /** + * Get dimension index in data store. Return -1 if not found. + * Can be used to index value from getRawValue. + */ + SeriesData.prototype.getDimensionIndex = function (dim) { + var dimIdx = this._recognizeDimIndex(dim); + if (dimIdx != null) { + return dimIdx; + } + if (dim == null) { + return -1; + } + var dimInfo = this._getDimInfo(dim); + return dimInfo ? dimInfo.storeDimIndex : this._dimOmitted ? this._schema.getSourceDimensionIndex(dim) : -1; + }; + /** + * The meanings of the input parameter `dim`: + * + * + If dim is a number (e.g., `1`), it means the index of the dimension. + * For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'. + * + If dim is a number-like string (e.g., `"1"`): + * + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`, + * it means that concrete name. + * + If not, it will be converted to a number, which means the index of the dimension. + * (why? because of the backward compatibility. We have been tolerating number-like string in + * dimension setting, although now it seems that it is not a good idea.) + * For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`, + * if no dimension name is defined as `"1"`. + * + If dim is a not-number-like string, it means the concrete dim name. + * For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`, + * or customized in `dimensions` property of option like `"age"`. + * + * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`). + */ + SeriesData.prototype._recognizeDimIndex = function (dim) { + if (isNumber(dim) + // If being a number-like string but not being defined as a dimension name. + || dim != null && !isNaN(dim) && !this._getDimInfo(dim) && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0)) { + return +dim; + } + }; + SeriesData.prototype._getStoreDimIndex = function (dim) { + var dimIdx = this.getDimensionIndex(dim); + if ("development" !== 'production') { + if (dimIdx == null) { + throw new Error('Unknown dimension ' + dim); + } + } + return dimIdx; + }; + /** + * Get type and calculation info of particular dimension + * @param dim + * Dimension can be concrete names like x, y, z, lng, lat, angle, radius + * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' + */ + SeriesData.prototype.getDimensionInfo = function (dim) { + // Do not clone, because there may be categories in dimInfo. + return this._getDimInfo(this.getDimension(dim)); + }; + SeriesData.prototype._initGetDimensionInfo = function (needsHasOwn) { + var dimensionInfos = this._dimInfos; + this._getDimInfo = needsHasOwn ? function (dimName) { + return dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined; + } : function (dimName) { + return dimensionInfos[dimName]; + }; + }; + /** + * concrete dimension name list on coord. + */ + SeriesData.prototype.getDimensionsOnCoord = function () { + return this._dimSummary.dataDimsOnCoord.slice(); + }; + SeriesData.prototype.mapDimension = function (coordDim, idx) { + var dimensionsSummary = this._dimSummary; + if (idx == null) { + return dimensionsSummary.encodeFirstDimNotExtra[coordDim]; + } + var dims = dimensionsSummary.encode[coordDim]; + return dims ? dims[idx] : null; + }; + SeriesData.prototype.mapDimensionsAll = function (coordDim) { + var dimensionsSummary = this._dimSummary; + var dims = dimensionsSummary.encode[coordDim]; + return (dims || []).slice(); + }; + SeriesData.prototype.getStore = function () { + return this._store; + }; + /** + * Initialize from data + * @param data source or data or data store. + * @param nameList The name of a datum is used on data diff and + * default label/tooltip. + * A name can be specified in encode.itemName, + * or dataItem.name (only for series option data), + * or provided in nameList from outside. + */ + SeriesData.prototype.initData = function (data, nameList, dimValueGetter) { + var _this = this; + var store; + if (data instanceof DataStore) { + store = data; + } + if (!store) { + var dimensions = this.dimensions; + var provider = isSourceInstance(data) || isArrayLike(data) ? new DefaultDataProvider(data, dimensions.length) : data; + store = new DataStore(); + var dimensionInfos = map$1(dimensions, function (dimName) { + return { + type: _this._dimInfos[dimName].type, + property: dimName + }; + }); + store.initData(provider, dimensionInfos, dimValueGetter); + } + this._store = store; + // Reset + this._nameList = (nameList || []).slice(); + this._idList = []; + this._nameRepeatCount = {}; + this._doInit(0, store.count()); + // Cache summary info for fast visit. See "dimensionHelper". + // Needs to be initialized after store is prepared. + this._dimSummary = summarizeDimensions(this, this._schema); + this.userOutput = this._dimSummary.userOutput; + }; + /** + * Caution: Can be only called on raw data (before `this._indices` created). + */ + SeriesData.prototype.appendData = function (data) { + var range = this._store.appendData(data); + this._doInit(range[0], range[1]); + }; + /** + * Caution: Can be only called on raw data (before `this._indices` created). + * This method does not modify `rawData` (`dataProvider`), but only + * add values to store. + * + * The final count will be increased by `Math.max(values.length, names.length)`. + * + * @param values That is the SourceType: 'arrayRows', like + * [ + * [12, 33, 44], + * [NaN, 43, 1], + * ['-', 'asdf', 0] + * ] + * Each item is exactly corresponding to a dimension. + */ + SeriesData.prototype.appendValues = function (values, names) { + var _a = this._store.appendValues(values, names.length), + start = _a.start, + end = _a.end; + var shouldMakeIdFromName = this._shouldMakeIdFromName(); + this._updateOrdinalMeta(); + if (names) { + for (var idx = start; idx < end; idx++) { + var sourceIdx = idx - start; + this._nameList[idx] = names[sourceIdx]; + if (shouldMakeIdFromName) { + makeIdFromName(this, idx); + } + } + } + }; + SeriesData.prototype._updateOrdinalMeta = function () { + var store = this._store; + var dimensions = this.dimensions; + for (var i = 0; i < dimensions.length; i++) { + var dimInfo = this._dimInfos[dimensions[i]]; + if (dimInfo.ordinalMeta) { + store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta); + } + } + }; + SeriesData.prototype._shouldMakeIdFromName = function () { + var provider = this._store.getProvider(); + return this._idDimIdx == null && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY && !provider.fillStorage; + }; + SeriesData.prototype._doInit = function (start, end) { + if (start >= end) { + return; + } + var store = this._store; + var provider = store.getProvider(); + this._updateOrdinalMeta(); + var nameList = this._nameList; + var idList = this._idList; + var sourceFormat = provider.getSource().sourceFormat; + var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; + // Each data item is value + // [1, 2] + // 2 + // Bar chart, line chart which uses category axis + // only gives the 'y' value. 'x' value is the indices of category + // Use a tempValue to normalize the value to be a (x, y) value + // If dataItem is {name: ...} or {id: ...}, it has highest priority. + // This kind of ids and names are always stored `_nameList` and `_idList`. + if (isFormatOriginal && !provider.pure) { + var sharedDataItem = []; + for (var idx = start; idx < end; idx++) { + // NOTICE: Try not to write things into dataItem + var dataItem = provider.getItem(idx, sharedDataItem); + if (!this.hasItemOption && isDataItemOption(dataItem)) { + this.hasItemOption = true; + } + if (dataItem) { + var itemName = dataItem.name; + if (nameList[idx] == null && itemName != null) { + nameList[idx] = convertOptionIdName(itemName, null); + } + var itemId = dataItem.id; + if (idList[idx] == null && itemId != null) { + idList[idx] = convertOptionIdName(itemId, null); + } + } + } + } + if (this._shouldMakeIdFromName()) { + for (var idx = start; idx < end; idx++) { + makeIdFromName(this, idx); + } + } + prepareInvertedIndex(this); + }; + /** + * PENDING: In fact currently this function is only used to short-circuit + * the calling of `scale.unionExtentFromData` when data have been filtered by modules + * like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on + * an axis, but if a "axis related data filter module" is used, the extent of the axis have + * been fixed and no need to calling `scale.unionExtentFromData` actually. + * But if we add "custom data filter" in future, which is not "axis related", this method may + * be still needed. + * + * Optimize for the scenario that data is filtered by a given extent. + * Consider that if data amount is more than hundreds of thousand, + * extent calculation will cost more than 10ms and the cache will + * be erased because of the filtering. + */ + SeriesData.prototype.getApproximateExtent = function (dim) { + return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim)); + }; + /** + * Calculate extent on a filtered data might be time consuming. + * Approximate extent is only used for: calculate extent of filtered data outside. + */ + SeriesData.prototype.setApproximateExtent = function (extent, dim) { + dim = this.getDimension(dim); + this._approximateExtent[dim] = extent.slice(); + }; + SeriesData.prototype.getCalculationInfo = function (key) { + return this._calculationInfo[key]; + }; + SeriesData.prototype.setCalculationInfo = function (key, value) { + isObject$2(key) ? extend(this._calculationInfo, key) : this._calculationInfo[key] = value; + }; + /** + * @return Never be null/undefined. `number` will be converted to string. Because: + * In most cases, name is used in display, where returning a string is more convenient. + * In other cases, name is used in query (see `indexOfName`), where we can keep the + * rule that name `2` equals to name `'2'`. + */ + SeriesData.prototype.getName = function (idx) { + var rawIndex = this.getRawIndex(idx); + var name = this._nameList[rawIndex]; + if (name == null && this._nameDimIdx != null) { + name = getIdNameFromStore(this, this._nameDimIdx, rawIndex); + } + if (name == null) { + name = ''; + } + return name; + }; + SeriesData.prototype._getCategory = function (dimIdx, idx) { + var ordinal = this._store.get(dimIdx, idx); + var ordinalMeta = this._store.getOrdinalMeta(dimIdx); + if (ordinalMeta) { + return ordinalMeta.categories[ordinal]; + } + return ordinal; + }; + /** + * @return Never null/undefined. `number` will be converted to string. Because: + * In all cases having encountered at present, id is used in making diff comparison, which + * are usually based on hash map. We can keep the rule that the internal id are always string + * (treat `2` is the same as `'2'`) to make the related logic simple. + */ + SeriesData.prototype.getId = function (idx) { + return getId(this, this.getRawIndex(idx)); + }; + SeriesData.prototype.count = function () { + return this._store.count(); + }; + /** + * Get value. Return NaN if idx is out of range. + * + * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead. + */ + SeriesData.prototype.get = function (dim, idx) { + var store = this._store; + var dimInfo = this._dimInfos[dim]; + if (dimInfo) { + return store.get(dimInfo.storeDimIndex, idx); + } + }; + /** + * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead. + */ + SeriesData.prototype.getByRawIndex = function (dim, rawIdx) { + var store = this._store; + var dimInfo = this._dimInfos[dim]; + if (dimInfo) { + return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx); + } + }; + SeriesData.prototype.getIndices = function () { + return this._store.getIndices(); + }; + SeriesData.prototype.getDataExtent = function (dim) { + return this._store.getDataExtent(this._getStoreDimIndex(dim)); + }; + SeriesData.prototype.getSum = function (dim) { + return this._store.getSum(this._getStoreDimIndex(dim)); + }; + SeriesData.prototype.getMedian = function (dim) { + return this._store.getMedian(this._getStoreDimIndex(dim)); + }; + SeriesData.prototype.getValues = function (dimensions, idx) { + var _this = this; + var store = this._store; + return isArray(dimensions) ? store.getValues(map$1(dimensions, function (dim) { + return _this._getStoreDimIndex(dim); + }), idx) : store.getValues(dimensions); + }; + /** + * If value is NaN. Including '-' + * Only check the coord dimensions. + */ + SeriesData.prototype.hasValue = function (idx) { + var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord; + for (var i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) { + // Ordinal type originally can be string or number. + // But when an ordinal type is used on coord, it can + // not be string but only number. So we can also use isNaN. + if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) { + return false; + } + } + return true; + }; + /** + * Retrieve the index with given name + */ + SeriesData.prototype.indexOfName = function (name) { + for (var i = 0, len = this._store.count(); i < len; i++) { + if (this.getName(i) === name) { + return i; + } + } + return -1; + }; + SeriesData.prototype.getRawIndex = function (idx) { + return this._store.getRawIndex(idx); + }; + SeriesData.prototype.indexOfRawIndex = function (rawIndex) { + return this._store.indexOfRawIndex(rawIndex); + }; + /** + * Only support the dimension which inverted index created. + * Do not support other cases until required. + * @param dim concrete dim + * @param value ordinal index + * @return rawIndex + */ + SeriesData.prototype.rawIndexOf = function (dim, value) { + var invertedIndices = dim && this._invertedIndicesMap[dim]; + if ("development" !== 'production') { + if (!invertedIndices) { + throw new Error('Do not supported yet'); + } + } + var rawIndex = invertedIndices[value]; + if (rawIndex == null || isNaN(rawIndex)) { + return INDEX_NOT_FOUND; + } + return rawIndex; + }; + /** + * Retrieve the index of nearest value + * @param dim + * @param value + * @param [maxDistance=Infinity] + * @return If and only if multiple indices has + * the same value, they are put to the result. + */ + SeriesData.prototype.indicesOfNearest = function (dim, value, maxDistance) { + return this._store.indicesOfNearest(this._getStoreDimIndex(dim), value, maxDistance); + }; + SeriesData.prototype.each = function (dims, cb, ctx) { + + if (isFunction(dims)) { + ctx = cb; + cb = dims; + dims = []; + } + // ctxCompat just for compat echarts3 + var fCtx = ctx || this; + var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); + this._store.each(dimIndices, fCtx ? bind(cb, fCtx) : cb); + }; + SeriesData.prototype.filterSelf = function (dims, cb, ctx) { + + if (isFunction(dims)) { + ctx = cb; + cb = dims; + dims = []; + } + // ctxCompat just for compat echarts3 + var fCtx = ctx || this; + var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); + this._store = this._store.filter(dimIndices, fCtx ? bind(cb, fCtx) : cb); + return this; + }; + /** + * Select data in range. (For optimization of filter) + * (Manually inline code, support 5 million data filtering in data zoom.) + */ + SeriesData.prototype.selectRange = function (range) { + + var _this = this; + var innerRange = {}; + var dims = keys(range); + each(dims, function (dim) { + var dimIdx = _this._getStoreDimIndex(dim); + innerRange[dimIdx] = range[dim]; + }); + this._store = this._store.selectRange(innerRange); + return this; + }; + /* eslint-enable max-len */ + SeriesData.prototype.mapArray = function (dims, cb, ctx) { + + if (isFunction(dims)) { + ctx = cb; + cb = dims; + dims = []; + } + // ctxCompat just for compat echarts3 + ctx = ctx || this; + var result = []; + this.each(dims, function () { + result.push(cb && cb.apply(this, arguments)); + }, ctx); + return result; + }; + SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) { + + // ctxCompat just for compat echarts3 + var fCtx = ctx || ctxCompat || this; + var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); + var list = cloneListForMapAndSample(this); + list._store = this._store.map(dimIndices, fCtx ? bind(cb, fCtx) : cb); + return list; + }; + SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) { + var _this = this; + // ctxCompat just for compat echarts3 + var fCtx = ctx || ctxCompat || this; + if ("development" !== 'production') { + each(normalizeDimensions(dims), function (dim) { + var dimInfo = _this.getDimensionInfo(dim); + if (!dimInfo.isCalculationCoord) { + console.error('Danger: only stack dimension can be modified'); + } + }); + } + var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); + // If do shallow clone here, if there are too many stacked series, + // it still cost lots of memory, because `_store.dimensions` are not shared. + // We should consider there probably be shallow clone happen in each series + // in consequent filter/map. + this._store.modify(dimIndices, fCtx ? bind(cb, fCtx) : cb); + }; + /** + * Large data down sampling on given dimension + * @param sampleIndex Sample index for name and id + */ + SeriesData.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) { + var list = cloneListForMapAndSample(this); + list._store = this._store.downSample(this._getStoreDimIndex(dimension), rate, sampleValue, sampleIndex); + return list; + }; + /** + * Large data down sampling using largest-triangle-three-buckets + * @param {string} valueDimension + * @param {number} targetCount + */ + SeriesData.prototype.lttbDownSample = function (valueDimension, rate) { + var list = cloneListForMapAndSample(this); + list._store = this._store.lttbDownSample(this._getStoreDimIndex(valueDimension), rate); + return list; + }; + SeriesData.prototype.getRawDataItem = function (idx) { + return this._store.getRawDataItem(idx); + }; + /** + * Get model of one data item. + */ + // TODO: Type of data item + SeriesData.prototype.getItemModel = function (idx) { + var hostModel = this.hostModel; + var dataItem = this.getRawDataItem(idx); + return new Model(dataItem, hostModel, hostModel && hostModel.ecModel); + }; + /** + * Create a data differ + */ + SeriesData.prototype.diff = function (otherList) { + var thisList = this; + return new DataDiffer(otherList ? otherList.getStore().getIndices() : [], this.getStore().getIndices(), function (idx) { + return getId(otherList, idx); + }, function (idx) { + return getId(thisList, idx); + }); + }; + /** + * Get visual property. + */ + SeriesData.prototype.getVisual = function (key) { + var visual = this._visual; + return visual && visual[key]; + }; + SeriesData.prototype.setVisual = function (kvObj, val) { + this._visual = this._visual || {}; + if (isObject$2(kvObj)) { + extend(this._visual, kvObj); + } else { + this._visual[kvObj] = val; + } + }; + /** + * Get visual property of single data item + */ + // eslint-disable-next-line + SeriesData.prototype.getItemVisual = function (idx, key) { + var itemVisual = this._itemVisuals[idx]; + var val = itemVisual && itemVisual[key]; + if (val == null) { + // Use global visual property + return this.getVisual(key); + } + return val; + }; + /** + * If exists visual property of single data item + */ + SeriesData.prototype.hasItemVisual = function () { + return this._itemVisuals.length > 0; + }; + /** + * Make sure itemVisual property is unique + */ + // TODO: use key to save visual to reduce memory. + SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) { + var itemVisuals = this._itemVisuals; + var itemVisual = itemVisuals[idx]; + if (!itemVisual) { + itemVisual = itemVisuals[idx] = {}; + } + var val = itemVisual[key]; + if (val == null) { + val = this.getVisual(key); + // TODO Performance? + if (isArray(val)) { + val = val.slice(); + } else if (isObject$2(val)) { + val = extend({}, val); + } + itemVisual[key] = val; + } + return val; + }; + // eslint-disable-next-line + SeriesData.prototype.setItemVisual = function (idx, key, value) { + var itemVisual = this._itemVisuals[idx] || {}; + this._itemVisuals[idx] = itemVisual; + if (isObject$2(key)) { + extend(itemVisual, key); + } else { + itemVisual[key] = value; + } + }; + /** + * Clear itemVisuals and list visual. + */ + SeriesData.prototype.clearAllVisual = function () { + this._visual = {}; + this._itemVisuals = []; + }; + SeriesData.prototype.setLayout = function (key, val) { + isObject$2(key) ? extend(this._layout, key) : this._layout[key] = val; + }; + /** + * Get layout property. + */ + SeriesData.prototype.getLayout = function (key) { + return this._layout[key]; + }; + /** + * Get layout of single data item + */ + SeriesData.prototype.getItemLayout = function (idx) { + return this._itemLayouts[idx]; + }; + /** + * Set layout of single data item + */ + SeriesData.prototype.setItemLayout = function (idx, layout, merge) { + this._itemLayouts[idx] = merge ? extend(this._itemLayouts[idx] || {}, layout) : layout; + }; + /** + * Clear all layout of single data item + */ + SeriesData.prototype.clearItemLayouts = function () { + this._itemLayouts.length = 0; + }; + /** + * Set graphic element relative to data. It can be set as null + */ + SeriesData.prototype.setItemGraphicEl = function (idx, el) { + var seriesIndex = this.hostModel && this.hostModel.seriesIndex; + setCommonECData(seriesIndex, this.dataType, idx, el); + this._graphicEls[idx] = el; + }; + SeriesData.prototype.getItemGraphicEl = function (idx) { + return this._graphicEls[idx]; + }; + SeriesData.prototype.eachItemGraphicEl = function (cb, context) { + each(this._graphicEls, function (el, idx) { + if (el) { + cb && cb.call(context, el, idx); + } + }); + }; + /** + * Shallow clone a new list except visual and layout properties, and graph elements. + * New list only change the indices. + */ + SeriesData.prototype.cloneShallow = function (list) { + if (!list) { + list = new SeriesData(this._schema ? this._schema : map$1(this.dimensions, this._getDimInfo, this), this.hostModel); + } + transferProperties(list, this); + list._store = this._store; + return list; + }; + /** + * Wrap some method to add more feature + */ + SeriesData.prototype.wrapMethod = function (methodName, injectFunction) { + var originalMethod = this[methodName]; + if (!isFunction(originalMethod)) { + return; + } + this.__wrappedMethods = this.__wrappedMethods || []; + this.__wrappedMethods.push(methodName); + this[methodName] = function () { + var res = originalMethod.apply(this, arguments); + return injectFunction.apply(this, [res].concat(slice(arguments))); + }; + }; + // ---------------------------------------------------------- + // A work around for internal method visiting private member. + // ---------------------------------------------------------- + SeriesData.internalField = function () { + prepareInvertedIndex = function (data) { + var invertedIndicesMap = data._invertedIndicesMap; + each(invertedIndicesMap, function (invertedIndices, dim) { + var dimInfo = data._dimInfos[dim]; + // Currently, only dimensions that has ordinalMeta can create inverted indices. + var ordinalMeta = dimInfo.ordinalMeta; + var store = data._store; + if (ordinalMeta) { + invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array$1(ordinalMeta.categories.length); + // The default value of TypedArray is 0. To avoid miss + // mapping to 0, we should set it as INDEX_NOT_FOUND. + for (var i = 0; i < invertedIndices.length; i++) { + invertedIndices[i] = INDEX_NOT_FOUND; + } + for (var i = 0; i < store.count(); i++) { + // Only support the case that all values are distinct. + invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i; + } + } + }); + }; + getIdNameFromStore = function (data, dimIdx, idx) { + return convertOptionIdName(data._getCategory(dimIdx, idx), null); + }; + /** + * @see the comment of `List['getId']`. + */ + getId = function (data, rawIndex) { + var id = data._idList[rawIndex]; + if (id == null && data._idDimIdx != null) { + id = getIdNameFromStore(data, data._idDimIdx, rawIndex); + } + if (id == null) { + id = ID_PREFIX + rawIndex; + } + return id; + }; + normalizeDimensions = function (dimensions) { + if (!isArray(dimensions)) { + dimensions = dimensions != null ? [dimensions] : []; + } + return dimensions; + }; + /** + * Data in excludeDimensions is copied, otherwise transferred. + */ + cloneListForMapAndSample = function (original) { + var list = new SeriesData(original._schema ? original._schema : map$1(original.dimensions, original._getDimInfo, original), original.hostModel); + // FIXME If needs stackedOn, value may already been stacked + transferProperties(list, original); + return list; + }; + transferProperties = function (target, source) { + each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) { + if (source.hasOwnProperty(propName)) { + target[propName] = source[propName]; + } + }); + target.__wrappedMethods = source.__wrappedMethods; + each(CLONE_PROPERTIES, function (propName) { + target[propName] = clone(source[propName]); + }); + target._calculationInfo = extend({}, source._calculationInfo); + }; + makeIdFromName = function (data, idx) { + var nameList = data._nameList; + var idList = data._idList; + var nameDimIdx = data._nameDimIdx; + var idDimIdx = data._idDimIdx; + var name = nameList[idx]; + var id = idList[idx]; + if (name == null && nameDimIdx != null) { + nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx); + } + if (id == null && idDimIdx != null) { + idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx); + } + if (id == null && name != null) { + var nameRepeatCount = data._nameRepeatCount; + var nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1; + id = name; + if (nmCnt > 1) { + id += '__ec__' + nmCnt; + } + idList[idx] = id; + } + }; + }(); + return SeriesData; + }(); + + /** + * For outside usage compat (like echarts-gl are using it). + */ + function createDimensions(source, opt) { + return prepareSeriesDataSchema(source, opt).dimensions; + } + /** + * This method builds the relationship between: + * + "what the coord sys or series requires (see `coordDimensions`)", + * + "what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)" + * + "what the data source provids (see `source`)". + * + * Some guess strategy will be adapted if user does not define something. + * If no 'value' dimension specified, the first no-named dimension will be + * named as 'value'. + * + * @return The results are always sorted by `storeDimIndex` asc. + */ + function prepareSeriesDataSchema( + // TODO: TYPE completeDimensions type + source, opt) { + if (!isSourceInstance(source)) { + source = createSourceFromSeriesDataOption(source); + } + opt = opt || {}; + var sysDims = opt.coordDimensions || []; + var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || []; + var coordDimNameMap = createHashMap(); + var resultList = []; + var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount); + // Try to ignore unused dimensions if sharing a high dimension datastore + // 30 is an experience value. + var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount); + var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine; + var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef); + var encodeDef = opt.encodeDefine; + if (!encodeDef && opt.encodeDefaulter) { + encodeDef = opt.encodeDefaulter(source, dimCount); + } + var encodeDefMap = createHashMap(encodeDef); + var indicesMap = new CtorInt32Array(dimCount); + for (var i = 0; i < indicesMap.length; i++) { + indicesMap[i] = -1; + } + function getResultItem(dimIdx) { + var idx = indicesMap[dimIdx]; + if (idx < 0) { + var dimDefItemRaw = dimsDef[dimIdx]; + var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : { + name: dimDefItemRaw + }; + var resultItem = new SeriesDimensionDefine(); + var userDimName = dimDefItem.name; + if (userDimName != null && dataDimNameMap.get(userDimName) != null) { + // Only if `series.dimensions` is defined in option + // displayName, will be set, and dimension will be displayed vertically in + // tooltip by default. + resultItem.name = resultItem.displayName = userDimName; + } + dimDefItem.type != null && (resultItem.type = dimDefItem.type); + dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName); + var newIdx = resultList.length; + indicesMap[dimIdx] = newIdx; + resultItem.storeDimIndex = dimIdx; + resultList.push(resultItem); + return resultItem; + } + return resultList[idx]; + } + if (!omitUnusedDimensions) { + for (var i = 0; i < dimCount; i++) { + getResultItem(i); + } + } + // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`. + encodeDefMap.each(function (dataDimsRaw, coordDim) { + var dataDims = normalizeToArray(dataDimsRaw).slice(); + // Note: It is allowed that `dataDims.length` is `0`, e.g., options is + // `{encode: {x: -1, y: 1}}`. Should not filter anything in + // this case. + if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) { + encodeDefMap.set(coordDim, false); + return; + } + var validDataDims = encodeDefMap.set(coordDim, []); + each(dataDims, function (resultDimIdxOrName, idx) { + // The input resultDimIdx can be dim name or index. + var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName; + if (resultDimIdx != null && resultDimIdx < dimCount) { + validDataDims[idx] = resultDimIdx; + applyDim(getResultItem(resultDimIdx), coordDim, idx); + } + }); + }); + // Apply templates and default order from `sysDims`. + var availDimIdx = 0; + each(sysDims, function (sysDimItemRaw) { + var coordDim; + var sysDimItemDimsDef; + var sysDimItemOtherDims; + var sysDimItem; + if (isString(sysDimItemRaw)) { + coordDim = sysDimItemRaw; + sysDimItem = {}; + } else { + sysDimItem = sysDimItemRaw; + coordDim = sysDimItem.name; + var ordinalMeta = sysDimItem.ordinalMeta; + sysDimItem.ordinalMeta = null; + sysDimItem = extend({}, sysDimItem); + sysDimItem.ordinalMeta = ordinalMeta; + // `coordDimIndex` should not be set directly. + sysDimItemDimsDef = sysDimItem.dimsDef; + sysDimItemOtherDims = sysDimItem.otherDims; + sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null; + } + var dataDims = encodeDefMap.get(coordDim); + // negative resultDimIdx means no need to mapping. + if (dataDims === false) { + return; + } + dataDims = normalizeToArray(dataDims); + // dimensions provides default dim sequences. + if (!dataDims.length) { + for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) { + while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) { + availDimIdx++; + } + availDimIdx < dimCount && dataDims.push(availDimIdx++); + } + } + // Apply templates. + each(dataDims, function (resultDimIdx, coordDimIndex) { + var resultItem = getResultItem(resultDimIdx); + // Coordinate system has a higher priority on dim type than source. + if (isUsingSourceDimensionsDef && sysDimItem.type != null) { + resultItem.type = sysDimItem.type; + } + applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex); + if (resultItem.name == null && sysDimItemDimsDef) { + var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex]; + !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = { + name: sysDimItemDimsDefItem + }); + resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name; + resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip; + } + // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}} + sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims); + }); + }); + function applyDim(resultItem, coordDim, coordDimIndex) { + if (VISUAL_DIMENSIONS.get(coordDim) != null) { + resultItem.otherDims[coordDim] = coordDimIndex; + } else { + resultItem.coordDim = coordDim; + resultItem.coordDimIndex = coordDimIndex; + coordDimNameMap.set(coordDim, true); + } + } + // Make sure the first extra dim is 'value'. + var generateCoord = opt.generateCoord; + var generateCoordCount = opt.generateCoordCount; + var fromZero = generateCoordCount != null; + generateCoordCount = generateCoord ? generateCoordCount || 1 : 0; + var extra = generateCoord || 'value'; + function ifNoNameFillWithCoordName(resultItem) { + if (resultItem.name == null) { + // Duplication will be removed in the next step. + resultItem.name = resultItem.coordDim; + } + } + // Set dim `name` and other `coordDim` and other props. + if (!omitUnusedDimensions) { + for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) { + var resultItem = getResultItem(resultDimIdx); + var coordDim = resultItem.coordDim; + if (coordDim == null) { + // TODO no need to generate coordDim for isExtraCoord? + resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero); + resultItem.coordDimIndex = 0; + // Series specified generateCoord is using out. + if (!generateCoord || generateCoordCount <= 0) { + resultItem.isExtraCoord = true; + } + generateCoordCount--; + } + ifNoNameFillWithCoordName(resultItem); + if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must + // Consider the case: + // { + // dataset: {source: [ + // ['2001', 123], + // ['2002', 456], + // ... + // ['The others', 987], + // ]}, + // series: {type: 'pie'} + // } + // The first column should better be treated as a "ordinal" although it + // might not be detected as an "ordinal" by `guessOrdinal`. + || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) { + resultItem.type = 'ordinal'; + } + } + } else { + each(resultList, function (resultItem) { + // PENDING: guessOrdinal or let user specify type: 'ordinal' manually? + ifNoNameFillWithCoordName(resultItem); + }); + // Sort dimensions: there are some rule that use the last dim as label, + // and for some latter travel process easier. + resultList.sort(function (item0, item1) { + return item0.storeDimIndex - item1.storeDimIndex; + }); + } + removeDuplication(resultList); + return new SeriesDataSchema({ + source: source, + dimensions: resultList, + fullDimensionCount: dimCount, + dimensionOmitted: omitUnusedDimensions + }); + } + function removeDuplication(result) { + var duplicationMap = createHashMap(); + for (var i = 0; i < result.length; i++) { + var dim = result[i]; + var dimOriginalName = dim.name; + var count = duplicationMap.get(dimOriginalName) || 0; + if (count > 0) { + // Starts from 0. + dim.name = dimOriginalName + (count - 1); + } + count++; + duplicationMap.set(dimOriginalName, count); + } + } + // ??? TODO + // Originally detect dimCount by data[0]. Should we + // optimize it to only by sysDims and dimensions and encode. + // So only necessary dims will be initialized. + // But + // (1) custom series should be considered. where other dims + // may be visited. + // (2) sometimes user need to calculate bubble size or use visualMap + // on other dimensions besides coordSys needed. + // So, dims that is not used by system, should be shared in data store? + function getDimCount(source, sysDims, dimsDef, optDimCount) { + // Note that the result dimCount should not small than columns count + // of data, otherwise `dataDimNameMap` checking will be incorrect. + var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0); + each(sysDims, function (sysDimItem) { + var sysDimItemDimsDef; + if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) { + dimCount = Math.max(dimCount, sysDimItemDimsDef.length); + } + }); + return dimCount; + } + function genCoordDimName(name, map, fromZero) { + if (fromZero || map.hasKey(name)) { + var i = 0; + while (map.hasKey(name + i)) { + i++; + } + name += i; + } + map.set(name, true); + return name; + } + + /** + * @class + * For example: + * { + * coordSysName: 'cartesian2d', + * coordSysDims: ['x', 'y', ...], + * axisMap: HashMap({ + * x: xAxisModel, + * y: yAxisModel + * }), + * categoryAxisMap: HashMap({ + * x: xAxisModel, + * y: undefined + * }), + * // The index of the first category axis in `coordSysDims`. + * // `null/undefined` means no category axis exists. + * firstCategoryDimIndex: 1, + * // To replace user specified encode. + * } + */ + var CoordSysInfo = /** @class */function () { + function CoordSysInfo(coordSysName) { + this.coordSysDims = []; + this.axisMap = createHashMap(); + this.categoryAxisMap = createHashMap(); + this.coordSysName = coordSysName; + } + return CoordSysInfo; + }(); + function getCoordSysInfoBySeries(seriesModel) { + var coordSysName = seriesModel.get('coordinateSystem'); + var result = new CoordSysInfo(coordSysName); + var fetch = fetchers[coordSysName]; + if (fetch) { + fetch(seriesModel, result, result.axisMap, result.categoryAxisMap); + return result; + } + } + var fetchers = { + cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) { + var xAxisModel = seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0]; + var yAxisModel = seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0]; + if ("development" !== 'production') { + if (!xAxisModel) { + throw new Error('xAxis "' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '" not found'); + } + if (!yAxisModel) { + throw new Error('yAxis "' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '" not found'); + } + } + result.coordSysDims = ['x', 'y']; + axisMap.set('x', xAxisModel); + axisMap.set('y', yAxisModel); + if (isCategory(xAxisModel)) { + categoryAxisMap.set('x', xAxisModel); + result.firstCategoryDimIndex = 0; + } + if (isCategory(yAxisModel)) { + categoryAxisMap.set('y', yAxisModel); + result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1); + } + }, + singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) { + var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0]; + if ("development" !== 'production') { + if (!singleAxisModel) { + throw new Error('singleAxis should be specified.'); + } + } + result.coordSysDims = ['single']; + axisMap.set('single', singleAxisModel); + if (isCategory(singleAxisModel)) { + categoryAxisMap.set('single', singleAxisModel); + result.firstCategoryDimIndex = 0; + } + }, + polar: function (seriesModel, result, axisMap, categoryAxisMap) { + var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0]; + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + if ("development" !== 'production') { + if (!angleAxisModel) { + throw new Error('angleAxis option not found'); + } + if (!radiusAxisModel) { + throw new Error('radiusAxis option not found'); + } + } + result.coordSysDims = ['radius', 'angle']; + axisMap.set('radius', radiusAxisModel); + axisMap.set('angle', angleAxisModel); + if (isCategory(radiusAxisModel)) { + categoryAxisMap.set('radius', radiusAxisModel); + result.firstCategoryDimIndex = 0; + } + if (isCategory(angleAxisModel)) { + categoryAxisMap.set('angle', angleAxisModel); + result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1); + } + }, + geo: function (seriesModel, result, axisMap, categoryAxisMap) { + result.coordSysDims = ['lng', 'lat']; + }, + parallel: function (seriesModel, result, axisMap, categoryAxisMap) { + var ecModel = seriesModel.ecModel; + var parallelModel = ecModel.getComponent('parallel', seriesModel.get('parallelIndex')); + var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice(); + each(parallelModel.parallelAxisIndex, function (axisIndex, index) { + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + var axisDim = coordSysDims[index]; + axisMap.set(axisDim, axisModel); + if (isCategory(axisModel)) { + categoryAxisMap.set(axisDim, axisModel); + if (result.firstCategoryDimIndex == null) { + result.firstCategoryDimIndex = index; + } + } + }); + } + }; + function isCategory(axisModel) { + return axisModel.get('type') === 'category'; + } + + /** + * Note that it is too complicated to support 3d stack by value + * (have to create two-dimension inverted index), so in 3d case + * we just support that stacked by index. + * + * @param seriesModel + * @param dimensionsInput The same as the input of . + * The input will be modified. + * @param opt + * @param opt.stackedCoordDimension Specify a coord dimension if needed. + * @param opt.byIndex=false + * @return calculationInfo + * { + * stackedDimension: string + * stackedByDimension: string + * isStackedByIndex: boolean + * stackedOverDimension: string + * stackResultDimension: string + * } + */ + function enableDataStack(seriesModel, dimensionsInput, opt) { + opt = opt || {}; + var byIndex = opt.byIndex; + var stackedCoordDimension = opt.stackedCoordDimension; + var dimensionDefineList; + var schema; + var store; + if (isLegacyDimensionsInput(dimensionsInput)) { + dimensionDefineList = dimensionsInput; + } else { + schema = dimensionsInput.schema; + dimensionDefineList = schema.dimensions; + store = dimensionsInput.store; + } + // Compatibal: when `stack` is set as '', do not stack. + var mayStack = !!(seriesModel && seriesModel.get('stack')); + var stackedByDimInfo; + var stackedDimInfo; + var stackResultDimension; + var stackedOverDimension; + each(dimensionDefineList, function (dimensionInfo, index) { + if (isString(dimensionInfo)) { + dimensionDefineList[index] = dimensionInfo = { + name: dimensionInfo + }; + } + if (mayStack && !dimensionInfo.isExtraCoord) { + // Find the first ordinal dimension as the stackedByDimInfo. + if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { + stackedByDimInfo = dimensionInfo; + } + // Find the first stackable dimension as the stackedDimInfo. + if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)) { + stackedDimInfo = dimensionInfo; + } + } + }); + if (stackedDimInfo && !byIndex && !stackedByDimInfo) { + // Compatible with previous design, value axis (time axis) only stack by index. + // It may make sense if the user provides elaborately constructed data. + byIndex = true; + } + // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`. + // That put stack logic in List is for using conveniently in echarts extensions, but it + // might not be a good way. + if (stackedDimInfo) { + // Use a weird name that not duplicated with other names. + // Also need to use seriesModel.id as postfix because different + // series may share same data store. The stack dimension needs to be distinguished. + stackResultDimension = '__\0ecstackresult_' + seriesModel.id; + stackedOverDimension = '__\0ecstackedover_' + seriesModel.id; + // Create inverted index to fast query index by value. + if (stackedByDimInfo) { + stackedByDimInfo.createInvertedIndices = true; + } + var stackedDimCoordDim_1 = stackedDimInfo.coordDim; + var stackedDimType = stackedDimInfo.type; + var stackedDimCoordIndex_1 = 0; + each(dimensionDefineList, function (dimensionInfo) { + if (dimensionInfo.coordDim === stackedDimCoordDim_1) { + stackedDimCoordIndex_1++; + } + }); + var stackedOverDimensionDefine = { + name: stackResultDimension, + coordDim: stackedDimCoordDim_1, + coordDimIndex: stackedDimCoordIndex_1, + type: stackedDimType, + isExtraCoord: true, + isCalculationCoord: true, + storeDimIndex: dimensionDefineList.length + }; + var stackResultDimensionDefine = { + name: stackedOverDimension, + // This dimension contains stack base (generally, 0), so do not set it as + // `stackedDimCoordDim` to avoid extent calculation, consider log scale. + coordDim: stackedOverDimension, + coordDimIndex: stackedDimCoordIndex_1 + 1, + type: stackedDimType, + isExtraCoord: true, + isCalculationCoord: true, + storeDimIndex: dimensionDefineList.length + 1 + }; + if (schema) { + if (store) { + stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType); + stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType); + } + schema.appendCalculationDimension(stackedOverDimensionDefine); + schema.appendCalculationDimension(stackResultDimensionDefine); + } else { + dimensionDefineList.push(stackedOverDimensionDefine); + dimensionDefineList.push(stackResultDimensionDefine); + } + } + return { + stackedDimension: stackedDimInfo && stackedDimInfo.name, + stackedByDimension: stackedByDimInfo && stackedByDimInfo.name, + isStackedByIndex: byIndex, + stackedOverDimension: stackedOverDimension, + stackResultDimension: stackResultDimension + }; + } + function isLegacyDimensionsInput(dimensionsInput) { + return !isSeriesDataSchema(dimensionsInput.schema); + } + function isDimensionStacked(data, stackedDim) { + // Each single series only maps to one pair of axis. So we do not need to + // check stackByDim, whatever stacked by a dimension or stacked by index. + return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension'); + } + function getStackedDimension(data, targetDim) { + return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim; + } + + function getCoordSysDimDefs(seriesModel, coordSysInfo) { + var coordSysName = seriesModel.get('coordinateSystem'); + var registeredCoordSys = CoordinateSystemManager.get(coordSysName); + var coordSysDimDefs; + if (coordSysInfo && coordSysInfo.coordSysDims) { + coordSysDimDefs = map(coordSysInfo.coordSysDims, function (dim) { + var dimInfo = { + name: dim + }; + var axisModel = coordSysInfo.axisMap.get(dim); + if (axisModel) { + var axisType = axisModel.get('type'); + dimInfo.type = getDimensionTypeByAxis(axisType); + } + return dimInfo; + }); + } + if (!coordSysDimDefs) { + // Get dimensions from registered coordinate system + coordSysDimDefs = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y']; + } + return coordSysDimDefs; + } + function injectOrdinalMeta(dimInfoList, createInvertedIndices, coordSysInfo) { + var firstCategoryDimIndex; + var hasNameEncode; + coordSysInfo && each(dimInfoList, function (dimInfo, dimIndex) { + var coordDim = dimInfo.coordDim; + var categoryAxisModel = coordSysInfo.categoryAxisMap.get(coordDim); + if (categoryAxisModel) { + if (firstCategoryDimIndex == null) { + firstCategoryDimIndex = dimIndex; + } + dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta(); + if (createInvertedIndices) { + dimInfo.createInvertedIndices = true; + } + } + if (dimInfo.otherDims.itemName != null) { + hasNameEncode = true; + } + }); + if (!hasNameEncode && firstCategoryDimIndex != null) { + dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0; + } + return firstCategoryDimIndex; + } + /** + * Caution: there are side effects to `sourceManager` in this method. + * Should better only be called in `Series['getInitialData']`. + */ + function createSeriesData(sourceRaw, seriesModel, opt) { + opt = opt || {}; + var sourceManager = seriesModel.getSourceManager(); + var source; + var isOriginalSource = false; + if (sourceRaw) { + isOriginalSource = true; + source = createSourceFromSeriesDataOption(sourceRaw); + } else { + source = sourceManager.getSource(); + // Is series.data. not dataset. + isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL; + } + var coordSysInfo = getCoordSysInfoBySeries(seriesModel); + var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo); + var useEncodeDefaulter = opt.useEncodeDefaulter; + var encodeDefaulter = isFunction(useEncodeDefaulter) ? useEncodeDefaulter : useEncodeDefaulter ? curry(makeSeriesEncodeForAxisCoordSys, coordSysDimDefs, seriesModel) : null; + var createDimensionOptions = { + coordDimensions: coordSysDimDefs, + generateCoord: opt.generateCoord, + encodeDefine: seriesModel.getEncode(), + encodeDefaulter: encodeDefaulter, + canOmitUnusedDimensions: !isOriginalSource + }; + var schema = prepareSeriesDataSchema(source, createDimensionOptions); + var firstCategoryDimIndex = injectOrdinalMeta(schema.dimensions, opt.createInvertedIndices, coordSysInfo); + var store = !isOriginalSource ? sourceManager.getSharedDataStore(schema) : null; + var stackCalculationInfo = enableDataStack(seriesModel, { + schema: schema, + store: store + }); + var data = new SeriesData(schema, seriesModel); + data.setCalculationInfo(stackCalculationInfo); + var dimValueGetter = firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source) ? function (itemOpt, dimName, dataIndex, dimIndex) { + // Use dataIndex as ordinal value in categoryAxis + return dimIndex === firstCategoryDimIndex ? dataIndex : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex); + } : null; + data.hasItemOption = false; + data.initData( + // Try to reuse the data store in sourceManager if using dataset. + isOriginalSource ? source : store, null, dimValueGetter); + return data; + } + function isNeedCompleteOrdinalData(source) { + if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) { + var sampleItem = firstDataNotNull(source.data || []); + return !isArray(getDataItemValue(sampleItem)); + } + } + function firstDataNotNull(arr) { + var i = 0; + while (i < arr.length && arr[i] == null) { + i++; + } + return arr[i]; + } + + var Scale = /** @class */function () { + function Scale(setting) { + this._setting = setting || {}; + this._extent = [Infinity, -Infinity]; + } + Scale.prototype.getSetting = function (name) { + return this._setting[name]; + }; + /** + * Set extent from data + */ + Scale.prototype.unionExtent = function (other) { + var extent = this._extent; + other[0] < extent[0] && (extent[0] = other[0]); + other[1] > extent[1] && (extent[1] = other[1]); + // not setExtent because in log axis it may transformed to power + // this.setExtent(extent[0], extent[1]); + }; + /** + * Set extent from data + */ + Scale.prototype.unionExtentFromData = function (data, dim) { + this.unionExtent(data.getApproximateExtent(dim)); + }; + /** + * Get extent + * + * Extent is always in increase order. + */ + Scale.prototype.getExtent = function () { + return this._extent.slice(); + }; + /** + * Set extent + */ + Scale.prototype.setExtent = function (start, end) { + var thisExtent = this._extent; + if (!isNaN(start)) { + thisExtent[0] = start; + } + if (!isNaN(end)) { + thisExtent[1] = end; + } + }; + /** + * If value is in extent range + */ + Scale.prototype.isInExtentRange = function (value) { + return this._extent[0] <= value && this._extent[1] >= value; + }; + /** + * When axis extent depends on data and no data exists, + * axis ticks should not be drawn, which is named 'blank'. + */ + Scale.prototype.isBlank = function () { + return this._isBlank; + }; + /** + * When axis extent depends on data and no data exists, + * axis ticks should not be drawn, which is named 'blank'. + */ + Scale.prototype.setBlank = function (isBlank) { + this._isBlank = isBlank; + }; + return Scale; + }(); + enableClassManagement(Scale); + + var uidBase = 0; + var OrdinalMeta = /** @class */function () { + function OrdinalMeta(opt) { + this.categories = opt.categories || []; + this._needCollect = opt.needCollect; + this._deduplication = opt.deduplication; + this.uid = ++uidBase; + } + OrdinalMeta.createByAxisModel = function (axisModel) { + var option = axisModel.option; + var data = option.data; + var categories = data && map(data, getName); + return new OrdinalMeta({ + categories: categories, + needCollect: !categories, + // deduplication is default in axis. + deduplication: option.dedplication !== false + }); + }; + OrdinalMeta.prototype.getOrdinal = function (category) { + // @ts-ignore + return this._getOrCreateMap().get(category); + }; + /** + * @return The ordinal. If not found, return NaN. + */ + OrdinalMeta.prototype.parseAndCollect = function (category) { + var index; + var needCollect = this._needCollect; + // The value of category dim can be the index of the given category set. + // This feature is only supported when !needCollect, because we should + // consider a common case: a value is 2017, which is a number but is + // expected to be tread as a category. This case usually happen in dataset, + // where it happent to be no need of the index feature. + if (!isString(category) && !needCollect) { + return category; + } + // Optimize for the scenario: + // category is ['2012-01-01', '2012-01-02', ...], where the input + // data has been ensured not duplicate and is large data. + // Notice, if a dataset dimension provide categroies, usually echarts + // should remove duplication except user tell echarts dont do that + // (set axis.deduplication = false), because echarts do not know whether + // the values in the category dimension has duplication (consider the + // parallel-aqi example) + if (needCollect && !this._deduplication) { + index = this.categories.length; + this.categories[index] = category; + return index; + } + var map = this._getOrCreateMap(); + // @ts-ignore + index = map.get(category); + if (index == null) { + if (needCollect) { + index = this.categories.length; + this.categories[index] = category; + // @ts-ignore + map.set(category, index); + } else { + index = NaN; + } + } + return index; + }; + // Consider big data, do not create map until needed. + OrdinalMeta.prototype._getOrCreateMap = function () { + return this._map || (this._map = createHashMap(this.categories)); + }; + return OrdinalMeta; + }(); + function getName(obj) { + if (isObject(obj) && obj.value != null) { + return obj.value; + } else { + return obj + ''; + } + } + + function isValueNice(val) { + var exp10 = Math.pow(10, quantityExponent(Math.abs(val))); + var f = Math.abs(val / exp10); + return f === 0 || f === 1 || f === 2 || f === 3 || f === 5; + } + function isIntervalOrLogScale(scale) { + return scale.type === 'interval' || scale.type === 'log'; + } + /** + * @param extent Both extent[0] and extent[1] should be valid number. + * Should be extent[0] < extent[1]. + * @param splitNumber splitNumber should be >= 1. + */ + function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) { + var result = {}; + var span = extent[1] - extent[0]; + var interval = result.interval = nice(span / splitNumber, true); + if (minInterval != null && interval < minInterval) { + interval = result.interval = minInterval; + } + if (maxInterval != null && interval > maxInterval) { + interval = result.interval = maxInterval; + } + // Tow more digital for tick. + var precision = result.intervalPrecision = getIntervalPrecision(interval); + // Niced extent inside original extent + var niceTickExtent = result.niceTickExtent = [round(Math.ceil(extent[0] / interval) * interval, precision), round(Math.floor(extent[1] / interval) * interval, precision)]; + fixExtent(niceTickExtent, extent); + return result; + } + function increaseInterval(interval) { + var exp10 = Math.pow(10, quantityExponent(interval)); + // Increase interval + var f = interval / exp10; + if (!f) { + f = 1; + } else if (f === 2) { + f = 3; + } else if (f === 3) { + f = 5; + } else { + // f is 1 or 5 + f *= 2; + } + return round(f * exp10); + } + /** + * @return interval precision + */ + function getIntervalPrecision(interval) { + // Tow more digital for tick. + return getPrecision(interval) + 2; + } + function clamp(niceTickExtent, idx, extent) { + niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]); + } + // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent. + function fixExtent(niceTickExtent, extent) { + !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]); + !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]); + clamp(niceTickExtent, 0, extent); + clamp(niceTickExtent, 1, extent); + if (niceTickExtent[0] > niceTickExtent[1]) { + niceTickExtent[0] = niceTickExtent[1]; + } + } + function contain$1(val, extent) { + return val >= extent[0] && val <= extent[1]; + } + function normalize$1(val, extent) { + if (extent[1] === extent[0]) { + return 0.5; + } + return (val - extent[0]) / (extent[1] - extent[0]); + } + function scale$2(val, extent) { + return val * (extent[1] - extent[0]) + extent[0]; + } + + var OrdinalScale = /** @class */function (_super) { + __extends(OrdinalScale, _super); + function OrdinalScale(setting) { + var _this = _super.call(this, setting) || this; + _this.type = 'ordinal'; + var ordinalMeta = _this.getSetting('ordinalMeta'); + // Caution: Should not use instanceof, consider ec-extensions using + // import approach to get OrdinalMeta class. + if (!ordinalMeta) { + ordinalMeta = new OrdinalMeta({}); + } + if (isArray(ordinalMeta)) { + ordinalMeta = new OrdinalMeta({ + categories: map(ordinalMeta, function (item) { + return isObject(item) ? item.value : item; + }) + }); + } + _this._ordinalMeta = ordinalMeta; + _this._extent = _this.getSetting('extent') || [0, ordinalMeta.categories.length - 1]; + return _this; + } + OrdinalScale.prototype.parse = function (val) { + // Caution: Math.round(null) will return `0` rather than `NaN` + if (val == null) { + return NaN; + } + return isString(val) ? this._ordinalMeta.getOrdinal(val) + // val might be float. + : Math.round(val); + }; + OrdinalScale.prototype.contain = function (rank) { + rank = this.parse(rank); + return contain$1(rank, this._extent) && this._ordinalMeta.categories[rank] != null; + }; + /** + * Normalize given rank or name to linear [0, 1] + * @param val raw ordinal number. + * @return normalized value in [0, 1]. + */ + OrdinalScale.prototype.normalize = function (val) { + val = this._getTickNumber(this.parse(val)); + return normalize$1(val, this._extent); + }; + /** + * @param val normalized value in [0, 1]. + * @return raw ordinal number. + */ + OrdinalScale.prototype.scale = function (val) { + val = Math.round(scale$2(val, this._extent)); + return this.getRawOrdinalNumber(val); + }; + OrdinalScale.prototype.getTicks = function () { + var ticks = []; + var extent = this._extent; + var rank = extent[0]; + while (rank <= extent[1]) { + ticks.push({ + value: rank + }); + rank++; + } + return ticks; + }; + OrdinalScale.prototype.getMinorTicks = function (splitNumber) { + // Not support. + return; + }; + /** + * @see `Ordinal['_ordinalNumbersByTick']` + */ + OrdinalScale.prototype.setSortInfo = function (info) { + if (info == null) { + this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null; + return; + } + var infoOrdinalNumbers = info.ordinalNumbers; + var ordinalsByTick = this._ordinalNumbersByTick = []; + var ticksByOrdinal = this._ticksByOrdinalNumber = []; + // Unnecessary support negative tick in `realtimeSort`. + var tickNum = 0; + var allCategoryLen = this._ordinalMeta.categories.length; + for (var len = Math.min(allCategoryLen, infoOrdinalNumbers.length); tickNum < len; ++tickNum) { + var ordinalNumber = infoOrdinalNumbers[tickNum]; + ordinalsByTick[tickNum] = ordinalNumber; + ticksByOrdinal[ordinalNumber] = tickNum; + } + // Handle that `series.data` only covers part of the `axis.category.data`. + var unusedOrdinal = 0; + for (; tickNum < allCategoryLen; ++tickNum) { + while (ticksByOrdinal[unusedOrdinal] != null) { + unusedOrdinal++; + } + ordinalsByTick.push(unusedOrdinal); + ticksByOrdinal[unusedOrdinal] = tickNum; + } + }; + OrdinalScale.prototype._getTickNumber = function (ordinal) { + var ticksByOrdinalNumber = this._ticksByOrdinalNumber; + // also support ordinal out of range of `ordinalMeta.categories.length`, + // where ordinal numbers are used as tick value directly. + return ticksByOrdinalNumber && ordinal >= 0 && ordinal < ticksByOrdinalNumber.length ? ticksByOrdinalNumber[ordinal] : ordinal; + }; + /** + * @usage + * ```js + * const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal); + * + * // case0 + * const rawOrdinalValue = axisModel.getCategories()[ordinalNumber]; + * // case1 + * const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber]; + * // case2 + * const coord = axis.dataToCoord(ordinalNumber); + * ``` + * + * @param {OrdinalNumber} tickNumber index of display + */ + OrdinalScale.prototype.getRawOrdinalNumber = function (tickNumber) { + var ordinalNumbersByTick = this._ordinalNumbersByTick; + // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`., + // where ordinal numbers are used as tick value directly. + return ordinalNumbersByTick && tickNumber >= 0 && tickNumber < ordinalNumbersByTick.length ? ordinalNumbersByTick[tickNumber] : tickNumber; + }; + /** + * Get item on tick + */ + OrdinalScale.prototype.getLabel = function (tick) { + if (!this.isBlank()) { + var ordinalNumber = this.getRawOrdinalNumber(tick.value); + var cateogry = this._ordinalMeta.categories[ordinalNumber]; + // Note that if no data, ordinalMeta.categories is an empty array. + // Return empty if it's not exist. + return cateogry == null ? '' : cateogry + ''; + } + }; + OrdinalScale.prototype.count = function () { + return this._extent[1] - this._extent[0] + 1; + }; + OrdinalScale.prototype.unionExtentFromData = function (data, dim) { + this.unionExtent(data.getApproximateExtent(dim)); + }; + /** + * @override + * If value is in extent range + */ + OrdinalScale.prototype.isInExtentRange = function (value) { + value = this._getTickNumber(value); + return this._extent[0] <= value && this._extent[1] >= value; + }; + OrdinalScale.prototype.getOrdinalMeta = function () { + return this._ordinalMeta; + }; + OrdinalScale.prototype.calcNiceTicks = function () {}; + OrdinalScale.prototype.calcNiceExtent = function () {}; + OrdinalScale.type = 'ordinal'; + return OrdinalScale; + }(Scale); + Scale.registerClass(OrdinalScale); + + var roundNumber = round; + var IntervalScale = /** @class */function (_super) { + __extends(IntervalScale, _super); + function IntervalScale() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'interval'; + // Step is calculated in adjustExtent. + _this._interval = 0; + _this._intervalPrecision = 2; + return _this; + } + IntervalScale.prototype.parse = function (val) { + return val; + }; + IntervalScale.prototype.contain = function (val) { + return contain$1(val, this._extent); + }; + IntervalScale.prototype.normalize = function (val) { + return normalize$1(val, this._extent); + }; + IntervalScale.prototype.scale = function (val) { + return scale$2(val, this._extent); + }; + IntervalScale.prototype.setExtent = function (start, end) { + var thisExtent = this._extent; + // start,end may be a Number like '25',so... + if (!isNaN(start)) { + thisExtent[0] = parseFloat(start); + } + if (!isNaN(end)) { + thisExtent[1] = parseFloat(end); + } + }; + IntervalScale.prototype.unionExtent = function (other) { + var extent = this._extent; + other[0] < extent[0] && (extent[0] = other[0]); + other[1] > extent[1] && (extent[1] = other[1]); + // unionExtent may called by it's sub classes + this.setExtent(extent[0], extent[1]); + }; + IntervalScale.prototype.getInterval = function () { + return this._interval; + }; + IntervalScale.prototype.setInterval = function (interval) { + this._interval = interval; + // Dropped auto calculated niceExtent and use user-set extent. + // We assume user wants to set both interval, min, max to get a better result. + this._niceExtent = this._extent.slice(); + this._intervalPrecision = getIntervalPrecision(interval); + }; + /** + * @param expandToNicedExtent Whether expand the ticks to niced extent. + */ + IntervalScale.prototype.getTicks = function (expandToNicedExtent) { + var interval = this._interval; + var extent = this._extent; + var niceTickExtent = this._niceExtent; + var intervalPrecision = this._intervalPrecision; + var ticks = []; + // If interval is 0, return []; + if (!interval) { + return ticks; + } + // Consider this case: using dataZoom toolbox, zoom and zoom. + var safeLimit = 10000; + if (extent[0] < niceTickExtent[0]) { + if (expandToNicedExtent) { + ticks.push({ + value: roundNumber(niceTickExtent[0] - interval, intervalPrecision) + }); + } else { + ticks.push({ + value: extent[0] + }); + } + } + var tick = niceTickExtent[0]; + while (tick <= niceTickExtent[1]) { + ticks.push({ + value: tick + }); + // Avoid rounding error + tick = roundNumber(tick + interval, intervalPrecision); + if (tick === ticks[ticks.length - 1].value) { + // Consider out of safe float point, e.g., + // -3711126.9907707 + 2e-10 === -3711126.9907707 + break; + } + if (ticks.length > safeLimit) { + return []; + } + } + // Consider this case: the last item of ticks is smaller + // than niceTickExtent[1] and niceTickExtent[1] === extent[1]. + var lastNiceTick = ticks.length ? ticks[ticks.length - 1].value : niceTickExtent[1]; + if (extent[1] > lastNiceTick) { + if (expandToNicedExtent) { + ticks.push({ + value: roundNumber(lastNiceTick + interval, intervalPrecision) + }); + } else { + ticks.push({ + value: extent[1] + }); + } + } + return ticks; + }; + IntervalScale.prototype.getMinorTicks = function (splitNumber) { + var ticks = this.getTicks(true); + var minorTicks = []; + var extent = this.getExtent(); + for (var i = 1; i < ticks.length; i++) { + var nextTick = ticks[i]; + var prevTick = ticks[i - 1]; + var count = 0; + var minorTicksGroup = []; + var interval = nextTick.value - prevTick.value; + var minorInterval = interval / splitNumber; + while (count < splitNumber - 1) { + var minorTick = roundNumber(prevTick.value + (count + 1) * minorInterval); + // For the first and last interval. The count may be less than splitNumber. + if (minorTick > extent[0] && minorTick < extent[1]) { + minorTicksGroup.push(minorTick); + } + count++; + } + minorTicks.push(minorTicksGroup); + } + return minorTicks; + }; + /** + * @param opt.precision If 'auto', use nice presision. + * @param opt.pad returns 1.50 but not 1.5 if precision is 2. + */ + IntervalScale.prototype.getLabel = function (data, opt) { + if (data == null) { + return ''; + } + var precision = opt && opt.precision; + if (precision == null) { + precision = getPrecision(data.value) || 0; + } else if (precision === 'auto') { + // Should be more precise then tick. + precision = this._intervalPrecision; + } + // (1) If `precision` is set, 12.005 should be display as '12.00500'. + // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'. + var dataNum = roundNumber(data.value, precision, true); + return addCommas(dataNum); + }; + /** + * @param splitNumber By default `5`. + */ + IntervalScale.prototype.calcNiceTicks = function (splitNumber, minInterval, maxInterval) { + splitNumber = splitNumber || 5; + var extent = this._extent; + var span = extent[1] - extent[0]; + if (!isFinite(span)) { + return; + } + // User may set axis min 0 and data are all negative + // FIXME If it needs to reverse ? + if (span < 0) { + span = -span; + extent.reverse(); + } + var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval); + this._intervalPrecision = result.intervalPrecision; + this._interval = result.interval; + this._niceExtent = result.niceTickExtent; + }; + IntervalScale.prototype.calcNiceExtent = function (opt) { + var extent = this._extent; + // If extent start and end are same, expand them + if (extent[0] === extent[1]) { + if (extent[0] !== 0) { + // Expand extent + // Note that extents can be both negative. See #13154 + var expandSize = Math.abs(extent[0]); + // In the fowllowing case + // Axis has been fixed max 100 + // Plus data are all 100 and axis extent are [100, 100]. + // Extend to the both side will cause expanded max is larger than fixed max. + // So only expand to the smaller side. + if (!opt.fixMax) { + extent[1] += expandSize / 2; + extent[0] -= expandSize / 2; + } else { + extent[0] -= expandSize / 2; + } + } else { + extent[1] = 1; + } + } + var span = extent[1] - extent[0]; + // If there are no data and extent are [Infinity, -Infinity] + if (!isFinite(span)) { + extent[0] = 0; + extent[1] = 1; + } + this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); + // let extent = this._extent; + var interval = this._interval; + if (!opt.fixMin) { + extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval); + } + if (!opt.fixMax) { + extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval); + } + }; + IntervalScale.prototype.setNiceExtent = function (min, max) { + this._niceExtent = [min, max]; + }; + IntervalScale.type = 'interval'; + return IntervalScale; + }(Scale); + Scale.registerClass(IntervalScale); + + /* global Float32Array */ + var supportFloat32Array = typeof Float32Array !== 'undefined'; + var Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array; + function createFloat32Array(arg) { + if (isArray(arg)) { + // Return self directly if don't support TypedArray. + return supportFloat32Array ? new Float32Array(arg) : arg; + } + // Else is number + return new Float32ArrayCtor(arg); + } + + var STACK_PREFIX = '__ec_stack_'; + function getSeriesStackId(seriesModel) { + return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex; + } + function getAxisKey(axis) { + return axis.dim + axis.index; + } + /** + * @return {Object} {width, offset, offsetCenter} If axis.type is not 'category', return undefined. + */ + function getLayoutOnAxis(opt) { + var params = []; + var baseAxis = opt.axis; + var axisKey = 'axis0'; + if (baseAxis.type !== 'category') { + return; + } + var bandWidth = baseAxis.getBandWidth(); + for (var i = 0; i < opt.count || 0; i++) { + params.push(defaults({ + bandWidth: bandWidth, + axisKey: axisKey, + stackId: STACK_PREFIX + i + }, opt)); + } + var widthAndOffsets = doCalBarWidthAndOffset(params); + var result = []; + for (var i = 0; i < opt.count; i++) { + var item = widthAndOffsets[axisKey][STACK_PREFIX + i]; + item.offsetCenter = item.offset + item.width / 2; + result.push(item); + } + return result; + } + function prepareLayoutBarSeries(seriesType, ecModel) { + var seriesModels = []; + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + // Check series coordinate, do layout for cartesian2d only + if (isOnCartesian(seriesModel)) { + seriesModels.push(seriesModel); + } + }); + return seriesModels; + } + /** + * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent + * values. + * This works for time axes, value axes, and log axes. + * For a single time axis, return value is in the form like + * {'x_0': [1000000]}. + * The value of 1000000 is in milliseconds. + */ + function getValueAxesMinGaps(barSeries) { + /** + * Map from axis.index to values. + * For a single time axis, axisValues is in the form like + * {'x_0': [1495555200000, 1495641600000, 1495728000000]}. + * Items in axisValues[x], e.g. 1495555200000, are time values of all + * series. + */ + var axisValues = {}; + each(barSeries, function (seriesModel) { + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + if (baseAxis.type !== 'time' && baseAxis.type !== 'value') { + return; + } + var data = seriesModel.getData(); + var key = baseAxis.dim + '_' + baseAxis.index; + var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim)); + var store = data.getStore(); + for (var i = 0, cnt = store.count(); i < cnt; ++i) { + var value = store.get(dimIdx, i); + if (!axisValues[key]) { + // No previous data for the axis + axisValues[key] = [value]; + } else { + // No value in previous series + axisValues[key].push(value); + } + // Ignore duplicated time values in the same axis + } + }); + + var axisMinGaps = {}; + for (var key in axisValues) { + if (axisValues.hasOwnProperty(key)) { + var valuesInAxis = axisValues[key]; + if (valuesInAxis) { + // Sort axis values into ascending order to calculate gaps + valuesInAxis.sort(function (a, b) { + return a - b; + }); + var min = null; + for (var j = 1; j < valuesInAxis.length; ++j) { + var delta = valuesInAxis[j] - valuesInAxis[j - 1]; + if (delta > 0) { + // Ignore 0 delta because they are of the same axis value + min = min === null ? delta : Math.min(min, delta); + } + } + // Set to null if only have one data + axisMinGaps[key] = min; + } + } + } + return axisMinGaps; + } + function makeColumnLayout(barSeries) { + var axisMinGaps = getValueAxesMinGaps(barSeries); + var seriesInfoList = []; + each(barSeries, function (seriesModel) { + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var axisExtent = baseAxis.getExtent(); + var bandWidth; + if (baseAxis.type === 'category') { + bandWidth = baseAxis.getBandWidth(); + } else if (baseAxis.type === 'value' || baseAxis.type === 'time') { + var key = baseAxis.dim + '_' + baseAxis.index; + var minGap = axisMinGaps[key]; + var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]); + var scale = baseAxis.scale.getExtent(); + var scaleSpan = Math.abs(scale[1] - scale[0]); + bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value + } else { + var data = seriesModel.getData(); + bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count(); + } + var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth); + var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth); + var barMinWidth = parsePercent$1( + // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis, + // the auto-calculated bar width might be less than 0.5 / 1. + seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidth); + var barGap = seriesModel.get('barGap'); + var barCategoryGap = seriesModel.get('barCategoryGap'); + seriesInfoList.push({ + bandWidth: bandWidth, + barWidth: barWidth, + barMaxWidth: barMaxWidth, + barMinWidth: barMinWidth, + barGap: barGap, + barCategoryGap: barCategoryGap, + axisKey: getAxisKey(baseAxis), + stackId: getSeriesStackId(seriesModel) + }); + }); + return doCalBarWidthAndOffset(seriesInfoList); + } + function doCalBarWidthAndOffset(seriesInfoList) { + // Columns info on each category axis. Key is cartesian name + var columnsMap = {}; + each(seriesInfoList, function (seriesInfo, idx) { + var axisKey = seriesInfo.axisKey; + var bandWidth = seriesInfo.bandWidth; + var columnsOnAxis = columnsMap[axisKey] || { + bandWidth: bandWidth, + remainedWidth: bandWidth, + autoWidthCount: 0, + categoryGap: null, + gap: '20%', + stacks: {} + }; + var stacks = columnsOnAxis.stacks; + columnsMap[axisKey] = columnsOnAxis; + var stackId = seriesInfo.stackId; + if (!stacks[stackId]) { + columnsOnAxis.autoWidthCount++; + } + stacks[stackId] = stacks[stackId] || { + width: 0, + maxWidth: 0 + }; + // Caution: In a single coordinate system, these barGrid attributes + // will be shared by series. Consider that they have default values, + // only the attributes set on the last series will work. + // Do not change this fact unless there will be a break change. + var barWidth = seriesInfo.barWidth; + if (barWidth && !stacks[stackId].width) { + // See #6312, do not restrict width. + stacks[stackId].width = barWidth; + barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); + columnsOnAxis.remainedWidth -= barWidth; + } + var barMaxWidth = seriesInfo.barMaxWidth; + barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); + var barMinWidth = seriesInfo.barMinWidth; + barMinWidth && (stacks[stackId].minWidth = barMinWidth); + var barGap = seriesInfo.barGap; + barGap != null && (columnsOnAxis.gap = barGap); + var barCategoryGap = seriesInfo.barCategoryGap; + barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap); + }); + var result = {}; + each(columnsMap, function (columnsOnAxis, coordSysName) { + result[coordSysName] = {}; + var stacks = columnsOnAxis.stacks; + var bandWidth = columnsOnAxis.bandWidth; + var categoryGapPercent = columnsOnAxis.categoryGap; + if (categoryGapPercent == null) { + var columnCount = keys(stacks).length; + // More columns in one group + // the spaces between group is smaller. Or the column will be too thin. + categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%'; + } + var categoryGap = parsePercent$1(categoryGapPercent, bandWidth); + var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1); + var remainedWidth = columnsOnAxis.remainedWidth; + var autoWidthCount = columnsOnAxis.autoWidthCount; + var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + // Find if any auto calculated bar exceeded maxBarWidth + each(stacks, function (column) { + var maxWidth = column.maxWidth; + var minWidth = column.minWidth; + if (!column.width) { + var finalWidth = autoWidth; + if (maxWidth && maxWidth < finalWidth) { + finalWidth = Math.min(maxWidth, remainedWidth); + } + // `minWidth` has higher priority. `minWidth` decide that whether the + // bar is able to be visible. So `minWidth` should not be restricted + // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In + // the extreme cases for `value` axis, bars are allowed to overlap + // with each other if `minWidth` specified. + if (minWidth && minWidth > finalWidth) { + finalWidth = minWidth; + } + if (finalWidth !== autoWidth) { + column.width = finalWidth; + remainedWidth -= finalWidth + barGapPercent * finalWidth; + autoWidthCount--; + } + } else { + // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as + // CSS does. Because barWidth can be a percent value, where + // `barMaxWidth` can be used to restrict the final width. + var finalWidth = column.width; + if (maxWidth) { + finalWidth = Math.min(finalWidth, maxWidth); + } + // `minWidth` has higher priority, as described above + if (minWidth) { + finalWidth = Math.max(finalWidth, minWidth); + } + column.width = finalWidth; + remainedWidth -= finalWidth + barGapPercent * finalWidth; + autoWidthCount--; + } + }); + // Recalculate width again + autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + var widthSum = 0; + var lastColumn; + each(stacks, function (column, idx) { + if (!column.width) { + column.width = autoWidth; + } + lastColumn = column; + widthSum += column.width * (1 + barGapPercent); + }); + if (lastColumn) { + widthSum -= lastColumn.width * barGapPercent; + } + var offset = -widthSum / 2; + each(stacks, function (column, stackId) { + result[coordSysName][stackId] = result[coordSysName][stackId] || { + bandWidth: bandWidth, + offset: offset, + width: column.width + }; + offset += column.width * (1 + barGapPercent); + }); + }); + return result; + } + function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) { + if (barWidthAndOffset && axis) { + var result = barWidthAndOffset[getAxisKey(axis)]; + if (result != null && seriesModel != null) { + return result[getSeriesStackId(seriesModel)]; + } + return result; + } + } + function layout(seriesType, ecModel) { + var seriesModels = prepareLayoutBarSeries(seriesType, ecModel); + var barWidthAndOffset = makeColumnLayout(seriesModels); + each(seriesModels, function (seriesModel) { + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var stackId = getSeriesStackId(seriesModel); + var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId]; + var columnOffset = columnLayoutInfo.offset; + var columnWidth = columnLayoutInfo.width; + data.setLayout({ + bandWidth: columnLayoutInfo.bandWidth, + offset: columnOffset, + size: columnWidth + }); + }); + } + // TODO: Do not support stack in large mode yet. + function createProgressiveLayout(seriesType) { + return { + seriesType: seriesType, + plan: createRenderPlanner(), + reset: function (seriesModel) { + if (!isOnCartesian(seriesModel)) { + return; + } + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var valueAxis = cartesian.getOtherAxis(baseAxis); + var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim)); + var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim)); + var drawBackground = seriesModel.get('showBackground', true); + var valueDim = data.mapDimension(valueAxis.dim); + var stackResultDim = data.getCalculationInfo('stackResultDimension'); + var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries'); + var isValueAxisH = valueAxis.isHorizontal(); + var valueAxisStart = getValueAxisStart(baseAxis, valueAxis); + var isLarge = isInLargeMode(seriesModel); + var barMinHeight = seriesModel.get('barMinHeight') || 0; + var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); + // Layout info. + var columnWidth = data.getLayout('size'); + var columnOffset = data.getLayout('offset'); + return { + progress: function (params, data) { + var count = params.count; + var largePoints = isLarge && createFloat32Array(count * 3); + var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3); + var largeDataIndices = isLarge && createFloat32Array(count); + var coordLayout = cartesian.master.getRect(); + var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height; + var dataIndex; + var store = data.getStore(); + var idxOffset = 0; + while ((dataIndex = params.next()) != null) { + var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex); + var baseValue = store.get(baseDimIdx, dataIndex); + var baseCoord = valueAxisStart; + var startValue = void 0; + // Because of the barMinHeight, we can not use the value in + // stackResultDimension directly. + if (stacked) { + startValue = +value - store.get(valueDimIdx, dataIndex); + } + var x = void 0; + var y = void 0; + var width = void 0; + var height = void 0; + if (isValueAxisH) { + var coord = cartesian.dataToPoint([value, baseValue]); + if (stacked) { + var startCoord = cartesian.dataToPoint([startValue, baseValue]); + baseCoord = startCoord[0]; + } + x = baseCoord; + y = coord[1] + columnOffset; + width = coord[0] - baseCoord; + height = columnWidth; + if (Math.abs(width) < barMinHeight) { + width = (width < 0 ? -1 : 1) * barMinHeight; + } + } else { + var coord = cartesian.dataToPoint([baseValue, value]); + if (stacked) { + var startCoord = cartesian.dataToPoint([baseValue, startValue]); + baseCoord = startCoord[1]; + } + x = coord[0] + columnOffset; + y = baseCoord; + width = columnWidth; + height = coord[1] - baseCoord; + if (Math.abs(height) < barMinHeight) { + // Include zero to has a positive bar + height = (height <= 0 ? -1 : 1) * barMinHeight; + } + } + if (!isLarge) { + data.setItemLayout(dataIndex, { + x: x, + y: y, + width: width, + height: height + }); + } else { + largePoints[idxOffset] = x; + largePoints[idxOffset + 1] = y; + largePoints[idxOffset + 2] = isValueAxisH ? width : height; + if (largeBackgroundPoints) { + largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x; + largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y; + largeBackgroundPoints[idxOffset + 2] = bgSize; + } + largeDataIndices[dataIndex] = dataIndex; + } + idxOffset += 3; + } + if (isLarge) { + data.setLayout({ + largePoints: largePoints, + largeDataIndices: largeDataIndices, + largeBackgroundPoints: largeBackgroundPoints, + valueAxisHorizontal: isValueAxisH + }); + } + } + }; + } + }; + } + function isOnCartesian(seriesModel) { + return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d'; + } + function isInLargeMode(seriesModel) { + return seriesModel.pipelineContext && seriesModel.pipelineContext.large; + } + // See cases in `test/bar-start.html` and `#7412`, `#8747`. + function getValueAxisStart(baseAxis, valueAxis) { + return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0)); + } + + // FIXME 公用? + var bisect = function (a, x, lo, hi) { + while (lo < hi) { + var mid = lo + hi >>> 1; + if (a[mid][1] < x) { + lo = mid + 1; + } else { + hi = mid; + } + } + return lo; + }; + var TimeScale = /** @class */function (_super) { + __extends(TimeScale, _super); + function TimeScale(settings) { + var _this = _super.call(this, settings) || this; + _this.type = 'time'; + return _this; + } + /** + * Get label is mainly for other components like dataZoom, tooltip. + */ + TimeScale.prototype.getLabel = function (tick) { + var useUTC = this.getSetting('useUTC'); + return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale')); + }; + TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) { + var isUTC = this.getSetting('useUTC'); + var lang = this.getSetting('locale'); + return leveledFormat(tick, idx, labelFormatter, lang, isUTC); + }; + /** + * @override + */ + TimeScale.prototype.getTicks = function () { + var interval = this._interval; + var extent = this._extent; + var ticks = []; + // If interval is 0, return []; + if (!interval) { + return ticks; + } + ticks.push({ + value: extent[0], + level: 0 + }); + var useUTC = this.getSetting('useUTC'); + var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent); + ticks = ticks.concat(innerTicks); + ticks.push({ + value: extent[1], + level: 0 + }); + return ticks; + }; + TimeScale.prototype.calcNiceExtent = function (opt) { + var extent = this._extent; + // If extent start and end are same, expand them + if (extent[0] === extent[1]) { + // Expand extent + extent[0] -= ONE_DAY; + extent[1] += ONE_DAY; + } + // If there are no data and extent are [Infinity, -Infinity] + if (extent[1] === -Infinity && extent[0] === Infinity) { + var d = new Date(); + extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate()); + extent[0] = extent[1] - ONE_DAY; + } + this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); + }; + TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) { + approxTickNum = approxTickNum || 10; + var extent = this._extent; + var span = extent[1] - extent[0]; + this._approxInterval = span / approxTickNum; + if (minInterval != null && this._approxInterval < minInterval) { + this._approxInterval = minInterval; + } + if (maxInterval != null && this._approxInterval > maxInterval) { + this._approxInterval = maxInterval; + } + var scaleIntervalsLen = scaleIntervals.length; + var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); + // Interval that can be used to calculate ticks + this._interval = scaleIntervals[idx][1]; + // Min level used when picking ticks from top down. + // We check one more level to avoid the ticks are to sparse in some case. + this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0]; + }; + TimeScale.prototype.parse = function (val) { + // val might be float. + return isNumber(val) ? val : +parseDate(val); + }; + TimeScale.prototype.contain = function (val) { + return contain$1(this.parse(val), this._extent); + }; + TimeScale.prototype.normalize = function (val) { + return normalize$1(this.parse(val), this._extent); + }; + TimeScale.prototype.scale = function (val) { + return scale$2(val, this._extent); + }; + TimeScale.type = 'time'; + return TimeScale; + }(IntervalScale); + /** + * This implementation was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + */ + var scaleIntervals = [ + // Format interval + ['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y + ]; + + function isUnitValueSame(unit, valueA, valueB, isUTC) { + var dateA = parseDate(valueA); + var dateB = parseDate(valueB); + var isSame = function (unit) { + return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC); + }; + var isSameYear = function () { + return isSame('year'); + }; + // const isSameHalfYear = () => isSameYear() && isSame('half-year'); + // const isSameQuater = () => isSameYear() && isSame('quarter'); + var isSameMonth = function () { + return isSameYear() && isSame('month'); + }; + var isSameDay = function () { + return isSameMonth() && isSame('day'); + }; + // const isSameHalfDay = () => isSameDay() && isSame('half-day'); + var isSameHour = function () { + return isSameDay() && isSame('hour'); + }; + var isSameMinute = function () { + return isSameHour() && isSame('minute'); + }; + var isSameSecond = function () { + return isSameMinute() && isSame('second'); + }; + var isSameMilliSecond = function () { + return isSameSecond() && isSame('millisecond'); + }; + switch (unit) { + case 'year': + return isSameYear(); + case 'month': + return isSameMonth(); + case 'day': + return isSameDay(); + case 'hour': + return isSameHour(); + case 'minute': + return isSameMinute(); + case 'second': + return isSameSecond(); + case 'millisecond': + return isSameMilliSecond(); + } + } + // const primaryUnitGetters = { + // year: fullYearGetterName(), + // month: monthGetterName(), + // day: dateGetterName(), + // hour: hoursGetterName(), + // minute: minutesGetterName(), + // second: secondsGetterName(), + // millisecond: millisecondsGetterName() + // }; + // const primaryUnitUTCGetters = { + // year: fullYearGetterName(true), + // month: monthGetterName(true), + // day: dateGetterName(true), + // hour: hoursGetterName(true), + // minute: minutesGetterName(true), + // second: secondsGetterName(true), + // millisecond: millisecondsGetterName(true) + // }; + // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) { + // step = step || 1; + // switch (getPrimaryTimeUnit(unitName)) { + // case 'year': + // date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step); + // break; + // case 'month': + // date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step); + // break; + // case 'day': + // date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step); + // break; + // case 'hour': + // date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step); + // break; + // case 'minute': + // date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step); + // break; + // case 'second': + // date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step); + // break; + // case 'millisecond': + // date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step); + // break; + // } + // return date.getTime(); + // } + // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]]; + // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]]; + // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]]; + function getDateInterval(approxInterval, daysInMonth) { + approxInterval /= ONE_DAY; + return approxInterval > 16 ? 16 + // Math.floor(daysInMonth / 2) + 1 // In this case we only want one tick between two months. + : approxInterval > 7.5 ? 7 // TODO week 7 or day 8? + : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1; + } + function getMonthInterval(approxInterval) { + var APPROX_ONE_MONTH = 30 * ONE_DAY; + approxInterval /= APPROX_ONE_MONTH; + return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1; + } + function getHourInterval(approxInterval) { + approxInterval /= ONE_HOUR; + return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1; + } + function getMinutesAndSecondsInterval(approxInterval, isMinutes) { + approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND; + return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1; + } + function getMillisecondsInterval(approxInterval) { + return nice(approxInterval, true); + } + function getFirstTimestampOfUnit(date, unitName, isUTC) { + var outDate = new Date(date); + switch (getPrimaryTimeUnit(unitName)) { + case 'year': + case 'month': + outDate[monthSetterName(isUTC)](0); + case 'day': + outDate[dateSetterName(isUTC)](1); + case 'hour': + outDate[hoursSetterName(isUTC)](0); + case 'minute': + outDate[minutesSetterName(isUTC)](0); + case 'second': + outDate[secondsSetterName(isUTC)](0); + outDate[millisecondsSetterName(isUTC)](0); + } + return outDate.getTime(); + } + function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) { + var safeLimit = 10000; + var unitNames = timeUnits; + var iter = 0; + function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) { + var date = new Date(minTimestamp); + var dateTime = minTimestamp; + var d = date[getMethodName](); + // if (isDate) { + // d -= 1; // Starts with 0; PENDING + // } + while (dateTime < maxTimestamp && dateTime <= extent[1]) { + out.push({ + value: dateTime + }); + d += interval; + date[setMethodName](d); + dateTime = date.getTime(); + } + // This extra tick is for calcuating ticks of next level. Will not been added to the final result + out.push({ + value: dateTime, + notAdd: true + }); + } + function addLevelTicks(unitName, lastLevelTicks, levelTicks) { + var newAddedTicks = []; + var isFirstLevel = !lastLevelTicks.length; + if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) { + return; + } + if (isFirstLevel) { + lastLevelTicks = [{ + // TODO Optimize. Not include so may ticks. + value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC) + }, { + value: extent[1] + }]; + } + for (var i = 0; i < lastLevelTicks.length - 1; i++) { + var startTick = lastLevelTicks[i].value; + var endTick = lastLevelTicks[i + 1].value; + if (startTick === endTick) { + continue; + } + var interval = void 0; + var getterName = void 0; + var setterName = void 0; + var isDate = false; + switch (unitName) { + case 'year': + interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365)); + getterName = fullYearGetterName(isUTC); + setterName = fullYearSetterName(isUTC); + break; + case 'half-year': + case 'quarter': + case 'month': + interval = getMonthInterval(approxInterval); + getterName = monthGetterName(isUTC); + setterName = monthSetterName(isUTC); + break; + case 'week': // PENDING If week is added. Ignore day. + case 'half-week': + case 'day': + interval = getDateInterval(approxInterval); // Use 32 days and let interval been 16 + getterName = dateGetterName(isUTC); + setterName = dateSetterName(isUTC); + isDate = true; + break; + case 'half-day': + case 'quarter-day': + case 'hour': + interval = getHourInterval(approxInterval); + getterName = hoursGetterName(isUTC); + setterName = hoursSetterName(isUTC); + break; + case 'minute': + interval = getMinutesAndSecondsInterval(approxInterval, true); + getterName = minutesGetterName(isUTC); + setterName = minutesSetterName(isUTC); + break; + case 'second': + interval = getMinutesAndSecondsInterval(approxInterval, false); + getterName = secondsGetterName(isUTC); + setterName = secondsSetterName(isUTC); + break; + case 'millisecond': + interval = getMillisecondsInterval(approxInterval); + getterName = millisecondsGetterName(isUTC); + setterName = millisecondsSetterName(isUTC); + break; + } + addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks); + if (unitName === 'year' && levelTicks.length > 1 && i === 0) { + // Add nearest years to the left extent. + levelTicks.unshift({ + value: levelTicks[0].value - interval + }); + } + } + for (var i = 0; i < newAddedTicks.length; i++) { + levelTicks.push(newAddedTicks[i]); + } + // newAddedTicks.length && console.log(unitName, newAddedTicks); + return newAddedTicks; + } + var levelsTicks = []; + var currentLevelTicks = []; + var tickCount = 0; + var lastLevelTickCount = 0; + for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) { + var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]); + if (!isPrimaryTimeUnit(unitNames[i])) { + // TODO + continue; + } + addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks); + var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null; + if (primaryTimeUnit !== nextPrimaryTimeUnit) { + if (currentLevelTicks.length) { + lastLevelTickCount = tickCount; + // Remove the duplicate so the tick count can be precisely. + currentLevelTicks.sort(function (a, b) { + return a.value - b.value; + }); + var levelTicksRemoveDuplicated = []; + for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) { + var tickValue = currentLevelTicks[i_1].value; + if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) { + levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]); + if (tickValue >= extent[0] && tickValue <= extent[1]) { + tickCount++; + } + } + } + var targetTickNum = (extent[1] - extent[0]) / approxInterval; + // Added too much in this level and not too less in last level + if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) { + break; + } + // Only treat primary time unit as one level. + levelsTicks.push(levelTicksRemoveDuplicated); + if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) { + break; + } + } + // Reset if next unitName is primary + currentLevelTicks = []; + } + } + if ("development" !== 'production') { + if (iter >= safeLimit) { + warn('Exceed safe limit.'); + } + } + var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) { + return filter(levelTicks, function (tick) { + return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd; + }); + }), function (levelTicks) { + return levelTicks.length > 0; + }); + var ticks = []; + var maxLevel = levelsTicksInExtent.length - 1; + for (var i = 0; i < levelsTicksInExtent.length; ++i) { + var levelTicks = levelsTicksInExtent[i]; + for (var k = 0; k < levelTicks.length; ++k) { + ticks.push({ + value: levelTicks[k].value, + level: maxLevel - i + }); + } + } + ticks.sort(function (a, b) { + return a.value - b.value; + }); + // Remove duplicates + var result = []; + for (var i = 0; i < ticks.length; ++i) { + if (i === 0 || ticks[i].value !== ticks[i - 1].value) { + result.push(ticks[i]); + } + } + return result; + } + Scale.registerClass(TimeScale); + + var scaleProto = Scale.prototype; + // FIXME:TS refactor: not good to call it directly with `this`? + var intervalScaleProto = IntervalScale.prototype; + var roundingErrorFix = round; + var mathFloor = Math.floor; + var mathCeil = Math.ceil; + var mathPow$1 = Math.pow; + var mathLog = Math.log; + var LogScale = /** @class */function (_super) { + __extends(LogScale, _super); + function LogScale() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'log'; + _this.base = 10; + _this._originalScale = new IntervalScale(); + // FIXME:TS actually used by `IntervalScale` + _this._interval = 0; + return _this; + } + /** + * @param Whether expand the ticks to niced extent. + */ + LogScale.prototype.getTicks = function (expandToNicedExtent) { + var originalScale = this._originalScale; + var extent = this._extent; + var originalExtent = originalScale.getExtent(); + var ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent); + return map(ticks, function (tick) { + var val = tick.value; + var powVal = round(mathPow$1(this.base, val)); + // Fix #4158 + powVal = val === extent[0] && this._fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal; + powVal = val === extent[1] && this._fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal; + return { + value: powVal + }; + }, this); + }; + LogScale.prototype.setExtent = function (start, end) { + var base = mathLog(this.base); + // log(-Infinity) is NaN, so safe guard here + start = mathLog(Math.max(0, start)) / base; + end = mathLog(Math.max(0, end)) / base; + intervalScaleProto.setExtent.call(this, start, end); + }; + /** + * @return {number} end + */ + LogScale.prototype.getExtent = function () { + var base = this.base; + var extent = scaleProto.getExtent.call(this); + extent[0] = mathPow$1(base, extent[0]); + extent[1] = mathPow$1(base, extent[1]); + // Fix #4158 + var originalScale = this._originalScale; + var originalExtent = originalScale.getExtent(); + this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0])); + this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1])); + return extent; + }; + LogScale.prototype.unionExtent = function (extent) { + this._originalScale.unionExtent(extent); + var base = this.base; + extent[0] = mathLog(extent[0]) / mathLog(base); + extent[1] = mathLog(extent[1]) / mathLog(base); + scaleProto.unionExtent.call(this, extent); + }; + LogScale.prototype.unionExtentFromData = function (data, dim) { + // TODO + // filter value that <= 0 + this.unionExtent(data.getApproximateExtent(dim)); + }; + /** + * Update interval and extent of intervals for nice ticks + * @param approxTickNum default 10 Given approx tick number + */ + LogScale.prototype.calcNiceTicks = function (approxTickNum) { + approxTickNum = approxTickNum || 10; + var extent = this._extent; + var span = extent[1] - extent[0]; + if (span === Infinity || span <= 0) { + return; + } + var interval = quantity(span); + var err = approxTickNum / span * interval; + // Filter ticks to get closer to the desired count. + if (err <= 0.5) { + interval *= 10; + } + // Interval should be integer + while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) { + interval *= 10; + } + var niceExtent = [round(mathCeil(extent[0] / interval) * interval), round(mathFloor(extent[1] / interval) * interval)]; + this._interval = interval; + this._niceExtent = niceExtent; + }; + LogScale.prototype.calcNiceExtent = function (opt) { + intervalScaleProto.calcNiceExtent.call(this, opt); + this._fixMin = opt.fixMin; + this._fixMax = opt.fixMax; + }; + LogScale.prototype.parse = function (val) { + return val; + }; + LogScale.prototype.contain = function (val) { + val = mathLog(val) / mathLog(this.base); + return contain$1(val, this._extent); + }; + LogScale.prototype.normalize = function (val) { + val = mathLog(val) / mathLog(this.base); + return normalize$1(val, this._extent); + }; + LogScale.prototype.scale = function (val) { + val = scale$2(val, this._extent); + return mathPow$1(this.base, val); + }; + LogScale.type = 'log'; + return LogScale; + }(Scale); + var proto = LogScale.prototype; + proto.getMinorTicks = intervalScaleProto.getMinorTicks; + proto.getLabel = intervalScaleProto.getLabel; + function fixRoundingError(val, originalVal) { + return roundingErrorFix(val, getPrecision(originalVal)); + } + Scale.registerClass(LogScale); + + var ScaleRawExtentInfo = /** @class */function () { + function ScaleRawExtentInfo(scale, model, + // Usually: data extent from all series on this axis. + originalExtent) { + this._prepareParams(scale, model, originalExtent); + } + /** + * Parameters depending on outside (like model, user callback) + * are prepared and fixed here. + */ + ScaleRawExtentInfo.prototype._prepareParams = function (scale, model, + // Usually: data extent from all series on this axis. + dataExtent) { + if (dataExtent[1] < dataExtent[0]) { + dataExtent = [NaN, NaN]; + } + this._dataMin = dataExtent[0]; + this._dataMax = dataExtent[1]; + var isOrdinal = this._isOrdinal = scale.type === 'ordinal'; + this._needCrossZero = scale.type === 'interval' && model.getNeedCrossZero && model.getNeedCrossZero(); + var modelMinRaw = this._modelMinRaw = model.get('min', true); + if (isFunction(modelMinRaw)) { + // This callback always provides users the full data extent (before data is filtered). + this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw({ + min: dataExtent[0], + max: dataExtent[1] + })); + } else if (modelMinRaw !== 'dataMin') { + this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw); + } + var modelMaxRaw = this._modelMaxRaw = model.get('max', true); + if (isFunction(modelMaxRaw)) { + // This callback always provides users the full data extent (before data is filtered). + this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw({ + min: dataExtent[0], + max: dataExtent[1] + })); + } else if (modelMaxRaw !== 'dataMax') { + this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw); + } + if (isOrdinal) { + // FIXME: there is a flaw here: if there is no "block" data processor like `dataZoom`, + // and progressive rendering is using, here the category result might just only contain + // the processed chunk rather than the entire result. + this._axisDataLen = model.getCategories().length; + } else { + var boundaryGap = model.get('boundaryGap'); + var boundaryGapArr = isArray(boundaryGap) ? boundaryGap : [boundaryGap || 0, boundaryGap || 0]; + if (typeof boundaryGapArr[0] === 'boolean' || typeof boundaryGapArr[1] === 'boolean') { + if ("development" !== 'production') { + console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., "20%". Currently, ' + 'boundaryGap is set to be 0.'); + } + this._boundaryGapInner = [0, 0]; + } else { + this._boundaryGapInner = [parsePercent(boundaryGapArr[0], 1), parsePercent(boundaryGapArr[1], 1)]; + } + } + }; + /** + * Calculate extent by prepared parameters. + * This method has no external dependency and can be called duplicatedly, + * getting the same result. + * If parameters changed, should call this method to recalcuate. + */ + ScaleRawExtentInfo.prototype.calculate = function () { + // Notice: When min/max is not set (that is, when there are null/undefined, + // which is the most common case), these cases should be ensured: + // (1) For 'ordinal', show all axis.data. + // (2) For others: + // + `boundaryGap` is applied (if min/max set, boundaryGap is + // disabled). + // + If `needCrossZero`, min/max should be zero, otherwise, min/max should + // be the result that originalExtent enlarged by boundaryGap. + // (3) If no data, it should be ensured that `scale.setBlank` is set. + var isOrdinal = this._isOrdinal; + var dataMin = this._dataMin; + var dataMax = this._dataMax; + var axisDataLen = this._axisDataLen; + var boundaryGapInner = this._boundaryGapInner; + var span = !isOrdinal ? dataMax - dataMin || Math.abs(dataMin) : null; + // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax', + // `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`. + var min = this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum; + var max = this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum; + // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed. + var minFixed = min != null; + var maxFixed = max != null; + if (min == null) { + min = isOrdinal ? axisDataLen ? 0 : NaN : dataMin - boundaryGapInner[0] * span; + } + if (max == null) { + max = isOrdinal ? axisDataLen ? axisDataLen - 1 : NaN : dataMax + boundaryGapInner[1] * span; + } + (min == null || !isFinite(min)) && (min = NaN); + (max == null || !isFinite(max)) && (max = NaN); + var isBlank = eqNaN(min) || eqNaN(max) || isOrdinal && !axisDataLen; + // If data extent modified, need to recalculated to ensure cross zero. + if (this._needCrossZero) { + // Axis is over zero and min is not set + if (min > 0 && max > 0 && !minFixed) { + min = 0; + // minFixed = true; + } + // Axis is under zero and max is not set + if (min < 0 && max < 0 && !maxFixed) { + max = 0; + // maxFixed = true; + } + // PENDING: + // When `needCrossZero` and all data is positive/negative, should it be ensured + // that the results processed by boundaryGap are positive/negative? + // If so, here `minFixed`/`maxFixed` need to be set. + } + + var determinedMin = this._determinedMin; + var determinedMax = this._determinedMax; + if (determinedMin != null) { + min = determinedMin; + minFixed = true; + } + if (determinedMax != null) { + max = determinedMax; + maxFixed = true; + } + // Ensure min/max be finite number or NaN here. (not to be null/undefined) + // `NaN` means min/max axis is blank. + return { + min: min, + max: max, + minFixed: minFixed, + maxFixed: maxFixed, + isBlank: isBlank + }; + }; + ScaleRawExtentInfo.prototype.modifyDataMinMax = function (minMaxName, val) { + if ("development" !== 'production') { + assert(!this.frozen); + } + this[DATA_MIN_MAX_ATTR[minMaxName]] = val; + }; + ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (minMaxName, val) { + var attr = DETERMINED_MIN_MAX_ATTR[minMaxName]; + if ("development" !== 'production') { + assert(!this.frozen + // Earse them usually means logic flaw. + && this[attr] == null); + } + this[attr] = val; + }; + ScaleRawExtentInfo.prototype.freeze = function () { + // @ts-ignore + this.frozen = true; + }; + return ScaleRawExtentInfo; + }(); + var DETERMINED_MIN_MAX_ATTR = { + min: '_determinedMin', + max: '_determinedMax' + }; + var DATA_MIN_MAX_ATTR = { + min: '_dataMin', + max: '_dataMax' + }; + /** + * Get scale min max and related info only depends on model settings. + * This method can be called after coordinate system created. + * For example, in data processing stage. + * + * Scale extent info probably be required multiple times during a workflow. + * For example: + * (1) `dataZoom` depends it to get the axis extent in "100%" state. + * (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified. + * (3) `coordSys.update` use it to finally decide the scale extent. + * But the callback of `min`/`max` should not be called multiple times. + * The code below should not be implemented repeatedly either. + * So we cache the result in the scale instance, which will be recreated at the beginning + * of the workflow (because `scale` instance will be recreated each round of the workflow). + */ + function ensureScaleRawExtentInfo(scale, model, + // Usually: data extent from all series on this axis. + originalExtent) { + // Do not permit to recreate. + var rawExtentInfo = scale.rawExtentInfo; + if (rawExtentInfo) { + return rawExtentInfo; + } + rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent); + // @ts-ignore + scale.rawExtentInfo = rawExtentInfo; + return rawExtentInfo; + } + function parseAxisModelMinMax(scale, minMax) { + return minMax == null ? null : eqNaN(minMax) ? NaN : scale.parse(minMax); + } + + /** + * Get axis scale extent before niced. + * Item of returned array can only be number (including Infinity and NaN). + * + * Caution: + * Precondition of calling this method: + * The scale extent has been initialized using series data extent via + * `scale.setExtent` or `scale.unionExtentFromData`; + */ + function getScaleExtent(scale, model) { + var scaleType = scale.type; + var rawExtentResult = ensureScaleRawExtentInfo(scale, model, scale.getExtent()).calculate(); + scale.setBlank(rawExtentResult.isBlank); + var min = rawExtentResult.min; + var max = rawExtentResult.max; + // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis + // is base axis + // FIXME + // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly. + // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent? + // Should not depend on series type `bar`? + // (3) Fix that might overlap when using dataZoom. + // (4) Consider other chart types using `barGrid`? + // See #6728, #4862, `test/bar-overflow-time-plot.html` + var ecModel = model.ecModel; + if (ecModel && scaleType === 'time' /* || scaleType === 'interval' */) { + var barSeriesModels = prepareLayoutBarSeries('bar', ecModel); + var isBaseAxisAndHasBarSeries_1 = false; + each(barSeriesModels, function (seriesModel) { + isBaseAxisAndHasBarSeries_1 = isBaseAxisAndHasBarSeries_1 || seriesModel.getBaseAxis() === model.axis; + }); + if (isBaseAxisAndHasBarSeries_1) { + // Calculate placement of bars on axis. TODO should be decoupled + // with barLayout + var barWidthAndOffset = makeColumnLayout(barSeriesModels); + // Adjust axis min and max to account for overflow + var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset); + min = adjustedScale.min; + max = adjustedScale.max; + } + } + return { + extent: [min, max], + // "fix" means "fixed", the value should not be + // changed in the subsequent steps. + fixMin: rawExtentResult.minFixed, + fixMax: rawExtentResult.maxFixed + }; + } + function adjustScaleForOverflow(min, max, model, + // Only support cartesian coord yet. + barWidthAndOffset) { + // Get Axis Length + var axisExtent = model.axis.getExtent(); + var axisLength = axisExtent[1] - axisExtent[0]; + // Get bars on current base axis and calculate min and max overflow + var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis); + if (barsOnCurrentAxis === undefined) { + return { + min: min, + max: max + }; + } + var minOverflow = Infinity; + each(barsOnCurrentAxis, function (item) { + minOverflow = Math.min(item.offset, minOverflow); + }); + var maxOverflow = -Infinity; + each(barsOnCurrentAxis, function (item) { + maxOverflow = Math.max(item.offset + item.width, maxOverflow); + }); + minOverflow = Math.abs(minOverflow); + maxOverflow = Math.abs(maxOverflow); + var totalOverFlow = minOverflow + maxOverflow; + // Calculate required buffer based on old range and overflow + var oldRange = max - min; + var oldRangePercentOfNew = 1 - (minOverflow + maxOverflow) / axisLength; + var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange; + max += overflowBuffer * (maxOverflow / totalOverFlow); + min -= overflowBuffer * (minOverflow / totalOverFlow); + return { + min: min, + max: max + }; + } + // Precondition of calling this method: + // The scale extent has been initialized using series data extent via + // `scale.setExtent` or `scale.unionExtentFromData`; + function niceScaleExtent(scale, inModel) { + var model = inModel; + var extentInfo = getScaleExtent(scale, model); + var extent = extentInfo.extent; + var splitNumber = model.get('splitNumber'); + if (scale instanceof LogScale) { + scale.base = model.get('logBase'); + } + var scaleType = scale.type; + var interval = model.get('interval'); + var isIntervalOrTime = scaleType === 'interval' || scaleType === 'time'; + scale.setExtent(extent[0], extent[1]); + scale.calcNiceExtent({ + splitNumber: splitNumber, + fixMin: extentInfo.fixMin, + fixMax: extentInfo.fixMax, + minInterval: isIntervalOrTime ? model.get('minInterval') : null, + maxInterval: isIntervalOrTime ? model.get('maxInterval') : null + }); + // If some one specified the min, max. And the default calculated interval + // is not good enough. He can specify the interval. It is often appeared + // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard + // to be 60. + // FIXME + if (interval != null) { + scale.setInterval && scale.setInterval(interval); + } + } + /** + * @param axisType Default retrieve from model.type + */ + function createScaleByModel(model, axisType) { + axisType = axisType || model.get('type'); + if (axisType) { + switch (axisType) { + // Buildin scale + case 'category': + return new OrdinalScale({ + ordinalMeta: model.getOrdinalMeta ? model.getOrdinalMeta() : model.getCategories(), + extent: [Infinity, -Infinity] + }); + case 'time': + return new TimeScale({ + locale: model.ecModel.getLocaleModel(), + useUTC: model.ecModel.get('useUTC') + }); + default: + // case 'value'/'interval', 'log', or others. + return new (Scale.getClass(axisType) || IntervalScale)(); + } + } + } + /** + * Check if the axis cross 0 + */ + function ifAxisCrossZero(axis) { + var dataExtent = axis.scale.getExtent(); + var min = dataExtent[0]; + var max = dataExtent[1]; + return !(min > 0 && max > 0 || min < 0 && max < 0); + } + /** + * @param axis + * @return Label formatter function. + * param: {number} tickValue, + * param: {number} idx, the index in all ticks. + * If category axis, this param is not required. + * return: {string} label string. + */ + function makeLabelFormatter(axis) { + var labelFormatter = axis.getLabelModel().get('formatter'); + var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null; + if (axis.scale.type === 'time') { + return function (tpl) { + return function (tick, idx) { + return axis.scale.getFormattedLabel(tick, idx, tpl); + }; + }(labelFormatter); + } else if (isString(labelFormatter)) { + return function (tpl) { + return function (tick) { + // For category axis, get raw value; for numeric axis, + // get formatted label like '1,333,444'. + var label = axis.scale.getLabel(tick); + var text = tpl.replace('{value}', label != null ? label : ''); + return text; + }; + }(labelFormatter); + } else if (isFunction(labelFormatter)) { + return function (cb) { + return function (tick, idx) { + // The original intention of `idx` is "the index of the tick in all ticks". + // But the previous implementation of category axis do not consider the + // `axisLabel.interval`, which cause that, for example, the `interval` is + // `1`, then the ticks "name5", "name7", "name9" are displayed, where the + // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep + // the definition here for back compatibility. + if (categoryTickStart != null) { + idx = tick.value - categoryTickStart; + } + return cb(getAxisRawValue(axis, tick), idx, tick.level != null ? { + level: tick.level + } : null); + }; + }(labelFormatter); + } else { + return function (tick) { + return axis.scale.getLabel(tick); + }; + } + } + function getAxisRawValue(axis, tick) { + // In category axis with data zoom, tick is not the original + // index of axis.data. So tick should not be exposed to user + // in category axis. + return axis.type === 'category' ? axis.scale.getLabel(tick) : tick.value; + } + /** + * @param axis + * @return Be null/undefined if no labels. + */ + function estimateLabelUnionRect(axis) { + var axisModel = axis.model; + var scale = axis.scale; + if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) { + return; + } + var realNumberScaleTicks; + var tickCount; + var categoryScaleExtent = scale.getExtent(); + // Optimize for large category data, avoid call `getTicks()`. + if (scale instanceof OrdinalScale) { + tickCount = scale.count(); + } else { + realNumberScaleTicks = scale.getTicks(); + tickCount = realNumberScaleTicks.length; + } + var axisLabelModel = axis.getLabelModel(); + var labelFormatter = makeLabelFormatter(axis); + var rect; + var step = 1; + // Simple optimization for large amount of labels + if (tickCount > 40) { + step = Math.ceil(tickCount / 40); + } + for (var i = 0; i < tickCount; i += step) { + var tick = realNumberScaleTicks ? realNumberScaleTicks[i] : { + value: categoryScaleExtent[0] + i + }; + var label = labelFormatter(tick, i); + var unrotatedSingleRect = axisLabelModel.getTextRect(label); + var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0); + rect ? rect.union(singleRect) : rect = singleRect; + } + return rect; + } + function rotateTextRect(textRect, rotate) { + var rotateRadians = rotate * Math.PI / 180; + var beforeWidth = textRect.width; + var beforeHeight = textRect.height; + var afterWidth = beforeWidth * Math.abs(Math.cos(rotateRadians)) + Math.abs(beforeHeight * Math.sin(rotateRadians)); + var afterHeight = beforeWidth * Math.abs(Math.sin(rotateRadians)) + Math.abs(beforeHeight * Math.cos(rotateRadians)); + var rotatedRect = new BoundingRect(textRect.x, textRect.y, afterWidth, afterHeight); + return rotatedRect; + } + /** + * @param model axisLabelModel or axisTickModel + * @return {number|String} Can be null|'auto'|number|function + */ + function getOptionCategoryInterval(model) { + var interval = model.get('interval'); + return interval == null ? 'auto' : interval; + } + /** + * Set `categoryInterval` as 0 implicitly indicates that + * show all labels regardless of overlap. + * @param {Object} axis axisModel.axis + */ + function shouldShowAllLabels(axis) { + return axis.type === 'category' && getOptionCategoryInterval(axis.getLabelModel()) === 0; + } + function getDataDimensionsOnAxis(data, axisDim) { + // Remove duplicated dat dimensions caused by `getStackedDimension`. + var dataDimMap = {}; + // Currently `mapDimensionsAll` will contain stack result dimension ('__\0ecstackresult'). + // PENDING: is it reasonable? Do we need to remove the original dim from "coord dim" since + // there has been stacked result dim? + each(data.mapDimensionsAll(axisDim), function (dataDim) { + // For example, the extent of the original dimension + // is [0.1, 0.5], the extent of the `stackResultDimension` + // is [7, 9], the final extent should NOT include [0.1, 0.5], + // because there is no graphic corresponding to [0.1, 0.5]. + // See the case in `test/area-stack.html` `main1`, where area line + // stack needs `yAxis` not start from 0. + dataDimMap[getStackedDimension(data, dataDim)] = true; + }); + return keys(dataDimMap); + } + function unionAxisExtentFromData(dataExtent, data, axisDim) { + if (data) { + each(getDataDimensionsOnAxis(data, axisDim), function (dim) { + var seriesExtent = data.getApproximateExtent(dim); + seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]); + seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]); + }); + } + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + var AxisModelCommonMixin = /** @class */function () { + function AxisModelCommonMixin() {} + AxisModelCommonMixin.prototype.getNeedCrossZero = function () { + var option = this.option; + return !option.scale; + }; + /** + * Should be implemented by each axis model if necessary. + * @return coordinate system model + */ + AxisModelCommonMixin.prototype.getCoordSysModel = function () { + return; + }; + return AxisModelCommonMixin; + }(); + + /** + * Create a multi dimension List structure from seriesModel. + */ + function createList(seriesModel) { + return createSeriesData(null, seriesModel); + } + var dataStack$1 = { + isDimensionStacked: isDimensionStacked, + enableDataStack: enableDataStack, + getStackedDimension: getStackedDimension + }; + /** + * Create scale + * @param {Array.} dataExtent + * @param {Object|module:echarts/Model} option If `optoin.type` + * is secified, it can only be `'value'` currently. + */ + function createScale(dataExtent, option) { + var axisModel = option; + if (!(option instanceof Model)) { + axisModel = new Model(option); + // FIXME + // Currently AxisModelCommonMixin has nothing to do with the + // the requirements of `axisHelper.createScaleByModel`. For + // example the methods `getCategories` and `getOrdinalMeta` + // are required for `'category'` axis, and ecModel is required + // for `'time'` axis. But occasionally echarts-gl happened + // to only use `'value'` axis. + // zrUtil.mixin(axisModel, AxisModelCommonMixin); + } + + var scale = createScaleByModel(axisModel); + scale.setExtent(dataExtent[0], dataExtent[1]); + niceScaleExtent(scale, axisModel); + return scale; + } + /** + * Mixin common methods to axis model, + * + * Include methods + * `getFormattedLabels() => Array.` + * `getCategories() => Array.` + * `getMin(origin: boolean) => number` + * `getMax(origin: boolean) => number` + * `getNeedCrossZero() => boolean` + */ + function mixinAxisModelCommonMethods(Model) { + mixin(Model, AxisModelCommonMixin); + } + function createTextStyle$1(textStyleModel, opts) { + opts = opts || {}; + return createTextStyle(textStyleModel, null, null, opts.state !== 'normal'); + } + + var helper = /*#__PURE__*/Object.freeze({ + __proto__: null, + createList: createList, + getLayoutRect: getLayoutRect, + dataStack: dataStack$1, + createScale: createScale, + mixinAxisModelCommonMethods: mixinAxisModelCommonMethods, + getECData: getECData, + createTextStyle: createTextStyle$1, + createDimensions: createDimensions, + createSymbol: createSymbol, + enableHoverEmphasis: enableHoverEmphasis + }); + + var EPSILON$4 = 1e-8; + function isAroundEqual$1(a, b) { + return Math.abs(a - b) < EPSILON$4; + } + function contain$2(points, x, y) { + var w = 0; + var p = points[0]; + if (!p) { + return false; + } + for (var i = 1; i < points.length; i++) { + var p2 = points[i]; + w += windingLine(p[0], p[1], p2[0], p2[1], x, y); + p = p2; + } + var p0 = points[0]; + if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) { + w += windingLine(p[0], p[1], p0[0], p0[1], x, y); + } + return w !== 0; + } + + var TMP_TRANSFORM = []; + function transformPoints(points, transform) { + for (var p = 0; p < points.length; p++) { + applyTransform(points[p], points[p], transform); + } + } + function updateBBoxFromPoints(points, min$1, max$1, projection) { + for (var i = 0; i < points.length; i++) { + var p = points[i]; + if (projection) { + // projection may return null point. + p = projection.project(p); + } + if (p && isFinite(p[0]) && isFinite(p[1])) { + min(min$1, min$1, p); + max(max$1, max$1, p); + } + } + } + function centroid(points) { + var signedArea = 0; + var cx = 0; + var cy = 0; + var len = points.length; + var x0 = points[len - 1][0]; + var y0 = points[len - 1][1]; + // Polygon should been closed. + for (var i = 0; i < len; i++) { + var x1 = points[i][0]; + var y1 = points[i][1]; + var a = x0 * y1 - x1 * y0; + signedArea += a; + cx += (x0 + x1) * a; + cy += (y0 + y1) * a; + x0 = x1; + y0 = y1; + } + return signedArea ? [cx / signedArea / 3, cy / signedArea / 3, signedArea] : [points[0][0] || 0, points[0][1] || 0]; + } + var Region = /** @class */function () { + function Region(name) { + this.name = name; + } + Region.prototype.setCenter = function (center) { + this._center = center; + }; + /** + * Get center point in data unit. That is, + * for GeoJSONRegion, the unit is lat/lng, + * for GeoSVGRegion, the unit is SVG local coord. + */ + Region.prototype.getCenter = function () { + var center = this._center; + if (!center) { + // In most cases there are no need to calculate this center. + // So calculate only when called. + center = this._center = this.calcCenter(); + } + return center; + }; + return Region; + }(); + var GeoJSONPolygonGeometry = /** @class */function () { + function GeoJSONPolygonGeometry(exterior, interiors) { + this.type = 'polygon'; + this.exterior = exterior; + this.interiors = interiors; + } + return GeoJSONPolygonGeometry; + }(); + var GeoJSONLineStringGeometry = /** @class */function () { + function GeoJSONLineStringGeometry(points) { + this.type = 'linestring'; + this.points = points; + } + return GeoJSONLineStringGeometry; + }(); + var GeoJSONRegion = /** @class */function (_super) { + __extends(GeoJSONRegion, _super); + function GeoJSONRegion(name, geometries, cp) { + var _this = _super.call(this, name) || this; + _this.type = 'geoJSON'; + _this.geometries = geometries; + _this._center = cp && [cp[0], cp[1]]; + return _this; + } + GeoJSONRegion.prototype.calcCenter = function () { + var geometries = this.geometries; + var largestGeo; + var largestGeoSize = 0; + for (var i = 0; i < geometries.length; i++) { + var geo = geometries[i]; + var exterior = geo.exterior; + // Simple trick to use points count instead of polygon area as region size. + // Ignore linestring + var size = exterior && exterior.length; + if (size > largestGeoSize) { + largestGeo = geo; + largestGeoSize = size; + } + } + if (largestGeo) { + return centroid(largestGeo.exterior); + } + // from bounding rect by default. + var rect = this.getBoundingRect(); + return [rect.x + rect.width / 2, rect.y + rect.height / 2]; + }; + GeoJSONRegion.prototype.getBoundingRect = function (projection) { + var rect = this._rect; + // Always recalculate if using projection. + if (rect && !projection) { + return rect; + } + var min = [Infinity, Infinity]; + var max = [-Infinity, -Infinity]; + var geometries = this.geometries; + each(geometries, function (geo) { + if (geo.type === 'polygon') { + // Doesn't consider hole + updateBBoxFromPoints(geo.exterior, min, max, projection); + } else { + each(geo.points, function (points) { + updateBBoxFromPoints(points, min, max, projection); + }); + } + }); + // Normalie invalid bounding. + if (!(isFinite(min[0]) && isFinite(min[1]) && isFinite(max[0]) && isFinite(max[1]))) { + min[0] = min[1] = max[0] = max[1] = 0; + } + rect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]); + if (!projection) { + this._rect = rect; + } + return rect; + }; + GeoJSONRegion.prototype.contain = function (coord) { + var rect = this.getBoundingRect(); + var geometries = this.geometries; + if (!rect.contain(coord[0], coord[1])) { + return false; + } + loopGeo: for (var i = 0, len = geometries.length; i < len; i++) { + var geo = geometries[i]; + // Only support polygon. + if (geo.type !== 'polygon') { + continue; + } + var exterior = geo.exterior; + var interiors = geo.interiors; + if (contain$2(exterior, coord[0], coord[1])) { + // Not in the region if point is in the hole. + for (var k = 0; k < (interiors ? interiors.length : 0); k++) { + if (contain$2(interiors[k], coord[0], coord[1])) { + continue loopGeo; + } + } + return true; + } + } + return false; + }; + /** + * Transform the raw coords to target bounding. + * @param x + * @param y + * @param width + * @param height + */ + GeoJSONRegion.prototype.transformTo = function (x, y, width, height) { + var rect = this.getBoundingRect(); + var aspect = rect.width / rect.height; + if (!width) { + width = aspect * height; + } else if (!height) { + height = width / aspect; + } + var target = new BoundingRect(x, y, width, height); + var transform = rect.calculateTransform(target); + var geometries = this.geometries; + for (var i = 0; i < geometries.length; i++) { + var geo = geometries[i]; + if (geo.type === 'polygon') { + transformPoints(geo.exterior, transform); + each(geo.interiors, function (interior) { + transformPoints(interior, transform); + }); + } else { + each(geo.points, function (points) { + transformPoints(points, transform); + }); + } + } + rect = this._rect; + rect.copy(target); + // Update center + this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2]; + }; + GeoJSONRegion.prototype.cloneShallow = function (name) { + name == null && (name = this.name); + var newRegion = new GeoJSONRegion(name, this.geometries, this._center); + newRegion._rect = this._rect; + newRegion.transformTo = null; // Simply avoid to be called. + return newRegion; + }; + return GeoJSONRegion; + }(Region); + var GeoSVGRegion = /** @class */function (_super) { + __extends(GeoSVGRegion, _super); + function GeoSVGRegion(name, elOnlyForCalculate) { + var _this = _super.call(this, name) || this; + _this.type = 'geoSVG'; + _this._elOnlyForCalculate = elOnlyForCalculate; + return _this; + } + GeoSVGRegion.prototype.calcCenter = function () { + var el = this._elOnlyForCalculate; + var rect = el.getBoundingRect(); + var center = [rect.x + rect.width / 2, rect.y + rect.height / 2]; + var mat = identity(TMP_TRANSFORM); + var target = el; + while (target && !target.isGeoSVGGraphicRoot) { + mul$1(mat, target.getLocalTransform(), mat); + target = target.parent; + } + invert(mat, mat); + applyTransform(center, center, mat); + return center; + }; + return GeoSVGRegion; + }(Region); + + function decode(json) { + if (!json.UTF8Encoding) { + return json; + } + var jsonCompressed = json; + var encodeScale = jsonCompressed.UTF8Scale; + if (encodeScale == null) { + encodeScale = 1024; + } + var features = jsonCompressed.features; + each(features, function (feature) { + var geometry = feature.geometry; + var encodeOffsets = geometry.encodeOffsets; + var coordinates = geometry.coordinates; + // Geometry may be appeded manually in the script after json loaded. + // In this case this geometry is usually not encoded. + if (!encodeOffsets) { + return; + } + switch (geometry.type) { + case 'LineString': + geometry.coordinates = decodeRing(coordinates, encodeOffsets, encodeScale); + break; + case 'Polygon': + decodeRings(coordinates, encodeOffsets, encodeScale); + break; + case 'MultiLineString': + decodeRings(coordinates, encodeOffsets, encodeScale); + break; + case 'MultiPolygon': + each(coordinates, function (rings, idx) { + return decodeRings(rings, encodeOffsets[idx], encodeScale); + }); + } + }); + // Has been decoded + jsonCompressed.UTF8Encoding = false; + return jsonCompressed; + } + function decodeRings(rings, encodeOffsets, encodeScale) { + for (var c = 0; c < rings.length; c++) { + rings[c] = decodeRing(rings[c], encodeOffsets[c], encodeScale); + } + } + function decodeRing(coordinate, encodeOffsets, encodeScale) { + var result = []; + var prevX = encodeOffsets[0]; + var prevY = encodeOffsets[1]; + for (var i = 0; i < coordinate.length; i += 2) { + var x = coordinate.charCodeAt(i) - 64; + var y = coordinate.charCodeAt(i + 1) - 64; + // ZigZag decoding + x = x >> 1 ^ -(x & 1); + y = y >> 1 ^ -(y & 1); + // Delta deocding + x += prevX; + y += prevY; + prevX = x; + prevY = y; + // Dequantize + result.push([x / encodeScale, y / encodeScale]); + } + return result; + } + function parseGeoJSON(geoJson, nameProperty) { + geoJson = decode(geoJson); + return map(filter(geoJson.features, function (featureObj) { + // Output of mapshaper may have geometry null + return featureObj.geometry && featureObj.properties && featureObj.geometry.coordinates.length > 0; + }), function (featureObj) { + var properties = featureObj.properties; + var geo = featureObj.geometry; + var geometries = []; + switch (geo.type) { + case 'Polygon': + var coordinates = geo.coordinates; + // According to the GeoJSON specification. + // First must be exterior, and the rest are all interior(holes). + geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1))); + break; + case 'MultiPolygon': + each(geo.coordinates, function (item) { + if (item[0]) { + geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1))); + } + }); + break; + case 'LineString': + geometries.push(new GeoJSONLineStringGeometry([geo.coordinates])); + break; + case 'MultiLineString': + geometries.push(new GeoJSONLineStringGeometry(geo.coordinates)); + } + var region = new GeoJSONRegion(properties[nameProperty || 'name'], geometries, properties.cp); + region.properties = properties; + return region; + }); + } + + var number = /*#__PURE__*/Object.freeze({ + __proto__: null, + linearMap: linearMap, + round: round, + asc: asc, + getPrecision: getPrecision, + getPrecisionSafe: getPrecisionSafe, + getPixelPrecision: getPixelPrecision, + getPercentWithPrecision: getPercentWithPrecision, + MAX_SAFE_INTEGER: MAX_SAFE_INTEGER, + remRadian: remRadian, + isRadianAroundZero: isRadianAroundZero, + parseDate: parseDate, + quantity: quantity, + quantityExponent: quantityExponent, + nice: nice, + quantile: quantile, + reformIntervals: reformIntervals, + isNumeric: isNumeric, + numericToNumber: numericToNumber + }); + + var time = /*#__PURE__*/Object.freeze({ + __proto__: null, + parse: parseDate, + format: format + }); + + var graphic$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + extendShape: extendShape, + extendPath: extendPath, + makePath: makePath, + makeImage: makeImage, + mergePath: mergePath$1, + resizePath: resizePath, + createIcon: createIcon, + updateProps: updateProps, + initProps: initProps, + getTransform: getTransform, + clipPointsByRect: clipPointsByRect, + clipRectByRect: clipRectByRect, + registerShape: registerShape, + getShapeClass: getShapeClass, + Group: Group, + Image: ZRImage, + Text: ZRText, + Circle: Circle, + Ellipse: Ellipse, + Sector: Sector, + Ring: Ring, + Polygon: Polygon, + Polyline: Polyline, + Rect: Rect, + Line: Line, + BezierCurve: BezierCurve, + Arc: Arc, + IncrementalDisplayable: IncrementalDisplayable, + CompoundPath: CompoundPath, + LinearGradient: LinearGradient, + RadialGradient: RadialGradient, + BoundingRect: BoundingRect + }); + + var format$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + addCommas: addCommas, + toCamelCase: toCamelCase, + normalizeCssArray: normalizeCssArray$1, + encodeHTML: encodeHTML, + formatTpl: formatTpl, + getTooltipMarker: getTooltipMarker, + formatTime: formatTime, + capitalFirst: capitalFirst, + truncateText: truncateText, + getTextRect: getTextRect + }); + + var util$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + map: map, + each: each, + indexOf: indexOf, + inherits: inherits, + reduce: reduce, + filter: filter, + bind: bind, + curry: curry, + isArray: isArray, + isString: isString, + isObject: isObject, + isFunction: isFunction, + extend: extend, + defaults: defaults, + clone: clone, + merge: merge + }); + + var inner$5 = makeInner(); + function createAxisLabels(axis) { + // Only ordinal scale support tick interval + return axis.type === 'category' ? makeCategoryLabels(axis) : makeRealNumberLabels(axis); + } + /** + * @param {module:echats/coord/Axis} axis + * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea. + * @return {Object} { + * ticks: Array. + * tickCategoryInterval: number + * } + */ + function createAxisTicks(axis, tickModel) { + // Only ordinal scale support tick interval + return axis.type === 'category' ? makeCategoryTicks(axis, tickModel) : { + ticks: map(axis.scale.getTicks(), function (tick) { + return tick.value; + }) + }; + } + function makeCategoryLabels(axis) { + var labelModel = axis.getLabelModel(); + var result = makeCategoryLabelsActually(axis, labelModel); + return !labelModel.get('show') || axis.scale.isBlank() ? { + labels: [], + labelCategoryInterval: result.labelCategoryInterval + } : result; + } + function makeCategoryLabelsActually(axis, labelModel) { + var labelsCache = getListCache(axis, 'labels'); + var optionLabelInterval = getOptionCategoryInterval(labelModel); + var result = listCacheGet(labelsCache, optionLabelInterval); + if (result) { + return result; + } + var labels; + var numericLabelInterval; + if (isFunction(optionLabelInterval)) { + labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval); + } else { + numericLabelInterval = optionLabelInterval === 'auto' ? makeAutoCategoryInterval(axis) : optionLabelInterval; + labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval); + } + // Cache to avoid calling interval function repeatedly. + return listCacheSet(labelsCache, optionLabelInterval, { + labels: labels, + labelCategoryInterval: numericLabelInterval + }); + } + function makeCategoryTicks(axis, tickModel) { + var ticksCache = getListCache(axis, 'ticks'); + var optionTickInterval = getOptionCategoryInterval(tickModel); + var result = listCacheGet(ticksCache, optionTickInterval); + if (result) { + return result; + } + var ticks; + var tickCategoryInterval; + // Optimize for the case that large category data and no label displayed, + // we should not return all ticks. + if (!tickModel.get('show') || axis.scale.isBlank()) { + ticks = []; + } + if (isFunction(optionTickInterval)) { + ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true); + } + // Always use label interval by default despite label show. Consider this + // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows + // labels. `splitLine` and `axisTick` should be consistent in this case. + else if (optionTickInterval === 'auto') { + var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel()); + tickCategoryInterval = labelsResult.labelCategoryInterval; + ticks = map(labelsResult.labels, function (labelItem) { + return labelItem.tickValue; + }); + } else { + tickCategoryInterval = optionTickInterval; + ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true); + } + // Cache to avoid calling interval function repeatedly. + return listCacheSet(ticksCache, optionTickInterval, { + ticks: ticks, + tickCategoryInterval: tickCategoryInterval + }); + } + function makeRealNumberLabels(axis) { + var ticks = axis.scale.getTicks(); + var labelFormatter = makeLabelFormatter(axis); + return { + labels: map(ticks, function (tick, idx) { + return { + level: tick.level, + formattedLabel: labelFormatter(tick, idx), + rawLabel: axis.scale.getLabel(tick), + tickValue: tick.value + }; + }) + }; + } + function getListCache(axis, prop) { + // Because key can be a function, and cache size always is small, we use array cache. + return inner$5(axis)[prop] || (inner$5(axis)[prop] = []); + } + function listCacheGet(cache, key) { + for (var i = 0; i < cache.length; i++) { + if (cache[i].key === key) { + return cache[i].value; + } + } + } + function listCacheSet(cache, key, value) { + cache.push({ + key: key, + value: value + }); + return value; + } + function makeAutoCategoryInterval(axis) { + var result = inner$5(axis).autoInterval; + return result != null ? result : inner$5(axis).autoInterval = axis.calculateCategoryInterval(); + } + /** + * Calculate interval for category axis ticks and labels. + * To get precise result, at least one of `getRotate` and `isHorizontal` + * should be implemented in axis. + */ + function calculateCategoryInterval(axis) { + var params = fetchAutoCategoryIntervalCalculationParams(axis); + var labelFormatter = makeLabelFormatter(axis); + var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI; + var ordinalScale = axis.scale; + var ordinalExtent = ordinalScale.getExtent(); + // Providing this method is for optimization: + // avoid generating a long array by `getTicks` + // in large category data case. + var tickCount = ordinalScale.count(); + if (ordinalExtent[1] - ordinalExtent[0] < 1) { + return 0; + } + var step = 1; + // Simple optimization. Empirical value: tick count should less than 40. + if (tickCount > 40) { + step = Math.max(1, Math.floor(tickCount / 40)); + } + var tickValue = ordinalExtent[0]; + var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue); + var unitW = Math.abs(unitSpan * Math.cos(rotation)); + var unitH = Math.abs(unitSpan * Math.sin(rotation)); + var maxW = 0; + var maxH = 0; + // Caution: Performance sensitive for large category data. + // Consider dataZoom, we should make appropriate step to avoid O(n) loop. + for (; tickValue <= ordinalExtent[1]; tickValue += step) { + var width = 0; + var height = 0; + // Not precise, do not consider align and vertical align + // and each distance from axis line yet. + var rect = getBoundingRect(labelFormatter({ + value: tickValue + }), params.font, 'center', 'top'); + // Magic number + width = rect.width * 1.3; + height = rect.height * 1.3; + // Min size, void long loop. + maxW = Math.max(maxW, width, 7); + maxH = Math.max(maxH, height, 7); + } + var dw = maxW / unitW; + var dh = maxH / unitH; + // 0/0 is NaN, 1/0 is Infinity. + isNaN(dw) && (dw = Infinity); + isNaN(dh) && (dh = Infinity); + var interval = Math.max(0, Math.floor(Math.min(dw, dh))); + var cache = inner$5(axis.model); + var axisExtent = axis.getExtent(); + var lastAutoInterval = cache.lastAutoInterval; + var lastTickCount = cache.lastTickCount; + // Use cache to keep interval stable while moving zoom window, + // otherwise the calculated interval might jitter when the zoom + // window size is close to the interval-changing size. + // For example, if all of the axis labels are `a, b, c, d, e, f, g`. + // The jitter will cause that sometimes the displayed labels are + // `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1). + if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 + // Always choose the bigger one, otherwise the critical + // point is not the same when zooming in or zooming out. + && lastAutoInterval > interval + // If the axis change is caused by chart resize, the cache should not + // be used. Otherwise some hidden labels might not be shown again. + && cache.axisExtent0 === axisExtent[0] && cache.axisExtent1 === axisExtent[1]) { + interval = lastAutoInterval; + } + // Only update cache if cache not used, otherwise the + // changing of interval is too insensitive. + else { + cache.lastTickCount = tickCount; + cache.lastAutoInterval = interval; + cache.axisExtent0 = axisExtent[0]; + cache.axisExtent1 = axisExtent[1]; + } + return interval; + } + function fetchAutoCategoryIntervalCalculationParams(axis) { + var labelModel = axis.getLabelModel(); + return { + axisRotate: axis.getRotate ? axis.getRotate() : axis.isHorizontal && !axis.isHorizontal() ? 90 : 0, + labelRotate: labelModel.get('rotate') || 0, + font: labelModel.getFont() + }; + } + function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) { + var labelFormatter = makeLabelFormatter(axis); + var ordinalScale = axis.scale; + var ordinalExtent = ordinalScale.getExtent(); + var labelModel = axis.getLabelModel(); + var result = []; + // TODO: axisType: ordinalTime, pick the tick from each month/day/year/... + var step = Math.max((categoryInterval || 0) + 1, 1); + var startTick = ordinalExtent[0]; + var tickCount = ordinalScale.count(); + // Calculate start tick based on zero if possible to keep label consistent + // while zooming and moving while interval > 0. Otherwise the selection + // of displayable ticks and symbols probably keep changing. + // 3 is empirical value. + if (startTick !== 0 && step > 1 && tickCount / step > 2) { + startTick = Math.round(Math.ceil(startTick / step) * step); + } + // (1) Only add min max label here but leave overlap checking + // to render stage, which also ensure the returned list + // suitable for splitLine and splitArea rendering. + // (2) Scales except category always contain min max label so + // do not need to perform this process. + var showAllLabel = shouldShowAllLabels(axis); + var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel; + var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel; + if (includeMinLabel && startTick !== ordinalExtent[0]) { + addItem(ordinalExtent[0]); + } + // Optimize: avoid generating large array by `ordinalScale.getTicks()`. + var tickValue = startTick; + for (; tickValue <= ordinalExtent[1]; tickValue += step) { + addItem(tickValue); + } + if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) { + addItem(ordinalExtent[1]); + } + function addItem(tickValue) { + var tickObj = { + value: tickValue + }; + result.push(onlyTick ? tickValue : { + formattedLabel: labelFormatter(tickObj), + rawLabel: ordinalScale.getLabel(tickObj), + tickValue: tickValue + }); + } + return result; + } + function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) { + var ordinalScale = axis.scale; + var labelFormatter = makeLabelFormatter(axis); + var result = []; + each(ordinalScale.getTicks(), function (tick) { + var rawLabel = ordinalScale.getLabel(tick); + var tickValue = tick.value; + if (categoryInterval(tick.value, rawLabel)) { + result.push(onlyTick ? tickValue : { + formattedLabel: labelFormatter(tick), + rawLabel: rawLabel, + tickValue: tickValue + }); + } + }); + return result; + } + + var NORMALIZED_EXTENT = [0, 1]; + /** + * Base class of Axis. + */ + var Axis = /** @class */function () { + function Axis(dim, scale, extent) { + this.onBand = false; + this.inverse = false; + this.dim = dim; + this.scale = scale; + this._extent = extent || [0, 0]; + } + /** + * If axis extent contain given coord + */ + Axis.prototype.contain = function (coord) { + var extent = this._extent; + var min = Math.min(extent[0], extent[1]); + var max = Math.max(extent[0], extent[1]); + return coord >= min && coord <= max; + }; + /** + * If axis extent contain given data + */ + Axis.prototype.containData = function (data) { + return this.scale.contain(data); + }; + /** + * Get coord extent. + */ + Axis.prototype.getExtent = function () { + return this._extent.slice(); + }; + /** + * Get precision used for formatting + */ + Axis.prototype.getPixelPrecision = function (dataExtent) { + return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent); + }; + /** + * Set coord extent + */ + Axis.prototype.setExtent = function (start, end) { + var extent = this._extent; + extent[0] = start; + extent[1] = end; + }; + /** + * Convert data to coord. Data is the rank if it has an ordinal scale + */ + Axis.prototype.dataToCoord = function (data, clamp) { + var extent = this._extent; + var scale = this.scale; + data = scale.normalize(data); + if (this.onBand && scale.type === 'ordinal') { + extent = extent.slice(); + fixExtentWithBands(extent, scale.count()); + } + return linearMap(data, NORMALIZED_EXTENT, extent, clamp); + }; + /** + * Convert coord to data. Data is the rank if it has an ordinal scale + */ + Axis.prototype.coordToData = function (coord, clamp) { + var extent = this._extent; + var scale = this.scale; + if (this.onBand && scale.type === 'ordinal') { + extent = extent.slice(); + fixExtentWithBands(extent, scale.count()); + } + var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp); + return this.scale.scale(t); + }; + /** + * Convert pixel point to data in axis + */ + Axis.prototype.pointToData = function (point, clamp) { + // Should be implemented in derived class if necessary. + return; + }; + /** + * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`, + * `axis.getTicksCoords` considers `onBand`, which is used by + * `boundaryGap:true` of category axis and splitLine and splitArea. + * @param opt.tickModel default: axis.model.getModel('axisTick') + * @param opt.clamp If `true`, the first and the last + * tick must be at the axis end points. Otherwise, clip ticks + * that outside the axis extent. + */ + Axis.prototype.getTicksCoords = function (opt) { + opt = opt || {}; + var tickModel = opt.tickModel || this.getTickModel(); + var result = createAxisTicks(this, tickModel); + var ticks = result.ticks; + var ticksCoords = map(ticks, function (tickVal) { + return { + coord: this.dataToCoord(this.scale.type === 'ordinal' ? this.scale.getRawOrdinalNumber(tickVal) : tickVal), + tickValue: tickVal + }; + }, this); + var alignWithLabel = tickModel.get('alignWithLabel'); + fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp); + return ticksCoords; + }; + Axis.prototype.getMinorTicksCoords = function () { + if (this.scale.type === 'ordinal') { + // Category axis doesn't support minor ticks + return []; + } + var minorTickModel = this.model.getModel('minorTick'); + var splitNumber = minorTickModel.get('splitNumber'); + // Protection. + if (!(splitNumber > 0 && splitNumber < 100)) { + splitNumber = 5; + } + var minorTicks = this.scale.getMinorTicks(splitNumber); + var minorTicksCoords = map(minorTicks, function (minorTicksGroup) { + return map(minorTicksGroup, function (minorTick) { + return { + coord: this.dataToCoord(minorTick), + tickValue: minorTick + }; + }, this); + }, this); + return minorTicksCoords; + }; + Axis.prototype.getViewLabels = function () { + return createAxisLabels(this).labels; + }; + Axis.prototype.getLabelModel = function () { + return this.model.getModel('axisLabel'); + }; + /** + * Notice here we only get the default tick model. For splitLine + * or splitArea, we should pass the splitLineModel or splitAreaModel + * manually when calling `getTicksCoords`. + * In GL, this method may be overridden to: + * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));` + */ + Axis.prototype.getTickModel = function () { + return this.model.getModel('axisTick'); + }; + /** + * Get width of band + */ + Axis.prototype.getBandWidth = function () { + var axisExtent = this._extent; + var dataExtent = this.scale.getExtent(); + var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); + // Fix #2728, avoid NaN when only one data. + len === 0 && (len = 1); + var size = Math.abs(axisExtent[1] - axisExtent[0]); + return Math.abs(size) / len; + }; + /** + * Only be called in category axis. + * Can be overridden, consider other axes like in 3D. + * @return Auto interval for cateogry axis tick and label + */ + Axis.prototype.calculateCategoryInterval = function () { + return calculateCategoryInterval(this); + }; + return Axis; + }(); + function fixExtentWithBands(extent, nTick) { + var size = extent[1] - extent[0]; + var len = nTick; + var margin = size / len / 2; + extent[0] += margin; + extent[1] -= margin; + } + // If axis has labels [1, 2, 3, 4]. Bands on the axis are + // |---1---|---2---|---3---|---4---|. + // So the displayed ticks and splitLine/splitArea should between + // each data item, otherwise cause misleading (e.g., split tow bars + // of a single data item when there are two bar series). + // Also consider if tickCategoryInterval > 0 and onBand, ticks and + // splitLine/spliteArea should layout appropriately corresponding + // to displayed labels. (So we should not use `getBandWidth` in this + // case). + function fixOnBandTicksCoords(axis, ticksCoords, alignWithLabel, clamp) { + var ticksLen = ticksCoords.length; + if (!axis.onBand || alignWithLabel || !ticksLen) { + return; + } + var axisExtent = axis.getExtent(); + var last; + var diffSize; + if (ticksLen === 1) { + ticksCoords[0].coord = axisExtent[0]; + last = ticksCoords[1] = { + coord: axisExtent[1] + }; + } else { + var crossLen = ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue; + var shift_1 = (ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) / crossLen; + each(ticksCoords, function (ticksItem) { + ticksItem.coord -= shift_1 / 2; + }); + var dataExtent = axis.scale.getExtent(); + diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue; + last = { + coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize + }; + ticksCoords.push(last); + } + var inverse = axisExtent[0] > axisExtent[1]; + // Handling clamp. + if (littleThan(ticksCoords[0].coord, axisExtent[0])) { + clamp ? ticksCoords[0].coord = axisExtent[0] : ticksCoords.shift(); + } + if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) { + ticksCoords.unshift({ + coord: axisExtent[0] + }); + } + if (littleThan(axisExtent[1], last.coord)) { + clamp ? last.coord = axisExtent[1] : ticksCoords.pop(); + } + if (clamp && littleThan(last.coord, axisExtent[1])) { + ticksCoords.push({ + coord: axisExtent[1] + }); + } + function littleThan(a, b) { + // Avoid rounding error cause calculated tick coord different with extent. + // It may cause an extra unnecessary tick added. + a = round(a); + b = round(b); + return inverse ? a > b : a < b; + } + } + + // --------------------- Deprecated Extension Methods --------------------- + // Should use `ComponentModel.extend` or `class XXXX extend ComponentModel` to create class. + // Then use `registerComponentModel` in `install` parameter when `use` this extension. For example: + // class Bar3DModel extends ComponentModel {} + // export function install(registers) { registers.registerComponentModel(Bar3DModel); } + // echarts.use(install); + function extendComponentModel(proto) { + var Model = ComponentModel.extend(proto); + ComponentModel.registerClass(Model); + return Model; + } + function extendComponentView(proto) { + var View = ComponentView.extend(proto); + ComponentView.registerClass(View); + return View; + } + function extendSeriesModel(proto) { + var Model = SeriesModel.extend(proto); + SeriesModel.registerClass(Model); + return Model; + } + function extendChartView(proto) { + var View = ChartView.extend(proto); + ChartView.registerClass(View); + return View; + } + + var PI2$6 = Math.PI * 2; + var CMD$3 = PathProxy.CMD; + var DEFAULT_SEARCH_SPACE = ['top', 'right', 'bottom', 'left']; + function getCandidateAnchor(pos, distance, rect, outPt, outDir) { + var width = rect.width; + var height = rect.height; + switch (pos) { + case 'top': + outPt.set(rect.x + width / 2, rect.y - distance); + outDir.set(0, -1); + break; + case 'bottom': + outPt.set(rect.x + width / 2, rect.y + height + distance); + outDir.set(0, 1); + break; + case 'left': + outPt.set(rect.x - distance, rect.y + height / 2); + outDir.set(-1, 0); + break; + case 'right': + outPt.set(rect.x + width + distance, rect.y + height / 2); + outDir.set(1, 0); + break; + } + } + function projectPointToArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y, out) { + x -= cx; + y -= cy; + var d = Math.sqrt(x * x + y * y); + x /= d; + y /= d; + // Intersect point. + var ox = x * r + cx; + var oy = y * r + cy; + if (Math.abs(startAngle - endAngle) % PI2$6 < 1e-4) { + // Is a circle + out[0] = ox; + out[1] = oy; + return d - r; + } + if (anticlockwise) { + var tmp = startAngle; + startAngle = normalizeRadian(endAngle); + endAngle = normalizeRadian(tmp); + } else { + startAngle = normalizeRadian(startAngle); + endAngle = normalizeRadian(endAngle); + } + if (startAngle > endAngle) { + endAngle += PI2$6; + } + var angle = Math.atan2(y, x); + if (angle < 0) { + angle += PI2$6; + } + if (angle >= startAngle && angle <= endAngle || angle + PI2$6 >= startAngle && angle + PI2$6 <= endAngle) { + // Project point is on the arc. + out[0] = ox; + out[1] = oy; + return d - r; + } + var x1 = r * Math.cos(startAngle) + cx; + var y1 = r * Math.sin(startAngle) + cy; + var x2 = r * Math.cos(endAngle) + cx; + var y2 = r * Math.sin(endAngle) + cy; + var d1 = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y); + var d2 = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y); + if (d1 < d2) { + out[0] = x1; + out[1] = y1; + return Math.sqrt(d1); + } else { + out[0] = x2; + out[1] = y2; + return Math.sqrt(d2); + } + } + function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) { + var dx = x - x1; + var dy = y - y1; + var dx1 = x2 - x1; + var dy1 = y2 - y1; + var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1); + dx1 /= lineLen; + dy1 /= lineLen; + // dot product + var projectedLen = dx * dx1 + dy * dy1; + var t = projectedLen / lineLen; + if (limitToEnds) { + t = Math.min(Math.max(t, 0), 1); + } + t *= lineLen; + var ox = out[0] = x1 + t * dx1; + var oy = out[1] = y1 + t * dy1; + return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y)); + } + function projectPointToRect(x1, y1, width, height, x, y, out) { + if (width < 0) { + x1 = x1 + width; + width = -width; + } + if (height < 0) { + y1 = y1 + height; + height = -height; + } + var x2 = x1 + width; + var y2 = y1 + height; + var ox = out[0] = Math.min(Math.max(x, x1), x2); + var oy = out[1] = Math.min(Math.max(y, y1), y2); + return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y)); + } + var tmpPt = []; + function nearestPointOnRect(pt, rect, out) { + var dist = projectPointToRect(rect.x, rect.y, rect.width, rect.height, pt.x, pt.y, tmpPt); + out.set(tmpPt[0], tmpPt[1]); + return dist; + } + /** + * Calculate min distance corresponding point. + * This method won't evaluate if point is in the path. + */ + function nearestPointOnPath(pt, path, out) { + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + var x1; + var y1; + var minDist = Infinity; + var data = path.data; + var x = pt.x; + var y = pt.y; + for (var i = 0; i < data.length;) { + var cmd = data[i++]; + if (i === 1) { + xi = data[i]; + yi = data[i + 1]; + x0 = xi; + y0 = yi; + } + var d = minDist; + switch (cmd) { + case CMD$3.M: + // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 + // 在 closePath 的时候使用 + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + break; + case CMD$3.L: + d = projectPointToLine(xi, yi, data[i], data[i + 1], x, y, tmpPt, true); + xi = data[i++]; + yi = data[i++]; + break; + case CMD$3.C: + d = cubicProjectPoint(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt); + xi = data[i++]; + yi = data[i++]; + break; + case CMD$3.Q: + d = quadraticProjectPoint(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y, tmpPt); + xi = data[i++]; + yi = data[i++]; + break; + case CMD$3.A: + // TODO Arc 判断的开销比较大 + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var theta = data[i++]; + var dTheta = data[i++]; + // TODO Arc 旋转 + i += 1; + var anticlockwise = !!(1 - data[i++]); + x1 = Math.cos(theta) * rx + cx; + y1 = Math.sin(theta) * ry + cy; + // 不是直接使用 arc 命令 + if (i <= 1) { + // 第一个命令起点还未定义 + x0 = x1; + y0 = y1; + } + // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放 + var _x = (x - cx) * ry / rx + cx; + d = projectPointToArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y, tmpPt); + xi = Math.cos(theta + dTheta) * rx + cx; + yi = Math.sin(theta + dTheta) * ry + cy; + break; + case CMD$3.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + d = projectPointToRect(x0, y0, width, height, x, y, tmpPt); + break; + case CMD$3.Z: + d = projectPointToLine(xi, yi, x0, y0, x, y, tmpPt, true); + xi = x0; + yi = y0; + break; + } + if (d < minDist) { + minDist = d; + out.set(tmpPt[0], tmpPt[1]); + } + } + return minDist; + } + // Temporal variable for intermediate usage. + var pt0 = new Point(); + var pt1 = new Point(); + var pt2 = new Point(); + var dir = new Point(); + var dir2 = new Point(); + /** + * Calculate a proper guide line based on the label position and graphic element definition + * @param label + * @param labelRect + * @param target + * @param targetRect + */ + function updateLabelLinePoints(target, labelLineModel) { + if (!target) { + return; + } + var labelLine = target.getTextGuideLine(); + var label = target.getTextContent(); + // Needs to create text guide in each charts. + if (!(label && labelLine)) { + return; + } + var labelGuideConfig = target.textGuideLineConfig || {}; + var points = [[0, 0], [0, 0], [0, 0]]; + var searchSpace = labelGuideConfig.candidates || DEFAULT_SEARCH_SPACE; + var labelRect = label.getBoundingRect().clone(); + labelRect.applyTransform(label.getComputedTransform()); + var minDist = Infinity; + var anchorPoint = labelGuideConfig.anchor; + var targetTransform = target.getComputedTransform(); + var targetInversedTransform = targetTransform && invert([], targetTransform); + var len = labelLineModel.get('length2') || 0; + if (anchorPoint) { + pt2.copy(anchorPoint); + } + for (var i = 0; i < searchSpace.length; i++) { + var candidate = searchSpace[i]; + getCandidateAnchor(candidate, 0, labelRect, pt0, dir); + Point.scaleAndAdd(pt1, pt0, dir, len); + // Transform to target coord space. + pt1.transform(targetInversedTransform); + // Note: getBoundingRect will ensure the `path` being created. + var boundingRect = target.getBoundingRect(); + var dist = anchorPoint ? anchorPoint.distance(pt1) : target instanceof Path ? nearestPointOnPath(pt1, target.path, pt2) : nearestPointOnRect(pt1, boundingRect, pt2); + // TODO pt2 is in the path + if (dist < minDist) { + minDist = dist; + // Transform back to global space. + pt1.transform(targetTransform); + pt2.transform(targetTransform); + pt2.toArray(points[0]); + pt1.toArray(points[1]); + pt0.toArray(points[2]); + } + } + limitTurnAngle(points, labelLineModel.get('minTurnAngle')); + labelLine.setShape({ + points: points + }); + } + // Temporal variable for the limitTurnAngle function + var tmpArr = []; + var tmpProjPoint = new Point(); + /** + * Reduce the line segment attached to the label to limit the turn angle between two segments. + * @param linePoints + * @param minTurnAngle Radian of minimum turn angle. 0 - 180 + */ + function limitTurnAngle(linePoints, minTurnAngle) { + if (!(minTurnAngle <= 180 && minTurnAngle > 0)) { + return; + } + minTurnAngle = minTurnAngle / 180 * Math.PI; + // The line points can be + // /pt1----pt2 (label) + // / + // pt0/ + pt0.fromArray(linePoints[0]); + pt1.fromArray(linePoints[1]); + pt2.fromArray(linePoints[2]); + Point.sub(dir, pt0, pt1); + Point.sub(dir2, pt2, pt1); + var len1 = dir.len(); + var len2 = dir2.len(); + if (len1 < 1e-3 || len2 < 1e-3) { + return; + } + dir.scale(1 / len1); + dir2.scale(1 / len2); + var angleCos = dir.dot(dir2); + var minTurnAngleCos = Math.cos(minTurnAngle); + if (minTurnAngleCos < angleCos) { + // Smaller than minTurnAngle + // Calculate project point of pt0 on pt1-pt2 + var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false); + tmpProjPoint.fromArray(tmpArr); + // Calculate new projected length with limited minTurnAngle and get the new connect point + tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); + // Limit the new calculated connect point between pt1 and pt2. + var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y); + if (isNaN(t)) { + return; + } + if (t < 0) { + Point.copy(tmpProjPoint, pt1); + } else if (t > 1) { + Point.copy(tmpProjPoint, pt2); + } + tmpProjPoint.toArray(linePoints[1]); + } + } + /** + * Limit the angle of line and the surface + * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite + */ + function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) { + if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) { + return; + } + maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI; + pt0.fromArray(linePoints[0]); + pt1.fromArray(linePoints[1]); + pt2.fromArray(linePoints[2]); + Point.sub(dir, pt1, pt0); + Point.sub(dir2, pt2, pt1); + var len1 = dir.len(); + var len2 = dir2.len(); + if (len1 < 1e-3 || len2 < 1e-3) { + return; + } + dir.scale(1 / len1); + dir2.scale(1 / len2); + var angleCos = dir.dot(surfaceNormal); + var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle); + if (angleCos < maxSurfaceAngleCos) { + // Calculate project point of pt0 on pt1-pt2 + var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false); + tmpProjPoint.fromArray(tmpArr); + var HALF_PI = Math.PI / 2; + var angle2 = Math.acos(dir2.dot(surfaceNormal)); + var newAngle = HALF_PI + angle2 - maxSurfaceAngle; + if (newAngle >= HALF_PI) { + // parallel + Point.copy(tmpProjPoint, pt2); + } else { + // Calculate new projected length with limited minTurnAngle and get the new connect point + tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); + // Limit the new calculated connect point between pt1 and pt2. + var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y); + if (isNaN(t)) { + return; + } + if (t < 0) { + Point.copy(tmpProjPoint, pt1); + } else if (t > 1) { + Point.copy(tmpProjPoint, pt2); + } + } + tmpProjPoint.toArray(linePoints[1]); + } + } + function setLabelLineState(labelLine, ignore, stateName, stateModel) { + var isNormal = stateName === 'normal'; + var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); + // Make sure display. + stateObj.ignore = ignore; + // Set smooth + var smooth = stateModel.get('smooth'); + if (smooth && smooth === true) { + smooth = 0.3; + } + stateObj.shape = stateObj.shape || {}; + if (smooth > 0) { + stateObj.shape.smooth = smooth; + } + var styleObj = stateModel.getModel('lineStyle').getLineStyle(); + isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj; + } + function buildLabelLinePath(path, shape) { + var smooth = shape.smooth; + var points = shape.points; + if (!points) { + return; + } + path.moveTo(points[0][0], points[0][1]); + if (smooth > 0 && points.length >= 3) { + var len1 = dist(points[0], points[1]); + var len2 = dist(points[1], points[2]); + if (!len1 || !len2) { + path.lineTo(points[1][0], points[1][1]); + path.lineTo(points[2][0], points[2][1]); + return; + } + var moveLen = Math.min(len1, len2) * smooth; + var midPoint0 = lerp([], points[1], points[0], moveLen / len1); + var midPoint2 = lerp([], points[1], points[2], moveLen / len2); + var midPoint1 = lerp([], midPoint0, midPoint2, 0.5); + path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]); + path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]); + } else { + for (var i = 1; i < points.length; i++) { + path.lineTo(points[i][0], points[i][1]); + } + } + } + /** + * Create a label line if necessary and set it's style. + */ + function setLabelLineStyle(targetEl, statesModels, defaultStyle) { + var labelLine = targetEl.getTextGuideLine(); + var label = targetEl.getTextContent(); + if (!label) { + // Not show label line if there is no label. + if (labelLine) { + targetEl.removeTextGuideLine(); + } + return; + } + var normalModel = statesModels.normal; + var showNormal = normalModel.get('show'); + var labelIgnoreNormal = label.ignore; + for (var i = 0; i < DISPLAY_STATES.length; i++) { + var stateName = DISPLAY_STATES[i]; + var stateModel = statesModels[stateName]; + var isNormal = stateName === 'normal'; + if (stateModel) { + var stateShow = stateModel.get('show'); + var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal); + if (isLabelIgnored // Not show when label is not shown in this state. + || !retrieve2(stateShow, showNormal) // Use normal state by default if not set. + ) { + var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName]; + if (stateObj) { + stateObj.ignore = true; + } + if (!!labelLine) { + setLabelLineState(labelLine, true, stateName, stateModel); + } + continue; + } + // Create labelLine if not exists + if (!labelLine) { + labelLine = new Polyline(); + targetEl.setTextGuideLine(labelLine); + // Reset state of normal because it's new created. + // NOTE: NORMAL should always been the first! + if (!isNormal && (labelIgnoreNormal || !showNormal)) { + setLabelLineState(labelLine, true, 'normal', statesModels.normal); + } + // Use same state proxy. + if (targetEl.stateProxy) { + labelLine.stateProxy = targetEl.stateProxy; + } + } + setLabelLineState(labelLine, false, stateName, stateModel); + } + } + if (labelLine) { + defaults(labelLine.style, defaultStyle); + // Not fill. + labelLine.style.fill = null; + var showAbove = normalModel.get('showAbove'); + var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {}; + labelLineConfig.showAbove = showAbove || false; + // Custom the buildPath. + labelLine.buildPath = buildLabelLinePath; + } + } + function getLabelLineStatesModels(itemModel, labelLineName) { + labelLineName = labelLineName || 'labelLine'; + var statesModels = { + normal: itemModel.getModel(labelLineName) + }; + for (var i = 0; i < SPECIAL_STATES.length; i++) { + var stateName = SPECIAL_STATES[i]; + statesModels[stateName] = itemModel.getModel([stateName, labelLineName]); + } + return statesModels; + } + + function prepareLayoutList(input) { + var list = []; + for (var i = 0; i < input.length; i++) { + var rawItem = input[i]; + if (rawItem.defaultAttr.ignore) { + continue; + } + var label = rawItem.label; + var transform = label.getComputedTransform(); + // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el. + var localRect = label.getBoundingRect(); + var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5; + var minMargin = label.style.margin || 0; + var globalRect = localRect.clone(); + globalRect.applyTransform(transform); + globalRect.x -= minMargin / 2; + globalRect.y -= minMargin / 2; + globalRect.width += minMargin; + globalRect.height += minMargin; + var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null; + list.push({ + label: label, + labelLine: rawItem.labelLine, + rect: globalRect, + localRect: localRect, + obb: obb, + priority: rawItem.priority, + defaultAttr: rawItem.defaultAttr, + layoutOption: rawItem.computedLayoutOption, + axisAligned: isAxisAligned, + transform: transform + }); + } + return list; + } + function shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) { + var len = list.length; + if (len < 2) { + return; + } + list.sort(function (a, b) { + return a.rect[xyDim] - b.rect[xyDim]; + }); + var lastPos = 0; + var delta; + var adjusted = false; + var totalShifts = 0; + for (var i = 0; i < len; i++) { + var item = list[i]; + var rect = item.rect; + delta = rect[xyDim] - lastPos; + if (delta < 0) { + // shiftForward(i, len, -delta); + rect[xyDim] -= delta; + item.label[xyDim] -= delta; + adjusted = true; + } + var shift = Math.max(-delta, 0); + totalShifts += shift; + lastPos = rect[xyDim] + rect[sizeDim]; + } + if (totalShifts > 0 && balanceShift) { + // Shift back to make the distribution more equally. + shiftList(-totalShifts / len, 0, len); + } + // TODO bleedMargin? + var first = list[0]; + var last = list[len - 1]; + var minGap; + var maxGap; + updateMinMaxGap(); + // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds. + minGap < 0 && squeezeGaps(-minGap, 0.8); + maxGap < 0 && squeezeGaps(maxGap, 0.8); + updateMinMaxGap(); + takeBoundsGap(minGap, maxGap, 1); + takeBoundsGap(maxGap, minGap, -1); + // Handle bailout when there is not enough space. + updateMinMaxGap(); + if (minGap < 0) { + squeezeWhenBailout(-minGap); + } + if (maxGap < 0) { + squeezeWhenBailout(maxGap); + } + function updateMinMaxGap() { + minGap = first.rect[xyDim] - minBound; + maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim]; + } + function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) { + if (gapThisBound < 0) { + // Move from other gap if can. + var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound); + if (moveFromMaxGap > 0) { + shiftList(moveFromMaxGap * moveDir, 0, len); + var remained = moveFromMaxGap + gapThisBound; + if (remained < 0) { + squeezeGaps(-remained * moveDir, 1); + } + } else { + squeezeGaps(-gapThisBound * moveDir, 1); + } + } + } + function shiftList(delta, start, end) { + if (delta !== 0) { + adjusted = true; + } + for (var i = start; i < end; i++) { + var item = list[i]; + var rect = item.rect; + rect[xyDim] += delta; + item.label[xyDim] += delta; + } + } + // Squeeze gaps if the labels exceed margin. + function squeezeGaps(delta, maxSqeezePercent) { + var gaps = []; + var totalGaps = 0; + for (var i = 1; i < len; i++) { + var prevItemRect = list[i - 1].rect; + var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0); + gaps.push(gap); + totalGaps += gap; + } + if (!totalGaps) { + return; + } + var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent); + if (delta > 0) { + for (var i = 0; i < len - 1; i++) { + // Distribute the shift delta to all gaps. + var movement = gaps[i] * squeezePercent; + // Forward + shiftList(movement, 0, i + 1); + } + } else { + // Backward + for (var i = len - 1; i > 0; i--) { + // Distribute the shift delta to all gaps. + var movement = gaps[i - 1] * squeezePercent; + shiftList(-movement, i, len); + } + } + } + /** + * Squeeze to allow overlap if there is no more space available. + * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds. + */ + function squeezeWhenBailout(delta) { + var dir = delta < 0 ? -1 : 1; + delta = Math.abs(delta); + var moveForEachLabel = Math.ceil(delta / (len - 1)); + for (var i = 0; i < len - 1; i++) { + if (dir > 0) { + // Forward + shiftList(moveForEachLabel, 0, i + 1); + } else { + // Backward + shiftList(-moveForEachLabel, len - i - 1, len); + } + delta -= moveForEachLabel; + if (delta <= 0) { + return; + } + } + } + return adjusted; + } + /** + * Adjust labels on x direction to avoid overlap. + */ + function shiftLayoutOnX(list, leftBound, rightBound, + // If average the shifts on all labels and add them to 0 + // TODO: Not sure if should enable it. + // Pros: The angle of lines will distribute more equally + // Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly. + balanceShift) { + return shiftLayout(list, 'x', 'width', leftBound, rightBound, balanceShift); + } + /** + * Adjust labels on y direction to avoid overlap. + */ + function shiftLayoutOnY(list, topBound, bottomBound, + // If average the shifts on all labels and add them to 0 + balanceShift) { + return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift); + } + function hideOverlap(labelList) { + var displayedLabels = []; + // TODO, render overflow visible first, put in the displayedLabels. + labelList.sort(function (a, b) { + return b.priority - a.priority; + }); + var globalRect = new BoundingRect(0, 0, 0, 0); + function hideEl(el) { + if (!el.ignore) { + // Show on emphasis. + var emphasisState = el.ensureState('emphasis'); + if (emphasisState.ignore == null) { + emphasisState.ignore = false; + } + } + el.ignore = true; + } + for (var i = 0; i < labelList.length; i++) { + var labelItem = labelList[i]; + var isAxisAligned = labelItem.axisAligned; + var localRect = labelItem.localRect; + var transform = labelItem.transform; + var label = labelItem.label; + var labelLine = labelItem.labelLine; + globalRect.copy(labelItem.rect); + // Add a threshold because layout may be aligned precisely. + globalRect.width -= 0.1; + globalRect.height -= 0.1; + globalRect.x += 0.05; + globalRect.y += 0.05; + var obb = labelItem.obb; + var overlapped = false; + for (var j = 0; j < displayedLabels.length; j++) { + var existsTextCfg = displayedLabels[j]; + // Fast rejection. + if (!globalRect.intersect(existsTextCfg.rect)) { + continue; + } + if (isAxisAligned && existsTextCfg.axisAligned) { + // Is overlapped + overlapped = true; + break; + } + if (!existsTextCfg.obb) { + // If self is not axis aligned. But other is. + existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform); + } + if (!obb) { + // If self is axis aligned. But other is not. + obb = new OrientedBoundingRect(localRect, transform); + } + if (obb.intersect(existsTextCfg.obb)) { + overlapped = true; + break; + } + } + // TODO Callback to determine if this overlap should be handled? + if (overlapped) { + hideEl(label); + labelLine && hideEl(labelLine); + } else { + label.attr('ignore', labelItem.defaultAttr.ignore); + labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore); + displayedLabels.push(labelItem); + } + } + } + + function cloneArr(points) { + if (points) { + var newPoints = []; + for (var i = 0; i < points.length; i++) { + newPoints.push(points[i].slice()); + } + return newPoints; + } + } + function prepareLayoutCallbackParams(labelItem, hostEl) { + var label = labelItem.label; + var labelLine = hostEl && hostEl.getTextGuideLine(); + return { + dataIndex: labelItem.dataIndex, + dataType: labelItem.dataType, + seriesIndex: labelItem.seriesModel.seriesIndex, + text: labelItem.label.style.text, + rect: labelItem.hostRect, + labelRect: labelItem.rect, + // x: labelAttr.x, + // y: labelAttr.y, + align: label.style.align, + verticalAlign: label.style.verticalAlign, + labelLinePoints: cloneArr(labelLine && labelLine.shape.points) + }; + } + var LABEL_OPTION_TO_STYLE_KEYS = ['align', 'verticalAlign', 'width', 'height', 'fontSize']; + var dummyTransformable = new Transformable(); + var labelLayoutInnerStore = makeInner(); + var labelLineAnimationStore = makeInner(); + function extendWithKeys(target, source, keys) { + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (source[key] != null) { + target[key] = source[key]; + } + } + } + var LABEL_LAYOUT_PROPS = ['x', 'y', 'rotation']; + var LabelManager = /** @class */function () { + function LabelManager() { + this._labelList = []; + this._chartViewList = []; + } + LabelManager.prototype.clearLabels = function () { + this._labelList = []; + this._chartViewList = []; + }; + /** + * Add label to manager + */ + LabelManager.prototype._addLabel = function (dataIndex, dataType, seriesModel, label, layoutOption) { + var labelStyle = label.style; + var hostEl = label.__hostTarget; + var textConfig = hostEl.textConfig || {}; + // TODO: If label is in other state. + var labelTransform = label.getComputedTransform(); + var labelRect = label.getBoundingRect().plain(); + BoundingRect.applyTransform(labelRect, labelRect, labelTransform); + if (labelTransform) { + dummyTransformable.setLocalTransform(labelTransform); + } else { + // Identity transform. + dummyTransformable.x = dummyTransformable.y = dummyTransformable.rotation = dummyTransformable.originX = dummyTransformable.originY = 0; + dummyTransformable.scaleX = dummyTransformable.scaleY = 1; + } + dummyTransformable.rotation = normalizeRadian(dummyTransformable.rotation); + var host = label.__hostTarget; + var hostRect; + if (host) { + hostRect = host.getBoundingRect().plain(); + var transform = host.getComputedTransform(); + BoundingRect.applyTransform(hostRect, hostRect, transform); + } + var labelGuide = hostRect && host.getTextGuideLine(); + this._labelList.push({ + label: label, + labelLine: labelGuide, + seriesModel: seriesModel, + dataIndex: dataIndex, + dataType: dataType, + layoutOption: layoutOption, + computedLayoutOption: null, + rect: labelRect, + hostRect: hostRect, + // Label with lower priority will be hidden when overlapped + // Use rect size as default priority + priority: hostRect ? hostRect.width * hostRect.height : 0, + // Save default label attributes. + // For restore if developers want get back to default value in callback. + defaultAttr: { + ignore: label.ignore, + labelGuideIgnore: labelGuide && labelGuide.ignore, + x: dummyTransformable.x, + y: dummyTransformable.y, + scaleX: dummyTransformable.scaleX, + scaleY: dummyTransformable.scaleY, + rotation: dummyTransformable.rotation, + style: { + x: labelStyle.x, + y: labelStyle.y, + align: labelStyle.align, + verticalAlign: labelStyle.verticalAlign, + width: labelStyle.width, + height: labelStyle.height, + fontSize: labelStyle.fontSize + }, + cursor: label.cursor, + attachedPos: textConfig.position, + attachedRot: textConfig.rotation + } + }); + }; + LabelManager.prototype.addLabelsOfSeries = function (chartView) { + var _this = this; + this._chartViewList.push(chartView); + var seriesModel = chartView.__model; + var layoutOption = seriesModel.get('labelLayout'); + /** + * Ignore layouting if it's not specified anything. + */ + if (!(isFunction(layoutOption) || keys(layoutOption).length)) { + return; + } + chartView.group.traverse(function (child) { + if (child.ignore) { + return true; // Stop traverse descendants. + } + // Only support label being hosted on graphic elements. + var textEl = child.getTextContent(); + var ecData = getECData(child); + // Can only attach the text on the element with dataIndex + if (textEl && !textEl.disableLabelLayout) { + _this._addLabel(ecData.dataIndex, ecData.dataType, seriesModel, textEl, layoutOption); + } + }); + }; + LabelManager.prototype.updateLayoutConfig = function (api) { + var width = api.getWidth(); + var height = api.getHeight(); + function createDragHandler(el, labelLineModel) { + return function () { + updateLabelLinePoints(el, labelLineModel); + }; + } + for (var i = 0; i < this._labelList.length; i++) { + var labelItem = this._labelList[i]; + var label = labelItem.label; + var hostEl = label.__hostTarget; + var defaultLabelAttr = labelItem.defaultAttr; + var layoutOption = void 0; + // TODO A global layout option? + if (isFunction(labelItem.layoutOption)) { + layoutOption = labelItem.layoutOption(prepareLayoutCallbackParams(labelItem, hostEl)); + } else { + layoutOption = labelItem.layoutOption; + } + layoutOption = layoutOption || {}; + labelItem.computedLayoutOption = layoutOption; + var degreeToRadian = Math.PI / 180; + // TODO hostEl should always exists. + // Or label should not have parent because the x, y is all in global space. + if (hostEl) { + hostEl.setTextConfig({ + // Force to set local false. + local: false, + // Ignore position and rotation config on the host el if x or y is changed. + position: layoutOption.x != null || layoutOption.y != null ? null : defaultLabelAttr.attachedPos, + // Ignore rotation config on the host el if rotation is changed. + rotation: layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.attachedRot, + offset: [layoutOption.dx || 0, layoutOption.dy || 0] + }); + } + var needsUpdateLabelLine = false; + if (layoutOption.x != null) { + // TODO width of chart view. + label.x = parsePercent$1(layoutOption.x, width); + label.setStyle('x', 0); // Ignore movement in style. TODO: origin. + needsUpdateLabelLine = true; + } else { + label.x = defaultLabelAttr.x; + label.setStyle('x', defaultLabelAttr.style.x); + } + if (layoutOption.y != null) { + // TODO height of chart view. + label.y = parsePercent$1(layoutOption.y, height); + label.setStyle('y', 0); // Ignore movement in style. + needsUpdateLabelLine = true; + } else { + label.y = defaultLabelAttr.y; + label.setStyle('y', defaultLabelAttr.style.y); + } + if (layoutOption.labelLinePoints) { + var guideLine = hostEl.getTextGuideLine(); + if (guideLine) { + guideLine.setShape({ + points: layoutOption.labelLinePoints + }); + // Not update + needsUpdateLabelLine = false; + } + } + var labelLayoutStore = labelLayoutInnerStore(label); + labelLayoutStore.needsUpdateLabelLine = needsUpdateLabelLine; + label.rotation = layoutOption.rotate != null ? layoutOption.rotate * degreeToRadian : defaultLabelAttr.rotation; + label.scaleX = defaultLabelAttr.scaleX; + label.scaleY = defaultLabelAttr.scaleY; + for (var k = 0; k < LABEL_OPTION_TO_STYLE_KEYS.length; k++) { + var key = LABEL_OPTION_TO_STYLE_KEYS[k]; + label.setStyle(key, layoutOption[key] != null ? layoutOption[key] : defaultLabelAttr.style[key]); + } + if (layoutOption.draggable) { + label.draggable = true; + label.cursor = 'move'; + if (hostEl) { + var hostModel = labelItem.seriesModel; + if (labelItem.dataIndex != null) { + var data = labelItem.seriesModel.getData(labelItem.dataType); + hostModel = data.getItemModel(labelItem.dataIndex); + } + label.on('drag', createDragHandler(hostEl, hostModel.getModel('labelLine'))); + } + } else { + // TODO Other drag functions? + label.off('drag'); + label.cursor = defaultLabelAttr.cursor; + } + } + }; + LabelManager.prototype.layout = function (api) { + var width = api.getWidth(); + var height = api.getHeight(); + var labelList = prepareLayoutList(this._labelList); + var labelsNeedsAdjustOnX = filter(labelList, function (item) { + return item.layoutOption.moveOverlap === 'shiftX'; + }); + var labelsNeedsAdjustOnY = filter(labelList, function (item) { + return item.layoutOption.moveOverlap === 'shiftY'; + }); + shiftLayoutOnX(labelsNeedsAdjustOnX, 0, width); + shiftLayoutOnY(labelsNeedsAdjustOnY, 0, height); + var labelsNeedsHideOverlap = filter(labelList, function (item) { + return item.layoutOption.hideOverlap; + }); + hideOverlap(labelsNeedsHideOverlap); + }; + /** + * Process all labels. Not only labels with layoutOption. + */ + LabelManager.prototype.processLabelsOverall = function () { + var _this = this; + each(this._chartViewList, function (chartView) { + var seriesModel = chartView.__model; + var ignoreLabelLineUpdate = chartView.ignoreLabelLineUpdate; + var animationEnabled = seriesModel.isAnimationEnabled(); + chartView.group.traverse(function (child) { + if (child.ignore && !child.forceLabelAnimation) { + return true; // Stop traverse descendants. + } + + var needsUpdateLabelLine = !ignoreLabelLineUpdate; + var label = child.getTextContent(); + if (!needsUpdateLabelLine && label) { + needsUpdateLabelLine = labelLayoutInnerStore(label).needsUpdateLabelLine; + } + if (needsUpdateLabelLine) { + _this._updateLabelLine(child, seriesModel); + } + if (animationEnabled) { + _this._animateLabels(child, seriesModel); + } + }); + }); + }; + LabelManager.prototype._updateLabelLine = function (el, seriesModel) { + // Only support label being hosted on graphic elements. + var textEl = el.getTextContent(); + // Update label line style. + var ecData = getECData(el); + var dataIndex = ecData.dataIndex; + // Only support labelLine on the labels represent data. + if (textEl && dataIndex != null) { + var data = seriesModel.getData(ecData.dataType); + var itemModel = data.getItemModel(dataIndex); + var defaultStyle = {}; + var visualStyle = data.getItemVisual(dataIndex, 'style'); + if (visualStyle) { + var visualType = data.getVisual('drawType'); + // Default to be same with main color + defaultStyle.stroke = visualStyle[visualType]; + } + var labelLineModel = itemModel.getModel('labelLine'); + setLabelLineStyle(el, getLabelLineStatesModels(itemModel), defaultStyle); + updateLabelLinePoints(el, labelLineModel); + } + }; + LabelManager.prototype._animateLabels = function (el, seriesModel) { + var textEl = el.getTextContent(); + var guideLine = el.getTextGuideLine(); + // Animate + if (textEl + // `forceLabelAnimation` has the highest priority + && (el.forceLabelAnimation || !textEl.ignore && !textEl.invisible && !el.disableLabelAnimation && !isElementRemoved(el))) { + var layoutStore = labelLayoutInnerStore(textEl); + var oldLayout = layoutStore.oldLayout; + var ecData = getECData(el); + var dataIndex = ecData.dataIndex; + var newProps = { + x: textEl.x, + y: textEl.y, + rotation: textEl.rotation + }; + var data = seriesModel.getData(ecData.dataType); + if (!oldLayout) { + textEl.attr(newProps); + // Disable fade in animation if value animation is enabled. + if (!labelInner(textEl).valueAnimation) { + var oldOpacity = retrieve2(textEl.style.opacity, 1); + // Fade in animation + textEl.style.opacity = 0; + initProps(textEl, { + style: { + opacity: oldOpacity + } + }, seriesModel, dataIndex); + } + } else { + textEl.attr(oldLayout); + // Make sure the animation from is in the right status. + var prevStates = el.prevStates; + if (prevStates) { + if (indexOf(prevStates, 'select') >= 0) { + textEl.attr(layoutStore.oldLayoutSelect); + } + if (indexOf(prevStates, 'emphasis') >= 0) { + textEl.attr(layoutStore.oldLayoutEmphasis); + } + } + updateProps(textEl, newProps, seriesModel, dataIndex); + } + layoutStore.oldLayout = newProps; + if (textEl.states.select) { + var layoutSelect = layoutStore.oldLayoutSelect = {}; + extendWithKeys(layoutSelect, newProps, LABEL_LAYOUT_PROPS); + extendWithKeys(layoutSelect, textEl.states.select, LABEL_LAYOUT_PROPS); + } + if (textEl.states.emphasis) { + var layoutEmphasis = layoutStore.oldLayoutEmphasis = {}; + extendWithKeys(layoutEmphasis, newProps, LABEL_LAYOUT_PROPS); + extendWithKeys(layoutEmphasis, textEl.states.emphasis, LABEL_LAYOUT_PROPS); + } + animateLabelValue(textEl, dataIndex, data, seriesModel, seriesModel); + } + if (guideLine && !guideLine.ignore && !guideLine.invisible) { + var layoutStore = labelLineAnimationStore(guideLine); + var oldLayout = layoutStore.oldLayout; + var newLayout = { + points: guideLine.shape.points + }; + if (!oldLayout) { + guideLine.setShape(newLayout); + guideLine.style.strokePercent = 0; + initProps(guideLine, { + style: { + strokePercent: 1 + } + }, seriesModel); + } else { + guideLine.attr({ + shape: oldLayout + }); + updateProps(guideLine, { + shape: newLayout + }, seriesModel); + } + layoutStore.oldLayout = newLayout; + } + }; + return LabelManager; + }(); + + var getLabelManager = makeInner(); + function installLabelLayout(registers) { + registers.registerUpdateLifecycle('series:beforeupdate', function (ecModel, api, params) { + // TODO api provide an namespace that can save stuff per instance + var labelManager = getLabelManager(api).labelManager; + if (!labelManager) { + labelManager = getLabelManager(api).labelManager = new LabelManager(); + } + labelManager.clearLabels(); + }); + registers.registerUpdateLifecycle('series:layoutlabels', function (ecModel, api, params) { + var labelManager = getLabelManager(api).labelManager; + params.updatedSeries.forEach(function (series) { + labelManager.addLabelsOfSeries(api.getViewOfSeriesModel(series)); + }); + labelManager.updateLayoutConfig(api); + labelManager.layout(api); + labelManager.processLabelsOverall(); + }); + } + + var mathSin$4 = Math.sin; + var mathCos$4 = Math.cos; + var PI$4 = Math.PI; + var PI2$7 = Math.PI * 2; + var degree = 180 / PI$4; + var SVGPathRebuilder = (function () { + function SVGPathRebuilder() { + } + SVGPathRebuilder.prototype.reset = function (precision) { + this._start = true; + this._d = []; + this._str = ''; + this._p = Math.pow(10, precision || 4); + }; + SVGPathRebuilder.prototype.moveTo = function (x, y) { + this._add('M', x, y); + }; + SVGPathRebuilder.prototype.lineTo = function (x, y) { + this._add('L', x, y); + }; + SVGPathRebuilder.prototype.bezierCurveTo = function (x, y, x2, y2, x3, y3) { + this._add('C', x, y, x2, y2, x3, y3); + }; + SVGPathRebuilder.prototype.quadraticCurveTo = function (x, y, x2, y2) { + this._add('Q', x, y, x2, y2); + }; + SVGPathRebuilder.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) { + this.ellipse(cx, cy, r, r, 0, startAngle, endAngle, anticlockwise); + }; + SVGPathRebuilder.prototype.ellipse = function (cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise) { + var dTheta = endAngle - startAngle; + var clockwise = !anticlockwise; + var dThetaPositive = Math.abs(dTheta); + var isCircle = isAroundZero$1(dThetaPositive - PI2$7) + || (clockwise ? dTheta >= PI2$7 : -dTheta >= PI2$7); + var unifiedTheta = dTheta > 0 ? dTheta % PI2$7 : (dTheta % PI2$7 + PI2$7); + var large = false; + if (isCircle) { + large = true; + } + else if (isAroundZero$1(dThetaPositive)) { + large = false; + } + else { + large = (unifiedTheta >= PI$4) === !!clockwise; + } + var x0 = cx + rx * mathCos$4(startAngle); + var y0 = cy + ry * mathSin$4(startAngle); + if (this._start) { + this._add('M', x0, y0); + } + var xRot = Math.round(psi * degree); + if (isCircle) { + var p = 1 / this._p; + var dTheta_1 = (clockwise ? 1 : -1) * (PI2$7 - p); + this._add('A', rx, ry, xRot, 1, +clockwise, cx + rx * mathCos$4(startAngle + dTheta_1), cy + ry * mathSin$4(startAngle + dTheta_1)); + if (p > 1e-2) { + this._add('A', rx, ry, xRot, 0, +clockwise, x0, y0); + } + } + else { + var x = cx + rx * mathCos$4(endAngle); + var y = cy + ry * mathSin$4(endAngle); + this._add('A', rx, ry, xRot, +large, +clockwise, x, y); + } + }; + SVGPathRebuilder.prototype.rect = function (x, y, w, h) { + this._add('M', x, y); + this._add('l', w, 0); + this._add('l', 0, h); + this._add('l', -w, 0); + this._add('Z'); + }; + SVGPathRebuilder.prototype.closePath = function () { + if (this._d.length > 0) { + this._add('Z'); + } + }; + SVGPathRebuilder.prototype._add = function (cmd, a, b, c, d, e, f, g, h) { + var vals = []; + var p = this._p; + for (var i = 1; i < arguments.length; i++) { + var val = arguments[i]; + if (isNaN(val)) { + this._invalid = true; + return; + } + vals.push(Math.round(val * p) / p); + } + this._d.push(cmd + vals.join(' ')); + this._start = cmd === 'Z'; + }; + SVGPathRebuilder.prototype.generateStr = function () { + this._str = this._invalid ? '' : this._d.join(''); + this._d = []; + }; + SVGPathRebuilder.prototype.getStr = function () { + return this._str; + }; + return SVGPathRebuilder; + }()); + + var NONE = 'none'; + var mathRound$1 = Math.round; + function pathHasFill(style) { + var fill = style.fill; + return fill != null && fill !== NONE; + } + function pathHasStroke(style) { + var stroke = style.stroke; + return stroke != null && stroke !== NONE; + } + var strokeProps = ['lineCap', 'miterLimit', 'lineJoin']; + var svgStrokeProps = map(strokeProps, function (prop) { return "stroke-" + prop.toLowerCase(); }); + function mapStyleToAttrs(updateAttr, style, el, forceUpdate) { + var opacity = style.opacity == null ? 1 : style.opacity; + if (el instanceof ZRImage) { + updateAttr('opacity', opacity); + return; + } + if (pathHasFill(style)) { + var fill = normalizeColor(style.fill); + updateAttr('fill', fill.color); + var fillOpacity = style.fillOpacity != null + ? style.fillOpacity * fill.opacity * opacity + : fill.opacity * opacity; + if (forceUpdate || fillOpacity < 1) { + updateAttr('fill-opacity', fillOpacity); + } + } + else { + updateAttr('fill', NONE); + } + if (pathHasStroke(style)) { + var stroke = normalizeColor(style.stroke); + updateAttr('stroke', stroke.color); + var strokeScale = style.strokeNoScale + ? el.getLineScale() + : 1; + var strokeWidth = (strokeScale ? (style.lineWidth || 0) / strokeScale : 0); + var strokeOpacity = style.strokeOpacity != null + ? style.strokeOpacity * stroke.opacity * opacity + : stroke.opacity * opacity; + var strokeFirst = style.strokeFirst; + if (forceUpdate || strokeWidth !== 1) { + updateAttr('stroke-width', strokeWidth); + } + if (forceUpdate || strokeFirst) { + updateAttr('paint-order', strokeFirst ? 'stroke' : 'fill'); + } + if (forceUpdate || strokeOpacity < 1) { + updateAttr('stroke-opacity', strokeOpacity); + } + if (style.lineDash) { + var _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1]; + if (lineDash) { + lineDashOffset = mathRound$1(lineDashOffset || 0); + updateAttr('stroke-dasharray', lineDash.join(',')); + if (lineDashOffset || forceUpdate) { + updateAttr('stroke-dashoffset', lineDashOffset); + } + } + } + else if (forceUpdate) { + updateAttr('stroke-dasharray', NONE); + } + for (var i = 0; i < strokeProps.length; i++) { + var propName = strokeProps[i]; + if (forceUpdate || style[propName] !== DEFAULT_PATH_STYLE[propName]) { + var val = style[propName] || DEFAULT_PATH_STYLE[propName]; + val && updateAttr(svgStrokeProps[i], val); + } + } + } + else if (forceUpdate) { + updateAttr('stroke', NONE); + } + } + + var SVGNS = 'http://www.w3.org/2000/svg'; + var XLINKNS = 'http://www.w3.org/1999/xlink'; + var XMLNS = 'http://www.w3.org/2000/xmlns/'; + var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'; + var META_DATA_PREFIX = 'ecmeta_'; + function createElement(name) { + return document.createElementNS(SVGNS, name); + } + function createVNode(tag, key, attrs, children, text) { + return { + tag: tag, + attrs: attrs || {}, + children: children, + text: text, + key: key + }; + } + function createElementOpen(name, attrs) { + var attrsStr = []; + if (attrs) { + for (var key in attrs) { + var val = attrs[key]; + var part = key; + if (val === false) { + continue; + } + else if (val !== true && val != null) { + part += "=\"" + val + "\""; + } + attrsStr.push(part); + } + } + return "<" + name + " " + attrsStr.join(' ') + ">"; + } + function createElementClose(name) { + return ""; + } + function vNodeToString(el, opts) { + opts = opts || {}; + var S = opts.newline ? '\n' : ''; + function convertElToString(el) { + var children = el.children, tag = el.tag, attrs = el.attrs, text = el.text; + return createElementOpen(tag, attrs) + + (tag !== 'style' ? encodeHTML(text) : text || '') + + (children ? "" + S + map(children, function (child) { return convertElToString(child); }).join(S) + S : '') + + createElementClose(tag); + } + return convertElToString(el); + } + function getCssString(selectorNodes, animationNodes, opts) { + opts = opts || {}; + var S = opts.newline ? '\n' : ''; + var bracketBegin = " {" + S; + var bracketEnd = S + "}"; + var selectors = map(keys(selectorNodes), function (className) { + return className + bracketBegin + map(keys(selectorNodes[className]), function (attrName) { + return attrName + ":" + selectorNodes[className][attrName] + ";"; + }).join(S) + bracketEnd; + }).join(S); + var animations = map(keys(animationNodes), function (animationName) { + return "@keyframes " + animationName + bracketBegin + map(keys(animationNodes[animationName]), function (percent) { + return percent + bracketBegin + map(keys(animationNodes[animationName][percent]), function (attrName) { + var val = animationNodes[animationName][percent][attrName]; + if (attrName === 'd') { + val = "path(\"" + val + "\")"; + } + return attrName + ":" + val + ";"; + }).join(S) + bracketEnd; + }).join(S) + bracketEnd; + }).join(S); + if (!selectors && !animations) { + return ''; + } + return [''].join(S); + } + function createBrushScope(zrId) { + return { + zrId: zrId, + shadowCache: {}, + patternCache: {}, + gradientCache: {}, + clipPathCache: {}, + defs: {}, + cssNodes: {}, + cssAnims: {}, + cssStyleCache: {}, + cssAnimIdx: 0, + shadowIdx: 0, + gradientIdx: 0, + patternIdx: 0, + clipPathIdx: 0 + }; + } + function createSVGVNode(width, height, children, useViewBox) { + return createVNode('svg', 'root', { + 'width': width, + 'height': height, + 'xmlns': SVGNS, + 'xmlns:xlink': XLINKNS, + 'version': '1.1', + 'baseProfile': 'full', + 'viewBox': useViewBox ? "0 0 " + width + " " + height : false + }, children); + } + + var cssClassIdx = 0; + function getClassId() { + return cssClassIdx++; + } + + var EASING_MAP = { + cubicIn: '0.32,0,0.67,0', + cubicOut: '0.33,1,0.68,1', + cubicInOut: '0.65,0,0.35,1', + quadraticIn: '0.11,0,0.5,0', + quadraticOut: '0.5,1,0.89,1', + quadraticInOut: '0.45,0,0.55,1', + quarticIn: '0.5,0,0.75,0', + quarticOut: '0.25,1,0.5,1', + quarticInOut: '0.76,0,0.24,1', + quinticIn: '0.64,0,0.78,0', + quinticOut: '0.22,1,0.36,1', + quinticInOut: '0.83,0,0.17,1', + sinusoidalIn: '0.12,0,0.39,0', + sinusoidalOut: '0.61,1,0.88,1', + sinusoidalInOut: '0.37,0,0.63,1', + exponentialIn: '0.7,0,0.84,0', + exponentialOut: '0.16,1,0.3,1', + exponentialInOut: '0.87,0,0.13,1', + circularIn: '0.55,0,1,0.45', + circularOut: '0,0.55,0.45,1', + circularInOut: '0.85,0,0.15,1' + }; + var transformOriginKey = 'transform-origin'; + function buildPathString(el, kfShape, path) { + var shape = extend({}, el.shape); + extend(shape, kfShape); + el.buildPath(path, shape); + var svgPathBuilder = new SVGPathRebuilder(); + svgPathBuilder.reset(getPathPrecision(el)); + path.rebuildPath(svgPathBuilder, 1); + svgPathBuilder.generateStr(); + return svgPathBuilder.getStr(); + } + function setTransformOrigin(target, transform) { + var originX = transform.originX, originY = transform.originY; + if (originX || originY) { + target[transformOriginKey] = originX + "px " + originY + "px"; + } + } + var ANIMATE_STYLE_MAP = { + fill: 'fill', + opacity: 'opacity', + lineWidth: 'stroke-width', + lineDashOffset: 'stroke-dashoffset' + }; + function addAnimation(cssAnim, scope) { + var animationName = scope.zrId + '-ani-' + scope.cssAnimIdx++; + scope.cssAnims[animationName] = cssAnim; + return animationName; + } + function createCompoundPathCSSAnimation(el, attrs, scope) { + var paths = el.shape.paths; + var composedAnim = {}; + var cssAnimationCfg; + var cssAnimationName; + each(paths, function (path) { + var subScope = createBrushScope(scope.zrId); + subScope.animation = true; + createCSSAnimation(path, {}, subScope, true); + var cssAnims = subScope.cssAnims; + var cssNodes = subScope.cssNodes; + var animNames = keys(cssAnims); + var len = animNames.length; + if (!len) { + return; + } + cssAnimationName = animNames[len - 1]; + var lastAnim = cssAnims[cssAnimationName]; + for (var percent in lastAnim) { + var kf = lastAnim[percent]; + composedAnim[percent] = composedAnim[percent] || { d: '' }; + composedAnim[percent].d += kf.d || ''; + } + for (var className in cssNodes) { + var val = cssNodes[className].animation; + if (val.indexOf(cssAnimationName) >= 0) { + cssAnimationCfg = val; + } + } + }); + if (!cssAnimationCfg) { + return; + } + attrs.d = false; + var animationName = addAnimation(composedAnim, scope); + return cssAnimationCfg.replace(cssAnimationName, animationName); + } + function getEasingFunc(easing) { + return isString(easing) + ? EASING_MAP[easing] + ? "cubic-bezier(" + EASING_MAP[easing] + ")" + : createCubicEasingFunc(easing) ? easing : '' + : ''; + } + function createCSSAnimation(el, attrs, scope, onlyShape) { + var animators = el.animators; + var len = animators.length; + var cssAnimations = []; + if (el instanceof CompoundPath) { + var animationCfg = createCompoundPathCSSAnimation(el, attrs, scope); + if (animationCfg) { + cssAnimations.push(animationCfg); + } + else if (!len) { + return; + } + } + else if (!len) { + return; + } + var groupAnimators = {}; + for (var i = 0; i < len; i++) { + var animator = animators[i]; + var cfgArr = [animator.getMaxTime() / 1000 + 's']; + var easing = getEasingFunc(animator.getClip().easing); + var delay = animator.getDelay(); + if (easing) { + cfgArr.push(easing); + } + else { + cfgArr.push('linear'); + } + if (delay) { + cfgArr.push(delay / 1000 + 's'); + } + if (animator.getLoop()) { + cfgArr.push('infinite'); + } + var cfg = cfgArr.join(' '); + groupAnimators[cfg] = groupAnimators[cfg] || [cfg, []]; + groupAnimators[cfg][1].push(animator); + } + function createSingleCSSAnimation(groupAnimator) { + var animators = groupAnimator[1]; + var len = animators.length; + var transformKfs = {}; + var shapeKfs = {}; + var finalKfs = {}; + var animationTimingFunctionAttrName = 'animation-timing-function'; + function saveAnimatorTrackToCssKfs(animator, cssKfs, toCssAttrName) { + var tracks = animator.getTracks(); + var maxTime = animator.getMaxTime(); + for (var k = 0; k < tracks.length; k++) { + var track = tracks[k]; + if (track.needsAnimate()) { + var kfs = track.keyframes; + var attrName = track.propName; + toCssAttrName && (attrName = toCssAttrName(attrName)); + if (attrName) { + for (var i = 0; i < kfs.length; i++) { + var kf = kfs[i]; + var percent = Math.round(kf.time / maxTime * 100) + '%'; + var kfEasing = getEasingFunc(kf.easing); + var rawValue = kf.rawValue; + if (isString(rawValue) || isNumber(rawValue)) { + cssKfs[percent] = cssKfs[percent] || {}; + cssKfs[percent][attrName] = kf.rawValue; + if (kfEasing) { + cssKfs[percent][animationTimingFunctionAttrName] = kfEasing; + } + } + } + } + } + } + } + for (var i = 0; i < len; i++) { + var animator = animators[i]; + var targetProp = animator.targetName; + if (!targetProp) { + !onlyShape && saveAnimatorTrackToCssKfs(animator, transformKfs); + } + else if (targetProp === 'shape') { + saveAnimatorTrackToCssKfs(animator, shapeKfs); + } + } + for (var percent in transformKfs) { + var transform = {}; + copyTransform(transform, el); + extend(transform, transformKfs[percent]); + var str = getSRTTransformString(transform); + var timingFunction = transformKfs[percent][animationTimingFunctionAttrName]; + finalKfs[percent] = str ? { + transform: str + } : {}; + setTransformOrigin(finalKfs[percent], transform); + if (timingFunction) { + finalKfs[percent][animationTimingFunctionAttrName] = timingFunction; + } + } + var path; + var canAnimateShape = true; + for (var percent in shapeKfs) { + finalKfs[percent] = finalKfs[percent] || {}; + var isFirst = !path; + var timingFunction = shapeKfs[percent][animationTimingFunctionAttrName]; + if (isFirst) { + path = new PathProxy(); + } + var len_1 = path.len(); + path.reset(); + finalKfs[percent].d = buildPathString(el, shapeKfs[percent], path); + var newLen = path.len(); + if (!isFirst && len_1 !== newLen) { + canAnimateShape = false; + break; + } + if (timingFunction) { + finalKfs[percent][animationTimingFunctionAttrName] = timingFunction; + } + } + if (!canAnimateShape) { + for (var percent in finalKfs) { + delete finalKfs[percent].d; + } + } + if (!onlyShape) { + for (var i = 0; i < len; i++) { + var animator = animators[i]; + var targetProp = animator.targetName; + if (targetProp === 'style') { + saveAnimatorTrackToCssKfs(animator, finalKfs, function (propName) { return ANIMATE_STYLE_MAP[propName]; }); + } + } + } + var percents = keys(finalKfs); + var allTransformOriginSame = true; + var transformOrigin; + for (var i = 1; i < percents.length; i++) { + var p0 = percents[i - 1]; + var p1 = percents[i]; + if (finalKfs[p0][transformOriginKey] !== finalKfs[p1][transformOriginKey]) { + allTransformOriginSame = false; + break; + } + transformOrigin = finalKfs[p0][transformOriginKey]; + } + if (allTransformOriginSame && transformOrigin) { + for (var percent in finalKfs) { + if (finalKfs[percent][transformOriginKey]) { + delete finalKfs[percent][transformOriginKey]; + } + } + attrs[transformOriginKey] = transformOrigin; + } + if (filter(percents, function (percent) { return keys(finalKfs[percent]).length > 0; }).length) { + var animationName = addAnimation(finalKfs, scope); + return animationName + " " + groupAnimator[0] + " both"; + } + } + for (var key in groupAnimators) { + var animationCfg = createSingleCSSAnimation(groupAnimators[key]); + if (animationCfg) { + cssAnimations.push(animationCfg); + } + } + if (cssAnimations.length) { + var className = scope.zrId + '-cls-' + getClassId(); + scope.cssNodes['.' + className] = { + animation: cssAnimations.join(',') + }; + attrs["class"] = className; + } + } + + function createCSSEmphasis(el, attrs, scope) { + if (!el.ignore) { + if (el.isSilent()) { + var style = { + 'pointer-events': 'none' + }; + setClassAttribute(style, attrs, scope, true); + } + else { + var emphasisStyle = el.states.emphasis && el.states.emphasis.style + ? el.states.emphasis.style + : {}; + var fill = emphasisStyle.fill; + if (!fill) { + var normalFill = el.style && el.style.fill; + var selectFill = el.states.select + && el.states.select.style + && el.states.select.style.fill; + var fromFill = el.currentStates.indexOf('select') >= 0 + ? (selectFill || normalFill) + : normalFill; + if (fromFill) { + fill = liftColor(fromFill); + } + } + var lineWidth = emphasisStyle.lineWidth; + if (lineWidth) { + var scaleX = (!emphasisStyle.strokeNoScale && el.transform) + ? el.transform[0] + : 1; + lineWidth = lineWidth / scaleX; + } + var style = { + cursor: 'pointer' + }; + if (fill) { + style.fill = fill; + } + if (emphasisStyle.stroke) { + style.stroke = emphasisStyle.stroke; + } + if (lineWidth) { + style['stroke-width'] = lineWidth; + } + setClassAttribute(style, attrs, scope, true); + } + } + } + function setClassAttribute(style, attrs, scope, withHover) { + var styleKey = JSON.stringify(style); + var className = scope.cssStyleCache[styleKey]; + if (!className) { + className = scope.zrId + '-cls-' + getClassId(); + scope.cssStyleCache[styleKey] = className; + scope.cssNodes['.' + className + (withHover ? ':hover' : '')] = style; + } + attrs["class"] = attrs["class"] ? (attrs["class"] + ' ' + className) : className; + } + + var round$2 = Math.round; + function isImageLike$1(val) { + return val && isString(val.src); + } + function isCanvasLike(val) { + return val && isFunction(val.toDataURL); + } + function setStyleAttrs(attrs, style, el, scope) { + mapStyleToAttrs(function (key, val) { + var isFillStroke = key === 'fill' || key === 'stroke'; + if (isFillStroke && isGradient(val)) { + setGradient(style, attrs, key, scope); + } + else if (isFillStroke && isPattern(val)) { + setPattern(el, attrs, key, scope); + } + else if (isFillStroke && val === 'none') { + attrs[key] = 'transparent'; + } + else { + attrs[key] = val; + } + }, style, el, false); + setShadow(el, attrs, scope); + } + function setMetaData(attrs, el) { + var metaData = getElementSSRData(el); + if (metaData) { + metaData.each(function (val, key) { + val != null && (attrs[(META_DATA_PREFIX + key).toLowerCase()] = val + ''); + }); + if (el.isSilent()) { + attrs[META_DATA_PREFIX + 'silent'] = 'true'; + } + } + } + function noRotateScale(m) { + return isAroundZero$1(m[0] - 1) + && isAroundZero$1(m[1]) + && isAroundZero$1(m[2]) + && isAroundZero$1(m[3] - 1); + } + function noTranslate(m) { + return isAroundZero$1(m[4]) && isAroundZero$1(m[5]); + } + function setTransform(attrs, m, compress) { + if (m && !(noTranslate(m) && noRotateScale(m))) { + var mul = compress ? 10 : 1e4; + attrs.transform = noRotateScale(m) + ? "translate(" + round$2(m[4] * mul) / mul + " " + round$2(m[5] * mul) / mul + ")" : getMatrixStr(m); + } + } + function convertPolyShape(shape, attrs, mul) { + var points = shape.points; + var strArr = []; + for (var i = 0; i < points.length; i++) { + strArr.push(round$2(points[i][0] * mul) / mul); + strArr.push(round$2(points[i][1] * mul) / mul); + } + attrs.points = strArr.join(' '); + } + function validatePolyShape(shape) { + return !shape.smooth; + } + function createAttrsConvert(desc) { + var normalizedDesc = map(desc, function (item) { + return (typeof item === 'string' ? [item, item] : item); + }); + return function (shape, attrs, mul) { + for (var i = 0; i < normalizedDesc.length; i++) { + var item = normalizedDesc[i]; + var val = shape[item[0]]; + if (val != null) { + attrs[item[1]] = round$2(val * mul) / mul; + } + } + }; + } + var builtinShapesDef = { + circle: [createAttrsConvert(['cx', 'cy', 'r'])], + polyline: [convertPolyShape, validatePolyShape], + polygon: [convertPolyShape, validatePolyShape] + }; + function hasShapeAnimation(el) { + var animators = el.animators; + for (var i = 0; i < animators.length; i++) { + if (animators[i].targetName === 'shape') { + return true; + } + } + return false; + } + function brushSVGPath(el, scope) { + var style = el.style; + var shape = el.shape; + var builtinShpDef = builtinShapesDef[el.type]; + var attrs = {}; + var needsAnimate = scope.animation; + var svgElType = 'path'; + var strokePercent = el.style.strokePercent; + var precision = (scope.compress && getPathPrecision(el)) || 4; + if (builtinShpDef + && !scope.willUpdate + && !(builtinShpDef[1] && !builtinShpDef[1](shape)) + && !(needsAnimate && hasShapeAnimation(el)) + && !(strokePercent < 1)) { + svgElType = el.type; + var mul = Math.pow(10, precision); + builtinShpDef[0](shape, attrs, mul); + } + else { + var needBuildPath = !el.path || el.shapeChanged(); + if (!el.path) { + el.createPathProxy(); + } + var path = el.path; + if (needBuildPath) { + path.beginPath(); + el.buildPath(path, el.shape); + el.pathUpdated(); + } + var pathVersion = path.getVersion(); + var elExt = el; + var svgPathBuilder = elExt.__svgPathBuilder; + if (elExt.__svgPathVersion !== pathVersion + || !svgPathBuilder + || strokePercent !== elExt.__svgPathStrokePercent) { + if (!svgPathBuilder) { + svgPathBuilder = elExt.__svgPathBuilder = new SVGPathRebuilder(); + } + svgPathBuilder.reset(precision); + path.rebuildPath(svgPathBuilder, strokePercent); + svgPathBuilder.generateStr(); + elExt.__svgPathVersion = pathVersion; + elExt.__svgPathStrokePercent = strokePercent; + } + attrs.d = svgPathBuilder.getStr(); + } + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el, scope); + setMetaData(attrs, el); + scope.animation && createCSSAnimation(el, attrs, scope); + scope.emphasis && createCSSEmphasis(el, attrs, scope); + return createVNode(svgElType, el.id + '', attrs); + } + function brushSVGImage(el, scope) { + var style = el.style; + var image = style.image; + if (image && !isString(image)) { + if (isImageLike$1(image)) { + image = image.src; + } + else if (isCanvasLike(image)) { + image = image.toDataURL(); + } + } + if (!image) { + return; + } + var x = style.x || 0; + var y = style.y || 0; + var dw = style.width; + var dh = style.height; + var attrs = { + href: image, + width: dw, + height: dh + }; + if (x) { + attrs.x = x; + } + if (y) { + attrs.y = y; + } + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el, scope); + setMetaData(attrs, el); + scope.animation && createCSSAnimation(el, attrs, scope); + return createVNode('image', el.id + '', attrs); + } + function brushSVGTSpan(el, scope) { + var style = el.style; + var text = style.text; + text != null && (text += ''); + if (!text || isNaN(style.x) || isNaN(style.y)) { + return; + } + var font = style.font || DEFAULT_FONT; + var x = style.x || 0; + var y = adjustTextY(style.y || 0, getLineHeight(font), style.textBaseline); + var textAlign = TEXT_ALIGN_TO_ANCHOR[style.textAlign] + || style.textAlign; + var attrs = { + 'dominant-baseline': 'central', + 'text-anchor': textAlign + }; + if (hasSeparateFont(style)) { + var separatedFontStr = ''; + var fontStyle = style.fontStyle; + var fontSize = parseFontSize(style.fontSize); + if (!parseFloat(fontSize)) { + return; + } + var fontFamily = style.fontFamily || DEFAULT_FONT_FAMILY; + var fontWeight = style.fontWeight; + separatedFontStr += "font-size:" + fontSize + ";font-family:" + fontFamily + ";"; + if (fontStyle && fontStyle !== 'normal') { + separatedFontStr += "font-style:" + fontStyle + ";"; + } + if (fontWeight && fontWeight !== 'normal') { + separatedFontStr += "font-weight:" + fontWeight + ";"; + } + attrs.style = separatedFontStr; + } + else { + attrs.style = "font: " + font; + } + if (text.match(/\s/)) { + attrs['xml:space'] = 'preserve'; + } + if (x) { + attrs.x = x; + } + if (y) { + attrs.y = y; + } + setTransform(attrs, el.transform); + setStyleAttrs(attrs, style, el, scope); + setMetaData(attrs, el); + scope.animation && createCSSAnimation(el, attrs, scope); + return createVNode('text', el.id + '', attrs, undefined, text); + } + function brush$1(el, scope) { + if (el instanceof Path) { + return brushSVGPath(el, scope); + } + else if (el instanceof ZRImage) { + return brushSVGImage(el, scope); + } + else if (el instanceof TSpan) { + return brushSVGTSpan(el, scope); + } + } + function setShadow(el, attrs, scope) { + var style = el.style; + if (hasShadow(style)) { + var shadowKey = getShadowKey(el); + var shadowCache = scope.shadowCache; + var shadowId = shadowCache[shadowKey]; + if (!shadowId) { + var globalScale = el.getGlobalScale(); + var scaleX = globalScale[0]; + var scaleY = globalScale[1]; + if (!scaleX || !scaleY) { + return; + } + var offsetX = style.shadowOffsetX || 0; + var offsetY = style.shadowOffsetY || 0; + var blur_1 = style.shadowBlur; + var _a = normalizeColor(style.shadowColor), opacity = _a.opacity, color = _a.color; + var stdDx = blur_1 / 2 / scaleX; + var stdDy = blur_1 / 2 / scaleY; + var stdDeviation = stdDx + ' ' + stdDy; + shadowId = scope.zrId + '-s' + scope.shadowIdx++; + scope.defs[shadowId] = createVNode('filter', shadowId, { + 'id': shadowId, + 'x': '-100%', + 'y': '-100%', + 'width': '300%', + 'height': '300%' + }, [ + createVNode('feDropShadow', '', { + 'dx': offsetX / scaleX, + 'dy': offsetY / scaleY, + 'stdDeviation': stdDeviation, + 'flood-color': color, + 'flood-opacity': opacity + }) + ]); + shadowCache[shadowKey] = shadowId; + } + attrs.filter = getIdURL(shadowId); + } + } + function setGradient(style, attrs, target, scope) { + var val = style[target]; + var gradientTag; + var gradientAttrs = { + 'gradientUnits': val.global + ? 'userSpaceOnUse' + : 'objectBoundingBox' + }; + if (isLinearGradient(val)) { + gradientTag = 'linearGradient'; + gradientAttrs.x1 = val.x; + gradientAttrs.y1 = val.y; + gradientAttrs.x2 = val.x2; + gradientAttrs.y2 = val.y2; + } + else if (isRadialGradient(val)) { + gradientTag = 'radialGradient'; + gradientAttrs.cx = retrieve2(val.x, 0.5); + gradientAttrs.cy = retrieve2(val.y, 0.5); + gradientAttrs.r = retrieve2(val.r, 0.5); + } + else { + if ("development" !== 'production') { + logError('Illegal gradient type.'); + } + return; + } + var colors = val.colorStops; + var colorStops = []; + for (var i = 0, len = colors.length; i < len; ++i) { + var offset = round4(colors[i].offset) * 100 + '%'; + var stopColor = colors[i].color; + var _a = normalizeColor(stopColor), color = _a.color, opacity = _a.opacity; + var stopsAttrs = { + 'offset': offset + }; + stopsAttrs['stop-color'] = color; + if (opacity < 1) { + stopsAttrs['stop-opacity'] = opacity; + } + colorStops.push(createVNode('stop', i + '', stopsAttrs)); + } + var gradientVNode = createVNode(gradientTag, '', gradientAttrs, colorStops); + var gradientKey = vNodeToString(gradientVNode); + var gradientCache = scope.gradientCache; + var gradientId = gradientCache[gradientKey]; + if (!gradientId) { + gradientId = scope.zrId + '-g' + scope.gradientIdx++; + gradientCache[gradientKey] = gradientId; + gradientAttrs.id = gradientId; + scope.defs[gradientId] = createVNode(gradientTag, gradientId, gradientAttrs, colorStops); + } + attrs[target] = getIdURL(gradientId); + } + function setPattern(el, attrs, target, scope) { + var val = el.style[target]; + var boundingRect = el.getBoundingRect(); + var patternAttrs = {}; + var repeat = val.repeat; + var noRepeat = repeat === 'no-repeat'; + var repeatX = repeat === 'repeat-x'; + var repeatY = repeat === 'repeat-y'; + var child; + if (isImagePattern(val)) { + var imageWidth_1 = val.imageWidth; + var imageHeight_1 = val.imageHeight; + var imageSrc = void 0; + var patternImage = val.image; + if (isString(patternImage)) { + imageSrc = patternImage; + } + else if (isImageLike$1(patternImage)) { + imageSrc = patternImage.src; + } + else if (isCanvasLike(patternImage)) { + imageSrc = patternImage.toDataURL(); + } + if (typeof Image === 'undefined') { + var errMsg = 'Image width/height must been given explictly in svg-ssr renderer.'; + assert(imageWidth_1, errMsg); + assert(imageHeight_1, errMsg); + } + else if (imageWidth_1 == null || imageHeight_1 == null) { + var setSizeToVNode_1 = function (vNode, img) { + if (vNode) { + var svgEl = vNode.elm; + var width = imageWidth_1 || img.width; + var height = imageHeight_1 || img.height; + if (vNode.tag === 'pattern') { + if (repeatX) { + height = 1; + width /= boundingRect.width; + } + else if (repeatY) { + width = 1; + height /= boundingRect.height; + } + } + vNode.attrs.width = width; + vNode.attrs.height = height; + if (svgEl) { + svgEl.setAttribute('width', width); + svgEl.setAttribute('height', height); + } + } + }; + var createdImage = createOrUpdateImage(imageSrc, null, el, function (img) { + noRepeat || setSizeToVNode_1(patternVNode, img); + setSizeToVNode_1(child, img); + }); + if (createdImage && createdImage.width && createdImage.height) { + imageWidth_1 = imageWidth_1 || createdImage.width; + imageHeight_1 = imageHeight_1 || createdImage.height; + } + } + child = createVNode('image', 'img', { + href: imageSrc, + width: imageWidth_1, + height: imageHeight_1 + }); + patternAttrs.width = imageWidth_1; + patternAttrs.height = imageHeight_1; + } + else if (val.svgElement) { + child = clone(val.svgElement); + patternAttrs.width = val.svgWidth; + patternAttrs.height = val.svgHeight; + } + if (!child) { + return; + } + var patternWidth; + var patternHeight; + if (noRepeat) { + patternWidth = patternHeight = 1; + } + else if (repeatX) { + patternHeight = 1; + patternWidth = patternAttrs.width / boundingRect.width; + } + else if (repeatY) { + patternWidth = 1; + patternHeight = patternAttrs.height / boundingRect.height; + } + else { + patternAttrs.patternUnits = 'userSpaceOnUse'; + } + if (patternWidth != null && !isNaN(patternWidth)) { + patternAttrs.width = patternWidth; + } + if (patternHeight != null && !isNaN(patternHeight)) { + patternAttrs.height = patternHeight; + } + var patternTransform = getSRTTransformString(val); + patternTransform && (patternAttrs.patternTransform = patternTransform); + var patternVNode = createVNode('pattern', '', patternAttrs, [child]); + var patternKey = vNodeToString(patternVNode); + var patternCache = scope.patternCache; + var patternId = patternCache[patternKey]; + if (!patternId) { + patternId = scope.zrId + '-p' + scope.patternIdx++; + patternCache[patternKey] = patternId; + patternAttrs.id = patternId; + patternVNode = scope.defs[patternId] = createVNode('pattern', patternId, patternAttrs, [child]); + } + attrs[target] = getIdURL(patternId); + } + function setClipPath(clipPath, attrs, scope) { + var clipPathCache = scope.clipPathCache, defs = scope.defs; + var clipPathId = clipPathCache[clipPath.id]; + if (!clipPathId) { + clipPathId = scope.zrId + '-c' + scope.clipPathIdx++; + var clipPathAttrs = { + id: clipPathId + }; + clipPathCache[clipPath.id] = clipPathId; + defs[clipPathId] = createVNode('clipPath', clipPathId, clipPathAttrs, [brushSVGPath(clipPath, scope)]); + } + attrs['clip-path'] = getIdURL(clipPathId); + } + + function createTextNode(text) { + return document.createTextNode(text); + } + function insertBefore(parentNode, newNode, referenceNode) { + parentNode.insertBefore(newNode, referenceNode); + } + function removeChild(node, child) { + node.removeChild(child); + } + function appendChild(node, child) { + node.appendChild(child); + } + function parentNode(node) { + return node.parentNode; + } + function nextSibling(node) { + return node.nextSibling; + } + function setTextContent(node, text) { + node.textContent = text; + } + + var colonChar = 58; + var xChar = 120; + var emptyNode = createVNode('', ''); + function isUndef(s) { + return s === undefined; + } + function isDef(s) { + return s !== undefined; + } + function createKeyToOldIdx(children, beginIdx, endIdx) { + var map = {}; + for (var i = beginIdx; i <= endIdx; ++i) { + var key = children[i].key; + if (key !== undefined) { + if ("development" !== 'production') { + if (map[key] != null) { + console.error("Duplicate key " + key); + } + } + map[key] = i; + } + } + return map; + } + function sameVnode(vnode1, vnode2) { + var isSameKey = vnode1.key === vnode2.key; + var isSameTag = vnode1.tag === vnode2.tag; + return isSameTag && isSameKey; + } + function createElm(vnode) { + var i; + var children = vnode.children; + var tag = vnode.tag; + if (isDef(tag)) { + var elm = (vnode.elm = createElement(tag)); + updateAttrs(emptyNode, vnode); + if (isArray(children)) { + for (i = 0; i < children.length; ++i) { + var ch = children[i]; + if (ch != null) { + appendChild(elm, createElm(ch)); + } + } + } + else if (isDef(vnode.text) && !isObject(vnode.text)) { + appendChild(elm, createTextNode(vnode.text)); + } + } + else { + vnode.elm = createTextNode(vnode.text); + } + return vnode.elm; + } + function addVnodes(parentElm, before, vnodes, startIdx, endIdx) { + for (; startIdx <= endIdx; ++startIdx) { + var ch = vnodes[startIdx]; + if (ch != null) { + insertBefore(parentElm, createElm(ch), before); + } + } + } + function removeVnodes(parentElm, vnodes, startIdx, endIdx) { + for (; startIdx <= endIdx; ++startIdx) { + var ch = vnodes[startIdx]; + if (ch != null) { + if (isDef(ch.tag)) { + var parent_1 = parentNode(ch.elm); + removeChild(parent_1, ch.elm); + } + else { + removeChild(parentElm, ch.elm); + } + } + } + } + function updateAttrs(oldVnode, vnode) { + var key; + var elm = vnode.elm; + var oldAttrs = oldVnode && oldVnode.attrs || {}; + var attrs = vnode.attrs || {}; + if (oldAttrs === attrs) { + return; + } + for (key in attrs) { + var cur = attrs[key]; + var old = oldAttrs[key]; + if (old !== cur) { + if (cur === true) { + elm.setAttribute(key, ''); + } + else if (cur === false) { + elm.removeAttribute(key); + } + else { + if (key === 'style') { + elm.style.cssText = cur; + } + else if (key.charCodeAt(0) !== xChar) { + elm.setAttribute(key, cur); + } + else if (key === 'xmlns:xlink' || key === 'xmlns') { + elm.setAttributeNS(XMLNS, key, cur); + } + else if (key.charCodeAt(3) === colonChar) { + elm.setAttributeNS(XML_NAMESPACE, key, cur); + } + else if (key.charCodeAt(5) === colonChar) { + elm.setAttributeNS(XLINKNS, key, cur); + } + else { + elm.setAttribute(key, cur); + } + } + } + } + for (key in oldAttrs) { + if (!(key in attrs)) { + elm.removeAttribute(key); + } + } + } + function updateChildren(parentElm, oldCh, newCh) { + var oldStartIdx = 0; + var newStartIdx = 0; + var oldEndIdx = oldCh.length - 1; + var oldStartVnode = oldCh[0]; + var oldEndVnode = oldCh[oldEndIdx]; + var newEndIdx = newCh.length - 1; + var newStartVnode = newCh[0]; + var newEndVnode = newCh[newEndIdx]; + var oldKeyToIdx; + var idxInOld; + var elmToMove; + var before; + while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) { + if (oldStartVnode == null) { + oldStartVnode = oldCh[++oldStartIdx]; + } + else if (oldEndVnode == null) { + oldEndVnode = oldCh[--oldEndIdx]; + } + else if (newStartVnode == null) { + newStartVnode = newCh[++newStartIdx]; + } + else if (newEndVnode == null) { + newEndVnode = newCh[--newEndIdx]; + } + else if (sameVnode(oldStartVnode, newStartVnode)) { + patchVnode(oldStartVnode, newStartVnode); + oldStartVnode = oldCh[++oldStartIdx]; + newStartVnode = newCh[++newStartIdx]; + } + else if (sameVnode(oldEndVnode, newEndVnode)) { + patchVnode(oldEndVnode, newEndVnode); + oldEndVnode = oldCh[--oldEndIdx]; + newEndVnode = newCh[--newEndIdx]; + } + else if (sameVnode(oldStartVnode, newEndVnode)) { + patchVnode(oldStartVnode, newEndVnode); + insertBefore(parentElm, oldStartVnode.elm, nextSibling(oldEndVnode.elm)); + oldStartVnode = oldCh[++oldStartIdx]; + newEndVnode = newCh[--newEndIdx]; + } + else if (sameVnode(oldEndVnode, newStartVnode)) { + patchVnode(oldEndVnode, newStartVnode); + insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm); + oldEndVnode = oldCh[--oldEndIdx]; + newStartVnode = newCh[++newStartIdx]; + } + else { + if (isUndef(oldKeyToIdx)) { + oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); + } + idxInOld = oldKeyToIdx[newStartVnode.key]; + if (isUndef(idxInOld)) { + insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm); + } + else { + elmToMove = oldCh[idxInOld]; + if (elmToMove.tag !== newStartVnode.tag) { + insertBefore(parentElm, createElm(newStartVnode), oldStartVnode.elm); + } + else { + patchVnode(elmToMove, newStartVnode); + oldCh[idxInOld] = undefined; + insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm); + } + } + newStartVnode = newCh[++newStartIdx]; + } + } + if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) { + if (oldStartIdx > oldEndIdx) { + before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm; + addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx); + } + else { + removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); + } + } + } + function patchVnode(oldVnode, vnode) { + var elm = (vnode.elm = oldVnode.elm); + var oldCh = oldVnode.children; + var ch = vnode.children; + if (oldVnode === vnode) { + return; + } + updateAttrs(oldVnode, vnode); + if (isUndef(vnode.text)) { + if (isDef(oldCh) && isDef(ch)) { + if (oldCh !== ch) { + updateChildren(elm, oldCh, ch); + } + } + else if (isDef(ch)) { + if (isDef(oldVnode.text)) { + setTextContent(elm, ''); + } + addVnodes(elm, null, ch, 0, ch.length - 1); + } + else if (isDef(oldCh)) { + removeVnodes(elm, oldCh, 0, oldCh.length - 1); + } + else if (isDef(oldVnode.text)) { + setTextContent(elm, ''); + } + } + else if (oldVnode.text !== vnode.text) { + if (isDef(oldCh)) { + removeVnodes(elm, oldCh, 0, oldCh.length - 1); + } + setTextContent(elm, vnode.text); + } + } + function patch(oldVnode, vnode) { + if (sameVnode(oldVnode, vnode)) { + patchVnode(oldVnode, vnode); + } + else { + var elm = oldVnode.elm; + var parent_2 = parentNode(elm); + createElm(vnode); + if (parent_2 !== null) { + insertBefore(parent_2, vnode.elm, nextSibling(elm)); + removeVnodes(parent_2, [oldVnode], 0, 0); + } + } + return vnode; + } + + var svgId = 0; + var SVGPainter = (function () { + function SVGPainter(root, storage, opts) { + this.type = 'svg'; + this.refreshHover = createMethodNotSupport('refreshHover'); + this.configLayer = createMethodNotSupport('configLayer'); + this.storage = storage; + this._opts = opts = extend({}, opts); + this.root = root; + this._id = 'zr' + svgId++; + this._oldVNode = createSVGVNode(opts.width, opts.height); + if (root && !opts.ssr) { + var viewport = this._viewport = document.createElement('div'); + viewport.style.cssText = 'position:relative;overflow:hidden'; + var svgDom = this._svgDom = this._oldVNode.elm = createElement('svg'); + updateAttrs(null, this._oldVNode); + viewport.appendChild(svgDom); + root.appendChild(viewport); + } + this.resize(opts.width, opts.height); + } + SVGPainter.prototype.getType = function () { + return this.type; + }; + SVGPainter.prototype.getViewportRoot = function () { + return this._viewport; + }; + SVGPainter.prototype.getViewportRootOffset = function () { + var viewportRoot = this.getViewportRoot(); + if (viewportRoot) { + return { + offsetLeft: viewportRoot.offsetLeft || 0, + offsetTop: viewportRoot.offsetTop || 0 + }; + } + }; + SVGPainter.prototype.getSvgDom = function () { + return this._svgDom; + }; + SVGPainter.prototype.refresh = function () { + if (this.root) { + var vnode = this.renderToVNode({ + willUpdate: true + }); + vnode.attrs.style = 'position:absolute;left:0;top:0;user-select:none'; + patch(this._oldVNode, vnode); + this._oldVNode = vnode; + } + }; + SVGPainter.prototype.renderOneToVNode = function (el) { + return brush$1(el, createBrushScope(this._id)); + }; + SVGPainter.prototype.renderToVNode = function (opts) { + opts = opts || {}; + var list = this.storage.getDisplayList(true); + var width = this._width; + var height = this._height; + var scope = createBrushScope(this._id); + scope.animation = opts.animation; + scope.willUpdate = opts.willUpdate; + scope.compress = opts.compress; + scope.emphasis = opts.emphasis; + var children = []; + var bgVNode = this._bgVNode = createBackgroundVNode(width, height, this._backgroundColor, scope); + bgVNode && children.push(bgVNode); + var mainVNode = !opts.compress + ? (this._mainVNode = createVNode('g', 'main', {}, [])) : null; + this._paintList(list, scope, mainVNode ? mainVNode.children : children); + mainVNode && children.push(mainVNode); + var defs = map(keys(scope.defs), function (id) { return scope.defs[id]; }); + if (defs.length) { + children.push(createVNode('defs', 'defs', {}, defs)); + } + if (opts.animation) { + var animationCssStr = getCssString(scope.cssNodes, scope.cssAnims, { newline: true }); + if (animationCssStr) { + var styleNode = createVNode('style', 'stl', {}, [], animationCssStr); + children.push(styleNode); + } + } + return createSVGVNode(width, height, children, opts.useViewBox); + }; + SVGPainter.prototype.renderToString = function (opts) { + opts = opts || {}; + return vNodeToString(this.renderToVNode({ + animation: retrieve2(opts.cssAnimation, true), + emphasis: retrieve2(opts.cssEmphasis, true), + willUpdate: false, + compress: true, + useViewBox: retrieve2(opts.useViewBox, true) + }), { newline: true }); + }; + SVGPainter.prototype.setBackgroundColor = function (backgroundColor) { + this._backgroundColor = backgroundColor; + }; + SVGPainter.prototype.getSvgRoot = function () { + return this._mainVNode && this._mainVNode.elm; + }; + SVGPainter.prototype._paintList = function (list, scope, out) { + var listLen = list.length; + var clipPathsGroupsStack = []; + var clipPathsGroupsStackDepth = 0; + var currentClipPathGroup; + var prevClipPaths; + var clipGroupNodeIdx = 0; + for (var i = 0; i < listLen; i++) { + var displayable = list[i]; + if (!displayable.invisible) { + var clipPaths = displayable.__clipPaths; + var len = clipPaths && clipPaths.length || 0; + var prevLen = prevClipPaths && prevClipPaths.length || 0; + var lca = void 0; + for (lca = Math.max(len - 1, prevLen - 1); lca >= 0; lca--) { + if (clipPaths && prevClipPaths + && clipPaths[lca] === prevClipPaths[lca]) { + break; + } + } + for (var i_1 = prevLen - 1; i_1 > lca; i_1--) { + clipPathsGroupsStackDepth--; + currentClipPathGroup = clipPathsGroupsStack[clipPathsGroupsStackDepth - 1]; + } + for (var i_2 = lca + 1; i_2 < len; i_2++) { + var groupAttrs = {}; + setClipPath(clipPaths[i_2], groupAttrs, scope); + var g = createVNode('g', 'clip-g-' + clipGroupNodeIdx++, groupAttrs, []); + (currentClipPathGroup ? currentClipPathGroup.children : out).push(g); + clipPathsGroupsStack[clipPathsGroupsStackDepth++] = g; + currentClipPathGroup = g; + } + prevClipPaths = clipPaths; + var ret = brush$1(displayable, scope); + if (ret) { + (currentClipPathGroup ? currentClipPathGroup.children : out).push(ret); + } + } + } + }; + SVGPainter.prototype.resize = function (width, height) { + var opts = this._opts; + var root = this.root; + var viewport = this._viewport; + width != null && (opts.width = width); + height != null && (opts.height = height); + if (root && viewport) { + viewport.style.display = 'none'; + width = getSize(root, 0, opts); + height = getSize(root, 1, opts); + viewport.style.display = ''; + } + if (this._width !== width || this._height !== height) { + this._width = width; + this._height = height; + if (viewport) { + var viewportStyle = viewport.style; + viewportStyle.width = width + 'px'; + viewportStyle.height = height + 'px'; + } + if (!isPattern(this._backgroundColor)) { + var svgDom = this._svgDom; + if (svgDom) { + svgDom.setAttribute('width', width); + svgDom.setAttribute('height', height); + } + var bgEl = this._bgVNode && this._bgVNode.elm; + if (bgEl) { + bgEl.setAttribute('width', width); + bgEl.setAttribute('height', height); + } + } + else { + this.refresh(); + } + } + }; + SVGPainter.prototype.getWidth = function () { + return this._width; + }; + SVGPainter.prototype.getHeight = function () { + return this._height; + }; + SVGPainter.prototype.dispose = function () { + if (this.root) { + this.root.innerHTML = ''; + } + this._svgDom = + this._viewport = + this.storage = + this._oldVNode = + this._bgVNode = + this._mainVNode = null; + }; + SVGPainter.prototype.clear = function () { + if (this._svgDom) { + this._svgDom.innerHTML = null; + } + this._oldVNode = null; + }; + SVGPainter.prototype.toDataURL = function (base64) { + var str = this.renderToString(); + var prefix = 'data:image/svg+xml;'; + if (base64) { + str = encodeBase64(str); + return str && prefix + 'base64,' + str; + } + return prefix + 'charset=UTF-8,' + encodeURIComponent(str); + }; + return SVGPainter; + }()); + function createMethodNotSupport(method) { + return function () { + if ("development" !== 'production') { + logError('In SVG mode painter not support method "' + method + '"'); + } + }; + } + function createBackgroundVNode(width, height, backgroundColor, scope) { + var bgVNode; + if (backgroundColor && backgroundColor !== 'none') { + bgVNode = createVNode('rect', 'bg', { + width: width, + height: height, + x: '0', + y: '0' + }); + if (isGradient(backgroundColor)) { + setGradient({ fill: backgroundColor }, bgVNode.attrs, 'fill', scope); + } + else if (isPattern(backgroundColor)) { + setPattern({ + style: { + fill: backgroundColor + }, + dirty: noop, + getBoundingRect: function () { return ({ width: width, height: height }); } + }, bgVNode.attrs, 'fill', scope); + } + else { + var _a = normalizeColor(backgroundColor), color = _a.color, opacity = _a.opacity; + bgVNode.attrs.fill = color; + opacity < 1 && (bgVNode.attrs['fill-opacity'] = opacity); + } + } + return bgVNode; + } + + function install(registers) { + registers.registerPainter('svg', SVGPainter); + } + + function createDom(id, painter, dpr) { + var newDom = platformApi.createCanvas(); + var width = painter.getWidth(); + var height = painter.getHeight(); + var newDomStyle = newDom.style; + if (newDomStyle) { + newDomStyle.position = 'absolute'; + newDomStyle.left = '0'; + newDomStyle.top = '0'; + newDomStyle.width = width + 'px'; + newDomStyle.height = height + 'px'; + newDom.setAttribute('data-zr-dom-id', id); + } + newDom.width = width * dpr; + newDom.height = height * dpr; + return newDom; + } + var Layer = (function (_super) { + __extends(Layer, _super); + function Layer(id, painter, dpr) { + var _this = _super.call(this) || this; + _this.motionBlur = false; + _this.lastFrameAlpha = 0.7; + _this.dpr = 1; + _this.virtual = false; + _this.config = {}; + _this.incremental = false; + _this.zlevel = 0; + _this.maxRepaintRectCount = 5; + _this.__dirty = true; + _this.__firstTimePaint = true; + _this.__used = false; + _this.__drawIndex = 0; + _this.__startIndex = 0; + _this.__endIndex = 0; + _this.__prevStartIndex = null; + _this.__prevEndIndex = null; + var dom; + dpr = dpr || devicePixelRatio; + if (typeof id === 'string') { + dom = createDom(id, painter, dpr); + } + else if (isObject(id)) { + dom = id; + id = dom.id; + } + _this.id = id; + _this.dom = dom; + var domStyle = dom.style; + if (domStyle) { + disableUserSelect(dom); + dom.onselectstart = function () { return false; }; + domStyle.padding = '0'; + domStyle.margin = '0'; + domStyle.borderWidth = '0'; + } + _this.painter = painter; + _this.dpr = dpr; + return _this; + } + Layer.prototype.getElementCount = function () { + return this.__endIndex - this.__startIndex; + }; + Layer.prototype.afterBrush = function () { + this.__prevStartIndex = this.__startIndex; + this.__prevEndIndex = this.__endIndex; + }; + Layer.prototype.initContext = function () { + this.ctx = this.dom.getContext('2d'); + this.ctx.dpr = this.dpr; + }; + Layer.prototype.setUnpainted = function () { + this.__firstTimePaint = true; + }; + Layer.prototype.createBackBuffer = function () { + var dpr = this.dpr; + this.domBack = createDom('back-' + this.id, this.painter, dpr); + this.ctxBack = this.domBack.getContext('2d'); + if (dpr !== 1) { + this.ctxBack.scale(dpr, dpr); + } + }; + Layer.prototype.createRepaintRects = function (displayList, prevList, viewWidth, viewHeight) { + if (this.__firstTimePaint) { + this.__firstTimePaint = false; + return null; + } + var mergedRepaintRects = []; + var maxRepaintRectCount = this.maxRepaintRectCount; + var full = false; + var pendingRect = new BoundingRect(0, 0, 0, 0); + function addRectToMergePool(rect) { + if (!rect.isFinite() || rect.isZero()) { + return; + } + if (mergedRepaintRects.length === 0) { + var boundingRect = new BoundingRect(0, 0, 0, 0); + boundingRect.copy(rect); + mergedRepaintRects.push(boundingRect); + } + else { + var isMerged = false; + var minDeltaArea = Infinity; + var bestRectToMergeIdx = 0; + for (var i = 0; i < mergedRepaintRects.length; ++i) { + var mergedRect = mergedRepaintRects[i]; + if (mergedRect.intersect(rect)) { + var pendingRect_1 = new BoundingRect(0, 0, 0, 0); + pendingRect_1.copy(mergedRect); + pendingRect_1.union(rect); + mergedRepaintRects[i] = pendingRect_1; + isMerged = true; + break; + } + else if (full) { + pendingRect.copy(rect); + pendingRect.union(mergedRect); + var aArea = rect.width * rect.height; + var bArea = mergedRect.width * mergedRect.height; + var pendingArea = pendingRect.width * pendingRect.height; + var deltaArea = pendingArea - aArea - bArea; + if (deltaArea < minDeltaArea) { + minDeltaArea = deltaArea; + bestRectToMergeIdx = i; + } + } + } + if (full) { + mergedRepaintRects[bestRectToMergeIdx].union(rect); + isMerged = true; + } + if (!isMerged) { + var boundingRect = new BoundingRect(0, 0, 0, 0); + boundingRect.copy(rect); + mergedRepaintRects.push(boundingRect); + } + if (!full) { + full = mergedRepaintRects.length >= maxRepaintRectCount; + } + } + } + for (var i = this.__startIndex; i < this.__endIndex; ++i) { + var el = displayList[i]; + if (el) { + var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true); + var prevRect = el.__isRendered && ((el.__dirty & REDRAW_BIT) || !shouldPaint) + ? el.getPrevPaintRect() + : null; + if (prevRect) { + addRectToMergePool(prevRect); + } + var curRect = shouldPaint && ((el.__dirty & REDRAW_BIT) || !el.__isRendered) + ? el.getPaintRect() + : null; + if (curRect) { + addRectToMergePool(curRect); + } + } + } + for (var i = this.__prevStartIndex; i < this.__prevEndIndex; ++i) { + var el = prevList[i]; + var shouldPaint = el && el.shouldBePainted(viewWidth, viewHeight, true, true); + if (el && (!shouldPaint || !el.__zr) && el.__isRendered) { + var prevRect = el.getPrevPaintRect(); + if (prevRect) { + addRectToMergePool(prevRect); + } + } + } + var hasIntersections; + do { + hasIntersections = false; + for (var i = 0; i < mergedRepaintRects.length;) { + if (mergedRepaintRects[i].isZero()) { + mergedRepaintRects.splice(i, 1); + continue; + } + for (var j = i + 1; j < mergedRepaintRects.length;) { + if (mergedRepaintRects[i].intersect(mergedRepaintRects[j])) { + hasIntersections = true; + mergedRepaintRects[i].union(mergedRepaintRects[j]); + mergedRepaintRects.splice(j, 1); + } + else { + j++; + } + } + i++; + } + } while (hasIntersections); + this._paintRects = mergedRepaintRects; + return mergedRepaintRects; + }; + Layer.prototype.debugGetPaintRects = function () { + return (this._paintRects || []).slice(); + }; + Layer.prototype.resize = function (width, height) { + var dpr = this.dpr; + var dom = this.dom; + var domStyle = dom.style; + var domBack = this.domBack; + if (domStyle) { + domStyle.width = width + 'px'; + domStyle.height = height + 'px'; + } + dom.width = width * dpr; + dom.height = height * dpr; + if (domBack) { + domBack.width = width * dpr; + domBack.height = height * dpr; + if (dpr !== 1) { + this.ctxBack.scale(dpr, dpr); + } + } + }; + Layer.prototype.clear = function (clearAll, clearColor, repaintRects) { + var dom = this.dom; + var ctx = this.ctx; + var width = dom.width; + var height = dom.height; + clearColor = clearColor || this.clearColor; + var haveMotionBLur = this.motionBlur && !clearAll; + var lastFrameAlpha = this.lastFrameAlpha; + var dpr = this.dpr; + var self = this; + if (haveMotionBLur) { + if (!this.domBack) { + this.createBackBuffer(); + } + this.ctxBack.globalCompositeOperation = 'copy'; + this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr); + } + var domBack = this.domBack; + function doClear(x, y, width, height) { + ctx.clearRect(x, y, width, height); + if (clearColor && clearColor !== 'transparent') { + var clearColorGradientOrPattern = void 0; + if (isGradientObject(clearColor)) { + var shouldCache = clearColor.global || (clearColor.__width === width + && clearColor.__height === height); + clearColorGradientOrPattern = shouldCache + && clearColor.__canvasGradient + || getCanvasGradient(ctx, clearColor, { + x: 0, + y: 0, + width: width, + height: height + }); + clearColor.__canvasGradient = clearColorGradientOrPattern; + clearColor.__width = width; + clearColor.__height = height; + } + else if (isImagePatternObject(clearColor)) { + clearColor.scaleX = clearColor.scaleX || dpr; + clearColor.scaleY = clearColor.scaleY || dpr; + clearColorGradientOrPattern = createCanvasPattern(ctx, clearColor, { + dirty: function () { + self.setUnpainted(); + self.painter.refresh(); + } + }); + } + ctx.save(); + ctx.fillStyle = clearColorGradientOrPattern || clearColor; + ctx.fillRect(x, y, width, height); + ctx.restore(); + } + if (haveMotionBLur) { + ctx.save(); + ctx.globalAlpha = lastFrameAlpha; + ctx.drawImage(domBack, x, y, width, height); + ctx.restore(); + } + } + if (!repaintRects || haveMotionBLur) { + doClear(0, 0, width, height); + } + else if (repaintRects.length) { + each(repaintRects, function (rect) { + doClear(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr); + }); + } + }; + return Layer; + }(Eventful)); + + var HOVER_LAYER_ZLEVEL = 1e5; + var CANVAS_ZLEVEL = 314159; + var EL_AFTER_INCREMENTAL_INC = 0.01; + var INCREMENTAL_INC = 0.001; + function isLayerValid(layer) { + if (!layer) { + return false; + } + if (layer.__builtin__) { + return true; + } + if (typeof (layer.resize) !== 'function' + || typeof (layer.refresh) !== 'function') { + return false; + } + return true; + } + function createRoot(width, height) { + var domRoot = document.createElement('div'); + domRoot.style.cssText = [ + 'position:relative', + 'width:' + width + 'px', + 'height:' + height + 'px', + 'padding:0', + 'margin:0', + 'border-width:0' + ].join(';') + ';'; + return domRoot; + } + var CanvasPainter = (function () { + function CanvasPainter(root, storage, opts, id) { + this.type = 'canvas'; + this._zlevelList = []; + this._prevDisplayList = []; + this._layers = {}; + this._layerConfig = {}; + this._needsManuallyCompositing = false; + this.type = 'canvas'; + var singleCanvas = !root.nodeName + || root.nodeName.toUpperCase() === 'CANVAS'; + this._opts = opts = extend({}, opts || {}); + this.dpr = opts.devicePixelRatio || devicePixelRatio; + this._singleCanvas = singleCanvas; + this.root = root; + var rootStyle = root.style; + if (rootStyle) { + disableUserSelect(root); + root.innerHTML = ''; + } + this.storage = storage; + var zlevelList = this._zlevelList; + this._prevDisplayList = []; + var layers = this._layers; + if (!singleCanvas) { + this._width = getSize(root, 0, opts); + this._height = getSize(root, 1, opts); + var domRoot = this._domRoot = createRoot(this._width, this._height); + root.appendChild(domRoot); + } + else { + var rootCanvas = root; + var width = rootCanvas.width; + var height = rootCanvas.height; + if (opts.width != null) { + width = opts.width; + } + if (opts.height != null) { + height = opts.height; + } + this.dpr = opts.devicePixelRatio || 1; + rootCanvas.width = width * this.dpr; + rootCanvas.height = height * this.dpr; + this._width = width; + this._height = height; + var mainLayer = new Layer(rootCanvas, this, this.dpr); + mainLayer.__builtin__ = true; + mainLayer.initContext(); + layers[CANVAS_ZLEVEL] = mainLayer; + mainLayer.zlevel = CANVAS_ZLEVEL; + zlevelList.push(CANVAS_ZLEVEL); + this._domRoot = root; + } + } + CanvasPainter.prototype.getType = function () { + return 'canvas'; + }; + CanvasPainter.prototype.isSingleCanvas = function () { + return this._singleCanvas; + }; + CanvasPainter.prototype.getViewportRoot = function () { + return this._domRoot; + }; + CanvasPainter.prototype.getViewportRootOffset = function () { + var viewportRoot = this.getViewportRoot(); + if (viewportRoot) { + return { + offsetLeft: viewportRoot.offsetLeft || 0, + offsetTop: viewportRoot.offsetTop || 0 + }; + } + }; + CanvasPainter.prototype.refresh = function (paintAll) { + var list = this.storage.getDisplayList(true); + var prevList = this._prevDisplayList; + var zlevelList = this._zlevelList; + this._redrawId = Math.random(); + this._paintList(list, prevList, paintAll, this._redrawId); + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + var layer = this._layers[z]; + if (!layer.__builtin__ && layer.refresh) { + var clearColor = i === 0 ? this._backgroundColor : null; + layer.refresh(clearColor); + } + } + if (this._opts.useDirtyRect) { + this._prevDisplayList = list.slice(); + } + return this; + }; + CanvasPainter.prototype.refreshHover = function () { + this._paintHoverList(this.storage.getDisplayList(false)); + }; + CanvasPainter.prototype._paintHoverList = function (list) { + var len = list.length; + var hoverLayer = this._hoverlayer; + hoverLayer && hoverLayer.clear(); + if (!len) { + return; + } + var scope = { + inHover: true, + viewWidth: this._width, + viewHeight: this._height + }; + var ctx; + for (var i = 0; i < len; i++) { + var el = list[i]; + if (el.__inHover) { + if (!hoverLayer) { + hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL); + } + if (!ctx) { + ctx = hoverLayer.ctx; + ctx.save(); + } + brush(ctx, el, scope, i === len - 1); + } + } + if (ctx) { + ctx.restore(); + } + }; + CanvasPainter.prototype.getHoverLayer = function () { + return this.getLayer(HOVER_LAYER_ZLEVEL); + }; + CanvasPainter.prototype.paintOne = function (ctx, el) { + brushSingle(ctx, el); + }; + CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) { + if (this._redrawId !== redrawId) { + return; + } + paintAll = paintAll || false; + this._updateLayerStatus(list); + var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover; + if (this._needsManuallyCompositing) { + this._compositeManually(); + } + if (needsRefreshHover) { + this._paintHoverList(list); + } + if (!finished) { + var self_1 = this; + requestAnimationFrame$1(function () { + self_1._paintList(list, prevList, paintAll, redrawId); + }); + } + else { + this.eachLayer(function (layer) { + layer.afterBrush && layer.afterBrush(); + }); + } + }; + CanvasPainter.prototype._compositeManually = function () { + var ctx = this.getLayer(CANVAS_ZLEVEL).ctx; + var width = this._domRoot.width; + var height = this._domRoot.height; + ctx.clearRect(0, 0, width, height); + this.eachBuiltinLayer(function (layer) { + if (layer.virtual) { + ctx.drawImage(layer.dom, 0, 0, width, height); + } + }); + }; + CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) { + var _this = this; + var layerList = []; + var useDirtyRect = this._opts.useDirtyRect; + for (var zi = 0; zi < this._zlevelList.length; zi++) { + var zlevel = this._zlevelList[zi]; + var layer = this._layers[zlevel]; + if (layer.__builtin__ + && layer !== this._hoverlayer + && (layer.__dirty || paintAll)) { + layerList.push(layer); + } + } + var finished = true; + var needsRefreshHover = false; + var _loop_1 = function (k) { + var layer = layerList[k]; + var ctx = layer.ctx; + var repaintRects = useDirtyRect + && layer.createRepaintRects(list, prevList, this_1._width, this_1._height); + var start = paintAll ? layer.__startIndex : layer.__drawIndex; + var useTimer = !paintAll && layer.incremental && Date.now; + var startTime = useTimer && Date.now(); + var clearColor = layer.zlevel === this_1._zlevelList[0] + ? this_1._backgroundColor : null; + if (layer.__startIndex === layer.__endIndex) { + layer.clear(false, clearColor, repaintRects); + } + else if (start === layer.__startIndex) { + var firstEl = list[start]; + if (!firstEl.incremental || !firstEl.notClear || paintAll) { + layer.clear(false, clearColor, repaintRects); + } + } + if (start === -1) { + console.error('For some unknown reason. drawIndex is -1'); + start = layer.__startIndex; + } + var i; + var repaint = function (repaintRect) { + var scope = { + inHover: false, + allClipped: false, + prevEl: null, + viewWidth: _this._width, + viewHeight: _this._height + }; + for (i = start; i < layer.__endIndex; i++) { + var el = list[i]; + if (el.__inHover) { + needsRefreshHover = true; + } + _this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1); + if (useTimer) { + var dTime = Date.now() - startTime; + if (dTime > 15) { + break; + } + } + } + if (scope.prevElClipPaths) { + ctx.restore(); + } + }; + if (repaintRects) { + if (repaintRects.length === 0) { + i = layer.__endIndex; + } + else { + var dpr = this_1.dpr; + for (var r = 0; r < repaintRects.length; ++r) { + var rect = repaintRects[r]; + ctx.save(); + ctx.beginPath(); + ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr); + ctx.clip(); + repaint(rect); + ctx.restore(); + } + } + } + else { + ctx.save(); + repaint(); + ctx.restore(); + } + layer.__drawIndex = i; + if (layer.__drawIndex < layer.__endIndex) { + finished = false; + } + }; + var this_1 = this; + for (var k = 0; k < layerList.length; k++) { + _loop_1(k); + } + if (env.wxa) { + each(this._layers, function (layer) { + if (layer && layer.ctx && layer.ctx.draw) { + layer.ctx.draw(); + } + }); + } + return { + finished: finished, + needsRefreshHover: needsRefreshHover + }; + }; + CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) { + var ctx = currentLayer.ctx; + if (useDirtyRect) { + var paintRect = el.getPaintRect(); + if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) { + brush(ctx, el, scope, isLast); + el.setPrevPaintRect(paintRect); + } + } + else { + brush(ctx, el, scope, isLast); + } + }; + CanvasPainter.prototype.getLayer = function (zlevel, virtual) { + if (this._singleCanvas && !this._needsManuallyCompositing) { + zlevel = CANVAS_ZLEVEL; + } + var layer = this._layers[zlevel]; + if (!layer) { + layer = new Layer('zr_' + zlevel, this, this.dpr); + layer.zlevel = zlevel; + layer.__builtin__ = true; + if (this._layerConfig[zlevel]) { + merge(layer, this._layerConfig[zlevel], true); + } + else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) { + merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true); + } + if (virtual) { + layer.virtual = virtual; + } + this.insertLayer(zlevel, layer); + layer.initContext(); + } + return layer; + }; + CanvasPainter.prototype.insertLayer = function (zlevel, layer) { + var layersMap = this._layers; + var zlevelList = this._zlevelList; + var len = zlevelList.length; + var domRoot = this._domRoot; + var prevLayer = null; + var i = -1; + if (layersMap[zlevel]) { + if ("development" !== 'production') { + logError('ZLevel ' + zlevel + ' has been used already'); + } + return; + } + if (!isLayerValid(layer)) { + if ("development" !== 'production') { + logError('Layer of zlevel ' + zlevel + ' is not valid'); + } + return; + } + if (len > 0 && zlevel > zlevelList[0]) { + for (i = 0; i < len - 1; i++) { + if (zlevelList[i] < zlevel + && zlevelList[i + 1] > zlevel) { + break; + } + } + prevLayer = layersMap[zlevelList[i]]; + } + zlevelList.splice(i + 1, 0, zlevel); + layersMap[zlevel] = layer; + if (!layer.virtual) { + if (prevLayer) { + var prevDom = prevLayer.dom; + if (prevDom.nextSibling) { + domRoot.insertBefore(layer.dom, prevDom.nextSibling); + } + else { + domRoot.appendChild(layer.dom); + } + } + else { + if (domRoot.firstChild) { + domRoot.insertBefore(layer.dom, domRoot.firstChild); + } + else { + domRoot.appendChild(layer.dom); + } + } + } + layer.painter || (layer.painter = this); + }; + CanvasPainter.prototype.eachLayer = function (cb, context) { + var zlevelList = this._zlevelList; + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + cb.call(context, this._layers[z], z); + } + }; + CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) { + var zlevelList = this._zlevelList; + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + var layer = this._layers[z]; + if (layer.__builtin__) { + cb.call(context, layer, z); + } + } + }; + CanvasPainter.prototype.eachOtherLayer = function (cb, context) { + var zlevelList = this._zlevelList; + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + var layer = this._layers[z]; + if (!layer.__builtin__) { + cb.call(context, layer, z); + } + } + }; + CanvasPainter.prototype.getLayers = function () { + return this._layers; + }; + CanvasPainter.prototype._updateLayerStatus = function (list) { + this.eachBuiltinLayer(function (layer, z) { + layer.__dirty = layer.__used = false; + }); + function updatePrevLayer(idx) { + if (prevLayer) { + if (prevLayer.__endIndex !== idx) { + prevLayer.__dirty = true; + } + prevLayer.__endIndex = idx; + } + } + if (this._singleCanvas) { + for (var i_1 = 1; i_1 < list.length; i_1++) { + var el = list[i_1]; + if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) { + this._needsManuallyCompositing = true; + break; + } + } + } + var prevLayer = null; + var incrementalLayerCount = 0; + var prevZlevel; + var i; + for (i = 0; i < list.length; i++) { + var el = list[i]; + var zlevel = el.zlevel; + var layer = void 0; + if (prevZlevel !== zlevel) { + prevZlevel = zlevel; + incrementalLayerCount = 0; + } + if (el.incremental) { + layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing); + layer.incremental = true; + incrementalLayerCount = 1; + } + else { + layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing); + } + if (!layer.__builtin__) { + logError('ZLevel ' + zlevel + ' has been used by unkown layer ' + layer.id); + } + if (layer !== prevLayer) { + layer.__used = true; + if (layer.__startIndex !== i) { + layer.__dirty = true; + } + layer.__startIndex = i; + if (!layer.incremental) { + layer.__drawIndex = i; + } + else { + layer.__drawIndex = -1; + } + updatePrevLayer(i); + prevLayer = layer; + } + if ((el.__dirty & REDRAW_BIT) && !el.__inHover) { + layer.__dirty = true; + if (layer.incremental && layer.__drawIndex < 0) { + layer.__drawIndex = i; + } + } + } + updatePrevLayer(i); + this.eachBuiltinLayer(function (layer, z) { + if (!layer.__used && layer.getElementCount() > 0) { + layer.__dirty = true; + layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0; + } + if (layer.__dirty && layer.__drawIndex < 0) { + layer.__drawIndex = layer.__startIndex; + } + }); + }; + CanvasPainter.prototype.clear = function () { + this.eachBuiltinLayer(this._clearLayer); + return this; + }; + CanvasPainter.prototype._clearLayer = function (layer) { + layer.clear(); + }; + CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) { + this._backgroundColor = backgroundColor; + each(this._layers, function (layer) { + layer.setUnpainted(); + }); + }; + CanvasPainter.prototype.configLayer = function (zlevel, config) { + if (config) { + var layerConfig = this._layerConfig; + if (!layerConfig[zlevel]) { + layerConfig[zlevel] = config; + } + else { + merge(layerConfig[zlevel], config, true); + } + for (var i = 0; i < this._zlevelList.length; i++) { + var _zlevel = this._zlevelList[i]; + if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) { + var layer = this._layers[_zlevel]; + merge(layer, layerConfig[zlevel], true); + } + } + } + }; + CanvasPainter.prototype.delLayer = function (zlevel) { + var layers = this._layers; + var zlevelList = this._zlevelList; + var layer = layers[zlevel]; + if (!layer) { + return; + } + layer.dom.parentNode.removeChild(layer.dom); + delete layers[zlevel]; + zlevelList.splice(indexOf(zlevelList, zlevel), 1); + }; + CanvasPainter.prototype.resize = function (width, height) { + if (!this._domRoot.style) { + if (width == null || height == null) { + return; + } + this._width = width; + this._height = height; + this.getLayer(CANVAS_ZLEVEL).resize(width, height); + } + else { + var domRoot = this._domRoot; + domRoot.style.display = 'none'; + var opts = this._opts; + var root = this.root; + width != null && (opts.width = width); + height != null && (opts.height = height); + width = getSize(root, 0, opts); + height = getSize(root, 1, opts); + domRoot.style.display = ''; + if (this._width !== width || height !== this._height) { + domRoot.style.width = width + 'px'; + domRoot.style.height = height + 'px'; + for (var id in this._layers) { + if (this._layers.hasOwnProperty(id)) { + this._layers[id].resize(width, height); + } + } + this.refresh(true); + } + this._width = width; + this._height = height; + } + return this; + }; + CanvasPainter.prototype.clearLayer = function (zlevel) { + var layer = this._layers[zlevel]; + if (layer) { + layer.clear(); + } + }; + CanvasPainter.prototype.dispose = function () { + this.root.innerHTML = ''; + this.root = + this.storage = + this._domRoot = + this._layers = null; + }; + CanvasPainter.prototype.getRenderedCanvas = function (opts) { + opts = opts || {}; + if (this._singleCanvas && !this._compositeManually) { + return this._layers[CANVAS_ZLEVEL].dom; + } + var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); + imageLayer.initContext(); + imageLayer.clear(false, opts.backgroundColor || this._backgroundColor); + var ctx = imageLayer.ctx; + if (opts.pixelRatio <= this.dpr) { + this.refresh(); + var width_1 = imageLayer.dom.width; + var height_1 = imageLayer.dom.height; + this.eachLayer(function (layer) { + if (layer.__builtin__) { + ctx.drawImage(layer.dom, 0, 0, width_1, height_1); + } + else if (layer.renderToCanvas) { + ctx.save(); + layer.renderToCanvas(ctx); + ctx.restore(); + } + }); + } + else { + var scope = { + inHover: false, + viewWidth: this._width, + viewHeight: this._height + }; + var displayList = this.storage.getDisplayList(true); + for (var i = 0, len = displayList.length; i < len; i++) { + var el = displayList[i]; + brush(ctx, el, scope, i === len - 1); + } + } + return imageLayer.dom; + }; + CanvasPainter.prototype.getWidth = function () { + return this._width; + }; + CanvasPainter.prototype.getHeight = function () { + return this._height; + }; + return CanvasPainter; + }()); + + function install$1(registers) { + registers.registerPainter('canvas', CanvasPainter); + } + + var LineSeriesModel = /** @class */function (_super) { + __extends(LineSeriesModel, _super); + function LineSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = LineSeriesModel.type; + _this.hasSymbolVisual = true; + return _this; + } + LineSeriesModel.prototype.getInitialData = function (option) { + if ("development" !== 'production') { + var coordSys = option.coordinateSystem; + if (coordSys !== 'polar' && coordSys !== 'cartesian2d') { + throw new Error('Line not support coordinateSystem besides cartesian and polar'); + } + } + return createSeriesData(null, this, { + useEncodeDefaulter: true + }); + }; + LineSeriesModel.prototype.getLegendIcon = function (opt) { + var group = new Group(); + var line = createSymbol('line', 0, opt.itemHeight / 2, opt.itemWidth, 0, opt.lineStyle.stroke, false); + group.add(line); + line.setStyle(opt.lineStyle); + var visualType = this.getData().getVisual('symbol'); + var visualRotate = this.getData().getVisual('symbolRotate'); + var symbolType = visualType === 'none' ? 'circle' : visualType; + // Symbol size is 80% when there is a line + var size = opt.itemHeight * 0.8; + var symbol = createSymbol(symbolType, (opt.itemWidth - size) / 2, (opt.itemHeight - size) / 2, size, size, opt.itemStyle.fill); + group.add(symbol); + symbol.setStyle(opt.itemStyle); + var symbolRotate = opt.iconRotate === 'inherit' ? visualRotate : opt.iconRotate || 0; + symbol.rotation = symbolRotate * Math.PI / 180; + symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]); + if (symbolType.indexOf('empty') > -1) { + symbol.style.stroke = symbol.style.fill; + symbol.style.fill = '#fff'; + symbol.style.lineWidth = 2; + } + return group; + }; + LineSeriesModel.type = 'series.line'; + LineSeriesModel.dependencies = ['grid', 'polar']; + LineSeriesModel.defaultOption = { + // zlevel: 0, + z: 3, + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + clip: true, + label: { + position: 'top' + }, + // itemStyle: { + // }, + endLabel: { + show: false, + valueAnimation: true, + distance: 8 + }, + lineStyle: { + width: 2, + type: 'solid' + }, + emphasis: { + scale: true + }, + // areaStyle: { + // origin of areaStyle. Valid values: + // `'auto'/null/undefined`: from axisLine to data + // `'start'`: from min to data + // `'end'`: from data to max + // origin: 'auto' + // }, + // false, 'start', 'end', 'middle' + step: false, + // Disabled if step is true + smooth: false, + smoothMonotone: null, + symbol: 'emptyCircle', + symbolSize: 4, + symbolRotate: null, + showSymbol: true, + // `false`: follow the label interval strategy. + // `true`: show all symbols. + // `'auto'`: If possible, show all symbols, otherwise + // follow the label interval strategy. + showAllSymbol: 'auto', + // Whether to connect break point. + connectNulls: false, + // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'. + sampling: 'none', + animationEasing: 'linear', + // Disable progressive + progressive: 0, + hoverLayerThreshold: Infinity, + universalTransition: { + divideShape: 'clone' + }, + triggerLineEvent: false + }; + return LineSeriesModel; + }(SeriesModel); + + /** + * @return label string. Not null/undefined + */ + function getDefaultLabel(data, dataIndex) { + var labelDims = data.mapDimensionsAll('defaultedLabel'); + var len = labelDims.length; + // Simple optimization (in lots of cases, label dims length is 1) + if (len === 1) { + var rawVal = retrieveRawValue(data, dataIndex, labelDims[0]); + return rawVal != null ? rawVal + '' : null; + } else if (len) { + var vals = []; + for (var i = 0; i < labelDims.length; i++) { + vals.push(retrieveRawValue(data, dataIndex, labelDims[i])); + } + return vals.join(' '); + } + } + function getDefaultInterpolatedLabel(data, interpolatedValue) { + var labelDims = data.mapDimensionsAll('defaultedLabel'); + if (!isArray(interpolatedValue)) { + return interpolatedValue + ''; + } + var vals = []; + for (var i = 0; i < labelDims.length; i++) { + var dimIndex = data.getDimensionIndex(labelDims[i]); + if (dimIndex >= 0) { + vals.push(interpolatedValue[dimIndex]); + } + } + return vals.join(' '); + } + + var Symbol = /** @class */function (_super) { + __extends(Symbol, _super); + function Symbol(data, idx, seriesScope, opts) { + var _this = _super.call(this) || this; + _this.updateData(data, idx, seriesScope, opts); + return _this; + } + Symbol.prototype._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) { + // Remove paths created before + this.removeAll(); + // let symbolPath = createSymbol( + // symbolType, -0.5, -0.5, 1, 1, color + // ); + // If width/height are set too small (e.g., set to 1) on ios10 + // and macOS Sierra, a circle stroke become a rect, no matter what + // the scale is set. So we set width/height as 2. See #4150. + var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, null, keepAspect); + symbolPath.attr({ + z2: 100, + culling: true, + scaleX: symbolSize[0] / 2, + scaleY: symbolSize[1] / 2 + }); + // Rewrite drift method + symbolPath.drift = driftSymbol; + this._symbolType = symbolType; + this.add(symbolPath); + }; + /** + * Stop animation + * @param {boolean} toLastFrame + */ + Symbol.prototype.stopSymbolAnimation = function (toLastFrame) { + this.childAt(0).stopAnimation(null, toLastFrame); + }; + Symbol.prototype.getSymbolType = function () { + return this._symbolType; + }; + /** + * FIXME: + * Caution: This method breaks the encapsulation of this module, + * but it indeed brings convenience. So do not use the method + * unless you detailedly know all the implements of `Symbol`, + * especially animation. + * + * Get symbol path element. + */ + Symbol.prototype.getSymbolPath = function () { + return this.childAt(0); + }; + /** + * Highlight symbol + */ + Symbol.prototype.highlight = function () { + enterEmphasis(this.childAt(0)); + }; + /** + * Downplay symbol + */ + Symbol.prototype.downplay = function () { + leaveEmphasis(this.childAt(0)); + }; + /** + * @param {number} zlevel + * @param {number} z + */ + Symbol.prototype.setZ = function (zlevel, z) { + var symbolPath = this.childAt(0); + symbolPath.zlevel = zlevel; + symbolPath.z = z; + }; + Symbol.prototype.setDraggable = function (draggable, hasCursorOption) { + var symbolPath = this.childAt(0); + symbolPath.draggable = draggable; + symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor; + }; + /** + * Update symbol properties + */ + Symbol.prototype.updateData = function (data, idx, seriesScope, opts) { + this.silent = false; + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var seriesModel = data.hostModel; + var symbolSize = Symbol.getSymbolSize(data, idx); + var isInit = symbolType !== this._symbolType; + var disableAnimation = opts && opts.disableAnimation; + if (isInit) { + var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect'); + this._createSymbol(symbolType, data, idx, symbolSize, keepAspect); + } else { + var symbolPath = this.childAt(0); + symbolPath.silent = false; + var target = { + scaleX: symbolSize[0] / 2, + scaleY: symbolSize[1] / 2 + }; + disableAnimation ? symbolPath.attr(target) : updateProps(symbolPath, target, seriesModel, idx); + saveOldStyle(symbolPath); + } + this._updateCommon(data, idx, symbolSize, seriesScope, opts); + if (isInit) { + var symbolPath = this.childAt(0); + if (!disableAnimation) { + var target = { + scaleX: this._sizeX, + scaleY: this._sizeY, + style: { + // Always fadeIn. Because it has fadeOut animation when symbol is removed.. + opacity: symbolPath.style.opacity + } + }; + symbolPath.scaleX = symbolPath.scaleY = 0; + symbolPath.style.opacity = 0; + initProps(symbolPath, target, seriesModel, idx); + } + } + if (disableAnimation) { + // Must stop leave transition manually if don't call initProps or updateProps. + this.childAt(0).stopAnimation('leave'); + } + }; + Symbol.prototype._updateCommon = function (data, idx, symbolSize, seriesScope, opts) { + var symbolPath = this.childAt(0); + var seriesModel = data.hostModel; + var emphasisItemStyle; + var blurItemStyle; + var selectItemStyle; + var focus; + var blurScope; + var emphasisDisabled; + var labelStatesModels; + var hoverScale; + var cursorStyle; + if (seriesScope) { + emphasisItemStyle = seriesScope.emphasisItemStyle; + blurItemStyle = seriesScope.blurItemStyle; + selectItemStyle = seriesScope.selectItemStyle; + focus = seriesScope.focus; + blurScope = seriesScope.blurScope; + labelStatesModels = seriesScope.labelStatesModels; + hoverScale = seriesScope.hoverScale; + cursorStyle = seriesScope.cursorStyle; + emphasisDisabled = seriesScope.emphasisDisabled; + } + if (!seriesScope || data.hasItemOption) { + var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx); + var emphasisModel = itemModel.getModel('emphasis'); + emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle(); + selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle(); + blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle(); + focus = emphasisModel.get('focus'); + blurScope = emphasisModel.get('blurScope'); + emphasisDisabled = emphasisModel.get('disabled'); + labelStatesModels = getLabelStatesModels(itemModel); + hoverScale = emphasisModel.getShallow('scale'); + cursorStyle = itemModel.getShallow('cursor'); + } + var symbolRotate = data.getItemVisual(idx, 'symbolRotate'); + symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0); + var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize); + if (symbolOffset) { + symbolPath.x = symbolOffset[0]; + symbolPath.y = symbolOffset[1]; + } + cursorStyle && symbolPath.attr('cursor', cursorStyle); + var symbolStyle = data.getItemVisual(idx, 'style'); + var visualColor = symbolStyle.fill; + if (symbolPath instanceof ZRImage) { + var pathStyle = symbolPath.style; + symbolPath.useStyle(extend({ + // TODO other properties like x, y ? + image: pathStyle.image, + x: pathStyle.x, + y: pathStyle.y, + width: pathStyle.width, + height: pathStyle.height + }, symbolStyle)); + } else { + if (symbolPath.__isEmptyBrush) { + // fill and stroke will be swapped if it's empty. + // So we cloned a new style to avoid it affecting the original style in visual storage. + // TODO Better implementation. No empty logic! + symbolPath.useStyle(extend({}, symbolStyle)); + } else { + symbolPath.useStyle(symbolStyle); + } + // Disable decal because symbol scale will been applied on the decal. + symbolPath.style.decal = null; + symbolPath.setColor(visualColor, opts && opts.symbolInnerColor); + symbolPath.style.strokeNoScale = true; + } + var liftZ = data.getItemVisual(idx, 'liftZ'); + var z2Origin = this._z2; + if (liftZ != null) { + if (z2Origin == null) { + this._z2 = symbolPath.z2; + symbolPath.z2 += liftZ; + } + } else if (z2Origin != null) { + symbolPath.z2 = z2Origin; + this._z2 = null; + } + var useNameLabel = opts && opts.useNameLabel; + setLabelStyle(symbolPath, labelStatesModels, { + labelFetcher: seriesModel, + labelDataIndex: idx, + defaultText: getLabelDefaultText, + inheritColor: visualColor, + defaultOpacity: symbolStyle.opacity + }); + // Do not execute util needed. + function getLabelDefaultText(idx) { + return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx); + } + this._sizeX = symbolSize[0] / 2; + this._sizeY = symbolSize[1] / 2; + var emphasisState = symbolPath.ensureState('emphasis'); + emphasisState.style = emphasisItemStyle; + symbolPath.ensureState('select').style = selectItemStyle; + symbolPath.ensureState('blur').style = blurItemStyle; + // null / undefined / true means to use default strategy. + // 0 / false / negative number / NaN / Infinity means no scale. + var scaleRatio = hoverScale == null || hoverScale === true ? Math.max(1.1, 3 / this._sizeY) + // PENDING: restrict hoverScale > 1? It seems unreasonable to scale down + : isFinite(hoverScale) && hoverScale > 0 ? +hoverScale : 1; + // always set scale to allow resetting + emphasisState.scaleX = this._sizeX * scaleRatio; + emphasisState.scaleY = this._sizeY * scaleRatio; + this.setSymbolScale(1); + toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled); + }; + Symbol.prototype.setSymbolScale = function (scale) { + this.scaleX = this.scaleY = scale; + }; + Symbol.prototype.fadeOut = function (cb, seriesModel, opt) { + var symbolPath = this.childAt(0); + var dataIndex = getECData(this).dataIndex; + var animationOpt = opt && opt.animation; + // Avoid mistaken hover when fading out + this.silent = symbolPath.silent = true; + // Not show text when animating + if (opt && opt.fadeLabel) { + var textContent = symbolPath.getTextContent(); + if (textContent) { + removeElement(textContent, { + style: { + opacity: 0 + } + }, seriesModel, { + dataIndex: dataIndex, + removeOpt: animationOpt, + cb: function () { + symbolPath.removeTextContent(); + } + }); + } + } else { + symbolPath.removeTextContent(); + } + removeElement(symbolPath, { + style: { + opacity: 0 + }, + scaleX: 0, + scaleY: 0 + }, seriesModel, { + dataIndex: dataIndex, + cb: cb, + removeOpt: animationOpt + }); + }; + Symbol.getSymbolSize = function (data, idx) { + return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + }; + return Symbol; + }(Group); + function driftSymbol(dx, dy) { + this.parent.drift(dx, dy); + } + + function symbolNeedsDraw(data, point, idx, opt) { + return point && !isNaN(point[0]) && !isNaN(point[1]) && !(opt.isIgnore && opt.isIgnore(idx)) + // We do not set clipShape on group, because it will cut part of + // the symbol element shape. We use the same clip shape here as + // the line clip. + && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) && data.getItemVisual(idx, 'symbol') !== 'none'; + } + function normalizeUpdateOpt(opt) { + if (opt != null && !isObject(opt)) { + opt = { + isIgnore: opt + }; + } + return opt || {}; + } + function makeSeriesScope(data) { + var seriesModel = data.hostModel; + var emphasisModel = seriesModel.getModel('emphasis'); + return { + emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(), + blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(), + selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(), + focus: emphasisModel.get('focus'), + blurScope: emphasisModel.get('blurScope'), + emphasisDisabled: emphasisModel.get('disabled'), + hoverScale: emphasisModel.get('scale'), + labelStatesModels: getLabelStatesModels(seriesModel), + cursorStyle: seriesModel.get('cursor') + }; + } + var SymbolDraw = /** @class */function () { + function SymbolDraw(SymbolCtor) { + this.group = new Group(); + this._SymbolCtor = SymbolCtor || Symbol; + } + /** + * Update symbols draw by new data + */ + SymbolDraw.prototype.updateData = function (data, opt) { + // Remove progressive els. + this._progressiveEls = null; + opt = normalizeUpdateOpt(opt); + var group = this.group; + var seriesModel = data.hostModel; + var oldData = this._data; + var SymbolCtor = this._SymbolCtor; + var disableAnimation = opt.disableAnimation; + var seriesScope = makeSeriesScope(data); + var symbolUpdateOpt = { + disableAnimation: disableAnimation + }; + var getSymbolPoint = opt.getSymbolPoint || function (idx) { + return data.getItemLayout(idx); + }; + // There is no oldLineData only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!oldData) { + group.removeAll(); + } + data.diff(oldData).add(function (newIdx) { + var point = getSymbolPoint(newIdx); + if (symbolNeedsDraw(data, point, newIdx, opt)) { + var symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt); + symbolEl.setPosition(point); + data.setItemGraphicEl(newIdx, symbolEl); + group.add(symbolEl); + } + }).update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + var point = getSymbolPoint(newIdx); + if (!symbolNeedsDraw(data, point, newIdx, opt)) { + group.remove(symbolEl); + return; + } + var newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle'; + var oldSymbolType = symbolEl && symbolEl.getSymbolType && symbolEl.getSymbolType(); + if (!symbolEl + // Create a new if symbol type changed. + || oldSymbolType && oldSymbolType !== newSymbolType) { + group.remove(symbolEl); + symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt); + symbolEl.setPosition(point); + } else { + symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt); + var target = { + x: point[0], + y: point[1] + }; + disableAnimation ? symbolEl.attr(target) : updateProps(symbolEl, target, seriesModel); + } + // Add back + group.add(symbolEl); + data.setItemGraphicEl(newIdx, symbolEl); + }).remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && el.fadeOut(function () { + group.remove(el); + }, seriesModel); + }).execute(); + this._getSymbolPoint = getSymbolPoint; + this._data = data; + }; + SymbolDraw.prototype.updateLayout = function () { + var _this = this; + var data = this._data; + if (data) { + // Not use animation + data.eachItemGraphicEl(function (el, idx) { + var point = _this._getSymbolPoint(idx); + el.setPosition(point); + el.markRedraw(); + }); + } + }; + SymbolDraw.prototype.incrementalPrepareUpdate = function (data) { + this._seriesScope = makeSeriesScope(data); + this._data = null; + this.group.removeAll(); + }; + /** + * Update symbols draw by new data + */ + SymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) { + // Clear + this._progressiveEls = []; + opt = normalizeUpdateOpt(opt); + function updateIncrementalAndHover(el) { + if (!el.isGroup) { + el.incremental = true; + el.ensureState('emphasis').hoverLayer = true; + } + } + for (var idx = taskParams.start; idx < taskParams.end; idx++) { + var point = data.getItemLayout(idx); + if (symbolNeedsDraw(data, point, idx, opt)) { + var el = new this._SymbolCtor(data, idx, this._seriesScope); + el.traverse(updateIncrementalAndHover); + el.setPosition(point); + this.group.add(el); + data.setItemGraphicEl(idx, el); + this._progressiveEls.push(el); + } + } + }; + SymbolDraw.prototype.eachRendered = function (cb) { + traverseElements(this._progressiveEls || this.group, cb); + }; + SymbolDraw.prototype.remove = function (enableAnimation) { + var group = this.group; + var data = this._data; + // Incremental model do not have this._data. + if (data && enableAnimation) { + data.eachItemGraphicEl(function (el) { + el.fadeOut(function () { + group.remove(el); + }, data.hostModel); + }); + } else { + group.removeAll(); + } + }; + return SymbolDraw; + }(); + + function prepareDataCoordInfo(coordSys, data, valueOrigin) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueStart = getValueStart(valueAxis, valueOrigin); + var baseAxisDim = baseAxis.dim; + var valueAxisDim = valueAxis.dim; + var valueDim = data.mapDimension(valueAxisDim); + var baseDim = data.mapDimension(baseAxisDim); + var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0; + var dims = map(coordSys.dimensions, function (coordDim) { + return data.mapDimension(coordDim); + }); + var stacked = false; + var stackResultDim = data.getCalculationInfo('stackResultDimension'); + if (isDimensionStacked(data, dims[0] /* , dims[1] */)) { + // jshint ignore:line + stacked = true; + dims[0] = stackResultDim; + } + if (isDimensionStacked(data, dims[1] /* , dims[0] */)) { + // jshint ignore:line + stacked = true; + dims[1] = stackResultDim; + } + return { + dataDimsForPoint: dims, + valueStart: valueStart, + valueAxisDim: valueAxisDim, + baseAxisDim: baseAxisDim, + stacked: !!stacked, + valueDim: valueDim, + baseDim: baseDim, + baseDataOffset: baseDataOffset, + stackedOverDimension: data.getCalculationInfo('stackedOverDimension') + }; + } + function getValueStart(valueAxis, valueOrigin) { + var valueStart = 0; + var extent = valueAxis.scale.getExtent(); + if (valueOrigin === 'start') { + valueStart = extent[0]; + } else if (valueOrigin === 'end') { + valueStart = extent[1]; + } + // If origin is specified as a number, use it as + // valueStart directly + else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) { + valueStart = valueOrigin; + } + // auto + else { + // Both positive + if (extent[0] > 0) { + valueStart = extent[0]; + } + // Both negative + else if (extent[1] < 0) { + valueStart = extent[1]; + } + // If is one positive, and one negative, onZero shall be true + } + + return valueStart; + } + function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) { + var value = NaN; + if (dataCoordInfo.stacked) { + value = data.get(data.getCalculationInfo('stackedOverDimension'), idx); + } + if (isNaN(value)) { + value = dataCoordInfo.valueStart; + } + var baseDataOffset = dataCoordInfo.baseDataOffset; + var stackedData = []; + stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx); + stackedData[1 - baseDataOffset] = value; + return coordSys.dataToPoint(stackedData); + } + + function diffData(oldData, newData) { + var diffResult = []; + newData.diff(oldData).add(function (idx) { + diffResult.push({ + cmd: '+', + idx: idx + }); + }).update(function (newIdx, oldIdx) { + diffResult.push({ + cmd: '=', + idx: oldIdx, + idx1: newIdx + }); + }).remove(function (idx) { + diffResult.push({ + cmd: '-', + idx: idx + }); + }).execute(); + return diffResult; + } + function lineAnimationDiff(oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys, oldValueOrigin, newValueOrigin) { + var diff = diffData(oldData, newData); + // let newIdList = newData.mapArray(newData.getId); + // let oldIdList = oldData.mapArray(oldData.getId); + // convertToIntId(newIdList, oldIdList); + // // FIXME One data ? + // diff = arrayDiff(oldIdList, newIdList); + var currPoints = []; + var nextPoints = []; + // Points for stacking base line + var currStackedPoints = []; + var nextStackedPoints = []; + var status = []; + var sortedIndices = []; + var rawIndices = []; + var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); + // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin); + var oldPoints = oldData.getLayout('points') || []; + var newPoints = newData.getLayout('points') || []; + for (var i = 0; i < diff.length; i++) { + var diffItem = diff[i]; + var pointAdded = true; + var oldIdx2 = void 0; + var newIdx2 = void 0; + // FIXME, animation is not so perfect when dataZoom window moves fast + // Which is in case remvoing or add more than one data in the tail or head + switch (diffItem.cmd) { + case '=': + oldIdx2 = diffItem.idx * 2; + newIdx2 = diffItem.idx1 * 2; + var currentX = oldPoints[oldIdx2]; + var currentY = oldPoints[oldIdx2 + 1]; + var nextX = newPoints[newIdx2]; + var nextY = newPoints[newIdx2 + 1]; + // If previous data is NaN, use next point directly + if (isNaN(currentX) || isNaN(currentY)) { + currentX = nextX; + currentY = nextY; + } + currPoints.push(currentX, currentY); + nextPoints.push(nextX, nextY); + currStackedPoints.push(oldStackedOnPoints[oldIdx2], oldStackedOnPoints[oldIdx2 + 1]); + nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]); + rawIndices.push(newData.getRawIndex(diffItem.idx1)); + break; + case '+': + var newIdx = diffItem.idx; + var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint; + var oldPt = oldCoordSys.dataToPoint([newData.get(newDataDimsForPoint[0], newIdx), newData.get(newDataDimsForPoint[1], newIdx)]); + newIdx2 = newIdx * 2; + currPoints.push(oldPt[0], oldPt[1]); + nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]); + var stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, newIdx); + currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]); + nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]); + rawIndices.push(newData.getRawIndex(newIdx)); + break; + case '-': + pointAdded = false; + } + // Original indices + if (pointAdded) { + status.push(diffItem); + sortedIndices.push(sortedIndices.length); + } + } + // Diff result may be crossed if all items are changed + // Sort by data index + sortedIndices.sort(function (a, b) { + return rawIndices[a] - rawIndices[b]; + }); + var len = currPoints.length; + var sortedCurrPoints = createFloat32Array(len); + var sortedNextPoints = createFloat32Array(len); + var sortedCurrStackedPoints = createFloat32Array(len); + var sortedNextStackedPoints = createFloat32Array(len); + var sortedStatus = []; + for (var i = 0; i < sortedIndices.length; i++) { + var idx = sortedIndices[i]; + var i2 = i * 2; + var idx2 = idx * 2; + sortedCurrPoints[i2] = currPoints[idx2]; + sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1]; + sortedNextPoints[i2] = nextPoints[idx2]; + sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1]; + sortedCurrStackedPoints[i2] = currStackedPoints[idx2]; + sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1]; + sortedNextStackedPoints[i2] = nextStackedPoints[idx2]; + sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1]; + sortedStatus[i] = status[idx]; + } + return { + current: sortedCurrPoints, + next: sortedNextPoints, + stackedOnCurrent: sortedCurrStackedPoints, + stackedOnNext: sortedNextStackedPoints, + status: sortedStatus + }; + } + + var mathMin$5 = Math.min; + var mathMax$5 = Math.max; + function isPointNull(x, y) { + return isNaN(x) || isNaN(y); + } + /** + * Draw smoothed line in non-monotone, in may cause undesired curve in extreme + * situations. This should be used when points are non-monotone neither in x or + * y dimension. + */ + function drawSegment(ctx, points, start, segLen, allLen, dir, smooth, smoothMonotone, connectNulls) { + var prevX; + var prevY; + var cpx0; + var cpy0; + var cpx1; + var cpy1; + var idx = start; + var k = 0; + for (; k < segLen; k++) { + var x = points[idx * 2]; + var y = points[idx * 2 + 1]; + if (idx >= allLen || idx < 0) { + break; + } + if (isPointNull(x, y)) { + if (connectNulls) { + idx += dir; + continue; + } + break; + } + if (idx === start) { + ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y); + cpx0 = x; + cpy0 = y; + } else { + var dx = x - prevX; + var dy = y - prevY; + // Ignore tiny segment. + if (dx * dx + dy * dy < 0.5) { + idx += dir; + continue; + } + if (smooth > 0) { + var nextIdx = idx + dir; + var nextX = points[nextIdx * 2]; + var nextY = points[nextIdx * 2 + 1]; + // Ignore duplicate point + while (nextX === x && nextY === y && k < segLen) { + k++; + nextIdx += dir; + idx += dir; + nextX = points[nextIdx * 2]; + nextY = points[nextIdx * 2 + 1]; + x = points[idx * 2]; + y = points[idx * 2 + 1]; + dx = x - prevX; + dy = y - prevY; + } + var tmpK = k + 1; + if (connectNulls) { + // Find next point not null + while (isPointNull(nextX, nextY) && tmpK < segLen) { + tmpK++; + nextIdx += dir; + nextX = points[nextIdx * 2]; + nextY = points[nextIdx * 2 + 1]; + } + } + var ratioNextSeg = 0.5; + var vx = 0; + var vy = 0; + var nextCpx0 = void 0; + var nextCpy0 = void 0; + // Is last point + if (tmpK >= segLen || isPointNull(nextX, nextY)) { + cpx1 = x; + cpy1 = y; + } else { + vx = nextX - prevX; + vy = nextY - prevY; + var dx0 = x - prevX; + var dx1 = nextX - x; + var dy0 = y - prevY; + var dy1 = nextY - y; + var lenPrevSeg = void 0; + var lenNextSeg = void 0; + if (smoothMonotone === 'x') { + lenPrevSeg = Math.abs(dx0); + lenNextSeg = Math.abs(dx1); + var dir_1 = vx > 0 ? 1 : -1; + cpx1 = x - dir_1 * lenPrevSeg * smooth; + cpy1 = y; + nextCpx0 = x + dir_1 * lenNextSeg * smooth; + nextCpy0 = y; + } else if (smoothMonotone === 'y') { + lenPrevSeg = Math.abs(dy0); + lenNextSeg = Math.abs(dy1); + var dir_2 = vy > 0 ? 1 : -1; + cpx1 = x; + cpy1 = y - dir_2 * lenPrevSeg * smooth; + nextCpx0 = x; + nextCpy0 = y + dir_2 * lenNextSeg * smooth; + } else { + lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0); + lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1); + // Use ratio of seg length + ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); + cpx1 = x - vx * smooth * (1 - ratioNextSeg); + cpy1 = y - vy * smooth * (1 - ratioNextSeg); + // cp0 of next segment + nextCpx0 = x + vx * smooth * ratioNextSeg; + nextCpy0 = y + vy * smooth * ratioNextSeg; + // Smooth constraint between point and next point. + // Avoid exceeding extreme after smoothing. + nextCpx0 = mathMin$5(nextCpx0, mathMax$5(nextX, x)); + nextCpy0 = mathMin$5(nextCpy0, mathMax$5(nextY, y)); + nextCpx0 = mathMax$5(nextCpx0, mathMin$5(nextX, x)); + nextCpy0 = mathMax$5(nextCpy0, mathMin$5(nextY, y)); + // Reclaculate cp1 based on the adjusted cp0 of next seg. + vx = nextCpx0 - x; + vy = nextCpy0 - y; + cpx1 = x - vx * lenPrevSeg / lenNextSeg; + cpy1 = y - vy * lenPrevSeg / lenNextSeg; + // Smooth constraint between point and prev point. + // Avoid exceeding extreme after smoothing. + cpx1 = mathMin$5(cpx1, mathMax$5(prevX, x)); + cpy1 = mathMin$5(cpy1, mathMax$5(prevY, y)); + cpx1 = mathMax$5(cpx1, mathMin$5(prevX, x)); + cpy1 = mathMax$5(cpy1, mathMin$5(prevY, y)); + // Adjust next cp0 again. + vx = x - cpx1; + vy = y - cpy1; + nextCpx0 = x + vx * lenNextSeg / lenPrevSeg; + nextCpy0 = y + vy * lenNextSeg / lenPrevSeg; + } + } + ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y); + cpx0 = nextCpx0; + cpy0 = nextCpy0; + } else { + ctx.lineTo(x, y); + } + } + prevX = x; + prevY = y; + idx += dir; + } + return k; + } + var ECPolylineShape = /** @class */function () { + function ECPolylineShape() { + this.smooth = 0; + this.smoothConstraint = true; + } + return ECPolylineShape; + }(); + var ECPolyline = /** @class */function (_super) { + __extends(ECPolyline, _super); + function ECPolyline(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'ec-polyline'; + return _this; + } + ECPolyline.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + ECPolyline.prototype.getDefaultShape = function () { + return new ECPolylineShape(); + }; + ECPolyline.prototype.buildPath = function (ctx, shape) { + var points = shape.points; + var i = 0; + var len = points.length / 2; + // const result = getBoundingBox(points, shape.smoothConstraint); + if (shape.connectNulls) { + // Must remove first and last null values avoid draw error in polygon + for (; len > 0; len--) { + if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) { + break; + } + } + for (; i < len; i++) { + if (!isPointNull(points[i * 2], points[i * 2 + 1])) { + break; + } + } + } + while (i < len) { + i += drawSegment(ctx, points, i, len, len, 1, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1; + } + }; + ECPolyline.prototype.getPointOn = function (xOrY, dim) { + if (!this.path) { + this.createPathProxy(); + this.buildPath(this.path, this.shape); + } + var path = this.path; + var data = path.data; + var CMD = PathProxy.CMD; + var x0; + var y0; + var isDimX = dim === 'x'; + var roots = []; + for (var i = 0; i < data.length;) { + var cmd = data[i++]; + var x = void 0; + var y = void 0; + var x2 = void 0; + var y2 = void 0; + var x3 = void 0; + var y3 = void 0; + var t = void 0; + switch (cmd) { + case CMD.M: + x0 = data[i++]; + y0 = data[i++]; + break; + case CMD.L: + x = data[i++]; + y = data[i++]; + t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0); + if (t <= 1 && t >= 0) { + var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0; + return isDimX ? [xOrY, val] : [val, xOrY]; + } + x0 = x; + y0 = y; + break; + case CMD.C: + x = data[i++]; + y = data[i++]; + x2 = data[i++]; + y2 = data[i++]; + x3 = data[i++]; + y3 = data[i++]; + var nRoot = isDimX ? cubicRootAt(x0, x, x2, x3, xOrY, roots) : cubicRootAt(y0, y, y2, y3, xOrY, roots); + if (nRoot > 0) { + for (var i_1 = 0; i_1 < nRoot; i_1++) { + var t_1 = roots[i_1]; + if (t_1 <= 1 && t_1 >= 0) { + var val = isDimX ? cubicAt(y0, y, y2, y3, t_1) : cubicAt(x0, x, x2, x3, t_1); + return isDimX ? [xOrY, val] : [val, xOrY]; + } + } + } + x0 = x3; + y0 = y3; + break; + } + } + }; + return ECPolyline; + }(Path); + var ECPolygonShape = /** @class */function (_super) { + __extends(ECPolygonShape, _super); + function ECPolygonShape() { + return _super !== null && _super.apply(this, arguments) || this; + } + return ECPolygonShape; + }(ECPolylineShape); + var ECPolygon = /** @class */function (_super) { + __extends(ECPolygon, _super); + function ECPolygon(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'ec-polygon'; + return _this; + } + ECPolygon.prototype.getDefaultShape = function () { + return new ECPolygonShape(); + }; + ECPolygon.prototype.buildPath = function (ctx, shape) { + var points = shape.points; + var stackedOnPoints = shape.stackedOnPoints; + var i = 0; + var len = points.length / 2; + var smoothMonotone = shape.smoothMonotone; + if (shape.connectNulls) { + // Must remove first and last null values avoid draw error in polygon + for (; len > 0; len--) { + if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) { + break; + } + } + for (; i < len; i++) { + if (!isPointNull(points[i * 2], points[i * 2 + 1])) { + break; + } + } + } + while (i < len) { + var k = drawSegment(ctx, points, i, len, len, 1, shape.smooth, smoothMonotone, shape.connectNulls); + drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls); + i += k + 1; + ctx.closePath(); + } + }; + return ECPolygon; + }(Path); + + function createGridClipPath(cartesian, hasAnimation, seriesModel, done, during) { + var rect = cartesian.getArea(); + var x = rect.x; + var y = rect.y; + var width = rect.width; + var height = rect.height; + var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2; + // Expand the clip path a bit to avoid the border is clipped and looks thinner + x -= lineWidth / 2; + y -= lineWidth / 2; + width += lineWidth; + height += lineWidth; + // fix: https://github.com/apache/incubator-echarts/issues/11369 + width = Math.ceil(width); + if (x !== Math.floor(x)) { + x = Math.floor(x); + // if no extra 1px on `width`, it will still be clipped since `x` is floored + width++; + } + var clipPath = new Rect({ + shape: { + x: x, + y: y, + width: width, + height: height + } + }); + if (hasAnimation) { + var baseAxis = cartesian.getBaseAxis(); + var isHorizontal = baseAxis.isHorizontal(); + var isAxisInversed = baseAxis.inverse; + if (isHorizontal) { + if (isAxisInversed) { + clipPath.shape.x += width; + } + clipPath.shape.width = 0; + } else { + if (!isAxisInversed) { + clipPath.shape.y += height; + } + clipPath.shape.height = 0; + } + var duringCb = isFunction(during) ? function (percent) { + during(percent, clipPath); + } : null; + initProps(clipPath, { + shape: { + width: width, + height: height, + x: x, + y: y + } + }, seriesModel, null, done, duringCb); + } + return clipPath; + } + function createPolarClipPath(polar, hasAnimation, seriesModel) { + var sectorArea = polar.getArea(); + // Avoid float number rounding error for symbol on the edge of axis extent. + var r0 = round(sectorArea.r0, 1); + var r = round(sectorArea.r, 1); + var clipPath = new Sector({ + shape: { + cx: round(polar.cx, 1), + cy: round(polar.cy, 1), + r0: r0, + r: r, + startAngle: sectorArea.startAngle, + endAngle: sectorArea.endAngle, + clockwise: sectorArea.clockwise + } + }); + if (hasAnimation) { + var isRadial = polar.getBaseAxis().dim === 'angle'; + if (isRadial) { + clipPath.shape.endAngle = sectorArea.startAngle; + } else { + clipPath.shape.r = r0; + } + initProps(clipPath, { + shape: { + endAngle: sectorArea.endAngle, + r: r + } + }, seriesModel); + } + return clipPath; + } + function createClipPath(coordSys, hasAnimation, seriesModel, done, during) { + if (!coordSys) { + return null; + } else if (coordSys.type === 'polar') { + return createPolarClipPath(coordSys, hasAnimation, seriesModel); + } else if (coordSys.type === 'cartesian2d') { + return createGridClipPath(coordSys, hasAnimation, seriesModel, done, during); + } + return null; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function isCoordinateSystemType(coordSys, type) { + return coordSys.type === type; + } + + function isPointsSame(points1, points2) { + if (points1.length !== points2.length) { + return; + } + for (var i = 0; i < points1.length; i++) { + if (points1[i] !== points2[i]) { + return; + } + } + return true; + } + function bboxFromPoints(points) { + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + for (var i = 0; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + if (!isNaN(x)) { + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + } + if (!isNaN(y)) { + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + } + } + return [[minX, minY], [maxX, maxY]]; + } + function getBoundingDiff(points1, points2) { + var _a = bboxFromPoints(points1), + min1 = _a[0], + max1 = _a[1]; + var _b = bboxFromPoints(points2), + min2 = _b[0], + max2 = _b[1]; + // Get a max value from each corner of two boundings. + return Math.max(Math.abs(min1[0] - min2[0]), Math.abs(min1[1] - min2[1]), Math.abs(max1[0] - max2[0]), Math.abs(max1[1] - max2[1])); + } + function getSmooth(smooth) { + return isNumber(smooth) ? smooth : smooth ? 0.5 : 0; + } + function getStackedOnPoints(coordSys, data, dataCoordInfo) { + if (!dataCoordInfo.valueDim) { + return []; + } + var len = data.count(); + var points = createFloat32Array(len * 2); + for (var idx = 0; idx < len; idx++) { + var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx); + points[idx * 2] = pt[0]; + points[idx * 2 + 1] = pt[1]; + } + return points; + } + function turnPointsIntoStep(points, coordSys, stepTurnAt, connectNulls) { + var baseAxis = coordSys.getBaseAxis(); + var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; + var stepPoints = []; + var i = 0; + var stepPt = []; + var pt = []; + var nextPt = []; + var filteredPoints = []; + if (connectNulls) { + for (i = 0; i < points.length; i += 2) { + if (!isNaN(points[i]) && !isNaN(points[i + 1])) { + filteredPoints.push(points[i], points[i + 1]); + } + } + points = filteredPoints; + } + for (i = 0; i < points.length - 2; i += 2) { + nextPt[0] = points[i + 2]; + nextPt[1] = points[i + 3]; + pt[0] = points[i]; + pt[1] = points[i + 1]; + stepPoints.push(pt[0], pt[1]); + switch (stepTurnAt) { + case 'end': + stepPt[baseIndex] = nextPt[baseIndex]; + stepPt[1 - baseIndex] = pt[1 - baseIndex]; + stepPoints.push(stepPt[0], stepPt[1]); + break; + case 'middle': + var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2; + var stepPt2 = []; + stepPt[baseIndex] = stepPt2[baseIndex] = middle; + stepPt[1 - baseIndex] = pt[1 - baseIndex]; + stepPt2[1 - baseIndex] = nextPt[1 - baseIndex]; + stepPoints.push(stepPt[0], stepPt[1]); + stepPoints.push(stepPt2[0], stepPt2[1]); + break; + default: + // default is start + stepPt[baseIndex] = pt[baseIndex]; + stepPt[1 - baseIndex] = nextPt[1 - baseIndex]; + stepPoints.push(stepPt[0], stepPt[1]); + } + } + // Last points + stepPoints.push(points[i++], points[i++]); + return stepPoints; + } + /** + * Clip color stops to edge. Avoid creating too large gradients. + * Which may lead to blurry when GPU acceleration is enabled. See #15680 + * + * The stops has been sorted from small to large. + */ + function clipColorStops(colorStops, maxSize) { + var newColorStops = []; + var len = colorStops.length; + // coord will always < 0 in prevOutOfRangeColorStop. + var prevOutOfRangeColorStop; + var prevInRangeColorStop; + function lerpStop(stop0, stop1, clippedCoord) { + var coord0 = stop0.coord; + var p = (clippedCoord - coord0) / (stop1.coord - coord0); + var color = lerp$1(p, [stop0.color, stop1.color]); + return { + coord: clippedCoord, + color: color + }; + } + for (var i = 0; i < len; i++) { + var stop_1 = colorStops[i]; + var coord = stop_1.coord; + if (coord < 0) { + prevOutOfRangeColorStop = stop_1; + } else if (coord > maxSize) { + if (prevInRangeColorStop) { + newColorStops.push(lerpStop(prevInRangeColorStop, stop_1, maxSize)); + } else if (prevOutOfRangeColorStop) { + // If there are two stops and coord range is between these two stops + newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0), lerpStop(prevOutOfRangeColorStop, stop_1, maxSize)); + } + // All following stop will be out of range. So just ignore them. + break; + } else { + if (prevOutOfRangeColorStop) { + newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0)); + // Reset + prevOutOfRangeColorStop = null; + } + newColorStops.push(stop_1); + prevInRangeColorStop = stop_1; + } + } + return newColorStops; + } + function getVisualGradient(data, coordSys, api) { + var visualMetaList = data.getVisual('visualMeta'); + if (!visualMetaList || !visualMetaList.length || !data.count()) { + // When data.count() is 0, gradient range can not be calculated. + return; + } + if (coordSys.type !== 'cartesian2d') { + if ("development" !== 'production') { + console.warn('Visual map on line style is only supported on cartesian2d.'); + } + return; + } + var coordDim; + var visualMeta; + for (var i = visualMetaList.length - 1; i >= 0; i--) { + var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension); + coordDim = dimInfo && dimInfo.coordDim; + // Can only be x or y + if (coordDim === 'x' || coordDim === 'y') { + visualMeta = visualMetaList[i]; + break; + } + } + if (!visualMeta) { + if ("development" !== 'production') { + console.warn('Visual map on line style only support x or y dimension.'); + } + return; + } + // If the area to be rendered is bigger than area defined by LinearGradient, + // the canvas spec prescribes that the color of the first stop and the last + // stop should be used. But if two stops are added at offset 0, in effect + // browsers use the color of the second stop to render area outside + // LinearGradient. So we can only infinitesimally extend area defined in + // LinearGradient to render `outerColors`. + var axis = coordSys.getAxis(coordDim); + // dataToCoord mapping may not be linear, but must be monotonic. + var colorStops = map(visualMeta.stops, function (stop) { + // offset will be calculated later. + return { + coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)), + color: stop.color + }; + }); + var stopLen = colorStops.length; + var outerColors = visualMeta.outerColors.slice(); + if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) { + colorStops.reverse(); + outerColors.reverse(); + } + var colorStopsInRange = clipColorStops(colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight()); + var inRangeStopLen = colorStopsInRange.length; + if (!inRangeStopLen && stopLen) { + // All stops are out of range. All will be the same color. + return colorStops[0].coord < 0 ? outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color : outerColors[0] ? outerColors[0] : colorStops[0].color; + } + var tinyExtent = 10; // Arbitrary value: 10px + var minCoord = colorStopsInRange[0].coord - tinyExtent; + var maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent; + var coordSpan = maxCoord - minCoord; + if (coordSpan < 1e-3) { + return 'transparent'; + } + each(colorStopsInRange, function (stop) { + stop.offset = (stop.coord - minCoord) / coordSpan; + }); + colorStopsInRange.push({ + // NOTE: inRangeStopLen may still be 0 if stoplen is zero. + offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5, + color: outerColors[1] || 'transparent' + }); + colorStopsInRange.unshift({ + offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5, + color: outerColors[0] || 'transparent' + }); + var gradient = new LinearGradient(0, 0, 0, 0, colorStopsInRange, true); + gradient[coordDim] = minCoord; + gradient[coordDim + '2'] = maxCoord; + return gradient; + } + function getIsIgnoreFunc(seriesModel, data, coordSys) { + var showAllSymbol = seriesModel.get('showAllSymbol'); + var isAuto = showAllSymbol === 'auto'; + if (showAllSymbol && !isAuto) { + return; + } + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + if (!categoryAxis) { + return; + } + // Note that category label interval strategy might bring some weird effect + // in some scenario: users may wonder why some of the symbols are not + // displayed. So we show all symbols as possible as we can. + if (isAuto + // Simplify the logic, do not determine label overlap here. + && canShowAllSymbolForCategory(categoryAxis, data)) { + return; + } + // Otherwise follow the label interval strategy on category axis. + var categoryDataDim = data.mapDimension(categoryAxis.dim); + var labelMap = {}; + each(categoryAxis.getViewLabels(), function (labelItem) { + var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(labelItem.tickValue); + labelMap[ordinalNumber] = 1; + }); + return function (dataIndex) { + return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex)); + }; + } + function canShowAllSymbolForCategory(categoryAxis, data) { + // In most cases, line is monotonous on category axis, and the label size + // is close with each other. So we check the symbol size and some of the + // label size alone with the category axis to estimate whether all symbol + // can be shown without overlap. + var axisExtent = categoryAxis.getExtent(); + var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count(); + isNaN(availSize) && (availSize = 0); // 0/0 is NaN. + // Sampling some points, max 5. + var dataLen = data.count(); + var step = Math.max(1, Math.round(dataLen / 5)); + for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) { + if (Symbol.getSymbolSize(data, dataIndex + // Only for cartesian, where `isHorizontal` exists. + )[categoryAxis.isHorizontal() ? 1 : 0] + // Empirical number + * 1.5 > availSize) { + return false; + } + } + return true; + } + function isPointNull$1(x, y) { + return isNaN(x) || isNaN(y); + } + function getLastIndexNotNull(points) { + var len = points.length / 2; + for (; len > 0; len--) { + if (!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])) { + break; + } + } + return len - 1; + } + function getPointAtIndex(points, idx) { + return [points[idx * 2], points[idx * 2 + 1]]; + } + function getIndexRange(points, xOrY, dim) { + var len = points.length / 2; + var dimIdx = dim === 'x' ? 0 : 1; + var a; + var b; + var prevIndex = 0; + var nextIndex = -1; + for (var i = 0; i < len; i++) { + b = points[i * 2 + dimIdx]; + if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) { + continue; + } + if (i === 0) { + a = b; + continue; + } + if (a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY) { + nextIndex = i; + break; + } + prevIndex = i; + a = b; + } + return { + range: [prevIndex, nextIndex], + t: (xOrY - a) / (b - a) + }; + } + function anyStateShowEndLabel(seriesModel) { + if (seriesModel.get(['endLabel', 'show'])) { + return true; + } + for (var i = 0; i < SPECIAL_STATES.length; i++) { + if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) { + return true; + } + } + return false; + } + function createLineClipPath(lineView, coordSys, hasAnimation, seriesModel) { + if (isCoordinateSystemType(coordSys, 'cartesian2d')) { + var endLabelModel_1 = seriesModel.getModel('endLabel'); + var valueAnimation_1 = endLabelModel_1.get('valueAnimation'); + var data_1 = seriesModel.getData(); + var labelAnimationRecord_1 = { + lastFrameIndex: 0 + }; + var during = anyStateShowEndLabel(seriesModel) ? function (percent, clipRect) { + lineView._endLabelOnDuring(percent, clipRect, data_1, labelAnimationRecord_1, valueAnimation_1, endLabelModel_1, coordSys); + } : null; + var isHorizontal = coordSys.getBaseAxis().isHorizontal(); + var clipPath = createGridClipPath(coordSys, hasAnimation, seriesModel, function () { + var endLabel = lineView._endLabel; + if (endLabel && hasAnimation) { + if (labelAnimationRecord_1.originalX != null) { + endLabel.attr({ + x: labelAnimationRecord_1.originalX, + y: labelAnimationRecord_1.originalY + }); + } + } + }, during); + // Expand clip shape to avoid clipping when line value exceeds axis + if (!seriesModel.get('clip', true)) { + var rectShape = clipPath.shape; + var expandSize = Math.max(rectShape.width, rectShape.height); + if (isHorizontal) { + rectShape.y -= expandSize; + rectShape.height += expandSize * 2; + } else { + rectShape.x -= expandSize; + rectShape.width += expandSize * 2; + } + } + // Set to the final frame. To make sure label layout is right. + if (during) { + during(1, clipPath); + } + return clipPath; + } else { + if ("development" !== 'production') { + if (seriesModel.get(['endLabel', 'show'])) { + console.warn('endLabel is not supported for lines in polar systems.'); + } + } + return createPolarClipPath(coordSys, hasAnimation, seriesModel); + } + } + function getEndLabelStateSpecified(endLabelModel, coordSys) { + var baseAxis = coordSys.getBaseAxis(); + var isHorizontal = baseAxis.isHorizontal(); + var isBaseInversed = baseAxis.inverse; + var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center'; + var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom'; + return { + normal: { + align: endLabelModel.get('align') || align, + verticalAlign: endLabelModel.get('verticalAlign') || verticalAlign + } + }; + } + var LineView = /** @class */function (_super) { + __extends(LineView, _super); + function LineView() { + return _super !== null && _super.apply(this, arguments) || this; + } + LineView.prototype.init = function () { + var lineGroup = new Group(); + var symbolDraw = new SymbolDraw(); + this.group.add(symbolDraw.group); + this._symbolDraw = symbolDraw; + this._lineGroup = lineGroup; + }; + LineView.prototype.render = function (seriesModel, ecModel, api) { + var _this = this; + var coordSys = seriesModel.coordinateSystem; + var group = this.group; + var data = seriesModel.getData(); + var lineStyleModel = seriesModel.getModel('lineStyle'); + var areaStyleModel = seriesModel.getModel('areaStyle'); + var points = data.getLayout('points') || []; + var isCoordSysPolar = coordSys.type === 'polar'; + var prevCoordSys = this._coordSys; + var symbolDraw = this._symbolDraw; + var polyline = this._polyline; + var polygon = this._polygon; + var lineGroup = this._lineGroup; + var hasAnimation = !ecModel.ssr && seriesModel.get('animation'); + var isAreaChart = !areaStyleModel.isEmpty(); + var valueOrigin = areaStyleModel.get('origin'); + var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin); + var stackedOnPoints = isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo); + var showSymbol = seriesModel.get('showSymbol'); + var connectNulls = seriesModel.get('connectNulls'); + var isIgnoreFunc = showSymbol && !isCoordSysPolar && getIsIgnoreFunc(seriesModel, data, coordSys); + // Remove temporary symbols + var oldData = this._data; + oldData && oldData.eachItemGraphicEl(function (el, idx) { + if (el.__temp) { + group.remove(el); + oldData.setItemGraphicEl(idx, null); + } + }); + // Remove previous created symbols if showSymbol changed to false + if (!showSymbol) { + symbolDraw.remove(); + } + group.add(lineGroup); + // FIXME step not support polar + var step = !isCoordSysPolar ? seriesModel.get('step') : false; + var clipShapeForSymbol; + if (coordSys && coordSys.getArea && seriesModel.get('clip', true)) { + clipShapeForSymbol = coordSys.getArea(); + // Avoid float number rounding error for symbol on the edge of axis extent. + // See #7913 and `test/dataZoom-clip.html`. + if (clipShapeForSymbol.width != null) { + clipShapeForSymbol.x -= 0.1; + clipShapeForSymbol.y -= 0.1; + clipShapeForSymbol.width += 0.2; + clipShapeForSymbol.height += 0.2; + } else if (clipShapeForSymbol.r0) { + clipShapeForSymbol.r0 -= 0.5; + clipShapeForSymbol.r += 0.5; + } + } + this._clipShapeForSymbol = clipShapeForSymbol; + var visualColor = getVisualGradient(data, coordSys, api) || data.getVisual('style')[data.getVisual('drawType')]; + // Initialization animation or coordinate system changed + if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) { + showSymbol && symbolDraw.updateData(data, { + isIgnore: isIgnoreFunc, + clipShape: clipShapeForSymbol, + disableAnimation: true, + getSymbolPoint: function (idx) { + return [points[idx * 2], points[idx * 2 + 1]]; + } + }); + hasAnimation && this._initSymbolLabelAnimation(data, coordSys, clipShapeForSymbol); + if (step) { + // TODO If stacked series is not step + points = turnPointsIntoStep(points, coordSys, step, connectNulls); + if (stackedOnPoints) { + stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls); + } + } + polyline = this._newPolyline(points); + if (isAreaChart) { + polygon = this._newPolygon(points, stackedOnPoints); + } // If areaStyle is removed + else if (polygon) { + lineGroup.remove(polygon); + polygon = this._polygon = null; + } + // NOTE: Must update _endLabel before setClipPath. + if (!isCoordSysPolar) { + this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor)); + } + lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel)); + } else { + if (isAreaChart && !polygon) { + // If areaStyle is added + polygon = this._newPolygon(points, stackedOnPoints); + } else if (polygon && !isAreaChart) { + // If areaStyle is removed + lineGroup.remove(polygon); + polygon = this._polygon = null; + } + // NOTE: Must update _endLabel before setClipPath. + if (!isCoordSysPolar) { + this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor)); + } + // Update clipPath + var oldClipPath = lineGroup.getClipPath(); + if (oldClipPath) { + var newClipPath = createLineClipPath(this, coordSys, false, seriesModel); + initProps(oldClipPath, { + shape: newClipPath.shape + }, seriesModel); + } else { + lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel)); + } + // Always update, or it is wrong in the case turning on legend + // because points are not changed. + showSymbol && symbolDraw.updateData(data, { + isIgnore: isIgnoreFunc, + clipShape: clipShapeForSymbol, + disableAnimation: true, + getSymbolPoint: function (idx) { + return [points[idx * 2], points[idx * 2 + 1]]; + } + }); + // In the case data zoom triggered refreshing frequently + // Data may not change if line has a category axis. So it should animate nothing. + if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) { + if (hasAnimation) { + this._doUpdateAnimation(data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls); + } else { + // Not do it in update with animation + if (step) { + // TODO If stacked series is not step + points = turnPointsIntoStep(points, coordSys, step, connectNulls); + if (stackedOnPoints) { + stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls); + } + } + polyline.setShape({ + points: points + }); + polygon && polygon.setShape({ + points: points, + stackedOnPoints: stackedOnPoints + }); + } + } + } + var emphasisModel = seriesModel.getModel('emphasis'); + var focus = emphasisModel.get('focus'); + var blurScope = emphasisModel.get('blurScope'); + var emphasisDisabled = emphasisModel.get('disabled'); + polyline.useStyle(defaults( + // Use color in lineStyle first + lineStyleModel.getLineStyle(), { + fill: 'none', + stroke: visualColor, + lineJoin: 'bevel' + })); + setStatesStylesFromModel(polyline, seriesModel, 'lineStyle'); + if (polyline.style.lineWidth > 0 && seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder') { + var emphasisLineStyle = polyline.getState('emphasis').style; + emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1; + } + // Needs seriesIndex for focus + getECData(polyline).seriesIndex = seriesModel.seriesIndex; + toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled); + var smooth = getSmooth(seriesModel.get('smooth')); + var smoothMonotone = seriesModel.get('smoothMonotone'); + polyline.setShape({ + smooth: smooth, + smoothMonotone: smoothMonotone, + connectNulls: connectNulls + }); + if (polygon) { + var stackedOnSeries = data.getCalculationInfo('stackedOnSeries'); + var stackedOnSmooth = 0; + polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), { + fill: visualColor, + opacity: 0.7, + lineJoin: 'bevel', + decal: data.getVisual('style').decal + })); + if (stackedOnSeries) { + stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth')); + } + polygon.setShape({ + smooth: smooth, + stackedOnSmooth: stackedOnSmooth, + smoothMonotone: smoothMonotone, + connectNulls: connectNulls + }); + setStatesStylesFromModel(polygon, seriesModel, 'areaStyle'); + // Needs seriesIndex for focus + getECData(polygon).seriesIndex = seriesModel.seriesIndex; + toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled); + } + var changePolyState = function (toState) { + _this._changePolyState(toState); + }; + data.eachItemGraphicEl(function (el) { + // Switch polyline / polygon state if element changed its state. + el && (el.onHoverStateChange = changePolyState); + }); + this._polyline.onHoverStateChange = changePolyState; + this._data = data; + // Save the coordinate system for transition animation when data changed + this._coordSys = coordSys; + this._stackedOnPoints = stackedOnPoints; + this._points = points; + this._step = step; + this._valueOrigin = valueOrigin; + if (seriesModel.get('triggerLineEvent')) { + this.packEventData(seriesModel, polyline); + polygon && this.packEventData(seriesModel, polygon); + } + }; + LineView.prototype.packEventData = function (seriesModel, el) { + getECData(el).eventData = { + componentType: 'series', + componentSubType: 'line', + componentIndex: seriesModel.componentIndex, + seriesIndex: seriesModel.seriesIndex, + seriesName: seriesModel.name, + seriesType: 'line' + }; + }; + LineView.prototype.highlight = function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload); + this._changePolyState('emphasis'); + if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) { + var points = data.getLayout('points'); + var symbol = data.getItemGraphicEl(dataIndex); + if (!symbol) { + // Create a temporary symbol if it is not exists + var x = points[dataIndex * 2]; + var y = points[dataIndex * 2 + 1]; + if (isNaN(x) || isNaN(y)) { + // Null data + return; + } + // fix #11360: shouldn't draw symbol outside clipShapeForSymbol + if (this._clipShapeForSymbol && !this._clipShapeForSymbol.contain(x, y)) { + return; + } + var zlevel = seriesModel.get('zlevel') || 0; + var z = seriesModel.get('z') || 0; + symbol = new Symbol(data, dataIndex); + symbol.x = x; + symbol.y = y; + symbol.setZ(zlevel, z); + // ensure label text of the temporary symbol is in front of line and area polygon + var symbolLabel = symbol.getSymbolPath().getTextContent(); + if (symbolLabel) { + symbolLabel.zlevel = zlevel; + symbolLabel.z = z; + symbolLabel.z2 = this._polyline.z2 + 1; + } + symbol.__temp = true; + data.setItemGraphicEl(dataIndex, symbol); + // Stop scale animation + symbol.stopSymbolAnimation(true); + this.group.add(symbol); + } + symbol.highlight(); + } else { + // Highlight whole series + ChartView.prototype.highlight.call(this, seriesModel, ecModel, api, payload); + } + }; + LineView.prototype.downplay = function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload); + this._changePolyState('normal'); + if (dataIndex != null && dataIndex >= 0) { + var symbol = data.getItemGraphicEl(dataIndex); + if (symbol) { + if (symbol.__temp) { + data.setItemGraphicEl(dataIndex, null); + this.group.remove(symbol); + } else { + symbol.downplay(); + } + } + } else { + // FIXME + // can not downplay completely. + // Downplay whole series + ChartView.prototype.downplay.call(this, seriesModel, ecModel, api, payload); + } + }; + LineView.prototype._changePolyState = function (toState) { + var polygon = this._polygon; + setStatesFlag(this._polyline, toState); + polygon && setStatesFlag(polygon, toState); + }; + LineView.prototype._newPolyline = function (points) { + var polyline = this._polyline; + // Remove previous created polyline + if (polyline) { + this._lineGroup.remove(polyline); + } + polyline = new ECPolyline({ + shape: { + points: points + }, + segmentIgnoreThreshold: 2, + z2: 10 + }); + this._lineGroup.add(polyline); + this._polyline = polyline; + return polyline; + }; + LineView.prototype._newPolygon = function (points, stackedOnPoints) { + var polygon = this._polygon; + // Remove previous created polygon + if (polygon) { + this._lineGroup.remove(polygon); + } + polygon = new ECPolygon({ + shape: { + points: points, + stackedOnPoints: stackedOnPoints + }, + segmentIgnoreThreshold: 2 + }); + this._lineGroup.add(polygon); + this._polygon = polygon; + return polygon; + }; + LineView.prototype._initSymbolLabelAnimation = function (data, coordSys, clipShape) { + var isHorizontalOrRadial; + var isCoordSysPolar; + var baseAxis = coordSys.getBaseAxis(); + var isAxisInverse = baseAxis.inverse; + if (coordSys.type === 'cartesian2d') { + isHorizontalOrRadial = baseAxis.isHorizontal(); + isCoordSysPolar = false; + } else if (coordSys.type === 'polar') { + isHorizontalOrRadial = baseAxis.dim === 'angle'; + isCoordSysPolar = true; + } + var seriesModel = data.hostModel; + var seriesDuration = seriesModel.get('animationDuration'); + if (isFunction(seriesDuration)) { + seriesDuration = seriesDuration(null); + } + var seriesDelay = seriesModel.get('animationDelay') || 0; + var seriesDelayValue = isFunction(seriesDelay) ? seriesDelay(null) : seriesDelay; + data.eachItemGraphicEl(function (symbol, idx) { + var el = symbol; + if (el) { + var point = [symbol.x, symbol.y]; + var start = void 0; + var end = void 0; + var current = void 0; + if (clipShape) { + if (isCoordSysPolar) { + var polarClip = clipShape; + var coord = coordSys.pointToCoord(point); + if (isHorizontalOrRadial) { + start = polarClip.startAngle; + end = polarClip.endAngle; + current = -coord[1] / 180 * Math.PI; + } else { + start = polarClip.r0; + end = polarClip.r; + current = coord[0]; + } + } else { + var gridClip = clipShape; + if (isHorizontalOrRadial) { + start = gridClip.x; + end = gridClip.x + gridClip.width; + current = symbol.x; + } else { + start = gridClip.y + gridClip.height; + end = gridClip.y; + current = symbol.y; + } + } + } + var ratio = end === start ? 0 : (current - start) / (end - start); + if (isAxisInverse) { + ratio = 1 - ratio; + } + var delay = isFunction(seriesDelay) ? seriesDelay(idx) : seriesDuration * ratio + seriesDelayValue; + var symbolPath = el.getSymbolPath(); + var text = symbolPath.getTextContent(); + el.attr({ + scaleX: 0, + scaleY: 0 + }); + el.animateTo({ + scaleX: 1, + scaleY: 1 + }, { + duration: 200, + setToFinal: true, + delay: delay + }); + if (text) { + text.animateFrom({ + style: { + opacity: 0 + } + }, { + duration: 300, + delay: delay + }); + } + symbolPath.disableLabelAnimation = true; + } + }); + }; + LineView.prototype._initOrUpdateEndLabel = function (seriesModel, coordSys, inheritColor) { + var endLabelModel = seriesModel.getModel('endLabel'); + if (anyStateShowEndLabel(seriesModel)) { + var data_2 = seriesModel.getData(); + var polyline = this._polyline; + // series may be filtered. + var points = data_2.getLayout('points'); + if (!points) { + polyline.removeTextContent(); + this._endLabel = null; + return; + } + var endLabel = this._endLabel; + if (!endLabel) { + endLabel = this._endLabel = new ZRText({ + z2: 200 // should be higher than item symbol + }); + + endLabel.ignoreClip = true; + polyline.setTextContent(this._endLabel); + polyline.disableLabelAnimation = true; + } + // Find last non-NaN data to display data + var dataIndex = getLastIndexNotNull(points); + if (dataIndex >= 0) { + setLabelStyle(polyline, getLabelStatesModels(seriesModel, 'endLabel'), { + inheritColor: inheritColor, + labelFetcher: seriesModel, + labelDataIndex: dataIndex, + defaultText: function (dataIndex, opt, interpolatedValue) { + return interpolatedValue != null ? getDefaultInterpolatedLabel(data_2, interpolatedValue) : getDefaultLabel(data_2, dataIndex); + }, + enableTextSetter: true + }, getEndLabelStateSpecified(endLabelModel, coordSys)); + polyline.textConfig.position = null; + } + } else if (this._endLabel) { + this._polyline.removeTextContent(); + this._endLabel = null; + } + }; + LineView.prototype._endLabelOnDuring = function (percent, clipRect, data, animationRecord, valueAnimation, endLabelModel, coordSys) { + var endLabel = this._endLabel; + var polyline = this._polyline; + if (endLabel) { + // NOTE: Don't remove percent < 1. percent === 1 means the first frame during render. + // The label is not prepared at this time. + if (percent < 1 && animationRecord.originalX == null) { + animationRecord.originalX = endLabel.x; + animationRecord.originalY = endLabel.y; + } + var points = data.getLayout('points'); + var seriesModel = data.hostModel; + var connectNulls = seriesModel.get('connectNulls'); + var precision = endLabelModel.get('precision'); + var distance = endLabelModel.get('distance') || 0; + var baseAxis = coordSys.getBaseAxis(); + var isHorizontal = baseAxis.isHorizontal(); + var isBaseInversed = baseAxis.inverse; + var clipShape = clipRect.shape; + var xOrY = isBaseInversed ? isHorizontal ? clipShape.x : clipShape.y + clipShape.height : isHorizontal ? clipShape.x + clipShape.width : clipShape.y; + var distanceX = (isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1); + var distanceY = (isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1); + var dim = isHorizontal ? 'x' : 'y'; + var dataIndexRange = getIndexRange(points, xOrY, dim); + var indices = dataIndexRange.range; + var diff = indices[1] - indices[0]; + var value = void 0; + if (diff >= 1) { + // diff > 1 && connectNulls, which is on the null data. + if (diff > 1 && !connectNulls) { + var pt = getPointAtIndex(points, indices[0]); + endLabel.attr({ + x: pt[0] + distanceX, + y: pt[1] + distanceY + }); + valueAnimation && (value = seriesModel.getRawValue(indices[0])); + } else { + var pt = polyline.getPointOn(xOrY, dim); + pt && endLabel.attr({ + x: pt[0] + distanceX, + y: pt[1] + distanceY + }); + var startValue = seriesModel.getRawValue(indices[0]); + var endValue = seriesModel.getRawValue(indices[1]); + valueAnimation && (value = interpolateRawValues(data, precision, startValue, endValue, dataIndexRange.t)); + } + animationRecord.lastFrameIndex = indices[0]; + } else { + // If diff <= 0, which is the range is not found(Include NaN) + // Choose the first point or last point. + var idx = percent === 1 || animationRecord.lastFrameIndex > 0 ? indices[0] : 0; + var pt = getPointAtIndex(points, idx); + valueAnimation && (value = seriesModel.getRawValue(idx)); + endLabel.attr({ + x: pt[0] + distanceX, + y: pt[1] + distanceY + }); + } + if (valueAnimation) { + var inner = labelInner(endLabel); + if (typeof inner.setLabelText === 'function') { + inner.setLabelText(value); + } + } + } + }; + /** + * @private + */ + // FIXME Two value axis + LineView.prototype._doUpdateAnimation = function (data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls) { + var polyline = this._polyline; + var polygon = this._polygon; + var seriesModel = data.hostModel; + var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys, this._valueOrigin); + var current = diff.current; + var stackedOnCurrent = diff.stackedOnCurrent; + var next = diff.next; + var stackedOnNext = diff.stackedOnNext; + if (step) { + // TODO If stacked series is not step + current = turnPointsIntoStep(diff.current, coordSys, step, connectNulls); + stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step, connectNulls); + next = turnPointsIntoStep(diff.next, coordSys, step, connectNulls); + stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step, connectNulls); + } + // Don't apply animation if diff is large. + // For better result and avoid memory explosion problems like + // https://github.com/apache/incubator-echarts/issues/12229 + if (getBoundingDiff(current, next) > 3000 || polygon && getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000) { + polyline.stopAnimation(); + polyline.setShape({ + points: next + }); + if (polygon) { + polygon.stopAnimation(); + polygon.setShape({ + points: next, + stackedOnPoints: stackedOnNext + }); + } + return; + } + polyline.shape.__points = diff.current; + polyline.shape.points = current; + var target = { + shape: { + points: next + } + }; + // Also animate the original points. + // If points reference is changed when turning into step line. + if (diff.current !== current) { + target.shape.__points = diff.next; + } + // Stop previous animation. + polyline.stopAnimation(); + updateProps(polyline, target, seriesModel); + if (polygon) { + polygon.setShape({ + // Reuse the points with polyline. + points: current, + stackedOnPoints: stackedOnCurrent + }); + polygon.stopAnimation(); + updateProps(polygon, { + shape: { + stackedOnPoints: stackedOnNext + } + }, seriesModel); + // If use attr directly in updateProps. + if (polyline.shape.points !== polygon.shape.points) { + polygon.shape.points = polyline.shape.points; + } + } + var updatedDataInfo = []; + var diffStatus = diff.status; + for (var i = 0; i < diffStatus.length; i++) { + var cmd = diffStatus[i].cmd; + if (cmd === '=') { + var el = data.getItemGraphicEl(diffStatus[i].idx1); + if (el) { + updatedDataInfo.push({ + el: el, + ptIdx: i // Index of points + }); + } + } + } + + if (polyline.animators && polyline.animators.length) { + polyline.animators[0].during(function () { + polygon && polygon.dirtyShape(); + var points = polyline.shape.__points; + for (var i = 0; i < updatedDataInfo.length; i++) { + var el = updatedDataInfo[i].el; + var offset = updatedDataInfo[i].ptIdx * 2; + el.x = points[offset]; + el.y = points[offset + 1]; + el.markRedraw(); + } + }); + } + }; + LineView.prototype.remove = function (ecModel) { + var group = this.group; + var oldData = this._data; + this._lineGroup.removeAll(); + this._symbolDraw.remove(true); + // Remove temporary created elements when highlighting + oldData && oldData.eachItemGraphicEl(function (el, idx) { + if (el.__temp) { + group.remove(el); + oldData.setItemGraphicEl(idx, null); + } + }); + this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._endLabel = this._data = null; + }; + LineView.type = 'line'; + return LineView; + }(ChartView); + + function pointsLayout(seriesType, forceStoreInTypedArray) { + return { + seriesType: seriesType, + plan: createRenderPlanner(), + reset: function (seriesModel) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + var pipelineContext = seriesModel.pipelineContext; + var useTypedArray = forceStoreInTypedArray || pipelineContext.large; + if (!coordSys) { + return; + } + var dims = map(coordSys.dimensions, function (dim) { + return data.mapDimension(dim); + }).slice(0, 2); + var dimLen = dims.length; + var stackResultDim = data.getCalculationInfo('stackResultDimension'); + if (isDimensionStacked(data, dims[0])) { + dims[0] = stackResultDim; + } + if (isDimensionStacked(data, dims[1])) { + dims[1] = stackResultDim; + } + var store = data.getStore(); + var dimIdx0 = data.getDimensionIndex(dims[0]); + var dimIdx1 = data.getDimensionIndex(dims[1]); + return dimLen && { + progress: function (params, data) { + var segCount = params.end - params.start; + var points = useTypedArray && createFloat32Array(segCount * dimLen); + var tmpIn = []; + var tmpOut = []; + for (var i = params.start, offset = 0; i < params.end; i++) { + var point = void 0; + if (dimLen === 1) { + var x = store.get(dimIdx0, i); + // NOTE: Make sure the second parameter is null to use default strategy. + point = coordSys.dataToPoint(x, null, tmpOut); + } else { + tmpIn[0] = store.get(dimIdx0, i); + tmpIn[1] = store.get(dimIdx1, i); + // Let coordinate system to handle the NaN data. + point = coordSys.dataToPoint(tmpIn, null, tmpOut); + } + if (useTypedArray) { + points[offset++] = point[0]; + points[offset++] = point[1]; + } else { + data.setItemLayout(i, point.slice()); + } + } + useTypedArray && data.setLayout('points', points); + } + }; + } + }; + } + + var samplers = { + average: function (frame) { + var sum = 0; + var count = 0; + for (var i = 0; i < frame.length; i++) { + if (!isNaN(frame[i])) { + sum += frame[i]; + count++; + } + } + // Return NaN if count is 0 + return count === 0 ? NaN : sum / count; + }, + sum: function (frame) { + var sum = 0; + for (var i = 0; i < frame.length; i++) { + // Ignore NaN + sum += frame[i] || 0; + } + return sum; + }, + max: function (frame) { + var max = -Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] > max && (max = frame[i]); + } + // NaN will cause illegal axis extent. + return isFinite(max) ? max : NaN; + }, + min: function (frame) { + var min = Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] < min && (min = frame[i]); + } + // NaN will cause illegal axis extent. + return isFinite(min) ? min : NaN; + }, + minmax: function (frame) { + var turningPointAbsoluteValue = -Infinity; + var turningPointOriginalValue = -Infinity; + for (var i = 0; i < frame.length; i++) { + var originalValue = frame[i]; + var absoluteValue = Math.abs(originalValue); + if (absoluteValue > turningPointAbsoluteValue) { + turningPointAbsoluteValue = absoluteValue; + turningPointOriginalValue = originalValue; + } + } + return isFinite(turningPointOriginalValue) ? turningPointOriginalValue : NaN; + }, + // TODO + // Median + nearest: function (frame) { + return frame[0]; + } + }; + var indexSampler = function (frame) { + return Math.round(frame.length / 2); + }; + function dataSample(seriesType) { + return { + seriesType: seriesType, + // FIXME:TS never used, so comment it + // modifyOutputEnd: true, + reset: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var sampling = seriesModel.get('sampling'); + var coordSys = seriesModel.coordinateSystem; + var count = data.count(); + // Only cartesian2d support down sampling. Disable it when there is few data. + if (count > 10 && coordSys.type === 'cartesian2d' && sampling) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var extent = baseAxis.getExtent(); + var dpr = api.getDevicePixelRatio(); + // Coordinste system has been resized + var size = Math.abs(extent[1] - extent[0]) * (dpr || 1); + var rate = Math.round(count / size); + if (isFinite(rate) && rate > 1) { + if (sampling === 'lttb') { + seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate)); + } + var sampler = void 0; + if (isString(sampling)) { + sampler = samplers[sampling]; + } else if (isFunction(sampling)) { + sampler = sampling; + } + if (sampler) { + // Only support sample the first dim mapped from value axis. + seriesModel.setData(data.downSample(data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler)); + } + } + } + } + }; + } + + function install$2(registers) { + registers.registerChartView(LineView); + registers.registerSeriesModel(LineSeriesModel); + registers.registerLayout(pointsLayout('line', true)); + registers.registerVisual({ + seriesType: 'line', + reset: function (seriesModel) { + var data = seriesModel.getData(); + // Visual coding for legend + var lineStyle = seriesModel.getModel('lineStyle').getLineStyle(); + if (lineStyle && !lineStyle.stroke) { + // Fill in visual should be palette color if + // has color callback + lineStyle.stroke = data.getVisual('style').fill; + } + data.setVisual('legendLineStyle', lineStyle); + } + }); + // Down sample after filter + registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('line')); + } + + var BaseBarSeriesModel = /** @class */function (_super) { + __extends(BaseBarSeriesModel, _super); + function BaseBarSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = BaseBarSeriesModel.type; + return _this; + } + BaseBarSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesData(null, this, { + useEncodeDefaulter: true + }); + }; + BaseBarSeriesModel.prototype.getMarkerPosition = function (value, dims, startingAtTick) { + var coordSys = this.coordinateSystem; + if (coordSys && coordSys.clampData) { + // PENDING if clamp ? + var clampData_1 = coordSys.clampData(value); + var pt_1 = coordSys.dataToPoint(clampData_1); + if (startingAtTick) { + each(coordSys.getAxes(), function (axis, idx) { + // If axis type is category, use tick coords instead + if (axis.type === 'category' && dims != null) { + var tickCoords = axis.getTicksCoords(); + var alignTicksWithLabel = axis.getTickModel().get('alignWithLabel'); + var targetTickId = clampData_1[idx]; + // The index of rightmost tick of markArea is 1 larger than x1/y1 index + var isEnd = dims[idx] === 'x1' || dims[idx] === 'y1'; + if (isEnd && !alignTicksWithLabel) { + targetTickId += 1; + } + // The only contains one tick, tickCoords is + // like [{coord: 0, tickValue: 0}, {coord: 0}] + // to the length should always be larger than 1 + if (tickCoords.length < 2) { + return; + } else if (tickCoords.length === 2) { + // The left value and right value of the axis are + // the same. coord is 0 in both items. Use the max + // value of the axis as the coord + pt_1[idx] = axis.toGlobalCoord(axis.getExtent()[isEnd ? 1 : 0]); + return; + } + var leftCoord = void 0; + var coord = void 0; + var stepTickValue = 1; + for (var i = 0; i < tickCoords.length; i++) { + var tickCoord = tickCoords[i].coord; + // The last item of tickCoords doesn't contain + // tickValue + var tickValue = i === tickCoords.length - 1 ? tickCoords[i - 1].tickValue + stepTickValue : tickCoords[i].tickValue; + if (tickValue === targetTickId) { + coord = tickCoord; + break; + } else if (tickValue < targetTickId) { + leftCoord = tickCoord; + } else if (leftCoord != null && tickValue > targetTickId) { + coord = (tickCoord + leftCoord) / 2; + break; + } + if (i === 1) { + // Here we assume the step of category axes is + // the same + stepTickValue = tickValue - tickCoords[0].tickValue; + } + } + if (coord == null) { + if (!leftCoord) { + // targetTickId is smaller than all tick ids in the + // visible area, use the leftmost tick coord + coord = tickCoords[0].coord; + } else if (leftCoord) { + // targetTickId is larger than all tick ids in the + // visible area, use the rightmost tick coord + coord = tickCoords[tickCoords.length - 1].coord; + } + } + pt_1[idx] = axis.toGlobalCoord(coord); + } + }); + } else { + var data = this.getData(); + var offset = data.getLayout('offset'); + var size = data.getLayout('size'); + var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1; + pt_1[offsetIndex] += offset + size / 2; + } + return pt_1; + } + return [NaN, NaN]; + }; + BaseBarSeriesModel.type = 'series.__base_bar__'; + BaseBarSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + // stack: null + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + barMinHeight: 0, + barMinAngle: 0, + // cursor: null, + large: false, + largeThreshold: 400, + progressive: 3e3, + progressiveChunkMode: 'mod' + }; + return BaseBarSeriesModel; + }(SeriesModel); + SeriesModel.registerClass(BaseBarSeriesModel); + + var BarSeriesModel = /** @class */function (_super) { + __extends(BarSeriesModel, _super); + function BarSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = BarSeriesModel.type; + return _this; + } + BarSeriesModel.prototype.getInitialData = function () { + return createSeriesData(null, this, { + useEncodeDefaulter: true, + createInvertedIndices: !!this.get('realtimeSort', true) || null + }); + }; + /** + * @override + */ + BarSeriesModel.prototype.getProgressive = function () { + // Do not support progressive in normal mode. + return this.get('large') ? this.get('progressive') : false; + }; + /** + * @override + */ + BarSeriesModel.prototype.getProgressiveThreshold = function () { + // Do not support progressive in normal mode. + var progressiveThreshold = this.get('progressiveThreshold'); + var largeThreshold = this.get('largeThreshold'); + if (largeThreshold > progressiveThreshold) { + progressiveThreshold = largeThreshold; + } + return progressiveThreshold; + }; + BarSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) { + return selectors.rect(data.getItemLayout(dataIndex)); + }; + BarSeriesModel.type = 'series.bar'; + BarSeriesModel.dependencies = ['grid', 'polar']; + BarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, { + // If clipped + // Only available on cartesian2d + clip: true, + roundCap: false, + showBackground: false, + backgroundStyle: { + color: 'rgba(180, 180, 180, 0.2)', + borderColor: null, + borderWidth: 0, + borderType: 'solid', + borderRadius: 0, + shadowBlur: 0, + shadowColor: null, + shadowOffsetX: 0, + shadowOffsetY: 0, + opacity: 1 + }, + select: { + itemStyle: { + borderColor: '#212121' + } + }, + realtimeSort: false + }); + return BarSeriesModel; + }(BaseBarSeriesModel); + + /** + * Sausage: similar to sector, but have half circle on both sides + */ + var SausageShape = /** @class */function () { + function SausageShape() { + this.cx = 0; + this.cy = 0; + this.r0 = 0; + this.r = 0; + this.startAngle = 0; + this.endAngle = Math.PI * 2; + this.clockwise = true; + } + return SausageShape; + }(); + var SausagePath = /** @class */function (_super) { + __extends(SausagePath, _super); + function SausagePath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'sausage'; + return _this; + } + SausagePath.prototype.getDefaultShape = function () { + return new SausageShape(); + }; + SausagePath.prototype.buildPath = function (ctx, shape) { + var cx = shape.cx; + var cy = shape.cy; + var r0 = Math.max(shape.r0 || 0, 0); + var r = Math.max(shape.r, 0); + var dr = (r - r0) * 0.5; + var rCenter = r0 + dr; + var startAngle = shape.startAngle; + var endAngle = shape.endAngle; + var clockwise = shape.clockwise; + var PI2 = Math.PI * 2; + var lessThanCircle = clockwise ? endAngle - startAngle < PI2 : startAngle - endAngle < PI2; + if (!lessThanCircle) { + // Normalize angles + startAngle = endAngle - (clockwise ? PI2 : -PI2); + } + var unitStartX = Math.cos(startAngle); + var unitStartY = Math.sin(startAngle); + var unitEndX = Math.cos(endAngle); + var unitEndY = Math.sin(endAngle); + if (lessThanCircle) { + ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy); + ctx.arc(unitStartX * rCenter + cx, unitStartY * rCenter + cy, dr, -Math.PI + startAngle, startAngle, !clockwise); + } else { + ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy); + } + ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise); + ctx.arc(unitEndX * rCenter + cx, unitEndY * rCenter + cy, dr, endAngle - Math.PI * 2, endAngle - Math.PI, !clockwise); + if (r0 !== 0) { + ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise); + } + // ctx.closePath(); + }; + + return SausagePath; + }(Path); + + function createSectorCalculateTextPosition(positionMapping, opts) { + opts = opts || {}; + var isRoundCap = opts.isRoundCap; + return function (out, opts, boundingRect) { + var textPosition = opts.position; + if (!textPosition || textPosition instanceof Array) { + return calculateTextPosition(out, opts, boundingRect); + } + var mappedSectorPosition = positionMapping(textPosition); + var distance = opts.distance != null ? opts.distance : 5; + var sector = this.shape; + var cx = sector.cx; + var cy = sector.cy; + var r = sector.r; + var r0 = sector.r0; + var middleR = (r + r0) / 2; + var startAngle = sector.startAngle; + var endAngle = sector.endAngle; + var middleAngle = (startAngle + endAngle) / 2; + var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0; + var mathCos = Math.cos; + var mathSin = Math.sin; + // base position: top-left + var x = cx + r * mathCos(startAngle); + var y = cy + r * mathSin(startAngle); + var textAlign = 'left'; + var textVerticalAlign = 'top'; + switch (mappedSectorPosition) { + case 'startArc': + x = cx + (r0 - distance) * mathCos(middleAngle); + y = cy + (r0 - distance) * mathSin(middleAngle); + textAlign = 'center'; + textVerticalAlign = 'top'; + break; + case 'insideStartArc': + x = cx + (r0 + distance) * mathCos(middleAngle); + y = cy + (r0 + distance) * mathSin(middleAngle); + textAlign = 'center'; + textVerticalAlign = 'bottom'; + break; + case 'startAngle': + x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, distance + extraDist, false); + y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, distance + extraDist, false); + textAlign = 'right'; + textVerticalAlign = 'middle'; + break; + case 'insideStartAngle': + x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, -distance + extraDist, false); + y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, -distance + extraDist, false); + textAlign = 'left'; + textVerticalAlign = 'middle'; + break; + case 'middle': + x = cx + middleR * mathCos(middleAngle); + y = cy + middleR * mathSin(middleAngle); + textAlign = 'center'; + textVerticalAlign = 'middle'; + break; + case 'endArc': + x = cx + (r + distance) * mathCos(middleAngle); + y = cy + (r + distance) * mathSin(middleAngle); + textAlign = 'center'; + textVerticalAlign = 'bottom'; + break; + case 'insideEndArc': + x = cx + (r - distance) * mathCos(middleAngle); + y = cy + (r - distance) * mathSin(middleAngle); + textAlign = 'center'; + textVerticalAlign = 'top'; + break; + case 'endAngle': + x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, distance + extraDist, true); + y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, distance + extraDist, true); + textAlign = 'left'; + textVerticalAlign = 'middle'; + break; + case 'insideEndAngle': + x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, -distance + extraDist, true); + y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, -distance + extraDist, true); + textAlign = 'right'; + textVerticalAlign = 'middle'; + break; + default: + return calculateTextPosition(out, opts, boundingRect); + } + out = out || {}; + out.x = x; + out.y = y; + out.align = textAlign; + out.verticalAlign = textVerticalAlign; + return out; + }; + } + function setSectorTextRotation(sector, textPosition, positionMapping, rotateType) { + if (isNumber(rotateType)) { + // user-set rotation + sector.setTextConfig({ + rotation: rotateType + }); + return; + } else if (isArray(textPosition)) { + // user-set position, use 0 as auto rotation + sector.setTextConfig({ + rotation: 0 + }); + return; + } + var shape = sector.shape; + var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle; + var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle; + var middleAngle = (startAngle + endAngle) / 2; + var anchorAngle; + var mappedSectorPosition = positionMapping(textPosition); + switch (mappedSectorPosition) { + case 'startArc': + case 'insideStartArc': + case 'middle': + case 'insideEndArc': + case 'endArc': + anchorAngle = middleAngle; + break; + case 'startAngle': + case 'insideStartAngle': + anchorAngle = startAngle; + break; + case 'endAngle': + case 'insideEndAngle': + anchorAngle = endAngle; + break; + default: + sector.setTextConfig({ + rotation: 0 + }); + return; + } + var rotate = Math.PI * 1.5 - anchorAngle; + /** + * TODO: labels with rotate > Math.PI / 2 should be rotate another + * half round flipped to increase readability. However, only middle + * position supports this for now, because in other positions, the + * anchor point is not at the center of the text, so the positions + * after rotating is not as expected. + */ + if (mappedSectorPosition === 'middle' && rotate > Math.PI / 2 && rotate < Math.PI * 1.5) { + rotate -= Math.PI; + } + sector.setTextConfig({ + rotation: rotate + }); + } + function adjustAngleDistanceX(angle, distance, isEnd) { + return distance * Math.sin(angle) * (isEnd ? -1 : 1); + } + function adjustAngleDistanceY(angle, distance, isEnd) { + return distance * Math.cos(angle) * (isEnd ? 1 : -1); + } + + function getSectorCornerRadius(model, shape, zeroIfNull) { + var cornerRadius = model.get('borderRadius'); + if (cornerRadius == null) { + return zeroIfNull ? { + cornerRadius: 0 + } : null; + } + if (!isArray(cornerRadius)) { + cornerRadius = [cornerRadius, cornerRadius, cornerRadius, cornerRadius]; + } + var dr = Math.abs(shape.r || 0 - shape.r0 || 0); + return { + cornerRadius: map(cornerRadius, function (cr) { + return parsePercent(cr, dr); + }) + }; + } + + var mathMax$6 = Math.max; + var mathMin$6 = Math.min; + function getClipArea(coord, data) { + var coordSysClipArea = coord.getArea && coord.getArea(); + if (isCoordinateSystemType(coord, 'cartesian2d')) { + var baseAxis = coord.getBaseAxis(); + // When boundaryGap is false or using time axis. bar may exceed the grid. + // We should not clip this part. + // See test/bar2.html + if (baseAxis.type !== 'category' || !baseAxis.onBand) { + var expandWidth = data.getLayout('bandWidth'); + if (baseAxis.isHorizontal()) { + coordSysClipArea.x -= expandWidth; + coordSysClipArea.width += expandWidth * 2; + } else { + coordSysClipArea.y -= expandWidth; + coordSysClipArea.height += expandWidth * 2; + } + } + } + return coordSysClipArea; + } + var BarView = /** @class */function (_super) { + __extends(BarView, _super); + function BarView() { + var _this = _super.call(this) || this; + _this.type = BarView.type; + _this._isFirstFrame = true; + return _this; + } + BarView.prototype.render = function (seriesModel, ecModel, api, payload) { + this._model = seriesModel; + this._removeOnRenderedListener(api); + this._updateDrawMode(seriesModel); + var coordinateSystemType = seriesModel.get('coordinateSystem'); + if (coordinateSystemType === 'cartesian2d' || coordinateSystemType === 'polar') { + // Clear previously rendered progressive elements. + this._progressiveEls = null; + this._isLargeDraw ? this._renderLarge(seriesModel, ecModel, api) : this._renderNormal(seriesModel, ecModel, api, payload); + } else if ("development" !== 'production') { + warn('Only cartesian2d and polar supported for bar.'); + } + }; + BarView.prototype.incrementalPrepareRender = function (seriesModel) { + this._clear(); + this._updateDrawMode(seriesModel); + // incremental also need to clip, otherwise might be overlow. + // But must not set clip in each frame, otherwise all of the children will be marked redraw. + this._updateLargeClip(seriesModel); + }; + BarView.prototype.incrementalRender = function (params, seriesModel) { + // Reset + this._progressiveEls = []; + // Do not support progressive in normal mode. + this._incrementalRenderLarge(params, seriesModel); + }; + BarView.prototype.eachRendered = function (cb) { + traverseElements(this._progressiveEls || this.group, cb); + }; + BarView.prototype._updateDrawMode = function (seriesModel) { + var isLargeDraw = seriesModel.pipelineContext.large; + if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) { + this._isLargeDraw = isLargeDraw; + this._clear(); + } + }; + BarView.prototype._renderNormal = function (seriesModel, ecModel, api, payload) { + var group = this.group; + var data = seriesModel.getData(); + var oldData = this._data; + var coord = seriesModel.coordinateSystem; + var baseAxis = coord.getBaseAxis(); + var isHorizontalOrRadial; + if (coord.type === 'cartesian2d') { + isHorizontalOrRadial = baseAxis.isHorizontal(); + } else if (coord.type === 'polar') { + isHorizontalOrRadial = baseAxis.dim === 'angle'; + } + var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null; + var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord); + if (realtimeSortCfg) { + this._enableRealtimeSort(realtimeSortCfg, data, api); + } + var needsClip = seriesModel.get('clip', true) || realtimeSortCfg; + var coordSysClipArea = getClipArea(coord, data); + // If there is clipPath created in large mode. Remove it. + group.removeClipPath(); + // We don't use clipPath in normal mode because we needs a perfect animation + // And don't want the label are clipped. + var roundCap = seriesModel.get('roundCap', true); + var drawBackground = seriesModel.get('showBackground', true); + var backgroundModel = seriesModel.getModel('backgroundStyle'); + var barBorderRadius = backgroundModel.get('borderRadius') || 0; + var bgEls = []; + var oldBgEls = this._backgroundEls; + var isInitSort = payload && payload.isInitSort; + var isChangeOrder = payload && payload.type === 'changeAxisOrder'; + function createBackground(dataIndex) { + var bgLayout = getLayout[coord.type](data, dataIndex); + var bgEl = createBackgroundEl(coord, isHorizontalOrRadial, bgLayout); + bgEl.useStyle(backgroundModel.getItemStyle()); + // Only cartesian2d support borderRadius. + if (coord.type === 'cartesian2d') { + bgEl.setShape('r', barBorderRadius); + } else { + bgEl.setShape('cornerRadius', barBorderRadius); + } + bgEls[dataIndex] = bgEl; + return bgEl; + } + data.diff(oldData).add(function (dataIndex) { + var itemModel = data.getItemModel(dataIndex); + var layout = getLayout[coord.type](data, dataIndex, itemModel); + if (drawBackground) { + createBackground(dataIndex); + } + // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in "axisProxy". + if (!data.hasValue(dataIndex) || !isValidLayout[coord.type](layout)) { + return; + } + var isClipped = false; + if (needsClip) { + // Clip will modify the layout params. + // And return a boolean to determine if the shape are fully clipped. + isClipped = clip[coord.type](coordSysClipArea, layout); + } + var el = elementCreator[coord.type](seriesModel, data, dataIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, false, roundCap); + if (realtimeSortCfg) { + /** + * Force label animation because even if the element is + * ignored because it's clipped, it may not be clipped after + * changing order. Then, if not using forceLabelAnimation, + * the label animation was never started, in which case, + * the label will be the final value and doesn't have label + * animation. + */ + el.forceLabelAnimation = true; + } + updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar'); + if (isInitSort) { + el.attr({ + shape: layout + }); + } else if (realtimeSortCfg) { + updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, dataIndex, isHorizontalOrRadial, false, false); + } else { + initProps(el, { + shape: layout + }, seriesModel, dataIndex); + } + data.setItemGraphicEl(dataIndex, el); + group.add(el); + el.ignore = isClipped; + }).update(function (newIndex, oldIndex) { + var itemModel = data.getItemModel(newIndex); + var layout = getLayout[coord.type](data, newIndex, itemModel); + if (drawBackground) { + var bgEl = void 0; + if (oldBgEls.length === 0) { + bgEl = createBackground(oldIndex); + } else { + bgEl = oldBgEls[oldIndex]; + bgEl.useStyle(backgroundModel.getItemStyle()); + // Only cartesian2d support borderRadius. + if (coord.type === 'cartesian2d') { + bgEl.setShape('r', barBorderRadius); + } else { + bgEl.setShape('cornerRadius', barBorderRadius); + } + bgEls[newIndex] = bgEl; + } + var bgLayout = getLayout[coord.type](data, newIndex); + var shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord); + updateProps(bgEl, { + shape: shape + }, animationModel, newIndex); + } + var el = oldData.getItemGraphicEl(oldIndex); + if (!data.hasValue(newIndex) || !isValidLayout[coord.type](layout)) { + group.remove(el); + return; + } + var isClipped = false; + if (needsClip) { + isClipped = clip[coord.type](coordSysClipArea, layout); + if (isClipped) { + group.remove(el); + } + } + if (!el) { + el = elementCreator[coord.type](seriesModel, data, newIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, !!el, roundCap); + } else { + saveOldStyle(el); + } + if (realtimeSortCfg) { + el.forceLabelAnimation = true; + } + if (isChangeOrder) { + var textEl = el.getTextContent(); + if (textEl) { + var labelInnerStore = labelInner(textEl); + if (labelInnerStore.prevValue != null) { + /** + * Set preValue to be value so that no new label + * should be started, otherwise, it will take a full + * `animationDurationUpdate` time to finish the + * animation, which is not expected. + */ + labelInnerStore.prevValue = labelInnerStore.value; + } + } + } + // Not change anything if only order changed. + // Especially not change label. + else { + updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar'); + } + if (isInitSort) { + el.attr({ + shape: layout + }); + } else if (realtimeSortCfg) { + updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, newIndex, isHorizontalOrRadial, true, isChangeOrder); + } else { + updateProps(el, { + shape: layout + }, seriesModel, newIndex, null); + } + data.setItemGraphicEl(newIndex, el); + el.ignore = isClipped; + group.add(el); + }).remove(function (dataIndex) { + var el = oldData.getItemGraphicEl(dataIndex); + el && removeElementWithFadeOut(el, seriesModel, dataIndex); + }).execute(); + var bgGroup = this._backgroundGroup || (this._backgroundGroup = new Group()); + bgGroup.removeAll(); + for (var i = 0; i < bgEls.length; ++i) { + bgGroup.add(bgEls[i]); + } + group.add(bgGroup); + this._backgroundEls = bgEls; + this._data = data; + }; + BarView.prototype._renderLarge = function (seriesModel, ecModel, api) { + this._clear(); + createLarge(seriesModel, this.group); + this._updateLargeClip(seriesModel); + }; + BarView.prototype._incrementalRenderLarge = function (params, seriesModel) { + this._removeBackground(); + createLarge(seriesModel, this.group, this._progressiveEls, true); + }; + BarView.prototype._updateLargeClip = function (seriesModel) { + // Use clipPath in large mode. + var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel); + var group = this.group; + if (clipPath) { + group.setClipPath(clipPath); + } else { + group.removeClipPath(); + } + }; + BarView.prototype._enableRealtimeSort = function (realtimeSortCfg, data, api) { + var _this = this; + // If no data in the first frame, wait for data to initSort + if (!data.count()) { + return; + } + var baseAxis = realtimeSortCfg.baseAxis; + if (this._isFirstFrame) { + this._dispatchInitSort(data, realtimeSortCfg, api); + this._isFirstFrame = false; + } else { + var orderMapping_1 = function (idx) { + var el = data.getItemGraphicEl(idx); + var shape = el && el.shape; + return shape && + // The result should be consistent with the initial sort by data value. + // Do not support the case that both positive and negative exist. + Math.abs(baseAxis.isHorizontal() ? shape.height : shape.width) + // If data is NaN, shape.xxx may be NaN, so use || 0 here in case + || 0; + }; + this._onRendered = function () { + _this._updateSortWithinSameData(data, orderMapping_1, baseAxis, api); + }; + api.getZr().on('rendered', this._onRendered); + } + }; + BarView.prototype._dataSort = function (data, baseAxis, orderMapping) { + var info = []; + data.each(data.mapDimension(baseAxis.dim), function (ordinalNumber, dataIdx) { + var mappedValue = orderMapping(dataIdx); + mappedValue = mappedValue == null ? NaN : mappedValue; + info.push({ + dataIndex: dataIdx, + mappedValue: mappedValue, + ordinalNumber: ordinalNumber + }); + }); + info.sort(function (a, b) { + // If NaN, it will be treated as min val. + return b.mappedValue - a.mappedValue; + }); + return { + ordinalNumbers: map(info, function (item) { + return item.ordinalNumber; + }) + }; + }; + BarView.prototype._isOrderChangedWithinSameData = function (data, orderMapping, baseAxis) { + var scale = baseAxis.scale; + var ordinalDataDim = data.mapDimension(baseAxis.dim); + var lastValue = Number.MAX_VALUE; + for (var tickNum = 0, len = scale.getOrdinalMeta().categories.length; tickNum < len; ++tickNum) { + var rawIdx = data.rawIndexOf(ordinalDataDim, scale.getRawOrdinalNumber(tickNum)); + var value = rawIdx < 0 + // If some tick have no bar, the tick will be treated as min. + ? Number.MIN_VALUE + // PENDING: if dataZoom on baseAxis exits, is it a performance issue? + : orderMapping(data.indexOfRawIndex(rawIdx)); + if (value > lastValue) { + return true; + } + lastValue = value; + } + return false; + }; + /* + * Consider the case when A and B changed order, whose representing + * bars are both out of sight, we don't wish to trigger reorder action + * as long as the order in the view doesn't change. + */ + BarView.prototype._isOrderDifferentInView = function (orderInfo, baseAxis) { + var scale = baseAxis.scale; + var extent = scale.getExtent(); + var tickNum = Math.max(0, extent[0]); + var tickMax = Math.min(extent[1], scale.getOrdinalMeta().categories.length - 1); + for (; tickNum <= tickMax; ++tickNum) { + if (orderInfo.ordinalNumbers[tickNum] !== scale.getRawOrdinalNumber(tickNum)) { + return true; + } + } + }; + BarView.prototype._updateSortWithinSameData = function (data, orderMapping, baseAxis, api) { + if (!this._isOrderChangedWithinSameData(data, orderMapping, baseAxis)) { + return; + } + var sortInfo = this._dataSort(data, baseAxis, orderMapping); + if (this._isOrderDifferentInView(sortInfo, baseAxis)) { + this._removeOnRenderedListener(api); + api.dispatchAction({ + type: 'changeAxisOrder', + componentType: baseAxis.dim + 'Axis', + axisId: baseAxis.index, + sortInfo: sortInfo + }); + } + }; + BarView.prototype._dispatchInitSort = function (data, realtimeSortCfg, api) { + var baseAxis = realtimeSortCfg.baseAxis; + var sortResult = this._dataSort(data, baseAxis, function (dataIdx) { + return data.get(data.mapDimension(realtimeSortCfg.otherAxis.dim), dataIdx); + }); + api.dispatchAction({ + type: 'changeAxisOrder', + componentType: baseAxis.dim + 'Axis', + isInitSort: true, + axisId: baseAxis.index, + sortInfo: sortResult + }); + }; + BarView.prototype.remove = function (ecModel, api) { + this._clear(this._model); + this._removeOnRenderedListener(api); + }; + BarView.prototype.dispose = function (ecModel, api) { + this._removeOnRenderedListener(api); + }; + BarView.prototype._removeOnRenderedListener = function (api) { + if (this._onRendered) { + api.getZr().off('rendered', this._onRendered); + this._onRendered = null; + } + }; + BarView.prototype._clear = function (model) { + var group = this.group; + var data = this._data; + if (model && model.isAnimationEnabled() && data && !this._isLargeDraw) { + this._removeBackground(); + this._backgroundEls = []; + data.eachItemGraphicEl(function (el) { + removeElementWithFadeOut(el, model, getECData(el).dataIndex); + }); + } else { + group.removeAll(); + } + this._data = null; + this._isFirstFrame = true; + }; + BarView.prototype._removeBackground = function () { + this.group.remove(this._backgroundGroup); + this._backgroundGroup = null; + }; + BarView.type = 'bar'; + return BarView; + }(ChartView); + var clip = { + cartesian2d: function (coordSysBoundingRect, layout) { + var signWidth = layout.width < 0 ? -1 : 1; + var signHeight = layout.height < 0 ? -1 : 1; + // Needs positive width and height + if (signWidth < 0) { + layout.x += layout.width; + layout.width = -layout.width; + } + if (signHeight < 0) { + layout.y += layout.height; + layout.height = -layout.height; + } + var coordSysX2 = coordSysBoundingRect.x + coordSysBoundingRect.width; + var coordSysY2 = coordSysBoundingRect.y + coordSysBoundingRect.height; + var x = mathMax$6(layout.x, coordSysBoundingRect.x); + var x2 = mathMin$6(layout.x + layout.width, coordSysX2); + var y = mathMax$6(layout.y, coordSysBoundingRect.y); + var y2 = mathMin$6(layout.y + layout.height, coordSysY2); + var xClipped = x2 < x; + var yClipped = y2 < y; + // When xClipped or yClipped, the element will be marked as `ignore`. + // But we should also place the element at the edge of the coord sys bounding rect. + // Because if data changed and the bar shows again, its transition animation + // will begin at this place. + layout.x = xClipped && x > coordSysX2 ? x2 : x; + layout.y = yClipped && y > coordSysY2 ? y2 : y; + layout.width = xClipped ? 0 : x2 - x; + layout.height = yClipped ? 0 : y2 - y; + // Reverse back + if (signWidth < 0) { + layout.x += layout.width; + layout.width = -layout.width; + } + if (signHeight < 0) { + layout.y += layout.height; + layout.height = -layout.height; + } + return xClipped || yClipped; + }, + polar: function (coordSysClipArea, layout) { + var signR = layout.r0 <= layout.r ? 1 : -1; + // Make sure r is larger than r0 + if (signR < 0) { + var tmp = layout.r; + layout.r = layout.r0; + layout.r0 = tmp; + } + var r = mathMin$6(layout.r, coordSysClipArea.r); + var r0 = mathMax$6(layout.r0, coordSysClipArea.r0); + layout.r = r; + layout.r0 = r0; + var clipped = r - r0 < 0; + // Reverse back + if (signR < 0) { + var tmp = layout.r; + layout.r = layout.r0; + layout.r0 = tmp; + } + return clipped; + } + }; + var elementCreator = { + cartesian2d: function (seriesModel, data, newIndex, layout, isHorizontal, animationModel, axisModel, isUpdate, roundCap) { + var rect = new Rect({ + shape: extend({}, layout), + z2: 1 + }); + rect.__dataIndex = newIndex; + rect.name = 'item'; + if (animationModel) { + var rectShape = rect.shape; + var animateProperty = isHorizontal ? 'height' : 'width'; + rectShape[animateProperty] = 0; + } + return rect; + }, + polar: function (seriesModel, data, newIndex, layout, isRadial, animationModel, axisModel, isUpdate, roundCap) { + var ShapeClass = !isRadial && roundCap ? SausagePath : Sector; + var sector = new ShapeClass({ + shape: layout, + z2: 1 + }); + sector.name = 'item'; + var positionMap = createPolarPositionMapping(isRadial); + sector.calculateTextPosition = createSectorCalculateTextPosition(positionMap, { + isRoundCap: ShapeClass === SausagePath + }); + // Animation + if (animationModel) { + var sectorShape = sector.shape; + var animateProperty = isRadial ? 'r' : 'endAngle'; + var animateTarget = {}; + sectorShape[animateProperty] = isRadial ? layout.r0 : layout.startAngle; + animateTarget[animateProperty] = layout[animateProperty]; + (isUpdate ? updateProps : initProps)(sector, { + shape: animateTarget + // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue + }, animationModel); + } + return sector; + } + }; + function shouldRealtimeSort(seriesModel, coordSys) { + var realtimeSortOption = seriesModel.get('realtimeSort', true); + var baseAxis = coordSys.getBaseAxis(); + if ("development" !== 'production') { + if (realtimeSortOption) { + if (baseAxis.type !== 'category') { + warn('`realtimeSort` will not work because this bar series is not based on a category axis.'); + } + if (coordSys.type !== 'cartesian2d') { + warn('`realtimeSort` will not work because this bar series is not on cartesian2d.'); + } + } + } + if (realtimeSortOption && baseAxis.type === 'category' && coordSys.type === 'cartesian2d') { + return { + baseAxis: baseAxis, + otherAxis: coordSys.getOtherAxis(baseAxis) + }; + } + } + function updateRealtimeAnimation(realtimeSortCfg, seriesAnimationModel, el, layout, newIndex, isHorizontal, isUpdate, isChangeOrder) { + var seriesTarget; + var axisTarget; + if (isHorizontal) { + axisTarget = { + x: layout.x, + width: layout.width + }; + seriesTarget = { + y: layout.y, + height: layout.height + }; + } else { + axisTarget = { + y: layout.y, + height: layout.height + }; + seriesTarget = { + x: layout.x, + width: layout.width + }; + } + if (!isChangeOrder) { + // Keep the original growth animation if only axis order changed. + // Not start a new animation. + (isUpdate ? updateProps : initProps)(el, { + shape: seriesTarget + }, seriesAnimationModel, newIndex, null); + } + var axisAnimationModel = seriesAnimationModel ? realtimeSortCfg.baseAxis.model : null; + (isUpdate ? updateProps : initProps)(el, { + shape: axisTarget + }, axisAnimationModel, newIndex); + } + function checkPropertiesNotValid(obj, props) { + for (var i = 0; i < props.length; i++) { + if (!isFinite(obj[props[i]])) { + return true; + } + } + return false; + } + var rectPropties = ['x', 'y', 'width', 'height']; + var polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle']; + var isValidLayout = { + cartesian2d: function (layout) { + return !checkPropertiesNotValid(layout, rectPropties); + }, + polar: function (layout) { + return !checkPropertiesNotValid(layout, polarPropties); + } + }; + var getLayout = { + // itemModel is only used to get borderWidth, which is not needed + // when calculating bar background layout. + cartesian2d: function (data, dataIndex, itemModel) { + var layout = data.getItemLayout(dataIndex); + var fixedLineWidth = itemModel ? getLineWidth(itemModel, layout) : 0; + // fix layout with lineWidth + var signX = layout.width > 0 ? 1 : -1; + var signY = layout.height > 0 ? 1 : -1; + return { + x: layout.x + signX * fixedLineWidth / 2, + y: layout.y + signY * fixedLineWidth / 2, + width: layout.width - signX * fixedLineWidth, + height: layout.height - signY * fixedLineWidth + }; + }, + polar: function (data, dataIndex, itemModel) { + var layout = data.getItemLayout(dataIndex); + return { + cx: layout.cx, + cy: layout.cy, + r0: layout.r0, + r: layout.r, + startAngle: layout.startAngle, + endAngle: layout.endAngle, + clockwise: layout.clockwise + }; + } + }; + function isZeroOnPolar(layout) { + return layout.startAngle != null && layout.endAngle != null && layout.startAngle === layout.endAngle; + } + function createPolarPositionMapping(isRadial) { + return function (isRadial) { + var arcOrAngle = isRadial ? 'Arc' : 'Angle'; + return function (position) { + switch (position) { + case 'start': + case 'insideStart': + case 'end': + case 'insideEnd': + return position + arcOrAngle; + default: + return position; + } + }; + }(isRadial); + } + function updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, isPolar) { + var style = data.getItemVisual(dataIndex, 'style'); + if (!isPolar) { + var borderRadius = itemModel.get(['itemStyle', 'borderRadius']) || 0; + el.setShape('r', borderRadius); + } else if (!seriesModel.get('roundCap')) { + var sectorShape = el.shape; + var cornerRadius = getSectorCornerRadius(itemModel.getModel('itemStyle'), sectorShape, true); + extend(sectorShape, cornerRadius); + el.setShape(sectorShape); + } + el.useStyle(style); + var cursorStyle = itemModel.getShallow('cursor'); + cursorStyle && el.attr('cursor', cursorStyle); + var labelPositionOutside = isPolar ? isHorizontalOrRadial ? layout.r >= layout.r0 ? 'endArc' : 'startArc' : layout.endAngle >= layout.startAngle ? 'endAngle' : 'startAngle' : isHorizontalOrRadial ? layout.height >= 0 ? 'bottom' : 'top' : layout.width >= 0 ? 'right' : 'left'; + var labelStatesModels = getLabelStatesModels(itemModel); + setLabelStyle(el, labelStatesModels, { + labelFetcher: seriesModel, + labelDataIndex: dataIndex, + defaultText: getDefaultLabel(seriesModel.getData(), dataIndex), + inheritColor: style.fill, + defaultOpacity: style.opacity, + defaultOutsidePosition: labelPositionOutside + }); + var label = el.getTextContent(); + if (isPolar && label) { + var position = itemModel.get(['label', 'position']); + el.textConfig.inside = position === 'middle' ? true : null; + setSectorTextRotation(el, position === 'outside' ? labelPositionOutside : position, createPolarPositionMapping(isHorizontalOrRadial), itemModel.get(['label', 'rotate'])); + } + setLabelValueAnimation(label, labelStatesModels, seriesModel.getRawValue(dataIndex), function (value) { + return getDefaultInterpolatedLabel(data, value); + }); + var emphasisModel = itemModel.getModel(['emphasis']); + toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + setStatesStylesFromModel(el, itemModel); + if (isZeroOnPolar(layout)) { + el.style.fill = 'none'; + el.style.stroke = 'none'; + each(el.states, function (state) { + if (state.style) { + state.style.fill = state.style.stroke = 'none'; + } + }); + } + } + // In case width or height are too small. + function getLineWidth(itemModel, rawLayout) { + // Has no border. + var borderColor = itemModel.get(['itemStyle', 'borderColor']); + if (!borderColor || borderColor === 'none') { + return 0; + } + var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0; + // width or height may be NaN for empty data + var width = isNaN(rawLayout.width) ? Number.MAX_VALUE : Math.abs(rawLayout.width); + var height = isNaN(rawLayout.height) ? Number.MAX_VALUE : Math.abs(rawLayout.height); + return Math.min(lineWidth, width, height); + } + var LagePathShape = /** @class */function () { + function LagePathShape() {} + return LagePathShape; + }(); + var LargePath = /** @class */function (_super) { + __extends(LargePath, _super); + function LargePath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'largeBar'; + return _this; + } + LargePath.prototype.getDefaultShape = function () { + return new LagePathShape(); + }; + LargePath.prototype.buildPath = function (ctx, shape) { + // Drawing lines is more efficient than drawing + // a whole line or drawing rects. + var points = shape.points; + var baseDimIdx = this.baseDimIdx; + var valueDimIdx = 1 - this.baseDimIdx; + var startPoint = []; + var size = []; + var barWidth = this.barWidth; + for (var i = 0; i < points.length; i += 3) { + size[baseDimIdx] = barWidth; + size[valueDimIdx] = points[i + 2]; + startPoint[baseDimIdx] = points[i + baseDimIdx]; + startPoint[valueDimIdx] = points[i + valueDimIdx]; + ctx.rect(startPoint[0], startPoint[1], size[0], size[1]); + } + }; + return LargePath; + }(Path); + function createLarge(seriesModel, group, progressiveEls, incremental) { + // TODO support polar + var data = seriesModel.getData(); + var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0; + var largeDataIndices = data.getLayout('largeDataIndices'); + var barWidth = data.getLayout('size'); + var backgroundModel = seriesModel.getModel('backgroundStyle'); + var bgPoints = data.getLayout('largeBackgroundPoints'); + if (bgPoints) { + var bgEl = new LargePath({ + shape: { + points: bgPoints + }, + incremental: !!incremental, + silent: true, + z2: 0 + }); + bgEl.baseDimIdx = baseDimIdx; + bgEl.largeDataIndices = largeDataIndices; + bgEl.barWidth = barWidth; + bgEl.useStyle(backgroundModel.getItemStyle()); + group.add(bgEl); + progressiveEls && progressiveEls.push(bgEl); + } + var el = new LargePath({ + shape: { + points: data.getLayout('largePoints') + }, + incremental: !!incremental, + ignoreCoarsePointer: true, + z2: 1 + }); + el.baseDimIdx = baseDimIdx; + el.largeDataIndices = largeDataIndices; + el.barWidth = barWidth; + group.add(el); + el.useStyle(data.getVisual('style')); + // Enable tooltip and user mouse/touch event handlers. + getECData(el).seriesIndex = seriesModel.seriesIndex; + if (!seriesModel.get('silent')) { + el.on('mousedown', largePathUpdateDataIndex); + el.on('mousemove', largePathUpdateDataIndex); + } + progressiveEls && progressiveEls.push(el); + } + // Use throttle to avoid frequently traverse to find dataIndex. + var largePathUpdateDataIndex = throttle(function (event) { + var largePath = this; + var dataIndex = largePathFindDataIndex(largePath, event.offsetX, event.offsetY); + getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null; + }, 30, false); + function largePathFindDataIndex(largePath, x, y) { + var baseDimIdx = largePath.baseDimIdx; + var valueDimIdx = 1 - baseDimIdx; + var points = largePath.shape.points; + var largeDataIndices = largePath.largeDataIndices; + var startPoint = []; + var size = []; + var barWidth = largePath.barWidth; + for (var i = 0, len = points.length / 3; i < len; i++) { + var ii = i * 3; + size[baseDimIdx] = barWidth; + size[valueDimIdx] = points[ii + 2]; + startPoint[baseDimIdx] = points[ii + baseDimIdx]; + startPoint[valueDimIdx] = points[ii + valueDimIdx]; + if (size[valueDimIdx] < 0) { + startPoint[valueDimIdx] += size[valueDimIdx]; + size[valueDimIdx] = -size[valueDimIdx]; + } + if (x >= startPoint[0] && x <= startPoint[0] + size[0] && y >= startPoint[1] && y <= startPoint[1] + size[1]) { + return largeDataIndices[i]; + } + } + return -1; + } + function createBackgroundShape(isHorizontalOrRadial, layout, coord) { + if (isCoordinateSystemType(coord, 'cartesian2d')) { + var rectShape = layout; + var coordLayout = coord.getArea(); + return { + x: isHorizontalOrRadial ? rectShape.x : coordLayout.x, + y: isHorizontalOrRadial ? coordLayout.y : rectShape.y, + width: isHorizontalOrRadial ? rectShape.width : coordLayout.width, + height: isHorizontalOrRadial ? coordLayout.height : rectShape.height + }; + } else { + var coordLayout = coord.getArea(); + var sectorShape = layout; + return { + cx: coordLayout.cx, + cy: coordLayout.cy, + r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0, + r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r, + startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0, + endAngle: isHorizontalOrRadial ? sectorShape.endAngle : Math.PI * 2 + }; + } + } + function createBackgroundEl(coord, isHorizontalOrRadial, layout) { + var ElementClz = coord.type === 'polar' ? Sector : Rect; + return new ElementClz({ + shape: createBackgroundShape(isHorizontalOrRadial, layout, coord), + silent: true, + z2: 0 + }); + } + + function install$3(registers) { + registers.registerChartView(BarView); + registers.registerSeriesModel(BarSeriesModel); + registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'bar')); + // Do layout after other overall layout, which can prepare some information. + registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('bar')); + // Down sample after filter + registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('bar')); + /** + * @payload + * @property {string} [componentType=series] + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ + registers.registerAction({ + type: 'changeAxisOrder', + event: 'changeAxisOrder', + update: 'update' + }, function (payload, ecModel) { + var componentType = payload.componentType || 'series'; + ecModel.eachComponent({ + mainType: componentType, + query: payload + }, function (componentModel) { + if (payload.sortInfo) { + componentModel.axis.setCategorySortInfo(payload.sortInfo); + } + }); + }); + } + + var PI2$8 = Math.PI * 2; + var RADIAN = Math.PI / 180; + function getViewRect(seriesModel, api) { + return getLayoutRect(seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + } + function getBasicPieLayout(seriesModel, api) { + var viewRect = getViewRect(seriesModel, api); + // center can be string or number when coordinateSystem is specified + var center = seriesModel.get('center'); + var radius = seriesModel.get('radius'); + if (!isArray(radius)) { + radius = [0, radius]; + } + var width = parsePercent$1(viewRect.width, api.getWidth()); + var height = parsePercent$1(viewRect.height, api.getHeight()); + var size = Math.min(width, height); + var r0 = parsePercent$1(radius[0], size / 2); + var r = parsePercent$1(radius[1], size / 2); + var cx; + var cy; + var coordSys = seriesModel.coordinateSystem; + if (coordSys) { + // percentage is not allowed when coordinate system is specified + var point = coordSys.dataToPoint(center); + cx = point[0] || 0; + cy = point[1] || 0; + } else { + if (!isArray(center)) { + center = [center, center]; + } + cx = parsePercent$1(center[0], width) + viewRect.x; + cy = parsePercent$1(center[1], height) + viewRect.y; + } + return { + cx: cx, + cy: cy, + r0: r0, + r: r + }; + } + function pieLayout(seriesType, ecModel, api) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + var viewRect = getViewRect(seriesModel, api); + var _a = getBasicPieLayout(seriesModel, api), + cx = _a.cx, + cy = _a.cy, + r = _a.r, + r0 = _a.r0; + var startAngle = -seriesModel.get('startAngle') * RADIAN; + var endAngle = seriesModel.get('endAngle'); + var padAngle = seriesModel.get('padAngle') * RADIAN; + endAngle = endAngle === 'auto' ? startAngle - PI2$8 : -endAngle * RADIAN; + var minAngle = seriesModel.get('minAngle') * RADIAN; + var minAndPadAngle = minAngle + padAngle; + var validDataCount = 0; + data.each(valueDim, function (value) { + !isNaN(value) && validDataCount++; + }); + var sum = data.getSum(valueDim); + // Sum may be 0 + var unitRadian = Math.PI / (sum || validDataCount) * 2; + var clockwise = seriesModel.get('clockwise'); + var roseType = seriesModel.get('roseType'); + var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); + // [0...max] + var extent = data.getDataExtent(valueDim); + extent[0] = 0; + var dir = clockwise ? 1 : -1; + var angles = [startAngle, endAngle]; + var halfPadAngle = dir * padAngle / 2; + normalizeArcAngles(angles, !clockwise); + startAngle = angles[0], endAngle = angles[1]; + var angleRange = Math.abs(endAngle - startAngle); + // In the case some sector angle is smaller than minAngle + var restAngle = angleRange; + var valueSumLargerThanMinAngle = 0; + var currentAngle = startAngle; + data.setLayout({ + viewRect: viewRect, + r: r + }); + data.each(valueDim, function (value, idx) { + var angle; + if (isNaN(value)) { + data.setItemLayout(idx, { + angle: NaN, + startAngle: NaN, + endAngle: NaN, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: r0, + r: roseType ? NaN : r + }); + return; + } + // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样? + if (roseType !== 'area') { + angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian; + } else { + angle = angleRange / validDataCount; + } + if (angle < minAndPadAngle) { + angle = minAndPadAngle; + restAngle -= minAndPadAngle; + } else { + valueSumLargerThanMinAngle += value; + } + var endAngle = currentAngle + dir * angle; + // calculate display angle + var actualStartAngle = 0; + var actualEndAngle = 0; + if (padAngle > angle) { + actualStartAngle = currentAngle + dir * angle / 2; + actualEndAngle = actualStartAngle; + } else { + actualStartAngle = currentAngle + halfPadAngle; + actualEndAngle = endAngle - halfPadAngle; + } + data.setItemLayout(idx, { + angle: angle, + startAngle: actualStartAngle, + endAngle: actualEndAngle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: r0, + r: roseType ? linearMap(value, extent, [r0, r]) : r + }); + currentAngle = endAngle; + }); + // Some sector is constrained by minAngle and padAngle + // Rest sectors needs recalculate angle + if (restAngle < PI2$8 && validDataCount) { + // Average the angle if rest angle is not enough after all angles is + // Constrained by minAngle and padAngle + if (restAngle <= 1e-3) { + var angle_1 = angleRange / validDataCount; + data.each(valueDim, function (value, idx) { + if (!isNaN(value)) { + var layout_1 = data.getItemLayout(idx); + layout_1.angle = angle_1; + var actualStartAngle = 0; + var actualEndAngle = 0; + if (angle_1 < padAngle) { + actualStartAngle = startAngle + dir * (idx + 1 / 2) * angle_1; + actualEndAngle = actualStartAngle; + } else { + actualStartAngle = startAngle + dir * idx * angle_1 + halfPadAngle; + actualEndAngle = startAngle + dir * (idx + 1) * angle_1 - halfPadAngle; + } + layout_1.startAngle = actualStartAngle; + layout_1.endAngle = actualEndAngle; + } + }); + } else { + unitRadian = restAngle / valueSumLargerThanMinAngle; + currentAngle = startAngle; + data.each(valueDim, function (value, idx) { + if (!isNaN(value)) { + var layout_2 = data.getItemLayout(idx); + var angle = layout_2.angle === minAndPadAngle ? minAndPadAngle : value * unitRadian; + var actualStartAngle = 0; + var actualEndAngle = 0; + if (angle < padAngle) { + actualStartAngle = currentAngle + dir * angle / 2; + actualEndAngle = actualStartAngle; + } else { + actualStartAngle = currentAngle + halfPadAngle; + actualEndAngle = currentAngle + dir * angle - halfPadAngle; + } + layout_2.startAngle = actualStartAngle; + layout_2.endAngle = actualEndAngle; + currentAngle += dir * angle; + } + }); + } + } + }); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function dataFilter(seriesType) { + return { + seriesType: seriesType, + reset: function (seriesModel, ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + var data = seriesModel.getData(); + data.filterSelf(function (idx) { + var name = data.getName(idx); + // If in any legend component the status is not selected. + for (var i = 0; i < legendModels.length; i++) { + // @ts-ignore FIXME: LegendModel + if (!legendModels[i].isSelected(name)) { + return false; + } + } + return true; + }); + } + }; + } + + var RADIAN$1 = Math.PI / 180; + function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) { + if (list.length < 2) { + return; + } + function recalculateXOnSemiToAlignOnEllipseCurve(semi) { + var rB = semi.rB; + var rB2 = rB * rB; + for (var i = 0; i < semi.list.length; i++) { + var item = semi.list[i]; + var dy = Math.abs(item.label.y - cy); + // horizontal r is always same with original r because x is not changed. + var rA = r + item.len; + var rA2 = rA * rA; + // Use ellipse implicit function to calculate x + var dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2); + var newX = cx + (dx + item.len2) * dir; + var deltaX = newX - item.label.x; + var newTargetWidth = item.targetTextWidth - deltaX * dir; + // text x is changed, so need to recalculate width. + constrainTextWidth(item, newTargetWidth, true); + item.label.x = newX; + } + } + // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve. + function recalculateX(items) { + // Extremes of + var topSemi = { + list: [], + maxY: 0 + }; + var bottomSemi = { + list: [], + maxY: 0 + }; + for (var i = 0; i < items.length; i++) { + if (items[i].labelAlignTo !== 'none') { + continue; + } + var item = items[i]; + var semi = item.label.y > cy ? bottomSemi : topSemi; + var dy = Math.abs(item.label.y - cy); + if (dy >= semi.maxY) { + var dx = item.label.x - cx - item.len2 * dir; + // horizontal r is always same with original r because x is not changed. + var rA = r + item.len; + // Canculate rB based on the topest / bottemest label. + var rB = Math.abs(dx) < rA ? Math.sqrt(dy * dy / (1 - dx * dx / rA / rA)) : rA; + semi.rB = rB; + semi.maxY = dy; + } + semi.list.push(item); + } + recalculateXOnSemiToAlignOnEllipseCurve(topSemi); + recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi); + } + var len = list.length; + for (var i = 0; i < len; i++) { + if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') { + var dx = list[i].label.x - farthestX; + list[i].linePoints[1][0] += dx; + list[i].label.x = farthestX; + } + } + if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) { + recalculateX(list); + } + } + function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) { + var leftList = []; + var rightList = []; + var leftmostX = Number.MAX_VALUE; + var rightmostX = -Number.MAX_VALUE; + for (var i = 0; i < labelLayoutList.length; i++) { + var label = labelLayoutList[i].label; + if (isPositionCenter(labelLayoutList[i])) { + continue; + } + if (label.x < cx) { + leftmostX = Math.min(leftmostX, label.x); + leftList.push(labelLayoutList[i]); + } else { + rightmostX = Math.max(rightmostX, label.x); + rightList.push(labelLayoutList[i]); + } + } + for (var i = 0; i < labelLayoutList.length; i++) { + var layout = labelLayoutList[i]; + if (!isPositionCenter(layout) && layout.linePoints) { + if (layout.labelStyleWidth != null) { + continue; + } + var label = layout.label; + var linePoints = layout.linePoints; + var targetTextWidth = void 0; + if (layout.labelAlignTo === 'edge') { + if (label.x < cx) { + targetTextWidth = linePoints[2][0] - layout.labelDistance - viewLeft - layout.edgeDistance; + } else { + targetTextWidth = viewLeft + viewWidth - layout.edgeDistance - linePoints[2][0] - layout.labelDistance; + } + } else if (layout.labelAlignTo === 'labelLine') { + if (label.x < cx) { + targetTextWidth = leftmostX - viewLeft - layout.bleedMargin; + } else { + targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin; + } + } else { + if (label.x < cx) { + targetTextWidth = label.x - viewLeft - layout.bleedMargin; + } else { + targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin; + } + } + layout.targetTextWidth = targetTextWidth; + constrainTextWidth(layout, targetTextWidth); + } + } + adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX); + adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX); + for (var i = 0; i < labelLayoutList.length; i++) { + var layout = labelLayoutList[i]; + if (!isPositionCenter(layout) && layout.linePoints) { + var label = layout.label; + var linePoints = layout.linePoints; + var isAlignToEdge = layout.labelAlignTo === 'edge'; + var padding = label.style.padding; + var paddingH = padding ? padding[1] + padding[3] : 0; + // textRect.width already contains paddingH if bgColor is set + var extraPaddingH = label.style.backgroundColor ? 0 : paddingH; + var realTextWidth = layout.rect.width + extraPaddingH; + var dist = linePoints[1][0] - linePoints[2][0]; + if (isAlignToEdge) { + if (label.x < cx) { + linePoints[2][0] = viewLeft + layout.edgeDistance + realTextWidth + layout.labelDistance; + } else { + linePoints[2][0] = viewLeft + viewWidth - layout.edgeDistance - realTextWidth - layout.labelDistance; + } + } else { + if (label.x < cx) { + linePoints[2][0] = label.x + layout.labelDistance; + } else { + linePoints[2][0] = label.x - layout.labelDistance; + } + linePoints[1][0] = linePoints[2][0] + dist; + } + linePoints[1][1] = linePoints[2][1] = label.y; + } + } + } + /** + * Set max width of each label, and then wrap each label to the max width. + * + * @param layout label layout + * @param availableWidth max width for the label to display + * @param forceRecalculate recaculate the text layout even if the current width + * is smaller than `availableWidth`. This is useful when the text was previously + * wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in + * which case, previous wrapping should be redo. + */ + function constrainTextWidth(layout, availableWidth, forceRecalculate) { + if (forceRecalculate === void 0) { + forceRecalculate = false; + } + if (layout.labelStyleWidth != null) { + // User-defined style.width has the highest priority. + return; + } + var label = layout.label; + var style = label.style; + var textRect = layout.rect; + var bgColor = style.backgroundColor; + var padding = style.padding; + var paddingH = padding ? padding[1] + padding[3] : 0; + var overflow = style.overflow; + // textRect.width already contains paddingH if bgColor is set + var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH); + if (availableWidth < oldOuterWidth || forceRecalculate) { + var oldHeight = textRect.height; + if (overflow && overflow.match('break')) { + // Temporarily set background to be null to calculate + // the bounding box without background. + label.setStyle('backgroundColor', null); + // Set constraining width + label.setStyle('width', availableWidth - paddingH); + // This is the real bounding box of the text without padding. + var innerRect = label.getBoundingRect(); + label.setStyle('width', Math.ceil(innerRect.width)); + label.setStyle('backgroundColor', bgColor); + } else { + var availableInnerWidth = availableWidth - paddingH; + var newWidth = availableWidth < oldOuterWidth + // Current text is too wide, use `availableWidth` as max width. + ? availableInnerWidth : + // Current available width is enough, but the text may have + // already been wrapped with a smaller available width. + forceRecalculate ? availableInnerWidth > layout.unconstrainedWidth + // Current available is larger than text width, + // so don't constrain width (otherwise it may have + // empty space in the background). + ? null + // Current available is smaller than text width, so + // use the current available width as constraining + // width. + : availableInnerWidth + // Current available width is enough, so no need to + // constrain. + : null; + label.setStyle('width', newWidth); + } + var newRect = label.getBoundingRect(); + textRect.width = newRect.width; + var margin = (label.style.margin || 0) + 2.1; + textRect.height = newRect.height + margin; + textRect.y -= (textRect.height - oldHeight) / 2; + } + } + function isPositionCenter(sectorShape) { + // Not change x for center label + return sectorShape.position === 'center'; + } + function pieLabelLayout(seriesModel) { + var data = seriesModel.getData(); + var labelLayoutList = []; + var cx; + var cy; + var hasLabelRotate = false; + var minShowLabelRadian = (seriesModel.get('minShowLabelAngle') || 0) * RADIAN$1; + var viewRect = data.getLayout('viewRect'); + var r = data.getLayout('r'); + var viewWidth = viewRect.width; + var viewLeft = viewRect.x; + var viewTop = viewRect.y; + var viewHeight = viewRect.height; + function setNotShow(el) { + el.ignore = true; + } + function isLabelShown(label) { + if (!label.ignore) { + return true; + } + for (var key in label.states) { + if (label.states[key].ignore === false) { + return true; + } + } + return false; + } + data.each(function (idx) { + var sector = data.getItemGraphicEl(idx); + var sectorShape = sector.shape; + var label = sector.getTextContent(); + var labelLine = sector.getTextGuideLine(); + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label'); + // Use position in normal or emphasis + var labelPosition = labelModel.get('position') || itemModel.get(['emphasis', 'label', 'position']); + var labelDistance = labelModel.get('distanceToLabelLine'); + var labelAlignTo = labelModel.get('alignTo'); + var edgeDistance = parsePercent$1(labelModel.get('edgeDistance'), viewWidth); + var bleedMargin = labelModel.get('bleedMargin'); + var labelLineModel = itemModel.getModel('labelLine'); + var labelLineLen = labelLineModel.get('length'); + labelLineLen = parsePercent$1(labelLineLen, viewWidth); + var labelLineLen2 = labelLineModel.get('length2'); + labelLineLen2 = parsePercent$1(labelLineLen2, viewWidth); + if (Math.abs(sectorShape.endAngle - sectorShape.startAngle) < minShowLabelRadian) { + each(label.states, setNotShow); + label.ignore = true; + if (labelLine) { + each(labelLine.states, setNotShow); + labelLine.ignore = true; + } + return; + } + if (!isLabelShown(label)) { + return; + } + var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2; + var nx = Math.cos(midAngle); + var ny = Math.sin(midAngle); + var textX; + var textY; + var linePoints; + var textAlign; + cx = sectorShape.cx; + cy = sectorShape.cy; + var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; + if (labelPosition === 'center') { + textX = sectorShape.cx; + textY = sectorShape.cy; + textAlign = 'center'; + } else { + var x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx; + var y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy; + textX = x1 + nx * 3; + textY = y1 + ny * 3; + if (!isLabelInside) { + // For roseType + var x2 = x1 + nx * (labelLineLen + r - sectorShape.r); + var y2 = y1 + ny * (labelLineLen + r - sectorShape.r); + var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2; + var y3 = y2; + if (labelAlignTo === 'edge') { + // Adjust textX because text align of edge is opposite + textX = nx < 0 ? viewLeft + edgeDistance : viewLeft + viewWidth - edgeDistance; + } else { + textX = x3 + (nx < 0 ? -labelDistance : labelDistance); + } + textY = y3; + linePoints = [[x1, y1], [x2, y2], [x3, y3]]; + } + textAlign = isLabelInside ? 'center' : labelAlignTo === 'edge' ? nx > 0 ? 'right' : 'left' : nx > 0 ? 'left' : 'right'; + } + var PI = Math.PI; + var labelRotate = 0; + var rotate = labelModel.get('rotate'); + if (isNumber(rotate)) { + labelRotate = rotate * (PI / 180); + } else if (labelPosition === 'center') { + labelRotate = 0; + } else if (rotate === 'radial' || rotate === true) { + var radialAngle = nx < 0 ? -midAngle + PI : -midAngle; + labelRotate = radialAngle; + } else if (rotate === 'tangential' && labelPosition !== 'outside' && labelPosition !== 'outer') { + var rad = Math.atan2(nx, ny); + if (rad < 0) { + rad = PI * 2 + rad; + } + var isDown = ny > 0; + if (isDown) { + rad = PI + rad; + } + labelRotate = rad - PI; + } + hasLabelRotate = !!labelRotate; + label.x = textX; + label.y = textY; + label.rotation = labelRotate; + label.setStyle({ + verticalAlign: 'middle' + }); + // Not sectorShape the inside label + if (!isLabelInside) { + var textRect = label.getBoundingRect().clone(); + textRect.applyTransform(label.getComputedTransform()); + // Text has a default 1px stroke. Exclude this. + var margin = (label.style.margin || 0) + 2.1; + textRect.y -= margin / 2; + textRect.height += margin; + labelLayoutList.push({ + label: label, + labelLine: labelLine, + position: labelPosition, + len: labelLineLen, + len2: labelLineLen2, + minTurnAngle: labelLineModel.get('minTurnAngle'), + maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'), + surfaceNormal: new Point(nx, ny), + linePoints: linePoints, + textAlign: textAlign, + labelDistance: labelDistance, + labelAlignTo: labelAlignTo, + edgeDistance: edgeDistance, + bleedMargin: bleedMargin, + rect: textRect, + unconstrainedWidth: textRect.width, + labelStyleWidth: label.style.width + }); + } else { + label.setStyle({ + align: textAlign + }); + var selectState = label.states.select; + if (selectState) { + selectState.x += label.x; + selectState.y += label.y; + } + } + sector.setTextConfig({ + inside: isLabelInside + }); + }); + if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { + avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop); + } + for (var i = 0; i < labelLayoutList.length; i++) { + var layout = labelLayoutList[i]; + var label = layout.label; + var labelLine = layout.labelLine; + var notShowLabel = isNaN(label.x) || isNaN(label.y); + if (label) { + label.setStyle({ + align: layout.textAlign + }); + if (notShowLabel) { + each(label.states, setNotShow); + label.ignore = true; + } + var selectState = label.states.select; + if (selectState) { + selectState.x += label.x; + selectState.y += label.y; + } + } + if (labelLine) { + var linePoints = layout.linePoints; + if (notShowLabel || !linePoints) { + each(labelLine.states, setNotShow); + labelLine.ignore = true; + } else { + limitTurnAngle(linePoints, layout.minTurnAngle); + limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle); + labelLine.setShape({ + points: linePoints + }); + // Set the anchor to the midpoint of sector + label.__hostTarget.textGuideLineConfig = { + anchor: new Point(linePoints[0][0], linePoints[0][1]) + }; + } + } + } + } + + /** + * Piece of pie including Sector, Label, LabelLine + */ + var PiePiece = /** @class */function (_super) { + __extends(PiePiece, _super); + function PiePiece(data, idx, startAngle) { + var _this = _super.call(this) || this; + _this.z2 = 2; + var text = new ZRText(); + _this.setTextContent(text); + _this.updateData(data, idx, startAngle, true); + return _this; + } + PiePiece.prototype.updateData = function (data, idx, startAngle, firstCreate) { + var sector = this; + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var emphasisModel = itemModel.getModel('emphasis'); + var layout = data.getItemLayout(idx); + // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified. + // see `setItemLayout` in `pieLayout.ts`. + var sectorShape = extend(getSectorCornerRadius(itemModel.getModel('itemStyle'), layout, true), layout); + // Ignore NaN data. + if (isNaN(sectorShape.startAngle)) { + // Use NaN shape to avoid drawing shape. + sector.setShape(sectorShape); + return; + } + if (firstCreate) { + sector.setShape(sectorShape); + var animationType = seriesModel.getShallow('animationType'); + if (seriesModel.ecModel.ssr) { + // Use scale animation in SSR mode(opacity?) + // Because CSS SVG animation doesn't support very customized shape animation. + initProps(sector, { + scaleX: 0, + scaleY: 0 + }, seriesModel, { + dataIndex: idx, + isFrom: true + }); + sector.originX = sectorShape.cx; + sector.originY = sectorShape.cy; + } else if (animationType === 'scale') { + sector.shape.r = layout.r0; + initProps(sector, { + shape: { + r: layout.r + } + }, seriesModel, idx); + } + // Expansion + else { + if (startAngle != null) { + sector.setShape({ + startAngle: startAngle, + endAngle: startAngle + }); + initProps(sector, { + shape: { + startAngle: layout.startAngle, + endAngle: layout.endAngle + } + }, seriesModel, idx); + } else { + sector.shape.endAngle = layout.startAngle; + updateProps(sector, { + shape: { + endAngle: layout.endAngle + } + }, seriesModel, idx); + } + } + } else { + saveOldStyle(sector); + // Transition animation from the old shape + updateProps(sector, { + shape: sectorShape + }, seriesModel, idx); + } + sector.useStyle(data.getItemVisual(idx, 'style')); + setStatesStylesFromModel(sector, itemModel); + var midAngle = (layout.startAngle + layout.endAngle) / 2; + var offset = seriesModel.get('selectedOffset'); + var dx = Math.cos(midAngle) * offset; + var dy = Math.sin(midAngle) * offset; + var cursorStyle = itemModel.getShallow('cursor'); + cursorStyle && sector.attr('cursor', cursorStyle); + this._updateLabel(seriesModel, data, idx); + sector.ensureState('emphasis').shape = extend({ + r: layout.r + (emphasisModel.get('scale') ? emphasisModel.get('scaleSize') || 0 : 0) + }, getSectorCornerRadius(emphasisModel.getModel('itemStyle'), layout)); + extend(sector.ensureState('select'), { + x: dx, + y: dy, + shape: getSectorCornerRadius(itemModel.getModel(['select', 'itemStyle']), layout) + }); + extend(sector.ensureState('blur'), { + shape: getSectorCornerRadius(itemModel.getModel(['blur', 'itemStyle']), layout) + }); + var labelLine = sector.getTextGuideLine(); + var labelText = sector.getTextContent(); + labelLine && extend(labelLine.ensureState('select'), { + x: dx, + y: dy + }); + // TODO: needs dx, dy in zrender? + extend(labelText.ensureState('select'), { + x: dx, + y: dy + }); + toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }; + PiePiece.prototype._updateLabel = function (seriesModel, data, idx) { + var sector = this; + var itemModel = data.getItemModel(idx); + var labelLineModel = itemModel.getModel('labelLine'); + var style = data.getItemVisual(idx, 'style'); + var visualColor = style && style.fill; + var visualOpacity = style && style.opacity; + setLabelStyle(sector, getLabelStatesModels(itemModel), { + labelFetcher: data.hostModel, + labelDataIndex: idx, + inheritColor: visualColor, + defaultOpacity: visualOpacity, + defaultText: seriesModel.getFormattedLabel(idx, 'normal') || data.getName(idx) + }); + var labelText = sector.getTextContent(); + // Set textConfig on sector. + sector.setTextConfig({ + // reset position, rotation + position: null, + rotation: null + }); + // Make sure update style on labelText after setLabelStyle. + // Because setLabelStyle will replace a new style on it. + labelText.attr({ + z2: 10 + }); + var labelPosition = seriesModel.get(['label', 'position']); + if (labelPosition !== 'outside' && labelPosition !== 'outer') { + sector.removeTextGuideLine(); + } else { + var polyline = this.getTextGuideLine(); + if (!polyline) { + polyline = new Polyline(); + this.setTextGuideLine(polyline); + } + // Default use item visual color + setLabelLineStyle(this, getLabelLineStatesModels(itemModel), { + stroke: visualColor, + opacity: retrieve3(labelLineModel.get(['lineStyle', 'opacity']), visualOpacity, 1) + }); + } + }; + return PiePiece; + }(Sector); + // Pie view + var PieView = /** @class */function (_super) { + __extends(PieView, _super); + function PieView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.ignoreLabelLineUpdate = true; + return _this; + } + PieView.prototype.render = function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var oldData = this._data; + var group = this.group; + var startAngle; + // First render + if (!oldData && data.count() > 0) { + var shape = data.getItemLayout(0); + for (var s = 1; isNaN(shape && shape.startAngle) && s < data.count(); ++s) { + shape = data.getItemLayout(s); + } + if (shape) { + startAngle = shape.startAngle; + } + } + // remove empty-circle if it exists + if (this._emptyCircleSector) { + group.remove(this._emptyCircleSector); + } + // when all data are filtered, show lightgray empty circle + if (data.count() === 0 && seriesModel.get('showEmptyCircle')) { + var sector = new Sector({ + shape: getBasicPieLayout(seriesModel, api) + }); + sector.useStyle(seriesModel.getModel('emptyCircleStyle').getItemStyle()); + this._emptyCircleSector = sector; + group.add(sector); + } + data.diff(oldData).add(function (idx) { + var piePiece = new PiePiece(data, idx, startAngle); + data.setItemGraphicEl(idx, piePiece); + group.add(piePiece); + }).update(function (newIdx, oldIdx) { + var piePiece = oldData.getItemGraphicEl(oldIdx); + piePiece.updateData(data, newIdx, startAngle); + piePiece.off('click'); + group.add(piePiece); + data.setItemGraphicEl(newIdx, piePiece); + }).remove(function (idx) { + var piePiece = oldData.getItemGraphicEl(idx); + removeElementWithFadeOut(piePiece, seriesModel, idx); + }).execute(); + pieLabelLayout(seriesModel); + // Always use initial animation. + if (seriesModel.get('animationTypeUpdate') !== 'expansion') { + this._data = data; + } + }; + PieView.prototype.dispose = function () {}; + PieView.prototype.containPoint = function (point, seriesModel) { + var data = seriesModel.getData(); + var itemLayout = data.getItemLayout(0); + if (itemLayout) { + var dx = point[0] - itemLayout.cx; + var dy = point[1] - itemLayout.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + return radius <= itemLayout.r && radius >= itemLayout.r0; + } + }; + PieView.type = 'pie'; + return PieView; + }(ChartView); + + /** + * [Usage]: + * (1) + * createListSimply(seriesModel, ['value']); + * (2) + * createListSimply(seriesModel, { + * coordDimensions: ['value'], + * dimensionsCount: 5 + * }); + */ + function createSeriesDataSimply(seriesModel, opt, nameList) { + opt = isArray(opt) && { + coordDimensions: opt + } || extend({ + encodeDefine: seriesModel.getEncode() + }, opt); + var source = seriesModel.getSource(); + var dimensions = prepareSeriesDataSchema(source, opt).dimensions; + var list = new SeriesData(dimensions, seriesModel); + list.initData(source, nameList); + return list; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /** + * LegendVisualProvider is an bridge that pick encoded color from data and + * provide to the legend component. + */ + var LegendVisualProvider = /** @class */function () { + function LegendVisualProvider( + // Function to get data after filtered. It stores all the encoding info + getDataWithEncodedVisual, + // Function to get raw data before filtered. + getRawData) { + this._getDataWithEncodedVisual = getDataWithEncodedVisual; + this._getRawData = getRawData; + } + LegendVisualProvider.prototype.getAllNames = function () { + var rawData = this._getRawData(); + // We find the name from the raw data. In case it's filtered by the legend component. + // Normally, the name can be found in rawData, but can't be found in filtered data will display as gray. + return rawData.mapArray(rawData.getName); + }; + LegendVisualProvider.prototype.containName = function (name) { + var rawData = this._getRawData(); + return rawData.indexOfName(name) >= 0; + }; + LegendVisualProvider.prototype.indexOfName = function (name) { + // Only get data when necessary. + // Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet. + // Invoking Series#getData immediately will throw an error. + var dataWithEncodedVisual = this._getDataWithEncodedVisual(); + return dataWithEncodedVisual.indexOfName(name); + }; + LegendVisualProvider.prototype.getItemVisual = function (dataIndex, key) { + // Get encoded visual properties from final filtered data. + var dataWithEncodedVisual = this._getDataWithEncodedVisual(); + return dataWithEncodedVisual.getItemVisual(dataIndex, key); + }; + return LegendVisualProvider; + }(); + + var innerData = makeInner(); + var PieSeriesModel = /** @class */function (_super) { + __extends(PieSeriesModel, _super); + function PieSeriesModel() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * @overwrite + */ + PieSeriesModel.prototype.init = function (option) { + _super.prototype.init.apply(this, arguments); + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); + this._defaultLabelLine(option); + }; + /** + * @overwrite + */ + PieSeriesModel.prototype.mergeOption = function () { + _super.prototype.mergeOption.apply(this, arguments); + }; + /** + * @overwrite + */ + PieSeriesModel.prototype.getInitialData = function () { + return createSeriesDataSimply(this, { + coordDimensions: ['value'], + encodeDefaulter: curry(makeSeriesEncodeForNameBased, this) + }); + }; + /** + * @overwrite + */ + PieSeriesModel.prototype.getDataParams = function (dataIndex) { + var data = this.getData(); + // update seats when data is changed + var dataInner = innerData(data); + var seats = dataInner.seats; + if (!seats) { + var valueList_1 = []; + data.each(data.mapDimension('value'), function (value) { + valueList_1.push(value); + }); + seats = dataInner.seats = getPercentSeats(valueList_1, data.hostModel.get('percentPrecision')); + } + var params = _super.prototype.getDataParams.call(this, dataIndex); + // seats may be empty when sum is 0 + params.percent = seats[dataIndex] || 0; + params.$vars.push('percent'); + return params; + }; + PieSeriesModel.prototype._defaultLabelLine = function (option) { + // Extend labelLine emphasis + defaultEmphasis(option, 'labelLine', ['show']); + var labelLineNormalOpt = option.labelLine; + var labelLineEmphasisOpt = option.emphasis.labelLine; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show; + }; + PieSeriesModel.type = 'series.pie'; + PieSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + legendHoverLink: true, + colorBy: 'data', + // 默认全局居中 + center: ['50%', '50%'], + radius: [0, '75%'], + // 默认顺时针 + clockwise: true, + startAngle: 90, + endAngle: 'auto', + padAngle: 0, + // 最小角度改为0 + minAngle: 0, + // If the angle of a sector less than `minShowLabelAngle`, + // the label will not be displayed. + minShowLabelAngle: 0, + // 选中时扇区偏移量 + selectedOffset: 10, + // 选择模式,默认关闭,可选single,multiple + // selectedMode: false, + // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积) + // roseType: null, + percentPrecision: 2, + // If still show when all data zero. + stillShowZeroSum: true, + // cursor: null, + left: 0, + top: 0, + right: 0, + bottom: 0, + width: null, + height: null, + label: { + // color: 'inherit', + // If rotate around circle + rotate: 0, + show: true, + overflow: 'truncate', + // 'outer', 'inside', 'center' + position: 'outer', + // 'none', 'labelLine', 'edge'. Works only when position is 'outer' + alignTo: 'none', + // Closest distance between label and chart edge. + // Works only position is 'outer' and alignTo is 'edge'. + edgeDistance: '25%', + // Works only position is 'outer' and alignTo is not 'edge'. + bleedMargin: 10, + // Distance between text and label line. + distanceToLabelLine: 5 + // formatter: 标签文本格式器,同 tooltip.formatter,不支持异步回调 + // 默认使用全局文本样式,详见 textStyle + // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 + }, + + // Enabled when label.normal.position is 'outer' + labelLine: { + show: true, + // 引导线两段中的第一段长度 + length: 15, + // 引导线两段中的第二段长度 + length2: 15, + smooth: false, + minTurnAngle: 90, + maxSurfaceAngle: 90, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + }, + itemStyle: { + borderWidth: 1, + borderJoin: 'round' + }, + showEmptyCircle: true, + emptyCircleStyle: { + color: 'lightgray', + opacity: 1 + }, + labelLayout: { + // Hide the overlapped label. + hideOverlap: true + }, + emphasis: { + scale: true, + scaleSize: 5 + }, + // If use strategy to avoid label overlapping + avoidLabelOverlap: true, + // Animation type. Valid values: expansion, scale + animationType: 'expansion', + animationDuration: 1000, + // Animation type when update. Valid values: transition, expansion + animationTypeUpdate: 'transition', + animationEasingUpdate: 'cubicInOut', + animationDurationUpdate: 500, + animationEasing: 'cubicInOut' + }; + return PieSeriesModel; + }(SeriesModel); + + function negativeDataFilter(seriesType) { + return { + seriesType: seriesType, + reset: function (seriesModel, ecModel) { + var data = seriesModel.getData(); + data.filterSelf(function (idx) { + // handle negative value condition + var valueDim = data.mapDimension('value'); + var curValue = data.get(valueDim, idx); + if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) { + return false; + } + return true; + }); + } + }; + } + + function install$4(registers) { + registers.registerChartView(PieView); + registers.registerSeriesModel(PieSeriesModel); + createLegacyDataSelectAction('pie', registers.registerAction); + registers.registerLayout(curry(pieLayout, 'pie')); + registers.registerProcessor(dataFilter('pie')); + registers.registerProcessor(negativeDataFilter('pie')); + } + + var ScatterSeriesModel = /** @class */function (_super) { + __extends(ScatterSeriesModel, _super); + function ScatterSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ScatterSeriesModel.type; + _this.hasSymbolVisual = true; + return _this; + } + ScatterSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesData(null, this, { + useEncodeDefaulter: true + }); + }; + ScatterSeriesModel.prototype.getProgressive = function () { + var progressive = this.option.progressive; + if (progressive == null) { + // PENDING + return this.option.large ? 5e3 : this.get('progressive'); + } + return progressive; + }; + ScatterSeriesModel.prototype.getProgressiveThreshold = function () { + var progressiveThreshold = this.option.progressiveThreshold; + if (progressiveThreshold == null) { + // PENDING + return this.option.large ? 1e4 : this.get('progressiveThreshold'); + } + return progressiveThreshold; + }; + ScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) { + return selectors.point(data.getItemLayout(dataIndex)); + }; + ScatterSeriesModel.prototype.getZLevelKey = function () { + // Each progressive series has individual key. + return this.getData().count() > this.getProgressiveThreshold() ? this.id : ''; + }; + ScatterSeriesModel.type = 'series.scatter'; + ScatterSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar']; + ScatterSeriesModel.defaultOption = { + coordinateSystem: 'cartesian2d', + // zlevel: 0, + z: 2, + legendHoverLink: true, + symbolSize: 10, + // symbolRotate: null, // 图形旋转控制 + large: false, + // Available when large is true + largeThreshold: 2000, + // cursor: null, + itemStyle: { + opacity: 0.8 + // color: 各异 + }, + + emphasis: { + scale: true + }, + // If clip the overflow graphics + // Works on cartesian / polar series + clip: true, + select: { + itemStyle: { + borderColor: '#212121' + } + }, + universalTransition: { + divideShape: 'clone' + } + // progressive: null + }; + + return ScatterSeriesModel; + }(SeriesModel); + + var BOOST_SIZE_THRESHOLD = 4; + var LargeSymbolPathShape = /** @class */function () { + function LargeSymbolPathShape() {} + return LargeSymbolPathShape; + }(); + var LargeSymbolPath = /** @class */function (_super) { + __extends(LargeSymbolPath, _super); + function LargeSymbolPath(opts) { + var _this = _super.call(this, opts) || this; + _this._off = 0; + _this.hoverDataIdx = -1; + return _this; + } + LargeSymbolPath.prototype.getDefaultShape = function () { + return new LargeSymbolPathShape(); + }; + LargeSymbolPath.prototype.reset = function () { + this.notClear = false; + this._off = 0; + }; + LargeSymbolPath.prototype.buildPath = function (path, shape) { + var points = shape.points; + var size = shape.size; + var symbolProxy = this.symbolProxy; + var symbolProxyShape = symbolProxy.shape; + var ctx = path.getContext ? path.getContext() : path; + var canBoost = ctx && size[0] < BOOST_SIZE_THRESHOLD; + var softClipShape = this.softClipShape; + var i; + // Do draw in afterBrush. + if (canBoost) { + this._ctx = ctx; + return; + } + this._ctx = null; + for (i = this._off; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + if (isNaN(x) || isNaN(y)) { + continue; + } + if (softClipShape && !softClipShape.contain(x, y)) { + continue; + } + symbolProxyShape.x = x - size[0] / 2; + symbolProxyShape.y = y - size[1] / 2; + symbolProxyShape.width = size[0]; + symbolProxyShape.height = size[1]; + symbolProxy.buildPath(path, symbolProxyShape, true); + } + if (this.incremental) { + this._off = i; + this.notClear = true; + } + }; + LargeSymbolPath.prototype.afterBrush = function () { + var shape = this.shape; + var points = shape.points; + var size = shape.size; + var ctx = this._ctx; + var softClipShape = this.softClipShape; + var i; + if (!ctx) { + return; + } + // PENDING If style or other canvas status changed? + for (i = this._off; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + if (isNaN(x) || isNaN(y)) { + continue; + } + if (softClipShape && !softClipShape.contain(x, y)) { + continue; + } + // fillRect is faster than building a rect path and draw. + // And it support light globalCompositeOperation. + ctx.fillRect(x - size[0] / 2, y - size[1] / 2, size[0], size[1]); + } + if (this.incremental) { + this._off = i; + this.notClear = true; + } + }; + LargeSymbolPath.prototype.findDataIndex = function (x, y) { + // TODO ??? + // Consider transform + var shape = this.shape; + var points = shape.points; + var size = shape.size; + var w = Math.max(size[0], 4); + var h = Math.max(size[1], 4); + // Not consider transform + // Treat each element as a rect + // top down traverse + for (var idx = points.length / 2 - 1; idx >= 0; idx--) { + var i = idx * 2; + var x0 = points[i] - w / 2; + var y0 = points[i + 1] - h / 2; + if (x >= x0 && y >= y0 && x <= x0 + w && y <= y0 + h) { + return idx; + } + } + return -1; + }; + LargeSymbolPath.prototype.contain = function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + x = localPos[0]; + y = localPos[1]; + if (rect.contain(x, y)) { + // Cache found data index. + var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y); + return dataIdx >= 0; + } + this.hoverDataIdx = -1; + return false; + }; + LargeSymbolPath.prototype.getBoundingRect = function () { + // Ignore stroke for large symbol draw. + var rect = this._rect; + if (!rect) { + var shape = this.shape; + var points = shape.points; + var size = shape.size; + var w = size[0]; + var h = size[1]; + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + for (var i = 0; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + } + rect = this._rect = new BoundingRect(minX - w / 2, minY - h / 2, maxX - minX + w, maxY - minY + h); + } + return rect; + }; + return LargeSymbolPath; + }(Path); + var LargeSymbolDraw = /** @class */function () { + function LargeSymbolDraw() { + this.group = new Group(); + } + /** + * Update symbols draw by new data + */ + LargeSymbolDraw.prototype.updateData = function (data, opt) { + this._clear(); + var symbolEl = this._create(); + symbolEl.setShape({ + points: data.getLayout('points') + }); + this._setCommon(symbolEl, data, opt); + }; + LargeSymbolDraw.prototype.updateLayout = function (data) { + var points = data.getLayout('points'); + this.group.eachChild(function (child) { + if (child.startIndex != null) { + var len = (child.endIndex - child.startIndex) * 2; + var byteOffset = child.startIndex * 4 * 2; + points = new Float32Array(points.buffer, byteOffset, len); + } + child.setShape('points', points); + // Reset draw cursor. + child.reset(); + }); + }; + LargeSymbolDraw.prototype.incrementalPrepareUpdate = function (data) { + this._clear(); + }; + LargeSymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) { + var lastAdded = this._newAdded[0]; + var points = data.getLayout('points'); + var oldPoints = lastAdded && lastAdded.shape.points; + // Merging the exists. Each element has 1e4 points. + // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization) + if (oldPoints && oldPoints.length < 2e4) { + var oldLen = oldPoints.length; + var newPoints = new Float32Array(oldLen + points.length); + // Concat two array + newPoints.set(oldPoints); + newPoints.set(points, oldLen); + // Update endIndex + lastAdded.endIndex = taskParams.end; + lastAdded.setShape({ + points: newPoints + }); + } else { + // Clear + this._newAdded = []; + var symbolEl = this._create(); + symbolEl.startIndex = taskParams.start; + symbolEl.endIndex = taskParams.end; + symbolEl.incremental = true; + symbolEl.setShape({ + points: points + }); + this._setCommon(symbolEl, data, opt); + } + }; + LargeSymbolDraw.prototype.eachRendered = function (cb) { + this._newAdded[0] && cb(this._newAdded[0]); + }; + LargeSymbolDraw.prototype._create = function () { + var symbolEl = new LargeSymbolPath({ + cursor: 'default' + }); + symbolEl.ignoreCoarsePointer = true; + this.group.add(symbolEl); + this._newAdded.push(symbolEl); + return symbolEl; + }; + LargeSymbolDraw.prototype._setCommon = function (symbolEl, data, opt) { + var hostModel = data.hostModel; + opt = opt || {}; + var size = data.getVisual('symbolSize'); + symbolEl.setShape('size', size instanceof Array ? size : [size, size]); + symbolEl.softClipShape = opt.clipShape || null; + // Create symbolProxy to build path for each data + symbolEl.symbolProxy = createSymbol(data.getVisual('symbol'), 0, 0, 0, 0); + // Use symbolProxy setColor method + symbolEl.setColor = symbolEl.symbolProxy.setColor; + var extrudeShadow = symbolEl.shape.size[0] < BOOST_SIZE_THRESHOLD; + symbolEl.useStyle( + // Draw shadow when doing fillRect is extremely slow. + hostModel.getModel('itemStyle').getItemStyle(extrudeShadow ? ['color', 'shadowBlur', 'shadowColor'] : ['color'])); + var globalStyle = data.getVisual('style'); + var visualColor = globalStyle && globalStyle.fill; + if (visualColor) { + symbolEl.setColor(visualColor); + } + var ecData = getECData(symbolEl); + // Enable tooltip + // PENDING May have performance issue when path is extremely large + ecData.seriesIndex = hostModel.seriesIndex; + symbolEl.on('mousemove', function (e) { + ecData.dataIndex = null; + var dataIndex = symbolEl.hoverDataIdx; + if (dataIndex >= 0) { + // Provide dataIndex for tooltip + ecData.dataIndex = dataIndex + (symbolEl.startIndex || 0); + } + }); + }; + LargeSymbolDraw.prototype.remove = function () { + this._clear(); + }; + LargeSymbolDraw.prototype._clear = function () { + this._newAdded = []; + this.group.removeAll(); + }; + return LargeSymbolDraw; + }(); + + var ScatterView = /** @class */function (_super) { + __extends(ScatterView, _super); + function ScatterView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ScatterView.type; + return _this; + } + ScatterView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var symbolDraw = this._updateSymbolDraw(data, seriesModel); + symbolDraw.updateData(data, { + // TODO + // If this parameter should be a shape or a bounding volume + // shape will be more general. + // But bounding volume like bounding rect will be much faster in the contain calculation + clipShape: this._getClipShape(seriesModel) + }); + this._finished = true; + }; + ScatterView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var symbolDraw = this._updateSymbolDraw(data, seriesModel); + symbolDraw.incrementalPrepareUpdate(data); + this._finished = false; + }; + ScatterView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) { + this._symbolDraw.incrementalUpdate(taskParams, seriesModel.getData(), { + clipShape: this._getClipShape(seriesModel) + }); + this._finished = taskParams.end === seriesModel.getData().count(); + }; + ScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + // Must mark group dirty and make sure the incremental layer will be cleared + // PENDING + this.group.dirty(); + if (!this._finished || data.count() > 1e4) { + return { + update: true + }; + } else { + var res = pointsLayout('').reset(seriesModel, ecModel, api); + if (res.progress) { + res.progress({ + start: 0, + end: data.count(), + count: data.count() + }, data); + } + this._symbolDraw.updateLayout(data); + } + }; + ScatterView.prototype.eachRendered = function (cb) { + this._symbolDraw && this._symbolDraw.eachRendered(cb); + }; + ScatterView.prototype._getClipShape = function (seriesModel) { + if (!seriesModel.get('clip', true)) { + return; + } + var coordSys = seriesModel.coordinateSystem; + // PENDING make `0.1` configurable, for example, `clipTolerance`? + return coordSys && coordSys.getArea && coordSys.getArea(.1); + }; + ScatterView.prototype._updateSymbolDraw = function (data, seriesModel) { + var symbolDraw = this._symbolDraw; + var pipelineContext = seriesModel.pipelineContext; + var isLargeDraw = pipelineContext.large; + if (!symbolDraw || isLargeDraw !== this._isLargeDraw) { + symbolDraw && symbolDraw.remove(); + symbolDraw = this._symbolDraw = isLargeDraw ? new LargeSymbolDraw() : new SymbolDraw(); + this._isLargeDraw = isLargeDraw; + this.group.removeAll(); + } + this.group.add(symbolDraw.group); + return symbolDraw; + }; + ScatterView.prototype.remove = function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(true); + this._symbolDraw = null; + }; + ScatterView.prototype.dispose = function () {}; + ScatterView.type = 'scatter'; + return ScatterView; + }(ChartView); + + var GridModel = /** @class */function (_super) { + __extends(GridModel, _super); + function GridModel() { + return _super !== null && _super.apply(this, arguments) || this; + } + GridModel.type = 'grid'; + GridModel.dependencies = ['xAxis', 'yAxis']; + GridModel.layoutMode = 'box'; + GridModel.defaultOption = { + show: false, + // zlevel: 0, + z: 0, + left: '10%', + top: 60, + right: '10%', + bottom: 70, + // If grid size contain label + containLabel: false, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 1, + borderColor: '#ccc' + }; + return GridModel; + }(ComponentModel); + + var CartesianAxisModel = /** @class */function (_super) { + __extends(CartesianAxisModel, _super); + function CartesianAxisModel() { + return _super !== null && _super.apply(this, arguments) || this; + } + CartesianAxisModel.prototype.getCoordSysModel = function () { + return this.getReferringComponents('grid', SINGLE_REFERRING).models[0]; + }; + CartesianAxisModel.type = 'cartesian2dAxis'; + return CartesianAxisModel; + }(ComponentModel); + mixin(CartesianAxisModel, AxisModelCommonMixin); + + var defaultOption = { + show: true, + // zlevel: 0, + z: 0, + // Inverse the axis. + inverse: false, + // Axis name displayed. + name: '', + // 'start' | 'middle' | 'end' + nameLocation: 'end', + // By degree. By default auto rotate by nameLocation. + nameRotate: null, + nameTruncate: { + maxWidth: null, + ellipsis: '...', + placeholder: '.' + }, + // Use global text style by default. + nameTextStyle: {}, + // The gap between axisName and axisLine. + nameGap: 15, + // Default `false` to support tooltip. + silent: false, + // Default `false` to avoid legacy user event listener fail. + triggerEvent: false, + tooltip: { + show: false + }, + axisPointer: {}, + axisLine: { + show: true, + onZero: true, + onZeroAxisIndex: null, + lineStyle: { + color: '#6E7079', + width: 1, + type: 'solid' + }, + // The arrow at both ends the the axis. + symbol: ['none', 'none'], + symbolSize: [10, 15] + }, + axisTick: { + show: true, + // Whether axisTick is inside the grid or outside the grid. + inside: false, + // The length of axisTick. + length: 5, + lineStyle: { + width: 1 + } + }, + axisLabel: { + show: true, + // Whether axisLabel is inside the grid or outside the grid. + inside: false, + rotate: 0, + // true | false | null/undefined (auto) + showMinLabel: null, + // true | false | null/undefined (auto) + showMaxLabel: null, + margin: 8, + // formatter: null, + fontSize: 12 + }, + splitLine: { + show: true, + lineStyle: { + color: ['#E0E6F1'], + width: 1, + type: 'solid' + } + }, + splitArea: { + show: false, + areaStyle: { + color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)'] + } + } + }; + var categoryAxis = merge({ + // The gap at both ends of the axis. For categoryAxis, boolean. + boundaryGap: true, + // Set false to faster category collection. + deduplication: null, + // splitArea: { + // show: false + // }, + splitLine: { + show: false + }, + axisTick: { + // If tick is align with label when boundaryGap is true + alignWithLabel: false, + interval: 'auto' + }, + axisLabel: { + interval: 'auto' + } + }, defaultOption); + var valueAxis = merge({ + boundaryGap: [0, 0], + axisLine: { + // Not shown when other axis is categoryAxis in cartesian + show: 'auto' + }, + axisTick: { + // Not shown when other axis is categoryAxis in cartesian + show: 'auto' + }, + // TODO + // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60] + splitNumber: 5, + minorTick: { + // Minor tick, not available for cateogry axis. + show: false, + // Split number of minor ticks. The value should be in range of (0, 100) + splitNumber: 5, + // Length of minor tick + length: 3, + // Line style + lineStyle: { + // Default to be same with axisTick + } + }, + minorSplitLine: { + show: false, + lineStyle: { + color: '#F4F7FD', + width: 1 + } + } + }, defaultOption); + var timeAxis = merge({ + splitNumber: 6, + axisLabel: { + // To eliminate labels that are not nice + showMinLabel: false, + showMaxLabel: false, + rich: { + primary: { + fontWeight: 'bold' + } + } + }, + splitLine: { + show: false + } + }, valueAxis); + var logAxis = defaults({ + logBase: 10 + }, valueAxis); + var axisDefault = { + category: categoryAxis, + value: valueAxis, + time: timeAxis, + log: logAxis + }; + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var AXIS_TYPES = { + value: 1, + category: 1, + time: 1, + log: 1 + }; + + /** + * Generate sub axis model class + * @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ... + */ + function axisModelCreator(registers, axisName, BaseAxisModelClass, extraDefaultOption) { + each(AXIS_TYPES, function (v, axisType) { + var defaultOption = merge(merge({}, axisDefault[axisType], true), extraDefaultOption, true); + var AxisModel = /** @class */function (_super) { + __extends(AxisModel, _super); + function AxisModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = axisName + 'Axis.' + axisType; + return _this; + } + AxisModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { + var layoutMode = fetchLayoutMode(this); + var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; + var themeModel = ecModel.getTheme(); + merge(option, themeModel.get(axisType + 'Axis')); + merge(option, this.getDefaultOption()); + option.type = getAxisType(option); + if (layoutMode) { + mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }; + AxisModel.prototype.optionUpdated = function () { + var thisOption = this.option; + if (thisOption.type === 'category') { + this.__ordinalMeta = OrdinalMeta.createByAxisModel(this); + } + }; + /** + * Should not be called before all of 'getInitailData' finished. + * Because categories are collected during initializing data. + */ + AxisModel.prototype.getCategories = function (rawData) { + var option = this.option; + // FIXME + // warning if called before all of 'getInitailData' finished. + if (option.type === 'category') { + if (rawData) { + return option.data; + } + return this.__ordinalMeta.categories; + } + }; + AxisModel.prototype.getOrdinalMeta = function () { + return this.__ordinalMeta; + }; + AxisModel.type = axisName + 'Axis.' + axisType; + AxisModel.defaultOption = defaultOption; + return AxisModel; + }(BaseAxisModelClass); + registers.registerComponentModel(AxisModel); + }); + registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType); + } + function getAxisType(option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); + } + + var Cartesian = /** @class */function () { + function Cartesian(name) { + this.type = 'cartesian'; + this._dimList = []; + this._axes = {}; + this.name = name || ''; + } + Cartesian.prototype.getAxis = function (dim) { + return this._axes[dim]; + }; + Cartesian.prototype.getAxes = function () { + return map(this._dimList, function (dim) { + return this._axes[dim]; + }, this); + }; + Cartesian.prototype.getAxesByScale = function (scaleType) { + scaleType = scaleType.toLowerCase(); + return filter(this.getAxes(), function (axis) { + return axis.scale.type === scaleType; + }); + }; + Cartesian.prototype.addAxis = function (axis) { + var dim = axis.dim; + this._axes[dim] = axis; + this._dimList.push(dim); + }; + return Cartesian; + }(); + + var cartesian2DDimensions = ['x', 'y']; + function canCalculateAffineTransform(scale) { + return scale.type === 'interval' || scale.type === 'time'; + } + var Cartesian2D = /** @class */function (_super) { + __extends(Cartesian2D, _super); + function Cartesian2D() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'cartesian2d'; + _this.dimensions = cartesian2DDimensions; + return _this; + } + /** + * Calculate an affine transform matrix if two axes are time or value. + * It's mainly for accelartion on the large time series data. + */ + Cartesian2D.prototype.calcAffineTransform = function () { + this._transform = this._invTransform = null; + var xAxisScale = this.getAxis('x').scale; + var yAxisScale = this.getAxis('y').scale; + if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) { + return; + } + var xScaleExtent = xAxisScale.getExtent(); + var yScaleExtent = yAxisScale.getExtent(); + var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]); + var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]); + var xScaleSpan = xScaleExtent[1] - xScaleExtent[0]; + var yScaleSpan = yScaleExtent[1] - yScaleExtent[0]; + if (!xScaleSpan || !yScaleSpan) { + return; + } + // Accelerate data to point calculation on the special large time series data. + var scaleX = (end[0] - start[0]) / xScaleSpan; + var scaleY = (end[1] - start[1]) / yScaleSpan; + var translateX = start[0] - xScaleExtent[0] * scaleX; + var translateY = start[1] - yScaleExtent[0] * scaleY; + var m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY]; + this._invTransform = invert([], m); + }; + /** + * Base axis will be used on stacking. + */ + Cartesian2D.prototype.getBaseAxis = function () { + return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x'); + }; + Cartesian2D.prototype.containPoint = function (point) { + var axisX = this.getAxis('x'); + var axisY = this.getAxis('y'); + return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1])); + }; + Cartesian2D.prototype.containData = function (data) { + return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]); + }; + Cartesian2D.prototype.containZone = function (data1, data2) { + var zoneDiag1 = this.dataToPoint(data1); + var zoneDiag2 = this.dataToPoint(data2); + var area = this.getArea(); + var zone = new BoundingRect(zoneDiag1[0], zoneDiag1[1], zoneDiag2[0] - zoneDiag1[0], zoneDiag2[1] - zoneDiag1[1]); + return area.intersect(zone); + }; + Cartesian2D.prototype.dataToPoint = function (data, clamp, out) { + out = out || []; + var xVal = data[0]; + var yVal = data[1]; + // Fast path + if (this._transform + // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated. + && xVal != null && isFinite(xVal) && yVal != null && isFinite(yVal)) { + return applyTransform(out, data, this._transform); + } + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp)); + out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp)); + return out; + }; + Cartesian2D.prototype.clampData = function (data, out) { + var xScale = this.getAxis('x').scale; + var yScale = this.getAxis('y').scale; + var xAxisExtent = xScale.getExtent(); + var yAxisExtent = yScale.getExtent(); + var x = xScale.parse(data[0]); + var y = yScale.parse(data[1]); + out = out || []; + out[0] = Math.min(Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), Math.max(xAxisExtent[0], xAxisExtent[1])); + out[1] = Math.min(Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), Math.max(yAxisExtent[0], yAxisExtent[1])); + return out; + }; + Cartesian2D.prototype.pointToData = function (point, clamp) { + var out = []; + if (this._invTransform) { + return applyTransform(out, point, this._invTransform); + } + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp); + out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp); + return out; + }; + Cartesian2D.prototype.getOtherAxis = function (axis) { + return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); + }; + /** + * Get rect area of cartesian. + * Area will have a contain function to determine if a point is in the coordinate system. + */ + Cartesian2D.prototype.getArea = function (tolerance) { + tolerance = tolerance || 0; + var xExtent = this.getAxis('x').getGlobalExtent(); + var yExtent = this.getAxis('y').getGlobalExtent(); + var x = Math.min(xExtent[0], xExtent[1]) - tolerance; + var y = Math.min(yExtent[0], yExtent[1]) - tolerance; + var width = Math.max(xExtent[0], xExtent[1]) - x + tolerance; + var height = Math.max(yExtent[0], yExtent[1]) - y + tolerance; + return new BoundingRect(x, y, width, height); + }; + return Cartesian2D; + }(Cartesian); + + var Axis2D = /** @class */function (_super) { + __extends(Axis2D, _super); + function Axis2D(dim, scale, coordExtent, axisType, position) { + var _this = _super.call(this, dim, scale, coordExtent) || this; + /** + * Index of axis, can be used as key + * Injected outside. + */ + _this.index = 0; + _this.type = axisType || 'value'; + _this.position = position || 'bottom'; + return _this; + } + Axis2D.prototype.isHorizontal = function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + }; + /** + * Each item cooresponds to this.getExtent(), which + * means globalExtent[0] may greater than globalExtent[1], + * unless `asc` is input. + * + * @param {boolean} [asc] + * @return {Array.} + */ + Axis2D.prototype.getGlobalExtent = function (asc) { + var ret = this.getExtent(); + ret[0] = this.toGlobalCoord(ret[0]); + ret[1] = this.toGlobalCoord(ret[1]); + asc && ret[0] > ret[1] && ret.reverse(); + return ret; + }; + Axis2D.prototype.pointToData = function (point, clamp) { + return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp); + }; + /** + * Set ordinalSortInfo + * @param info new OrdinalSortInfo + */ + Axis2D.prototype.setCategorySortInfo = function (info) { + if (this.type !== 'category') { + return false; + } + this.model.option.categorySortInfo = info; + this.scale.setSortInfo(info); + }; + return Axis2D; + }(Axis); + + /** + * Can only be called after coordinate system creation stage. + * (Can be called before coordinate system update stage). + */ + function layout$1(gridModel, axisModel, opt) { + opt = opt || {}; + var grid = gridModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0]; + var rawAxisPosition = axis.position; + var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition; + var axisDim = axis.dim; + var rect = grid.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + var idx = { + left: 0, + right: 1, + top: 0, + bottom: 1, + onZero: 2 + }; + var axisOffset = axisModel.get('offset') || 0; + var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset]; + if (otherAxisOnZeroOf) { + var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0)); + posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]); + } + // Axis position + layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; + // Axis rotation + layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); + // Tick and label direction, x y is axisDim + var dirMap = { + top: -1, + bottom: 1, + left: -1, + right: 1 + }; + layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition]; + layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0; + if (axisModel.get(['axisTick', 'inside'])) { + layout.tickDirection = -layout.tickDirection; + } + if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) { + layout.labelDirection = -layout.labelDirection; + } + // Special label rotation + var labelRotate = axisModel.get(['axisLabel', 'rotate']); + layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; + // Over splitLine and splitArea + layout.z2 = 1; + return layout; + } + function isCartesian2DSeries(seriesModel) { + return seriesModel.get('coordinateSystem') === 'cartesian2d'; + } + function findAxisModels(seriesModel) { + var axisModelMap = { + xAxisModel: null, + yAxisModel: null + }; + each(axisModelMap, function (v, key) { + var axisType = key.replace(/Model$/, ''); + var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0]; + if ("development" !== 'production') { + if (!axisModel) { + throw new Error(axisType + ' "' + retrieve3(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '" not found'); + } + } + axisModelMap[key] = axisModel; + }); + return axisModelMap; + } + + var mathLog$1 = Math.log; + function alignScaleTicks(scale, axisModel, alignToScale) { + var intervalScaleProto = IntervalScale.prototype; + // NOTE: There is a precondition for log scale here: + // In log scale we store _interval and _extent of exponent value. + // So if we use the method of InternalScale to set/get these data. + // It process the exponent value, which is linear and what we want here. + var alignToTicks = intervalScaleProto.getTicks.call(alignToScale); + var alignToNicedTicks = intervalScaleProto.getTicks.call(alignToScale, true); + var alignToSplitNumber = alignToTicks.length - 1; + var alignToInterval = intervalScaleProto.getInterval.call(alignToScale); + var scaleExtent = getScaleExtent(scale, axisModel); + var rawExtent = scaleExtent.extent; + var isMinFixed = scaleExtent.fixMin; + var isMaxFixed = scaleExtent.fixMax; + if (scale.type === 'log') { + var logBase = mathLog$1(scale.base); + rawExtent = [mathLog$1(rawExtent[0]) / logBase, mathLog$1(rawExtent[1]) / logBase]; + } + scale.setExtent(rawExtent[0], rawExtent[1]); + scale.calcNiceExtent({ + splitNumber: alignToSplitNumber, + fixMin: isMinFixed, + fixMax: isMaxFixed + }); + var extent = intervalScaleProto.getExtent.call(scale); + // Need to update the rawExtent. + // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax' + if (isMinFixed) { + rawExtent[0] = extent[0]; + } + if (isMaxFixed) { + rawExtent[1] = extent[1]; + } + var interval = intervalScaleProto.getInterval.call(scale); + var min = rawExtent[0]; + var max = rawExtent[1]; + if (isMinFixed && isMaxFixed) { + // User set min, max, divide to get new interval + interval = (max - min) / alignToSplitNumber; + } else if (isMinFixed) { + max = rawExtent[0] + interval * alignToSplitNumber; + // User set min, expand extent on the other side + while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])) { + interval = increaseInterval(interval); + max = rawExtent[0] + interval * alignToSplitNumber; + } + } else if (isMaxFixed) { + // User set max, expand extent on the other side + min = rawExtent[1] - interval * alignToSplitNumber; + while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])) { + interval = increaseInterval(interval); + min = rawExtent[1] - interval * alignToSplitNumber; + } + } else { + var nicedSplitNumber = scale.getTicks().length - 1; + if (nicedSplitNumber > alignToSplitNumber) { + interval = increaseInterval(interval); + } + var range = interval * alignToSplitNumber; + max = Math.ceil(rawExtent[1] / interval) * interval; + min = round(max - range); + // Not change the result that crossing zero. + if (min < 0 && rawExtent[0] >= 0) { + min = 0; + max = round(range); + } else if (max > 0 && rawExtent[1] <= 0) { + max = 0; + min = -round(range); + } + } + // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale + var t0 = (alignToTicks[0].value - alignToNicedTicks[0].value) / alignToInterval; + var t1 = (alignToTicks[alignToSplitNumber].value - alignToNicedTicks[alignToSplitNumber].value) / alignToInterval; + // NOTE: Must in setExtent -> setInterval -> setNiceExtent order. + intervalScaleProto.setExtent.call(scale, min + interval * t0, max + interval * t1); + intervalScaleProto.setInterval.call(scale, interval); + if (t0 || t1) { + intervalScaleProto.setNiceExtent.call(scale, min + interval, max - interval); + } + if ("development" !== 'production') { + var ticks = intervalScaleProto.getTicks.call(scale); + if (ticks[1] && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))) { + warn( + // eslint-disable-next-line + "The ticks may be not readable when set min: " + axisModel.get('min') + ", max: " + axisModel.get('max') + " and alignTicks: true"); + } + } + } + + var Grid = /** @class */function () { + function Grid(gridModel, ecModel, api) { + // FIXME:TS where used (different from registered type 'cartesian2d')? + this.type = 'grid'; + this._coordsMap = {}; + this._coordsList = []; + this._axesMap = {}; + this._axesList = []; + this.axisPointerEnabled = true; + this.dimensions = cartesian2DDimensions; + this._initCartesian(gridModel, ecModel, api); + this.model = gridModel; + } + Grid.prototype.getRect = function () { + return this._rect; + }; + Grid.prototype.update = function (ecModel, api) { + var axesMap = this._axesMap; + this._updateScale(ecModel, this.model); + function updateAxisTicks(axes) { + var alignTo; + // Axis is added in order of axisIndex. + var axesIndices = keys(axes); + var len = axesIndices.length; + if (!len) { + return; + } + var axisNeedsAlign = []; + // Process once and calculate the ticks for those don't use alignTicks. + for (var i = len - 1; i >= 0; i--) { + var idx = +axesIndices[i]; // Convert to number. + var axis = axes[idx]; + var model = axis.model; + var scale = axis.scale; + if ( + // Only value and log axis without interval support alignTicks. + isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) { + axisNeedsAlign.push(axis); + } else { + niceScaleExtent(scale, model); + if (isIntervalOrLogScale(scale)) { + // Can only align to interval or log axis. + alignTo = axis; + } + } + } + // All axes has set alignTicks. Pick the first one. + // PENDING. Should we find the axis that both set interval, min, max and align to this one? + if (axisNeedsAlign.length) { + if (!alignTo) { + alignTo = axisNeedsAlign.pop(); + niceScaleExtent(alignTo.scale, alignTo.model); + } + each(axisNeedsAlign, function (axis) { + alignScaleTicks(axis.scale, axis.model, alignTo.scale); + }); + } + } + updateAxisTicks(axesMap.x); + updateAxisTicks(axesMap.y); + // Key: axisDim_axisIndex, value: boolean, whether onZero target. + var onZeroRecords = {}; + each(axesMap.x, function (xAxis) { + fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords); + }); + each(axesMap.y, function (yAxis) { + fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords); + }); + // Resize again if containLabel is enabled + // FIXME It may cause getting wrong grid size in data processing stage + this.resize(this.model, api); + }; + /** + * Resize the grid + */ + Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) { + var boxLayoutParams = gridModel.getBoxLayoutParams(); + var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel'); + var gridRect = getLayoutRect(boxLayoutParams, { + width: api.getWidth(), + height: api.getHeight() + }); + this._rect = gridRect; + var axesList = this._axesList; + adjustAxes(); + // Minus label size + if (isContainLabel) { + each(axesList, function (axis) { + if (!axis.model.get(['axisLabel', 'inside'])) { + var labelUnionRect = estimateLabelUnionRect(axis); + if (labelUnionRect) { + var dim = axis.isHorizontal() ? 'height' : 'width'; + var margin = axis.model.get(['axisLabel', 'margin']); + gridRect[dim] -= labelUnionRect[dim] + margin; + if (axis.position === 'top') { + gridRect.y += labelUnionRect.height + margin; + } else if (axis.position === 'left') { + gridRect.x += labelUnionRect.width + margin; + } + } + } + }); + adjustAxes(); + } + each(this._coordsList, function (coord) { + // Calculate affine matrix to accelerate the data to point transform. + // If all the axes scales are time or value. + coord.calcAffineTransform(); + }); + function adjustAxes() { + each(axesList, function (axis) { + var isHorizontal = axis.isHorizontal(); + var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height]; + var idx = axis.inverse ? 1 : 0; + axis.setExtent(extent[idx], extent[1 - idx]); + updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y); + }); + } + }; + Grid.prototype.getAxis = function (dim, axisIndex) { + var axesMapOnDim = this._axesMap[dim]; + if (axesMapOnDim != null) { + return axesMapOnDim[axisIndex || 0]; + } + }; + Grid.prototype.getAxes = function () { + return this._axesList.slice(); + }; + Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) { + if (xAxisIndex != null && yAxisIndex != null) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + return this._coordsMap[key]; + } + if (isObject(xAxisIndex)) { + yAxisIndex = xAxisIndex.yAxisIndex; + xAxisIndex = xAxisIndex.xAxisIndex; + } + for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) { + if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) { + return coordList[i]; + } + } + }; + Grid.prototype.getCartesians = function () { + return this._coordsList.slice(); + }; + /** + * @implements + */ + Grid.prototype.convertToPixel = function (ecModel, finder, value) { + var target = this._findConvertTarget(finder); + return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null; + }; + /** + * @implements + */ + Grid.prototype.convertFromPixel = function (ecModel, finder, value) { + var target = this._findConvertTarget(finder); + return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null; + }; + Grid.prototype._findConvertTarget = function (finder) { + var seriesModel = finder.seriesModel; + var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0]; + var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0]; + var gridModel = finder.gridModel; + var coordsList = this._coordsList; + var cartesian; + var axis; + if (seriesModel) { + cartesian = seriesModel.coordinateSystem; + indexOf(coordsList, cartesian) < 0 && (cartesian = null); + } else if (xAxisModel && yAxisModel) { + cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); + } else if (xAxisModel) { + axis = this.getAxis('x', xAxisModel.componentIndex); + } else if (yAxisModel) { + axis = this.getAxis('y', yAxisModel.componentIndex); + } + // Lowest priority. + else if (gridModel) { + var grid = gridModel.coordinateSystem; + if (grid === this) { + cartesian = this._coordsList[0]; + } + } + return { + cartesian: cartesian, + axis: axis + }; + }; + /** + * @implements + */ + Grid.prototype.containPoint = function (point) { + var coord = this._coordsList[0]; + if (coord) { + return coord.containPoint(point); + } + }; + /** + * Initialize cartesian coordinate systems + */ + Grid.prototype._initCartesian = function (gridModel, ecModel, api) { + var _this = this; + var grid = this; + var axisPositionUsed = { + left: false, + right: false, + top: false, + bottom: false + }; + var axesMap = { + x: {}, + y: {} + }; + var axesCount = { + x: 0, + y: 0 + }; + // Create axis + ecModel.eachComponent('xAxis', createAxisCreator('x'), this); + ecModel.eachComponent('yAxis', createAxisCreator('y'), this); + if (!axesCount.x || !axesCount.y) { + // Roll back when there no either x or y axis + this._axesMap = {}; + this._axesList = []; + return; + } + this._axesMap = axesMap; + // Create cartesian2d + each(axesMap.x, function (xAxis, xAxisIndex) { + each(axesMap.y, function (yAxis, yAxisIndex) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + var cartesian = new Cartesian2D(key); + cartesian.master = _this; + cartesian.model = gridModel; + _this._coordsMap[key] = cartesian; + _this._coordsList.push(cartesian); + cartesian.addAxis(xAxis); + cartesian.addAxis(yAxis); + }); + }); + function createAxisCreator(dimName) { + return function (axisModel, idx) { + if (!isAxisUsedInTheGrid(axisModel, gridModel)) { + return; + } + var axisPosition = axisModel.get('position'); + if (dimName === 'x') { + // Fix position + if (axisPosition !== 'top' && axisPosition !== 'bottom') { + // Default bottom of X + axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom'; + } + } else { + // Fix position + if (axisPosition !== 'left' && axisPosition !== 'right') { + // Default left of Y + axisPosition = axisPositionUsed.left ? 'right' : 'left'; + } + } + axisPositionUsed[axisPosition] = true; + var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition); + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + // Inject axis into axisModel + axisModel.axis = axis; + // Inject axisModel into axis + axis.model = axisModel; + // Inject grid info axis + axis.grid = grid; + // Index of axis, can be used as key + axis.index = idx; + grid._axesList.push(axis); + axesMap[dimName][idx] = axis; + axesCount[dimName]++; + }; + } + }; + /** + * Update cartesian properties from series. + */ + Grid.prototype._updateScale = function (ecModel, gridModel) { + // Reset scale + each(this._axesList, function (axis) { + axis.scale.setExtent(Infinity, -Infinity); + if (axis.type === 'category') { + var categorySortInfo = axis.model.get('categorySortInfo'); + axis.scale.setSortInfo(categorySortInfo); + } + }); + ecModel.eachSeries(function (seriesModel) { + if (isCartesian2DSeries(seriesModel)) { + var axesModelMap = findAxisModels(seriesModel); + var xAxisModel = axesModelMap.xAxisModel; + var yAxisModel = axesModelMap.yAxisModel; + if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) { + return; + } + var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); + var data = seriesModel.getData(); + var xAxis = cartesian.getAxis('x'); + var yAxis = cartesian.getAxis('y'); + unionExtent(data, xAxis); + unionExtent(data, yAxis); + } + }, this); + function unionExtent(data, axis) { + each(getDataDimensionsOnAxis(data, axis.dim), function (dim) { + axis.scale.unionExtentFromData(data, dim); + }); + } + }; + /** + * @param dim 'x' or 'y' or 'auto' or null/undefined + */ + Grid.prototype.getTooltipAxes = function (dim) { + var baseAxes = []; + var otherAxes = []; + each(this.getCartesians(), function (cartesian) { + var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis(); + var otherAxis = cartesian.getOtherAxis(baseAxis); + indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis); + indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis); + }); + return { + baseAxes: baseAxes, + otherAxes: otherAxes + }; + }; + Grid.create = function (ecModel, api) { + var grids = []; + ecModel.eachComponent('grid', function (gridModel, idx) { + var grid = new Grid(gridModel, ecModel, api); + grid.name = 'grid_' + idx; + // dataSampling requires axis extent, so resize + // should be performed in create stage. + grid.resize(gridModel, api, true); + gridModel.coordinateSystem = grid; + grids.push(grid); + }); + // Inject the coordinateSystems into seriesModel + ecModel.eachSeries(function (seriesModel) { + if (!isCartesian2DSeries(seriesModel)) { + return; + } + var axesModelMap = findAxisModels(seriesModel); + var xAxisModel = axesModelMap.xAxisModel; + var yAxisModel = axesModelMap.yAxisModel; + var gridModel = xAxisModel.getCoordSysModel(); + if ("development" !== 'production') { + if (!gridModel) { + throw new Error('Grid "' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '" not found'); + } + if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) { + throw new Error('xAxis and yAxis must use the same grid'); + } + } + var grid = gridModel.coordinateSystem; + seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); + }); + return grids; + }; + // For deciding which dimensions to use when creating list data + Grid.dimensions = cartesian2DDimensions; + return Grid; + }(); + /** + * Check if the axis is used in the specified grid. + */ + function isAxisUsedInTheGrid(axisModel, gridModel) { + return axisModel.getCoordSysModel() === gridModel; + } + function fixAxisOnZero(axesMap, otherAxisDim, axis, + // Key: see `getOnZeroRecordKey` + onZeroRecords) { + axis.getAxesOnZeroOf = function () { + // TODO: onZero of multiple axes. + return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : []; + }; + // onZero can not be enabled in these two situations: + // 1. When any other axis is a category axis. + // 2. When no axis is cross 0 point. + var otherAxes = axesMap[otherAxisDim]; + var otherAxisOnZeroOf; + var axisModel = axis.model; + var onZero = axisModel.get(['axisLine', 'onZero']); + var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']); + if (!onZero) { + return; + } + // If target axis is specified. + if (onZeroAxisIndex != null) { + if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) { + otherAxisOnZeroOf = otherAxes[onZeroAxisIndex]; + } + } else { + // Find the first available other axis. + for (var idx in otherAxes) { + if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) + // Consider that two Y axes on one value axis, + // if both onZero, the two Y axes overlap. + && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) { + otherAxisOnZeroOf = otherAxes[idx]; + break; + } + } + } + if (otherAxisOnZeroOf) { + onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true; + } + function getOnZeroRecordKey(axis) { + return axis.dim + '_' + axis.index; + } + } + function canOnZeroToAxis(axis) { + return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis); + } + function updateAxisTransform(axis, coordBase) { + var axisExtent = axis.getExtent(); + var axisExtentSum = axisExtent[0] + axisExtent[1]; + // Fast transform + axis.toGlobalCoord = axis.dim === 'x' ? function (coord) { + return coord + coordBase; + } : function (coord) { + return axisExtentSum - coord + coordBase; + }; + axis.toLocalCoord = axis.dim === 'x' ? function (coord) { + return coord - coordBase; + } : function (coord) { + return axisExtentSum - coord + coordBase; + }; + } + + var PI$5 = Math.PI; + /** + * A final axis is translated and rotated from a "standard axis". + * So opt.position and opt.rotation is required. + * + * A standard axis is and axis from [0, 0] to [0, axisExtent[1]], + * for example: (0, 0) ------------> (0, 50) + * + * nameDirection or tickDirection or labelDirection is 1 means tick + * or label is below the standard axis, whereas is -1 means above + * the standard axis. labelOffset means offset between label and axis, + * which is useful when 'onZero', where axisLabel is in the grid and + * label in outside grid. + * + * Tips: like always, + * positive rotation represents anticlockwise, and negative rotation + * represents clockwise. + * The direction of position coordinate is the same as the direction + * of screen coordinate. + * + * Do not need to consider axis 'inverse', which is auto processed by + * axis extent. + */ + var AxisBuilder = /** @class */function () { + function AxisBuilder(axisModel, opt) { + this.group = new Group(); + this.opt = opt; + this.axisModel = axisModel; + // Default value + defaults(opt, { + labelOffset: 0, + nameDirection: 1, + tickDirection: 1, + labelDirection: 1, + silent: true, + handleAutoShown: function () { + return true; + } + }); + // FIXME Not use a separate text group? + var transformGroup = new Group({ + x: opt.position[0], + y: opt.position[1], + rotation: opt.rotation + }); + // this.group.add(transformGroup); + // this._transformGroup = transformGroup; + transformGroup.updateTransform(); + this._transformGroup = transformGroup; + } + AxisBuilder.prototype.hasBuilder = function (name) { + return !!builders[name]; + }; + AxisBuilder.prototype.add = function (name) { + builders[name](this.opt, this.axisModel, this.group, this._transformGroup); + }; + AxisBuilder.prototype.getGroup = function () { + return this.group; + }; + AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) { + var rotationDiff = remRadian(textRotation - axisRotation); + var textAlign; + var textVerticalAlign; + if (isRadianAroundZero(rotationDiff)) { + // Label is parallel with axis line. + textVerticalAlign = direction > 0 ? 'top' : 'bottom'; + textAlign = 'center'; + } else if (isRadianAroundZero(rotationDiff - PI$5)) { + // Label is inverse parallel with axis line. + textVerticalAlign = direction > 0 ? 'bottom' : 'top'; + textAlign = 'center'; + } else { + textVerticalAlign = 'middle'; + if (rotationDiff > 0 && rotationDiff < PI$5) { + textAlign = direction > 0 ? 'right' : 'left'; + } else { + textAlign = direction > 0 ? 'left' : 'right'; + } + } + return { + rotation: rotationDiff, + textAlign: textAlign, + textVerticalAlign: textVerticalAlign + }; + }; + AxisBuilder.makeAxisEventDataBase = function (axisModel) { + var eventData = { + componentType: axisModel.mainType, + componentIndex: axisModel.componentIndex + }; + eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex; + return eventData; + }; + AxisBuilder.isLabelSilent = function (axisModel) { + var tooltipOpt = axisModel.get('tooltip'); + return axisModel.get('silent') + // Consider mouse cursor, add these restrictions. + || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show); + }; + return AxisBuilder; + }(); + var builders = { + axisLine: function (opt, axisModel, group, transformGroup) { + var shown = axisModel.get(['axisLine', 'show']); + if (shown === 'auto' && opt.handleAutoShown) { + shown = opt.handleAutoShown('axisLine'); + } + if (!shown) { + return; + } + var extent = axisModel.axis.getExtent(); + var matrix = transformGroup.transform; + var pt1 = [extent[0], 0]; + var pt2 = [extent[1], 0]; + var inverse = pt1[0] > pt2[0]; + if (matrix) { + applyTransform(pt1, pt1, matrix); + applyTransform(pt2, pt2, matrix); + } + var lineStyle = extend({ + lineCap: 'round' + }, axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle()); + var line = new Line({ + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: lineStyle, + strokeContainThreshold: opt.strokeContainThreshold || 5, + silent: true, + z2: 1 + }); + subPixelOptimizeLine$1(line.shape, line.style.lineWidth); + line.anid = 'line'; + group.add(line); + var arrows = axisModel.get(['axisLine', 'symbol']); + if (arrows != null) { + var arrowSize = axisModel.get(['axisLine', 'symbolSize']); + if (isString(arrows)) { + // Use the same arrow for start and end point + arrows = [arrows, arrows]; + } + if (isString(arrowSize) || isNumber(arrowSize)) { + // Use the same size for width and height + arrowSize = [arrowSize, arrowSize]; + } + var arrowOffset = normalizeSymbolOffset(axisModel.get(['axisLine', 'symbolOffset']) || 0, arrowSize); + var symbolWidth_1 = arrowSize[0]; + var symbolHeight_1 = arrowSize[1]; + each([{ + rotate: opt.rotation + Math.PI / 2, + offset: arrowOffset[0], + r: 0 + }, { + rotate: opt.rotation - Math.PI / 2, + offset: arrowOffset[1], + r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) + }], function (point, index) { + if (arrows[index] !== 'none' && arrows[index] != null) { + var symbol = createSymbol(arrows[index], -symbolWidth_1 / 2, -symbolHeight_1 / 2, symbolWidth_1, symbolHeight_1, lineStyle.stroke, true); + // Calculate arrow position with offset + var r = point.r + point.offset; + var pt = inverse ? pt2 : pt1; + symbol.attr({ + rotation: point.rotate, + x: pt[0] + r * Math.cos(opt.rotation), + y: pt[1] - r * Math.sin(opt.rotation), + silent: true, + z2: 11 + }); + group.add(symbol); + } + }); + } + }, + axisTickLabel: function (opt, axisModel, group, transformGroup) { + var ticksEls = buildAxisMajorTicks(group, transformGroup, axisModel, opt); + var labelEls = buildAxisLabel(group, transformGroup, axisModel, opt); + fixMinMaxLabelShow(axisModel, labelEls, ticksEls); + buildAxisMinorTicks(group, transformGroup, axisModel, opt.tickDirection); + // This bit fixes the label overlap issue for the time chart. + // See https://github.com/apache/echarts/issues/14266 for more. + if (axisModel.get(['axisLabel', 'hideOverlap'])) { + var labelList = prepareLayoutList(map(labelEls, function (label) { + return { + label: label, + priority: label.z2, + defaultAttr: { + ignore: label.ignore + } + }; + })); + hideOverlap(labelList); + } + }, + axisName: function (opt, axisModel, group, transformGroup) { + var name = retrieve(opt.axisName, axisModel.get('name')); + if (!name) { + return; + } + var nameLocation = axisModel.get('nameLocation'); + var nameDirection = opt.nameDirection; + var textStyleModel = axisModel.getModel('nameTextStyle'); + var gap = axisModel.get('nameGap') || 0; + var extent = axisModel.axis.getExtent(); + var gapSignal = extent[0] > extent[1] ? -1 : 1; + var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, + // Reuse labelOffset. + isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0]; + var labelLayout; + var nameRotation = axisModel.get('nameRotate'); + if (nameRotation != null) { + nameRotation = nameRotation * PI$5 / 180; // To radian. + } + + var axisNameAvailableWidth; + if (isNameLocationCenter(nameLocation)) { + labelLayout = AxisBuilder.innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, + // Adapt to axis. + nameDirection); + } else { + labelLayout = endTextLayout(opt.rotation, nameLocation, nameRotation || 0, extent); + axisNameAvailableWidth = opt.axisNameAvailableWidth; + if (axisNameAvailableWidth != null) { + axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation)); + !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null); + } + } + var textFont = textStyleModel.getFont(); + var truncateOpt = axisModel.get('nameTruncate', true) || {}; + var ellipsis = truncateOpt.ellipsis; + var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth); + var textEl = new ZRText({ + x: pos[0], + y: pos[1], + rotation: labelLayout.rotation, + silent: AxisBuilder.isLabelSilent(axisModel), + style: createTextStyle(textStyleModel, { + text: name, + font: textFont, + overflow: 'truncate', + width: maxWidth, + ellipsis: ellipsis, + fill: textStyleModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']), + align: textStyleModel.get('align') || labelLayout.textAlign, + verticalAlign: textStyleModel.get('verticalAlign') || labelLayout.textVerticalAlign + }), + z2: 1 + }); + setTooltipConfig({ + el: textEl, + componentModel: axisModel, + itemName: name + }); + textEl.__fullText = name; + // Id for animation + textEl.anid = 'name'; + if (axisModel.get('triggerEvent')) { + var eventData = AxisBuilder.makeAxisEventDataBase(axisModel); + eventData.targetType = 'axisName'; + eventData.name = name; + getECData(textEl).eventData = eventData; + } + // FIXME + transformGroup.add(textEl); + textEl.updateTransform(); + group.add(textEl); + textEl.decomposeTransform(); + } + }; + function endTextLayout(rotation, textPosition, textRotate, extent) { + var rotationDiff = remRadian(textRotate - rotation); + var textAlign; + var textVerticalAlign; + var inverse = extent[0] > extent[1]; + var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse; + if (isRadianAroundZero(rotationDiff - PI$5 / 2)) { + textVerticalAlign = onLeft ? 'bottom' : 'top'; + textAlign = 'center'; + } else if (isRadianAroundZero(rotationDiff - PI$5 * 1.5)) { + textVerticalAlign = onLeft ? 'top' : 'bottom'; + textAlign = 'center'; + } else { + textVerticalAlign = 'middle'; + if (rotationDiff < PI$5 * 1.5 && rotationDiff > PI$5 / 2) { + textAlign = onLeft ? 'left' : 'right'; + } else { + textAlign = onLeft ? 'right' : 'left'; + } + } + return { + rotation: rotationDiff, + textAlign: textAlign, + textVerticalAlign: textVerticalAlign + }; + } + function fixMinMaxLabelShow(axisModel, labelEls, tickEls) { + if (shouldShowAllLabels(axisModel.axis)) { + return; + } + // If min or max are user set, we need to check + // If the tick on min(max) are overlap on their neighbour tick + // If they are overlapped, we need to hide the min(max) tick label + var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel']); + var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']); + // FIXME + // Have not consider onBand yet, where tick els is more than label els. + labelEls = labelEls || []; + tickEls = tickEls || []; + var firstLabel = labelEls[0]; + var nextLabel = labelEls[1]; + var lastLabel = labelEls[labelEls.length - 1]; + var prevLabel = labelEls[labelEls.length - 2]; + var firstTick = tickEls[0]; + var nextTick = tickEls[1]; + var lastTick = tickEls[tickEls.length - 1]; + var prevTick = tickEls[tickEls.length - 2]; + if (showMinLabel === false) { + ignoreEl(firstLabel); + ignoreEl(firstTick); + } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) { + if (showMinLabel) { + ignoreEl(nextLabel); + ignoreEl(nextTick); + } else { + ignoreEl(firstLabel); + ignoreEl(firstTick); + } + } + if (showMaxLabel === false) { + ignoreEl(lastLabel); + ignoreEl(lastTick); + } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) { + if (showMaxLabel) { + ignoreEl(prevLabel); + ignoreEl(prevTick); + } else { + ignoreEl(lastLabel); + ignoreEl(lastTick); + } + } + } + function ignoreEl(el) { + el && (el.ignore = true); + } + function isTwoLabelOverlapped(current, next) { + // current and next has the same rotation. + var firstRect = current && current.getBoundingRect().clone(); + var nextRect = next && next.getBoundingRect().clone(); + if (!firstRect || !nextRect) { + return; + } + // When checking intersect of two rotated labels, we use mRotationBack + // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`. + var mRotationBack = identity([]); + rotate(mRotationBack, mRotationBack, -current.rotation); + firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform())); + nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform())); + return firstRect.intersect(nextRect); + } + function isNameLocationCenter(nameLocation) { + return nameLocation === 'middle' || nameLocation === 'center'; + } + function createTicks(ticksCoords, tickTransform, tickEndCoord, tickLineStyle, anidPrefix) { + var tickEls = []; + var pt1 = []; + var pt2 = []; + for (var i = 0; i < ticksCoords.length; i++) { + var tickCoord = ticksCoords[i].coord; + pt1[0] = tickCoord; + pt1[1] = 0; + pt2[0] = tickCoord; + pt2[1] = tickEndCoord; + if (tickTransform) { + applyTransform(pt1, pt1, tickTransform); + applyTransform(pt2, pt2, tickTransform); + } + // Tick line, Not use group transform to have better line draw + var tickEl = new Line({ + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: tickLineStyle, + z2: 2, + autoBatch: true, + silent: true + }); + subPixelOptimizeLine$1(tickEl.shape, tickEl.style.lineWidth); + tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue; + tickEls.push(tickEl); + } + return tickEls; + } + function buildAxisMajorTicks(group, transformGroup, axisModel, opt) { + var axis = axisModel.axis; + var tickModel = axisModel.getModel('axisTick'); + var shown = tickModel.get('show'); + if (shown === 'auto' && opt.handleAutoShown) { + shown = opt.handleAutoShown('axisTick'); + } + if (!shown || axis.scale.isBlank()) { + return; + } + var lineStyleModel = tickModel.getModel('lineStyle'); + var tickEndCoord = opt.tickDirection * tickModel.get('length'); + var ticksCoords = axis.getTicksCoords(); + var ticksEls = createTicks(ticksCoords, transformGroup.transform, tickEndCoord, defaults(lineStyleModel.getLineStyle(), { + stroke: axisModel.get(['axisLine', 'lineStyle', 'color']) + }), 'ticks'); + for (var i = 0; i < ticksEls.length; i++) { + group.add(ticksEls[i]); + } + return ticksEls; + } + function buildAxisMinorTicks(group, transformGroup, axisModel, tickDirection) { + var axis = axisModel.axis; + var minorTickModel = axisModel.getModel('minorTick'); + if (!minorTickModel.get('show') || axis.scale.isBlank()) { + return; + } + var minorTicksCoords = axis.getMinorTicksCoords(); + if (!minorTicksCoords.length) { + return; + } + var lineStyleModel = minorTickModel.getModel('lineStyle'); + var tickEndCoord = tickDirection * minorTickModel.get('length'); + var minorTickLineStyle = defaults(lineStyleModel.getLineStyle(), defaults(axisModel.getModel('axisTick').getLineStyle(), { + stroke: axisModel.get(['axisLine', 'lineStyle', 'color']) + })); + for (var i = 0; i < minorTicksCoords.length; i++) { + var minorTicksEls = createTicks(minorTicksCoords[i], transformGroup.transform, tickEndCoord, minorTickLineStyle, 'minorticks_' + i); + for (var k = 0; k < minorTicksEls.length; k++) { + group.add(minorTicksEls[k]); + } + } + } + function buildAxisLabel(group, transformGroup, axisModel, opt) { + var axis = axisModel.axis; + var show = retrieve(opt.axisLabelShow, axisModel.get(['axisLabel', 'show'])); + if (!show || axis.scale.isBlank()) { + return; + } + var labelModel = axisModel.getModel('axisLabel'); + var labelMargin = labelModel.get('margin'); + var labels = axis.getViewLabels(); + // Special label rotate. + var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$5 / 180; + var labelLayout = AxisBuilder.innerTextLayout(opt.rotation, labelRotation, opt.labelDirection); + var rawCategoryData = axisModel.getCategories && axisModel.getCategories(true); + var labelEls = []; + var silent = AxisBuilder.isLabelSilent(axisModel); + var triggerEvent = axisModel.get('triggerEvent'); + each(labels, function (labelItem, index) { + var tickValue = axis.scale.type === 'ordinal' ? axis.scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue; + var formattedLabel = labelItem.formattedLabel; + var rawLabel = labelItem.rawLabel; + var itemLabelModel = labelModel; + if (rawCategoryData && rawCategoryData[tickValue]) { + var rawCategoryItem = rawCategoryData[tickValue]; + if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) { + itemLabelModel = new Model(rawCategoryItem.textStyle, labelModel, axisModel.ecModel); + } + } + var textColor = itemLabelModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']); + var tickCoord = axis.dataToCoord(tickValue); + var align = itemLabelModel.getShallow('align', true) || labelLayout.textAlign; + var alignMin = retrieve2(itemLabelModel.getShallow('alignMinLabel', true), align); + var alignMax = retrieve2(itemLabelModel.getShallow('alignMaxLabel', true), align); + var verticalAlign = itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign; + var verticalAlignMin = retrieve2(itemLabelModel.getShallow('verticalAlignMinLabel', true), verticalAlign); + var verticalAlignMax = retrieve2(itemLabelModel.getShallow('verticalAlignMaxLabel', true), verticalAlign); + var textEl = new ZRText({ + x: tickCoord, + y: opt.labelOffset + opt.labelDirection * labelMargin, + rotation: labelLayout.rotation, + silent: silent, + z2: 10 + (labelItem.level || 0), + style: createTextStyle(itemLabelModel, { + text: formattedLabel, + align: index === 0 ? alignMin : index === labels.length - 1 ? alignMax : align, + verticalAlign: index === 0 ? verticalAlignMin : index === labels.length - 1 ? verticalAlignMax : verticalAlign, + fill: isFunction(textColor) ? textColor( + // (1) In category axis with data zoom, tick is not the original + // index of axis.data. So tick should not be exposed to user + // in category axis. + // (2) Compatible with previous version, which always use formatted label as + // input. But in interval scale the formatted label is like '223,445', which + // maked user replace ','. So we modify it to return original val but remain + // it as 'string' to avoid error in replacing. + axis.type === 'category' ? rawLabel : axis.type === 'value' ? tickValue + '' : tickValue, index) : textColor + }) + }); + textEl.anid = 'label_' + tickValue; + // Pack data for mouse event + if (triggerEvent) { + var eventData = AxisBuilder.makeAxisEventDataBase(axisModel); + eventData.targetType = 'axisLabel'; + eventData.value = rawLabel; + eventData.tickIndex = index; + if (axis.type === 'category') { + eventData.dataIndex = tickValue; + } + getECData(textEl).eventData = eventData; + } + // FIXME + transformGroup.add(textEl); + textEl.updateTransform(); + labelEls.push(textEl); + group.add(textEl); + textEl.decomposeTransform(); + }); + return labelEls; + } + + // Build axisPointerModel, mergin tooltip.axisPointer model for each axis. + // allAxesInfo should be updated when setOption performed. + function collect(ecModel, api) { + var result = { + /** + * key: makeKey(axis.model) + * value: { + * axis, + * coordSys, + * axisPointerModel, + * triggerTooltip, + * triggerEmphasis, + * involveSeries, + * snap, + * seriesModels, + * seriesDataCount + * } + */ + axesInfo: {}, + seriesInvolved: false, + /** + * key: makeKey(coordSys.model) + * value: Object: key makeKey(axis.model), value: axisInfo + */ + coordSysAxesInfo: {}, + coordSysMap: {} + }; + collectAxesInfo(result, ecModel, api); + // Check seriesInvolved for performance, in case too many series in some chart. + result.seriesInvolved && collectSeriesInfo(result, ecModel); + return result; + } + function collectAxesInfo(result, ecModel, api) { + var globalTooltipModel = ecModel.getComponent('tooltip'); + var globalAxisPointerModel = ecModel.getComponent('axisPointer'); + // links can only be set on global. + var linksOption = globalAxisPointerModel.get('link', true) || []; + var linkGroups = []; + // Collect axes info. + each(api.getCoordinateSystems(), function (coordSys) { + // Some coordinate system do not support axes, like geo. + if (!coordSys.axisPointerEnabled) { + return; + } + var coordSysKey = makeKey(coordSys.model); + var axesInfoInCoordSys = result.coordSysAxesInfo[coordSysKey] = {}; + result.coordSysMap[coordSysKey] = coordSys; + // Set tooltip (like 'cross') is a convenient way to show axisPointer + // for user. So we enable setting tooltip on coordSys model. + var coordSysModel = coordSys.model; + var baseTooltipModel = coordSysModel.getModel('tooltip', globalTooltipModel); + each(coordSys.getAxes(), curry(saveTooltipAxisInfo, false, null)); + // If axis tooltip used, choose tooltip axis for each coordSys. + // Notice this case: coordSys is `grid` but not `cartesian2D` here. + if (coordSys.getTooltipAxes && globalTooltipModel + // If tooltip.showContent is set as false, tooltip will not + // show but axisPointer will show as normal. + && baseTooltipModel.get('show')) { + // Compatible with previous logic. But series.tooltip.trigger: 'axis' + // or series.data[n].tooltip.trigger: 'axis' are not support any more. + var triggerAxis = baseTooltipModel.get('trigger') === 'axis'; + var cross = baseTooltipModel.get(['axisPointer', 'type']) === 'cross'; + var tooltipAxes = coordSys.getTooltipAxes(baseTooltipModel.get(['axisPointer', 'axis'])); + if (triggerAxis || cross) { + each(tooltipAxes.baseAxes, curry(saveTooltipAxisInfo, cross ? 'cross' : true, triggerAxis)); + } + if (cross) { + each(tooltipAxes.otherAxes, curry(saveTooltipAxisInfo, 'cross', false)); + } + } + // fromTooltip: true | false | 'cross' + // triggerTooltip: true | false | null + function saveTooltipAxisInfo(fromTooltip, triggerTooltip, axis) { + var axisPointerModel = axis.model.getModel('axisPointer', globalAxisPointerModel); + var axisPointerShow = axisPointerModel.get('show'); + if (!axisPointerShow || axisPointerShow === 'auto' && !fromTooltip && !isHandleTrigger(axisPointerModel)) { + return; + } + if (triggerTooltip == null) { + triggerTooltip = axisPointerModel.get('triggerTooltip'); + } + axisPointerModel = fromTooltip ? makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) : axisPointerModel; + var snap = axisPointerModel.get('snap'); + var triggerEmphasis = axisPointerModel.get('triggerEmphasis'); + var axisKey = makeKey(axis.model); + var involveSeries = triggerTooltip || snap || axis.type === 'category'; + // If result.axesInfo[key] exist, override it (tooltip has higher priority). + var axisInfo = result.axesInfo[axisKey] = { + key: axisKey, + axis: axis, + coordSys: coordSys, + axisPointerModel: axisPointerModel, + triggerTooltip: triggerTooltip, + triggerEmphasis: triggerEmphasis, + involveSeries: involveSeries, + snap: snap, + useHandle: isHandleTrigger(axisPointerModel), + seriesModels: [], + linkGroup: null + }; + axesInfoInCoordSys[axisKey] = axisInfo; + result.seriesInvolved = result.seriesInvolved || involveSeries; + var groupIndex = getLinkGroupIndex(linksOption, axis); + if (groupIndex != null) { + var linkGroup = linkGroups[groupIndex] || (linkGroups[groupIndex] = { + axesInfo: {} + }); + linkGroup.axesInfo[axisKey] = axisInfo; + linkGroup.mapper = linksOption[groupIndex].mapper; + axisInfo.linkGroup = linkGroup; + } + } + }); + } + function makeAxisPointerModel(axis, baseTooltipModel, globalAxisPointerModel, ecModel, fromTooltip, triggerTooltip) { + var tooltipAxisPointerModel = baseTooltipModel.getModel('axisPointer'); + var fields = ['type', 'snap', 'lineStyle', 'shadowStyle', 'label', 'animation', 'animationDurationUpdate', 'animationEasingUpdate', 'z']; + var volatileOption = {}; + each(fields, function (field) { + volatileOption[field] = clone(tooltipAxisPointerModel.get(field)); + }); + // category axis do not auto snap, otherwise some tick that do not + // has value can not be hovered. value/time/log axis default snap if + // triggered from tooltip and trigger tooltip. + volatileOption.snap = axis.type !== 'category' && !!triggerTooltip; + // Compatible with previous behavior, tooltip axis does not show label by default. + // Only these properties can be overridden from tooltip to axisPointer. + if (tooltipAxisPointerModel.get('type') === 'cross') { + volatileOption.type = 'line'; + } + var labelOption = volatileOption.label || (volatileOption.label = {}); + // Follow the convention, do not show label when triggered by tooltip by default. + labelOption.show == null && (labelOption.show = false); + if (fromTooltip === 'cross') { + // When 'cross', both axes show labels. + var tooltipAxisPointerLabelShow = tooltipAxisPointerModel.get(['label', 'show']); + labelOption.show = tooltipAxisPointerLabelShow != null ? tooltipAxisPointerLabelShow : true; + // If triggerTooltip, this is a base axis, which should better not use cross style + // (cross style is dashed by default) + if (!triggerTooltip) { + var crossStyle = volatileOption.lineStyle = tooltipAxisPointerModel.get('crossStyle'); + crossStyle && defaults(labelOption, crossStyle.textStyle); + } + } + return axis.model.getModel('axisPointer', new Model(volatileOption, globalAxisPointerModel, ecModel)); + } + function collectSeriesInfo(result, ecModel) { + // Prepare data for axis trigger + ecModel.eachSeries(function (seriesModel) { + // Notice this case: this coordSys is `cartesian2D` but not `grid`. + var coordSys = seriesModel.coordinateSystem; + var seriesTooltipTrigger = seriesModel.get(['tooltip', 'trigger'], true); + var seriesTooltipShow = seriesModel.get(['tooltip', 'show'], true); + if (!coordSys || seriesTooltipTrigger === 'none' || seriesTooltipTrigger === false || seriesTooltipTrigger === 'item' || seriesTooltipShow === false || seriesModel.get(['axisPointer', 'show'], true) === false) { + return; + } + each(result.coordSysAxesInfo[makeKey(coordSys.model)], function (axisInfo) { + var axis = axisInfo.axis; + if (coordSys.getAxis(axis.dim) === axis) { + axisInfo.seriesModels.push(seriesModel); + axisInfo.seriesDataCount == null && (axisInfo.seriesDataCount = 0); + axisInfo.seriesDataCount += seriesModel.getData().count(); + } + }); + }); + } + /** + * For example: + * { + * axisPointer: { + * links: [{ + * xAxisIndex: [2, 4], + * yAxisIndex: 'all' + * }, { + * xAxisId: ['a5', 'a7'], + * xAxisName: 'xxx' + * }] + * } + * } + */ + function getLinkGroupIndex(linksOption, axis) { + var axisModel = axis.model; + var dim = axis.dim; + for (var i = 0; i < linksOption.length; i++) { + var linkOption = linksOption[i] || {}; + if (checkPropInLink(linkOption[dim + 'AxisId'], axisModel.id) || checkPropInLink(linkOption[dim + 'AxisIndex'], axisModel.componentIndex) || checkPropInLink(linkOption[dim + 'AxisName'], axisModel.name)) { + return i; + } + } + } + function checkPropInLink(linkPropValue, axisPropValue) { + return linkPropValue === 'all' || isArray(linkPropValue) && indexOf(linkPropValue, axisPropValue) >= 0 || linkPropValue === axisPropValue; + } + function fixValue(axisModel) { + var axisInfo = getAxisInfo(axisModel); + if (!axisInfo) { + return; + } + var axisPointerModel = axisInfo.axisPointerModel; + var scale = axisInfo.axis.scale; + var option = axisPointerModel.option; + var status = axisPointerModel.get('status'); + var value = axisPointerModel.get('value'); + // Parse init value for category and time axis. + if (value != null) { + value = scale.parse(value); + } + var useHandle = isHandleTrigger(axisPointerModel); + // If `handle` used, `axisPointer` will always be displayed, so value + // and status should be initialized. + if (status == null) { + option.status = useHandle ? 'show' : 'hide'; + } + var extent = scale.getExtent().slice(); + extent[0] > extent[1] && extent.reverse(); + if ( + // Pick a value on axis when initializing. + value == null + // If both `handle` and `dataZoom` are used, value may be out of axis extent, + // where we should re-pick a value to keep `handle` displaying normally. + || value > extent[1]) { + // Make handle displayed on the end of the axis when init, which looks better. + value = extent[1]; + } + if (value < extent[0]) { + value = extent[0]; + } + option.value = value; + if (useHandle) { + option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show'; + } + } + function getAxisInfo(axisModel) { + var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo; + return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)]; + } + function getAxisPointerModel(axisModel) { + var axisInfo = getAxisInfo(axisModel); + return axisInfo && axisInfo.axisPointerModel; + } + function isHandleTrigger(axisPointerModel) { + return !!axisPointerModel.get(['handle', 'show']); + } + /** + * @param {module:echarts/model/Model} model + * @return {string} unique key + */ + function makeKey(model) { + return model.type + '||' + model.id; + } + + var axisPointerClazz = {}; + /** + * Base class of AxisView. + */ + var AxisView = /** @class */function (_super) { + __extends(AxisView, _super); + function AxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = AxisView.type; + return _this; + } + /** + * @override + */ + AxisView.prototype.render = function (axisModel, ecModel, api, payload) { + // FIXME + // This process should proformed after coordinate systems updated + // (axis scale updated), and should be performed each time update. + // So put it here temporarily, although it is not appropriate to + // put a model-writing procedure in `view`. + this.axisPointerClass && fixValue(axisModel); + _super.prototype.render.apply(this, arguments); + this._doUpdateAxisPointerClass(axisModel, api, true); + }; + /** + * Action handler. + */ + AxisView.prototype.updateAxisPointer = function (axisModel, ecModel, api, payload) { + this._doUpdateAxisPointerClass(axisModel, api, false); + }; + /** + * @override + */ + AxisView.prototype.remove = function (ecModel, api) { + var axisPointer = this._axisPointer; + axisPointer && axisPointer.remove(api); + }; + /** + * @override + */ + AxisView.prototype.dispose = function (ecModel, api) { + this._disposeAxisPointer(api); + _super.prototype.dispose.apply(this, arguments); + }; + AxisView.prototype._doUpdateAxisPointerClass = function (axisModel, api, forceRender) { + var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass); + if (!Clazz) { + return; + } + var axisPointerModel = getAxisPointerModel(axisModel); + axisPointerModel ? (this._axisPointer || (this._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : this._disposeAxisPointer(api); + }; + AxisView.prototype._disposeAxisPointer = function (api) { + this._axisPointer && this._axisPointer.dispose(api); + this._axisPointer = null; + }; + AxisView.registerAxisPointerClass = function (type, clazz) { + if ("development" !== 'production') { + if (axisPointerClazz[type]) { + throw new Error('axisPointer ' + type + ' exists'); + } + } + axisPointerClazz[type] = clazz; + }; + AxisView.getAxisPointerClass = function (type) { + return type && axisPointerClazz[type]; + }; + AxisView.type = 'axis'; + return AxisView; + }(ComponentView); + + var inner$6 = makeInner(); + function rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel) { + var axis = axisModel.axis; + if (axis.scale.isBlank()) { + return; + } + // TODO: TYPE + var splitAreaModel = axisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var gridRect = gridModel.coordinateSystem.getRect(); + var ticksCoords = axis.getTicksCoords({ + tickModel: splitAreaModel, + clamp: true + }); + if (!ticksCoords.length) { + return; + } + // For Making appropriate splitArea animation, the color and anid + // should be corresponding to previous one if possible. + var areaColorsLen = areaColors.length; + var lastSplitAreaColors = inner$6(axisView).splitAreaColors; + var newSplitAreaColors = createHashMap(); + var colorIndex = 0; + if (lastSplitAreaColors) { + for (var i = 0; i < ticksCoords.length; i++) { + var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue); + if (cIndex != null) { + colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen; + break; + } + } + } + var prev = axis.toGlobalCoord(ticksCoords[0].coord); + var areaStyle = areaStyleModel.getAreaStyle(); + areaColors = isArray(areaColors) ? areaColors : [areaColors]; + for (var i = 1; i < ticksCoords.length; i++) { + var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); + var x = void 0; + var y = void 0; + var width = void 0; + var height = void 0; + if (axis.isHorizontal()) { + x = prev; + y = gridRect.y; + width = tickCoord - x; + height = gridRect.height; + prev = x + width; + } else { + x = gridRect.x; + y = prev; + width = gridRect.width; + height = tickCoord - y; + prev = y + height; + } + var tickValue = ticksCoords[i - 1].tickValue; + tickValue != null && newSplitAreaColors.set(tickValue, colorIndex); + axisGroup.add(new Rect({ + anid: tickValue != null ? 'area_' + tickValue : null, + shape: { + x: x, + y: y, + width: width, + height: height + }, + style: defaults({ + fill: areaColors[colorIndex] + }, areaStyle), + autoBatch: true, + silent: true + })); + colorIndex = (colorIndex + 1) % areaColorsLen; + } + inner$6(axisView).splitAreaColors = newSplitAreaColors; + } + function rectCoordAxisHandleRemove(axisView) { + inner$6(axisView).splitAreaColors = null; + } + + var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName']; + var selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine']; + var CartesianAxisView = /** @class */function (_super) { + __extends(CartesianAxisView, _super); + function CartesianAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CartesianAxisView.type; + _this.axisPointerClass = 'CartesianAxisPointer'; + return _this; + } + /** + * @override + */ + CartesianAxisView.prototype.render = function (axisModel, ecModel, api, payload) { + this.group.removeAll(); + var oldAxisGroup = this._axisGroup; + this._axisGroup = new Group(); + this.group.add(this._axisGroup); + if (!axisModel.get('show')) { + return; + } + var gridModel = axisModel.getCoordSysModel(); + var layout = layout$1(gridModel, axisModel); + var axisBuilder = new AxisBuilder(axisModel, extend({ + handleAutoShown: function (elementType) { + var cartesians = gridModel.coordinateSystem.getCartesians(); + for (var i = 0; i < cartesians.length; i++) { + if (isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) { + // Still show axis tick or axisLine if other axis is value / log + return true; + } + } + // Not show axisTick or axisLine if other axis is category / time + return false; + } + }, layout)); + each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + this._axisGroup.add(axisBuilder.getGroup()); + each(selfBuilderAttrs, function (name) { + if (axisModel.get([name, 'show'])) { + axisElementBuilders[name](this, this._axisGroup, axisModel, gridModel); + } + }, this); + // THIS is a special case for bar racing chart. + // Update the axis label from the natural initial layout to + // sorted layout should has no animation. + var isInitialSortFromBarRacing = payload && payload.type === 'changeAxisOrder' && payload.isInitSort; + if (!isInitialSortFromBarRacing) { + groupTransition(oldAxisGroup, this._axisGroup, axisModel); + } + _super.prototype.render.call(this, axisModel, ecModel, api, payload); + }; + CartesianAxisView.prototype.remove = function () { + rectCoordAxisHandleRemove(this); + }; + CartesianAxisView.type = 'cartesianAxis'; + return CartesianAxisView; + }(AxisView); + var axisElementBuilders = { + splitLine: function (axisView, axisGroup, axisModel, gridModel) { + var axis = axisModel.axis; + if (axis.scale.isBlank()) { + return; + } + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + lineColors = isArray(lineColors) ? lineColors : [lineColors]; + var gridRect = gridModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + var lineCount = 0; + var ticksCoords = axis.getTicksCoords({ + tickModel: splitLineModel + }); + var p1 = []; + var p2 = []; + var lineStyle = lineStyleModel.getLineStyle(); + for (var i = 0; i < ticksCoords.length; i++) { + var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + var colorIndex = lineCount++ % lineColors.length; + var tickValue = ticksCoords[i].tickValue; + var line = new Line({ + anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null, + autoBatch: true, + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: defaults({ + stroke: lineColors[colorIndex] + }, lineStyle), + silent: true + }); + subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth); + axisGroup.add(line); + } + }, + minorSplitLine: function (axisView, axisGroup, axisModel, gridModel) { + var axis = axisModel.axis; + var minorSplitLineModel = axisModel.getModel('minorSplitLine'); + var lineStyleModel = minorSplitLineModel.getModel('lineStyle'); + var gridRect = gridModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + var minorTicksCoords = axis.getMinorTicksCoords(); + if (!minorTicksCoords.length) { + return; + } + var p1 = []; + var p2 = []; + var lineStyle = lineStyleModel.getLineStyle(); + for (var i = 0; i < minorTicksCoords.length; i++) { + for (var k = 0; k < minorTicksCoords[i].length; k++) { + var tickCoord = axis.toGlobalCoord(minorTicksCoords[i][k].coord); + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + var line = new Line({ + anid: 'minor_line_' + minorTicksCoords[i][k].tickValue, + autoBatch: true, + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: lineStyle, + silent: true + }); + subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth); + axisGroup.add(line); + } + } + }, + splitArea: function (axisView, axisGroup, axisModel, gridModel) { + rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel); + } + }; + var CartesianXAxisView = /** @class */function (_super) { + __extends(CartesianXAxisView, _super); + function CartesianXAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CartesianXAxisView.type; + return _this; + } + CartesianXAxisView.type = 'xAxis'; + return CartesianXAxisView; + }(CartesianAxisView); + var CartesianYAxisView = /** @class */function (_super) { + __extends(CartesianYAxisView, _super); + function CartesianYAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CartesianXAxisView.type; + return _this; + } + CartesianYAxisView.type = 'yAxis'; + return CartesianYAxisView; + }(CartesianAxisView); + + // Grid view + var GridView = /** @class */function (_super) { + __extends(GridView, _super); + function GridView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'grid'; + return _this; + } + GridView.prototype.render = function (gridModel, ecModel) { + this.group.removeAll(); + if (gridModel.get('show')) { + this.group.add(new Rect({ + shape: gridModel.coordinateSystem.getRect(), + style: defaults({ + fill: gridModel.get('backgroundColor') + }, gridModel.getItemStyle()), + silent: true, + z2: -1 + })); + } + }; + GridView.type = 'grid'; + return GridView; + }(ComponentView); + var extraOption = { + // gridIndex: 0, + // gridId: '', + offset: 0 + }; + function install$5(registers) { + registers.registerComponentView(GridView); + registers.registerComponentModel(GridModel); + registers.registerCoordinateSystem('cartesian2d', Grid); + axisModelCreator(registers, 'x', CartesianAxisModel, extraOption); + axisModelCreator(registers, 'y', CartesianAxisModel, extraOption); + registers.registerComponentView(CartesianXAxisView); + registers.registerComponentView(CartesianYAxisView); + registers.registerPreprocessor(function (option) { + // Only create grid when need + if (option.xAxis && option.yAxis && !option.grid) { + option.grid = {}; + } + }); + } + + function install$6(registers) { + // In case developer forget to include grid component + use(install$5); + registers.registerSeriesModel(ScatterSeriesModel); + registers.registerChartView(ScatterView); + registers.registerLayout(pointsLayout('scatter')); + } + + function radarLayout(ecModel) { + ecModel.eachSeriesByType('radar', function (seriesModel) { + var data = seriesModel.getData(); + var points = []; + var coordSys = seriesModel.coordinateSystem; + if (!coordSys) { + return; + } + var axes = coordSys.getIndicatorAxes(); + each(axes, function (axis, axisIndex) { + data.each(data.mapDimension(axes[axisIndex].dim), function (val, dataIndex) { + points[dataIndex] = points[dataIndex] || []; + var point = coordSys.dataToPoint(val, axisIndex); + points[dataIndex][axisIndex] = isValidPoint(point) ? point : getValueMissingPoint(coordSys); + }); + }); + // Close polygon + data.each(function (idx) { + // TODO + // Is it appropriate to connect to the next data when some data is missing? + // Or, should trade it like `connectNull` in line chart? + var firstPoint = find(points[idx], function (point) { + return isValidPoint(point); + }) || getValueMissingPoint(coordSys); + // Copy the first actual point to the end of the array + points[idx].push(firstPoint.slice()); + data.setItemLayout(idx, points[idx]); + }); + }); + } + function isValidPoint(point) { + return !isNaN(point[0]) && !isNaN(point[1]); + } + function getValueMissingPoint(coordSys) { + // It is error-prone to input [NaN, NaN] into polygon, polygon. + // (probably cause problem when refreshing or animating) + return [coordSys.cx, coordSys.cy]; + } + + function radarBackwardCompat(option) { + var polarOptArr = option.polar; + if (polarOptArr) { + if (!isArray(polarOptArr)) { + polarOptArr = [polarOptArr]; + } + var polarNotRadar_1 = []; + each(polarOptArr, function (polarOpt, idx) { + if (polarOpt.indicator) { + if (polarOpt.type && !polarOpt.shape) { + polarOpt.shape = polarOpt.type; + } + option.radar = option.radar || []; + if (!isArray(option.radar)) { + option.radar = [option.radar]; + } + option.radar.push(polarOpt); + } else { + polarNotRadar_1.push(polarOpt); + } + }); + option.polar = polarNotRadar_1; + } + each(option.series, function (seriesOpt) { + if (seriesOpt && seriesOpt.type === 'radar' && seriesOpt.polarIndex) { + seriesOpt.radarIndex = seriesOpt.polarIndex; + } + }); + } + + var RadarView = /** @class */function (_super) { + __extends(RadarView, _super); + function RadarView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = RadarView.type; + return _this; + } + RadarView.prototype.render = function (seriesModel, ecModel, api) { + var polar = seriesModel.coordinateSystem; + var group = this.group; + var data = seriesModel.getData(); + var oldData = this._data; + function createSymbol$1(data, idx) { + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + if (symbolType === 'none') { + return; + } + var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + var symbolPath = createSymbol(symbolType, -1, -1, 2, 2); + var symbolRotate = data.getItemVisual(idx, 'symbolRotate') || 0; + symbolPath.attr({ + style: { + strokeNoScale: true + }, + z2: 100, + scaleX: symbolSize[0] / 2, + scaleY: symbolSize[1] / 2, + rotation: symbolRotate * Math.PI / 180 || 0 + }); + return symbolPath; + } + function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) { + // Simply rerender all + symbolGroup.removeAll(); + for (var i = 0; i < newPoints.length - 1; i++) { + var symbolPath = createSymbol$1(data, idx); + if (symbolPath) { + symbolPath.__dimIdx = i; + if (oldPoints[i]) { + symbolPath.setPosition(oldPoints[i]); + graphic[isInit ? 'initProps' : 'updateProps'](symbolPath, { + x: newPoints[i][0], + y: newPoints[i][1] + }, seriesModel, idx); + } else { + symbolPath.setPosition(newPoints[i]); + } + symbolGroup.add(symbolPath); + } + } + } + function getInitialPoints(points) { + return map(points, function (pt) { + return [polar.cx, polar.cy]; + }); + } + data.diff(oldData).add(function (idx) { + var points = data.getItemLayout(idx); + if (!points) { + return; + } + var polygon = new Polygon(); + var polyline = new Polyline(); + var target = { + shape: { + points: points + } + }; + polygon.shape.points = getInitialPoints(points); + polyline.shape.points = getInitialPoints(points); + initProps(polygon, target, seriesModel, idx); + initProps(polyline, target, seriesModel, idx); + var itemGroup = new Group(); + var symbolGroup = new Group(); + itemGroup.add(polyline); + itemGroup.add(polygon); + itemGroup.add(symbolGroup); + updateSymbols(polyline.shape.points, points, symbolGroup, data, idx, true); + data.setItemGraphicEl(idx, itemGroup); + }).update(function (newIdx, oldIdx) { + var itemGroup = oldData.getItemGraphicEl(oldIdx); + var polyline = itemGroup.childAt(0); + var polygon = itemGroup.childAt(1); + var symbolGroup = itemGroup.childAt(2); + var target = { + shape: { + points: data.getItemLayout(newIdx) + } + }; + if (!target.shape.points) { + return; + } + updateSymbols(polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false); + saveOldStyle(polygon); + saveOldStyle(polyline); + updateProps(polyline, target, seriesModel); + updateProps(polygon, target, seriesModel); + data.setItemGraphicEl(newIdx, itemGroup); + }).remove(function (idx) { + group.remove(oldData.getItemGraphicEl(idx)); + }).execute(); + data.eachItemGraphicEl(function (itemGroup, idx) { + var itemModel = data.getItemModel(idx); + var polyline = itemGroup.childAt(0); + var polygon = itemGroup.childAt(1); + var symbolGroup = itemGroup.childAt(2); + // Radar uses the visual encoded from itemStyle. + var itemStyle = data.getItemVisual(idx, 'style'); + var color = itemStyle.fill; + group.add(itemGroup); + polyline.useStyle(defaults(itemModel.getModel('lineStyle').getLineStyle(), { + fill: 'none', + stroke: color + })); + setStatesStylesFromModel(polyline, itemModel, 'lineStyle'); + setStatesStylesFromModel(polygon, itemModel, 'areaStyle'); + var areaStyleModel = itemModel.getModel('areaStyle'); + var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty(); + polygon.ignore = polygonIgnore; + each(['emphasis', 'select', 'blur'], function (stateName) { + var stateModel = itemModel.getModel([stateName, 'areaStyle']); + var stateIgnore = stateModel.isEmpty() && stateModel.parentModel.isEmpty(); + // Won't be ignore if normal state is not ignore. + polygon.ensureState(stateName).ignore = stateIgnore && polygonIgnore; + }); + polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), { + fill: color, + opacity: 0.7, + decal: itemStyle.decal + })); + var emphasisModel = itemModel.getModel('emphasis'); + var itemHoverStyle = emphasisModel.getModel('itemStyle').getItemStyle(); + symbolGroup.eachChild(function (symbolPath) { + if (symbolPath instanceof ZRImage) { + var pathStyle = symbolPath.style; + symbolPath.useStyle(extend({ + // TODO other properties like x, y ? + image: pathStyle.image, + x: pathStyle.x, + y: pathStyle.y, + width: pathStyle.width, + height: pathStyle.height + }, itemStyle)); + } else { + symbolPath.useStyle(itemStyle); + symbolPath.setColor(color); + symbolPath.style.strokeNoScale = true; + } + var pathEmphasisState = symbolPath.ensureState('emphasis'); + pathEmphasisState.style = clone(itemHoverStyle); + var defaultText = data.getStore().get(data.getDimensionIndex(symbolPath.__dimIdx), idx); + (defaultText == null || isNaN(defaultText)) && (defaultText = ''); + setLabelStyle(symbolPath, getLabelStatesModels(itemModel), { + labelFetcher: data.hostModel, + labelDataIndex: idx, + labelDimIndex: symbolPath.__dimIdx, + defaultText: defaultText, + inheritColor: color, + defaultOpacity: itemStyle.opacity + }); + }); + toggleHoverEmphasis(itemGroup, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }); + this._data = data; + }; + RadarView.prototype.remove = function () { + this.group.removeAll(); + this._data = null; + }; + RadarView.type = 'radar'; + return RadarView; + }(ChartView); + + var RadarSeriesModel = /** @class */function (_super) { + __extends(RadarSeriesModel, _super); + function RadarSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = RadarSeriesModel.type; + _this.hasSymbolVisual = true; + return _this; + } + // Overwrite + RadarSeriesModel.prototype.init = function (option) { + _super.prototype.init.apply(this, arguments); + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); + }; + RadarSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesDataSimply(this, { + generateCoord: 'indicator_', + generateCoordCount: Infinity + }); + }; + RadarSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + var data = this.getData(); + var coordSys = this.coordinateSystem; + var indicatorAxes = coordSys.getIndicatorAxes(); + var name = this.getData().getName(dataIndex); + var nameToDisplay = name === '' ? this.name : name; + var markerColor = retrieveVisualColorForTooltipMarker(this, dataIndex); + return createTooltipMarkup('section', { + header: nameToDisplay, + sortBlocks: true, + blocks: map(indicatorAxes, function (axis) { + var val = data.get(data.mapDimension(axis.dim), dataIndex); + return createTooltipMarkup('nameValue', { + markerType: 'subItem', + markerColor: markerColor, + name: axis.name, + value: val, + sortParam: val + }); + }) + }); + }; + RadarSeriesModel.prototype.getTooltipPosition = function (dataIndex) { + if (dataIndex != null) { + var data_1 = this.getData(); + var coordSys = this.coordinateSystem; + var values = data_1.getValues(map(coordSys.dimensions, function (dim) { + return data_1.mapDimension(dim); + }), dataIndex); + for (var i = 0, len = values.length; i < len; i++) { + if (!isNaN(values[i])) { + var indicatorAxes = coordSys.getIndicatorAxes(); + return coordSys.coordToPoint(indicatorAxes[i].dataToCoord(values[i]), i); + } + } + } + }; + RadarSeriesModel.type = 'series.radar'; + RadarSeriesModel.dependencies = ['radar']; + RadarSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + colorBy: 'data', + coordinateSystem: 'radar', + legendHoverLink: true, + radarIndex: 0, + lineStyle: { + width: 2, + type: 'solid', + join: 'round' + }, + label: { + position: 'top' + }, + // areaStyle: { + // }, + // itemStyle: {} + symbolSize: 8 + // symbolRotate: null + }; + + return RadarSeriesModel; + }(SeriesModel); + + var valueAxisDefault = axisDefault.value; + function defaultsShow(opt, show) { + return defaults({ + show: show + }, opt); + } + var RadarModel = /** @class */function (_super) { + __extends(RadarModel, _super); + function RadarModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = RadarModel.type; + return _this; + } + RadarModel.prototype.optionUpdated = function () { + var boundaryGap = this.get('boundaryGap'); + var splitNumber = this.get('splitNumber'); + var scale = this.get('scale'); + var axisLine = this.get('axisLine'); + var axisTick = this.get('axisTick'); + // let axisType = this.get('axisType'); + var axisLabel = this.get('axisLabel'); + var nameTextStyle = this.get('axisName'); + var showName = this.get(['axisName', 'show']); + var nameFormatter = this.get(['axisName', 'formatter']); + var nameGap = this.get('axisNameGap'); + var triggerEvent = this.get('triggerEvent'); + var indicatorModels = map(this.get('indicator') || [], function (indicatorOpt) { + // PENDING + if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) { + indicatorOpt.min = 0; + } else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) { + indicatorOpt.max = 0; + } + var iNameTextStyle = nameTextStyle; + if (indicatorOpt.color != null) { + iNameTextStyle = defaults({ + color: indicatorOpt.color + }, nameTextStyle); + } + // Use same configuration + var innerIndicatorOpt = merge(clone(indicatorOpt), { + boundaryGap: boundaryGap, + splitNumber: splitNumber, + scale: scale, + axisLine: axisLine, + axisTick: axisTick, + // axisType: axisType, + axisLabel: axisLabel, + // Compatible with 2 and use text + name: indicatorOpt.text, + showName: showName, + nameLocation: 'end', + nameGap: nameGap, + // min: 0, + nameTextStyle: iNameTextStyle, + triggerEvent: triggerEvent + }, false); + if (isString(nameFormatter)) { + var indName = innerIndicatorOpt.name; + innerIndicatorOpt.name = nameFormatter.replace('{value}', indName != null ? indName : ''); + } else if (isFunction(nameFormatter)) { + innerIndicatorOpt.name = nameFormatter(innerIndicatorOpt.name, innerIndicatorOpt); + } + var model = new Model(innerIndicatorOpt, null, this.ecModel); + mixin(model, AxisModelCommonMixin.prototype); + // For triggerEvent. + model.mainType = 'radar'; + model.componentIndex = this.componentIndex; + return model; + }, this); + this._indicatorModels = indicatorModels; + }; + RadarModel.prototype.getIndicatorModels = function () { + return this._indicatorModels; + }; + RadarModel.type = 'radar'; + RadarModel.defaultOption = { + // zlevel: 0, + z: 0, + center: ['50%', '50%'], + radius: '75%', + startAngle: 90, + axisName: { + show: true + // formatter: null + // textStyle: {} + }, + + boundaryGap: [0, 0], + splitNumber: 5, + axisNameGap: 15, + scale: false, + // Polygon or circle + shape: 'polygon', + axisLine: merge({ + lineStyle: { + color: '#bbb' + } + }, valueAxisDefault.axisLine), + axisLabel: defaultsShow(valueAxisDefault.axisLabel, false), + axisTick: defaultsShow(valueAxisDefault.axisTick, false), + // axisType: 'value', + splitLine: defaultsShow(valueAxisDefault.splitLine, true), + splitArea: defaultsShow(valueAxisDefault.splitArea, true), + // {text, min, max} + indicator: [] + }; + return RadarModel; + }(ComponentModel); + + var axisBuilderAttrs$1 = ['axisLine', 'axisTickLabel', 'axisName']; + var RadarView$1 = /** @class */function (_super) { + __extends(RadarView, _super); + function RadarView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = RadarView.type; + return _this; + } + RadarView.prototype.render = function (radarModel, ecModel, api) { + var group = this.group; + group.removeAll(); + this._buildAxes(radarModel); + this._buildSplitLineAndArea(radarModel); + }; + RadarView.prototype._buildAxes = function (radarModel) { + var radar = radarModel.coordinateSystem; + var indicatorAxes = radar.getIndicatorAxes(); + var axisBuilders = map(indicatorAxes, function (indicatorAxis) { + var axisName = indicatorAxis.model.get('showName') ? indicatorAxis.name : ''; // hide name + var axisBuilder = new AxisBuilder(indicatorAxis.model, { + axisName: axisName, + position: [radar.cx, radar.cy], + rotation: indicatorAxis.angle, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1 + }); + return axisBuilder; + }); + each(axisBuilders, function (axisBuilder) { + each(axisBuilderAttrs$1, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + }, this); + }; + RadarView.prototype._buildSplitLineAndArea = function (radarModel) { + var radar = radarModel.coordinateSystem; + var indicatorAxes = radar.getIndicatorAxes(); + if (!indicatorAxes.length) { + return; + } + var shape = radarModel.get('shape'); + var splitLineModel = radarModel.getModel('splitLine'); + var splitAreaModel = radarModel.getModel('splitArea'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var showSplitLine = splitLineModel.get('show'); + var showSplitArea = splitAreaModel.get('show'); + var splitLineColors = lineStyleModel.get('color'); + var splitAreaColors = areaStyleModel.get('color'); + var splitLineColorsArr = isArray(splitLineColors) ? splitLineColors : [splitLineColors]; + var splitAreaColorsArr = isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors]; + var splitLines = []; + var splitAreas = []; + function getColorIndex(areaOrLine, areaOrLineColorList, idx) { + var colorIndex = idx % areaOrLineColorList.length; + areaOrLine[colorIndex] = areaOrLine[colorIndex] || []; + return colorIndex; + } + if (shape === 'circle') { + var ticksRadius = indicatorAxes[0].getTicksCoords(); + var cx = radar.cx; + var cy = radar.cy; + for (var i = 0; i < ticksRadius.length; i++) { + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColorsArr, i); + splitLines[colorIndex].push(new Circle({ + shape: { + cx: cx, + cy: cy, + r: ticksRadius[i].coord + } + })); + } + if (showSplitArea && i < ticksRadius.length - 1) { + var colorIndex = getColorIndex(splitAreas, splitAreaColorsArr, i); + splitAreas[colorIndex].push(new Ring({ + shape: { + cx: cx, + cy: cy, + r0: ticksRadius[i].coord, + r: ticksRadius[i + 1].coord + } + })); + } + } + } + // Polyyon + else { + var realSplitNumber_1; + var axesTicksPoints = map(indicatorAxes, function (indicatorAxis, idx) { + var ticksCoords = indicatorAxis.getTicksCoords(); + realSplitNumber_1 = realSplitNumber_1 == null ? ticksCoords.length - 1 : Math.min(ticksCoords.length - 1, realSplitNumber_1); + return map(ticksCoords, function (tickCoord) { + return radar.coordToPoint(tickCoord.coord, idx); + }); + }); + var prevPoints = []; + for (var i = 0; i <= realSplitNumber_1; i++) { + var points = []; + for (var j = 0; j < indicatorAxes.length; j++) { + points.push(axesTicksPoints[j][i]); + } + // Close + if (points[0]) { + points.push(points[0].slice()); + } else { + if ("development" !== 'production') { + console.error('Can\'t draw value axis ' + i); + } + } + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColorsArr, i); + splitLines[colorIndex].push(new Polyline({ + shape: { + points: points + } + })); + } + if (showSplitArea && prevPoints) { + var colorIndex = getColorIndex(splitAreas, splitAreaColorsArr, i - 1); + splitAreas[colorIndex].push(new Polygon({ + shape: { + points: points.concat(prevPoints) + } + })); + } + prevPoints = points.slice().reverse(); + } + } + var lineStyle = lineStyleModel.getLineStyle(); + var areaStyle = areaStyleModel.getAreaStyle(); + // Add splitArea before splitLine + each(splitAreas, function (splitAreas, idx) { + this.group.add(mergePath$1(splitAreas, { + style: defaults({ + stroke: 'none', + fill: splitAreaColorsArr[idx % splitAreaColorsArr.length] + }, areaStyle), + silent: true + })); + }, this); + each(splitLines, function (splitLines, idx) { + this.group.add(mergePath$1(splitLines, { + style: defaults({ + fill: 'none', + stroke: splitLineColorsArr[idx % splitLineColorsArr.length] + }, lineStyle), + silent: true + })); + }, this); + }; + RadarView.type = 'radar'; + return RadarView; + }(ComponentView); + + var IndicatorAxis = /** @class */function (_super) { + __extends(IndicatorAxis, _super); + function IndicatorAxis(dim, scale, radiusExtent) { + var _this = _super.call(this, dim, scale, radiusExtent) || this; + _this.type = 'value'; + _this.angle = 0; + _this.name = ''; + return _this; + } + return IndicatorAxis; + }(Axis); + + var Radar = /** @class */function () { + function Radar(radarModel, ecModel, api) { + /** + * + * Radar dimensions + */ + this.dimensions = []; + this._model = radarModel; + this._indicatorAxes = map(radarModel.getIndicatorModels(), function (indicatorModel, idx) { + var dim = 'indicator_' + idx; + var indicatorAxis = new IndicatorAxis(dim, new IntervalScale() + // (indicatorModel.get('axisType') === 'log') ? new LogScale() : new IntervalScale() + ); + + indicatorAxis.name = indicatorModel.get('name'); + // Inject model and axis + indicatorAxis.model = indicatorModel; + indicatorModel.axis = indicatorAxis; + this.dimensions.push(dim); + return indicatorAxis; + }, this); + this.resize(radarModel, api); + } + Radar.prototype.getIndicatorAxes = function () { + return this._indicatorAxes; + }; + Radar.prototype.dataToPoint = function (value, indicatorIndex) { + var indicatorAxis = this._indicatorAxes[indicatorIndex]; + return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex); + }; + // TODO: API should be coordToPoint([coord, indicatorIndex]) + Radar.prototype.coordToPoint = function (coord, indicatorIndex) { + var indicatorAxis = this._indicatorAxes[indicatorIndex]; + var angle = indicatorAxis.angle; + var x = this.cx + coord * Math.cos(angle); + var y = this.cy - coord * Math.sin(angle); + return [x, y]; + }; + Radar.prototype.pointToData = function (pt) { + var dx = pt[0] - this.cx; + var dy = pt[1] - this.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + dx /= radius; + dy /= radius; + var radian = Math.atan2(-dy, dx); + // Find the closest angle + // FIXME index can calculated directly + var minRadianDiff = Infinity; + var closestAxis; + var closestAxisIdx = -1; + for (var i = 0; i < this._indicatorAxes.length; i++) { + var indicatorAxis = this._indicatorAxes[i]; + var diff = Math.abs(radian - indicatorAxis.angle); + if (diff < minRadianDiff) { + closestAxis = indicatorAxis; + closestAxisIdx = i; + minRadianDiff = diff; + } + } + return [closestAxisIdx, +(closestAxis && closestAxis.coordToData(radius))]; + }; + Radar.prototype.resize = function (radarModel, api) { + var center = radarModel.get('center'); + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + var viewSize = Math.min(viewWidth, viewHeight) / 2; + this.cx = parsePercent$1(center[0], viewWidth); + this.cy = parsePercent$1(center[1], viewHeight); + this.startAngle = radarModel.get('startAngle') * Math.PI / 180; + // radius may be single value like `20`, `'80%'`, or array like `[10, '80%']` + var radius = radarModel.get('radius'); + if (isString(radius) || isNumber(radius)) { + radius = [0, radius]; + } + this.r0 = parsePercent$1(radius[0], viewSize); + this.r = parsePercent$1(radius[1], viewSize); + each(this._indicatorAxes, function (indicatorAxis, idx) { + indicatorAxis.setExtent(this.r0, this.r); + var angle = this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length; + // Normalize to [-PI, PI] + angle = Math.atan2(Math.sin(angle), Math.cos(angle)); + indicatorAxis.angle = angle; + }, this); + }; + Radar.prototype.update = function (ecModel, api) { + var indicatorAxes = this._indicatorAxes; + var radarModel = this._model; + each(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.setExtent(Infinity, -Infinity); + }); + ecModel.eachSeriesByType('radar', function (radarSeries, idx) { + if (radarSeries.get('coordinateSystem') !== 'radar' + // @ts-ignore + || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel) { + return; + } + var data = radarSeries.getData(); + each(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.unionExtentFromData(data, data.mapDimension(indicatorAxis.dim)); + }); + }, this); + var splitNumber = radarModel.get('splitNumber'); + var dummyScale = new IntervalScale(); + dummyScale.setExtent(0, splitNumber); + dummyScale.setInterval(1); + // Force all the axis fixing the maxSplitNumber. + each(indicatorAxes, function (indicatorAxis, idx) { + alignScaleTicks(indicatorAxis.scale, indicatorAxis.model, dummyScale); + }); + }; + Radar.prototype.convertToPixel = function (ecModel, finder, value) { + console.warn('Not implemented.'); + return null; + }; + Radar.prototype.convertFromPixel = function (ecModel, finder, pixel) { + console.warn('Not implemented.'); + return null; + }; + Radar.prototype.containPoint = function (point) { + console.warn('Not implemented.'); + return false; + }; + Radar.create = function (ecModel, api) { + var radarList = []; + ecModel.eachComponent('radar', function (radarModel) { + var radar = new Radar(radarModel, ecModel, api); + radarList.push(radar); + radarModel.coordinateSystem = radar; + }); + ecModel.eachSeriesByType('radar', function (radarSeries) { + if (radarSeries.get('coordinateSystem') === 'radar') { + // Inject coordinate system + // @ts-ignore + radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0]; + } + }); + return radarList; + }; + /** + * Radar dimensions is based on the data + */ + Radar.dimensions = []; + return Radar; + }(); + + function install$7(registers) { + registers.registerCoordinateSystem('radar', Radar); + registers.registerComponentModel(RadarModel); + registers.registerComponentView(RadarView$1); + registers.registerVisual({ + seriesType: 'radar', + reset: function (seriesModel) { + var data = seriesModel.getData(); + // itemVisual symbol is for selected data + data.each(function (idx) { + data.setItemVisual(idx, 'legendIcon', 'roundRect'); + }); + // visual is for unselected data + data.setVisual('legendIcon', 'roundRect'); + } + }); + } + + function install$8(registers) { + use(install$7); + registers.registerChartView(RadarView); + registers.registerSeriesModel(RadarSeriesModel); + registers.registerLayout(radarLayout); + registers.registerProcessor(dataFilter('radar')); + registers.registerPreprocessor(radarBackwardCompat); + } + + var ATTR = '\0_ec_interaction_mutex'; + function take(zr, resourceKey, userKey) { + var store = getStore(zr); + store[resourceKey] = userKey; + } + function release(zr, resourceKey, userKey) { + var store = getStore(zr); + var uKey = store[resourceKey]; + if (uKey === userKey) { + store[resourceKey] = null; + } + } + function isTaken(zr, resourceKey) { + return !!getStore(zr)[resourceKey]; + } + function getStore(zr) { + return zr[ATTR] || (zr[ATTR] = {}); + } + /** + * payload: { + * type: 'takeGlobalCursor', + * key: 'dataZoomSelect', or 'brush', or ..., + * If no userKey, release global cursor. + * } + */ + // TODO: SELF REGISTERED. + registerAction({ + type: 'takeGlobalCursor', + event: 'globalCursorTaken', + update: 'update' + }, noop); + + var RoamController = /** @class */function (_super) { + __extends(RoamController, _super); + function RoamController(zr) { + var _this = _super.call(this) || this; + _this._zr = zr; + // Avoid two roamController bind the same handler + var mousedownHandler = bind(_this._mousedownHandler, _this); + var mousemoveHandler = bind(_this._mousemoveHandler, _this); + var mouseupHandler = bind(_this._mouseupHandler, _this); + var mousewheelHandler = bind(_this._mousewheelHandler, _this); + var pinchHandler = bind(_this._pinchHandler, _this); + /** + * Notice: only enable needed types. For example, if 'zoom' + * is not needed, 'zoom' should not be enabled, otherwise + * default mousewheel behaviour (scroll page) will be disabled. + */ + _this.enable = function (controlType, opt) { + // Disable previous first + this.disable(); + this._opt = defaults(clone(opt) || {}, { + zoomOnMouseWheel: true, + moveOnMouseMove: true, + // By default, wheel do not trigger move. + moveOnMouseWheel: false, + preventDefaultMouseMove: true + }); + if (controlType == null) { + controlType = true; + } + if (controlType === true || controlType === 'move' || controlType === 'pan') { + zr.on('mousedown', mousedownHandler); + zr.on('mousemove', mousemoveHandler); + zr.on('mouseup', mouseupHandler); + } + if (controlType === true || controlType === 'scale' || controlType === 'zoom') { + zr.on('mousewheel', mousewheelHandler); + zr.on('pinch', pinchHandler); + } + }; + _this.disable = function () { + zr.off('mousedown', mousedownHandler); + zr.off('mousemove', mousemoveHandler); + zr.off('mouseup', mouseupHandler); + zr.off('mousewheel', mousewheelHandler); + zr.off('pinch', pinchHandler); + }; + return _this; + } + RoamController.prototype.isDragging = function () { + return this._dragging; + }; + RoamController.prototype.isPinching = function () { + return this._pinching; + }; + RoamController.prototype.setPointerChecker = function (pointerChecker) { + this.pointerChecker = pointerChecker; + }; + RoamController.prototype.dispose = function () { + this.disable(); + }; + RoamController.prototype._mousedownHandler = function (e) { + if (isMiddleOrRightButtonOnMouseUpDown(e)) { + return; + } + var el = e.target; + while (el) { + if (el.draggable) { + return; + } + // check if host is draggable + el = el.__hostTarget || el.parent; + } + var x = e.offsetX; + var y = e.offsetY; + // Only check on mosedown, but not mousemove. + // Mouse can be out of target when mouse moving. + if (this.pointerChecker && this.pointerChecker(e, x, y)) { + this._x = x; + this._y = y; + this._dragging = true; + } + }; + RoamController.prototype._mousemoveHandler = function (e) { + if (!this._dragging || !isAvailableBehavior('moveOnMouseMove', e, this._opt) || e.gestureEvent === 'pinch' || isTaken(this._zr, 'globalPan')) { + return; + } + var x = e.offsetX; + var y = e.offsetY; + var oldX = this._x; + var oldY = this._y; + var dx = x - oldX; + var dy = y - oldY; + this._x = x; + this._y = y; + this._opt.preventDefaultMouseMove && stop(e.event); + trigger(this, 'pan', 'moveOnMouseMove', e, { + dx: dx, + dy: dy, + oldX: oldX, + oldY: oldY, + newX: x, + newY: y, + isAvailableBehavior: null + }); + }; + RoamController.prototype._mouseupHandler = function (e) { + if (!isMiddleOrRightButtonOnMouseUpDown(e)) { + this._dragging = false; + } + }; + RoamController.prototype._mousewheelHandler = function (e) { + var shouldZoom = isAvailableBehavior('zoomOnMouseWheel', e, this._opt); + var shouldMove = isAvailableBehavior('moveOnMouseWheel', e, this._opt); + var wheelDelta = e.wheelDelta; + var absWheelDeltaDelta = Math.abs(wheelDelta); + var originX = e.offsetX; + var originY = e.offsetY; + // wheelDelta maybe -0 in chrome mac. + if (wheelDelta === 0 || !shouldZoom && !shouldMove) { + return; + } + // If both `shouldZoom` and `shouldMove` is true, trigger + // their event both, and the final behavior is determined + // by event listener themselves. + if (shouldZoom) { + // Convenience: + // Mac and VM Windows on Mac: scroll up: zoom out. + // Windows: scroll up: zoom in. + // FIXME: Should do more test in different environment. + // wheelDelta is too complicated in difference nvironment + // (https://developer.mozilla.org/en-US/docs/Web/Events/mousewheel), + // although it has been normallized by zrender. + // wheelDelta of mouse wheel is bigger than touch pad. + var factor = absWheelDeltaDelta > 3 ? 1.4 : absWheelDeltaDelta > 1 ? 1.2 : 1.1; + var scale = wheelDelta > 0 ? factor : 1 / factor; + checkPointerAndTrigger(this, 'zoom', 'zoomOnMouseWheel', e, { + scale: scale, + originX: originX, + originY: originY, + isAvailableBehavior: null + }); + } + if (shouldMove) { + // FIXME: Should do more test in different environment. + var absDelta = Math.abs(wheelDelta); + // wheelDelta of mouse wheel is bigger than touch pad. + var scrollDelta = (wheelDelta > 0 ? 1 : -1) * (absDelta > 3 ? 0.4 : absDelta > 1 ? 0.15 : 0.05); + checkPointerAndTrigger(this, 'scrollMove', 'moveOnMouseWheel', e, { + scrollDelta: scrollDelta, + originX: originX, + originY: originY, + isAvailableBehavior: null + }); + } + }; + RoamController.prototype._pinchHandler = function (e) { + if (isTaken(this._zr, 'globalPan')) { + return; + } + var scale = e.pinchScale > 1 ? 1.1 : 1 / 1.1; + checkPointerAndTrigger(this, 'zoom', null, e, { + scale: scale, + originX: e.pinchX, + originY: e.pinchY, + isAvailableBehavior: null + }); + }; + return RoamController; + }(Eventful); + function checkPointerAndTrigger(controller, eventName, behaviorToCheck, e, contollerEvent) { + if (controller.pointerChecker && controller.pointerChecker(e, contollerEvent.originX, contollerEvent.originY)) { + // When mouse is out of roamController rect, + // default befavoius should not be be disabled, otherwise + // page sliding is disabled, contrary to expectation. + stop(e.event); + trigger(controller, eventName, behaviorToCheck, e, contollerEvent); + } + } + function trigger(controller, eventName, behaviorToCheck, e, contollerEvent) { + // Also provide behavior checker for event listener, for some case that + // multiple components share one listener. + contollerEvent.isAvailableBehavior = bind(isAvailableBehavior, null, behaviorToCheck, e); + // TODO should not have type issue. + controller.trigger(eventName, contollerEvent); + } + // settings: { + // zoomOnMouseWheel + // moveOnMouseMove + // moveOnMouseWheel + // } + // The value can be: true / false / 'shift' / 'ctrl' / 'alt'. + function isAvailableBehavior(behaviorToCheck, e, settings) { + var setting = settings[behaviorToCheck]; + return !behaviorToCheck || setting && (!isString(setting) || e.event[setting + 'Key']); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /** + * For geo and graph. + */ + function updateViewOnPan(controllerHost, dx, dy) { + var target = controllerHost.target; + target.x += dx; + target.y += dy; + target.dirty(); + } + /** + * For geo and graph. + */ + function updateViewOnZoom(controllerHost, zoomDelta, zoomX, zoomY) { + var target = controllerHost.target; + var zoomLimit = controllerHost.zoomLimit; + var newZoom = controllerHost.zoom = controllerHost.zoom || 1; + newZoom *= zoomDelta; + if (zoomLimit) { + var zoomMin = zoomLimit.min || 0; + var zoomMax = zoomLimit.max || Infinity; + newZoom = Math.max(Math.min(zoomMax, newZoom), zoomMin); + } + var zoomScale = newZoom / controllerHost.zoom; + controllerHost.zoom = newZoom; + // Keep the mouse center when scaling + target.x -= (zoomX - target.x) * (zoomScale - 1); + target.y -= (zoomY - target.y) * (zoomScale - 1); + target.scaleX *= zoomScale; + target.scaleY *= zoomScale; + target.dirty(); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var IRRELEVANT_EXCLUDES = { + 'axisPointer': 1, + 'tooltip': 1, + 'brush': 1 + }; + /** + * Avoid that: mouse click on a elements that is over geo or graph, + * but roam is triggered. + */ + function onIrrelevantElement(e, api, targetCoordSysModel) { + var model = api.getComponentByElement(e.topTarget); + // If model is axisModel, it works only if it is injected with coordinateSystem. + var coordSys = model && model.coordinateSystem; + return model && model !== targetCoordSysModel && !IRRELEVANT_EXCLUDES.hasOwnProperty(model.mainType) && coordSys && coordSys.model !== targetCoordSysModel; + } + + function parseXML(svg) { + if (isString(svg)) { + var parser = new DOMParser(); + svg = parser.parseFromString(svg, 'text/xml'); + } + var svgNode = svg; + if (svgNode.nodeType === 9) { + svgNode = svgNode.firstChild; + } + while (svgNode.nodeName.toLowerCase() !== 'svg' || svgNode.nodeType !== 1) { + svgNode = svgNode.nextSibling; + } + return svgNode; + } + + var nodeParsers; + var INHERITABLE_STYLE_ATTRIBUTES_MAP = { + 'fill': 'fill', + 'stroke': 'stroke', + 'stroke-width': 'lineWidth', + 'opacity': 'opacity', + 'fill-opacity': 'fillOpacity', + 'stroke-opacity': 'strokeOpacity', + 'stroke-dasharray': 'lineDash', + 'stroke-dashoffset': 'lineDashOffset', + 'stroke-linecap': 'lineCap', + 'stroke-linejoin': 'lineJoin', + 'stroke-miterlimit': 'miterLimit', + 'font-family': 'fontFamily', + 'font-size': 'fontSize', + 'font-style': 'fontStyle', + 'font-weight': 'fontWeight', + 'text-anchor': 'textAlign', + 'visibility': 'visibility', + 'display': 'display' + }; + var INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS = keys(INHERITABLE_STYLE_ATTRIBUTES_MAP); + var SELF_STYLE_ATTRIBUTES_MAP = { + 'alignment-baseline': 'textBaseline', + 'stop-color': 'stopColor' + }; + var SELF_STYLE_ATTRIBUTES_MAP_KEYS = keys(SELF_STYLE_ATTRIBUTES_MAP); + var SVGParser = (function () { + function SVGParser() { + this._defs = {}; + this._root = null; + } + SVGParser.prototype.parse = function (xml, opt) { + opt = opt || {}; + var svg = parseXML(xml); + if ("development" !== 'production') { + if (!svg) { + throw new Error('Illegal svg'); + } + } + this._defsUsePending = []; + var root = new Group(); + this._root = root; + var named = []; + var viewBox = svg.getAttribute('viewBox') || ''; + var width = parseFloat((svg.getAttribute('width') || opt.width)); + var height = parseFloat((svg.getAttribute('height') || opt.height)); + isNaN(width) && (width = null); + isNaN(height) && (height = null); + parseAttributes(svg, root, null, true, false); + var child = svg.firstChild; + while (child) { + this._parseNode(child, root, named, null, false, false); + child = child.nextSibling; + } + applyDefs(this._defs, this._defsUsePending); + this._defsUsePending = []; + var viewBoxRect; + var viewBoxTransform; + if (viewBox) { + var viewBoxArr = splitNumberSequence(viewBox); + if (viewBoxArr.length >= 4) { + viewBoxRect = { + x: parseFloat((viewBoxArr[0] || 0)), + y: parseFloat((viewBoxArr[1] || 0)), + width: parseFloat(viewBoxArr[2]), + height: parseFloat(viewBoxArr[3]) + }; + } + } + if (viewBoxRect && width != null && height != null) { + viewBoxTransform = makeViewBoxTransform(viewBoxRect, { x: 0, y: 0, width: width, height: height }); + if (!opt.ignoreViewBox) { + var elRoot = root; + root = new Group(); + root.add(elRoot); + elRoot.scaleX = elRoot.scaleY = viewBoxTransform.scale; + elRoot.x = viewBoxTransform.x; + elRoot.y = viewBoxTransform.y; + } + } + if (!opt.ignoreRootClip && width != null && height != null) { + root.setClipPath(new Rect({ + shape: { x: 0, y: 0, width: width, height: height } + })); + } + return { + root: root, + width: width, + height: height, + viewBoxRect: viewBoxRect, + viewBoxTransform: viewBoxTransform, + named: named + }; + }; + SVGParser.prototype._parseNode = function (xmlNode, parentGroup, named, namedFrom, isInDefs, isInText) { + var nodeName = xmlNode.nodeName.toLowerCase(); + var el; + var namedFromForSub = namedFrom; + if (nodeName === 'defs') { + isInDefs = true; + } + if (nodeName === 'text') { + isInText = true; + } + if (nodeName === 'defs' || nodeName === 'switch') { + el = parentGroup; + } + else { + if (!isInDefs) { + var parser_1 = nodeParsers[nodeName]; + if (parser_1 && hasOwn(nodeParsers, nodeName)) { + el = parser_1.call(this, xmlNode, parentGroup); + var nameAttr = xmlNode.getAttribute('name'); + if (nameAttr) { + var newNamed = { + name: nameAttr, + namedFrom: null, + svgNodeTagLower: nodeName, + el: el + }; + named.push(newNamed); + if (nodeName === 'g') { + namedFromForSub = newNamed; + } + } + else if (namedFrom) { + named.push({ + name: namedFrom.name, + namedFrom: namedFrom, + svgNodeTagLower: nodeName, + el: el + }); + } + parentGroup.add(el); + } + } + var parser = paintServerParsers[nodeName]; + if (parser && hasOwn(paintServerParsers, nodeName)) { + var def = parser.call(this, xmlNode); + var id = xmlNode.getAttribute('id'); + if (id) { + this._defs[id] = def; + } + } + } + if (el && el.isGroup) { + var child = xmlNode.firstChild; + while (child) { + if (child.nodeType === 1) { + this._parseNode(child, el, named, namedFromForSub, isInDefs, isInText); + } + else if (child.nodeType === 3 && isInText) { + this._parseText(child, el); + } + child = child.nextSibling; + } + } + }; + SVGParser.prototype._parseText = function (xmlNode, parentGroup) { + var text = new TSpan({ + style: { + text: xmlNode.textContent + }, + silent: true, + x: this._textX || 0, + y: this._textY || 0 + }); + inheritStyle(parentGroup, text); + parseAttributes(xmlNode, text, this._defsUsePending, false, false); + applyTextAlignment(text, parentGroup); + var textStyle = text.style; + var fontSize = textStyle.fontSize; + if (fontSize && fontSize < 9) { + textStyle.fontSize = 9; + text.scaleX *= fontSize / 9; + text.scaleY *= fontSize / 9; + } + var font = (textStyle.fontSize || textStyle.fontFamily) && [ + textStyle.fontStyle, + textStyle.fontWeight, + (textStyle.fontSize || 12) + 'px', + textStyle.fontFamily || 'sans-serif' + ].join(' '); + textStyle.font = font; + var rect = text.getBoundingRect(); + this._textX += rect.width; + parentGroup.add(text); + return text; + }; + SVGParser.internalField = (function () { + nodeParsers = { + 'g': function (xmlNode, parentGroup) { + var g = new Group(); + inheritStyle(parentGroup, g); + parseAttributes(xmlNode, g, this._defsUsePending, false, false); + return g; + }, + 'rect': function (xmlNode, parentGroup) { + var rect = new Rect(); + inheritStyle(parentGroup, rect); + parseAttributes(xmlNode, rect, this._defsUsePending, false, false); + rect.setShape({ + x: parseFloat(xmlNode.getAttribute('x') || '0'), + y: parseFloat(xmlNode.getAttribute('y') || '0'), + width: parseFloat(xmlNode.getAttribute('width') || '0'), + height: parseFloat(xmlNode.getAttribute('height') || '0') + }); + rect.silent = true; + return rect; + }, + 'circle': function (xmlNode, parentGroup) { + var circle = new Circle(); + inheritStyle(parentGroup, circle); + parseAttributes(xmlNode, circle, this._defsUsePending, false, false); + circle.setShape({ + cx: parseFloat(xmlNode.getAttribute('cx') || '0'), + cy: parseFloat(xmlNode.getAttribute('cy') || '0'), + r: parseFloat(xmlNode.getAttribute('r') || '0') + }); + circle.silent = true; + return circle; + }, + 'line': function (xmlNode, parentGroup) { + var line = new Line(); + inheritStyle(parentGroup, line); + parseAttributes(xmlNode, line, this._defsUsePending, false, false); + line.setShape({ + x1: parseFloat(xmlNode.getAttribute('x1') || '0'), + y1: parseFloat(xmlNode.getAttribute('y1') || '0'), + x2: parseFloat(xmlNode.getAttribute('x2') || '0'), + y2: parseFloat(xmlNode.getAttribute('y2') || '0') + }); + line.silent = true; + return line; + }, + 'ellipse': function (xmlNode, parentGroup) { + var ellipse = new Ellipse(); + inheritStyle(parentGroup, ellipse); + parseAttributes(xmlNode, ellipse, this._defsUsePending, false, false); + ellipse.setShape({ + cx: parseFloat(xmlNode.getAttribute('cx') || '0'), + cy: parseFloat(xmlNode.getAttribute('cy') || '0'), + rx: parseFloat(xmlNode.getAttribute('rx') || '0'), + ry: parseFloat(xmlNode.getAttribute('ry') || '0') + }); + ellipse.silent = true; + return ellipse; + }, + 'polygon': function (xmlNode, parentGroup) { + var pointsStr = xmlNode.getAttribute('points'); + var pointsArr; + if (pointsStr) { + pointsArr = parsePoints(pointsStr); + } + var polygon = new Polygon({ + shape: { + points: pointsArr || [] + }, + silent: true + }); + inheritStyle(parentGroup, polygon); + parseAttributes(xmlNode, polygon, this._defsUsePending, false, false); + return polygon; + }, + 'polyline': function (xmlNode, parentGroup) { + var pointsStr = xmlNode.getAttribute('points'); + var pointsArr; + if (pointsStr) { + pointsArr = parsePoints(pointsStr); + } + var polyline = new Polyline({ + shape: { + points: pointsArr || [] + }, + silent: true + }); + inheritStyle(parentGroup, polyline); + parseAttributes(xmlNode, polyline, this._defsUsePending, false, false); + return polyline; + }, + 'image': function (xmlNode, parentGroup) { + var img = new ZRImage(); + inheritStyle(parentGroup, img); + parseAttributes(xmlNode, img, this._defsUsePending, false, false); + img.setStyle({ + image: xmlNode.getAttribute('xlink:href') || xmlNode.getAttribute('href'), + x: +xmlNode.getAttribute('x'), + y: +xmlNode.getAttribute('y'), + width: +xmlNode.getAttribute('width'), + height: +xmlNode.getAttribute('height') + }); + img.silent = true; + return img; + }, + 'text': function (xmlNode, parentGroup) { + var x = xmlNode.getAttribute('x') || '0'; + var y = xmlNode.getAttribute('y') || '0'; + var dx = xmlNode.getAttribute('dx') || '0'; + var dy = xmlNode.getAttribute('dy') || '0'; + this._textX = parseFloat(x) + parseFloat(dx); + this._textY = parseFloat(y) + parseFloat(dy); + var g = new Group(); + inheritStyle(parentGroup, g); + parseAttributes(xmlNode, g, this._defsUsePending, false, true); + return g; + }, + 'tspan': function (xmlNode, parentGroup) { + var x = xmlNode.getAttribute('x'); + var y = xmlNode.getAttribute('y'); + if (x != null) { + this._textX = parseFloat(x); + } + if (y != null) { + this._textY = parseFloat(y); + } + var dx = xmlNode.getAttribute('dx') || '0'; + var dy = xmlNode.getAttribute('dy') || '0'; + var g = new Group(); + inheritStyle(parentGroup, g); + parseAttributes(xmlNode, g, this._defsUsePending, false, true); + this._textX += parseFloat(dx); + this._textY += parseFloat(dy); + return g; + }, + 'path': function (xmlNode, parentGroup) { + var d = xmlNode.getAttribute('d') || ''; + var path = createFromString(d); + inheritStyle(parentGroup, path); + parseAttributes(xmlNode, path, this._defsUsePending, false, false); + path.silent = true; + return path; + } + }; + })(); + return SVGParser; + }()); + var paintServerParsers = { + 'lineargradient': function (xmlNode) { + var x1 = parseInt(xmlNode.getAttribute('x1') || '0', 10); + var y1 = parseInt(xmlNode.getAttribute('y1') || '0', 10); + var x2 = parseInt(xmlNode.getAttribute('x2') || '10', 10); + var y2 = parseInt(xmlNode.getAttribute('y2') || '0', 10); + var gradient = new LinearGradient(x1, y1, x2, y2); + parsePaintServerUnit(xmlNode, gradient); + parseGradientColorStops(xmlNode, gradient); + return gradient; + }, + 'radialgradient': function (xmlNode) { + var cx = parseInt(xmlNode.getAttribute('cx') || '0', 10); + var cy = parseInt(xmlNode.getAttribute('cy') || '0', 10); + var r = parseInt(xmlNode.getAttribute('r') || '0', 10); + var gradient = new RadialGradient(cx, cy, r); + parsePaintServerUnit(xmlNode, gradient); + parseGradientColorStops(xmlNode, gradient); + return gradient; + } + }; + function parsePaintServerUnit(xmlNode, gradient) { + var gradientUnits = xmlNode.getAttribute('gradientUnits'); + if (gradientUnits === 'userSpaceOnUse') { + gradient.global = true; + } + } + function parseGradientColorStops(xmlNode, gradient) { + var stop = xmlNode.firstChild; + while (stop) { + if (stop.nodeType === 1 + && stop.nodeName.toLocaleLowerCase() === 'stop') { + var offsetStr = stop.getAttribute('offset'); + var offset = void 0; + if (offsetStr && offsetStr.indexOf('%') > 0) { + offset = parseInt(offsetStr, 10) / 100; + } + else if (offsetStr) { + offset = parseFloat(offsetStr); + } + else { + offset = 0; + } + var styleVals = {}; + parseInlineStyle(stop, styleVals, styleVals); + var stopColor = styleVals.stopColor + || stop.getAttribute('stop-color') + || '#000000'; + gradient.colorStops.push({ + offset: offset, + color: stopColor + }); + } + stop = stop.nextSibling; + } + } + function inheritStyle(parent, child) { + if (parent && parent.__inheritedStyle) { + if (!child.__inheritedStyle) { + child.__inheritedStyle = {}; + } + defaults(child.__inheritedStyle, parent.__inheritedStyle); + } + } + function parsePoints(pointsString) { + var list = splitNumberSequence(pointsString); + var points = []; + for (var i = 0; i < list.length; i += 2) { + var x = parseFloat(list[i]); + var y = parseFloat(list[i + 1]); + points.push([x, y]); + } + return points; + } + function parseAttributes(xmlNode, el, defsUsePending, onlyInlineStyle, isTextGroup) { + var disp = el; + var inheritedStyle = disp.__inheritedStyle = disp.__inheritedStyle || {}; + var selfStyle = {}; + if (xmlNode.nodeType === 1) { + parseTransformAttribute(xmlNode, el); + parseInlineStyle(xmlNode, inheritedStyle, selfStyle); + if (!onlyInlineStyle) { + parseAttributeStyle(xmlNode, inheritedStyle, selfStyle); + } + } + disp.style = disp.style || {}; + if (inheritedStyle.fill != null) { + disp.style.fill = getFillStrokeStyle(disp, 'fill', inheritedStyle.fill, defsUsePending); + } + if (inheritedStyle.stroke != null) { + disp.style.stroke = getFillStrokeStyle(disp, 'stroke', inheritedStyle.stroke, defsUsePending); + } + each([ + 'lineWidth', 'opacity', 'fillOpacity', 'strokeOpacity', 'miterLimit', 'fontSize' + ], function (propName) { + if (inheritedStyle[propName] != null) { + disp.style[propName] = parseFloat(inheritedStyle[propName]); + } + }); + each([ + 'lineDashOffset', 'lineCap', 'lineJoin', 'fontWeight', 'fontFamily', 'fontStyle', 'textAlign' + ], function (propName) { + if (inheritedStyle[propName] != null) { + disp.style[propName] = inheritedStyle[propName]; + } + }); + if (isTextGroup) { + disp.__selfStyle = selfStyle; + } + if (inheritedStyle.lineDash) { + disp.style.lineDash = map(splitNumberSequence(inheritedStyle.lineDash), function (str) { + return parseFloat(str); + }); + } + if (inheritedStyle.visibility === 'hidden' || inheritedStyle.visibility === 'collapse') { + disp.invisible = true; + } + if (inheritedStyle.display === 'none') { + disp.ignore = true; + } + } + function applyTextAlignment(text, parentGroup) { + var parentSelfStyle = parentGroup.__selfStyle; + if (parentSelfStyle) { + var textBaseline = parentSelfStyle.textBaseline; + var zrTextBaseline = textBaseline; + if (!textBaseline || textBaseline === 'auto') { + zrTextBaseline = 'alphabetic'; + } + else if (textBaseline === 'baseline') { + zrTextBaseline = 'alphabetic'; + } + else if (textBaseline === 'before-edge' || textBaseline === 'text-before-edge') { + zrTextBaseline = 'top'; + } + else if (textBaseline === 'after-edge' || textBaseline === 'text-after-edge') { + zrTextBaseline = 'bottom'; + } + else if (textBaseline === 'central' || textBaseline === 'mathematical') { + zrTextBaseline = 'middle'; + } + text.style.textBaseline = zrTextBaseline; + } + var parentInheritedStyle = parentGroup.__inheritedStyle; + if (parentInheritedStyle) { + var textAlign = parentInheritedStyle.textAlign; + var zrTextAlign = textAlign; + if (textAlign) { + if (textAlign === 'middle') { + zrTextAlign = 'center'; + } + text.style.textAlign = zrTextAlign; + } + } + } + var urlRegex = /^url\(\s*#(.*?)\)/; + function getFillStrokeStyle(el, method, str, defsUsePending) { + var urlMatch = str && str.match(urlRegex); + if (urlMatch) { + var url = trim(urlMatch[1]); + defsUsePending.push([el, method, url]); + return; + } + if (str === 'none') { + str = null; + } + return str; + } + function applyDefs(defs, defsUsePending) { + for (var i = 0; i < defsUsePending.length; i++) { + var item = defsUsePending[i]; + item[0].style[item[1]] = defs[item[2]]; + } + } + var numberReg$1 = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g; + function splitNumberSequence(rawStr) { + return rawStr.match(numberReg$1) || []; + } + var transformRegex = /(translate|scale|rotate|skewX|skewY|matrix)\(([\-\s0-9\.eE,]*)\)/g; + var DEGREE_TO_ANGLE = Math.PI / 180; + function parseTransformAttribute(xmlNode, node) { + var transform = xmlNode.getAttribute('transform'); + if (transform) { + transform = transform.replace(/,/g, ' '); + var transformOps_1 = []; + var mt = null; + transform.replace(transformRegex, function (str, type, value) { + transformOps_1.push(type, value); + return ''; + }); + for (var i = transformOps_1.length - 1; i > 0; i -= 2) { + var value = transformOps_1[i]; + var type = transformOps_1[i - 1]; + var valueArr = splitNumberSequence(value); + mt = mt || create$1(); + switch (type) { + case 'translate': + translate(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || '0')]); + break; + case 'scale': + scale$1(mt, mt, [parseFloat(valueArr[0]), parseFloat(valueArr[1] || valueArr[0])]); + break; + case 'rotate': + rotate(mt, mt, -parseFloat(valueArr[0]) * DEGREE_TO_ANGLE, [ + parseFloat(valueArr[1] || '0'), + parseFloat(valueArr[2] || '0') + ]); + break; + case 'skewX': + var sx = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE); + mul$1(mt, [1, 0, sx, 1, 0, 0], mt); + break; + case 'skewY': + var sy = Math.tan(parseFloat(valueArr[0]) * DEGREE_TO_ANGLE); + mul$1(mt, [1, sy, 0, 1, 0, 0], mt); + break; + case 'matrix': + mt[0] = parseFloat(valueArr[0]); + mt[1] = parseFloat(valueArr[1]); + mt[2] = parseFloat(valueArr[2]); + mt[3] = parseFloat(valueArr[3]); + mt[4] = parseFloat(valueArr[4]); + mt[5] = parseFloat(valueArr[5]); + break; + } + } + node.setLocalTransform(mt); + } + } + var styleRegex = /([^\s:;]+)\s*:\s*([^:;]+)/g; + function parseInlineStyle(xmlNode, inheritableStyleResult, selfStyleResult) { + var style = xmlNode.getAttribute('style'); + if (!style) { + return; + } + styleRegex.lastIndex = 0; + var styleRegResult; + while ((styleRegResult = styleRegex.exec(style)) != null) { + var svgStlAttr = styleRegResult[1]; + var zrInheritableStlAttr = hasOwn(INHERITABLE_STYLE_ATTRIBUTES_MAP, svgStlAttr) + ? INHERITABLE_STYLE_ATTRIBUTES_MAP[svgStlAttr] + : null; + if (zrInheritableStlAttr) { + inheritableStyleResult[zrInheritableStlAttr] = styleRegResult[2]; + } + var zrSelfStlAttr = hasOwn(SELF_STYLE_ATTRIBUTES_MAP, svgStlAttr) + ? SELF_STYLE_ATTRIBUTES_MAP[svgStlAttr] + : null; + if (zrSelfStlAttr) { + selfStyleResult[zrSelfStlAttr] = styleRegResult[2]; + } + } + } + function parseAttributeStyle(xmlNode, inheritableStyleResult, selfStyleResult) { + for (var i = 0; i < INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) { + var svgAttrName = INHERITABLE_STYLE_ATTRIBUTES_MAP_KEYS[i]; + var attrValue = xmlNode.getAttribute(svgAttrName); + if (attrValue != null) { + inheritableStyleResult[INHERITABLE_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue; + } + } + for (var i = 0; i < SELF_STYLE_ATTRIBUTES_MAP_KEYS.length; i++) { + var svgAttrName = SELF_STYLE_ATTRIBUTES_MAP_KEYS[i]; + var attrValue = xmlNode.getAttribute(svgAttrName); + if (attrValue != null) { + selfStyleResult[SELF_STYLE_ATTRIBUTES_MAP[svgAttrName]] = attrValue; + } + } + } + function makeViewBoxTransform(viewBoxRect, boundingRect) { + var scaleX = boundingRect.width / viewBoxRect.width; + var scaleY = boundingRect.height / viewBoxRect.height; + var scale = Math.min(scaleX, scaleY); + return { + scale: scale, + x: -(viewBoxRect.x + viewBoxRect.width / 2) * scale + (boundingRect.x + boundingRect.width / 2), + y: -(viewBoxRect.y + viewBoxRect.height / 2) * scale + (boundingRect.y + boundingRect.height / 2) + }; + } + function parseSVG(xml, opt) { + var parser = new SVGParser(); + return parser.parse(xml, opt); + } + + /** + * "region available" means that: enable users to set attribute `name="xxx"` on those tags + * to make it be a region. + * 1. region styles and its label styles can be defined in echarts opton: + * ```js + * geo: { + * regions: [{ + * name: 'xxx', + * itemStyle: { ... }, + * label: { ... } + * }, { + * ... + * }, + * ...] + * }; + * ``` + * 2. name can be duplicated in different SVG tag. All of the tags with the same name share + * a region option. For exampel if there are two representing two lung lobes. They have + * no common parents but both of them need to display label "lung" inside. + */ + var REGION_AVAILABLE_SVG_TAG_MAP = createHashMap(['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path', + // are also enabled because some SVG might paint text itself, + // but still need to trigger events or tooltip. + 'text', 'tspan', + // is also enabled because this case: if multiple tags share one name + // and need label displayed, every tags will display the name, which is not + // expected. So we can put them into a . Thereby only one label + // displayed and located based on the bounding rect of the . + 'g']); + var GeoSVGResource = /** @class */function () { + function GeoSVGResource(mapName, svg) { + this.type = 'geoSVG'; + // All used graphics. key: hostKey, value: root + this._usedGraphicMap = createHashMap(); + // All unused graphics. + this._freedGraphics = []; + this._mapName = mapName; + // Only perform parse to XML object here, which might be time + // consiming for large SVG. + // Although convert XML to zrender element is also time consiming, + // if we do it here, the clone of zrender elements has to be + // required. So we do it once for each geo instance, util real + // performance issues call for optimizing it. + this._parsedXML = parseXML(svg); + } + GeoSVGResource.prototype.load = function /* nameMap: NameMap */ + () { + // In the "load" stage, graphic need to be built to + // get boundingRect for geo coordinate system. + var firstGraphic = this._firstGraphic; + // Create the return data structure only when first graphic created. + // Because they will be used in geo coordinate system update stage, + // and `regions` will be mounted at `geo` coordinate system, + // in which there is no "view" info, so that it should better not to + // make references to graphic elements. + if (!firstGraphic) { + firstGraphic = this._firstGraphic = this._buildGraphic(this._parsedXML); + this._freedGraphics.push(firstGraphic); + this._boundingRect = this._firstGraphic.boundingRect.clone(); + // PENDING: `nameMap` will not be supported until some real requirement come. + // if (nameMap) { + // named = applyNameMap(named, nameMap); + // } + var _a = createRegions(firstGraphic.named), + regions = _a.regions, + regionsMap = _a.regionsMap; + this._regions = regions; + this._regionsMap = regionsMap; + } + return { + boundingRect: this._boundingRect, + regions: this._regions, + regionsMap: this._regionsMap + }; + }; + GeoSVGResource.prototype._buildGraphic = function (svgXML) { + var result; + var rootFromParse; + try { + result = svgXML && parseSVG(svgXML, { + ignoreViewBox: true, + ignoreRootClip: true + }) || {}; + rootFromParse = result.root; + assert(rootFromParse != null); + } catch (e) { + throw new Error('Invalid svg format\n' + e.message); + } + // Note: we keep the covenant that the root has no transform. So always add an extra root. + var root = new Group(); + root.add(rootFromParse); + root.isGeoSVGGraphicRoot = true; + // [THE_RULE_OF_VIEWPORT_AND_VIEWBOX] + // + // Consider: `` + // - the `width/height` we call it `svgWidth/svgHeight` for short. + // - `(0, 0, svgWidth, svgHeight)` defines the viewport of the SVG, or say, + // "viewport boundingRect", or `boundingRect` for short. + // - `viewBox` defines the transform from the real content ot the viewport. + // `viewBox` has the same unit as the content of SVG. + // If `viewBox` exists, a transform is defined, so the unit of `svgWidth/svgHeight` become + // different from the content of SVG. Otherwise, they are the same. + // + // If both `svgWidth/svgHeight/viewBox` are specified in a SVG file, the transform rule will be: + // 0. `boundingRect` is `(0, 0, svgWidth, svgHeight)`. Set it to Geo['_rect'] (View['_rect']). + // 1. Make a transform from `viewBox` to `boundingRect`. + // Note: only support `preserveAspectRatio 'xMidYMid'` here. That is, this transform will preserve + // the aspect ratio. + // 2. Make a transform from boundingRect to Geo['_viewRect'] (View['_viewRect']) + // (`Geo`/`View` will do this job). + // Note: this transform might not preserve aspect radio, which depending on how users specify + // viewRect in echarts option (e.g., `geo.left/top/width/height` will not preserve aspect ratio, + // but `geo.layoutCenter/layoutSize` will preserve aspect ratio). + // + // If `svgWidth/svgHeight` not specified, we use `viewBox` as the `boundingRect` to make the SVG + // layout look good. + // + // If neither `svgWidth/svgHeight` nor `viewBox` are not specified, we calculate the boundingRect + // of the SVG content and use them to make SVG layout look good. + var svgWidth = result.width; + var svgHeight = result.height; + var viewBoxRect = result.viewBoxRect; + var boundingRect = this._boundingRect; + if (!boundingRect) { + var bRectX = void 0; + var bRectY = void 0; + var bRectWidth = void 0; + var bRectHeight = void 0; + if (svgWidth != null) { + bRectX = 0; + bRectWidth = svgWidth; + } else if (viewBoxRect) { + bRectX = viewBoxRect.x; + bRectWidth = viewBoxRect.width; + } + if (svgHeight != null) { + bRectY = 0; + bRectHeight = svgHeight; + } else if (viewBoxRect) { + bRectY = viewBoxRect.y; + bRectHeight = viewBoxRect.height; + } + // If both viewBox and svgWidth/svgHeight not specified, + // we have to determine how to layout those element to make them look good. + if (bRectX == null || bRectY == null) { + var calculatedBoundingRect = rootFromParse.getBoundingRect(); + if (bRectX == null) { + bRectX = calculatedBoundingRect.x; + bRectWidth = calculatedBoundingRect.width; + } + if (bRectY == null) { + bRectY = calculatedBoundingRect.y; + bRectHeight = calculatedBoundingRect.height; + } + } + boundingRect = this._boundingRect = new BoundingRect(bRectX, bRectY, bRectWidth, bRectHeight); + } + if (viewBoxRect) { + var viewBoxTransform = makeViewBoxTransform(viewBoxRect, boundingRect); + // Only support `preserveAspectRatio 'xMidYMid'` + rootFromParse.scaleX = rootFromParse.scaleY = viewBoxTransform.scale; + rootFromParse.x = viewBoxTransform.x; + rootFromParse.y = viewBoxTransform.y; + } + // SVG needs to clip based on `viewBox`. And some SVG files really rely on this feature. + // They do not strictly confine all of the content inside a display rect, but deliberately + // use a `viewBox` to define a displayable rect. + // PENDING: + // The drawback of the `setClipPath` here is: the region label (genereted by echarts) near the + // edge might also be clipped, because region labels are put as `textContent` of the SVG path. + root.setClipPath(new Rect({ + shape: boundingRect.plain() + })); + var named = []; + each(result.named, function (namedItem) { + if (REGION_AVAILABLE_SVG_TAG_MAP.get(namedItem.svgNodeTagLower) != null) { + named.push(namedItem); + setSilent(namedItem.el); + } + }); + return { + root: root, + boundingRect: boundingRect, + named: named + }; + }; + /** + * Consider: + * (1) One graphic element can not be shared by different `geoView` running simultaneously. + * Notice, also need to consider multiple echarts instances share a `mapRecord`. + * (2) Converting SVG to graphic elements is time consuming. + * (3) In the current architecture, `load` should be called frequently to get boundingRect, + * and it is called without view info. + * So we maintain graphic elements in this module, and enables `view` to use/return these + * graphics from/to the pool with it's uid. + */ + GeoSVGResource.prototype.useGraphic = function (hostKey /* , nameMap: NameMap */) { + var usedRootMap = this._usedGraphicMap; + var svgGraphic = usedRootMap.get(hostKey); + if (svgGraphic) { + return svgGraphic; + } + svgGraphic = this._freedGraphics.pop() + // use the first boundingRect to avoid duplicated boundingRect calculation. + || this._buildGraphic(this._parsedXML); + usedRootMap.set(hostKey, svgGraphic); + // PENDING: `nameMap` will not be supported until some real requirement come. + // `nameMap` can only be obtained from echarts option. + // The original `named` must not be modified. + // if (nameMap) { + // svgGraphic = extend({}, svgGraphic); + // svgGraphic.named = applyNameMap(svgGraphic.named, nameMap); + // } + return svgGraphic; + }; + GeoSVGResource.prototype.freeGraphic = function (hostKey) { + var usedRootMap = this._usedGraphicMap; + var svgGraphic = usedRootMap.get(hostKey); + if (svgGraphic) { + usedRootMap.removeKey(hostKey); + this._freedGraphics.push(svgGraphic); + } + }; + return GeoSVGResource; + }(); + function setSilent(el) { + // Only named element has silent: false, other elements should + // act as background and has no user interaction. + el.silent = false; + // text|tspan will be converted to group. + if (el.isGroup) { + el.traverse(function (child) { + child.silent = false; + }); + } + } + function createRegions(named) { + var regions = []; + var regionsMap = createHashMap(); + // Create resions only for the first graphic. + each(named, function (namedItem) { + // Region has feature to calculate center for tooltip or other features. + // If there is a , the center should be the center of the + // bounding rect of the g. + if (namedItem.namedFrom != null) { + return; + } + var region = new GeoSVGRegion(namedItem.name, namedItem.el); + // PENDING: if `nameMap` supported, this region can not be mounted on + // `this`, but can only be created each time `load()` called. + regions.push(region); + // PENDING: if multiple tag named with the same name, only one will be + // found by `_regionsMap`. `_regionsMap` is used to find a coordinate + // by name. We use `region.getCenter()` as the coordinate. + regionsMap.set(namedItem.name, region); + }); + return { + regions: regions, + regionsMap: regionsMap + }; + } + // PENDING: `nameMap` will not be supported until some real requirement come. + // /** + // * Use the alias in geoNameMap. + // * The input `named` must not be modified. + // */ + // function applyNameMap( + // named: GeoSVGGraphicRecord['named'], + // nameMap: NameMap + // ): GeoSVGGraphicRecord['named'] { + // const result = [] as GeoSVGGraphicRecord['named']; + // for (let i = 0; i < named.length; i++) { + // let regionGraphic = named[i]; + // const name = regionGraphic.name; + // if (nameMap && nameMap.hasOwnProperty(name)) { + // regionGraphic = extend({}, regionGraphic); + // regionGraphic.name = name; + // } + // result.push(regionGraphic); + // } + // return result; + // } + + var geoCoord = [126, 25]; + var nanhaiName = '南海诸岛'; + var points$1 = [[[0, 3.5], [7, 11.2], [15, 11.9], [30, 7], [42, 0.7], [52, 0.7], [56, 7.7], [59, 0.7], [64, 0.7], [64, 0], [5, 0], [0, 3.5]], [[13, 16.1], [19, 14.7], [16, 21.7], [11, 23.1], [13, 16.1]], [[12, 32.2], [14, 38.5], [15, 38.5], [13, 32.2], [12, 32.2]], [[16, 47.6], [12, 53.2], [13, 53.2], [18, 47.6], [16, 47.6]], [[6, 64.4], [8, 70], [9, 70], [8, 64.4], [6, 64.4]], [[23, 82.6], [29, 79.8], [30, 79.8], [25, 82.6], [23, 82.6]], [[37, 70.7], [43, 62.3], [44, 62.3], [39, 70.7], [37, 70.7]], [[48, 51.1], [51, 45.5], [53, 45.5], [50, 51.1], [48, 51.1]], [[51, 35], [51, 28.7], [53, 28.7], [53, 35], [51, 35]], [[52, 22.4], [55, 17.5], [56, 17.5], [53, 22.4], [52, 22.4]], [[58, 12.6], [62, 7], [63, 7], [60, 12.6], [58, 12.6]], [[0, 3.5], [0, 93.1], [64, 93.1], [64, 0], [63, 0], [63, 92.4], [1, 92.4], [1, 3.5], [0, 3.5]]]; + for (var i = 0; i < points$1.length; i++) { + for (var k = 0; k < points$1[i].length; k++) { + points$1[i][k][0] /= 10.5; + points$1[i][k][1] /= -10.5 / 0.75; + points$1[i][k][0] += geoCoord[0]; + points$1[i][k][1] += geoCoord[1]; + } + } + function fixNanhai(mapType, regions) { + if (mapType === 'china') { + for (var i = 0; i < regions.length; i++) { + // Already exists. + if (regions[i].name === nanhaiName) { + return; + } + } + regions.push(new GeoJSONRegion(nanhaiName, map(points$1, function (exterior) { + return { + type: 'polygon', + exterior: exterior + }; + }), geoCoord)); + } + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var coordsOffsetMap = { + '南海诸岛': [32, 80], + // 全国 + '广东': [0, -10], + '香港': [10, 5], + '澳门': [-10, 10], + // '北京': [-10, 0], + '天津': [5, 5] + }; + function fixTextCoords(mapType, region) { + if (mapType === 'china') { + var coordFix = coordsOffsetMap[region.name]; + if (coordFix) { + var cp = region.getCenter(); + cp[0] += coordFix[0] / 10.5; + cp[1] += -coordFix[1] / (10.5 / 0.75); + region.setCenter(cp); + } + } + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + // Fix for 钓鱼岛 + // let Region = require('../Region'); + // let zrUtil = require('zrender/lib/core/util'); + // let geoCoord = [126, 25]; + var points$2 = [[[123.45165252685547, 25.73527164402261], [123.49731445312499, 25.73527164402261], [123.49731445312499, 25.750734064600884], [123.45165252685547, 25.750734064600884], [123.45165252685547, 25.73527164402261]]]; + function fixDiaoyuIsland(mapType, region) { + if (mapType === 'china' && region.name === '台湾') { + region.geometries.push({ + type: 'polygon', + exterior: points$2[0] + }); + } + } + + var DEFAULT_NAME_PROPERTY = 'name'; + var GeoJSONResource = /** @class */function () { + function GeoJSONResource(mapName, geoJSON, specialAreas) { + this.type = 'geoJSON'; + this._parsedMap = createHashMap(); + this._mapName = mapName; + this._specialAreas = specialAreas; + // PENDING: delay the parse to the first usage to rapid up the FMP? + this._geoJSON = parseInput(geoJSON); + } + /** + * @param nameMap can be null/undefined + * @param nameProperty can be null/undefined + */ + GeoJSONResource.prototype.load = function (nameMap, nameProperty) { + nameProperty = nameProperty || DEFAULT_NAME_PROPERTY; + var parsed = this._parsedMap.get(nameProperty); + if (!parsed) { + var rawRegions = this._parseToRegions(nameProperty); + parsed = this._parsedMap.set(nameProperty, { + regions: rawRegions, + boundingRect: calculateBoundingRect(rawRegions) + }); + } + var regionsMap = createHashMap(); + var finalRegions = []; + each(parsed.regions, function (region) { + var regionName = region.name; + // Try use the alias in geoNameMap + if (nameMap && hasOwn(nameMap, regionName)) { + region = region.cloneShallow(regionName = nameMap[regionName]); + } + finalRegions.push(region); + regionsMap.set(regionName, region); + }); + return { + regions: finalRegions, + boundingRect: parsed.boundingRect || new BoundingRect(0, 0, 0, 0), + regionsMap: regionsMap + }; + }; + GeoJSONResource.prototype._parseToRegions = function (nameProperty) { + var mapName = this._mapName; + var geoJSON = this._geoJSON; + var rawRegions; + // https://jsperf.com/try-catch-performance-overhead + try { + rawRegions = geoJSON ? parseGeoJSON(geoJSON, nameProperty) : []; + } catch (e) { + throw new Error('Invalid geoJson format\n' + e.message); + } + fixNanhai(mapName, rawRegions); + each(rawRegions, function (region) { + var regionName = region.name; + fixTextCoords(mapName, region); + fixDiaoyuIsland(mapName, region); + // Some area like Alaska in USA map needs to be tansformed + // to look better + var specialArea = this._specialAreas && this._specialAreas[regionName]; + if (specialArea) { + region.transformTo(specialArea.left, specialArea.top, specialArea.width, specialArea.height); + } + }, this); + return rawRegions; + }; + /** + * Only for exporting to users. + * **MUST NOT** used internally. + */ + GeoJSONResource.prototype.getMapForUser = function () { + return { + // For backward compatibility, use geoJson + // PENDING: it has been returning them without clone. + // do we need to avoid outsite modification? + geoJson: this._geoJSON, + geoJSON: this._geoJSON, + specialAreas: this._specialAreas + }; + }; + return GeoJSONResource; + }(); + function calculateBoundingRect(regions) { + var rect; + for (var i = 0; i < regions.length; i++) { + var regionRect = regions[i].getBoundingRect(); + rect = rect || regionRect.clone(); + rect.union(regionRect); + } + return rect; + } + function parseInput(source) { + return !isString(source) ? source : typeof JSON !== 'undefined' && JSON.parse ? JSON.parse(source) : new Function('return (' + source + ');')(); + } + + var storage = createHashMap(); + var geoSourceManager = { + /** + * Compatible with previous `echarts.registerMap`. + * + * @usage + * ```js + * + * echarts.registerMap('USA', geoJson, specialAreas); + * + * echarts.registerMap('USA', { + * geoJson: geoJson, + * specialAreas: {...} + * }); + * echarts.registerMap('USA', { + * geoJSON: geoJson, + * specialAreas: {...} + * }); + * + * echarts.registerMap('airport', { + * svg: svg + * } + * ``` + * + * Note: + * Do not support that register multiple geoJSON or SVG + * one map name. Because different geoJSON and SVG have + * different unit. It's not easy to make sure how those + * units are mapping/normalize. + * If intending to use multiple geoJSON or SVG, we can + * use multiple geo coordinate system. + */ + registerMap: function (mapName, rawDef, rawSpecialAreas) { + if (rawDef.svg) { + var resource = new GeoSVGResource(mapName, rawDef.svg); + storage.set(mapName, resource); + } else { + // Recommend: + // echarts.registerMap('eu', { geoJSON: xxx, specialAreas: xxx }); + // Backward compatibility: + // echarts.registerMap('eu', geoJSON, specialAreas); + // echarts.registerMap('eu', { geoJson: xxx, specialAreas: xxx }); + var geoJSON = rawDef.geoJson || rawDef.geoJSON; + if (geoJSON && !rawDef.features) { + rawSpecialAreas = rawDef.specialAreas; + } else { + geoJSON = rawDef; + } + var resource = new GeoJSONResource(mapName, geoJSON, rawSpecialAreas); + storage.set(mapName, resource); + } + }, + getGeoResource: function (mapName) { + return storage.get(mapName); + }, + /** + * Only for exporting to users. + * **MUST NOT** used internally. + */ + getMapForUser: function (mapName) { + var resource = storage.get(mapName); + // Do not support return SVG until some real requirement come. + return resource && resource.type === 'geoJSON' && resource.getMapForUser(); + }, + load: function (mapName, nameMap, nameProperty) { + var resource = storage.get(mapName); + if (!resource) { + if ("development" !== 'production') { + console.error('Map ' + mapName + ' not exists. The GeoJSON of the map must be provided.'); + } + return; + } + return resource.load(nameMap, nameProperty); + } + }; + + /** + * Only these tags enable use `itemStyle` if they are named in SVG. + * Other tags like might not suitable for `itemStyle`. + * They will not be considered to be styled until some requirements come. + */ + var OPTION_STYLE_ENABLED_TAGS = ['rect', 'circle', 'line', 'ellipse', 'polygon', 'polyline', 'path']; + var OPTION_STYLE_ENABLED_TAG_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS); + var STATE_TRIGGER_TAG_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g'])); + var LABEL_HOST_MAP = createHashMap(OPTION_STYLE_ENABLED_TAGS.concat(['g'])); + var mapLabelRaw = makeInner(); + function getFixedItemStyle(model) { + var itemStyle = model.getItemStyle(); + var areaColor = model.get('areaColor'); + // If user want the color not to be changed when hover, + // they should both set areaColor and color to be null. + if (areaColor != null) { + itemStyle.fill = areaColor; + } + return itemStyle; + } + // Only stroke can be used for line. + // Using fill in style if stroke not exits. + // TODO Not sure yet. Perhaps a separate `lineStyle`? + function fixLineStyle(styleHost) { + var style = styleHost.style; + if (style) { + style.stroke = style.stroke || style.fill; + style.fill = null; + } + } + var MapDraw = /** @class */function () { + function MapDraw(api) { + var group = new Group(); + this.uid = getUID('ec_map_draw'); + this._controller = new RoamController(api.getZr()); + this._controllerHost = { + target: group + }; + this.group = group; + group.add(this._regionsGroup = new Group()); + group.add(this._svgGroup = new Group()); + } + MapDraw.prototype.draw = function (mapOrGeoModel, ecModel, api, fromView, payload) { + var isGeo = mapOrGeoModel.mainType === 'geo'; + // Map series has data. GEO model that controlled by map series + // will be assigned with map data. Other GEO model has no data. + var data = mapOrGeoModel.getData && mapOrGeoModel.getData(); + isGeo && ecModel.eachComponent({ + mainType: 'series', + subType: 'map' + }, function (mapSeries) { + if (!data && mapSeries.getHostGeoModel() === mapOrGeoModel) { + data = mapSeries.getData(); + } + }); + var geo = mapOrGeoModel.coordinateSystem; + var regionsGroup = this._regionsGroup; + var group = this.group; + var transformInfo = geo.getTransformInfo(); + var transformInfoRaw = transformInfo.raw; + var transformInfoRoam = transformInfo.roam; + // No animation when first draw or in action + var isFirstDraw = !regionsGroup.childAt(0) || payload; + if (isFirstDraw) { + group.x = transformInfoRoam.x; + group.y = transformInfoRoam.y; + group.scaleX = transformInfoRoam.scaleX; + group.scaleY = transformInfoRoam.scaleY; + group.dirty(); + } else { + updateProps(group, transformInfoRoam, mapOrGeoModel); + } + var isVisualEncodedByVisualMap = data && data.getVisual('visualMeta') && data.getVisual('visualMeta').length > 0; + var viewBuildCtx = { + api: api, + geo: geo, + mapOrGeoModel: mapOrGeoModel, + data: data, + isVisualEncodedByVisualMap: isVisualEncodedByVisualMap, + isGeo: isGeo, + transformInfoRaw: transformInfoRaw + }; + if (geo.resourceType === 'geoJSON') { + this._buildGeoJSON(viewBuildCtx); + } else if (geo.resourceType === 'geoSVG') { + this._buildSVG(viewBuildCtx); + } + this._updateController(mapOrGeoModel, ecModel, api); + this._updateMapSelectHandler(mapOrGeoModel, regionsGroup, api, fromView); + }; + MapDraw.prototype._buildGeoJSON = function (viewBuildCtx) { + var regionsGroupByName = this._regionsGroupByName = createHashMap(); + var regionsInfoByName = createHashMap(); + var regionsGroup = this._regionsGroup; + var transformInfoRaw = viewBuildCtx.transformInfoRaw; + var mapOrGeoModel = viewBuildCtx.mapOrGeoModel; + var data = viewBuildCtx.data; + var projection = viewBuildCtx.geo.projection; + var projectionStream = projection && projection.stream; + function transformPoint(point, project) { + if (project) { + // projection may return null point. + point = project(point); + } + return point && [point[0] * transformInfoRaw.scaleX + transformInfoRaw.x, point[1] * transformInfoRaw.scaleY + transformInfoRaw.y]; + } + function transformPolygonPoints(inPoints) { + var outPoints = []; + // If projectionStream is provided. Use it instead of single point project. + var project = !projectionStream && projection && projection.project; + for (var i = 0; i < inPoints.length; ++i) { + var newPt = transformPoint(inPoints[i], project); + newPt && outPoints.push(newPt); + } + return outPoints; + } + function getPolyShape(points) { + return { + shape: { + points: transformPolygonPoints(points) + } + }; + } + regionsGroup.removeAll(); + // Only when the resource is GeoJSON, there is `geo.regions`. + each(viewBuildCtx.geo.regions, function (region) { + var regionName = region.name; + // Consider in GeoJson properties.name may be duplicated, for example, + // there is multiple region named "United Kindom" or "France" (so many + // colonies). And it is not appropriate to merge them in geo, which + // will make them share the same label and bring trouble in label + // location calculation. + var regionGroup = regionsGroupByName.get(regionName); + var _a = regionsInfoByName.get(regionName) || {}, + dataIdx = _a.dataIdx, + regionModel = _a.regionModel; + if (!regionGroup) { + regionGroup = regionsGroupByName.set(regionName, new Group()); + regionsGroup.add(regionGroup); + dataIdx = data ? data.indexOfName(regionName) : null; + regionModel = viewBuildCtx.isGeo ? mapOrGeoModel.getRegionModel(regionName) : data ? data.getItemModel(dataIdx) : null; + regionsInfoByName.set(regionName, { + dataIdx: dataIdx, + regionModel: regionModel + }); + } + var polygonSubpaths = []; + var polylineSubpaths = []; + each(region.geometries, function (geometry) { + // Polygon and MultiPolygon + if (geometry.type === 'polygon') { + var polys = [geometry.exterior].concat(geometry.interiors || []); + if (projectionStream) { + polys = projectPolys(polys, projectionStream); + } + each(polys, function (poly) { + polygonSubpaths.push(new Polygon(getPolyShape(poly))); + }); + } + // LineString and MultiLineString + else { + var points = geometry.points; + if (projectionStream) { + points = projectPolys(points, projectionStream, true); + } + each(points, function (points) { + polylineSubpaths.push(new Polyline(getPolyShape(points))); + }); + } + }); + var centerPt = transformPoint(region.getCenter(), projection && projection.project); + function createCompoundPath(subpaths, isLine) { + if (!subpaths.length) { + return; + } + var compoundPath = new CompoundPath({ + culling: true, + segmentIgnoreThreshold: 1, + shape: { + paths: subpaths + } + }); + regionGroup.add(compoundPath); + applyOptionStyleForRegion(viewBuildCtx, compoundPath, dataIdx, regionModel); + resetLabelForRegion(viewBuildCtx, compoundPath, regionName, regionModel, mapOrGeoModel, dataIdx, centerPt); + if (isLine) { + fixLineStyle(compoundPath); + each(compoundPath.states, fixLineStyle); + } + } + createCompoundPath(polygonSubpaths); + createCompoundPath(polylineSubpaths, true); + }); + // Ensure children have been added to `regionGroup` before calling them. + regionsGroupByName.each(function (regionGroup, regionName) { + var _a = regionsInfoByName.get(regionName), + dataIdx = _a.dataIdx, + regionModel = _a.regionModel; + resetEventTriggerForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel, dataIdx); + resetTooltipForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel); + resetStateTriggerForRegion(viewBuildCtx, regionGroup, regionName, regionModel, mapOrGeoModel); + }, this); + }; + MapDraw.prototype._buildSVG = function (viewBuildCtx) { + var mapName = viewBuildCtx.geo.map; + var transformInfoRaw = viewBuildCtx.transformInfoRaw; + this._svgGroup.x = transformInfoRaw.x; + this._svgGroup.y = transformInfoRaw.y; + this._svgGroup.scaleX = transformInfoRaw.scaleX; + this._svgGroup.scaleY = transformInfoRaw.scaleY; + if (this._svgResourceChanged(mapName)) { + this._freeSVG(); + this._useSVG(mapName); + } + var svgDispatcherMap = this._svgDispatcherMap = createHashMap(); + var focusSelf = false; + each(this._svgGraphicRecord.named, function (namedItem) { + // Note that we also allow different elements have the same name. + // For example, a glyph of a city and the label of the city have + // the same name and their tooltip info can be defined in a single + // region option. + var regionName = namedItem.name; + var mapOrGeoModel = viewBuildCtx.mapOrGeoModel; + var data = viewBuildCtx.data; + var svgNodeTagLower = namedItem.svgNodeTagLower; + var el = namedItem.el; + var dataIdx = data ? data.indexOfName(regionName) : null; + var regionModel = mapOrGeoModel.getRegionModel(regionName); + if (OPTION_STYLE_ENABLED_TAG_MAP.get(svgNodeTagLower) != null && el instanceof Displayable) { + applyOptionStyleForRegion(viewBuildCtx, el, dataIdx, regionModel); + } + if (el instanceof Displayable) { + el.culling = true; + } + // We do not know how the SVG like so we'd better not to change z2. + // Otherwise it might bring some unexpected result. For example, + // an area hovered that make some inner city can not be clicked. + el.z2EmphasisLift = 0; + // If self named: + if (!namedItem.namedFrom) { + // label should batter to be displayed based on the center of + // if it is named rather than displayed on each child. + if (LABEL_HOST_MAP.get(svgNodeTagLower) != null) { + resetLabelForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx, null); + } + resetEventTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, dataIdx); + resetTooltipForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel); + if (STATE_TRIGGER_TAG_MAP.get(svgNodeTagLower) != null) { + var focus_1 = resetStateTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel); + if (focus_1 === 'self') { + focusSelf = true; + } + var els = svgDispatcherMap.get(regionName) || svgDispatcherMap.set(regionName, []); + els.push(el); + } + } + }, this); + this._enableBlurEntireSVG(focusSelf, viewBuildCtx); + }; + MapDraw.prototype._enableBlurEntireSVG = function (focusSelf, viewBuildCtx) { + // It's a little complicated to support blurring the entire geoSVG in series-map. + // So do not support it until some requirements come. + // At present, in series-map, only regions can be blurred. + if (focusSelf && viewBuildCtx.isGeo) { + var blurStyle = viewBuildCtx.mapOrGeoModel.getModel(['blur', 'itemStyle']).getItemStyle(); + // Only support `opacity` here. Because not sure that other props are suitable for + // all of the elements generated by SVG (especially for Text/TSpan/Image/... ). + var opacity_1 = blurStyle.opacity; + this._svgGraphicRecord.root.traverse(function (el) { + if (!el.isGroup) { + // PENDING: clear those settings to SVG elements when `_freeSVG`. + // (Currently it happen not to be needed.) + setDefaultStateProxy(el); + var style = el.ensureState('blur').style || {}; + // Do not overwrite the region style that already set from region option. + if (style.opacity == null && opacity_1 != null) { + style.opacity = opacity_1; + } + // If `ensureState('blur').style = {}`, there will be default opacity. + // Enable `stateTransition` (animation). + el.ensureState('emphasis'); + } + }); + } + }; + MapDraw.prototype.remove = function () { + this._regionsGroup.removeAll(); + this._regionsGroupByName = null; + this._svgGroup.removeAll(); + this._freeSVG(); + this._controller.dispose(); + this._controllerHost = null; + }; + MapDraw.prototype.findHighDownDispatchers = function (name, geoModel) { + if (name == null) { + return []; + } + var geo = geoModel.coordinateSystem; + if (geo.resourceType === 'geoJSON') { + var regionsGroupByName = this._regionsGroupByName; + if (regionsGroupByName) { + var regionGroup = regionsGroupByName.get(name); + return regionGroup ? [regionGroup] : []; + } + } else if (geo.resourceType === 'geoSVG') { + return this._svgDispatcherMap && this._svgDispatcherMap.get(name) || []; + } + }; + MapDraw.prototype._svgResourceChanged = function (mapName) { + return this._svgMapName !== mapName; + }; + MapDraw.prototype._useSVG = function (mapName) { + var resource = geoSourceManager.getGeoResource(mapName); + if (resource && resource.type === 'geoSVG') { + var svgGraphic = resource.useGraphic(this.uid); + this._svgGroup.add(svgGraphic.root); + this._svgGraphicRecord = svgGraphic; + this._svgMapName = mapName; + } + }; + MapDraw.prototype._freeSVG = function () { + var mapName = this._svgMapName; + if (mapName == null) { + return; + } + var resource = geoSourceManager.getGeoResource(mapName); + if (resource && resource.type === 'geoSVG') { + resource.freeGraphic(this.uid); + } + this._svgGraphicRecord = null; + this._svgDispatcherMap = null; + this._svgGroup.removeAll(); + this._svgMapName = null; + }; + MapDraw.prototype._updateController = function (mapOrGeoModel, ecModel, api) { + var geo = mapOrGeoModel.coordinateSystem; + var controller = this._controller; + var controllerHost = this._controllerHost; + // @ts-ignore FIXME:TS + controllerHost.zoomLimit = mapOrGeoModel.get('scaleLimit'); + controllerHost.zoom = geo.getZoom(); + // roamType is will be set default true if it is null + // @ts-ignore FIXME:TS + controller.enable(mapOrGeoModel.get('roam') || false); + var mainType = mapOrGeoModel.mainType; + function makeActionBase() { + var action = { + type: 'geoRoam', + componentType: mainType + }; + action[mainType + 'Id'] = mapOrGeoModel.id; + return action; + } + controller.off('pan').on('pan', function (e) { + this._mouseDownFlag = false; + updateViewOnPan(controllerHost, e.dx, e.dy); + api.dispatchAction(extend(makeActionBase(), { + dx: e.dx, + dy: e.dy, + animation: { + duration: 0 + } + })); + }, this); + controller.off('zoom').on('zoom', function (e) { + this._mouseDownFlag = false; + updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY); + api.dispatchAction(extend(makeActionBase(), { + zoom: e.scale, + originX: e.originX, + originY: e.originY, + animation: { + duration: 0 + } + })); + }, this); + controller.setPointerChecker(function (e, x, y) { + return geo.containPoint([x, y]) && !onIrrelevantElement(e, api, mapOrGeoModel); + }); + }; + /** + * FIXME: this is a temporarily workaround. + * When `geoRoam` the elements need to be reset in `MapView['render']`, because the props like + * `ignore` might have been modified by `LabelManager`, and `LabelManager#addLabelsOfSeries` + * will subsequently cache `defaultAttr` like `ignore`. If do not do this reset, the modified + * props will have no chance to be restored. + * Note: This reset should be after `clearStates` in `renderSeries` because `useStates` in + * `renderSeries` will cache the modified `ignore` to `el._normalState`. + * TODO: + * Use clone/immutable in `LabelManager`? + */ + MapDraw.prototype.resetForLabelLayout = function () { + this.group.traverse(function (el) { + var label = el.getTextContent(); + if (label) { + label.ignore = mapLabelRaw(label).ignore; + } + }); + }; + MapDraw.prototype._updateMapSelectHandler = function (mapOrGeoModel, regionsGroup, api, fromView) { + var mapDraw = this; + regionsGroup.off('mousedown'); + regionsGroup.off('click'); + // @ts-ignore FIXME:TS resolve type conflict + if (mapOrGeoModel.get('selectedMode')) { + regionsGroup.on('mousedown', function () { + mapDraw._mouseDownFlag = true; + }); + regionsGroup.on('click', function (e) { + if (!mapDraw._mouseDownFlag) { + return; + } + mapDraw._mouseDownFlag = false; + }); + } + }; + return MapDraw; + }(); + function applyOptionStyleForRegion(viewBuildCtx, el, dataIndex, regionModel) { + // All of the path are using `itemStyle`, because + // (1) Some SVG also use fill on polyline (The different between + // polyline and polygon is "open" or "close" but not fill or not). + // (2) For the common props like opacity, if some use itemStyle + // and some use `lineStyle`, it might confuse users. + // (3) Most SVG use , where can not detect whether to draw a "line" + // or a filled shape, so use `itemStyle` for . + var normalStyleModel = regionModel.getModel('itemStyle'); + var emphasisStyleModel = regionModel.getModel(['emphasis', 'itemStyle']); + var blurStyleModel = regionModel.getModel(['blur', 'itemStyle']); + var selectStyleModel = regionModel.getModel(['select', 'itemStyle']); + // NOTE: DON'T use 'style' in visual when drawing map. + // This component is used for drawing underlying map for both geo component and map series. + var normalStyle = getFixedItemStyle(normalStyleModel); + var emphasisStyle = getFixedItemStyle(emphasisStyleModel); + var selectStyle = getFixedItemStyle(selectStyleModel); + var blurStyle = getFixedItemStyle(blurStyleModel); + // Update the itemStyle if has data visual + var data = viewBuildCtx.data; + if (data) { + // Only visual color of each item will be used. It can be encoded by visualMap + // But visual color of series is used in symbol drawing + // Visual color for each series is for the symbol draw + var style = data.getItemVisual(dataIndex, 'style'); + var decal = data.getItemVisual(dataIndex, 'decal'); + if (viewBuildCtx.isVisualEncodedByVisualMap && style.fill) { + normalStyle.fill = style.fill; + } + if (decal) { + normalStyle.decal = createOrUpdatePatternFromDecal(decal, viewBuildCtx.api); + } + } + // SVG text, tspan and image can be named but not supporeted + // to be styled by region option yet. + el.setStyle(normalStyle); + el.style.strokeNoScale = true; + el.ensureState('emphasis').style = emphasisStyle; + el.ensureState('select').style = selectStyle; + el.ensureState('blur').style = blurStyle; + // Enable blur + setDefaultStateProxy(el); + } + function resetLabelForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel, + // Exist only if `viewBuildCtx.data` exists. + dataIdx, + // If labelXY not provided, use `textConfig.position: 'inside'` + labelXY) { + var data = viewBuildCtx.data; + var isGeo = viewBuildCtx.isGeo; + var isDataNaN = data && isNaN(data.get(data.mapDimension('value'), dataIdx)); + var itemLayout = data && data.getItemLayout(dataIdx); + // In the following cases label will be drawn + // 1. In map series and data value is NaN + // 2. In geo component + // 3. Region has no series legendIcon, which will be add a showLabel flag in mapSymbolLayout + if (isGeo || isDataNaN || itemLayout && itemLayout.showLabel) { + var query = !isGeo ? dataIdx : regionName; + var labelFetcher = void 0; + // Consider dataIdx not found. + if (!data || dataIdx >= 0) { + labelFetcher = mapOrGeoModel; + } + var specifiedTextOpt = labelXY ? { + normal: { + align: 'center', + verticalAlign: 'middle' + } + } : null; + // Caveat: must be called after `setDefaultStateProxy(el);` called. + // because textContent will be assign with `el.stateProxy` inside. + setLabelStyle(el, getLabelStatesModels(regionModel), { + labelFetcher: labelFetcher, + labelDataIndex: query, + defaultText: regionName + }, specifiedTextOpt); + var textEl = el.getTextContent(); + if (textEl) { + mapLabelRaw(textEl).ignore = textEl.ignore; + if (el.textConfig && labelXY) { + // Compute a relative offset based on the el bounding rect. + var rect = el.getBoundingRect().clone(); + // Need to make sure the percent position base on the same rect in normal and + // emphasis state. Otherwise if using boundingRect of el, but the emphasis state + // has borderWidth (even 0.5px), the text position will be changed obviously + // if the position is very big like ['1234%', '1345%']. + el.textConfig.layoutRect = rect; + el.textConfig.position = [(labelXY[0] - rect.x) / rect.width * 100 + '%', (labelXY[1] - rect.y) / rect.height * 100 + '%']; + } + } + // PENDING: + // If labelLayout is enabled (test/label-layout.html), el.dataIndex should be specified. + // But el.dataIndex is also used to determine whether user event should be triggered, + // where el.seriesIndex or el.dataModel must be specified. At present for a single el + // there is not case that "only label layout enabled but user event disabled", so here + // we depends `resetEventTriggerForRegion` to do the job of setting `el.dataIndex`. + el.disableLabelAnimation = true; + } else { + el.removeTextContent(); + el.removeTextConfig(); + el.disableLabelAnimation = null; + } + } + function resetEventTriggerForRegion(viewBuildCtx, eventTrigger, regionName, regionModel, mapOrGeoModel, + // Exist only if `viewBuildCtx.data` exists. + dataIdx) { + // setItemGraphicEl, setHoverStyle after all polygons and labels + // are added to the regionGroup + if (viewBuildCtx.data) { + // FIXME: when series-map use a SVG map, and there are duplicated name specified + // on different SVG elements, after `data.setItemGraphicEl(...)`: + // (1) all of them will be mounted with `dataIndex`, `seriesIndex`, so that tooltip + // can be triggered only mouse hover. That's correct. + // (2) only the last element will be kept in `data`, so that if trigger tooltip + // by `dispatchAction`, only the last one can be found and triggered. That might be + // not correct. We will fix it in future if anyone demanding that. + viewBuildCtx.data.setItemGraphicEl(dataIdx, eventTrigger); + } + // series-map will not trigger "geoselectchange" no matter it is + // based on a declared geo component. Because series-map will + // trigger "selectchange". If it trigger both the two events, + // If users call `chart.dispatchAction({type: 'toggleSelect'})`, + // it not easy to also fire event "geoselectchanged". + else { + // Package custom mouse event for geo component + getECData(eventTrigger).eventData = { + componentType: 'geo', + componentIndex: mapOrGeoModel.componentIndex, + geoIndex: mapOrGeoModel.componentIndex, + name: regionName, + region: regionModel && regionModel.option || {} + }; + } + } + function resetTooltipForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel) { + if (!viewBuildCtx.data) { + setTooltipConfig({ + el: el, + componentModel: mapOrGeoModel, + itemName: regionName, + // @ts-ignore FIXME:TS fix the "compatible with each other"? + itemTooltipOption: regionModel.get('tooltip') + }); + } + } + function resetStateTriggerForRegion(viewBuildCtx, el, regionName, regionModel, mapOrGeoModel) { + // @ts-ignore FIXME:TS fix the "compatible with each other"? + el.highDownSilentOnTouch = !!mapOrGeoModel.get('selectedMode'); + // @ts-ignore FIXME:TS fix the "compatible with each other"? + var emphasisModel = regionModel.getModel('emphasis'); + var focus = emphasisModel.get('focus'); + toggleHoverEmphasis(el, focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + if (viewBuildCtx.isGeo) { + enableComponentHighDownFeatures(el, mapOrGeoModel, regionName); + } + return focus; + } + function projectPolys(rings, + // Polygons include exterior and interiors. Or polylines. + createStream, isLine) { + var polygons = []; + var curPoly; + function startPolygon() { + curPoly = []; + } + function endPolygon() { + if (curPoly.length) { + polygons.push(curPoly); + curPoly = []; + } + } + var stream = createStream({ + polygonStart: startPolygon, + polygonEnd: endPolygon, + lineStart: startPolygon, + lineEnd: endPolygon, + point: function (x, y) { + // May have NaN values from stream. + if (isFinite(x) && isFinite(y)) { + curPoly.push([x, y]); + } + }, + sphere: function () {} + }); + !isLine && stream.polygonStart(); + each(rings, function (ring) { + stream.lineStart(); + for (var i = 0; i < ring.length; i++) { + stream.point(ring[i][0], ring[i][1]); + } + stream.lineEnd(); + }); + !isLine && stream.polygonEnd(); + return polygons; + } + // @ts-ignore FIXME:TS fix the "compatible with each other"? + + var MapView = /** @class */function (_super) { + __extends(MapView, _super); + function MapView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MapView.type; + return _this; + } + MapView.prototype.render = function (mapModel, ecModel, api, payload) { + // Not render if it is an toggleSelect action from self + if (payload && payload.type === 'mapToggleSelect' && payload.from === this.uid) { + return; + } + var group = this.group; + group.removeAll(); + if (mapModel.getHostGeoModel()) { + return; + } + if (this._mapDraw && payload && payload.type === 'geoRoam') { + this._mapDraw.resetForLabelLayout(); + } + // Not update map if it is an roam action from self + if (!(payload && payload.type === 'geoRoam' && payload.componentType === 'series' && payload.seriesId === mapModel.id)) { + if (mapModel.needsDrawMap) { + var mapDraw = this._mapDraw || new MapDraw(api); + group.add(mapDraw.group); + mapDraw.draw(mapModel, ecModel, api, this, payload); + this._mapDraw = mapDraw; + } else { + // Remove drawn map + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + } + } else { + var mapDraw = this._mapDraw; + mapDraw && group.add(mapDraw.group); + } + mapModel.get('showLegendSymbol') && ecModel.getComponent('legend') && this._renderSymbols(mapModel, ecModel, api); + }; + MapView.prototype.remove = function () { + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + this.group.removeAll(); + }; + MapView.prototype.dispose = function () { + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + }; + MapView.prototype._renderSymbols = function (mapModel, ecModel, api) { + var originalData = mapModel.originalData; + var group = this.group; + originalData.each(originalData.mapDimension('value'), function (value, originalDataIndex) { + if (isNaN(value)) { + return; + } + var layout = originalData.getItemLayout(originalDataIndex); + if (!layout || !layout.point) { + // Not exists in map + return; + } + var point = layout.point; + var offset = layout.offset; + var circle = new Circle({ + style: { + // Because the special of map draw. + // Which needs statistic of multiple series and draw on one map. + // And each series also need a symbol with legend color + // + // Layout and visual are put one the different data + // TODO + fill: mapModel.getData().getVisual('style').fill + }, + shape: { + cx: point[0] + offset * 9, + cy: point[1], + r: 3 + }, + silent: true, + // Do not overlap the first series, on which labels are displayed. + z2: 8 + (!offset ? Z2_EMPHASIS_LIFT + 1 : 0) + }); + // Only the series that has the first value on the same region is in charge of rendering the label. + // But consider the case: + // series: [ + // {id: 'X', type: 'map', map: 'm', {data: [{name: 'A', value: 11}, {name: 'B', {value: 22}]}, + // {id: 'Y', type: 'map', map: 'm', {data: [{name: 'A', value: 21}, {name: 'C', {value: 33}]} + // ] + // The offset `0` of item `A` is at series `X`, but of item `C` is at series `Y`. + // For backward compatibility, we follow the rule that render label `A` by the + // settings on series `X` but render label `C` by the settings on series `Y`. + if (!offset) { + var fullData = mapModel.mainSeries.getData(); + var name_1 = originalData.getName(originalDataIndex); + var fullIndex_1 = fullData.indexOfName(name_1); + var itemModel = originalData.getItemModel(originalDataIndex); + var labelModel = itemModel.getModel('label'); + var regionGroup = fullData.getItemGraphicEl(fullIndex_1); + // `getFormattedLabel` needs to use `getData` inside. Here + // `mapModel.getData()` is shallow cloned from `mainSeries.getData()`. + // FIXME + // If this is not the `mainSeries`, the item model (like label formatter) + // set on original data item will never get. But it has been working + // like that from the beginning, and this scenario is rarely encountered. + // So it won't be fixed until we have to. + setLabelStyle(circle, getLabelStatesModels(itemModel), { + labelFetcher: { + getFormattedLabel: function (idx, state) { + return mapModel.getFormattedLabel(fullIndex_1, state); + } + }, + defaultText: name_1 + }); + circle.disableLabelAnimation = true; + if (!labelModel.get('position')) { + circle.setTextConfig({ + position: 'bottom' + }); + } + regionGroup.onHoverStateChange = function (toState) { + setStatesFlag(circle, toState); + }; + } + group.add(circle); + }); + }; + MapView.type = 'map'; + return MapView; + }(ChartView); + + var MapSeries = /** @class */function (_super) { + __extends(MapSeries, _super); + function MapSeries() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MapSeries.type; + // Only first map series of same mapType will drawMap. + _this.needsDrawMap = false; + // Group of all map series with same mapType + _this.seriesGroup = []; + _this.getTooltipPosition = function (dataIndex) { + if (dataIndex != null) { + var name_1 = this.getData().getName(dataIndex); + var geo = this.coordinateSystem; + var region = geo.getRegion(name_1); + return region && geo.dataToPoint(region.getCenter()); + } + }; + return _this; + } + MapSeries.prototype.getInitialData = function (option) { + var data = createSeriesDataSimply(this, { + coordDimensions: ['value'], + encodeDefaulter: curry(makeSeriesEncodeForNameBased, this) + }); + var dataNameMap = createHashMap(); + var toAppendNames = []; + for (var i = 0, len = data.count(); i < len; i++) { + var name_2 = data.getName(i); + dataNameMap.set(name_2, true); + } + var geoSource = geoSourceManager.load(this.getMapType(), this.option.nameMap, this.option.nameProperty); + each(geoSource.regions, function (region) { + var name = region.name; + if (!dataNameMap.get(name)) { + toAppendNames.push(name); + } + }); + // Complete data with missing regions. The consequent processes (like visual + // map and render) can not be performed without a "full data". For example, + // find `dataIndex` by name. + data.appendValues([], toAppendNames); + return data; + }; + /** + * If no host geo model, return null, which means using a + * inner exclusive geo model. + */ + MapSeries.prototype.getHostGeoModel = function () { + var geoIndex = this.option.geoIndex; + return geoIndex != null ? this.ecModel.getComponent('geo', geoIndex) : null; + }; + MapSeries.prototype.getMapType = function () { + return (this.getHostGeoModel() || this).option.map; + }; + // _fillOption(option, mapName) { + // Shallow clone + // option = zrUtil.extend({}, option); + // option.data = geoCreator.getFilledRegions(option.data, mapName, option.nameMap); + // return option; + // } + MapSeries.prototype.getRawValue = function (dataIndex) { + // Use value stored in data instead because it is calculated from multiple series + // FIXME Provide all value of multiple series ? + var data = this.getData(); + return data.get(data.mapDimension('value'), dataIndex); + }; + /** + * Get model of region + */ + MapSeries.prototype.getRegionModel = function (regionName) { + var data = this.getData(); + return data.getItemModel(data.indexOfName(regionName)); + }; + /** + * Map tooltip formatter + */ + MapSeries.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + // FIXME orignalData and data is a bit confusing + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var name = data.getName(dataIndex); + var seriesGroup = this.seriesGroup; + var seriesNames = []; + for (var i = 0; i < seriesGroup.length; i++) { + var otherIndex = seriesGroup[i].originalData.indexOfName(name); + var valueDim = data.mapDimension('value'); + if (!isNaN(seriesGroup[i].originalData.get(valueDim, otherIndex))) { + seriesNames.push(seriesGroup[i].name); + } + } + return createTooltipMarkup('section', { + header: seriesNames.join(', '), + noHeader: !seriesNames.length, + blocks: [createTooltipMarkup('nameValue', { + name: name, + value: value + })] + }); + }; + MapSeries.prototype.setZoom = function (zoom) { + this.option.zoom = zoom; + }; + MapSeries.prototype.setCenter = function (center) { + this.option.center = center; + }; + MapSeries.prototype.getLegendIcon = function (opt) { + var iconType = opt.icon || 'roundRect'; + var icon = createSymbol(iconType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill); + icon.setStyle(opt.itemStyle); + // Map do not use itemStyle.borderWidth as border width + icon.style.stroke = 'none'; + // No rotation because no series visual symbol for map + if (iconType.indexOf('empty') > -1) { + icon.style.stroke = icon.style.fill; + icon.style.fill = '#fff'; + icon.style.lineWidth = 2; + } + return icon; + }; + MapSeries.type = 'series.map'; + MapSeries.dependencies = ['geo']; + MapSeries.layoutMode = 'box'; + MapSeries.defaultOption = { + // 一级层叠 + // zlevel: 0, + // 二级层叠 + z: 2, + coordinateSystem: 'geo', + // map should be explicitly specified since ec3. + map: '', + // If `geoIndex` is not specified, a exclusive geo will be + // created. Otherwise use the specified geo component, and + // `map` and `mapType` are ignored. + // geoIndex: 0, + // 'center' | 'left' | 'right' | 'x%' | {number} + left: 'center', + // 'center' | 'top' | 'bottom' | 'x%' | {number} + top: 'center', + // right + // bottom + // width: + // height + // Aspect is width / height. Inited to be geoJson bbox aspect + // This parameter is used for scale this aspect + // Default value: + // for geoSVG source: 1, + // for geoJSON source: 0.75. + aspectScale: null, + // Layout with center and size + // If you want to put map in a fixed size box with right aspect ratio + // This two properties may be more convenient. + // layoutCenter: [50%, 50%] + // layoutSize: 100 + showLegendSymbol: true, + // Define left-top, right-bottom coords to control view + // For example, [ [180, 90], [-180, -90] ], + // higher priority than center and zoom + boundingCoords: null, + // Default on center of map + center: null, + zoom: 1, + scaleLimit: null, + selectedMode: true, + label: { + show: false, + color: '#000' + }, + // scaleLimit: null, + itemStyle: { + borderWidth: 0.5, + borderColor: '#444', + areaColor: '#eee' + }, + emphasis: { + label: { + show: true, + color: 'rgb(100,0,0)' + }, + itemStyle: { + areaColor: 'rgba(255,215,0,0.8)' + } + }, + select: { + label: { + show: true, + color: 'rgb(100,0,0)' + }, + itemStyle: { + color: 'rgba(255,215,0,0.8)' + } + }, + nameProperty: 'name' + }; + return MapSeries; + }(SeriesModel); + + // FIXME 公用? + function dataStatistics(datas, statisticType) { + var dataNameMap = {}; + each(datas, function (data) { + data.each(data.mapDimension('value'), function (value, idx) { + // Add prefix to avoid conflict with Object.prototype. + var mapKey = 'ec-' + data.getName(idx); + dataNameMap[mapKey] = dataNameMap[mapKey] || []; + if (!isNaN(value)) { + dataNameMap[mapKey].push(value); + } + }); + }); + return datas[0].map(datas[0].mapDimension('value'), function (value, idx) { + var mapKey = 'ec-' + datas[0].getName(idx); + var sum = 0; + var min = Infinity; + var max = -Infinity; + var len = dataNameMap[mapKey].length; + for (var i = 0; i < len; i++) { + min = Math.min(min, dataNameMap[mapKey][i]); + max = Math.max(max, dataNameMap[mapKey][i]); + sum += dataNameMap[mapKey][i]; + } + var result; + if (statisticType === 'min') { + result = min; + } else if (statisticType === 'max') { + result = max; + } else if (statisticType === 'average') { + result = sum / len; + } else { + result = sum; + } + return len === 0 ? NaN : result; + }); + } + function mapDataStatistic(ecModel) { + var seriesGroups = {}; + ecModel.eachSeriesByType('map', function (seriesModel) { + var hostGeoModel = seriesModel.getHostGeoModel(); + var key = hostGeoModel ? 'o' + hostGeoModel.id : 'i' + seriesModel.getMapType(); + (seriesGroups[key] = seriesGroups[key] || []).push(seriesModel); + }); + each(seriesGroups, function (seriesList, key) { + var data = dataStatistics(map(seriesList, function (seriesModel) { + return seriesModel.getData(); + }), seriesList[0].get('mapValueCalculation')); + for (var i = 0; i < seriesList.length; i++) { + seriesList[i].originalData = seriesList[i].getData(); + } + // FIXME Put where? + for (var i = 0; i < seriesList.length; i++) { + seriesList[i].seriesGroup = seriesList; + seriesList[i].needsDrawMap = i === 0 && !seriesList[i].getHostGeoModel(); + seriesList[i].setData(data.cloneShallow()); + seriesList[i].mainSeries = seriesList[0]; + } + }); + } + + function mapSymbolLayout(ecModel) { + var processedMapType = {}; + ecModel.eachSeriesByType('map', function (mapSeries) { + var mapType = mapSeries.getMapType(); + if (mapSeries.getHostGeoModel() || processedMapType[mapType]) { + return; + } + var mapSymbolOffsets = {}; + each(mapSeries.seriesGroup, function (subMapSeries) { + var geo = subMapSeries.coordinateSystem; + var data = subMapSeries.originalData; + if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) { + data.each(data.mapDimension('value'), function (value, idx) { + var name = data.getName(idx); + var region = geo.getRegion(name); + // If input series.data is [11, 22, '-'/null/undefined, 44], + // it will be filled with NaN: [11, 22, NaN, 44] and NaN will + // not be drawn. So here must validate if value is NaN. + if (!region || isNaN(value)) { + return; + } + var offset = mapSymbolOffsets[name] || 0; + var point = geo.dataToPoint(region.getCenter()); + mapSymbolOffsets[name] = offset + 1; + data.setItemLayout(idx, { + point: point, + offset: offset + }); + }); + } + }); + // Show label of those region not has legendIcon (which is offset 0) + var data = mapSeries.getData(); + data.each(function (idx) { + var name = data.getName(idx); + var layout = data.getItemLayout(idx) || {}; + layout.showLabel = !mapSymbolOffsets[name]; + data.setItemLayout(idx, layout); + }); + processedMapType[mapType] = true; + }); + } + + var v2ApplyTransform = applyTransform; + var View = /** @class */function (_super) { + __extends(View, _super); + function View(name) { + var _this = _super.call(this) || this; + _this.type = 'view'; + _this.dimensions = ['x', 'y']; + /** + * Represents the transform brought by roam/zoom. + * If `View['_viewRect']` applies roam transform, + * we can get the final displayed rect. + */ + _this._roamTransformable = new Transformable(); + /** + * Represents the transform from `View['_rect']` to `View['_viewRect']`. + */ + _this._rawTransformable = new Transformable(); + _this.name = name; + return _this; + } + View.prototype.setBoundingRect = function (x, y, width, height) { + this._rect = new BoundingRect(x, y, width, height); + return this._rect; + }; + /** + * @return {module:zrender/core/BoundingRect} + */ + View.prototype.getBoundingRect = function () { + return this._rect; + }; + View.prototype.setViewRect = function (x, y, width, height) { + this._transformTo(x, y, width, height); + this._viewRect = new BoundingRect(x, y, width, height); + }; + /** + * Transformed to particular position and size + */ + View.prototype._transformTo = function (x, y, width, height) { + var rect = this.getBoundingRect(); + var rawTransform = this._rawTransformable; + rawTransform.transform = rect.calculateTransform(new BoundingRect(x, y, width, height)); + var rawParent = rawTransform.parent; + rawTransform.parent = null; + rawTransform.decomposeTransform(); + rawTransform.parent = rawParent; + this._updateTransform(); + }; + /** + * Set center of view + */ + View.prototype.setCenter = function (centerCoord, api) { + if (!centerCoord) { + return; + } + this._center = [parsePercent$1(centerCoord[0], api.getWidth()), parsePercent$1(centerCoord[1], api.getHeight())]; + this._updateCenterAndZoom(); + }; + View.prototype.setZoom = function (zoom) { + zoom = zoom || 1; + var zoomLimit = this.zoomLimit; + if (zoomLimit) { + if (zoomLimit.max != null) { + zoom = Math.min(zoomLimit.max, zoom); + } + if (zoomLimit.min != null) { + zoom = Math.max(zoomLimit.min, zoom); + } + } + this._zoom = zoom; + this._updateCenterAndZoom(); + }; + /** + * Get default center without roam + */ + View.prototype.getDefaultCenter = function () { + // Rect before any transform + var rawRect = this.getBoundingRect(); + var cx = rawRect.x + rawRect.width / 2; + var cy = rawRect.y + rawRect.height / 2; + return [cx, cy]; + }; + View.prototype.getCenter = function () { + return this._center || this.getDefaultCenter(); + }; + View.prototype.getZoom = function () { + return this._zoom || 1; + }; + View.prototype.getRoamTransform = function () { + return this._roamTransformable.getLocalTransform(); + }; + /** + * Remove roam + */ + View.prototype._updateCenterAndZoom = function () { + // Must update after view transform updated + var rawTransformMatrix = this._rawTransformable.getLocalTransform(); + var roamTransform = this._roamTransformable; + var defaultCenter = this.getDefaultCenter(); + var center = this.getCenter(); + var zoom = this.getZoom(); + center = applyTransform([], center, rawTransformMatrix); + defaultCenter = applyTransform([], defaultCenter, rawTransformMatrix); + roamTransform.originX = center[0]; + roamTransform.originY = center[1]; + roamTransform.x = defaultCenter[0] - center[0]; + roamTransform.y = defaultCenter[1] - center[1]; + roamTransform.scaleX = roamTransform.scaleY = zoom; + this._updateTransform(); + }; + /** + * Update transform props on `this` based on the current + * `this._roamTransformable` and `this._rawTransformable`. + */ + View.prototype._updateTransform = function () { + var roamTransformable = this._roamTransformable; + var rawTransformable = this._rawTransformable; + rawTransformable.parent = roamTransformable; + roamTransformable.updateTransform(); + rawTransformable.updateTransform(); + copy$1(this.transform || (this.transform = []), rawTransformable.transform || create$1()); + this._rawTransform = rawTransformable.getLocalTransform(); + this.invTransform = this.invTransform || []; + invert(this.invTransform, this.transform); + this.decomposeTransform(); + }; + View.prototype.getTransformInfo = function () { + var rawTransformable = this._rawTransformable; + var roamTransformable = this._roamTransformable; + // Because roamTransformabel has `originX/originY` modified, + // but the caller of `getTransformInfo` can not handle `originX/originY`, + // so need to recalculate them. + var dummyTransformable = new Transformable(); + dummyTransformable.transform = roamTransformable.transform; + dummyTransformable.decomposeTransform(); + return { + roam: { + x: dummyTransformable.x, + y: dummyTransformable.y, + scaleX: dummyTransformable.scaleX, + scaleY: dummyTransformable.scaleY + }, + raw: { + x: rawTransformable.x, + y: rawTransformable.y, + scaleX: rawTransformable.scaleX, + scaleY: rawTransformable.scaleY + } + }; + }; + View.prototype.getViewRect = function () { + return this._viewRect; + }; + /** + * Get view rect after roam transform + */ + View.prototype.getViewRectAfterRoam = function () { + var rect = this.getBoundingRect().clone(); + rect.applyTransform(this.transform); + return rect; + }; + /** + * Convert a single (lon, lat) data item to (x, y) point. + */ + View.prototype.dataToPoint = function (data, noRoam, out) { + var transform = noRoam ? this._rawTransform : this.transform; + out = out || []; + return transform ? v2ApplyTransform(out, data, transform) : copy(out, data); + }; + /** + * Convert a (x, y) point to (lon, lat) data + */ + View.prototype.pointToData = function (point) { + var invTransform = this.invTransform; + return invTransform ? v2ApplyTransform([], point, invTransform) : [point[0], point[1]]; + }; + View.prototype.convertToPixel = function (ecModel, finder, value) { + var coordSys = getCoordSys(finder); + return coordSys === this ? coordSys.dataToPoint(value) : null; + }; + View.prototype.convertFromPixel = function (ecModel, finder, pixel) { + var coordSys = getCoordSys(finder); + return coordSys === this ? coordSys.pointToData(pixel) : null; + }; + /** + * @implements + */ + View.prototype.containPoint = function (point) { + return this.getViewRectAfterRoam().contain(point[0], point[1]); + }; + View.dimensions = ['x', 'y']; + return View; + }(Transformable); + function getCoordSys(finder) { + var seriesModel = finder.seriesModel; + return seriesModel ? seriesModel.coordinateSystem : null; // e.g., graph. + } + + var GEO_DEFAULT_PARAMS = { + 'geoJSON': { + aspectScale: 0.75, + invertLongitute: true + }, + 'geoSVG': { + aspectScale: 1, + invertLongitute: false + } + }; + var geo2DDimensions = ['lng', 'lat']; + var Geo = /** @class */function (_super) { + __extends(Geo, _super); + function Geo(name, map, opt) { + var _this = _super.call(this, name) || this; + _this.dimensions = geo2DDimensions; + _this.type = 'geo'; + // Only store specified name coord via `addGeoCoord`. + _this._nameCoordMap = createHashMap(); + _this.map = map; + var projection = opt.projection; + var source = geoSourceManager.load(map, opt.nameMap, opt.nameProperty); + var resource = geoSourceManager.getGeoResource(map); + var resourceType = _this.resourceType = resource ? resource.type : null; + var regions = _this.regions = source.regions; + var defaultParams = GEO_DEFAULT_PARAMS[resource.type]; + _this._regionsMap = source.regionsMap; + _this.regions = source.regions; + if ("development" !== 'production' && projection) { + // Do some check + if (resourceType === 'geoSVG') { + if ("development" !== 'production') { + warn("Map " + map + " with SVG source can't use projection. Only GeoJSON source supports projection."); + } + projection = null; + } + if (!(projection.project && projection.unproject)) { + if ("development" !== 'production') { + warn('project and unproject must be both provided in the projeciton.'); + } + projection = null; + } + } + _this.projection = projection; + var boundingRect; + if (projection) { + // Can't reuse the raw bounding rect + for (var i = 0; i < regions.length; i++) { + var regionRect = regions[i].getBoundingRect(projection); + boundingRect = boundingRect || regionRect.clone(); + boundingRect.union(regionRect); + } + } else { + boundingRect = source.boundingRect; + } + _this.setBoundingRect(boundingRect.x, boundingRect.y, boundingRect.width, boundingRect.height); + // aspectScale and invertLongitute actually is the parameters default raw projection. + // So we ignore them if projection is given. + // Ignore default aspect scale if projection exits. + _this.aspectScale = projection ? 1 : retrieve2(opt.aspectScale, defaultParams.aspectScale); + // Not invert longitude if projection exits. + _this._invertLongitute = projection ? false : defaultParams.invertLongitute; + return _this; + } + Geo.prototype._transformTo = function (x, y, width, height) { + var rect = this.getBoundingRect(); + var invertLongitute = this._invertLongitute; + rect = rect.clone(); + if (invertLongitute) { + // Longitude is inverted. + rect.y = -rect.y - rect.height; + } + var rawTransformable = this._rawTransformable; + rawTransformable.transform = rect.calculateTransform(new BoundingRect(x, y, width, height)); + var rawParent = rawTransformable.parent; + rawTransformable.parent = null; + rawTransformable.decomposeTransform(); + rawTransformable.parent = rawParent; + if (invertLongitute) { + rawTransformable.scaleY = -rawTransformable.scaleY; + } + this._updateTransform(); + }; + Geo.prototype.getRegion = function (name) { + return this._regionsMap.get(name); + }; + Geo.prototype.getRegionByCoord = function (coord) { + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + var region = regions[i]; + if (region.type === 'geoJSON' && region.contain(coord)) { + return regions[i]; + } + } + }; + /** + * Add geoCoord for indexing by name + */ + Geo.prototype.addGeoCoord = function (name, geoCoord) { + this._nameCoordMap.set(name, geoCoord); + }; + /** + * Get geoCoord by name + */ + Geo.prototype.getGeoCoord = function (name) { + var region = this._regionsMap.get(name); + // Calculate center only on demand. + return this._nameCoordMap.get(name) || region && region.getCenter(); + }; + Geo.prototype.dataToPoint = function (data, noRoam, out) { + if (isString(data)) { + // Map area name to geoCoord + data = this.getGeoCoord(data); + } + if (data) { + var projection = this.projection; + if (projection) { + // projection may return null point. + data = projection.project(data); + } + return data && this.projectedToPoint(data, noRoam, out); + } + }; + Geo.prototype.pointToData = function (point) { + var projection = this.projection; + if (projection) { + // projection may return null point. + point = projection.unproject(point); + } + return point && this.pointToProjected(point); + }; + /** + * Point to projected data. Same with pointToData when projection is used. + */ + Geo.prototype.pointToProjected = function (point) { + return _super.prototype.pointToData.call(this, point); + }; + Geo.prototype.projectedToPoint = function (projected, noRoam, out) { + return _super.prototype.dataToPoint.call(this, projected, noRoam, out); + }; + Geo.prototype.convertToPixel = function (ecModel, finder, value) { + var coordSys = getCoordSys$1(finder); + return coordSys === this ? coordSys.dataToPoint(value) : null; + }; + Geo.prototype.convertFromPixel = function (ecModel, finder, pixel) { + var coordSys = getCoordSys$1(finder); + return coordSys === this ? coordSys.pointToData(pixel) : null; + }; + return Geo; + }(View); + mixin(Geo, View); + function getCoordSys$1(finder) { + var geoModel = finder.geoModel; + var seriesModel = finder.seriesModel; + return geoModel ? geoModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem // For map series. + || (seriesModel.getReferringComponents('geo', SINGLE_REFERRING).models[0] || {}).coordinateSystem : null; + } + + /** + * Resize method bound to the geo + */ + function resizeGeo(geoModel, api) { + var boundingCoords = geoModel.get('boundingCoords'); + if (boundingCoords != null) { + var leftTop_1 = boundingCoords[0]; + var rightBottom_1 = boundingCoords[1]; + if (!(isFinite(leftTop_1[0]) && isFinite(leftTop_1[1]) && isFinite(rightBottom_1[0]) && isFinite(rightBottom_1[1]))) { + if ("development" !== 'production') { + console.error('Invalid boundingCoords'); + } + } else { + // Sample around the lng/lat rect and use projection to calculate actual bounding rect. + var projection_1 = this.projection; + if (projection_1) { + var xMin = leftTop_1[0]; + var yMin = leftTop_1[1]; + var xMax = rightBottom_1[0]; + var yMax = rightBottom_1[1]; + leftTop_1 = [Infinity, Infinity]; + rightBottom_1 = [-Infinity, -Infinity]; + // TODO better way? + var sampleLine = function (x0, y0, x1, y1) { + var dx = x1 - x0; + var dy = y1 - y0; + for (var i = 0; i <= 100; i++) { + var p = i / 100; + var pt = projection_1.project([x0 + dx * p, y0 + dy * p]); + min(leftTop_1, leftTop_1, pt); + max(rightBottom_1, rightBottom_1, pt); + } + }; + // Top + sampleLine(xMin, yMin, xMax, yMin); + // Right + sampleLine(xMax, yMin, xMax, yMax); + // Bottom + sampleLine(xMax, yMax, xMin, yMax); + // Left + sampleLine(xMin, yMax, xMax, yMin); + } + this.setBoundingRect(leftTop_1[0], leftTop_1[1], rightBottom_1[0] - leftTop_1[0], rightBottom_1[1] - leftTop_1[1]); + } + } + var rect = this.getBoundingRect(); + var centerOption = geoModel.get('layoutCenter'); + var sizeOption = geoModel.get('layoutSize'); + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + var aspect = rect.width / rect.height * this.aspectScale; + var useCenterAndSize = false; + var center; + var size; + if (centerOption && sizeOption) { + center = [parsePercent$1(centerOption[0], viewWidth), parsePercent$1(centerOption[1], viewHeight)]; + size = parsePercent$1(sizeOption, Math.min(viewWidth, viewHeight)); + if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) { + useCenterAndSize = true; + } else { + if ("development" !== 'production') { + console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.'); + } + } + } + var viewRect; + if (useCenterAndSize) { + viewRect = {}; + if (aspect > 1) { + // Width is same with size + viewRect.width = size; + viewRect.height = size / aspect; + } else { + viewRect.height = size; + viewRect.width = size * aspect; + } + viewRect.y = center[1] - viewRect.height / 2; + viewRect.x = center[0] - viewRect.width / 2; + } else { + // Use left/top/width/height + var boxLayoutOption = geoModel.getBoxLayoutParams(); + boxLayoutOption.aspect = aspect; + viewRect = getLayoutRect(boxLayoutOption, { + width: viewWidth, + height: viewHeight + }); + } + this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height); + this.setCenter(geoModel.get('center'), api); + this.setZoom(geoModel.get('zoom')); + } + // Back compat for ECharts2, where the coord map is set on map series: + // {type: 'map', geoCoord: {'cityA': [116.46,39.92], 'cityA': [119.12,24.61]}}, + function setGeoCoords(geo, model) { + each(model.get('geoCoord'), function (geoCoord, name) { + geo.addGeoCoord(name, geoCoord); + }); + } + var GeoCreator = /** @class */function () { + function GeoCreator() { + // For deciding which dimensions to use when creating list data + this.dimensions = geo2DDimensions; + } + GeoCreator.prototype.create = function (ecModel, api) { + var geoList = []; + function getCommonGeoProperties(model) { + return { + nameProperty: model.get('nameProperty'), + aspectScale: model.get('aspectScale'), + projection: model.get('projection') + }; + } + // FIXME Create each time may be slow + ecModel.eachComponent('geo', function (geoModel, idx) { + var mapName = geoModel.get('map'); + var geo = new Geo(mapName + idx, mapName, extend({ + nameMap: geoModel.get('nameMap') + }, getCommonGeoProperties(geoModel))); + geo.zoomLimit = geoModel.get('scaleLimit'); + geoList.push(geo); + // setGeoCoords(geo, geoModel); + geoModel.coordinateSystem = geo; + geo.model = geoModel; + // Inject resize method + geo.resize = resizeGeo; + geo.resize(geoModel, api); + }); + ecModel.eachSeries(function (seriesModel) { + var coordSys = seriesModel.get('coordinateSystem'); + if (coordSys === 'geo') { + var geoIndex = seriesModel.get('geoIndex') || 0; + seriesModel.coordinateSystem = geoList[geoIndex]; + } + }); + // If has map series + var mapModelGroupBySeries = {}; + ecModel.eachSeriesByType('map', function (seriesModel) { + if (!seriesModel.getHostGeoModel()) { + var mapType = seriesModel.getMapType(); + mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || []; + mapModelGroupBySeries[mapType].push(seriesModel); + } + }); + each(mapModelGroupBySeries, function (mapSeries, mapType) { + var nameMapList = map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('nameMap'); + }); + var geo = new Geo(mapType, mapType, extend({ + nameMap: mergeAll(nameMapList) + }, getCommonGeoProperties(mapSeries[0]))); + geo.zoomLimit = retrieve.apply(null, map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('scaleLimit'); + })); + geoList.push(geo); + // Inject resize method + geo.resize = resizeGeo; + geo.resize(mapSeries[0], api); + each(mapSeries, function (singleMapSeries) { + singleMapSeries.coordinateSystem = geo; + setGeoCoords(geo, singleMapSeries); + }); + }); + return geoList; + }; + /** + * Fill given regions array + */ + GeoCreator.prototype.getFilledRegions = function (originRegionArr, mapName, nameMap, nameProperty) { + // Not use the original + var regionsArr = (originRegionArr || []).slice(); + var dataNameMap = createHashMap(); + for (var i = 0; i < regionsArr.length; i++) { + dataNameMap.set(regionsArr[i].name, regionsArr[i]); + } + var source = geoSourceManager.load(mapName, nameMap, nameProperty); + each(source.regions, function (region) { + var name = region.name; + !dataNameMap.get(name) && regionsArr.push({ + name: name + }); + }); + return regionsArr; + }; + return GeoCreator; + }(); + var geoCreator = new GeoCreator(); + + var GeoModel = /** @class */function (_super) { + __extends(GeoModel, _super); + function GeoModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GeoModel.type; + return _this; + } + GeoModel.prototype.init = function (option, parentModel, ecModel) { + var source = geoSourceManager.getGeoResource(option.map); + if (source && source.type === 'geoJSON') { + var itemStyle = option.itemStyle = option.itemStyle || {}; + if (!('color' in itemStyle)) { + itemStyle.color = '#eee'; + } + } + this.mergeDefaultAndTheme(option, ecModel); + // Default label emphasis `show` + defaultEmphasis(option, 'label', ['show']); + }; + GeoModel.prototype.optionUpdated = function () { + var _this = this; + var option = this.option; + option.regions = geoCreator.getFilledRegions(option.regions, option.map, option.nameMap, option.nameProperty); + var selectedMap = {}; + this._optionModelMap = reduce(option.regions || [], function (optionModelMap, regionOpt) { + var regionName = regionOpt.name; + if (regionName) { + optionModelMap.set(regionName, new Model(regionOpt, _this, _this.ecModel)); + if (regionOpt.selected) { + selectedMap[regionName] = true; + } + } + return optionModelMap; + }, createHashMap()); + if (!option.selectedMap) { + option.selectedMap = selectedMap; + } + }; + /** + * Get model of region. + */ + GeoModel.prototype.getRegionModel = function (name) { + return this._optionModelMap.get(name) || new Model(null, this, this.ecModel); + }; + /** + * Format label + * @param name Region name + */ + GeoModel.prototype.getFormattedLabel = function (name, status) { + var regionModel = this.getRegionModel(name); + var formatter = status === 'normal' ? regionModel.get(['label', 'formatter']) : regionModel.get(['emphasis', 'label', 'formatter']); + var params = { + name: name + }; + if (isFunction(formatter)) { + params.status = status; + return formatter(params); + } else if (isString(formatter)) { + return formatter.replace('{a}', name != null ? name : ''); + } + }; + GeoModel.prototype.setZoom = function (zoom) { + this.option.zoom = zoom; + }; + GeoModel.prototype.setCenter = function (center) { + this.option.center = center; + }; + // PENGING If selectedMode is null ? + GeoModel.prototype.select = function (name) { + var option = this.option; + var selectedMode = option.selectedMode; + if (!selectedMode) { + return; + } + if (selectedMode !== 'multiple') { + option.selectedMap = null; + } + var selectedMap = option.selectedMap || (option.selectedMap = {}); + selectedMap[name] = true; + }; + GeoModel.prototype.unSelect = function (name) { + var selectedMap = this.option.selectedMap; + if (selectedMap) { + selectedMap[name] = false; + } + }; + GeoModel.prototype.toggleSelected = function (name) { + this[this.isSelected(name) ? 'unSelect' : 'select'](name); + }; + GeoModel.prototype.isSelected = function (name) { + var selectedMap = this.option.selectedMap; + return !!(selectedMap && selectedMap[name]); + }; + GeoModel.type = 'geo'; + GeoModel.layoutMode = 'box'; + GeoModel.defaultOption = { + // zlevel: 0, + z: 0, + show: true, + left: 'center', + top: 'center', + // Default value: + // for geoSVG source: 1, + // for geoJSON source: 0.75. + aspectScale: null, + // /// Layout with center and size + // If you want to put map in a fixed size box with right aspect ratio + // This two properties may be more convenient + // layoutCenter: [50%, 50%] + // layoutSize: 100 + silent: false, + // Map type + map: '', + // Define left-top, right-bottom coords to control view + // For example, [ [180, 90], [-180, -90] ] + boundingCoords: null, + // Default on center of map + center: null, + zoom: 1, + scaleLimit: null, + // selectedMode: false + label: { + show: false, + color: '#000' + }, + itemStyle: { + borderWidth: 0.5, + borderColor: '#444' + // Default color: + // + geoJSON: #eee + // + geoSVG: null (use SVG original `fill`) + // color: '#eee' + }, + + emphasis: { + label: { + show: true, + color: 'rgb(100,0,0)' + }, + itemStyle: { + color: 'rgba(255,215,0,0.8)' + } + }, + select: { + label: { + show: true, + color: 'rgb(100,0,0)' + }, + itemStyle: { + color: 'rgba(255,215,0,0.8)' + } + }, + regions: [] + // tooltip: { + // show: false + // } + }; + + return GeoModel; + }(ComponentModel); + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function getCenterCoord(view, point) { + // Use projected coord as center because it's linear. + return view.pointToProjected ? view.pointToProjected(point) : view.pointToData(point); + } + function updateCenterAndZoom(view, payload, zoomLimit, api) { + var previousZoom = view.getZoom(); + var center = view.getCenter(); + var zoom = payload.zoom; + var point = view.projectedToPoint ? view.projectedToPoint(center) : view.dataToPoint(center); + if (payload.dx != null && payload.dy != null) { + point[0] -= payload.dx; + point[1] -= payload.dy; + view.setCenter(getCenterCoord(view, point), api); + } + if (zoom != null) { + if (zoomLimit) { + var zoomMin = zoomLimit.min || 0; + var zoomMax = zoomLimit.max || Infinity; + zoom = Math.max(Math.min(previousZoom * zoom, zoomMax), zoomMin) / previousZoom; + } + // Zoom on given point(originX, originY) + view.scaleX *= zoom; + view.scaleY *= zoom; + var fixX = (payload.originX - view.x) * (zoom - 1); + var fixY = (payload.originY - view.y) * (zoom - 1); + view.x -= fixX; + view.y -= fixY; + view.updateTransform(); + // Get the new center + view.setCenter(getCenterCoord(view, point), api); + view.setZoom(zoom * previousZoom); + } + return { + center: view.getCenter(), + zoom: view.getZoom() + }; + } + + var GeoView = /** @class */function (_super) { + __extends(GeoView, _super); + function GeoView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GeoView.type; + _this.focusBlurEnabled = true; + return _this; + } + GeoView.prototype.init = function (ecModel, api) { + this._api = api; + }; + GeoView.prototype.render = function (geoModel, ecModel, api, payload) { + this._model = geoModel; + if (!geoModel.get('show')) { + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + return; + } + if (!this._mapDraw) { + this._mapDraw = new MapDraw(api); + } + var mapDraw = this._mapDraw; + mapDraw.draw(geoModel, ecModel, api, this, payload); + mapDraw.group.on('click', this._handleRegionClick, this); + mapDraw.group.silent = geoModel.get('silent'); + this.group.add(mapDraw.group); + this.updateSelectStatus(geoModel, ecModel, api); + }; + GeoView.prototype._handleRegionClick = function (e) { + var eventData; + findEventDispatcher(e.target, function (current) { + return (eventData = getECData(current).eventData) != null; + }, true); + if (eventData) { + this._api.dispatchAction({ + type: 'geoToggleSelect', + geoId: this._model.id, + name: eventData.name + }); + } + }; + GeoView.prototype.updateSelectStatus = function (model, ecModel, api) { + var _this = this; + this._mapDraw.group.traverse(function (node) { + var eventData = getECData(node).eventData; + if (eventData) { + _this._model.isSelected(eventData.name) ? api.enterSelect(node) : api.leaveSelect(node); + // No need to traverse children. + return true; + } + }); + }; + GeoView.prototype.findHighDownDispatchers = function (name) { + return this._mapDraw && this._mapDraw.findHighDownDispatchers(name, this._model); + }; + GeoView.prototype.dispose = function () { + this._mapDraw && this._mapDraw.remove(); + }; + GeoView.type = 'geo'; + return GeoView; + }(ComponentView); + + function registerMap$1(mapName, geoJson, specialAreas) { + geoSourceManager.registerMap(mapName, geoJson, specialAreas); + } + function install$9(registers) { + registers.registerCoordinateSystem('geo', geoCreator); + registers.registerComponentModel(GeoModel); + registers.registerComponentView(GeoView); + registers.registerImpl('registerMap', registerMap$1); + registers.registerImpl('getMap', function (mapName) { + return geoSourceManager.getMapForUser(mapName); + }); + function makeAction(method, actionInfo) { + actionInfo.update = 'geo:updateSelectStatus'; + registers.registerAction(actionInfo, function (payload, ecModel) { + var selected = {}; + var allSelected = []; + ecModel.eachComponent({ + mainType: 'geo', + query: payload + }, function (geoModel) { + geoModel[method](payload.name); + var geo = geoModel.coordinateSystem; + each(geo.regions, function (region) { + selected[region.name] = geoModel.isSelected(region.name) || false; + }); + // Notice: there might be duplicated name in different regions. + var names = []; + each(selected, function (v, name) { + selected[name] && names.push(name); + }); + allSelected.push({ + geoIndex: geoModel.componentIndex, + // Use singular, the same naming convention as the event `selectchanged`. + name: names + }); + }); + return { + selected: selected, + allSelected: allSelected, + name: payload.name + }; + }); + } + makeAction('toggleSelected', { + type: 'geoToggleSelect', + event: 'geoselectchanged' + }); + makeAction('select', { + type: 'geoSelect', + event: 'geoselected' + }); + makeAction('unSelect', { + type: 'geoUnSelect', + event: 'geounselected' + }); + /** + * @payload + * @property {string} [componentType=series] + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ + registers.registerAction({ + type: 'geoRoam', + event: 'geoRoam', + update: 'updateTransform' + }, function (payload, ecModel, api) { + var componentType = payload.componentType || 'series'; + ecModel.eachComponent({ + mainType: componentType, + query: payload + }, function (componentModel) { + var geo = componentModel.coordinateSystem; + if (geo.type !== 'geo') { + return; + } + var res = updateCenterAndZoom(geo, payload, componentModel.get('scaleLimit'), api); + componentModel.setCenter && componentModel.setCenter(res.center); + componentModel.setZoom && componentModel.setZoom(res.zoom); + // All map series with same `map` use the same geo coordinate system + // So the center and zoom must be in sync. Include the series not selected by legend + if (componentType === 'series') { + each(componentModel.seriesGroup, function (seriesModel) { + seriesModel.setCenter(res.center); + seriesModel.setZoom(res.zoom); + }); + } + }); + }); + } + + function install$a(registers) { + use(install$9); + registers.registerChartView(MapView); + registers.registerSeriesModel(MapSeries); + registers.registerLayout(mapSymbolLayout); + registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, mapDataStatistic); + createLegacyDataSelectAction('map', registers.registerAction); + } + + /** + * Initialize all computational message for following algorithm. + */ + function init$2(inRoot) { + var root = inRoot; + root.hierNode = { + defaultAncestor: null, + ancestor: root, + prelim: 0, + modifier: 0, + change: 0, + shift: 0, + i: 0, + thread: null + }; + var nodes = [root]; + var node; + var children; + while (node = nodes.pop()) { + // jshint ignore:line + children = node.children; + if (node.isExpand && children.length) { + var n = children.length; + for (var i = n - 1; i >= 0; i--) { + var child = children[i]; + child.hierNode = { + defaultAncestor: null, + ancestor: child, + prelim: 0, + modifier: 0, + change: 0, + shift: 0, + i: i, + thread: null + }; + nodes.push(child); + } + } + } + } + /** + * The implementation of this function was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + * + * Computes a preliminary x coordinate for node. Before that, this function is + * applied recursively to the children of node, as well as the function + * apportion(). After spacing out the children by calling executeShifts(), the + * node is placed to the midpoint of its outermost children. + */ + function firstWalk(node, separation) { + var children = node.isExpand ? node.children : []; + var siblings = node.parentNode.children; + var subtreeW = node.hierNode.i ? siblings[node.hierNode.i - 1] : null; + if (children.length) { + executeShifts(node); + var midPoint = (children[0].hierNode.prelim + children[children.length - 1].hierNode.prelim) / 2; + if (subtreeW) { + node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW); + node.hierNode.modifier = node.hierNode.prelim - midPoint; + } else { + node.hierNode.prelim = midPoint; + } + } else if (subtreeW) { + node.hierNode.prelim = subtreeW.hierNode.prelim + separation(node, subtreeW); + } + node.parentNode.hierNode.defaultAncestor = apportion(node, subtreeW, node.parentNode.hierNode.defaultAncestor || siblings[0], separation); + } + /** + * The implementation of this function was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + * + * Computes all real x-coordinates by summing up the modifiers recursively. + */ + function secondWalk(node) { + var nodeX = node.hierNode.prelim + node.parentNode.hierNode.modifier; + node.setLayout({ + x: nodeX + }, true); + node.hierNode.modifier += node.parentNode.hierNode.modifier; + } + function separation(cb) { + return arguments.length ? cb : defaultSeparation; + } + /** + * Transform the common coordinate to radial coordinate. + */ + function radialCoordinate(rad, r) { + rad -= Math.PI / 2; + return { + x: r * Math.cos(rad), + y: r * Math.sin(rad) + }; + } + /** + * Get the layout position of the whole view. + */ + function getViewRect$1(seriesModel, api) { + return getLayoutRect(seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + } + /** + * All other shifts, applied to the smaller subtrees between w- and w+, are + * performed by this function. + * + * The implementation of this function was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + */ + function executeShifts(node) { + var children = node.children; + var n = children.length; + var shift = 0; + var change = 0; + while (--n >= 0) { + var child = children[n]; + child.hierNode.prelim += shift; + child.hierNode.modifier += shift; + change += child.hierNode.change; + shift += child.hierNode.shift + change; + } + } + /** + * The implementation of this function was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + * + * The core of the algorithm. Here, a new subtree is combined with the + * previous subtrees. Threads are used to traverse the inside and outside + * contours of the left and right subtree up to the highest common level. + * Whenever two nodes of the inside contours conflict, we compute the left + * one of the greatest uncommon ancestors using the function nextAncestor() + * and call moveSubtree() to shift the subtree and prepare the shifts of + * smaller subtrees. Finally, we add a new thread (if necessary). + */ + function apportion(subtreeV, subtreeW, ancestor, separation) { + if (subtreeW) { + var nodeOutRight = subtreeV; + var nodeInRight = subtreeV; + var nodeOutLeft = nodeInRight.parentNode.children[0]; + var nodeInLeft = subtreeW; + var sumOutRight = nodeOutRight.hierNode.modifier; + var sumInRight = nodeInRight.hierNode.modifier; + var sumOutLeft = nodeOutLeft.hierNode.modifier; + var sumInLeft = nodeInLeft.hierNode.modifier; + while (nodeInLeft = nextRight(nodeInLeft), nodeInRight = nextLeft(nodeInRight), nodeInLeft && nodeInRight) { + nodeOutRight = nextRight(nodeOutRight); + nodeOutLeft = nextLeft(nodeOutLeft); + nodeOutRight.hierNode.ancestor = subtreeV; + var shift = nodeInLeft.hierNode.prelim + sumInLeft - nodeInRight.hierNode.prelim - sumInRight + separation(nodeInLeft, nodeInRight); + if (shift > 0) { + moveSubtree(nextAncestor(nodeInLeft, subtreeV, ancestor), subtreeV, shift); + sumInRight += shift; + sumOutRight += shift; + } + sumInLeft += nodeInLeft.hierNode.modifier; + sumInRight += nodeInRight.hierNode.modifier; + sumOutRight += nodeOutRight.hierNode.modifier; + sumOutLeft += nodeOutLeft.hierNode.modifier; + } + if (nodeInLeft && !nextRight(nodeOutRight)) { + nodeOutRight.hierNode.thread = nodeInLeft; + nodeOutRight.hierNode.modifier += sumInLeft - sumOutRight; + } + if (nodeInRight && !nextLeft(nodeOutLeft)) { + nodeOutLeft.hierNode.thread = nodeInRight; + nodeOutLeft.hierNode.modifier += sumInRight - sumOutLeft; + ancestor = subtreeV; + } + } + return ancestor; + } + /** + * This function is used to traverse the right contour of a subtree. + * It returns the rightmost child of node or the thread of node. The function + * returns null if and only if node is on the highest depth of its subtree. + */ + function nextRight(node) { + var children = node.children; + return children.length && node.isExpand ? children[children.length - 1] : node.hierNode.thread; + } + /** + * This function is used to traverse the left contour of a subtree (or a subforest). + * It returns the leftmost child of node or the thread of node. The function + * returns null if and only if node is on the highest depth of its subtree. + */ + function nextLeft(node) { + var children = node.children; + return children.length && node.isExpand ? children[0] : node.hierNode.thread; + } + /** + * If nodeInLeft’s ancestor is a sibling of node, returns nodeInLeft’s ancestor. + * Otherwise, returns the specified ancestor. + */ + function nextAncestor(nodeInLeft, node, ancestor) { + return nodeInLeft.hierNode.ancestor.parentNode === node.parentNode ? nodeInLeft.hierNode.ancestor : ancestor; + } + /** + * The implementation of this function was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + * + * Shifts the current subtree rooted at wr. + * This is done by increasing prelim(w+) and modifier(w+) by shift. + */ + function moveSubtree(wl, wr, shift) { + var change = shift / (wr.hierNode.i - wl.hierNode.i); + wr.hierNode.change -= change; + wr.hierNode.shift += shift; + wr.hierNode.modifier += shift; + wr.hierNode.prelim += shift; + wl.hierNode.change += change; + } + /** + * The implementation of this function was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + */ + function defaultSeparation(node1, node2) { + return node1.parentNode === node2.parentNode ? 1 : 2; + } + + var TreeEdgeShape = /** @class */function () { + function TreeEdgeShape() { + this.parentPoint = []; + this.childPoints = []; + } + return TreeEdgeShape; + }(); + var TreePath = /** @class */function (_super) { + __extends(TreePath, _super); + function TreePath(opts) { + return _super.call(this, opts) || this; + } + TreePath.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + TreePath.prototype.getDefaultShape = function () { + return new TreeEdgeShape(); + }; + TreePath.prototype.buildPath = function (ctx, shape) { + var childPoints = shape.childPoints; + var childLen = childPoints.length; + var parentPoint = shape.parentPoint; + var firstChildPos = childPoints[0]; + var lastChildPos = childPoints[childLen - 1]; + if (childLen === 1) { + ctx.moveTo(parentPoint[0], parentPoint[1]); + ctx.lineTo(firstChildPos[0], firstChildPos[1]); + return; + } + var orient = shape.orient; + var forkDim = orient === 'TB' || orient === 'BT' ? 0 : 1; + var otherDim = 1 - forkDim; + var forkPosition = parsePercent$1(shape.forkPosition, 1); + var tmpPoint = []; + tmpPoint[forkDim] = parentPoint[forkDim]; + tmpPoint[otherDim] = parentPoint[otherDim] + (lastChildPos[otherDim] - parentPoint[otherDim]) * forkPosition; + ctx.moveTo(parentPoint[0], parentPoint[1]); + ctx.lineTo(tmpPoint[0], tmpPoint[1]); + ctx.moveTo(firstChildPos[0], firstChildPos[1]); + tmpPoint[forkDim] = firstChildPos[forkDim]; + ctx.lineTo(tmpPoint[0], tmpPoint[1]); + tmpPoint[forkDim] = lastChildPos[forkDim]; + ctx.lineTo(tmpPoint[0], tmpPoint[1]); + ctx.lineTo(lastChildPos[0], lastChildPos[1]); + for (var i = 1; i < childLen - 1; i++) { + var point = childPoints[i]; + ctx.moveTo(point[0], point[1]); + tmpPoint[forkDim] = point[forkDim]; + ctx.lineTo(tmpPoint[0], tmpPoint[1]); + } + }; + return TreePath; + }(Path); + var TreeView = /** @class */function (_super) { + __extends(TreeView, _super); + function TreeView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TreeView.type; + _this._mainGroup = new Group(); + return _this; + } + TreeView.prototype.init = function (ecModel, api) { + this._controller = new RoamController(api.getZr()); + this._controllerHost = { + target: this.group + }; + this.group.add(this._mainGroup); + }; + TreeView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var layoutInfo = seriesModel.layoutInfo; + var group = this._mainGroup; + var layout = seriesModel.get('layout'); + if (layout === 'radial') { + group.x = layoutInfo.x + layoutInfo.width / 2; + group.y = layoutInfo.y + layoutInfo.height / 2; + } else { + group.x = layoutInfo.x; + group.y = layoutInfo.y; + } + this._updateViewCoordSys(seriesModel, api); + this._updateController(seriesModel, ecModel, api); + var oldData = this._data; + data.diff(oldData).add(function (newIdx) { + if (symbolNeedsDraw$1(data, newIdx)) { + // Create node and edge + updateNode(data, newIdx, null, group, seriesModel); + } + }).update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + if (!symbolNeedsDraw$1(data, newIdx)) { + symbolEl && removeNode(oldData, oldIdx, symbolEl, group, seriesModel); + return; + } + // Update node and edge + updateNode(data, newIdx, symbolEl, group, seriesModel); + }).remove(function (oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + // When remove a collapsed node of subtree, since the collapsed + // node haven't been initialized with a symbol element, + // you can't found it's symbol element through index. + // so if we want to remove the symbol element we should insure + // that the symbol element is not null. + if (symbolEl) { + removeNode(oldData, oldIdx, symbolEl, group, seriesModel); + } + }).execute(); + this._nodeScaleRatio = seriesModel.get('nodeScaleRatio'); + this._updateNodeAndLinkScale(seriesModel); + if (seriesModel.get('expandAndCollapse') === true) { + data.eachItemGraphicEl(function (el, dataIndex) { + el.off('click').on('click', function () { + api.dispatchAction({ + type: 'treeExpandAndCollapse', + seriesId: seriesModel.id, + dataIndex: dataIndex + }); + }); + }); + } + this._data = data; + }; + TreeView.prototype._updateViewCoordSys = function (seriesModel, api) { + var data = seriesModel.getData(); + var points = []; + data.each(function (idx) { + var layout = data.getItemLayout(idx); + if (layout && !isNaN(layout.x) && !isNaN(layout.y)) { + points.push([+layout.x, +layout.y]); + } + }); + var min = []; + var max = []; + fromPoints(points, min, max); + // If don't Store min max when collapse the root node after roam, + // the root node will disappear. + var oldMin = this._min; + var oldMax = this._max; + // If width or height is 0 + if (max[0] - min[0] === 0) { + min[0] = oldMin ? oldMin[0] : min[0] - 1; + max[0] = oldMax ? oldMax[0] : max[0] + 1; + } + if (max[1] - min[1] === 0) { + min[1] = oldMin ? oldMin[1] : min[1] - 1; + max[1] = oldMax ? oldMax[1] : max[1] + 1; + } + var viewCoordSys = seriesModel.coordinateSystem = new View(); + viewCoordSys.zoomLimit = seriesModel.get('scaleLimit'); + viewCoordSys.setBoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]); + viewCoordSys.setCenter(seriesModel.get('center'), api); + viewCoordSys.setZoom(seriesModel.get('zoom')); + // Here we use viewCoordSys just for computing the 'position' and 'scale' of the group + this.group.attr({ + x: viewCoordSys.x, + y: viewCoordSys.y, + scaleX: viewCoordSys.scaleX, + scaleY: viewCoordSys.scaleY + }); + this._min = min; + this._max = max; + }; + TreeView.prototype._updateController = function (seriesModel, ecModel, api) { + var _this = this; + var controller = this._controller; + var controllerHost = this._controllerHost; + var group = this.group; + controller.setPointerChecker(function (e, x, y) { + var rect = group.getBoundingRect(); + rect.applyTransform(group.transform); + return rect.contain(x, y) && !onIrrelevantElement(e, api, seriesModel); + }); + controller.enable(seriesModel.get('roam')); + controllerHost.zoomLimit = seriesModel.get('scaleLimit'); + controllerHost.zoom = seriesModel.coordinateSystem.getZoom(); + controller.off('pan').off('zoom').on('pan', function (e) { + updateViewOnPan(controllerHost, e.dx, e.dy); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'treeRoam', + dx: e.dx, + dy: e.dy + }); + }).on('zoom', function (e) { + updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'treeRoam', + zoom: e.scale, + originX: e.originX, + originY: e.originY + }); + _this._updateNodeAndLinkScale(seriesModel); + // Only update label layout on zoom + api.updateLabelLayout(); + }); + }; + TreeView.prototype._updateNodeAndLinkScale = function (seriesModel) { + var data = seriesModel.getData(); + var nodeScale = this._getNodeGlobalScale(seriesModel); + data.eachItemGraphicEl(function (el, idx) { + el.setSymbolScale(nodeScale); + }); + }; + TreeView.prototype._getNodeGlobalScale = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type !== 'view') { + return 1; + } + var nodeScaleRatio = this._nodeScaleRatio; + var groupZoom = coordSys.scaleX || 1; + // Scale node when zoom changes + var roamZoom = coordSys.getZoom(); + var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1; + return nodeScale / groupZoom; + }; + TreeView.prototype.dispose = function () { + this._controller && this._controller.dispose(); + this._controllerHost = null; + }; + TreeView.prototype.remove = function () { + this._mainGroup.removeAll(); + this._data = null; + }; + TreeView.type = 'tree'; + return TreeView; + }(ChartView); + function symbolNeedsDraw$1(data, dataIndex) { + var layout = data.getItemLayout(dataIndex); + return layout && !isNaN(layout.x) && !isNaN(layout.y); + } + function updateNode(data, dataIndex, symbolEl, group, seriesModel) { + var isInit = !symbolEl; + var node = data.tree.getNodeByDataIndex(dataIndex); + var itemModel = node.getModel(); + var visualColor = node.getVisual('style').fill; + var symbolInnerColor = node.isExpand === false && node.children.length !== 0 ? visualColor : '#fff'; + var virtualRoot = data.tree.root; + var source = node.parentNode === virtualRoot ? node : node.parentNode || node; + var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex); + var sourceLayout = source.getLayout(); + var sourceOldLayout = sourceSymbolEl ? { + x: sourceSymbolEl.__oldX, + y: sourceSymbolEl.__oldY, + rawX: sourceSymbolEl.__radialOldRawX, + rawY: sourceSymbolEl.__radialOldRawY + } : sourceLayout; + var targetLayout = node.getLayout(); + if (isInit) { + symbolEl = new Symbol(data, dataIndex, null, { + symbolInnerColor: symbolInnerColor, + useNameLabel: true + }); + symbolEl.x = sourceOldLayout.x; + symbolEl.y = sourceOldLayout.y; + } else { + symbolEl.updateData(data, dataIndex, null, { + symbolInnerColor: symbolInnerColor, + useNameLabel: true + }); + } + symbolEl.__radialOldRawX = symbolEl.__radialRawX; + symbolEl.__radialOldRawY = symbolEl.__radialRawY; + symbolEl.__radialRawX = targetLayout.rawX; + symbolEl.__radialRawY = targetLayout.rawY; + group.add(symbolEl); + data.setItemGraphicEl(dataIndex, symbolEl); + symbolEl.__oldX = symbolEl.x; + symbolEl.__oldY = symbolEl.y; + updateProps(symbolEl, { + x: targetLayout.x, + y: targetLayout.y + }, seriesModel); + var symbolPath = symbolEl.getSymbolPath(); + if (seriesModel.get('layout') === 'radial') { + var realRoot = virtualRoot.children[0]; + var rootLayout = realRoot.getLayout(); + var length_1 = realRoot.children.length; + var rad = void 0; + var isLeft = void 0; + if (targetLayout.x === rootLayout.x && node.isExpand === true && realRoot.children.length) { + var center = { + x: (realRoot.children[0].getLayout().x + realRoot.children[length_1 - 1].getLayout().x) / 2, + y: (realRoot.children[0].getLayout().y + realRoot.children[length_1 - 1].getLayout().y) / 2 + }; + rad = Math.atan2(center.y - rootLayout.y, center.x - rootLayout.x); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + isLeft = center.x < rootLayout.x; + if (isLeft) { + rad = rad - Math.PI; + } + } else { + rad = Math.atan2(targetLayout.y - rootLayout.y, targetLayout.x - rootLayout.x); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + if (node.children.length === 0 || node.children.length !== 0 && node.isExpand === false) { + isLeft = targetLayout.x < rootLayout.x; + if (isLeft) { + rad = rad - Math.PI; + } + } else { + isLeft = targetLayout.x > rootLayout.x; + if (!isLeft) { + rad = rad - Math.PI; + } + } + } + var textPosition = isLeft ? 'left' : 'right'; + var normalLabelModel = itemModel.getModel('label'); + var rotate = normalLabelModel.get('rotate'); + var labelRotateRadian = rotate * (Math.PI / 180); + var textContent = symbolPath.getTextContent(); + if (textContent) { + symbolPath.setTextConfig({ + position: normalLabelModel.get('position') || textPosition, + rotation: rotate == null ? -rad : labelRotateRadian, + origin: 'center' + }); + textContent.setStyle('verticalAlign', 'middle'); + } + } + // Handle status + var focus = itemModel.get(['emphasis', 'focus']); + var focusDataIndices = focus === 'relative' ? concatArray(node.getAncestorsIndices(), node.getDescendantIndices()) : focus === 'ancestor' ? node.getAncestorsIndices() : focus === 'descendant' ? node.getDescendantIndices() : null; + if (focusDataIndices) { + // Modify the focus to data indices. + getECData(symbolEl).focus = focusDataIndices; + } + drawEdge(seriesModel, node, virtualRoot, symbolEl, sourceOldLayout, sourceLayout, targetLayout, group); + if (symbolEl.__edge) { + symbolEl.onHoverStateChange = function (toState) { + if (toState !== 'blur') { + // NOTE: Ensure the parent elements will been blurred firstly. + // According to the return of getAncestorsIndices and getDescendantIndices + // TODO: A bit tricky. + var parentEl = node.parentNode && data.getItemGraphicEl(node.parentNode.dataIndex); + if (!(parentEl && parentEl.hoverState === HOVER_STATE_BLUR)) { + setStatesFlag(symbolEl.__edge, toState); + } + } + }; + } + } + function drawEdge(seriesModel, node, virtualRoot, symbolEl, sourceOldLayout, sourceLayout, targetLayout, group) { + var itemModel = node.getModel(); + var edgeShape = seriesModel.get('edgeShape'); + var layout = seriesModel.get('layout'); + var orient = seriesModel.getOrient(); + var curvature = seriesModel.get(['lineStyle', 'curveness']); + var edgeForkPosition = seriesModel.get('edgeForkPosition'); + var lineStyle = itemModel.getModel('lineStyle').getLineStyle(); + var edge = symbolEl.__edge; + // curve edge from node -> parent + // polyline edge from node -> children + if (edgeShape === 'curve') { + if (node.parentNode && node.parentNode !== virtualRoot) { + if (!edge) { + edge = symbolEl.__edge = new BezierCurve({ + shape: getEdgeShape(layout, orient, curvature, sourceOldLayout, sourceOldLayout) + }); + } + updateProps(edge, { + shape: getEdgeShape(layout, orient, curvature, sourceLayout, targetLayout) + }, seriesModel); + } + } else if (edgeShape === 'polyline') { + if (layout === 'orthogonal') { + if (node !== virtualRoot && node.children && node.children.length !== 0 && node.isExpand === true) { + var children = node.children; + var childPoints = []; + for (var i = 0; i < children.length; i++) { + var childLayout = children[i].getLayout(); + childPoints.push([childLayout.x, childLayout.y]); + } + if (!edge) { + edge = symbolEl.__edge = new TreePath({ + shape: { + parentPoint: [targetLayout.x, targetLayout.y], + childPoints: [[targetLayout.x, targetLayout.y]], + orient: orient, + forkPosition: edgeForkPosition + } + }); + } + updateProps(edge, { + shape: { + parentPoint: [targetLayout.x, targetLayout.y], + childPoints: childPoints + } + }, seriesModel); + } + } else { + if ("development" !== 'production') { + throw new Error('The polyline edgeShape can only be used in orthogonal layout'); + } + } + } + // show all edge when edgeShape is 'curve', filter node `isExpand` is false when edgeShape is 'polyline' + if (edge && !(edgeShape === 'polyline' && !node.isExpand)) { + edge.useStyle(defaults({ + strokeNoScale: true, + fill: null + }, lineStyle)); + setStatesStylesFromModel(edge, itemModel, 'lineStyle'); + setDefaultStateProxy(edge); + group.add(edge); + } + } + function removeNodeEdge(node, data, group, seriesModel, removeAnimationOpt) { + var virtualRoot = data.tree.root; + var _a = getSourceNode(virtualRoot, node), + source = _a.source, + sourceLayout = _a.sourceLayout; + var symbolEl = data.getItemGraphicEl(node.dataIndex); + if (!symbolEl) { + return; + } + var sourceSymbolEl = data.getItemGraphicEl(source.dataIndex); + var sourceEdge = sourceSymbolEl.__edge; + // 1. when expand the sub tree, delete the children node should delete the edge of + // the source at the same time. because the polyline edge shape is only owned by the source. + // 2.when the node is the only children of the source, delete the node should delete the edge of + // the source at the same time. the same reason as above. + var edge = symbolEl.__edge || (source.isExpand === false || source.children.length === 1 ? sourceEdge : undefined); + var edgeShape = seriesModel.get('edgeShape'); + var layoutOpt = seriesModel.get('layout'); + var orient = seriesModel.get('orient'); + var curvature = seriesModel.get(['lineStyle', 'curveness']); + if (edge) { + if (edgeShape === 'curve') { + removeElement(edge, { + shape: getEdgeShape(layoutOpt, orient, curvature, sourceLayout, sourceLayout), + style: { + opacity: 0 + } + }, seriesModel, { + cb: function () { + group.remove(edge); + }, + removeOpt: removeAnimationOpt + }); + } else if (edgeShape === 'polyline' && seriesModel.get('layout') === 'orthogonal') { + removeElement(edge, { + shape: { + parentPoint: [sourceLayout.x, sourceLayout.y], + childPoints: [[sourceLayout.x, sourceLayout.y]] + }, + style: { + opacity: 0 + } + }, seriesModel, { + cb: function () { + group.remove(edge); + }, + removeOpt: removeAnimationOpt + }); + } + } + } + function getSourceNode(virtualRoot, node) { + var source = node.parentNode === virtualRoot ? node : node.parentNode || node; + var sourceLayout; + while (sourceLayout = source.getLayout(), sourceLayout == null) { + source = source.parentNode === virtualRoot ? source : source.parentNode || source; + } + return { + source: source, + sourceLayout: sourceLayout + }; + } + function removeNode(data, dataIndex, symbolEl, group, seriesModel) { + var node = data.tree.getNodeByDataIndex(dataIndex); + var virtualRoot = data.tree.root; + var sourceLayout = getSourceNode(virtualRoot, node).sourceLayout; + // Use same duration and easing with update to have more consistent animation. + var removeAnimationOpt = { + duration: seriesModel.get('animationDurationUpdate'), + easing: seriesModel.get('animationEasingUpdate') + }; + removeElement(symbolEl, { + x: sourceLayout.x + 1, + y: sourceLayout.y + 1 + }, seriesModel, { + cb: function () { + group.remove(symbolEl); + data.setItemGraphicEl(dataIndex, null); + }, + removeOpt: removeAnimationOpt + }); + symbolEl.fadeOut(null, data.hostModel, { + fadeLabel: true, + animation: removeAnimationOpt + }); + // remove edge as parent node + node.children.forEach(function (childNode) { + removeNodeEdge(childNode, data, group, seriesModel, removeAnimationOpt); + }); + // remove edge as child node + removeNodeEdge(node, data, group, seriesModel, removeAnimationOpt); + } + function getEdgeShape(layoutOpt, orient, curvature, sourceLayout, targetLayout) { + var cpx1; + var cpy1; + var cpx2; + var cpy2; + var x1; + var x2; + var y1; + var y2; + if (layoutOpt === 'radial') { + x1 = sourceLayout.rawX; + y1 = sourceLayout.rawY; + x2 = targetLayout.rawX; + y2 = targetLayout.rawY; + var radialCoor1 = radialCoordinate(x1, y1); + var radialCoor2 = radialCoordinate(x1, y1 + (y2 - y1) * curvature); + var radialCoor3 = radialCoordinate(x2, y2 + (y1 - y2) * curvature); + var radialCoor4 = radialCoordinate(x2, y2); + return { + x1: radialCoor1.x || 0, + y1: radialCoor1.y || 0, + x2: radialCoor4.x || 0, + y2: radialCoor4.y || 0, + cpx1: radialCoor2.x || 0, + cpy1: radialCoor2.y || 0, + cpx2: radialCoor3.x || 0, + cpy2: radialCoor3.y || 0 + }; + } else { + x1 = sourceLayout.x; + y1 = sourceLayout.y; + x2 = targetLayout.x; + y2 = targetLayout.y; + if (orient === 'LR' || orient === 'RL') { + cpx1 = x1 + (x2 - x1) * curvature; + cpy1 = y1; + cpx2 = x2 + (x1 - x2) * curvature; + cpy2 = y2; + } + if (orient === 'TB' || orient === 'BT') { + cpx1 = x1; + cpy1 = y1 + (y2 - y1) * curvature; + cpx2 = x2; + cpy2 = y2 + (y1 - y2) * curvature; + } + } + return { + x1: x1, + y1: y1, + x2: x2, + y2: y2, + cpx1: cpx1, + cpy1: cpy1, + cpx2: cpx2, + cpy2: cpy2 + }; + } + + var inner$7 = makeInner(); + function linkSeriesData(opt) { + var mainData = opt.mainData; + var datas = opt.datas; + if (!datas) { + datas = { + main: mainData + }; + opt.datasAttr = { + main: 'data' + }; + } + opt.datas = opt.mainData = null; + linkAll(mainData, datas, opt); + // Porxy data original methods. + each(datas, function (data) { + each(mainData.TRANSFERABLE_METHODS, function (methodName) { + data.wrapMethod(methodName, curry(transferInjection, opt)); + }); + }); + // Beyond transfer, additional features should be added to `cloneShallow`. + mainData.wrapMethod('cloneShallow', curry(cloneShallowInjection, opt)); + // Only mainData trigger change, because struct.update may trigger + // another changable methods, which may bring about dead lock. + each(mainData.CHANGABLE_METHODS, function (methodName) { + mainData.wrapMethod(methodName, curry(changeInjection, opt)); + }); + // Make sure datas contains mainData. + assert(datas[mainData.dataType] === mainData); + } + function transferInjection(opt, res) { + if (isMainData(this)) { + // Transfer datas to new main data. + var datas = extend({}, inner$7(this).datas); + datas[this.dataType] = res; + linkAll(res, datas, opt); + } else { + // Modify the reference in main data to point newData. + linkSingle(res, this.dataType, inner$7(this).mainData, opt); + } + return res; + } + function changeInjection(opt, res) { + opt.struct && opt.struct.update(); + return res; + } + function cloneShallowInjection(opt, res) { + // cloneShallow, which brings about some fragilities, may be inappropriate + // to be exposed as an API. So for implementation simplicity we can make + // the restriction that cloneShallow of not-mainData should not be invoked + // outside, but only be invoked here. + each(inner$7(res).datas, function (data, dataType) { + data !== res && linkSingle(data.cloneShallow(), dataType, res, opt); + }); + return res; + } + /** + * Supplement method to List. + * + * @public + * @param [dataType] If not specified, return mainData. + */ + function getLinkedData(dataType) { + var mainData = inner$7(this).mainData; + return dataType == null || mainData == null ? mainData : inner$7(mainData).datas[dataType]; + } + /** + * Get list of all linked data + */ + function getLinkedDataAll() { + var mainData = inner$7(this).mainData; + return mainData == null ? [{ + data: mainData + }] : map(keys(inner$7(mainData).datas), function (type) { + return { + type: type, + data: inner$7(mainData).datas[type] + }; + }); + } + function isMainData(data) { + return inner$7(data).mainData === data; + } + function linkAll(mainData, datas, opt) { + inner$7(mainData).datas = {}; + each(datas, function (data, dataType) { + linkSingle(data, dataType, mainData, opt); + }); + } + function linkSingle(data, dataType, mainData, opt) { + inner$7(mainData).datas[dataType] = data; + inner$7(data).mainData = mainData; + data.dataType = dataType; + if (opt.struct) { + data[opt.structAttr] = opt.struct; + opt.struct[opt.datasAttr[dataType]] = data; + } + // Supplement method. + data.getLinkedData = getLinkedData; + data.getLinkedDataAll = getLinkedDataAll; + } + + var TreeNode = /** @class */function () { + function TreeNode(name, hostTree) { + this.depth = 0; + this.height = 0; + /** + * Reference to list item. + * Do not persistent dataIndex outside, + * besause it may be changed by list. + * If dataIndex -1, + * this node is logical deleted (filtered) in list. + */ + this.dataIndex = -1; + this.children = []; + this.viewChildren = []; + this.isExpand = false; + this.name = name || ''; + this.hostTree = hostTree; + } + /** + * The node is removed. + */ + TreeNode.prototype.isRemoved = function () { + return this.dataIndex < 0; + }; + TreeNode.prototype.eachNode = function (options, cb, context) { + if (isFunction(options)) { + context = cb; + cb = options; + options = null; + } + options = options || {}; + if (isString(options)) { + options = { + order: options + }; + } + var order = options.order || 'preorder'; + var children = this[options.attr || 'children']; + var suppressVisitSub; + order === 'preorder' && (suppressVisitSub = cb.call(context, this)); + for (var i = 0; !suppressVisitSub && i < children.length; i++) { + children[i].eachNode(options, cb, context); + } + order === 'postorder' && cb.call(context, this); + }; + /** + * Update depth and height of this subtree. + */ + TreeNode.prototype.updateDepthAndHeight = function (depth) { + var height = 0; + this.depth = depth; + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + child.updateDepthAndHeight(depth + 1); + if (child.height > height) { + height = child.height; + } + } + this.height = height + 1; + }; + TreeNode.prototype.getNodeById = function (id) { + if (this.getId() === id) { + return this; + } + for (var i = 0, children = this.children, len = children.length; i < len; i++) { + var res = children[i].getNodeById(id); + if (res) { + return res; + } + } + }; + TreeNode.prototype.contains = function (node) { + if (node === this) { + return true; + } + for (var i = 0, children = this.children, len = children.length; i < len; i++) { + var res = children[i].contains(node); + if (res) { + return res; + } + } + }; + /** + * @param includeSelf Default false. + * @return order: [root, child, grandchild, ...] + */ + TreeNode.prototype.getAncestors = function (includeSelf) { + var ancestors = []; + var node = includeSelf ? this : this.parentNode; + while (node) { + ancestors.push(node); + node = node.parentNode; + } + ancestors.reverse(); + return ancestors; + }; + TreeNode.prototype.getAncestorsIndices = function () { + var indices = []; + var currNode = this; + while (currNode) { + indices.push(currNode.dataIndex); + currNode = currNode.parentNode; + } + indices.reverse(); + return indices; + }; + TreeNode.prototype.getDescendantIndices = function () { + var indices = []; + this.eachNode(function (childNode) { + indices.push(childNode.dataIndex); + }); + return indices; + }; + TreeNode.prototype.getValue = function (dimension) { + var data = this.hostTree.data; + return data.getStore().get(data.getDimensionIndex(dimension || 'value'), this.dataIndex); + }; + TreeNode.prototype.setLayout = function (layout, merge) { + this.dataIndex >= 0 && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge); + }; + /** + * @return {Object} layout + */ + TreeNode.prototype.getLayout = function () { + return this.hostTree.data.getItemLayout(this.dataIndex); + }; + // @depcrecated + // getModel(path: S): Model + // eslint-disable-next-line @typescript-eslint/no-unused-vars + TreeNode.prototype.getModel = function (path) { + if (this.dataIndex < 0) { + return; + } + var hostTree = this.hostTree; + var itemModel = hostTree.data.getItemModel(this.dataIndex); + return itemModel.getModel(path); + }; + // TODO: TYPE More specific model + TreeNode.prototype.getLevelModel = function () { + return (this.hostTree.levelModels || [])[this.depth]; + }; + TreeNode.prototype.setVisual = function (key, value) { + this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, key, value); + }; + /** + * Get item visual + * FIXME: make return type better + */ + TreeNode.prototype.getVisual = function (key) { + return this.hostTree.data.getItemVisual(this.dataIndex, key); + }; + TreeNode.prototype.getRawIndex = function () { + return this.hostTree.data.getRawIndex(this.dataIndex); + }; + TreeNode.prototype.getId = function () { + return this.hostTree.data.getId(this.dataIndex); + }; + /** + * index in parent's children + */ + TreeNode.prototype.getChildIndex = function () { + if (this.parentNode) { + var children = this.parentNode.children; + for (var i = 0; i < children.length; ++i) { + if (children[i] === this) { + return i; + } + } + return -1; + } + return -1; + }; + /** + * if this is an ancestor of another node + * + * @param node another node + * @return if is ancestor + */ + TreeNode.prototype.isAncestorOf = function (node) { + var parent = node.parentNode; + while (parent) { + if (parent === this) { + return true; + } + parent = parent.parentNode; + } + return false; + }; + /** + * if this is an descendant of another node + * + * @param node another node + * @return if is descendant + */ + TreeNode.prototype.isDescendantOf = function (node) { + return node !== this && node.isAncestorOf(this); + }; + return TreeNode; + }(); + var Tree = /** @class */function () { + function Tree(hostModel) { + this.type = 'tree'; + this._nodes = []; + this.hostModel = hostModel; + } + Tree.prototype.eachNode = function (options, cb, context) { + this.root.eachNode(options, cb, context); + }; + Tree.prototype.getNodeByDataIndex = function (dataIndex) { + var rawIndex = this.data.getRawIndex(dataIndex); + return this._nodes[rawIndex]; + }; + Tree.prototype.getNodeById = function (name) { + return this.root.getNodeById(name); + }; + /** + * Update item available by list, + * when list has been performed options like 'filterSelf' or 'map'. + */ + Tree.prototype.update = function () { + var data = this.data; + var nodes = this._nodes; + for (var i = 0, len = nodes.length; i < len; i++) { + nodes[i].dataIndex = -1; + } + for (var i = 0, len = data.count(); i < len; i++) { + nodes[data.getRawIndex(i)].dataIndex = i; + } + }; + /** + * Clear all layouts + */ + Tree.prototype.clearLayouts = function () { + this.data.clearItemLayouts(); + }; + /** + * data node format: + * { + * name: ... + * value: ... + * children: [ + * { + * name: ... + * value: ... + * children: ... + * }, + * ... + * ] + * } + */ + Tree.createTree = function (dataRoot, hostModel, beforeLink) { + var tree = new Tree(hostModel); + var listData = []; + var dimMax = 1; + buildHierarchy(dataRoot); + function buildHierarchy(dataNode, parentNode) { + var value = dataNode.value; + dimMax = Math.max(dimMax, isArray(value) ? value.length : 1); + listData.push(dataNode); + var node = new TreeNode(convertOptionIdName(dataNode.name, ''), tree); + parentNode ? addChild(node, parentNode) : tree.root = node; + tree._nodes.push(node); + var children = dataNode.children; + if (children) { + for (var i = 0; i < children.length; i++) { + buildHierarchy(children[i], node); + } + } + } + tree.root.updateDepthAndHeight(0); + var dimensions = prepareSeriesDataSchema(listData, { + coordDimensions: ['value'], + dimensionsCount: dimMax + }).dimensions; + var list = new SeriesData(dimensions, hostModel); + list.initData(listData); + beforeLink && beforeLink(list); + linkSeriesData({ + mainData: list, + struct: tree, + structAttr: 'tree' + }); + tree.update(); + return tree; + }; + return Tree; + }(); + /** + * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote, + * so this function is not ready and not necessary to be public. + */ + function addChild(child, node) { + var children = node.children; + if (child.parentNode === node) { + return; + } + children.push(child); + child.parentNode = node; + } + + function retrieveTargetInfo(payload, validPayloadTypes, seriesModel) { + if (payload && indexOf(validPayloadTypes, payload.type) >= 0) { + var root = seriesModel.getData().tree.root; + var targetNode = payload.targetNode; + if (isString(targetNode)) { + targetNode = root.getNodeById(targetNode); + } + if (targetNode && root.contains(targetNode)) { + return { + node: targetNode + }; + } + var targetNodeId = payload.targetNodeId; + if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) { + return { + node: targetNode + }; + } + } + } + // Not includes the given node at the last item. + function getPathToRoot(node) { + var path = []; + while (node) { + node = node.parentNode; + node && path.push(node); + } + return path.reverse(); + } + function aboveViewRoot(viewRoot, node) { + var viewPath = getPathToRoot(viewRoot); + return indexOf(viewPath, node) >= 0; + } + // From root to the input node (the input node will be included). + function wrapTreePathInfo(node, seriesModel) { + var treePathInfo = []; + while (node) { + var nodeDataIndex = node.dataIndex; + treePathInfo.push({ + name: node.name, + dataIndex: nodeDataIndex, + value: seriesModel.getRawValue(nodeDataIndex) + }); + node = node.parentNode; + } + treePathInfo.reverse(); + return treePathInfo; + } + + var TreeSeriesModel = /** @class */function (_super) { + __extends(TreeSeriesModel, _super); + function TreeSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.hasSymbolVisual = true; + // Do it self. + _this.ignoreStyleOnData = true; + return _this; + } + /** + * Init a tree data structure from data in option series + */ + TreeSeriesModel.prototype.getInitialData = function (option) { + // create a virtual root + var root = { + name: option.name, + children: option.data + }; + var leaves = option.leaves || {}; + var leavesModel = new Model(leaves, this, this.ecModel); + var tree = Tree.createTree(root, this, beforeLink); + function beforeLink(nodeData) { + nodeData.wrapMethod('getItemModel', function (model, idx) { + var node = tree.getNodeByDataIndex(idx); + if (!(node && node.children.length && node.isExpand)) { + model.parentModel = leavesModel; + } + return model; + }); + } + var treeDepth = 0; + tree.eachNode('preorder', function (node) { + if (node.depth > treeDepth) { + treeDepth = node.depth; + } + }); + var expandAndCollapse = option.expandAndCollapse; + var expandTreeDepth = expandAndCollapse && option.initialTreeDepth >= 0 ? option.initialTreeDepth : treeDepth; + tree.root.eachNode('preorder', function (node) { + var item = node.hostTree.data.getRawDataItem(node.dataIndex); + // Add item.collapsed != null, because users can collapse node original in the series.data. + node.isExpand = item && item.collapsed != null ? !item.collapsed : node.depth <= expandTreeDepth; + }); + return tree.data; + }; + /** + * Make the configuration 'orient' backward compatibly, with 'horizontal = LR', 'vertical = TB'. + * @returns {string} orient + */ + TreeSeriesModel.prototype.getOrient = function () { + var orient = this.get('orient'); + if (orient === 'horizontal') { + orient = 'LR'; + } else if (orient === 'vertical') { + orient = 'TB'; + } + return orient; + }; + TreeSeriesModel.prototype.setZoom = function (zoom) { + this.option.zoom = zoom; + }; + TreeSeriesModel.prototype.setCenter = function (center) { + this.option.center = center; + }; + TreeSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + var tree = this.getData().tree; + var realRoot = tree.root.children[0]; + var node = tree.getNodeByDataIndex(dataIndex); + var value = node.getValue(); + var name = node.name; + while (node && node !== realRoot) { + name = node.parentNode.name + '.' + name; + node = node.parentNode; + } + return createTooltipMarkup('nameValue', { + name: name, + value: value, + noValue: isNaN(value) || value == null + }); + }; + // Add tree path to tooltip param + TreeSeriesModel.prototype.getDataParams = function (dataIndex) { + var params = _super.prototype.getDataParams.apply(this, arguments); + var node = this.getData().tree.getNodeByDataIndex(dataIndex); + params.treeAncestors = wrapTreePathInfo(node, this); + params.collapsed = !node.isExpand; + return params; + }; + TreeSeriesModel.type = 'series.tree'; + // can support the position parameters 'left', 'top','right','bottom', 'width', + // 'height' in the setOption() with 'merge' mode normal. + TreeSeriesModel.layoutMode = 'box'; + TreeSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'view', + // the position of the whole view + left: '12%', + top: '12%', + right: '12%', + bottom: '12%', + // the layout of the tree, two value can be selected, 'orthogonal' or 'radial' + layout: 'orthogonal', + // value can be 'polyline' + edgeShape: 'curve', + edgeForkPosition: '50%', + // true | false | 'move' | 'scale', see module:component/helper/RoamController. + roam: false, + // Symbol size scale ratio in roam + nodeScaleRatio: 0.4, + // Default on center of graph + center: null, + zoom: 1, + orient: 'LR', + symbol: 'emptyCircle', + symbolSize: 7, + expandAndCollapse: true, + initialTreeDepth: 2, + lineStyle: { + color: '#ccc', + width: 1.5, + curveness: 0.5 + }, + itemStyle: { + color: 'lightsteelblue', + // borderColor: '#c23531', + borderWidth: 1.5 + }, + label: { + show: true + }, + animationEasing: 'linear', + animationDuration: 700, + animationDurationUpdate: 500 + }; + return TreeSeriesModel; + }(SeriesModel); + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /** + * Traverse the tree from bottom to top and do something + */ + function eachAfter(root, callback, separation) { + var nodes = [root]; + var next = []; + var node; + while (node = nodes.pop()) { + // jshint ignore:line + next.push(node); + if (node.isExpand) { + var children = node.children; + if (children.length) { + for (var i = 0; i < children.length; i++) { + nodes.push(children[i]); + } + } + } + } + while (node = next.pop()) { + // jshint ignore:line + callback(node, separation); + } + } + /** + * Traverse the tree from top to bottom and do something + */ + function eachBefore(root, callback) { + var nodes = [root]; + var node; + while (node = nodes.pop()) { + // jshint ignore:line + callback(node); + if (node.isExpand) { + var children = node.children; + if (children.length) { + for (var i = children.length - 1; i >= 0; i--) { + nodes.push(children[i]); + } + } + } + } + } + + function treeLayout(ecModel, api) { + ecModel.eachSeriesByType('tree', function (seriesModel) { + commonLayout(seriesModel, api); + }); + } + function commonLayout(seriesModel, api) { + var layoutInfo = getViewRect$1(seriesModel, api); + seriesModel.layoutInfo = layoutInfo; + var layout = seriesModel.get('layout'); + var width = 0; + var height = 0; + var separation$1 = null; + if (layout === 'radial') { + width = 2 * Math.PI; + height = Math.min(layoutInfo.height, layoutInfo.width) / 2; + separation$1 = separation(function (node1, node2) { + return (node1.parentNode === node2.parentNode ? 1 : 2) / node1.depth; + }); + } else { + width = layoutInfo.width; + height = layoutInfo.height; + separation$1 = separation(); + } + var virtualRoot = seriesModel.getData().tree.root; + var realRoot = virtualRoot.children[0]; + if (realRoot) { + init$2(virtualRoot); + eachAfter(realRoot, firstWalk, separation$1); + virtualRoot.hierNode.modifier = -realRoot.hierNode.prelim; + eachBefore(realRoot, secondWalk); + var left_1 = realRoot; + var right_1 = realRoot; + var bottom_1 = realRoot; + eachBefore(realRoot, function (node) { + var x = node.getLayout().x; + if (x < left_1.getLayout().x) { + left_1 = node; + } + if (x > right_1.getLayout().x) { + right_1 = node; + } + if (node.depth > bottom_1.depth) { + bottom_1 = node; + } + }); + var delta = left_1 === right_1 ? 1 : separation$1(left_1, right_1) / 2; + var tx_1 = delta - left_1.getLayout().x; + var kx_1 = 0; + var ky_1 = 0; + var coorX_1 = 0; + var coorY_1 = 0; + if (layout === 'radial') { + kx_1 = width / (right_1.getLayout().x + delta + tx_1); + // here we use (node.depth - 1), bucause the real root's depth is 1 + ky_1 = height / (bottom_1.depth - 1 || 1); + eachBefore(realRoot, function (node) { + coorX_1 = (node.getLayout().x + tx_1) * kx_1; + coorY_1 = (node.depth - 1) * ky_1; + var finalCoor = radialCoordinate(coorX_1, coorY_1); + node.setLayout({ + x: finalCoor.x, + y: finalCoor.y, + rawX: coorX_1, + rawY: coorY_1 + }, true); + }); + } else { + var orient_1 = seriesModel.getOrient(); + if (orient_1 === 'RL' || orient_1 === 'LR') { + ky_1 = height / (right_1.getLayout().x + delta + tx_1); + kx_1 = width / (bottom_1.depth - 1 || 1); + eachBefore(realRoot, function (node) { + coorY_1 = (node.getLayout().x + tx_1) * ky_1; + coorX_1 = orient_1 === 'LR' ? (node.depth - 1) * kx_1 : width - (node.depth - 1) * kx_1; + node.setLayout({ + x: coorX_1, + y: coorY_1 + }, true); + }); + } else if (orient_1 === 'TB' || orient_1 === 'BT') { + kx_1 = width / (right_1.getLayout().x + delta + tx_1); + ky_1 = height / (bottom_1.depth - 1 || 1); + eachBefore(realRoot, function (node) { + coorX_1 = (node.getLayout().x + tx_1) * kx_1; + coorY_1 = orient_1 === 'TB' ? (node.depth - 1) * ky_1 : height - (node.depth - 1) * ky_1; + node.setLayout({ + x: coorX_1, + y: coorY_1 + }, true); + }); + } + } + } + } + + function treeVisual(ecModel) { + ecModel.eachSeriesByType('tree', function (seriesModel) { + var data = seriesModel.getData(); + var tree = data.tree; + tree.eachNode(function (node) { + var model = node.getModel(); + // TODO Optimize + var style = model.getModel('itemStyle').getItemStyle(); + var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style'); + extend(existsStyle, style); + }); + }); + } + + function installTreeAction(registers) { + registers.registerAction({ + type: 'treeExpandAndCollapse', + event: 'treeExpandAndCollapse', + update: 'update' + }, function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'series', + subType: 'tree', + query: payload + }, function (seriesModel) { + var dataIndex = payload.dataIndex; + var tree = seriesModel.getData().tree; + var node = tree.getNodeByDataIndex(dataIndex); + node.isExpand = !node.isExpand; + }); + }); + registers.registerAction({ + type: 'treeRoam', + event: 'treeRoam', + // Here we set 'none' instead of 'update', because roam action + // just need to update the transform matrix without having to recalculate + // the layout. So don't need to go through the whole update process, such + // as 'dataPrcocess', 'coordSystemUpdate', 'layout' and so on. + update: 'none' + }, function (payload, ecModel, api) { + ecModel.eachComponent({ + mainType: 'series', + subType: 'tree', + query: payload + }, function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var res = updateCenterAndZoom(coordSys, payload, undefined, api); + seriesModel.setCenter && seriesModel.setCenter(res.center); + seriesModel.setZoom && seriesModel.setZoom(res.zoom); + }); + }); + } + + function install$b(registers) { + registers.registerChartView(TreeView); + registers.registerSeriesModel(TreeSeriesModel); + registers.registerLayout(treeLayout); + registers.registerVisual(treeVisual); + installTreeAction(registers); + } + + var actionTypes = ['treemapZoomToNode', 'treemapRender', 'treemapMove']; + function installTreemapAction(registers) { + for (var i = 0; i < actionTypes.length; i++) { + registers.registerAction({ + type: actionTypes[i], + update: 'updateView' + }, noop); + } + registers.registerAction({ + type: 'treemapRootToNode', + update: 'updateView' + }, function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'series', + subType: 'treemap', + query: payload + }, handleRootToNode); + function handleRootToNode(model, index) { + var types = ['treemapZoomToNode', 'treemapRootToNode']; + var targetInfo = retrieveTargetInfo(payload, types, model); + if (targetInfo) { + var originViewRoot = model.getViewRoot(); + if (originViewRoot) { + payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) ? 'rollUp' : 'drillDown'; + } + model.resetViewRoot(targetInfo.node); + } + } + }); + } + + function enableAriaDecalForTree(seriesModel) { + var data = seriesModel.getData(); + var tree = data.tree; + var decalPaletteScope = {}; + tree.eachNode(function (node) { + // Use decal of level 1 node + var current = node; + while (current && current.depth > 1) { + current = current.parentNode; + } + var decal = getDecalFromPalette(seriesModel.ecModel, current.name || current.dataIndex + '', decalPaletteScope); + node.setVisual('decal', decal); + }); + } + + var TreemapSeriesModel = /** @class */function (_super) { + __extends(TreemapSeriesModel, _super); + function TreemapSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TreemapSeriesModel.type; + _this.preventUsingHoverLayer = true; + return _this; + } + /** + * @override + */ + TreemapSeriesModel.prototype.getInitialData = function (option, ecModel) { + // Create a virtual root. + var root = { + name: option.name, + children: option.data + }; + completeTreeValue(root); + var levels = option.levels || []; + // Used in "visual priority" in `treemapVisual.js`. + // This way is a little tricky, must satisfy the precondition: + // 1. There is no `treeNode.getModel('itemStyle.xxx')` used. + // 2. The `Model.prototype.getModel()` will not use any clone-like way. + var designatedVisualItemStyle = this.designatedVisualItemStyle = {}; + var designatedVisualModel = new Model({ + itemStyle: designatedVisualItemStyle + }, this, ecModel); + levels = option.levels = setDefault(levels, ecModel); + var levelModels = map(levels || [], function (levelDefine) { + return new Model(levelDefine, designatedVisualModel, ecModel); + }, this); + // Make sure always a new tree is created when setOption, + // in TreemapView, we check whether oldTree === newTree + // to choose mappings approach among old shapes and new shapes. + var tree = Tree.createTree(root, this, beforeLink); + function beforeLink(nodeData) { + nodeData.wrapMethod('getItemModel', function (model, idx) { + var node = tree.getNodeByDataIndex(idx); + var levelModel = node ? levelModels[node.depth] : null; + // If no levelModel, we also need `designatedVisualModel`. + model.parentModel = levelModel || designatedVisualModel; + return model; + }); + } + return tree.data; + }; + TreemapSeriesModel.prototype.optionUpdated = function () { + this.resetViewRoot(); + }; + /** + * @override + * @param {number} dataIndex + * @param {boolean} [mutipleSeries=false] + */ + TreemapSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var name = data.getName(dataIndex); + return createTooltipMarkup('nameValue', { + name: name, + value: value + }); + }; + /** + * Add tree path to tooltip param + * + * @override + * @param {number} dataIndex + * @return {Object} + */ + TreemapSeriesModel.prototype.getDataParams = function (dataIndex) { + var params = _super.prototype.getDataParams.apply(this, arguments); + var node = this.getData().tree.getNodeByDataIndex(dataIndex); + params.treeAncestors = wrapTreePathInfo(node, this); + // compatitable the previous code. + params.treePathInfo = params.treeAncestors; + return params; + }; + /** + * @public + * @param {Object} layoutInfo { + * x: containerGroup x + * y: containerGroup y + * width: containerGroup width + * height: containerGroup height + * } + */ + TreemapSeriesModel.prototype.setLayoutInfo = function (layoutInfo) { + /** + * @readOnly + * @type {Object} + */ + this.layoutInfo = this.layoutInfo || {}; + extend(this.layoutInfo, layoutInfo); + }; + /** + * @param {string} id + * @return {number} index + */ + TreemapSeriesModel.prototype.mapIdToIndex = function (id) { + // A feature is implemented: + // index is monotone increasing with the sequence of + // input id at the first time. + // This feature can make sure that each data item and its + // mapped color have the same index between data list and + // color list at the beginning, which is useful for user + // to adjust data-color mapping. + /** + * @private + * @type {Object} + */ + var idIndexMap = this._idIndexMap; + if (!idIndexMap) { + idIndexMap = this._idIndexMap = createHashMap(); + /** + * @private + * @type {number} + */ + this._idIndexMapCount = 0; + } + var index = idIndexMap.get(id); + if (index == null) { + idIndexMap.set(id, index = this._idIndexMapCount++); + } + return index; + }; + TreemapSeriesModel.prototype.getViewRoot = function () { + return this._viewRoot; + }; + TreemapSeriesModel.prototype.resetViewRoot = function (viewRoot) { + viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot; + var root = this.getRawData().tree.root; + if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) { + this._viewRoot = root; + } + }; + TreemapSeriesModel.prototype.enableAriaDecal = function () { + enableAriaDecalForTree(this); + }; + TreemapSeriesModel.type = 'series.treemap'; + TreemapSeriesModel.layoutMode = 'box'; + TreemapSeriesModel.defaultOption = { + // Disable progressive rendering + progressive: 0, + // size: ['80%', '80%'], // deprecated, compatible with ec2. + left: 'center', + top: 'middle', + width: '80%', + height: '80%', + sort: true, + clipWindow: 'origin', + squareRatio: 0.5 * (1 + Math.sqrt(5)), + leafDepth: null, + drillDownIcon: '▶', + // to align specialized icon. ▷▶❒❐▼✚ + zoomToNodeRatio: 0.32 * 0.32, + roam: true, + nodeClick: 'zoomToNode', + animation: true, + animationDurationUpdate: 900, + animationEasing: 'quinticInOut', + breadcrumb: { + show: true, + height: 22, + left: 'center', + top: 'bottom', + // right + // bottom + emptyItemWidth: 25, + itemStyle: { + color: 'rgba(0,0,0,0.7)', + textStyle: { + color: '#fff' + } + }, + emphasis: { + itemStyle: { + color: 'rgba(0,0,0,0.9)' // '#5793f3', + } + } + }, + + label: { + show: true, + // Do not use textDistance, for ellipsis rect just the same as treemap node rect. + distance: 0, + padding: 5, + position: 'inside', + // formatter: null, + color: '#fff', + overflow: 'truncate' + // align + // verticalAlign + }, + + upperLabel: { + show: false, + position: [0, '50%'], + height: 20, + // formatter: null, + // color: '#fff', + overflow: 'truncate', + // align: null, + verticalAlign: 'middle' + }, + itemStyle: { + color: null, + colorAlpha: null, + colorSaturation: null, + borderWidth: 0, + gapWidth: 0, + borderColor: '#fff', + borderColorSaturation: null // If specified, borderColor will be ineffective, and the + // border color is evaluated by color of current node and + // borderColorSaturation. + }, + + emphasis: { + upperLabel: { + show: true, + position: [0, '50%'], + overflow: 'truncate', + verticalAlign: 'middle' + } + }, + visualDimension: 0, + visualMin: null, + visualMax: null, + color: [], + // level[n].color (if necessary). + // + Specify color list of each level. level[0].color would be global + // color list if not specified. (see method `setDefault`). + // + But set as a empty array to forbid fetch color from global palette + // when using nodeModel.get('color'), otherwise nodes on deep level + // will always has color palette set and are not able to inherit color + // from parent node. + // + TreemapSeries.color can not be set as 'none', otherwise effect + // legend color fetching (see seriesColor.js). + colorAlpha: null, + colorSaturation: null, + colorMappingBy: 'index', + visibleMin: 10, + // be rendered. Only works when sort is 'asc' or 'desc'. + childrenVisibleMin: null, + // grandchildren will not show. + // Why grandchildren? If not grandchildren but children, + // some siblings show children and some not, + // the appearance may be mess and not consistent, + levels: [] // Each item: { + // visibleMin, itemStyle, visualDimension, label + // } + }; + + return TreemapSeriesModel; + }(SeriesModel); + /** + * @param {Object} dataNode + */ + function completeTreeValue(dataNode) { + // Postorder travel tree. + // If value of none-leaf node is not set, + // calculate it by suming up the value of all children. + var sum = 0; + each(dataNode.children, function (child) { + completeTreeValue(child); + var childValue = child.value; + isArray(childValue) && (childValue = childValue[0]); + sum += childValue; + }); + var thisValue = dataNode.value; + if (isArray(thisValue)) { + thisValue = thisValue[0]; + } + if (thisValue == null || isNaN(thisValue)) { + thisValue = sum; + } + // Value should not less than 0. + if (thisValue < 0) { + thisValue = 0; + } + isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue; + } + /** + * set default to level configuration + */ + function setDefault(levels, ecModel) { + var globalColorList = normalizeToArray(ecModel.get('color')); + var globalDecalList = normalizeToArray(ecModel.get(['aria', 'decal', 'decals'])); + if (!globalColorList) { + return; + } + levels = levels || []; + var hasColorDefine; + var hasDecalDefine; + each(levels, function (levelDefine) { + var model = new Model(levelDefine); + var modelColor = model.get('color'); + var modelDecal = model.get('decal'); + if (model.get(['itemStyle', 'color']) || modelColor && modelColor !== 'none') { + hasColorDefine = true; + } + if (model.get(['itemStyle', 'decal']) || modelDecal && modelDecal !== 'none') { + hasDecalDefine = true; + } + }); + var level0 = levels[0] || (levels[0] = {}); + if (!hasColorDefine) { + level0.color = globalColorList.slice(); + } + if (!hasDecalDefine && globalDecalList) { + level0.decal = globalDecalList.slice(); + } + return levels; + } + + var TEXT_PADDING = 8; + var ITEM_GAP = 8; + var ARRAY_LENGTH = 5; + var Breadcrumb = /** @class */function () { + function Breadcrumb(containerGroup) { + this.group = new Group(); + containerGroup.add(this.group); + } + Breadcrumb.prototype.render = function (seriesModel, api, targetNode, onSelect) { + var model = seriesModel.getModel('breadcrumb'); + var thisGroup = this.group; + thisGroup.removeAll(); + if (!model.get('show') || !targetNode) { + return; + } + var normalStyleModel = model.getModel('itemStyle'); + var emphasisModel = model.getModel('emphasis'); + var textStyleModel = normalStyleModel.getModel('textStyle'); + var emphasisTextStyleModel = emphasisModel.getModel(['itemStyle', 'textStyle']); + var layoutParam = { + pos: { + left: model.get('left'), + right: model.get('right'), + top: model.get('top'), + bottom: model.get('bottom') + }, + box: { + width: api.getWidth(), + height: api.getHeight() + }, + emptyItemWidth: model.get('emptyItemWidth'), + totalWidth: 0, + renderList: [] + }; + this._prepare(targetNode, layoutParam, textStyleModel); + this._renderContent(seriesModel, layoutParam, normalStyleModel, emphasisModel, textStyleModel, emphasisTextStyleModel, onSelect); + positionElement(thisGroup, layoutParam.pos, layoutParam.box); + }; + /** + * Prepare render list and total width + * @private + */ + Breadcrumb.prototype._prepare = function (targetNode, layoutParam, textStyleModel) { + for (var node = targetNode; node; node = node.parentNode) { + var text = convertOptionIdName(node.getModel().get('name'), ''); + var textRect = textStyleModel.getTextRect(text); + var itemWidth = Math.max(textRect.width + TEXT_PADDING * 2, layoutParam.emptyItemWidth); + layoutParam.totalWidth += itemWidth + ITEM_GAP; + layoutParam.renderList.push({ + node: node, + text: text, + width: itemWidth + }); + } + }; + /** + * @private + */ + Breadcrumb.prototype._renderContent = function (seriesModel, layoutParam, normalStyleModel, emphasisModel, textStyleModel, emphasisTextStyleModel, onSelect) { + // Start rendering. + var lastX = 0; + var emptyItemWidth = layoutParam.emptyItemWidth; + var height = seriesModel.get(['breadcrumb', 'height']); + var availableSize = getAvailableSize(layoutParam.pos, layoutParam.box); + var totalWidth = layoutParam.totalWidth; + var renderList = layoutParam.renderList; + var emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle(); + for (var i = renderList.length - 1; i >= 0; i--) { + var item = renderList[i]; + var itemNode = item.node; + var itemWidth = item.width; + var text = item.text; + // Hdie text and shorten width if necessary. + if (totalWidth > availableSize.width) { + totalWidth -= itemWidth - emptyItemWidth; + itemWidth = emptyItemWidth; + text = null; + } + var el = new Polygon({ + shape: { + points: makeItemPoints(lastX, 0, itemWidth, height, i === renderList.length - 1, i === 0) + }, + style: defaults(normalStyleModel.getItemStyle(), { + lineJoin: 'bevel' + }), + textContent: new ZRText({ + style: createTextStyle(textStyleModel, { + text: text + }) + }), + textConfig: { + position: 'inside' + }, + z2: Z2_EMPHASIS_LIFT * 1e4, + onclick: curry(onSelect, itemNode) + }); + el.disableLabelAnimation = true; + el.getTextContent().ensureState('emphasis').style = createTextStyle(emphasisTextStyleModel, { + text: text + }); + el.ensureState('emphasis').style = emphasisItemStyle; + toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + this.group.add(el); + packEventData(el, seriesModel, itemNode); + lastX += itemWidth + ITEM_GAP; + } + }; + Breadcrumb.prototype.remove = function () { + this.group.removeAll(); + }; + return Breadcrumb; + }(); + function makeItemPoints(x, y, itemWidth, itemHeight, head, tail) { + var points = [[head ? x : x - ARRAY_LENGTH, y], [x + itemWidth, y], [x + itemWidth, y + itemHeight], [head ? x : x - ARRAY_LENGTH, y + itemHeight]]; + !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]); + !head && points.push([x, y + itemHeight / 2]); + return points; + } + // Package custom mouse event. + function packEventData(el, seriesModel, itemNode) { + getECData(el).eventData = { + componentType: 'series', + componentSubType: 'treemap', + componentIndex: seriesModel.componentIndex, + seriesIndex: seriesModel.seriesIndex, + seriesName: seriesModel.name, + seriesType: 'treemap', + selfType: 'breadcrumb', + nodeData: { + dataIndex: itemNode && itemNode.dataIndex, + name: itemNode && itemNode.name + }, + treePathInfo: itemNode && wrapTreePathInfo(itemNode, seriesModel) + }; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /** + * Animate multiple elements with a single done-callback. + * + * @example + * animation + * .createWrap() + * .add(el1, {x: 10, y: 10}) + * .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400) + * .done(function () { // done }) + * .start('cubicOut'); + */ + var AnimationWrap = /** @class */function () { + function AnimationWrap() { + this._storage = []; + this._elExistsMap = {}; + } + /** + * Caution: a el can only be added once, otherwise 'done' + * might not be called. This method checks this (by el.id), + * suppresses adding and returns false when existing el found. + * + * @return Whether adding succeeded. + */ + AnimationWrap.prototype.add = function (el, target, duration, delay, easing) { + if (this._elExistsMap[el.id]) { + return false; + } + this._elExistsMap[el.id] = true; + this._storage.push({ + el: el, + target: target, + duration: duration, + delay: delay, + easing: easing + }); + return true; + }; + /** + * Only execute when animation done/aborted. + */ + AnimationWrap.prototype.finished = function (callback) { + this._finishedCallback = callback; + return this; + }; + /** + * Will stop exist animation firstly. + */ + AnimationWrap.prototype.start = function () { + var _this = this; + var count = this._storage.length; + var checkTerminate = function () { + count--; + if (count <= 0) { + // Guard. + _this._storage.length = 0; + _this._elExistsMap = {}; + _this._finishedCallback && _this._finishedCallback(); + } + }; + for (var i = 0, len = this._storage.length; i < len; i++) { + var item = this._storage[i]; + item.el.animateTo(item.target, { + duration: item.duration, + delay: item.delay, + easing: item.easing, + setToFinal: true, + done: checkTerminate, + aborted: checkTerminate + }); + } + return this; + }; + return AnimationWrap; + }(); + function createWrap() { + return new AnimationWrap(); + } + + var Group$1 = Group; + var Rect$1 = Rect; + var DRAG_THRESHOLD = 3; + var PATH_LABEL_NOAMAL = 'label'; + var PATH_UPPERLABEL_NORMAL = 'upperLabel'; + // Should larger than emphasis states lift z + var Z2_BASE = Z2_EMPHASIS_LIFT * 10; // Should bigger than every z2. + var Z2_BG = Z2_EMPHASIS_LIFT * 2; + var Z2_CONTENT = Z2_EMPHASIS_LIFT * 3; + var getStateItemStyle = makeStyleMapper([['fill', 'color'], + // `borderColor` and `borderWidth` has been occupied, + // so use `stroke` to indicate the stroke of the rect. + ['stroke', 'strokeColor'], ['lineWidth', 'strokeWidth'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'] + // Option decal is in `DecalObject` but style.decal is in `PatternObject`. + // So do not transfer decal directly. + ]); + + var getItemStyleNormal = function (model) { + // Normal style props should include emphasis style props. + var itemStyle = getStateItemStyle(model); + // Clear styles set by emphasis. + itemStyle.stroke = itemStyle.fill = itemStyle.lineWidth = null; + return itemStyle; + }; + var inner$8 = makeInner(); + var TreemapView = /** @class */function (_super) { + __extends(TreemapView, _super); + function TreemapView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TreemapView.type; + _this._state = 'ready'; + _this._storage = createStorage(); + return _this; + } + /** + * @override + */ + TreemapView.prototype.render = function (seriesModel, ecModel, api, payload) { + var models = ecModel.findComponents({ + mainType: 'series', + subType: 'treemap', + query: payload + }); + if (indexOf(models, seriesModel) < 0) { + return; + } + this.seriesModel = seriesModel; + this.api = api; + this.ecModel = ecModel; + var types = ['treemapZoomToNode', 'treemapRootToNode']; + var targetInfo = retrieveTargetInfo(payload, types, seriesModel); + var payloadType = payload && payload.type; + var layoutInfo = seriesModel.layoutInfo; + var isInit = !this._oldTree; + var thisStorage = this._storage; + // Mark new root when action is treemapRootToNode. + var reRoot = payloadType === 'treemapRootToNode' && targetInfo && thisStorage ? { + rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()], + direction: payload.direction + } : null; + var containerGroup = this._giveContainerGroup(layoutInfo); + var hasAnimation = seriesModel.get('animation'); + var renderResult = this._doRender(containerGroup, seriesModel, reRoot); + hasAnimation && !isInit && (!payloadType || payloadType === 'treemapZoomToNode' || payloadType === 'treemapRootToNode') ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot) : renderResult.renderFinally(); + this._resetController(api); + this._renderBreadcrumb(seriesModel, api, targetInfo); + }; + TreemapView.prototype._giveContainerGroup = function (layoutInfo) { + var containerGroup = this._containerGroup; + if (!containerGroup) { + // FIXME + // 加一层containerGroup是为了clip,但是现在clip功能并没有实现。 + containerGroup = this._containerGroup = new Group$1(); + this._initEvents(containerGroup); + this.group.add(containerGroup); + } + containerGroup.x = layoutInfo.x; + containerGroup.y = layoutInfo.y; + return containerGroup; + }; + TreemapView.prototype._doRender = function (containerGroup, seriesModel, reRoot) { + var thisTree = seriesModel.getData().tree; + var oldTree = this._oldTree; + // Clear last shape records. + var lastsForAnimation = createStorage(); + var thisStorage = createStorage(); + var oldStorage = this._storage; + var willInvisibleEls = []; + function doRenderNode(thisNode, oldNode, parentGroup, depth) { + return renderNode(seriesModel, thisStorage, oldStorage, reRoot, lastsForAnimation, willInvisibleEls, thisNode, oldNode, parentGroup, depth); + } + // Notice: When thisTree and oldTree are the same tree (see list.cloneShallow), + // the oldTree is actually losted, so we cannot find all of the old graphic + // elements from tree. So we use this strategy: make element storage, move + // from old storage to new storage, clear old storage. + dualTravel(thisTree.root ? [thisTree.root] : [], oldTree && oldTree.root ? [oldTree.root] : [], containerGroup, thisTree === oldTree || !oldTree, 0); + // Process all removing. + var willDeleteEls = clearStorage(oldStorage); + this._oldTree = thisTree; + this._storage = thisStorage; + return { + lastsForAnimation: lastsForAnimation, + willDeleteEls: willDeleteEls, + renderFinally: renderFinally + }; + function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) { + // When 'render' is triggered by action, + // 'this' and 'old' may be the same tree, + // we use rawIndex in that case. + if (sameTree) { + oldViewChildren = thisViewChildren; + each(thisViewChildren, function (child, index) { + !child.isRemoved() && processNode(index, index); + }); + } + // Diff hierarchically (diff only in each subtree, but not whole). + // because, consistency of view is important. + else { + new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey).add(processNode).update(processNode).remove(curry(processNode, null)).execute(); + } + function getKey(node) { + // Identify by name or raw index. + return node.getId(); + } + function processNode(newIndex, oldIndex) { + var thisNode = newIndex != null ? thisViewChildren[newIndex] : null; + var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null; + var group = doRenderNode(thisNode, oldNode, parentGroup, depth); + group && dualTravel(thisNode && thisNode.viewChildren || [], oldNode && oldNode.viewChildren || [], group, sameTree, depth + 1); + } + } + function clearStorage(storage) { + var willDeleteEls = createStorage(); + storage && each(storage, function (store, storageName) { + var delEls = willDeleteEls[storageName]; + each(store, function (el) { + el && (delEls.push(el), inner$8(el).willDelete = true); + }); + }); + return willDeleteEls; + } + function renderFinally() { + each(willDeleteEls, function (els) { + each(els, function (el) { + el.parent && el.parent.remove(el); + }); + }); + each(willInvisibleEls, function (el) { + el.invisible = true; + // Setting invisible is for optimizing, so no need to set dirty, + // just mark as invisible. + el.dirty(); + }); + } + }; + TreemapView.prototype._doAnimation = function (containerGroup, renderResult, seriesModel, reRoot) { + var durationOption = seriesModel.get('animationDurationUpdate'); + var easingOption = seriesModel.get('animationEasing'); + // TODO: do not support function until necessary. + var duration = (isFunction(durationOption) ? 0 : durationOption) || 0; + var easing = (isFunction(easingOption) ? null : easingOption) || 'cubicOut'; + var animationWrap = createWrap(); + // Make delete animations. + each(renderResult.willDeleteEls, function (store, storageName) { + each(store, function (el, rawIndex) { + if (el.invisible) { + return; + } + var parent = el.parent; // Always has parent, and parent is nodeGroup. + var target; + var innerStore = inner$8(parent); + if (reRoot && reRoot.direction === 'drillDown') { + target = parent === reRoot.rootNodeGroup + // This is the content element of view root. + // Only `content` will enter this branch, because + // `background` and `nodeGroup` will not be deleted. + ? { + shape: { + x: 0, + y: 0, + width: innerStore.nodeWidth, + height: innerStore.nodeHeight + }, + style: { + opacity: 0 + } + } + // Others. + : { + style: { + opacity: 0 + } + }; + } else { + var targetX = 0; + var targetY = 0; + if (!innerStore.willDelete) { + // Let node animate to right-bottom corner, cooperating with fadeout, + // which is appropriate for user understanding. + // Divided by 2 for reRoot rolling up effect. + targetX = innerStore.nodeWidth / 2; + targetY = innerStore.nodeHeight / 2; + } + target = storageName === 'nodeGroup' ? { + x: targetX, + y: targetY, + style: { + opacity: 0 + } + } : { + shape: { + x: targetX, + y: targetY, + width: 0, + height: 0 + }, + style: { + opacity: 0 + } + }; + } + // TODO: do not support delay until necessary. + target && animationWrap.add(el, target, duration, 0, easing); + }); + }); + // Make other animations + each(this._storage, function (store, storageName) { + each(store, function (el, rawIndex) { + var last = renderResult.lastsForAnimation[storageName][rawIndex]; + var target = {}; + if (!last) { + return; + } + if (el instanceof Group) { + if (last.oldX != null) { + target.x = el.x; + target.y = el.y; + el.x = last.oldX; + el.y = last.oldY; + } + } else { + if (last.oldShape) { + target.shape = extend({}, el.shape); + el.setShape(last.oldShape); + } + if (last.fadein) { + el.setStyle('opacity', 0); + target.style = { + opacity: 1 + }; + } + // When animation is stopped for succedent animation starting, + // el.style.opacity might not be 1 + else if (el.style.opacity !== 1) { + target.style = { + opacity: 1 + }; + } + } + animationWrap.add(el, target, duration, 0, easing); + }); + }, this); + this._state = 'animating'; + animationWrap.finished(bind(function () { + this._state = 'ready'; + renderResult.renderFinally(); + }, this)).start(); + }; + TreemapView.prototype._resetController = function (api) { + var controller = this._controller; + // Init controller. + if (!controller) { + controller = this._controller = new RoamController(api.getZr()); + controller.enable(this.seriesModel.get('roam')); + controller.on('pan', bind(this._onPan, this)); + controller.on('zoom', bind(this._onZoom, this)); + } + var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight()); + controller.setPointerChecker(function (e, x, y) { + return rect.contain(x, y); + }); + }; + TreemapView.prototype._clearController = function () { + var controller = this._controller; + if (controller) { + controller.dispose(); + controller = null; + } + }; + TreemapView.prototype._onPan = function (e) { + if (this._state !== 'animating' && (Math.abs(e.dx) > DRAG_THRESHOLD || Math.abs(e.dy) > DRAG_THRESHOLD)) { + // These param must not be cached. + var root = this.seriesModel.getData().tree.root; + if (!root) { + return; + } + var rootLayout = root.getLayout(); + if (!rootLayout) { + return; + } + this.api.dispatchAction({ + type: 'treemapMove', + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: { + x: rootLayout.x + e.dx, + y: rootLayout.y + e.dy, + width: rootLayout.width, + height: rootLayout.height + } + }); + } + }; + TreemapView.prototype._onZoom = function (e) { + var mouseX = e.originX; + var mouseY = e.originY; + if (this._state !== 'animating') { + // These param must not be cached. + var root = this.seriesModel.getData().tree.root; + if (!root) { + return; + } + var rootLayout = root.getLayout(); + if (!rootLayout) { + return; + } + var rect = new BoundingRect(rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height); + var layoutInfo = this.seriesModel.layoutInfo; + // Transform mouse coord from global to containerGroup. + mouseX -= layoutInfo.x; + mouseY -= layoutInfo.y; + // Scale root bounding rect. + var m = create$1(); + translate(m, m, [-mouseX, -mouseY]); + scale$1(m, m, [e.scale, e.scale]); + translate(m, m, [mouseX, mouseY]); + rect.applyTransform(m); + this.api.dispatchAction({ + type: 'treemapRender', + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + } + }); + } + }; + TreemapView.prototype._initEvents = function (containerGroup) { + var _this = this; + containerGroup.on('click', function (e) { + if (_this._state !== 'ready') { + return; + } + var nodeClick = _this.seriesModel.get('nodeClick', true); + if (!nodeClick) { + return; + } + var targetInfo = _this.findTarget(e.offsetX, e.offsetY); + if (!targetInfo) { + return; + } + var node = targetInfo.node; + if (node.getLayout().isLeafRoot) { + _this._rootToNode(targetInfo); + } else { + if (nodeClick === 'zoomToNode') { + _this._zoomToNode(targetInfo); + } else if (nodeClick === 'link') { + var itemModel = node.hostTree.data.getItemModel(node.dataIndex); + var link = itemModel.get('link', true); + var linkTarget = itemModel.get('target', true) || 'blank'; + link && windowOpen(link, linkTarget); + } + } + }, this); + }; + TreemapView.prototype._renderBreadcrumb = function (seriesModel, api, targetInfo) { + var _this = this; + if (!targetInfo) { + targetInfo = seriesModel.get('leafDepth', true) != null ? { + node: seriesModel.getViewRoot() + } + // FIXME + // better way? + // Find breadcrumb tail on center of containerGroup. + : this.findTarget(api.getWidth() / 2, api.getHeight() / 2); + if (!targetInfo) { + targetInfo = { + node: seriesModel.getData().tree.root + }; + } + } + (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group))).render(seriesModel, api, targetInfo.node, function (node) { + if (_this._state !== 'animating') { + aboveViewRoot(seriesModel.getViewRoot(), node) ? _this._rootToNode({ + node: node + }) : _this._zoomToNode({ + node: node + }); + } + }); + }; + /** + * @override + */ + TreemapView.prototype.remove = function () { + this._clearController(); + this._containerGroup && this._containerGroup.removeAll(); + this._storage = createStorage(); + this._state = 'ready'; + this._breadcrumb && this._breadcrumb.remove(); + }; + TreemapView.prototype.dispose = function () { + this._clearController(); + }; + TreemapView.prototype._zoomToNode = function (targetInfo) { + this.api.dispatchAction({ + type: 'treemapZoomToNode', + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: targetInfo.node + }); + }; + TreemapView.prototype._rootToNode = function (targetInfo) { + this.api.dispatchAction({ + type: 'treemapRootToNode', + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: targetInfo.node + }); + }; + /** + * @public + * @param {number} x Global coord x. + * @param {number} y Global coord y. + * @return {Object} info If not found, return undefined; + * @return {number} info.node Target node. + * @return {number} info.offsetX x refer to target node. + * @return {number} info.offsetY y refer to target node. + */ + TreemapView.prototype.findTarget = function (x, y) { + var targetInfo; + var viewRoot = this.seriesModel.getViewRoot(); + viewRoot.eachNode({ + attr: 'viewChildren', + order: 'preorder' + }, function (node) { + var bgEl = this._storage.background[node.getRawIndex()]; + // If invisible, there might be no element. + if (bgEl) { + var point = bgEl.transformCoordToLocal(x, y); + var shape = bgEl.shape; + // For performance consideration, don't use 'getBoundingRect'. + if (shape.x <= point[0] && point[0] <= shape.x + shape.width && shape.y <= point[1] && point[1] <= shape.y + shape.height) { + targetInfo = { + node: node, + offsetX: point[0], + offsetY: point[1] + }; + } else { + return false; // Suppress visit subtree. + } + } + }, this); + return targetInfo; + }; + TreemapView.type = 'treemap'; + return TreemapView; + }(ChartView); + /** + * @inner + */ + function createStorage() { + return { + nodeGroup: [], + background: [], + content: [] + }; + } + /** + * @inner + * @return Return undefined means do not travel further. + */ + function renderNode(seriesModel, thisStorage, oldStorage, reRoot, lastsForAnimation, willInvisibleEls, thisNode, oldNode, parentGroup, depth) { + // Whether under viewRoot. + if (!thisNode) { + // Deleting nodes will be performed finally. This method just find + // element from old storage, or create new element, set them to new + // storage, and set styles. + return; + } + // ------------------------------------------------------------------- + // Start of closure variables available in "Procedures in renderNode". + var thisLayout = thisNode.getLayout(); + var data = seriesModel.getData(); + var nodeModel = thisNode.getModel(); + // Only for enabling highlight/downplay. Clear firstly. + // Because some node will not be rendered. + data.setItemGraphicEl(thisNode.dataIndex, null); + if (!thisLayout || !thisLayout.isInView) { + return; + } + var thisWidth = thisLayout.width; + var thisHeight = thisLayout.height; + var borderWidth = thisLayout.borderWidth; + var thisInvisible = thisLayout.invisible; + var thisRawIndex = thisNode.getRawIndex(); + var oldRawIndex = oldNode && oldNode.getRawIndex(); + var thisViewChildren = thisNode.viewChildren; + var upperHeight = thisLayout.upperHeight; + var isParent = thisViewChildren && thisViewChildren.length; + var itemStyleNormalModel = nodeModel.getModel('itemStyle'); + var itemStyleEmphasisModel = nodeModel.getModel(['emphasis', 'itemStyle']); + var itemStyleBlurModel = nodeModel.getModel(['blur', 'itemStyle']); + var itemStyleSelectModel = nodeModel.getModel(['select', 'itemStyle']); + var borderRadius = itemStyleNormalModel.get('borderRadius') || 0; + // End of closure ariables available in "Procedures in renderNode". + // ----------------------------------------------------------------- + // Node group + var group = giveGraphic('nodeGroup', Group$1); + if (!group) { + return; + } + parentGroup.add(group); + // x,y are not set when el is above view root. + group.x = thisLayout.x || 0; + group.y = thisLayout.y || 0; + group.markRedraw(); + inner$8(group).nodeWidth = thisWidth; + inner$8(group).nodeHeight = thisHeight; + if (thisLayout.isAboveViewRoot) { + return group; + } + // Background + var bg = giveGraphic('background', Rect$1, depth, Z2_BG); + bg && renderBackground(group, bg, isParent && thisLayout.upperLabelHeight); + var emphasisModel = nodeModel.getModel('emphasis'); + var focus = emphasisModel.get('focus'); + var blurScope = emphasisModel.get('blurScope'); + var isDisabled = emphasisModel.get('disabled'); + var focusOrIndices = focus === 'ancestor' ? thisNode.getAncestorsIndices() : focus === 'descendant' ? thisNode.getDescendantIndices() : focus; + // No children, render content. + if (isParent) { + // Because of the implementation about "traverse" in graphic hover style, we + // can not set hover listener on the "group" of non-leaf node. Otherwise the + // hover event from the descendents will be listenered. + if (isHighDownDispatcher(group)) { + setAsHighDownDispatcher(group, false); + } + if (bg) { + setAsHighDownDispatcher(bg, !isDisabled); + // Only for enabling highlight/downplay. + data.setItemGraphicEl(thisNode.dataIndex, bg); + enableHoverFocus(bg, focusOrIndices, blurScope); + } + } else { + var content = giveGraphic('content', Rect$1, depth, Z2_CONTENT); + content && renderContent(group, content); + bg.disableMorphing = true; + if (bg && isHighDownDispatcher(bg)) { + setAsHighDownDispatcher(bg, false); + } + setAsHighDownDispatcher(group, !isDisabled); + // Only for enabling highlight/downplay. + data.setItemGraphicEl(thisNode.dataIndex, group); + enableHoverFocus(group, focusOrIndices, blurScope); + } + return group; + // ---------------------------- + // | Procedures in renderNode | + // ---------------------------- + function renderBackground(group, bg, useUpperLabel) { + var ecData = getECData(bg); + // For tooltip. + ecData.dataIndex = thisNode.dataIndex; + ecData.seriesIndex = seriesModel.seriesIndex; + bg.setShape({ + x: 0, + y: 0, + width: thisWidth, + height: thisHeight, + r: borderRadius + }); + if (thisInvisible) { + // If invisible, do not set visual, otherwise the element will + // change immediately before animation. We think it is OK to + // remain its origin color when moving out of the view window. + processInvisible(bg); + } else { + bg.invisible = false; + var style = thisNode.getVisual('style'); + var visualBorderColor = style.stroke; + var normalStyle = getItemStyleNormal(itemStyleNormalModel); + normalStyle.fill = visualBorderColor; + var emphasisStyle = getStateItemStyle(itemStyleEmphasisModel); + emphasisStyle.fill = itemStyleEmphasisModel.get('borderColor'); + var blurStyle = getStateItemStyle(itemStyleBlurModel); + blurStyle.fill = itemStyleBlurModel.get('borderColor'); + var selectStyle = getStateItemStyle(itemStyleSelectModel); + selectStyle.fill = itemStyleSelectModel.get('borderColor'); + if (useUpperLabel) { + var upperLabelWidth = thisWidth - 2 * borderWidth; + prepareText( + // PENDING: convert ZRColor to ColorString for text. + bg, visualBorderColor, style.opacity, { + x: borderWidth, + y: 0, + width: upperLabelWidth, + height: upperHeight + }); + } + // For old bg. + else { + bg.removeTextContent(); + } + bg.setStyle(normalStyle); + bg.ensureState('emphasis').style = emphasisStyle; + bg.ensureState('blur').style = blurStyle; + bg.ensureState('select').style = selectStyle; + setDefaultStateProxy(bg); + } + group.add(bg); + } + function renderContent(group, content) { + var ecData = getECData(content); + // For tooltip. + ecData.dataIndex = thisNode.dataIndex; + ecData.seriesIndex = seriesModel.seriesIndex; + var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0); + var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0); + content.culling = true; + content.setShape({ + x: borderWidth, + y: borderWidth, + width: contentWidth, + height: contentHeight, + r: borderRadius + }); + if (thisInvisible) { + // If invisible, do not set visual, otherwise the element will + // change immediately before animation. We think it is OK to + // remain its origin color when moving out of the view window. + processInvisible(content); + } else { + content.invisible = false; + var nodeStyle = thisNode.getVisual('style'); + var visualColor = nodeStyle.fill; + var normalStyle = getItemStyleNormal(itemStyleNormalModel); + normalStyle.fill = visualColor; + normalStyle.decal = nodeStyle.decal; + var emphasisStyle = getStateItemStyle(itemStyleEmphasisModel); + var blurStyle = getStateItemStyle(itemStyleBlurModel); + var selectStyle = getStateItemStyle(itemStyleSelectModel); + // PENDING: convert ZRColor to ColorString for text. + prepareText(content, visualColor, nodeStyle.opacity, null); + content.setStyle(normalStyle); + content.ensureState('emphasis').style = emphasisStyle; + content.ensureState('blur').style = blurStyle; + content.ensureState('select').style = selectStyle; + setDefaultStateProxy(content); + } + group.add(content); + } + function processInvisible(element) { + // Delay invisible setting utill animation finished, + // avoid element vanish suddenly before animation. + !element.invisible && willInvisibleEls.push(element); + } + function prepareText(rectEl, visualColor, visualOpacity, + // Can be null/undefined + upperLabelRect) { + var normalLabelModel = nodeModel.getModel(upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL); + var defaultText = convertOptionIdName(nodeModel.get('name'), null); + var isShow = normalLabelModel.getShallow('show'); + setLabelStyle(rectEl, getLabelStatesModels(nodeModel, upperLabelRect ? PATH_UPPERLABEL_NORMAL : PATH_LABEL_NOAMAL), { + defaultText: isShow ? defaultText : null, + inheritColor: visualColor, + defaultOpacity: visualOpacity, + labelFetcher: seriesModel, + labelDataIndex: thisNode.dataIndex + }); + var textEl = rectEl.getTextContent(); + if (!textEl) { + return; + } + var textStyle = textEl.style; + var textPadding = normalizeCssArray(textStyle.padding || 0); + if (upperLabelRect) { + rectEl.setTextConfig({ + layoutRect: upperLabelRect + }); + textEl.disableLabelLayout = true; + } + textEl.beforeUpdate = function () { + var width = Math.max((upperLabelRect ? upperLabelRect.width : rectEl.shape.width) - textPadding[1] - textPadding[3], 0); + var height = Math.max((upperLabelRect ? upperLabelRect.height : rectEl.shape.height) - textPadding[0] - textPadding[2], 0); + if (textStyle.width !== width || textStyle.height !== height) { + textEl.setStyle({ + width: width, + height: height + }); + } + }; + textStyle.truncateMinChar = 2; + textStyle.lineOverflow = 'truncate'; + addDrillDownIcon(textStyle, upperLabelRect, thisLayout); + var textEmphasisState = textEl.getState('emphasis'); + addDrillDownIcon(textEmphasisState ? textEmphasisState.style : null, upperLabelRect, thisLayout); + } + function addDrillDownIcon(style, upperLabelRect, thisLayout) { + var text = style ? style.text : null; + if (!upperLabelRect && thisLayout.isLeafRoot && text != null) { + var iconChar = seriesModel.get('drillDownIcon', true); + style.text = iconChar ? iconChar + ' ' + text : text; + } + } + function giveGraphic(storageName, Ctor, depth, z) { + var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex]; + var lasts = lastsForAnimation[storageName]; + if (element) { + // Remove from oldStorage + oldStorage[storageName][oldRawIndex] = null; + prepareAnimationWhenHasOld(lasts, element); + } + // If invisible and no old element, do not create new element (for optimizing). + else if (!thisInvisible) { + element = new Ctor(); + if (element instanceof Displayable) { + element.z2 = calculateZ2(depth, z); + } + prepareAnimationWhenNoOld(lasts, element); + } + // Set to thisStorage + return thisStorage[storageName][thisRawIndex] = element; + } + function prepareAnimationWhenHasOld(lasts, element) { + var lastCfg = lasts[thisRawIndex] = {}; + if (element instanceof Group$1) { + lastCfg.oldX = element.x; + lastCfg.oldY = element.y; + } else { + lastCfg.oldShape = extend({}, element.shape); + } + } + // If a element is new, we need to find the animation start point carefully, + // otherwise it will looks strange when 'zoomToNode'. + function prepareAnimationWhenNoOld(lasts, element) { + var lastCfg = lasts[thisRawIndex] = {}; + var parentNode = thisNode.parentNode; + var isGroup = element instanceof Group; + if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) { + var parentOldX = 0; + var parentOldY = 0; + // New nodes appear from right-bottom corner in 'zoomToNode' animation. + // For convenience, get old bounding rect from background. + var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()]; + if (!reRoot && parentOldBg && parentOldBg.oldShape) { + parentOldX = parentOldBg.oldShape.width; + parentOldY = parentOldBg.oldShape.height; + } + // When no parent old shape found, its parent is new too, + // so we can just use {x:0, y:0}. + if (isGroup) { + lastCfg.oldX = 0; + lastCfg.oldY = parentOldY; + } else { + lastCfg.oldShape = { + x: parentOldX, + y: parentOldY, + width: 0, + height: 0 + }; + } + } + // Fade in, user can be aware that these nodes are new. + lastCfg.fadein = !isGroup; + } + } + // We cannot set all background with the same z, because the behaviour of + // drill down and roll up differ background creation sequence from tree + // hierarchy sequence, which cause lower background elements to overlap + // upper ones. So we calculate z based on depth. + // Moreover, we try to shrink down z interval to [0, 1] to avoid that + // treemap with large z overlaps other components. + function calculateZ2(depth, z2InLevel) { + return depth * Z2_BASE + z2InLevel; + } + + var each$3 = each; + var isObject$3 = isObject; + var CATEGORY_DEFAULT_VISUAL_INDEX = -1; + var VisualMapping = /** @class */function () { + function VisualMapping(option) { + var mappingMethod = option.mappingMethod; + var visualType = option.type; + var thisOption = this.option = clone(option); + this.type = visualType; + this.mappingMethod = mappingMethod; + this._normalizeData = normalizers[mappingMethod]; + var visualHandler = VisualMapping.visualHandlers[visualType]; + this.applyVisual = visualHandler.applyVisual; + this.getColorMapper = visualHandler.getColorMapper; + this._normalizedToVisual = visualHandler._normalizedToVisual[mappingMethod]; + if (mappingMethod === 'piecewise') { + normalizeVisualRange(thisOption); + preprocessForPiecewise(thisOption); + } else if (mappingMethod === 'category') { + thisOption.categories ? preprocessForSpecifiedCategory(thisOption) + // categories is ordinal when thisOption.categories not specified, + // which need no more preprocess except normalize visual. + : normalizeVisualRange(thisOption, true); + } else { + // mappingMethod === 'linear' or 'fixed' + assert(mappingMethod !== 'linear' || thisOption.dataExtent); + normalizeVisualRange(thisOption); + } + } + VisualMapping.prototype.mapValueToVisual = function (value) { + var normalized = this._normalizeData(value); + return this._normalizedToVisual(normalized, value); + }; + VisualMapping.prototype.getNormalizer = function () { + return bind(this._normalizeData, this); + }; + /** + * List available visual types. + * + * @public + * @return {Array.} + */ + VisualMapping.listVisualTypes = function () { + return keys(VisualMapping.visualHandlers); + }; + // /** + // * @public + // */ + // static addVisualHandler(name, handler) { + // visualHandlers[name] = handler; + // } + /** + * @public + */ + VisualMapping.isValidType = function (visualType) { + return VisualMapping.visualHandlers.hasOwnProperty(visualType); + }; + /** + * Convenient method. + * Visual can be Object or Array or primary type. + */ + VisualMapping.eachVisual = function (visual, callback, context) { + if (isObject(visual)) { + each(visual, callback, context); + } else { + callback.call(context, visual); + } + }; + VisualMapping.mapVisual = function (visual, callback, context) { + var isPrimary; + var newVisual = isArray(visual) ? [] : isObject(visual) ? {} : (isPrimary = true, null); + VisualMapping.eachVisual(visual, function (v, key) { + var newVal = callback.call(context, v, key); + isPrimary ? newVisual = newVal : newVisual[key] = newVal; + }); + return newVisual; + }; + /** + * Retrieve visual properties from given object. + */ + VisualMapping.retrieveVisuals = function (obj) { + var ret = {}; + var hasVisual; + obj && each$3(VisualMapping.visualHandlers, function (h, visualType) { + if (obj.hasOwnProperty(visualType)) { + ret[visualType] = obj[visualType]; + hasVisual = true; + } + }); + return hasVisual ? ret : null; + }; + /** + * Give order to visual types, considering colorSaturation, colorAlpha depends on color. + * + * @public + * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...} + * IF Array, like: ['color', 'symbol', 'colorSaturation'] + * @return {Array.} Sorted visual types. + */ + VisualMapping.prepareVisualTypes = function (visualTypes) { + if (isArray(visualTypes)) { + visualTypes = visualTypes.slice(); + } else if (isObject$3(visualTypes)) { + var types_1 = []; + each$3(visualTypes, function (item, type) { + types_1.push(type); + }); + visualTypes = types_1; + } else { + return []; + } + visualTypes.sort(function (type1, type2) { + // color should be front of colorSaturation, colorAlpha, ... + // symbol and symbolSize do not matter. + return type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0 ? 1 : -1; + }); + return visualTypes; + }; + /** + * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'. + * Other visuals are only depends on themself. + */ + VisualMapping.dependsOn = function (visualType1, visualType2) { + return visualType2 === 'color' ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) : visualType1 === visualType2; + }; + /** + * @param value + * @param pieceList [{value: ..., interval: [min, max]}, ...] + * Always from small to big. + * @param findClosestWhenOutside Default to be false + * @return index + */ + VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) { + var possibleI; + var abs = Infinity; + // value has the higher priority. + for (var i = 0, len = pieceList.length; i < len; i++) { + var pieceValue = pieceList[i].value; + if (pieceValue != null) { + if (pieceValue === value + // FIXME + // It is supposed to compare value according to value type of dimension, + // but currently value type can exactly be string or number. + // Compromise for numeric-like string (like '12'), especially + // in the case that visualMap.categories is ['22', '33']. + || isString(pieceValue) && pieceValue === value + '') { + return i; + } + findClosestWhenOutside && updatePossible(pieceValue, i); + } + } + for (var i = 0, len = pieceList.length; i < len; i++) { + var piece = pieceList[i]; + var interval = piece.interval; + var close_1 = piece.close; + if (interval) { + if (interval[0] === -Infinity) { + if (littleThan(close_1[1], value, interval[1])) { + return i; + } + } else if (interval[1] === Infinity) { + if (littleThan(close_1[0], interval[0], value)) { + return i; + } + } else if (littleThan(close_1[0], interval[0], value) && littleThan(close_1[1], value, interval[1])) { + return i; + } + findClosestWhenOutside && updatePossible(interval[0], i); + findClosestWhenOutside && updatePossible(interval[1], i); + } + } + if (findClosestWhenOutside) { + return value === Infinity ? pieceList.length - 1 : value === -Infinity ? 0 : possibleI; + } + function updatePossible(val, index) { + var newAbs = Math.abs(val - value); + if (newAbs < abs) { + abs = newAbs; + possibleI = index; + } + } + }; + VisualMapping.visualHandlers = { + color: { + applyVisual: makeApplyVisual('color'), + getColorMapper: function () { + var thisOption = this.option; + return bind(thisOption.mappingMethod === 'category' ? function (value, isNormalized) { + !isNormalized && (value = this._normalizeData(value)); + return doMapCategory.call(this, value); + } : function (value, isNormalized, out) { + // If output rgb array + // which will be much faster and useful in pixel manipulation + var returnRGBArray = !!out; + !isNormalized && (value = this._normalizeData(value)); + out = fastLerp(value, thisOption.parsedVisual, out); + return returnRGBArray ? out : stringify(out, 'rgba'); + }, this); + }, + _normalizedToVisual: { + linear: function (normalized) { + return stringify(fastLerp(normalized, this.option.parsedVisual), 'rgba'); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = stringify(fastLerp(normalized, this.option.parsedVisual), 'rgba'); + } + return result; + }, + fixed: doMapFixed + } + }, + colorHue: makePartialColorVisualHandler(function (color$1, value) { + return modifyHSL(color$1, value); + }), + colorSaturation: makePartialColorVisualHandler(function (color$1, value) { + return modifyHSL(color$1, null, value); + }), + colorLightness: makePartialColorVisualHandler(function (color$1, value) { + return modifyHSL(color$1, null, null, value); + }), + colorAlpha: makePartialColorVisualHandler(function (color$1, value) { + return modifyAlpha(color$1, value); + }), + decal: { + applyVisual: makeApplyVisual('decal'), + _normalizedToVisual: { + linear: null, + category: doMapCategory, + piecewise: null, + fixed: null + } + }, + opacity: { + applyVisual: makeApplyVisual('opacity'), + _normalizedToVisual: createNormalizedToNumericVisual([0, 1]) + }, + liftZ: { + applyVisual: makeApplyVisual('liftZ'), + _normalizedToVisual: { + linear: doMapFixed, + category: doMapFixed, + piecewise: doMapFixed, + fixed: doMapFixed + } + }, + symbol: { + applyVisual: function (value, getter, setter) { + var symbolCfg = this.mapValueToVisual(value); + setter('symbol', symbolCfg); + }, + _normalizedToVisual: { + linear: doMapToArray, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = doMapToArray.call(this, normalized); + } + return result; + }, + fixed: doMapFixed + } + }, + symbolSize: { + applyVisual: makeApplyVisual('symbolSize'), + _normalizedToVisual: createNormalizedToNumericVisual([0, 1]) + } + }; + return VisualMapping; + }(); + function preprocessForPiecewise(thisOption) { + var pieceList = thisOption.pieceList; + thisOption.hasSpecialVisual = false; + each(pieceList, function (piece, index) { + piece.originIndex = index; + // piece.visual is "result visual value" but not + // a visual range, so it does not need to be normalized. + if (piece.visual != null) { + thisOption.hasSpecialVisual = true; + } + }); + } + function preprocessForSpecifiedCategory(thisOption) { + // Hash categories. + var categories = thisOption.categories; + var categoryMap = thisOption.categoryMap = {}; + var visual = thisOption.visual; + each$3(categories, function (cate, index) { + categoryMap[cate] = index; + }); + // Process visual map input. + if (!isArray(visual)) { + var visualArr_1 = []; + if (isObject(visual)) { + each$3(visual, function (v, cate) { + var index = categoryMap[cate]; + visualArr_1[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v; + }); + } else { + // Is primary type, represents default visual. + visualArr_1[CATEGORY_DEFAULT_VISUAL_INDEX] = visual; + } + visual = setVisualToOption(thisOption, visualArr_1); + } + // Remove categories that has no visual, + // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX. + for (var i = categories.length - 1; i >= 0; i--) { + if (visual[i] == null) { + delete categoryMap[categories[i]]; + categories.pop(); + } + } + } + function normalizeVisualRange(thisOption, isCategory) { + var visual = thisOption.visual; + var visualArr = []; + if (isObject(visual)) { + each$3(visual, function (v) { + visualArr.push(v); + }); + } else if (visual != null) { + visualArr.push(visual); + } + var doNotNeedPair = { + color: 1, + symbol: 1 + }; + if (!isCategory && visualArr.length === 1 && !doNotNeedPair.hasOwnProperty(thisOption.type)) { + // Do not care visualArr.length === 0, which is illegal. + visualArr[1] = visualArr[0]; + } + setVisualToOption(thisOption, visualArr); + } + function makePartialColorVisualHandler(applyValue) { + return { + applyVisual: function (value, getter, setter) { + // Only used in HSL + var colorChannel = this.mapValueToVisual(value); + // Must not be array value + setter('color', applyValue(getter('color'), colorChannel)); + }, + _normalizedToVisual: createNormalizedToNumericVisual([0, 1]) + }; + } + function doMapToArray(normalized) { + var visual = this.option.visual; + return visual[Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true))] || {}; // TODO {}? + } + + function makeApplyVisual(visualType) { + return function (value, getter, setter) { + setter(visualType, this.mapValueToVisual(value)); + }; + } + function doMapCategory(normalized) { + var visual = this.option.visual; + return visual[this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX ? normalized % visual.length : normalized]; + } + function doMapFixed() { + // visual will be convert to array. + return this.option.visual[0]; + } + /** + * Create mapped to numeric visual + */ + function createNormalizedToNumericVisual(sourceExtent) { + return { + linear: function (normalized) { + return linearMap(normalized, sourceExtent, this.option.visual, true); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = linearMap(normalized, sourceExtent, this.option.visual, true); + } + return result; + }, + fixed: doMapFixed + }; + } + function getSpecifiedVisual(value) { + var thisOption = this.option; + var pieceList = thisOption.pieceList; + if (thisOption.hasSpecialVisual) { + var pieceIndex = VisualMapping.findPieceIndex(value, pieceList); + var piece = pieceList[pieceIndex]; + if (piece && piece.visual) { + return piece.visual[this.type]; + } + } + } + function setVisualToOption(thisOption, visualArr) { + thisOption.visual = visualArr; + if (thisOption.type === 'color') { + thisOption.parsedVisual = map(visualArr, function (item) { + var color$1 = parse(item); + if (!color$1 && "development" !== 'production') { + warn("'" + item + "' is an illegal color, fallback to '#000000'", true); + } + return color$1 || [0, 0, 0, 1]; + }); + } + return visualArr; + } + /** + * Normalizers by mapping methods. + */ + var normalizers = { + linear: function (value) { + return linearMap(value, this.option.dataExtent, [0, 1], true); + }, + piecewise: function (value) { + var pieceList = this.option.pieceList; + var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true); + if (pieceIndex != null) { + return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true); + } + }, + category: function (value) { + var index = this.option.categories ? this.option.categoryMap[value] : value; // ordinal value + return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index; + }, + fixed: noop + }; + function littleThan(close, a, b) { + return close ? a <= b : a < b; + } + + var ITEM_STYLE_NORMAL = 'itemStyle'; + var inner$9 = makeInner(); + var treemapVisual = { + seriesType: 'treemap', + reset: function (seriesModel) { + var tree = seriesModel.getData().tree; + var root = tree.root; + if (root.isRemoved()) { + return; + } + travelTree(root, + // Visual should calculate from tree root but not view root. + {}, seriesModel.getViewRoot().getAncestors(), seriesModel); + } + }; + function travelTree(node, designatedVisual, viewRootAncestors, seriesModel) { + var nodeModel = node.getModel(); + var nodeLayout = node.getLayout(); + var data = node.hostTree.data; + // Optimize + if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) { + return; + } + var nodeItemStyleModel = nodeModel.getModel(ITEM_STYLE_NORMAL); + var visuals = buildVisuals(nodeItemStyleModel, designatedVisual, seriesModel); + var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style'); + // calculate border color + var borderColor = nodeItemStyleModel.get('borderColor'); + var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation'); + var thisNodeColor; + if (borderColorSaturation != null) { + // For performance, do not always execute 'calculateColor'. + thisNodeColor = calculateColor(visuals); + borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor); + } + existsStyle.stroke = borderColor; + var viewChildren = node.viewChildren; + if (!viewChildren || !viewChildren.length) { + thisNodeColor = calculateColor(visuals); + // Apply visual to this node. + existsStyle.fill = thisNodeColor; + } else { + var mapping_1 = buildVisualMapping(node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren); + // Designate visual to children. + each(viewChildren, function (child, index) { + // If higher than viewRoot, only ancestors of viewRoot is needed to visit. + if (child.depth >= viewRootAncestors.length || child === viewRootAncestors[child.depth]) { + var childVisual = mapVisual(nodeModel, visuals, child, index, mapping_1, seriesModel); + travelTree(child, childVisual, viewRootAncestors, seriesModel); + } + }); + } + } + function buildVisuals(nodeItemStyleModel, designatedVisual, seriesModel) { + var visuals = extend({}, designatedVisual); + var designatedVisualItemStyle = seriesModel.designatedVisualItemStyle; + each(['color', 'colorAlpha', 'colorSaturation'], function (visualName) { + // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel + designatedVisualItemStyle[visualName] = designatedVisual[visualName]; + var val = nodeItemStyleModel.get(visualName); + designatedVisualItemStyle[visualName] = null; + val != null && (visuals[visualName] = val); + }); + return visuals; + } + function calculateColor(visuals) { + var color = getValueVisualDefine(visuals, 'color'); + if (color) { + var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha'); + var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation'); + if (colorSaturation) { + color = modifyHSL(color, null, null, colorSaturation); + } + if (colorAlpha) { + color = modifyAlpha(color, colorAlpha); + } + return color; + } + } + function calculateBorderColor(borderColorSaturation, thisNodeColor) { + return thisNodeColor != null + // Can only be string + ? modifyHSL(thisNodeColor, null, null, borderColorSaturation) : null; + } + function getValueVisualDefine(visuals, name) { + var value = visuals[name]; + if (value != null && value !== 'none') { + return value; + } + } + function buildVisualMapping(node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren) { + if (!viewChildren || !viewChildren.length) { + return; + } + var rangeVisual = getRangeVisual(nodeModel, 'color') || visuals.color != null && visuals.color !== 'none' && (getRangeVisual(nodeModel, 'colorAlpha') || getRangeVisual(nodeModel, 'colorSaturation')); + if (!rangeVisual) { + return; + } + var visualMin = nodeModel.get('visualMin'); + var visualMax = nodeModel.get('visualMax'); + var dataExtent = nodeLayout.dataExtent.slice(); + visualMin != null && visualMin < dataExtent[0] && (dataExtent[0] = visualMin); + visualMax != null && visualMax > dataExtent[1] && (dataExtent[1] = visualMax); + var colorMappingBy = nodeModel.get('colorMappingBy'); + var opt = { + type: rangeVisual.name, + dataExtent: dataExtent, + visual: rangeVisual.range + }; + if (opt.type === 'color' && (colorMappingBy === 'index' || colorMappingBy === 'id')) { + opt.mappingMethod = 'category'; + opt.loop = true; + // categories is ordinal, so do not set opt.categories. + } else { + opt.mappingMethod = 'linear'; + } + var mapping = new VisualMapping(opt); + inner$9(mapping).drColorMappingBy = colorMappingBy; + return mapping; + } + // Notice: If we don't have the attribute 'colorRange', but only use + // attribute 'color' to represent both concepts of 'colorRange' and 'color', + // (It means 'colorRange' when 'color' is Array, means 'color' when not array), + // this problem will be encountered: + // If a level-1 node doesn't have children, and its siblings have children, + // and colorRange is set on level-1, then the node cannot be colored. + // So we separate 'colorRange' and 'color' to different attributes. + function getRangeVisual(nodeModel, name) { + // 'colorRange', 'colorARange', 'colorSRange'. + // If not exists on this node, fetch from levels and series. + var range = nodeModel.get(name); + return isArray(range) && range.length ? { + name: name, + range: range + } : null; + } + function mapVisual(nodeModel, visuals, child, index, mapping, seriesModel) { + var childVisuals = extend({}, visuals); + if (mapping) { + // Only support color, colorAlpha, colorSaturation. + var mappingType = mapping.type; + var colorMappingBy = mappingType === 'color' && inner$9(mapping).drColorMappingBy; + var value = colorMappingBy === 'index' ? index : colorMappingBy === 'id' ? seriesModel.mapIdToIndex(child.getId()) : child.getValue(nodeModel.get('visualDimension')); + childVisuals[mappingType] = mapping.mapValueToVisual(value); + } + return childVisuals; + } + + var mathMax$7 = Math.max; + var mathMin$7 = Math.min; + var retrieveValue = retrieve; + var each$4 = each; + var PATH_BORDER_WIDTH = ['itemStyle', 'borderWidth']; + var PATH_GAP_WIDTH = ['itemStyle', 'gapWidth']; + var PATH_UPPER_LABEL_SHOW = ['upperLabel', 'show']; + var PATH_UPPER_LABEL_HEIGHT = ['upperLabel', 'height']; + /** + * @public + */ + var treemapLayout = { + seriesType: 'treemap', + reset: function (seriesModel, ecModel, api, payload) { + // Layout result in each node: + // {x, y, width, height, area, borderWidth} + var ecWidth = api.getWidth(); + var ecHeight = api.getHeight(); + var seriesOption = seriesModel.option; + var layoutInfo = getLayoutRect(seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + var size = seriesOption.size || []; // Compatible with ec2. + var containerWidth = parsePercent$1(retrieveValue(layoutInfo.width, size[0]), ecWidth); + var containerHeight = parsePercent$1(retrieveValue(layoutInfo.height, size[1]), ecHeight); + // Fetch payload info. + var payloadType = payload && payload.type; + var types = ['treemapZoomToNode', 'treemapRootToNode']; + var targetInfo = retrieveTargetInfo(payload, types, seriesModel); + var rootRect = payloadType === 'treemapRender' || payloadType === 'treemapMove' ? payload.rootRect : null; + var viewRoot = seriesModel.getViewRoot(); + var viewAbovePath = getPathToRoot(viewRoot); + if (payloadType !== 'treemapMove') { + var rootSize = payloadType === 'treemapZoomToNode' ? estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) : rootRect ? [rootRect.width, rootRect.height] : [containerWidth, containerHeight]; + var sort_1 = seriesOption.sort; + if (sort_1 && sort_1 !== 'asc' && sort_1 !== 'desc') { + // Default to be desc order. + sort_1 = 'desc'; + } + var options = { + squareRatio: seriesOption.squareRatio, + sort: sort_1, + leafDepth: seriesOption.leafDepth + }; + // layout should be cleared because using updateView but not update. + viewRoot.hostTree.clearLayouts(); + // TODO + // optimize: if out of view clip, do not layout. + // But take care that if do not render node out of view clip, + // how to calculate start po + var viewRootLayout_1 = { + x: 0, + y: 0, + width: rootSize[0], + height: rootSize[1], + area: rootSize[0] * rootSize[1] + }; + viewRoot.setLayout(viewRootLayout_1); + squarify(viewRoot, options, false, 0); + // Supplement layout. + viewRootLayout_1 = viewRoot.getLayout(); + each$4(viewAbovePath, function (node, index) { + var childValue = (viewAbovePath[index + 1] || viewRoot).getValue(); + node.setLayout(extend({ + dataExtent: [childValue, childValue], + borderWidth: 0, + upperHeight: 0 + }, viewRootLayout_1)); + }); + } + var treeRoot = seriesModel.getData().tree.root; + treeRoot.setLayout(calculateRootPosition(layoutInfo, rootRect, targetInfo), true); + seriesModel.setLayoutInfo(layoutInfo); + // FIXME + // 现在没有clip功能,暂时取ec高宽。 + prunning(treeRoot, + // Transform to base element coordinate system. + new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight), viewAbovePath, viewRoot, 0); + } + }; + /** + * Layout treemap with squarify algorithm. + * The original presentation of this algorithm + * was made by Mark Bruls, Kees Huizing, and Jarke J. van Wijk + * . + * The implementation of this algorithm was originally copied from "d3.js" + * + * with some modifications made for this program. + * See the license statement at the head of this file. + * + * @protected + * @param {module:echarts/data/Tree~TreeNode} node + * @param {Object} options + * @param {string} options.sort 'asc' or 'desc' + * @param {number} options.squareRatio + * @param {boolean} hideChildren + * @param {number} depth + */ + function squarify(node, options, hideChildren, depth) { + var width; + var height; + if (node.isRemoved()) { + return; + } + var thisLayout = node.getLayout(); + width = thisLayout.width; + height = thisLayout.height; + // Considering border and gap + var nodeModel = node.getModel(); + var borderWidth = nodeModel.get(PATH_BORDER_WIDTH); + var halfGapWidth = nodeModel.get(PATH_GAP_WIDTH) / 2; + var upperLabelHeight = getUpperLabelHeight(nodeModel); + var upperHeight = Math.max(borderWidth, upperLabelHeight); + var layoutOffset = borderWidth - halfGapWidth; + var layoutOffsetUpper = upperHeight - halfGapWidth; + node.setLayout({ + borderWidth: borderWidth, + upperHeight: upperHeight, + upperLabelHeight: upperLabelHeight + }, true); + width = mathMax$7(width - 2 * layoutOffset, 0); + height = mathMax$7(height - layoutOffset - layoutOffsetUpper, 0); + var totalArea = width * height; + var viewChildren = initChildren(node, nodeModel, totalArea, options, hideChildren, depth); + if (!viewChildren.length) { + return; + } + var rect = { + x: layoutOffset, + y: layoutOffsetUpper, + width: width, + height: height + }; + var rowFixedLength = mathMin$7(width, height); + var best = Infinity; // the best row score so far + var row = []; + row.area = 0; + for (var i = 0, len = viewChildren.length; i < len;) { + var child = viewChildren[i]; + row.push(child); + row.area += child.getLayout().area; + var score = worst(row, rowFixedLength, options.squareRatio); + // continue with this orientation + if (score <= best) { + i++; + best = score; + } + // abort, and try a different orientation + else { + row.area -= row.pop().getLayout().area; + position(row, rowFixedLength, rect, halfGapWidth, false); + rowFixedLength = mathMin$7(rect.width, rect.height); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, rowFixedLength, rect, halfGapWidth, true); + } + if (!hideChildren) { + var childrenVisibleMin = nodeModel.get('childrenVisibleMin'); + if (childrenVisibleMin != null && totalArea < childrenVisibleMin) { + hideChildren = true; + } + } + for (var i = 0, len = viewChildren.length; i < len; i++) { + squarify(viewChildren[i], options, hideChildren, depth + 1); + } + } + /** + * Set area to each child, and calculate data extent for visual coding. + */ + function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) { + var viewChildren = node.children || []; + var orderBy = options.sort; + orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null); + var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth; + // leafDepth has higher priority. + if (hideChildren && !overLeafDepth) { + return node.viewChildren = []; + } + // Sort children, order by desc. + viewChildren = filter(viewChildren, function (child) { + return !child.isRemoved(); + }); + sort$1(viewChildren, orderBy); + var info = statistic(nodeModel, viewChildren, orderBy); + if (info.sum === 0) { + return node.viewChildren = []; + } + info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren); + if (info.sum === 0) { + return node.viewChildren = []; + } + // Set area to each child. + for (var i = 0, len = viewChildren.length; i < len; i++) { + var area = viewChildren[i].getValue() / info.sum * totalArea; + // Do not use setLayout({...}, true), because it is needed to clear last layout. + viewChildren[i].setLayout({ + area: area + }); + } + if (overLeafDepth) { + viewChildren.length && node.setLayout({ + isLeafRoot: true + }, true); + viewChildren.length = 0; + } + node.viewChildren = viewChildren; + node.setLayout({ + dataExtent: info.dataExtent + }, true); + return viewChildren; + } + /** + * Consider 'visibleMin'. Modify viewChildren and get new sum. + */ + function filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) { + // visibleMin is not supported yet when no option.sort. + if (!orderBy) { + return sum; + } + var visibleMin = nodeModel.get('visibleMin'); + var len = orderedChildren.length; + var deletePoint = len; + // Always travel from little value to big value. + for (var i = len - 1; i >= 0; i--) { + var value = orderedChildren[orderBy === 'asc' ? len - i - 1 : i].getValue(); + if (value / sum * totalArea < visibleMin) { + deletePoint = i; + sum -= value; + } + } + orderBy === 'asc' ? orderedChildren.splice(0, len - deletePoint) : orderedChildren.splice(deletePoint, len - deletePoint); + return sum; + } + /** + * Sort + */ + function sort$1(viewChildren, orderBy) { + if (orderBy) { + viewChildren.sort(function (a, b) { + var diff = orderBy === 'asc' ? a.getValue() - b.getValue() : b.getValue() - a.getValue(); + return diff === 0 ? orderBy === 'asc' ? a.dataIndex - b.dataIndex : b.dataIndex - a.dataIndex : diff; + }); + } + return viewChildren; + } + /** + * Statistic + */ + function statistic(nodeModel, children, orderBy) { + // Calculate sum. + var sum = 0; + for (var i = 0, len = children.length; i < len; i++) { + sum += children[i].getValue(); + } + // Statistic data extent for latter visual coding. + // Notice: data extent should be calculate based on raw children + // but not filtered view children, otherwise visual mapping will not + // be stable when zoom (where children is filtered by visibleMin). + var dimension = nodeModel.get('visualDimension'); + var dataExtent; + // The same as area dimension. + if (!children || !children.length) { + dataExtent = [NaN, NaN]; + } else if (dimension === 'value' && orderBy) { + dataExtent = [children[children.length - 1].getValue(), children[0].getValue()]; + orderBy === 'asc' && dataExtent.reverse(); + } + // Other dimension. + else { + dataExtent = [Infinity, -Infinity]; + each$4(children, function (child) { + var value = child.getValue(dimension); + value < dataExtent[0] && (dataExtent[0] = value); + value > dataExtent[1] && (dataExtent[1] = value); + }); + } + return { + sum: sum, + dataExtent: dataExtent + }; + } + /** + * Computes the score for the specified row, + * as the worst aspect ratio. + */ + function worst(row, rowFixedLength, ratio) { + var areaMax = 0; + var areaMin = Infinity; + for (var i = 0, area = void 0, len = row.length; i < len; i++) { + area = row[i].getLayout().area; + if (area) { + area < areaMin && (areaMin = area); + area > areaMax && (areaMax = area); + } + } + var squareArea = row.area * row.area; + var f = rowFixedLength * rowFixedLength * ratio; + return squareArea ? mathMax$7(f * areaMax / squareArea, squareArea / (f * areaMin)) : Infinity; + } + /** + * Positions the specified row of nodes. Modifies `rect`. + */ + function position(row, rowFixedLength, rect, halfGapWidth, flush) { + // When rowFixedLength === rect.width, + // it is horizontal subdivision, + // rowFixedLength is the width of the subdivision, + // rowOtherLength is the height of the subdivision, + // and nodes will be positioned from left to right. + // wh[idx0WhenH] means: when horizontal, + // wh[idx0WhenH] => wh[0] => 'width'. + // xy[idx1WhenH] => xy[1] => 'y'. + var idx0WhenH = rowFixedLength === rect.width ? 0 : 1; + var idx1WhenH = 1 - idx0WhenH; + var xy = ['x', 'y']; + var wh = ['width', 'height']; + var last = rect[xy[idx0WhenH]]; + var rowOtherLength = rowFixedLength ? row.area / rowFixedLength : 0; + if (flush || rowOtherLength > rect[wh[idx1WhenH]]) { + rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow + } + + for (var i = 0, rowLen = row.length; i < rowLen; i++) { + var node = row[i]; + var nodeLayout = {}; + var step = rowOtherLength ? node.getLayout().area / rowOtherLength : 0; + var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax$7(rowOtherLength - 2 * halfGapWidth, 0); + // We use Math.max/min to avoid negative width/height when considering gap width. + var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last; + var modWH = i === rowLen - 1 || remain < step ? remain : step; + var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax$7(modWH - 2 * halfGapWidth, 0); + nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin$7(halfGapWidth, wh1 / 2); + nodeLayout[xy[idx0WhenH]] = last + mathMin$7(halfGapWidth, wh0 / 2); + last += modWH; + node.setLayout(nodeLayout, true); + } + rect[xy[idx1WhenH]] += rowOtherLength; + rect[wh[idx1WhenH]] -= rowOtherLength; + } + // Return [containerWidth, containerHeight] as default. + function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) { + // If targetInfo.node exists, we zoom to the node, + // so estimate whole width and height by target node. + var currNode = (targetInfo || {}).node; + var defaultSize = [containerWidth, containerHeight]; + if (!currNode || currNode === viewRoot) { + return defaultSize; + } + var parent; + var viewArea = containerWidth * containerHeight; + var area = viewArea * seriesModel.option.zoomToNodeRatio; + while (parent = currNode.parentNode) { + // jshint ignore:line + var sum = 0; + var siblings = parent.children; + for (var i = 0, len = siblings.length; i < len; i++) { + sum += siblings[i].getValue(); + } + var currNodeValue = currNode.getValue(); + if (currNodeValue === 0) { + return defaultSize; + } + area *= sum / currNodeValue; + // Considering border, suppose aspect ratio is 1. + var parentModel = parent.getModel(); + var borderWidth = parentModel.get(PATH_BORDER_WIDTH); + var upperHeight = Math.max(borderWidth, getUpperLabelHeight(parentModel)); + area += 4 * borderWidth * borderWidth + (3 * borderWidth + upperHeight) * Math.pow(area, 0.5); + area > MAX_SAFE_INTEGER && (area = MAX_SAFE_INTEGER); + currNode = parent; + } + area < viewArea && (area = viewArea); + var scale = Math.pow(area / viewArea, 0.5); + return [containerWidth * scale, containerHeight * scale]; + } + // Root position based on coord of containerGroup + function calculateRootPosition(layoutInfo, rootRect, targetInfo) { + if (rootRect) { + return { + x: rootRect.x, + y: rootRect.y + }; + } + var defaultPosition = { + x: 0, + y: 0 + }; + if (!targetInfo) { + return defaultPosition; + } + // If targetInfo is fetched by 'retrieveTargetInfo', + // old tree and new tree are the same tree, + // so the node still exists and we can visit it. + var targetNode = targetInfo.node; + var layout = targetNode.getLayout(); + if (!layout) { + return defaultPosition; + } + // Transform coord from local to container. + var targetCenter = [layout.width / 2, layout.height / 2]; + var node = targetNode; + while (node) { + var nodeLayout = node.getLayout(); + targetCenter[0] += nodeLayout.x; + targetCenter[1] += nodeLayout.y; + node = node.parentNode; + } + return { + x: layoutInfo.width / 2 - targetCenter[0], + y: layoutInfo.height / 2 - targetCenter[1] + }; + } + // Mark nodes visible for prunning when visual coding and rendering. + // Prunning depends on layout and root position, so we have to do it after layout. + function prunning(node, clipRect, viewAbovePath, viewRoot, depth) { + var nodeLayout = node.getLayout(); + var nodeInViewAbovePath = viewAbovePath[depth]; + var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node; + if (nodeInViewAbovePath && !isAboveViewRoot || depth === viewAbovePath.length && node !== viewRoot) { + return; + } + node.setLayout({ + // isInView means: viewRoot sub tree + viewAbovePath + isInView: true, + // invisible only means: outside view clip so that the node can not + // see but still layout for animation preparation but not render. + invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout), + isAboveViewRoot: isAboveViewRoot + }, true); + // Transform to child coordinate. + var childClipRect = new BoundingRect(clipRect.x - nodeLayout.x, clipRect.y - nodeLayout.y, clipRect.width, clipRect.height); + each$4(node.viewChildren || [], function (child) { + prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1); + }); + } + function getUpperLabelHeight(model) { + return model.get(PATH_UPPER_LABEL_SHOW) ? model.get(PATH_UPPER_LABEL_HEIGHT) : 0; + } + + function install$c(registers) { + registers.registerSeriesModel(TreemapSeriesModel); + registers.registerChartView(TreemapView); + registers.registerVisual(treemapVisual); + registers.registerLayout(treemapLayout); + installTreemapAction(registers); + } + + function categoryFilter(ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + ecModel.eachSeriesByType('graph', function (graphSeries) { + var categoriesData = graphSeries.getCategoriesData(); + var graph = graphSeries.getGraph(); + var data = graph.data; + var categoryNames = categoriesData.mapArray(categoriesData.getName); + data.filterSelf(function (idx) { + var model = data.getItemModel(idx); + var category = model.getShallow('category'); + if (category != null) { + if (isNumber(category)) { + category = categoryNames[category]; + } + // If in any legend component the status is not selected. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(category)) { + return false; + } + } + } + return true; + }); + }); + } + + function categoryVisual(ecModel) { + var paletteScope = {}; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var categoriesData = seriesModel.getCategoriesData(); + var data = seriesModel.getData(); + var categoryNameIdxMap = {}; + categoriesData.each(function (idx) { + var name = categoriesData.getName(idx); + // Add prefix to avoid conflict with Object.prototype. + categoryNameIdxMap['ec-' + name] = idx; + var itemModel = categoriesData.getItemModel(idx); + var style = itemModel.getModel('itemStyle').getItemStyle(); + if (!style.fill) { + // Get color from palette. + style.fill = seriesModel.getColorFromPalette(name, paletteScope); + } + categoriesData.setItemVisual(idx, 'style', style); + var symbolVisualList = ['symbol', 'symbolSize', 'symbolKeepAspect']; + for (var i = 0; i < symbolVisualList.length; i++) { + var symbolVisual = itemModel.getShallow(symbolVisualList[i], true); + if (symbolVisual != null) { + categoriesData.setItemVisual(idx, symbolVisualList[i], symbolVisual); + } + } + }); + // Assign category color to visual + if (categoriesData.count()) { + data.each(function (idx) { + var model = data.getItemModel(idx); + var categoryIdx = model.getShallow('category'); + if (categoryIdx != null) { + if (isString(categoryIdx)) { + categoryIdx = categoryNameIdxMap['ec-' + categoryIdx]; + } + var categoryStyle = categoriesData.getItemVisual(categoryIdx, 'style'); + var style = data.ensureUniqueItemVisual(idx, 'style'); + extend(style, categoryStyle); + var visualList = ['symbol', 'symbolSize', 'symbolKeepAspect']; + for (var i = 0; i < visualList.length; i++) { + data.setItemVisual(idx, visualList[i], categoriesData.getItemVisual(categoryIdx, visualList[i])); + } + } + }); + } + }); + } + + function normalize$2(a) { + if (!(a instanceof Array)) { + a = [a, a]; + } + return a; + } + function graphEdgeVisual(ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var graph = seriesModel.getGraph(); + var edgeData = seriesModel.getEdgeData(); + var symbolType = normalize$2(seriesModel.get('edgeSymbol')); + var symbolSize = normalize$2(seriesModel.get('edgeSymbolSize')); + // const colorQuery = ['lineStyle', 'color'] as const; + // const opacityQuery = ['lineStyle', 'opacity'] as const; + edgeData.setVisual('fromSymbol', symbolType && symbolType[0]); + edgeData.setVisual('toSymbol', symbolType && symbolType[1]); + edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); + edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]); + edgeData.setVisual('style', seriesModel.getModel('lineStyle').getLineStyle()); + edgeData.each(function (idx) { + var itemModel = edgeData.getItemModel(idx); + var edge = graph.getEdgeByIndex(idx); + var symbolType = normalize$2(itemModel.getShallow('symbol', true)); + var symbolSize = normalize$2(itemModel.getShallow('symbolSize', true)); + // Edge visual must after node visual + var style = itemModel.getModel('lineStyle').getLineStyle(); + var existsStyle = edgeData.ensureUniqueItemVisual(idx, 'style'); + extend(existsStyle, style); + switch (existsStyle.stroke) { + case 'source': + { + var nodeStyle = edge.node1.getVisual('style'); + existsStyle.stroke = nodeStyle && nodeStyle.fill; + break; + } + case 'target': + { + var nodeStyle = edge.node2.getVisual('style'); + existsStyle.stroke = nodeStyle && nodeStyle.fill; + break; + } + } + symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]); + symbolType[1] && edge.setVisual('toSymbol', symbolType[1]); + symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]); + symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]); + }); + }); + } + + var KEY_DELIMITER = '-->'; + /** + * params handler + * @param {module:echarts/model/SeriesModel} seriesModel + * @returns {*} + */ + var getAutoCurvenessParams = function (seriesModel) { + return seriesModel.get('autoCurveness') || null; + }; + /** + * Generate a list of edge curvatures, 20 is the default + * @param {module:echarts/model/SeriesModel} seriesModel + * @param {number} appendLength + * @return 20 => [0, -0.2, 0.2, -0.4, 0.4, -0.6, 0.6, -0.8, 0.8, -1, 1, -1.2, 1.2, -1.4, 1.4, -1.6, 1.6, -1.8, 1.8, -2] + */ + var createCurveness = function (seriesModel, appendLength) { + var autoCurvenessParmas = getAutoCurvenessParams(seriesModel); + var length = 20; + var curvenessList = []; + // handler the function set + if (isNumber(autoCurvenessParmas)) { + length = autoCurvenessParmas; + } else if (isArray(autoCurvenessParmas)) { + seriesModel.__curvenessList = autoCurvenessParmas; + return; + } + // append length + if (appendLength > length) { + length = appendLength; + } + // make sure the length is even + var len = length % 2 ? length + 2 : length + 3; + curvenessList = []; + for (var i = 0; i < len; i++) { + curvenessList.push((i % 2 ? i + 1 : i) / 10 * (i % 2 ? -1 : 1)); + } + seriesModel.__curvenessList = curvenessList; + }; + /** + * Create different cache key data in the positive and negative directions, in order to set the curvature later + * @param {number|string|module:echarts/data/Graph.Node} n1 + * @param {number|string|module:echarts/data/Graph.Node} n2 + * @param {module:echarts/model/SeriesModel} seriesModel + * @returns {string} key + */ + var getKeyOfEdges = function (n1, n2, seriesModel) { + var source = [n1.id, n1.dataIndex].join('.'); + var target = [n2.id, n2.dataIndex].join('.'); + return [seriesModel.uid, source, target].join(KEY_DELIMITER); + }; + /** + * get opposite key + * @param {string} key + * @returns {string} + */ + var getOppositeKey = function (key) { + var keys = key.split(KEY_DELIMITER); + return [keys[0], keys[2], keys[1]].join(KEY_DELIMITER); + }; + /** + * get edgeMap with key + * @param edge + * @param {module:echarts/model/SeriesModel} seriesModel + */ + var getEdgeFromMap = function (edge, seriesModel) { + var key = getKeyOfEdges(edge.node1, edge.node2, seriesModel); + return seriesModel.__edgeMap[key]; + }; + /** + * calculate all cases total length + * @param edge + * @param seriesModel + * @returns {number} + */ + var getTotalLengthBetweenNodes = function (edge, seriesModel) { + var len = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node1, edge.node2, seriesModel), seriesModel); + var lenV = getEdgeMapLengthWithKey(getKeyOfEdges(edge.node2, edge.node1, seriesModel), seriesModel); + return len + lenV; + }; + /** + * + * @param key + */ + var getEdgeMapLengthWithKey = function (key, seriesModel) { + var edgeMap = seriesModel.__edgeMap; + return edgeMap[key] ? edgeMap[key].length : 0; + }; + /** + * Count the number of edges between the same two points, used to obtain the curvature table and the parity of the edge + * @see /graph/GraphSeries.js@getInitialData + * @param {module:echarts/model/SeriesModel} seriesModel + */ + function initCurvenessList(seriesModel) { + if (!getAutoCurvenessParams(seriesModel)) { + return; + } + seriesModel.__curvenessList = []; + seriesModel.__edgeMap = {}; + // calc the array of curveness List + createCurveness(seriesModel); + } + /** + * set edgeMap with key + * @param {number|string|module:echarts/data/Graph.Node} n1 + * @param {number|string|module:echarts/data/Graph.Node} n2 + * @param {module:echarts/model/SeriesModel} seriesModel + * @param {number} index + */ + function createEdgeMapForCurveness(n1, n2, seriesModel, index) { + if (!getAutoCurvenessParams(seriesModel)) { + return; + } + var key = getKeyOfEdges(n1, n2, seriesModel); + var edgeMap = seriesModel.__edgeMap; + var oppositeEdges = edgeMap[getOppositeKey(key)]; + // set direction + if (edgeMap[key] && !oppositeEdges) { + edgeMap[key].isForward = true; + } else if (oppositeEdges && edgeMap[key]) { + oppositeEdges.isForward = true; + edgeMap[key].isForward = false; + } + edgeMap[key] = edgeMap[key] || []; + edgeMap[key].push(index); + } + /** + * get curvature for edge + * @param edge + * @param {module:echarts/model/SeriesModel} seriesModel + * @param index + */ + function getCurvenessForEdge(edge, seriesModel, index, needReverse) { + var autoCurvenessParams = getAutoCurvenessParams(seriesModel); + var isArrayParam = isArray(autoCurvenessParams); + if (!autoCurvenessParams) { + return null; + } + var edgeArray = getEdgeFromMap(edge, seriesModel); + if (!edgeArray) { + return null; + } + var edgeIndex = -1; + for (var i = 0; i < edgeArray.length; i++) { + if (edgeArray[i] === index) { + edgeIndex = i; + break; + } + } + // if totalLen is Longer createCurveness + var totalLen = getTotalLengthBetweenNodes(edge, seriesModel); + createCurveness(seriesModel, totalLen); + edge.lineStyle = edge.lineStyle || {}; + // if is opposite edge, must set curvenss to opposite number + var curKey = getKeyOfEdges(edge.node1, edge.node2, seriesModel); + var curvenessList = seriesModel.__curvenessList; + // if pass array no need parity + var parityCorrection = isArrayParam ? 0 : totalLen % 2 ? 0 : 1; + if (!edgeArray.isForward) { + // the opposite edge show outside + var oppositeKey = getOppositeKey(curKey); + var len = getEdgeMapLengthWithKey(oppositeKey, seriesModel); + var resValue = curvenessList[edgeIndex + len + parityCorrection]; + // isNeedReverse, simple, force type need reverse the curveness in the junction of the forword and the opposite + if (needReverse) { + // set as array may make the parity handle with the len of opposite + if (isArrayParam) { + if (autoCurvenessParams && autoCurvenessParams[0] === 0) { + return (len + parityCorrection) % 2 ? resValue : -resValue; + } else { + return ((len % 2 ? 0 : 1) + parityCorrection) % 2 ? resValue : -resValue; + } + } else { + return (len + parityCorrection) % 2 ? resValue : -resValue; + } + } else { + return curvenessList[edgeIndex + len + parityCorrection]; + } + } else { + return curvenessList[parityCorrection + edgeIndex]; + } + } + + function simpleLayout(seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + var graph = seriesModel.getGraph(); + graph.eachNode(function (node) { + var model = node.getModel(); + node.setLayout([+model.get('x'), +model.get('y')]); + }); + simpleLayoutEdge(graph, seriesModel); + } + function simpleLayoutEdge(graph, seriesModel) { + graph.eachEdge(function (edge, index) { + var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), -getCurvenessForEdge(edge, seriesModel, index, true), 0); + var p1 = clone$1(edge.node1.getLayout()); + var p2 = clone$1(edge.node2.getLayout()); + var points = [p1, p2]; + if (+curveness) { + points.push([(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness, (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness]); + } + edge.setLayout(points); + }); + } + + function graphSimpleLayout(ecModel, api) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var layout = seriesModel.get('layout'); + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + var data_1 = seriesModel.getData(); + var dimensions_1 = []; + each(coordSys.dimensions, function (coordDim) { + dimensions_1 = dimensions_1.concat(data_1.mapDimensionsAll(coordDim)); + }); + for (var dataIndex = 0; dataIndex < data_1.count(); dataIndex++) { + var value = []; + var hasValue = false; + for (var i = 0; i < dimensions_1.length; i++) { + var val = data_1.get(dimensions_1[i], dataIndex); + if (!isNaN(val)) { + hasValue = true; + } + value.push(val); + } + if (hasValue) { + data_1.setItemLayout(dataIndex, coordSys.dataToPoint(value)); + } else { + // Also {Array.}, not undefined to avoid if...else... statement + data_1.setItemLayout(dataIndex, [NaN, NaN]); + } + } + simpleLayoutEdge(data_1.graph, seriesModel); + } else if (!layout || layout === 'none') { + simpleLayout(seriesModel); + } + }); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function getNodeGlobalScale(seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type !== 'view') { + return 1; + } + var nodeScaleRatio = seriesModel.option.nodeScaleRatio; + var groupZoom = coordSys.scaleX; + // Scale node when zoom changes + var roamZoom = coordSys.getZoom(); + var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1; + return nodeScale / groupZoom; + } + function getSymbolSize(node) { + var symbolSize = node.getVisual('symbolSize'); + if (symbolSize instanceof Array) { + symbolSize = (symbolSize[0] + symbolSize[1]) / 2; + } + return +symbolSize; + } + + var PI$6 = Math.PI; + var _symbolRadiansHalf = []; + /** + * `basedOn` can be: + * 'value': + * This layout is not accurate and have same bad case. For example, + * if the min value is very smaller than the max value, the nodes + * with the min value probably overlap even though there is enough + * space to layout them. So we only use this approach in the as the + * init layout of the force layout. + * FIXME + * Probably we do not need this method any more but use + * `basedOn: 'symbolSize'` in force layout if + * delay its init operations to GraphView. + * 'symbolSize': + * This approach work only if all of the symbol size calculated. + * That is, the progressive rendering is not applied to graph. + * FIXME + * If progressive rendering is applied to graph some day, + * probably we have to use `basedOn: 'value'`. + */ + function circularLayout(seriesModel, basedOn, draggingNode, pointer) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + var rect = coordSys.getBoundingRect(); + var nodeData = seriesModel.getData(); + var graph = nodeData.graph; + var cx = rect.width / 2 + rect.x; + var cy = rect.height / 2 + rect.y; + var r = Math.min(rect.width, rect.height) / 2; + var count = nodeData.count(); + nodeData.setLayout({ + cx: cx, + cy: cy + }); + if (!count) { + return; + } + if (draggingNode) { + var _a = coordSys.pointToData(pointer), + tempX = _a[0], + tempY = _a[1]; + var v = [tempX - cx, tempY - cy]; + normalize(v, v); + scale(v, v, r); + draggingNode.setLayout([cx + v[0], cy + v[1]], true); + var circularRotateLabel = seriesModel.get(['circular', 'rotateLabel']); + rotateNodeLabel(draggingNode, circularRotateLabel, cx, cy); + } + _layoutNodesBasedOn[basedOn](seriesModel, graph, nodeData, r, cx, cy, count); + graph.eachEdge(function (edge, index) { + var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), getCurvenessForEdge(edge, seriesModel, index), 0); + var p1 = clone$1(edge.node1.getLayout()); + var p2 = clone$1(edge.node2.getLayout()); + var cp1; + var x12 = (p1[0] + p2[0]) / 2; + var y12 = (p1[1] + p2[1]) / 2; + if (+curveness) { + curveness *= 3; + cp1 = [cx * curveness + x12 * (1 - curveness), cy * curveness + y12 * (1 - curveness)]; + } + edge.setLayout([p1, p2, cp1]); + }); + } + var _layoutNodesBasedOn = { + value: function (seriesModel, graph, nodeData, r, cx, cy, count) { + var angle = 0; + var sum = nodeData.getSum('value'); + var unitAngle = Math.PI * 2 / (sum || count); + graph.eachNode(function (node) { + var value = node.getValue('value'); + var radianHalf = unitAngle * (sum ? value : 1) / 2; + angle += radianHalf; + node.setLayout([r * Math.cos(angle) + cx, r * Math.sin(angle) + cy]); + angle += radianHalf; + }); + }, + symbolSize: function (seriesModel, graph, nodeData, r, cx, cy, count) { + var sumRadian = 0; + _symbolRadiansHalf.length = count; + var nodeScale = getNodeGlobalScale(seriesModel); + graph.eachNode(function (node) { + var symbolSize = getSymbolSize(node); + // Normally this case will not happen, but we still add + // some the defensive code (2px is an arbitrary value). + isNaN(symbolSize) && (symbolSize = 2); + symbolSize < 0 && (symbolSize = 0); + symbolSize *= nodeScale; + var symbolRadianHalf = Math.asin(symbolSize / 2 / r); + // when `symbolSize / 2` is bigger than `r`. + isNaN(symbolRadianHalf) && (symbolRadianHalf = PI$6 / 2); + _symbolRadiansHalf[node.dataIndex] = symbolRadianHalf; + sumRadian += symbolRadianHalf * 2; + }); + var halfRemainRadian = (2 * PI$6 - sumRadian) / count / 2; + var angle = 0; + graph.eachNode(function (node) { + var radianHalf = halfRemainRadian + _symbolRadiansHalf[node.dataIndex]; + angle += radianHalf; + // init circular layout for + // 1. layout undefined node + // 2. not fixed node + (!node.getLayout() || !node.getLayout().fixed) && node.setLayout([r * Math.cos(angle) + cx, r * Math.sin(angle) + cy]); + angle += radianHalf; + }); + } + }; + function rotateNodeLabel(node, circularRotateLabel, cx, cy) { + var el = node.getGraphicEl(); + // need to check if el exists. '-' value may not create node element. + if (!el) { + return; + } + var nodeModel = node.getModel(); + var labelRotate = nodeModel.get(['label', 'rotate']) || 0; + var symbolPath = el.getSymbolPath(); + if (circularRotateLabel) { + var pos = node.getLayout(); + var rad = Math.atan2(pos[1] - cy, pos[0] - cx); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + var isLeft = pos[0] < cx; + if (isLeft) { + rad = rad - Math.PI; + } + var textPosition = isLeft ? 'left' : 'right'; + symbolPath.setTextConfig({ + rotation: -rad, + position: textPosition, + origin: 'center' + }); + var emphasisState = symbolPath.ensureState('emphasis'); + extend(emphasisState.textConfig || (emphasisState.textConfig = {}), { + position: textPosition + }); + } else { + symbolPath.setTextConfig({ + rotation: labelRotate *= Math.PI / 180 + }); + } + } + + function graphCircularLayout(ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + if (seriesModel.get('layout') === 'circular') { + circularLayout(seriesModel, 'symbolSize'); + } + }); + } + + var scaleAndAdd$1 = scaleAndAdd; + // function adjacentNode(n, e) { + // return e.n1 === n ? e.n2 : e.n1; + // } + function forceLayout(inNodes, inEdges, opts) { + var nodes = inNodes; + var edges = inEdges; + var rect = opts.rect; + var width = rect.width; + var height = rect.height; + var center = [rect.x + width / 2, rect.y + height / 2]; + // let scale = opts.scale || 1; + var gravity = opts.gravity == null ? 0.1 : opts.gravity; + // for (let i = 0; i < edges.length; i++) { + // let e = edges[i]; + // let n1 = e.n1; + // let n2 = e.n2; + // n1.edges = n1.edges || []; + // n2.edges = n2.edges || []; + // n1.edges.push(e); + // n2.edges.push(e); + // } + // Init position + for (var i = 0; i < nodes.length; i++) { + var n = nodes[i]; + if (!n.p) { + n.p = create(width * (Math.random() - 0.5) + center[0], height * (Math.random() - 0.5) + center[1]); + } + n.pp = clone$1(n.p); + n.edges = null; + } + // Formula in 'Graph Drawing by Force-directed Placement' + // let k = scale * Math.sqrt(width * height / nodes.length); + // let k2 = k * k; + var initialFriction = opts.friction == null ? 0.6 : opts.friction; + var friction = initialFriction; + var beforeStepCallback; + var afterStepCallback; + return { + warmUp: function () { + friction = initialFriction * 0.8; + }, + setFixed: function (idx) { + nodes[idx].fixed = true; + }, + setUnfixed: function (idx) { + nodes[idx].fixed = false; + }, + /** + * Before step hook + */ + beforeStep: function (cb) { + beforeStepCallback = cb; + }, + /** + * After step hook + */ + afterStep: function (cb) { + afterStepCallback = cb; + }, + /** + * Some formulas were originally copied from "d3.js" + * https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/layout/force.js + * with some modifications made for this project. + * See the license statement at the head of this file. + */ + step: function (cb) { + beforeStepCallback && beforeStepCallback(nodes, edges); + var v12 = []; + var nLen = nodes.length; + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + if (e.ignoreForceLayout) { + continue; + } + var n1 = e.n1; + var n2 = e.n2; + sub(v12, n2.p, n1.p); + var d = len(v12) - e.d; + var w = n2.w / (n1.w + n2.w); + if (isNaN(w)) { + w = 0; + } + normalize(v12, v12); + !n1.fixed && scaleAndAdd$1(n1.p, n1.p, v12, w * d * friction); + !n2.fixed && scaleAndAdd$1(n2.p, n2.p, v12, -(1 - w) * d * friction); + } + // Gravity + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + sub(v12, center, n.p); + // let d = vec2.len(v12); + // vec2.scale(v12, v12, 1 / d); + // let gravityFactor = gravity; + scaleAndAdd$1(n.p, n.p, v12, gravity * friction); + } + } + // Repulsive + // PENDING + for (var i = 0; i < nLen; i++) { + var n1 = nodes[i]; + for (var j = i + 1; j < nLen; j++) { + var n2 = nodes[j]; + sub(v12, n2.p, n1.p); + var d = len(v12); + if (d === 0) { + // Random repulse + set(v12, Math.random() - 0.5, Math.random() - 0.5); + d = 1; + } + var repFact = (n1.rep + n2.rep) / d / d; + !n1.fixed && scaleAndAdd$1(n1.pp, n1.pp, v12, repFact); + !n2.fixed && scaleAndAdd$1(n2.pp, n2.pp, v12, -repFact); + } + } + var v = []; + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + sub(v, n.p, n.pp); + scaleAndAdd$1(n.p, n.p, v, friction); + copy(n.pp, n.p); + } + } + friction = friction * 0.992; + var finished = friction < 0.01; + afterStepCallback && afterStepCallback(nodes, edges, finished); + cb && cb(finished); + } + }; + } + + function graphForceLayout(ecModel) { + ecModel.eachSeriesByType('graph', function (graphSeries) { + var coordSys = graphSeries.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + if (graphSeries.get('layout') === 'force') { + var preservedPoints_1 = graphSeries.preservedPoints || {}; + var graph_1 = graphSeries.getGraph(); + var nodeData_1 = graph_1.data; + var edgeData = graph_1.edgeData; + var forceModel = graphSeries.getModel('force'); + var initLayout = forceModel.get('initLayout'); + if (graphSeries.preservedPoints) { + nodeData_1.each(function (idx) { + var id = nodeData_1.getId(idx); + nodeData_1.setItemLayout(idx, preservedPoints_1[id] || [NaN, NaN]); + }); + } else if (!initLayout || initLayout === 'none') { + simpleLayout(graphSeries); + } else if (initLayout === 'circular') { + circularLayout(graphSeries, 'value'); + } + var nodeDataExtent_1 = nodeData_1.getDataExtent('value'); + var edgeDataExtent_1 = edgeData.getDataExtent('value'); + // let edgeDataExtent = edgeData.getDataExtent('value'); + var repulsion = forceModel.get('repulsion'); + var edgeLength = forceModel.get('edgeLength'); + var repulsionArr_1 = isArray(repulsion) ? repulsion : [repulsion, repulsion]; + var edgeLengthArr_1 = isArray(edgeLength) ? edgeLength : [edgeLength, edgeLength]; + // Larger value has smaller length + edgeLengthArr_1 = [edgeLengthArr_1[1], edgeLengthArr_1[0]]; + var nodes_1 = nodeData_1.mapArray('value', function (value, idx) { + var point = nodeData_1.getItemLayout(idx); + var rep = linearMap(value, nodeDataExtent_1, repulsionArr_1); + if (isNaN(rep)) { + rep = (repulsionArr_1[0] + repulsionArr_1[1]) / 2; + } + return { + w: rep, + rep: rep, + fixed: nodeData_1.getItemModel(idx).get('fixed'), + p: !point || isNaN(point[0]) || isNaN(point[1]) ? null : point + }; + }); + var edges = edgeData.mapArray('value', function (value, idx) { + var edge = graph_1.getEdgeByIndex(idx); + var d = linearMap(value, edgeDataExtent_1, edgeLengthArr_1); + if (isNaN(d)) { + d = (edgeLengthArr_1[0] + edgeLengthArr_1[1]) / 2; + } + var edgeModel = edge.getModel(); + var curveness = retrieve3(edge.getModel().get(['lineStyle', 'curveness']), -getCurvenessForEdge(edge, graphSeries, idx, true), 0); + return { + n1: nodes_1[edge.node1.dataIndex], + n2: nodes_1[edge.node2.dataIndex], + d: d, + curveness: curveness, + ignoreForceLayout: edgeModel.get('ignoreForceLayout') + }; + }); + // let coordSys = graphSeries.coordinateSystem; + var rect = coordSys.getBoundingRect(); + var forceInstance = forceLayout(nodes_1, edges, { + rect: rect, + gravity: forceModel.get('gravity'), + friction: forceModel.get('friction') + }); + forceInstance.beforeStep(function (nodes, edges) { + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i].fixed) { + // Write back to layout instance + copy(nodes[i].p, graph_1.getNodeByIndex(i).getLayout()); + } + } + }); + forceInstance.afterStep(function (nodes, edges, stopped) { + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].fixed) { + graph_1.getNodeByIndex(i).setLayout(nodes[i].p); + } + preservedPoints_1[nodeData_1.getId(i)] = nodes[i].p; + } + for (var i = 0, l = edges.length; i < l; i++) { + var e = edges[i]; + var edge = graph_1.getEdgeByIndex(i); + var p1 = e.n1.p; + var p2 = e.n2.p; + var points = edge.getLayout(); + points = points ? points.slice() : []; + points[0] = points[0] || []; + points[1] = points[1] || []; + copy(points[0], p1); + copy(points[1], p2); + if (+e.curveness) { + points[2] = [(p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness, (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness]; + } + edge.setLayout(points); + } + }); + graphSeries.forceLayout = forceInstance; + graphSeries.preservedPoints = preservedPoints_1; + // Step to get the layout + forceInstance.step(); + } else { + // Remove prev injected forceLayout instance + graphSeries.forceLayout = null; + } + }); + } + + function getViewRect$2(seriesModel, api, aspect) { + var option = extend(seriesModel.getBoxLayoutParams(), { + aspect: aspect + }); + return getLayoutRect(option, { + width: api.getWidth(), + height: api.getHeight() + }); + } + function createViewCoordSys(ecModel, api) { + var viewList = []; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var coordSysType = seriesModel.get('coordinateSystem'); + if (!coordSysType || coordSysType === 'view') { + var data_1 = seriesModel.getData(); + var positions = data_1.mapArray(function (idx) { + var itemModel = data_1.getItemModel(idx); + return [+itemModel.get('x'), +itemModel.get('y')]; + }); + var min = []; + var max = []; + fromPoints(positions, min, max); + // If width or height is 0 + if (max[0] - min[0] === 0) { + max[0] += 1; + min[0] -= 1; + } + if (max[1] - min[1] === 0) { + max[1] += 1; + min[1] -= 1; + } + var aspect = (max[0] - min[0]) / (max[1] - min[1]); + // FIXME If get view rect after data processed? + var viewRect = getViewRect$2(seriesModel, api, aspect); + // Position may be NaN, use view rect instead + if (isNaN(aspect)) { + min = [viewRect.x, viewRect.y]; + max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height]; + } + var bbWidth = max[0] - min[0]; + var bbHeight = max[1] - min[1]; + var viewWidth = viewRect.width; + var viewHeight = viewRect.height; + var viewCoordSys = seriesModel.coordinateSystem = new View(); + viewCoordSys.zoomLimit = seriesModel.get('scaleLimit'); + viewCoordSys.setBoundingRect(min[0], min[1], bbWidth, bbHeight); + viewCoordSys.setViewRect(viewRect.x, viewRect.y, viewWidth, viewHeight); + // Update roam info + viewCoordSys.setCenter(seriesModel.get('center'), api); + viewCoordSys.setZoom(seriesModel.get('zoom')); + viewList.push(viewCoordSys); + } + }); + return viewList; + } + + var straightLineProto = Line.prototype; + var bezierCurveProto = BezierCurve.prototype; + var StraightLineShape = /** @class */function () { + function StraightLineShape() { + // Start point + this.x1 = 0; + this.y1 = 0; + // End point + this.x2 = 0; + this.y2 = 0; + this.percent = 1; + } + return StraightLineShape; + }(); + var CurveShape = /** @class */function (_super) { + __extends(CurveShape, _super); + function CurveShape() { + return _super !== null && _super.apply(this, arguments) || this; + } + return CurveShape; + }(StraightLineShape); + function isStraightLine(shape) { + return isNaN(+shape.cpx1) || isNaN(+shape.cpy1); + } + var ECLinePath = /** @class */function (_super) { + __extends(ECLinePath, _super); + function ECLinePath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'ec-line'; + return _this; + } + ECLinePath.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + ECLinePath.prototype.getDefaultShape = function () { + return new StraightLineShape(); + }; + ECLinePath.prototype.buildPath = function (ctx, shape) { + if (isStraightLine(shape)) { + straightLineProto.buildPath.call(this, ctx, shape); + } else { + bezierCurveProto.buildPath.call(this, ctx, shape); + } + }; + ECLinePath.prototype.pointAt = function (t) { + if (isStraightLine(this.shape)) { + return straightLineProto.pointAt.call(this, t); + } else { + return bezierCurveProto.pointAt.call(this, t); + } + }; + ECLinePath.prototype.tangentAt = function (t) { + var shape = this.shape; + var p = isStraightLine(shape) ? [shape.x2 - shape.x1, shape.y2 - shape.y1] : bezierCurveProto.tangentAt.call(this, t); + return normalize(p, p); + }; + return ECLinePath; + }(Path); + + var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol']; + function makeSymbolTypeKey(symbolCategory) { + return '_' + symbolCategory + 'Type'; + } + function makeSymbolTypeValue(name, lineData, idx) { + var symbolType = lineData.getItemVisual(idx, name); + if (!symbolType || symbolType === 'none') { + return symbolType; + } + var symbolSize = lineData.getItemVisual(idx, name + 'Size'); + var symbolRotate = lineData.getItemVisual(idx, name + 'Rotate'); + var symbolOffset = lineData.getItemVisual(idx, name + 'Offset'); + var symbolKeepAspect = lineData.getItemVisual(idx, name + 'KeepAspect'); + var symbolSizeArr = normalizeSymbolSize(symbolSize); + var symbolOffsetArr = normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr); + return symbolType + symbolSizeArr + symbolOffsetArr + (symbolRotate || '') + (symbolKeepAspect || ''); + } + /** + * @inner + */ + function createSymbol$1(name, lineData, idx) { + var symbolType = lineData.getItemVisual(idx, name); + if (!symbolType || symbolType === 'none') { + return; + } + var symbolSize = lineData.getItemVisual(idx, name + 'Size'); + var symbolRotate = lineData.getItemVisual(idx, name + 'Rotate'); + var symbolOffset = lineData.getItemVisual(idx, name + 'Offset'); + var symbolKeepAspect = lineData.getItemVisual(idx, name + 'KeepAspect'); + var symbolSizeArr = normalizeSymbolSize(symbolSize); + var symbolOffsetArr = normalizeSymbolOffset(symbolOffset || 0, symbolSizeArr); + var symbolPath = createSymbol(symbolType, -symbolSizeArr[0] / 2 + symbolOffsetArr[0], -symbolSizeArr[1] / 2 + symbolOffsetArr[1], symbolSizeArr[0], symbolSizeArr[1], null, symbolKeepAspect); + symbolPath.__specifiedRotation = symbolRotate == null || isNaN(symbolRotate) ? void 0 : +symbolRotate * Math.PI / 180 || 0; + symbolPath.name = name; + return symbolPath; + } + function createLine(points) { + var line = new ECLinePath({ + name: 'line', + subPixelOptimize: true + }); + setLinePoints(line.shape, points); + return line; + } + function setLinePoints(targetShape, points) { + targetShape.x1 = points[0][0]; + targetShape.y1 = points[0][1]; + targetShape.x2 = points[1][0]; + targetShape.y2 = points[1][1]; + targetShape.percent = 1; + var cp1 = points[2]; + if (cp1) { + targetShape.cpx1 = cp1[0]; + targetShape.cpy1 = cp1[1]; + } else { + targetShape.cpx1 = NaN; + targetShape.cpy1 = NaN; + } + } + var Line$1 = /** @class */function (_super) { + __extends(Line, _super); + function Line(lineData, idx, seriesScope) { + var _this = _super.call(this) || this; + _this._createLine(lineData, idx, seriesScope); + return _this; + } + Line.prototype._createLine = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + var linePoints = lineData.getItemLayout(idx); + var line = createLine(linePoints); + line.shape.percent = 0; + initProps(line, { + shape: { + percent: 1 + } + }, seriesModel, idx); + this.add(line); + each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = createSymbol$1(symbolCategory, lineData, idx); + // symbols must added after line to make sure + // it will be updated after line#update. + // Or symbol position and rotation update in line#beforeUpdate will be one frame slow + this.add(symbol); + this[makeSymbolTypeKey(symbolCategory)] = makeSymbolTypeValue(symbolCategory, lineData, idx); + }, this); + this._updateCommonStl(lineData, idx, seriesScope); + }; + // TODO More strict on the List type in parameters? + Line.prototype.updateData = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + var line = this.childOfName('line'); + var linePoints = lineData.getItemLayout(idx); + var target = { + shape: {} + }; + setLinePoints(target.shape, linePoints); + updateProps(line, target, seriesModel, idx); + each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbolType = makeSymbolTypeValue(symbolCategory, lineData, idx); + var key = makeSymbolTypeKey(symbolCategory); + // Symbol changed + if (this[key] !== symbolType) { + this.remove(this.childOfName(symbolCategory)); + var symbol = createSymbol$1(symbolCategory, lineData, idx); + this.add(symbol); + } + this[key] = symbolType; + }, this); + this._updateCommonStl(lineData, idx, seriesScope); + }; + Line.prototype.getLinePath = function () { + return this.childAt(0); + }; + Line.prototype._updateCommonStl = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + var line = this.childOfName('line'); + var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle; + var blurLineStyle = seriesScope && seriesScope.blurLineStyle; + var selectLineStyle = seriesScope && seriesScope.selectLineStyle; + var labelStatesModels = seriesScope && seriesScope.labelStatesModels; + var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled; + var focus = seriesScope && seriesScope.focus; + var blurScope = seriesScope && seriesScope.blurScope; + // Optimization for large dataset + if (!seriesScope || lineData.hasItemOption) { + var itemModel = lineData.getItemModel(idx); + var emphasisModel = itemModel.getModel('emphasis'); + emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle(); + blurLineStyle = itemModel.getModel(['blur', 'lineStyle']).getLineStyle(); + selectLineStyle = itemModel.getModel(['select', 'lineStyle']).getLineStyle(); + emphasisDisabled = emphasisModel.get('disabled'); + focus = emphasisModel.get('focus'); + blurScope = emphasisModel.get('blurScope'); + labelStatesModels = getLabelStatesModels(itemModel); + } + var lineStyle = lineData.getItemVisual(idx, 'style'); + var visualColor = lineStyle.stroke; + line.useStyle(lineStyle); + line.style.fill = null; + line.style.strokeNoScale = true; + line.ensureState('emphasis').style = emphasisLineStyle; + line.ensureState('blur').style = blurLineStyle; + line.ensureState('select').style = selectLineStyle; + // Update symbol + each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = this.childOfName(symbolCategory); + if (symbol) { + // Share opacity and color with line. + symbol.setColor(visualColor); + symbol.style.opacity = lineStyle.opacity; + for (var i = 0; i < SPECIAL_STATES.length; i++) { + var stateName = SPECIAL_STATES[i]; + var lineState = line.getState(stateName); + if (lineState) { + var lineStateStyle = lineState.style || {}; + var state = symbol.ensureState(stateName); + var stateStyle = state.style || (state.style = {}); + if (lineStateStyle.stroke != null) { + stateStyle[symbol.__isEmptyBrush ? 'stroke' : 'fill'] = lineStateStyle.stroke; + } + if (lineStateStyle.opacity != null) { + stateStyle.opacity = lineStateStyle.opacity; + } + } + } + symbol.markRedraw(); + } + }, this); + var rawVal = seriesModel.getRawValue(idx); + setLabelStyle(this, labelStatesModels, { + labelDataIndex: idx, + labelFetcher: { + getFormattedLabel: function (dataIndex, stateName) { + return seriesModel.getFormattedLabel(dataIndex, stateName, lineData.dataType); + } + }, + inheritColor: visualColor || '#000', + defaultOpacity: lineStyle.opacity, + defaultText: (rawVal == null ? lineData.getName(idx) : isFinite(rawVal) ? round(rawVal) : rawVal) + '' + }); + var label = this.getTextContent(); + // Always set `textStyle` even if `normalStyle.text` is null, because default + // values have to be set on `normalStyle`. + if (label) { + var labelNormalModel = labelStatesModels.normal; + label.__align = label.style.align; + label.__verticalAlign = label.style.verticalAlign; + // 'start', 'middle', 'end' + label.__position = labelNormalModel.get('position') || 'middle'; + var distance = labelNormalModel.get('distance'); + if (!isArray(distance)) { + distance = [distance, distance]; + } + label.__labelDistance = distance; + } + this.setTextConfig({ + position: null, + local: true, + inside: false // Can't be inside for stroke element. + }); + + toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled); + }; + Line.prototype.highlight = function () { + enterEmphasis(this); + }; + Line.prototype.downplay = function () { + leaveEmphasis(this); + }; + Line.prototype.updateLayout = function (lineData, idx) { + this.setLinePoints(lineData.getItemLayout(idx)); + }; + Line.prototype.setLinePoints = function (points) { + var linePath = this.childOfName('line'); + setLinePoints(linePath.shape, points); + linePath.dirty(); + }; + Line.prototype.beforeUpdate = function () { + var lineGroup = this; + var symbolFrom = lineGroup.childOfName('fromSymbol'); + var symbolTo = lineGroup.childOfName('toSymbol'); + var label = lineGroup.getTextContent(); + // Quick reject + if (!symbolFrom && !symbolTo && (!label || label.ignore)) { + return; + } + var invScale = 1; + var parentNode = this.parent; + while (parentNode) { + if (parentNode.scaleX) { + invScale /= parentNode.scaleX; + } + parentNode = parentNode.parent; + } + var line = lineGroup.childOfName('line'); + // If line not changed + // FIXME Parent scale changed + if (!this.__dirty && !line.__dirty) { + return; + } + var percent = line.shape.percent; + var fromPos = line.pointAt(0); + var toPos = line.pointAt(percent); + var d = sub([], toPos, fromPos); + normalize(d, d); + function setSymbolRotation(symbol, percent) { + // Fix #12388 + // when symbol is set to be 'arrow' in markLine, + // symbolRotate value will be ignored, and compulsively use tangent angle. + // rotate by default if symbol rotation is not specified + var specifiedRotation = symbol.__specifiedRotation; + if (specifiedRotation == null) { + var tangent = line.tangentAt(percent); + symbol.attr('rotation', (percent === 1 ? -1 : 1) * Math.PI / 2 - Math.atan2(tangent[1], tangent[0])); + } else { + symbol.attr('rotation', specifiedRotation); + } + } + if (symbolFrom) { + symbolFrom.setPosition(fromPos); + setSymbolRotation(symbolFrom, 0); + symbolFrom.scaleX = symbolFrom.scaleY = invScale * percent; + symbolFrom.markRedraw(); + } + if (symbolTo) { + symbolTo.setPosition(toPos); + setSymbolRotation(symbolTo, 1); + symbolTo.scaleX = symbolTo.scaleY = invScale * percent; + symbolTo.markRedraw(); + } + if (label && !label.ignore) { + label.x = label.y = 0; + label.originX = label.originY = 0; + var textAlign = void 0; + var textVerticalAlign = void 0; + var distance = label.__labelDistance; + var distanceX = distance[0] * invScale; + var distanceY = distance[1] * invScale; + var halfPercent = percent / 2; + var tangent = line.tangentAt(halfPercent); + var n = [tangent[1], -tangent[0]]; + var cp = line.pointAt(halfPercent); + if (n[1] > 0) { + n[0] = -n[0]; + n[1] = -n[1]; + } + var dir = tangent[0] < 0 ? -1 : 1; + if (label.__position !== 'start' && label.__position !== 'end') { + var rotation = -Math.atan2(tangent[1], tangent[0]); + if (toPos[0] < fromPos[0]) { + rotation = Math.PI + rotation; + } + label.rotation = rotation; + } + var dy = void 0; + switch (label.__position) { + case 'insideStartTop': + case 'insideMiddleTop': + case 'insideEndTop': + case 'middle': + dy = -distanceY; + textVerticalAlign = 'bottom'; + break; + case 'insideStartBottom': + case 'insideMiddleBottom': + case 'insideEndBottom': + dy = distanceY; + textVerticalAlign = 'top'; + break; + default: + dy = 0; + textVerticalAlign = 'middle'; + } + switch (label.__position) { + case 'end': + label.x = d[0] * distanceX + toPos[0]; + label.y = d[1] * distanceY + toPos[1]; + textAlign = d[0] > 0.8 ? 'left' : d[0] < -0.8 ? 'right' : 'center'; + textVerticalAlign = d[1] > 0.8 ? 'top' : d[1] < -0.8 ? 'bottom' : 'middle'; + break; + case 'start': + label.x = -d[0] * distanceX + fromPos[0]; + label.y = -d[1] * distanceY + fromPos[1]; + textAlign = d[0] > 0.8 ? 'right' : d[0] < -0.8 ? 'left' : 'center'; + textVerticalAlign = d[1] > 0.8 ? 'bottom' : d[1] < -0.8 ? 'top' : 'middle'; + break; + case 'insideStartTop': + case 'insideStart': + case 'insideStartBottom': + label.x = distanceX * dir + fromPos[0]; + label.y = fromPos[1] + dy; + textAlign = tangent[0] < 0 ? 'right' : 'left'; + label.originX = -distanceX * dir; + label.originY = -dy; + break; + case 'insideMiddleTop': + case 'insideMiddle': + case 'insideMiddleBottom': + case 'middle': + label.x = cp[0]; + label.y = cp[1] + dy; + textAlign = 'center'; + label.originY = -dy; + break; + case 'insideEndTop': + case 'insideEnd': + case 'insideEndBottom': + label.x = -distanceX * dir + toPos[0]; + label.y = toPos[1] + dy; + textAlign = tangent[0] >= 0 ? 'right' : 'left'; + label.originX = distanceX * dir; + label.originY = -dy; + break; + } + label.scaleX = label.scaleY = invScale; + label.setStyle({ + // Use the user specified text align and baseline first + verticalAlign: label.__verticalAlign || textVerticalAlign, + align: label.__align || textAlign + }); + } + }; + return Line; + }(Group); + + var LineDraw = /** @class */function () { + function LineDraw(LineCtor) { + this.group = new Group(); + this._LineCtor = LineCtor || Line$1; + } + LineDraw.prototype.updateData = function (lineData) { + var _this = this; + // Remove progressive els. + this._progressiveEls = null; + var lineDraw = this; + var group = lineDraw.group; + var oldLineData = lineDraw._lineData; + lineDraw._lineData = lineData; + // There is no oldLineData only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!oldLineData) { + group.removeAll(); + } + var seriesScope = makeSeriesScope$1(lineData); + lineData.diff(oldLineData).add(function (idx) { + _this._doAdd(lineData, idx, seriesScope); + }).update(function (newIdx, oldIdx) { + _this._doUpdate(oldLineData, lineData, oldIdx, newIdx, seriesScope); + }).remove(function (idx) { + group.remove(oldLineData.getItemGraphicEl(idx)); + }).execute(); + }; + LineDraw.prototype.updateLayout = function () { + var lineData = this._lineData; + // Do not support update layout in incremental mode. + if (!lineData) { + return; + } + lineData.eachItemGraphicEl(function (el, idx) { + el.updateLayout(lineData, idx); + }, this); + }; + LineDraw.prototype.incrementalPrepareUpdate = function (lineData) { + this._seriesScope = makeSeriesScope$1(lineData); + this._lineData = null; + this.group.removeAll(); + }; + LineDraw.prototype.incrementalUpdate = function (taskParams, lineData) { + this._progressiveEls = []; + function updateIncrementalAndHover(el) { + if (!el.isGroup && !isEffectObject(el)) { + el.incremental = true; + el.ensureState('emphasis').hoverLayer = true; + } + } + for (var idx = taskParams.start; idx < taskParams.end; idx++) { + var itemLayout = lineData.getItemLayout(idx); + if (lineNeedsDraw(itemLayout)) { + var el = new this._LineCtor(lineData, idx, this._seriesScope); + el.traverse(updateIncrementalAndHover); + this.group.add(el); + lineData.setItemGraphicEl(idx, el); + this._progressiveEls.push(el); + } + } + }; + LineDraw.prototype.remove = function () { + this.group.removeAll(); + }; + LineDraw.prototype.eachRendered = function (cb) { + traverseElements(this._progressiveEls || this.group, cb); + }; + LineDraw.prototype._doAdd = function (lineData, idx, seriesScope) { + var itemLayout = lineData.getItemLayout(idx); + if (!lineNeedsDraw(itemLayout)) { + return; + } + var el = new this._LineCtor(lineData, idx, seriesScope); + lineData.setItemGraphicEl(idx, el); + this.group.add(el); + }; + LineDraw.prototype._doUpdate = function (oldLineData, newLineData, oldIdx, newIdx, seriesScope) { + var itemEl = oldLineData.getItemGraphicEl(oldIdx); + if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) { + this.group.remove(itemEl); + return; + } + if (!itemEl) { + itemEl = new this._LineCtor(newLineData, newIdx, seriesScope); + } else { + itemEl.updateData(newLineData, newIdx, seriesScope); + } + newLineData.setItemGraphicEl(newIdx, itemEl); + this.group.add(itemEl); + }; + return LineDraw; + }(); + function isEffectObject(el) { + return el.animators && el.animators.length > 0; + } + function makeSeriesScope$1(lineData) { + var hostModel = lineData.hostModel; + var emphasisModel = hostModel.getModel('emphasis'); + return { + lineStyle: hostModel.getModel('lineStyle').getLineStyle(), + emphasisLineStyle: emphasisModel.getModel(['lineStyle']).getLineStyle(), + blurLineStyle: hostModel.getModel(['blur', 'lineStyle']).getLineStyle(), + selectLineStyle: hostModel.getModel(['select', 'lineStyle']).getLineStyle(), + emphasisDisabled: emphasisModel.get('disabled'), + blurScope: emphasisModel.get('blurScope'), + focus: emphasisModel.get('focus'), + labelStatesModels: getLabelStatesModels(hostModel) + }; + } + function isPointNaN(pt) { + return isNaN(pt[0]) || isNaN(pt[1]); + } + function lineNeedsDraw(pts) { + return pts && !isPointNaN(pts[0]) && !isPointNaN(pts[1]); + } + + var v1 = []; + var v2 = []; + var v3 = []; + var quadraticAt$1 = quadraticAt; + var v2DistSquare = distSquare; + var mathAbs$2 = Math.abs; + function intersectCurveCircle(curvePoints, center, radius) { + var p0 = curvePoints[0]; + var p1 = curvePoints[1]; + var p2 = curvePoints[2]; + var d = Infinity; + var t; + var radiusSquare = radius * radius; + var interval = 0.1; + for (var _t = 0.1; _t <= 0.9; _t += 0.1) { + v1[0] = quadraticAt$1(p0[0], p1[0], p2[0], _t); + v1[1] = quadraticAt$1(p0[1], p1[1], p2[1], _t); + var diff = mathAbs$2(v2DistSquare(v1, center) - radiusSquare); + if (diff < d) { + d = diff; + t = _t; + } + } + // Assume the segment is monotone,Find root through Bisection method + // At most 32 iteration + for (var i = 0; i < 32; i++) { + // let prev = t - interval; + var next = t + interval; + // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev); + // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev); + v2[0] = quadraticAt$1(p0[0], p1[0], p2[0], t); + v2[1] = quadraticAt$1(p0[1], p1[1], p2[1], t); + v3[0] = quadraticAt$1(p0[0], p1[0], p2[0], next); + v3[1] = quadraticAt$1(p0[1], p1[1], p2[1], next); + var diff = v2DistSquare(v2, center) - radiusSquare; + if (mathAbs$2(diff) < 1e-2) { + break; + } + // let prevDiff = v2DistSquare(v1, center) - radiusSquare; + var nextDiff = v2DistSquare(v3, center) - radiusSquare; + interval /= 2; + if (diff < 0) { + if (nextDiff >= 0) { + t = t + interval; + } else { + t = t - interval; + } + } else { + if (nextDiff >= 0) { + t = t - interval; + } else { + t = t + interval; + } + } + } + return t; + } + // Adjust edge to avoid + function adjustEdge(graph, scale) { + var tmp0 = []; + var quadraticSubdivide$1 = quadraticSubdivide; + var pts = [[], [], []]; + var pts2 = [[], []]; + var v = []; + scale /= 2; + graph.eachEdge(function (edge, idx) { + var linePoints = edge.getLayout(); + var fromSymbol = edge.getVisual('fromSymbol'); + var toSymbol = edge.getVisual('toSymbol'); + if (!linePoints.__original) { + linePoints.__original = [clone$1(linePoints[0]), clone$1(linePoints[1])]; + if (linePoints[2]) { + linePoints.__original.push(clone$1(linePoints[2])); + } + } + var originalPoints = linePoints.__original; + // Quadratic curve + if (linePoints[2] != null) { + copy(pts[0], originalPoints[0]); + copy(pts[1], originalPoints[2]); + copy(pts[2], originalPoints[1]); + if (fromSymbol && fromSymbol !== 'none') { + var symbolSize = getSymbolSize(edge.node1); + var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale); + // Subdivide and get the second + quadraticSubdivide$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[0][0] = tmp0[3]; + pts[1][0] = tmp0[4]; + quadraticSubdivide$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0); + pts[0][1] = tmp0[3]; + pts[1][1] = tmp0[4]; + } + if (toSymbol && toSymbol !== 'none') { + var symbolSize = getSymbolSize(edge.node2); + var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale); + // Subdivide and get the first + quadraticSubdivide$1(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[1][0] = tmp0[1]; + pts[2][0] = tmp0[2]; + quadraticSubdivide$1(pts[0][1], pts[1][1], pts[2][1], t, tmp0); + pts[1][1] = tmp0[1]; + pts[2][1] = tmp0[2]; + } + // Copy back to layout + copy(linePoints[0], pts[0]); + copy(linePoints[1], pts[2]); + copy(linePoints[2], pts[1]); + } + // Line + else { + copy(pts2[0], originalPoints[0]); + copy(pts2[1], originalPoints[1]); + sub(v, pts2[1], pts2[0]); + normalize(v, v); + if (fromSymbol && fromSymbol !== 'none') { + var symbolSize = getSymbolSize(edge.node1); + scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale); + } + if (toSymbol && toSymbol !== 'none') { + var symbolSize = getSymbolSize(edge.node2); + scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale); + } + copy(linePoints[0], pts2[0]); + copy(linePoints[1], pts2[1]); + } + }); + } + + function isViewCoordSys(coordSys) { + return coordSys.type === 'view'; + } + var GraphView = /** @class */function (_super) { + __extends(GraphView, _super); + function GraphView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GraphView.type; + return _this; + } + GraphView.prototype.init = function (ecModel, api) { + var symbolDraw = new SymbolDraw(); + var lineDraw = new LineDraw(); + var group = this.group; + this._controller = new RoamController(api.getZr()); + this._controllerHost = { + target: group + }; + group.add(symbolDraw.group); + group.add(lineDraw.group); + this._symbolDraw = symbolDraw; + this._lineDraw = lineDraw; + this._firstRender = true; + }; + GraphView.prototype.render = function (seriesModel, ecModel, api) { + var _this = this; + var coordSys = seriesModel.coordinateSystem; + this._model = seriesModel; + var symbolDraw = this._symbolDraw; + var lineDraw = this._lineDraw; + var group = this.group; + if (isViewCoordSys(coordSys)) { + var groupNewProp = { + x: coordSys.x, + y: coordSys.y, + scaleX: coordSys.scaleX, + scaleY: coordSys.scaleY + }; + if (this._firstRender) { + group.attr(groupNewProp); + } else { + updateProps(group, groupNewProp, seriesModel); + } + } + // Fix edge contact point with node + adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel)); + var data = seriesModel.getData(); + symbolDraw.updateData(data); + var edgeData = seriesModel.getEdgeData(); + // TODO: TYPE + lineDraw.updateData(edgeData); + this._updateNodeAndLinkScale(); + this._updateController(seriesModel, ecModel, api); + clearTimeout(this._layoutTimeout); + var forceLayout = seriesModel.forceLayout; + var layoutAnimation = seriesModel.get(['force', 'layoutAnimation']); + if (forceLayout) { + this._startForceLayoutIteration(forceLayout, layoutAnimation); + } + var layout = seriesModel.get('layout'); + data.graph.eachNode(function (node) { + var idx = node.dataIndex; + var el = node.getGraphicEl(); + var itemModel = node.getModel(); + if (!el) { + return; + } + // Update draggable + el.off('drag').off('dragend'); + var draggable = itemModel.get('draggable'); + if (draggable) { + el.on('drag', function (e) { + switch (layout) { + case 'force': + forceLayout.warmUp(); + !_this._layouting && _this._startForceLayoutIteration(forceLayout, layoutAnimation); + forceLayout.setFixed(idx); + // Write position back to layout + data.setItemLayout(idx, [el.x, el.y]); + break; + case 'circular': + data.setItemLayout(idx, [el.x, el.y]); + // mark node fixed + node.setLayout({ + fixed: true + }, true); + // recalculate circular layout + circularLayout(seriesModel, 'symbolSize', node, [e.offsetX, e.offsetY]); + _this.updateLayout(seriesModel); + break; + case 'none': + default: + data.setItemLayout(idx, [el.x, el.y]); + // update edge + simpleLayoutEdge(seriesModel.getGraph(), seriesModel); + _this.updateLayout(seriesModel); + break; + } + }).on('dragend', function () { + if (forceLayout) { + forceLayout.setUnfixed(idx); + } + }); + } + el.setDraggable(draggable, !!itemModel.get('cursor')); + var focus = itemModel.get(['emphasis', 'focus']); + if (focus === 'adjacency') { + getECData(el).focus = node.getAdjacentDataIndices(); + } + }); + data.graph.eachEdge(function (edge) { + var el = edge.getGraphicEl(); + var focus = edge.getModel().get(['emphasis', 'focus']); + if (!el) { + return; + } + if (focus === 'adjacency') { + getECData(el).focus = { + edge: [edge.dataIndex], + node: [edge.node1.dataIndex, edge.node2.dataIndex] + }; + } + }); + var circularRotateLabel = seriesModel.get('layout') === 'circular' && seriesModel.get(['circular', 'rotateLabel']); + var cx = data.getLayout('cx'); + var cy = data.getLayout('cy'); + data.graph.eachNode(function (node) { + rotateNodeLabel(node, circularRotateLabel, cx, cy); + }); + this._firstRender = false; + }; + GraphView.prototype.dispose = function () { + this.remove(); + this._controller && this._controller.dispose(); + this._controllerHost = null; + }; + GraphView.prototype._startForceLayoutIteration = function (forceLayout, layoutAnimation) { + var self = this; + (function step() { + forceLayout.step(function (stopped) { + self.updateLayout(self._model); + (self._layouting = !stopped) && (layoutAnimation ? self._layoutTimeout = setTimeout(step, 16) : step()); + }); + })(); + }; + GraphView.prototype._updateController = function (seriesModel, ecModel, api) { + var _this = this; + var controller = this._controller; + var controllerHost = this._controllerHost; + var group = this.group; + controller.setPointerChecker(function (e, x, y) { + var rect = group.getBoundingRect(); + rect.applyTransform(group.transform); + return rect.contain(x, y) && !onIrrelevantElement(e, api, seriesModel); + }); + if (!isViewCoordSys(seriesModel.coordinateSystem)) { + controller.disable(); + return; + } + controller.enable(seriesModel.get('roam')); + controllerHost.zoomLimit = seriesModel.get('scaleLimit'); + controllerHost.zoom = seriesModel.coordinateSystem.getZoom(); + controller.off('pan').off('zoom').on('pan', function (e) { + updateViewOnPan(controllerHost, e.dx, e.dy); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + dx: e.dx, + dy: e.dy + }); + }).on('zoom', function (e) { + updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY); + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + zoom: e.scale, + originX: e.originX, + originY: e.originY + }); + _this._updateNodeAndLinkScale(); + adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel)); + _this._lineDraw.updateLayout(); + // Only update label layout on zoom + api.updateLabelLayout(); + }); + }; + GraphView.prototype._updateNodeAndLinkScale = function () { + var seriesModel = this._model; + var data = seriesModel.getData(); + var nodeScale = getNodeGlobalScale(seriesModel); + data.eachItemGraphicEl(function (el, idx) { + el && el.setSymbolScale(nodeScale); + }); + }; + GraphView.prototype.updateLayout = function (seriesModel) { + adjustEdge(seriesModel.getGraph(), getNodeGlobalScale(seriesModel)); + this._symbolDraw.updateLayout(); + this._lineDraw.updateLayout(); + }; + GraphView.prototype.remove = function () { + clearTimeout(this._layoutTimeout); + this._layouting = false; + this._layoutTimeout = null; + this._symbolDraw && this._symbolDraw.remove(); + this._lineDraw && this._lineDraw.remove(); + }; + GraphView.type = 'graph'; + return GraphView; + }(ChartView); + + // id may be function name of Object, add a prefix to avoid this problem. + function generateNodeKey(id) { + return '_EC_' + id; + } + var Graph = /** @class */function () { + function Graph(directed) { + this.type = 'graph'; + this.nodes = []; + this.edges = []; + this._nodesMap = {}; + /** + * @type {Object.} + * @private + */ + this._edgesMap = {}; + this._directed = directed || false; + } + /** + * If is directed graph + */ + Graph.prototype.isDirected = function () { + return this._directed; + }; + /** + * Add a new node + */ + Graph.prototype.addNode = function (id, dataIndex) { + id = id == null ? '' + dataIndex : '' + id; + var nodesMap = this._nodesMap; + if (nodesMap[generateNodeKey(id)]) { + if ("development" !== 'production') { + console.error('Graph nodes have duplicate name or id'); + } + return; + } + var node = new GraphNode(id, dataIndex); + node.hostGraph = this; + this.nodes.push(node); + nodesMap[generateNodeKey(id)] = node; + return node; + }; + /** + * Get node by data index + */ + Graph.prototype.getNodeByIndex = function (dataIndex) { + var rawIdx = this.data.getRawIndex(dataIndex); + return this.nodes[rawIdx]; + }; + /** + * Get node by id + */ + Graph.prototype.getNodeById = function (id) { + return this._nodesMap[generateNodeKey(id)]; + }; + /** + * Add a new edge + */ + Graph.prototype.addEdge = function (n1, n2, dataIndex) { + var nodesMap = this._nodesMap; + var edgesMap = this._edgesMap; + // PENDING + if (isNumber(n1)) { + n1 = this.nodes[n1]; + } + if (isNumber(n2)) { + n2 = this.nodes[n2]; + } + if (!(n1 instanceof GraphNode)) { + n1 = nodesMap[generateNodeKey(n1)]; + } + if (!(n2 instanceof GraphNode)) { + n2 = nodesMap[generateNodeKey(n2)]; + } + if (!n1 || !n2) { + return; + } + var key = n1.id + '-' + n2.id; + var edge = new GraphEdge(n1, n2, dataIndex); + edge.hostGraph = this; + if (this._directed) { + n1.outEdges.push(edge); + n2.inEdges.push(edge); + } + n1.edges.push(edge); + if (n1 !== n2) { + n2.edges.push(edge); + } + this.edges.push(edge); + edgesMap[key] = edge; + return edge; + }; + /** + * Get edge by data index + */ + Graph.prototype.getEdgeByIndex = function (dataIndex) { + var rawIdx = this.edgeData.getRawIndex(dataIndex); + return this.edges[rawIdx]; + }; + /** + * Get edge by two linked nodes + */ + Graph.prototype.getEdge = function (n1, n2) { + if (n1 instanceof GraphNode) { + n1 = n1.id; + } + if (n2 instanceof GraphNode) { + n2 = n2.id; + } + var edgesMap = this._edgesMap; + if (this._directed) { + return edgesMap[n1 + '-' + n2]; + } else { + return edgesMap[n1 + '-' + n2] || edgesMap[n2 + '-' + n1]; + } + }; + /** + * Iterate all nodes + */ + Graph.prototype.eachNode = function (cb, context) { + var nodes = this.nodes; + var len = nodes.length; + for (var i = 0; i < len; i++) { + if (nodes[i].dataIndex >= 0) { + cb.call(context, nodes[i], i); + } + } + }; + /** + * Iterate all edges + */ + Graph.prototype.eachEdge = function (cb, context) { + var edges = this.edges; + var len = edges.length; + for (var i = 0; i < len; i++) { + if (edges[i].dataIndex >= 0 && edges[i].node1.dataIndex >= 0 && edges[i].node2.dataIndex >= 0) { + cb.call(context, edges[i], i); + } + } + }; + /** + * Breadth first traverse + * Return true to stop traversing + */ + Graph.prototype.breadthFirstTraverse = function (cb, startNode, direction, context) { + if (!(startNode instanceof GraphNode)) { + startNode = this._nodesMap[generateNodeKey(startNode)]; + } + if (!startNode) { + return; + } + var edgeType = direction === 'out' ? 'outEdges' : direction === 'in' ? 'inEdges' : 'edges'; + for (var i = 0; i < this.nodes.length; i++) { + this.nodes[i].__visited = false; + } + if (cb.call(context, startNode, null)) { + return; + } + var queue = [startNode]; + while (queue.length) { + var currentNode = queue.shift(); + var edges = currentNode[edgeType]; + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + var otherNode = e.node1 === currentNode ? e.node2 : e.node1; + if (!otherNode.__visited) { + if (cb.call(context, otherNode, currentNode)) { + // Stop traversing + return; + } + queue.push(otherNode); + otherNode.__visited = true; + } + } + } + }; + // TODO + // depthFirstTraverse( + // cb, startNode, direction, context + // ) { + // }; + // Filter update + Graph.prototype.update = function () { + var data = this.data; + var edgeData = this.edgeData; + var nodes = this.nodes; + var edges = this.edges; + for (var i = 0, len = nodes.length; i < len; i++) { + nodes[i].dataIndex = -1; + } + for (var i = 0, len = data.count(); i < len; i++) { + nodes[data.getRawIndex(i)].dataIndex = i; + } + edgeData.filterSelf(function (idx) { + var edge = edges[edgeData.getRawIndex(idx)]; + return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0; + }); + // Update edge + for (var i = 0, len = edges.length; i < len; i++) { + edges[i].dataIndex = -1; + } + for (var i = 0, len = edgeData.count(); i < len; i++) { + edges[edgeData.getRawIndex(i)].dataIndex = i; + } + }; + /** + * @return {module:echarts/data/Graph} + */ + Graph.prototype.clone = function () { + var graph = new Graph(this._directed); + var nodes = this.nodes; + var edges = this.edges; + for (var i = 0; i < nodes.length; i++) { + graph.addNode(nodes[i].id, nodes[i].dataIndex); + } + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + graph.addEdge(e.node1.id, e.node2.id, e.dataIndex); + } + return graph; + }; + return Graph; + }(); + var GraphNode = /** @class */function () { + function GraphNode(id, dataIndex) { + this.inEdges = []; + this.outEdges = []; + this.edges = []; + this.dataIndex = -1; + this.id = id == null ? '' : id; + this.dataIndex = dataIndex == null ? -1 : dataIndex; + } + /** + * @return {number} + */ + GraphNode.prototype.degree = function () { + return this.edges.length; + }; + /** + * @return {number} + */ + GraphNode.prototype.inDegree = function () { + return this.inEdges.length; + }; + /** + * @return {number} + */ + GraphNode.prototype.outDegree = function () { + return this.outEdges.length; + }; + GraphNode.prototype.getModel = function (path) { + if (this.dataIndex < 0) { + return; + } + var graph = this.hostGraph; + var itemModel = graph.data.getItemModel(this.dataIndex); + return itemModel.getModel(path); + }; + GraphNode.prototype.getAdjacentDataIndices = function () { + var dataIndices = { + edge: [], + node: [] + }; + for (var i = 0; i < this.edges.length; i++) { + var adjacentEdge = this.edges[i]; + if (adjacentEdge.dataIndex < 0) { + continue; + } + dataIndices.edge.push(adjacentEdge.dataIndex); + dataIndices.node.push(adjacentEdge.node1.dataIndex, adjacentEdge.node2.dataIndex); + } + return dataIndices; + }; + GraphNode.prototype.getTrajectoryDataIndices = function () { + var connectedEdgesMap = createHashMap(); + var connectedNodesMap = createHashMap(); + for (var i = 0; i < this.edges.length; i++) { + var adjacentEdge = this.edges[i]; + if (adjacentEdge.dataIndex < 0) { + continue; + } + connectedEdgesMap.set(adjacentEdge.dataIndex, true); + var sourceNodesQueue = [adjacentEdge.node1]; + var targetNodesQueue = [adjacentEdge.node2]; + var nodeIteratorIndex = 0; + while (nodeIteratorIndex < sourceNodesQueue.length) { + var sourceNode = sourceNodesQueue[nodeIteratorIndex]; + nodeIteratorIndex++; + connectedNodesMap.set(sourceNode.dataIndex, true); + for (var j = 0; j < sourceNode.inEdges.length; j++) { + connectedEdgesMap.set(sourceNode.inEdges[j].dataIndex, true); + sourceNodesQueue.push(sourceNode.inEdges[j].node1); + } + } + nodeIteratorIndex = 0; + while (nodeIteratorIndex < targetNodesQueue.length) { + var targetNode = targetNodesQueue[nodeIteratorIndex]; + nodeIteratorIndex++; + connectedNodesMap.set(targetNode.dataIndex, true); + for (var j = 0; j < targetNode.outEdges.length; j++) { + connectedEdgesMap.set(targetNode.outEdges[j].dataIndex, true); + targetNodesQueue.push(targetNode.outEdges[j].node2); + } + } + } + return { + edge: connectedEdgesMap.keys(), + node: connectedNodesMap.keys() + }; + }; + return GraphNode; + }(); + var GraphEdge = /** @class */function () { + function GraphEdge(n1, n2, dataIndex) { + this.dataIndex = -1; + this.node1 = n1; + this.node2 = n2; + this.dataIndex = dataIndex == null ? -1 : dataIndex; + } + // eslint-disable-next-line @typescript-eslint/no-unused-vars + GraphEdge.prototype.getModel = function (path) { + if (this.dataIndex < 0) { + return; + } + var graph = this.hostGraph; + var itemModel = graph.edgeData.getItemModel(this.dataIndex); + return itemModel.getModel(path); + }; + GraphEdge.prototype.getAdjacentDataIndices = function () { + return { + edge: [this.dataIndex], + node: [this.node1.dataIndex, this.node2.dataIndex] + }; + }; + GraphEdge.prototype.getTrajectoryDataIndices = function () { + var connectedEdgesMap = createHashMap(); + var connectedNodesMap = createHashMap(); + connectedEdgesMap.set(this.dataIndex, true); + var sourceNodes = [this.node1]; + var targetNodes = [this.node2]; + var nodeIteratorIndex = 0; + while (nodeIteratorIndex < sourceNodes.length) { + var sourceNode = sourceNodes[nodeIteratorIndex]; + nodeIteratorIndex++; + connectedNodesMap.set(sourceNode.dataIndex, true); + for (var j = 0; j < sourceNode.inEdges.length; j++) { + connectedEdgesMap.set(sourceNode.inEdges[j].dataIndex, true); + sourceNodes.push(sourceNode.inEdges[j].node1); + } + } + nodeIteratorIndex = 0; + while (nodeIteratorIndex < targetNodes.length) { + var targetNode = targetNodes[nodeIteratorIndex]; + nodeIteratorIndex++; + connectedNodesMap.set(targetNode.dataIndex, true); + for (var j = 0; j < targetNode.outEdges.length; j++) { + connectedEdgesMap.set(targetNode.outEdges[j].dataIndex, true); + targetNodes.push(targetNode.outEdges[j].node2); + } + } + return { + edge: connectedEdgesMap.keys(), + node: connectedNodesMap.keys() + }; + }; + return GraphEdge; + }(); + function createGraphDataProxyMixin(hostName, dataName) { + return { + /** + * @param Default 'value'. can be 'a', 'b', 'c', 'd', 'e'. + */ + getValue: function (dimension) { + var data = this[hostName][dataName]; + return data.getStore().get(data.getDimensionIndex(dimension || 'value'), this.dataIndex); + }, + // TODO: TYPE stricter type. + setVisual: function (key, value) { + this.dataIndex >= 0 && this[hostName][dataName].setItemVisual(this.dataIndex, key, value); + }, + getVisual: function (key) { + return this[hostName][dataName].getItemVisual(this.dataIndex, key); + }, + setLayout: function (layout, merge) { + this.dataIndex >= 0 && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge); + }, + getLayout: function () { + return this[hostName][dataName].getItemLayout(this.dataIndex); + }, + getGraphicEl: function () { + return this[hostName][dataName].getItemGraphicEl(this.dataIndex); + }, + getRawIndex: function () { + return this[hostName][dataName].getRawIndex(this.dataIndex); + } + }; + } + mixin(GraphNode, createGraphDataProxyMixin('hostGraph', 'data')); + mixin(GraphEdge, createGraphDataProxyMixin('hostGraph', 'edgeData')); + + function createGraphFromNodeEdge(nodes, edges, seriesModel, directed, beforeLink) { + // ??? TODO + // support dataset? + var graph = new Graph(directed); + for (var i = 0; i < nodes.length; i++) { + graph.addNode(retrieve( + // Id, name, dataIndex + nodes[i].id, nodes[i].name, i), i); + } + var linkNameList = []; + var validEdges = []; + var linkCount = 0; + for (var i = 0; i < edges.length; i++) { + var link = edges[i]; + var source = link.source; + var target = link.target; + // addEdge may fail when source or target not exists + if (graph.addEdge(source, target, linkCount)) { + validEdges.push(link); + linkNameList.push(retrieve(convertOptionIdName(link.id, null), source + ' > ' + target)); + linkCount++; + } + } + var coordSys = seriesModel.get('coordinateSystem'); + var nodeData; + if (coordSys === 'cartesian2d' || coordSys === 'polar') { + nodeData = createSeriesData(nodes, seriesModel); + } else { + var coordSysCtor = CoordinateSystemManager.get(coordSys); + var coordDimensions = coordSysCtor ? coordSysCtor.dimensions || [] : []; + // FIXME: Some geo do not need `value` dimenson, whereas `calendar` needs + // `value` dimension, but graph need `value` dimension. It's better to + // uniform this behavior. + if (indexOf(coordDimensions, 'value') < 0) { + coordDimensions.concat(['value']); + } + var dimensions = prepareSeriesDataSchema(nodes, { + coordDimensions: coordDimensions, + encodeDefine: seriesModel.getEncode() + }).dimensions; + nodeData = new SeriesData(dimensions, seriesModel); + nodeData.initData(nodes); + } + var edgeData = new SeriesData(['value'], seriesModel); + edgeData.initData(validEdges, linkNameList); + beforeLink && beforeLink(nodeData, edgeData); + linkSeriesData({ + mainData: nodeData, + struct: graph, + structAttr: 'graph', + datas: { + node: nodeData, + edge: edgeData + }, + datasAttr: { + node: 'data', + edge: 'edgeData' + } + }); + // Update dataIndex of nodes and edges because invalid edge may be removed + graph.update(); + return graph; + } + + var GraphSeriesModel = /** @class */function (_super) { + __extends(GraphSeriesModel, _super); + function GraphSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GraphSeriesModel.type; + _this.hasSymbolVisual = true; + return _this; + } + GraphSeriesModel.prototype.init = function (option) { + _super.prototype.init.apply(this, arguments); + var self = this; + function getCategoriesData() { + return self._categoriesData; + } + // Provide data for legend select + this.legendVisualProvider = new LegendVisualProvider(getCategoriesData, getCategoriesData); + this.fillDataTextStyle(option.edges || option.links); + this._updateCategoriesData(); + }; + GraphSeriesModel.prototype.mergeOption = function (option) { + _super.prototype.mergeOption.apply(this, arguments); + this.fillDataTextStyle(option.edges || option.links); + this._updateCategoriesData(); + }; + GraphSeriesModel.prototype.mergeDefaultAndTheme = function (option) { + _super.prototype.mergeDefaultAndTheme.apply(this, arguments); + defaultEmphasis(option, 'edgeLabel', ['show']); + }; + GraphSeriesModel.prototype.getInitialData = function (option, ecModel) { + var edges = option.edges || option.links || []; + var nodes = option.data || option.nodes || []; + var self = this; + if (nodes && edges) { + // auto curveness + initCurvenessList(this); + var graph = createGraphFromNodeEdge(nodes, edges, this, true, beforeLink); + each(graph.edges, function (edge) { + createEdgeMapForCurveness(edge.node1, edge.node2, this, edge.dataIndex); + }, this); + return graph.data; + } + function beforeLink(nodeData, edgeData) { + // Overwrite nodeData.getItemModel to + nodeData.wrapMethod('getItemModel', function (model) { + var categoriesModels = self._categoriesModels; + var categoryIdx = model.getShallow('category'); + var categoryModel = categoriesModels[categoryIdx]; + if (categoryModel) { + categoryModel.parentModel = model.parentModel; + model.parentModel = categoryModel; + } + return model; + }); + // TODO Inherit resolveParentPath by default in Model#getModel? + var oldGetModel = Model.prototype.getModel; + function newGetModel(path, parentModel) { + var model = oldGetModel.call(this, path, parentModel); + model.resolveParentPath = resolveParentPath; + return model; + } + edgeData.wrapMethod('getItemModel', function (model) { + model.resolveParentPath = resolveParentPath; + model.getModel = newGetModel; + return model; + }); + function resolveParentPath(pathArr) { + if (pathArr && (pathArr[0] === 'label' || pathArr[1] === 'label')) { + var newPathArr = pathArr.slice(); + if (pathArr[0] === 'label') { + newPathArr[0] = 'edgeLabel'; + } else if (pathArr[1] === 'label') { + newPathArr[1] = 'edgeLabel'; + } + return newPathArr; + } + return pathArr; + } + } + }; + GraphSeriesModel.prototype.getGraph = function () { + return this.getData().graph; + }; + GraphSeriesModel.prototype.getEdgeData = function () { + return this.getGraph().edgeData; + }; + GraphSeriesModel.prototype.getCategoriesData = function () { + return this._categoriesData; + }; + GraphSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + if (dataType === 'edge') { + var nodeData = this.getData(); + var params = this.getDataParams(dataIndex, dataType); + var edge = nodeData.graph.getEdgeByIndex(dataIndex); + var sourceName = nodeData.getName(edge.node1.dataIndex); + var targetName = nodeData.getName(edge.node2.dataIndex); + var nameArr = []; + sourceName != null && nameArr.push(sourceName); + targetName != null && nameArr.push(targetName); + return createTooltipMarkup('nameValue', { + name: nameArr.join(' > '), + value: params.value, + noValue: params.value == null + }); + } + // dataType === 'node' or empty + var nodeMarkup = defaultSeriesFormatTooltip({ + series: this, + dataIndex: dataIndex, + multipleSeries: multipleSeries + }); + return nodeMarkup; + }; + GraphSeriesModel.prototype._updateCategoriesData = function () { + var categories = map(this.option.categories || [], function (category) { + // Data must has value + return category.value != null ? category : extend({ + value: 0 + }, category); + }); + var categoriesData = new SeriesData(['value'], this); + categoriesData.initData(categories); + this._categoriesData = categoriesData; + this._categoriesModels = categoriesData.mapArray(function (idx) { + return categoriesData.getItemModel(idx); + }); + }; + GraphSeriesModel.prototype.setZoom = function (zoom) { + this.option.zoom = zoom; + }; + GraphSeriesModel.prototype.setCenter = function (center) { + this.option.center = center; + }; + GraphSeriesModel.prototype.isAnimationEnabled = function () { + return _super.prototype.isAnimationEnabled.call(this) + // Not enable animation when do force layout + && !(this.get('layout') === 'force' && this.get(['force', 'layoutAnimation'])); + }; + GraphSeriesModel.type = 'series.graph'; + GraphSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar']; + GraphSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'view', + // Default option for all coordinate systems + // xAxisIndex: 0, + // yAxisIndex: 0, + // polarIndex: 0, + // geoIndex: 0, + legendHoverLink: true, + layout: null, + // Configuration of circular layout + circular: { + rotateLabel: false + }, + // Configuration of force directed layout + force: { + initLayout: null, + // Node repulsion. Can be an array to represent range. + repulsion: [0, 50], + gravity: 0.1, + // Initial friction + friction: 0.6, + // Edge length. Can be an array to represent range. + edgeLength: 30, + layoutAnimation: true + }, + left: 'center', + top: 'center', + // right: null, + // bottom: null, + // width: '80%', + // height: '80%', + symbol: 'circle', + symbolSize: 10, + edgeSymbol: ['none', 'none'], + edgeSymbolSize: 10, + edgeLabel: { + position: 'middle', + distance: 5 + }, + draggable: false, + roam: false, + // Default on center of graph + center: null, + zoom: 1, + // Symbol size scale ratio in roam + nodeScaleRatio: 0.6, + // cursor: null, + // categories: [], + // data: [] + // Or + // nodes: [] + // + // links: [] + // Or + // edges: [] + label: { + show: false, + formatter: '{b}' + }, + itemStyle: {}, + lineStyle: { + color: '#aaa', + width: 1, + opacity: 0.5 + }, + emphasis: { + scale: true, + label: { + show: true + } + }, + select: { + itemStyle: { + borderColor: '#212121' + } + } + }; + return GraphSeriesModel; + }(SeriesModel); + + var actionInfo = { + type: 'graphRoam', + event: 'graphRoam', + update: 'none' + }; + function install$d(registers) { + registers.registerChartView(GraphView); + registers.registerSeriesModel(GraphSeriesModel); + registers.registerProcessor(categoryFilter); + registers.registerVisual(categoryVisual); + registers.registerVisual(graphEdgeVisual); + registers.registerLayout(graphSimpleLayout); + registers.registerLayout(registers.PRIORITY.VISUAL.POST_CHART_LAYOUT, graphCircularLayout); + registers.registerLayout(graphForceLayout); + registers.registerCoordinateSystem('graphView', { + dimensions: View.dimensions, + create: createViewCoordSys + }); + // Register legacy focus actions + registers.registerAction({ + type: 'focusNodeAdjacency', + event: 'focusNodeAdjacency', + update: 'series:focusNodeAdjacency' + }, noop); + registers.registerAction({ + type: 'unfocusNodeAdjacency', + event: 'unfocusNodeAdjacency', + update: 'series:unfocusNodeAdjacency' + }, noop); + // Register roam action. + registers.registerAction(actionInfo, function (payload, ecModel, api) { + ecModel.eachComponent({ + mainType: 'series', + query: payload + }, function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var res = updateCenterAndZoom(coordSys, payload, undefined, api); + seriesModel.setCenter && seriesModel.setCenter(res.center); + seriesModel.setZoom && seriesModel.setZoom(res.zoom); + }); + }); + } + + var PointerShape = /** @class */function () { + function PointerShape() { + this.angle = 0; + this.width = 10; + this.r = 10; + this.x = 0; + this.y = 0; + } + return PointerShape; + }(); + var PointerPath = /** @class */function (_super) { + __extends(PointerPath, _super); + function PointerPath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'pointer'; + return _this; + } + PointerPath.prototype.getDefaultShape = function () { + return new PointerShape(); + }; + PointerPath.prototype.buildPath = function (ctx, shape) { + var mathCos = Math.cos; + var mathSin = Math.sin; + var r = shape.r; + var width = shape.width; + var angle = shape.angle; + var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2); + var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2); + angle = shape.angle - Math.PI / 2; + ctx.moveTo(x, y); + ctx.lineTo(shape.x + mathCos(angle) * width, shape.y + mathSin(angle) * width); + ctx.lineTo(shape.x + mathCos(shape.angle) * r, shape.y + mathSin(shape.angle) * r); + ctx.lineTo(shape.x - mathCos(angle) * width, shape.y - mathSin(angle) * width); + ctx.lineTo(x, y); + }; + return PointerPath; + }(Path); + + function parsePosition(seriesModel, api) { + var center = seriesModel.get('center'); + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent$1(center[0], api.getWidth()); + var cy = parsePercent$1(center[1], api.getHeight()); + var r = parsePercent$1(seriesModel.get('radius'), size / 2); + return { + cx: cx, + cy: cy, + r: r + }; + } + function formatLabel(value, labelFormatter) { + var label = value == null ? '' : value + ''; + if (labelFormatter) { + if (isString(labelFormatter)) { + label = labelFormatter.replace('{value}', label); + } else if (isFunction(labelFormatter)) { + label = labelFormatter(value); + } + } + return label; + } + var GaugeView = /** @class */function (_super) { + __extends(GaugeView, _super); + function GaugeView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GaugeView.type; + return _this; + } + GaugeView.prototype.render = function (seriesModel, ecModel, api) { + this.group.removeAll(); + var colorList = seriesModel.get(['axisLine', 'lineStyle', 'color']); + var posInfo = parsePosition(seriesModel, api); + this._renderMain(seriesModel, ecModel, api, colorList, posInfo); + this._data = seriesModel.getData(); + }; + GaugeView.prototype.dispose = function () {}; + GaugeView.prototype._renderMain = function (seriesModel, ecModel, api, colorList, posInfo) { + var group = this.group; + var clockwise = seriesModel.get('clockwise'); + var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI; + var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI; + var axisLineModel = seriesModel.getModel('axisLine'); + var roundCap = axisLineModel.get('roundCap'); + var MainPath = roundCap ? SausagePath : Sector; + var showAxis = axisLineModel.get('show'); + var lineStyleModel = axisLineModel.getModel('lineStyle'); + var axisLineWidth = lineStyleModel.get('width'); + var angles = [startAngle, endAngle]; + normalizeArcAngles(angles, !clockwise); + startAngle = angles[0]; + endAngle = angles[1]; + var angleRangeSpan = endAngle - startAngle; + var prevEndAngle = startAngle; + var sectors = []; + for (var i = 0; showAxis && i < colorList.length; i++) { + // Clamp + var percent = Math.min(Math.max(colorList[i][0], 0), 1); + endAngle = startAngle + angleRangeSpan * percent; + var sector = new MainPath({ + shape: { + startAngle: prevEndAngle, + endAngle: endAngle, + cx: posInfo.cx, + cy: posInfo.cy, + clockwise: clockwise, + r0: posInfo.r - axisLineWidth, + r: posInfo.r + }, + silent: true + }); + sector.setStyle({ + fill: colorList[i][1] + }); + sector.setStyle(lineStyleModel.getLineStyle( + // Because we use sector to simulate arc + // so the properties for stroking are useless + ['color', 'width'])); + sectors.push(sector); + prevEndAngle = endAngle; + } + sectors.reverse(); + each(sectors, function (sector) { + return group.add(sector); + }); + var getColor = function (percent) { + // Less than 0 + if (percent <= 0) { + return colorList[0][1]; + } + var i; + for (i = 0; i < colorList.length; i++) { + if (colorList[i][0] >= percent && (i === 0 ? 0 : colorList[i - 1][0]) < percent) { + return colorList[i][1]; + } + } + // More than 1 + return colorList[i - 1][1]; + }; + this._renderTicks(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth); + this._renderTitleAndDetail(seriesModel, ecModel, api, getColor, posInfo); + this._renderAnchor(seriesModel, posInfo); + this._renderPointer(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth); + }; + GaugeView.prototype._renderTicks = function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth) { + var group = this.group; + var cx = posInfo.cx; + var cy = posInfo.cy; + var r = posInfo.r; + var minVal = +seriesModel.get('min'); + var maxVal = +seriesModel.get('max'); + var splitLineModel = seriesModel.getModel('splitLine'); + var tickModel = seriesModel.getModel('axisTick'); + var labelModel = seriesModel.getModel('axisLabel'); + var splitNumber = seriesModel.get('splitNumber'); + var subSplitNumber = tickModel.get('splitNumber'); + var splitLineLen = parsePercent$1(splitLineModel.get('length'), r); + var tickLen = parsePercent$1(tickModel.get('length'), r); + var angle = startAngle; + var step = (endAngle - startAngle) / splitNumber; + var subStep = step / subSplitNumber; + var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle(); + var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle(); + var splitLineDistance = splitLineModel.get('distance'); + var unitX; + var unitY; + for (var i = 0; i <= splitNumber; i++) { + unitX = Math.cos(angle); + unitY = Math.sin(angle); + // Split line + if (splitLineModel.get('show')) { + var distance = splitLineDistance ? splitLineDistance + axisLineWidth : axisLineWidth; + var splitLine = new Line({ + shape: { + x1: unitX * (r - distance) + cx, + y1: unitY * (r - distance) + cy, + x2: unitX * (r - splitLineLen - distance) + cx, + y2: unitY * (r - splitLineLen - distance) + cy + }, + style: splitLineStyle, + silent: true + }); + if (splitLineStyle.stroke === 'auto') { + splitLine.setStyle({ + stroke: getColor(i / splitNumber) + }); + } + group.add(splitLine); + } + // Label + if (labelModel.get('show')) { + var distance = labelModel.get('distance') + splitLineDistance; + var label = formatLabel(round(i / splitNumber * (maxVal - minVal) + minVal), labelModel.get('formatter')); + var autoColor = getColor(i / splitNumber); + var textStyleX = unitX * (r - splitLineLen - distance) + cx; + var textStyleY = unitY * (r - splitLineLen - distance) + cy; + var rotateType = labelModel.get('rotate'); + var rotate = 0; + if (rotateType === 'radial') { + rotate = -angle + 2 * Math.PI; + if (rotate > Math.PI / 2) { + rotate += Math.PI; + } + } else if (rotateType === 'tangential') { + rotate = -angle - Math.PI / 2; + } else if (isNumber(rotateType)) { + rotate = rotateType * Math.PI / 180; + } + if (rotate === 0) { + group.add(new ZRText({ + style: createTextStyle(labelModel, { + text: label, + x: textStyleX, + y: textStyleY, + verticalAlign: unitY < -0.8 ? 'top' : unitY > 0.8 ? 'bottom' : 'middle', + align: unitX < -0.4 ? 'left' : unitX > 0.4 ? 'right' : 'center' + }, { + inheritColor: autoColor + }), + silent: true + })); + } else { + group.add(new ZRText({ + style: createTextStyle(labelModel, { + text: label, + x: textStyleX, + y: textStyleY, + verticalAlign: 'middle', + align: 'center' + }, { + inheritColor: autoColor + }), + silent: true, + originX: textStyleX, + originY: textStyleY, + rotation: rotate + })); + } + } + // Axis tick + if (tickModel.get('show') && i !== splitNumber) { + var distance = tickModel.get('distance'); + distance = distance ? distance + axisLineWidth : axisLineWidth; + for (var j = 0; j <= subSplitNumber; j++) { + unitX = Math.cos(angle); + unitY = Math.sin(angle); + var tickLine = new Line({ + shape: { + x1: unitX * (r - distance) + cx, + y1: unitY * (r - distance) + cy, + x2: unitX * (r - tickLen - distance) + cx, + y2: unitY * (r - tickLen - distance) + cy + }, + silent: true, + style: tickLineStyle + }); + if (tickLineStyle.stroke === 'auto') { + tickLine.setStyle({ + stroke: getColor((i + j / subSplitNumber) / splitNumber) + }); + } + group.add(tickLine); + angle += subStep; + } + angle -= subStep; + } else { + angle += step; + } + } + }; + GaugeView.prototype._renderPointer = function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise, axisLineWidth) { + var group = this.group; + var oldData = this._data; + var oldProgressData = this._progressEls; + var progressList = []; + var showPointer = seriesModel.get(['pointer', 'show']); + var progressModel = seriesModel.getModel('progress'); + var showProgress = progressModel.get('show'); + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + var minVal = +seriesModel.get('min'); + var maxVal = +seriesModel.get('max'); + var valueExtent = [minVal, maxVal]; + var angleExtent = [startAngle, endAngle]; + function createPointer(idx, angle) { + var itemModel = data.getItemModel(idx); + var pointerModel = itemModel.getModel('pointer'); + var pointerWidth = parsePercent$1(pointerModel.get('width'), posInfo.r); + var pointerLength = parsePercent$1(pointerModel.get('length'), posInfo.r); + var pointerStr = seriesModel.get(['pointer', 'icon']); + var pointerOffset = pointerModel.get('offsetCenter'); + var pointerOffsetX = parsePercent$1(pointerOffset[0], posInfo.r); + var pointerOffsetY = parsePercent$1(pointerOffset[1], posInfo.r); + var pointerKeepAspect = pointerModel.get('keepAspect'); + var pointer; + // not exist icon type will be set 'rect' + if (pointerStr) { + pointer = createSymbol(pointerStr, pointerOffsetX - pointerWidth / 2, pointerOffsetY - pointerLength, pointerWidth, pointerLength, null, pointerKeepAspect); + } else { + pointer = new PointerPath({ + shape: { + angle: -Math.PI / 2, + width: pointerWidth, + r: pointerLength, + x: pointerOffsetX, + y: pointerOffsetY + } + }); + } + pointer.rotation = -(angle + Math.PI / 2); + pointer.x = posInfo.cx; + pointer.y = posInfo.cy; + return pointer; + } + function createProgress(idx, endAngle) { + var roundCap = progressModel.get('roundCap'); + var ProgressPath = roundCap ? SausagePath : Sector; + var isOverlap = progressModel.get('overlap'); + var progressWidth = isOverlap ? progressModel.get('width') : axisLineWidth / data.count(); + var r0 = isOverlap ? posInfo.r - progressWidth : posInfo.r - (idx + 1) * progressWidth; + var r = isOverlap ? posInfo.r : posInfo.r - idx * progressWidth; + var progress = new ProgressPath({ + shape: { + startAngle: startAngle, + endAngle: endAngle, + cx: posInfo.cx, + cy: posInfo.cy, + clockwise: clockwise, + r0: r0, + r: r + } + }); + isOverlap && (progress.z2 = maxVal - data.get(valueDim, idx) % maxVal); + return progress; + } + if (showProgress || showPointer) { + data.diff(oldData).add(function (idx) { + var val = data.get(valueDim, idx); + if (showPointer) { + var pointer = createPointer(idx, startAngle); + // TODO hide pointer on NaN value? + initProps(pointer, { + rotation: -((isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) + Math.PI / 2) + }, seriesModel); + group.add(pointer); + data.setItemGraphicEl(idx, pointer); + } + if (showProgress) { + var progress = createProgress(idx, startAngle); + var isClip = progressModel.get('clip'); + initProps(progress, { + shape: { + endAngle: linearMap(val, valueExtent, angleExtent, isClip) + } + }, seriesModel); + group.add(progress); + // Add data index and series index for indexing the data by element + // Useful in tooltip + setCommonECData(seriesModel.seriesIndex, data.dataType, idx, progress); + progressList[idx] = progress; + } + }).update(function (newIdx, oldIdx) { + var val = data.get(valueDim, newIdx); + if (showPointer) { + var previousPointer = oldData.getItemGraphicEl(oldIdx); + var previousRotate = previousPointer ? previousPointer.rotation : startAngle; + var pointer = createPointer(newIdx, previousRotate); + pointer.rotation = previousRotate; + updateProps(pointer, { + rotation: -((isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) + Math.PI / 2) + }, seriesModel); + group.add(pointer); + data.setItemGraphicEl(newIdx, pointer); + } + if (showProgress) { + var previousProgress = oldProgressData[oldIdx]; + var previousEndAngle = previousProgress ? previousProgress.shape.endAngle : startAngle; + var progress = createProgress(newIdx, previousEndAngle); + var isClip = progressModel.get('clip'); + updateProps(progress, { + shape: { + endAngle: linearMap(val, valueExtent, angleExtent, isClip) + } + }, seriesModel); + group.add(progress); + // Add data index and series index for indexing the data by element + // Useful in tooltip + setCommonECData(seriesModel.seriesIndex, data.dataType, newIdx, progress); + progressList[newIdx] = progress; + } + }).execute(); + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var emphasisModel = itemModel.getModel('emphasis'); + var focus = emphasisModel.get('focus'); + var blurScope = emphasisModel.get('blurScope'); + var emphasisDisabled = emphasisModel.get('disabled'); + if (showPointer) { + var pointer = data.getItemGraphicEl(idx); + var symbolStyle = data.getItemVisual(idx, 'style'); + var visualColor = symbolStyle.fill; + if (pointer instanceof ZRImage) { + var pathStyle = pointer.style; + pointer.useStyle(extend({ + image: pathStyle.image, + x: pathStyle.x, + y: pathStyle.y, + width: pathStyle.width, + height: pathStyle.height + }, symbolStyle)); + } else { + pointer.useStyle(symbolStyle); + pointer.type !== 'pointer' && pointer.setColor(visualColor); + } + pointer.setStyle(itemModel.getModel(['pointer', 'itemStyle']).getItemStyle()); + if (pointer.style.fill === 'auto') { + pointer.setStyle('fill', getColor(linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true))); + } + pointer.z2EmphasisLift = 0; + setStatesStylesFromModel(pointer, itemModel); + toggleHoverEmphasis(pointer, focus, blurScope, emphasisDisabled); + } + if (showProgress) { + var progress = progressList[idx]; + progress.useStyle(data.getItemVisual(idx, 'style')); + progress.setStyle(itemModel.getModel(['progress', 'itemStyle']).getItemStyle()); + progress.z2EmphasisLift = 0; + setStatesStylesFromModel(progress, itemModel); + toggleHoverEmphasis(progress, focus, blurScope, emphasisDisabled); + } + }); + this._progressEls = progressList; + } + }; + GaugeView.prototype._renderAnchor = function (seriesModel, posInfo) { + var anchorModel = seriesModel.getModel('anchor'); + var showAnchor = anchorModel.get('show'); + if (showAnchor) { + var anchorSize = anchorModel.get('size'); + var anchorType = anchorModel.get('icon'); + var offsetCenter = anchorModel.get('offsetCenter'); + var anchorKeepAspect = anchorModel.get('keepAspect'); + var anchor = createSymbol(anchorType, posInfo.cx - anchorSize / 2 + parsePercent$1(offsetCenter[0], posInfo.r), posInfo.cy - anchorSize / 2 + parsePercent$1(offsetCenter[1], posInfo.r), anchorSize, anchorSize, null, anchorKeepAspect); + anchor.z2 = anchorModel.get('showAbove') ? 1 : 0; + anchor.setStyle(anchorModel.getModel('itemStyle').getItemStyle()); + this.group.add(anchor); + } + }; + GaugeView.prototype._renderTitleAndDetail = function (seriesModel, ecModel, api, getColor, posInfo) { + var _this = this; + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + var minVal = +seriesModel.get('min'); + var maxVal = +seriesModel.get('max'); + var contentGroup = new Group(); + var newTitleEls = []; + var newDetailEls = []; + var hasAnimation = seriesModel.isAnimationEnabled(); + var showPointerAbove = seriesModel.get(['pointer', 'showAbove']); + data.diff(this._data).add(function (idx) { + newTitleEls[idx] = new ZRText({ + silent: true + }); + newDetailEls[idx] = new ZRText({ + silent: true + }); + }).update(function (idx, oldIdx) { + newTitleEls[idx] = _this._titleEls[oldIdx]; + newDetailEls[idx] = _this._detailEls[oldIdx]; + }).execute(); + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var value = data.get(valueDim, idx); + var itemGroup = new Group(); + var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true)); + var itemTitleModel = itemModel.getModel('title'); + if (itemTitleModel.get('show')) { + var titleOffsetCenter = itemTitleModel.get('offsetCenter'); + var titleX = posInfo.cx + parsePercent$1(titleOffsetCenter[0], posInfo.r); + var titleY = posInfo.cy + parsePercent$1(titleOffsetCenter[1], posInfo.r); + var labelEl = newTitleEls[idx]; + labelEl.attr({ + z2: showPointerAbove ? 0 : 2, + style: createTextStyle(itemTitleModel, { + x: titleX, + y: titleY, + text: data.getName(idx), + align: 'center', + verticalAlign: 'middle' + }, { + inheritColor: autoColor + }) + }); + itemGroup.add(labelEl); + } + var itemDetailModel = itemModel.getModel('detail'); + if (itemDetailModel.get('show')) { + var detailOffsetCenter = itemDetailModel.get('offsetCenter'); + var detailX = posInfo.cx + parsePercent$1(detailOffsetCenter[0], posInfo.r); + var detailY = posInfo.cy + parsePercent$1(detailOffsetCenter[1], posInfo.r); + var width = parsePercent$1(itemDetailModel.get('width'), posInfo.r); + var height = parsePercent$1(itemDetailModel.get('height'), posInfo.r); + var detailColor = seriesModel.get(['progress', 'show']) ? data.getItemVisual(idx, 'style').fill : autoColor; + var labelEl = newDetailEls[idx]; + var formatter_1 = itemDetailModel.get('formatter'); + labelEl.attr({ + z2: showPointerAbove ? 0 : 2, + style: createTextStyle(itemDetailModel, { + x: detailX, + y: detailY, + text: formatLabel(value, formatter_1), + width: isNaN(width) ? null : width, + height: isNaN(height) ? null : height, + align: 'center', + verticalAlign: 'middle' + }, { + inheritColor: detailColor + }) + }); + setLabelValueAnimation(labelEl, { + normal: itemDetailModel + }, value, function (value) { + return formatLabel(value, formatter_1); + }); + hasAnimation && animateLabelValue(labelEl, idx, data, seriesModel, { + getFormattedLabel: function (labelDataIndex, status, dataType, labelDimIndex, fmt, extendParams) { + return formatLabel(extendParams ? extendParams.interpolatedValue : value, formatter_1); + } + }); + itemGroup.add(labelEl); + } + contentGroup.add(itemGroup); + }); + this.group.add(contentGroup); + this._titleEls = newTitleEls; + this._detailEls = newDetailEls; + }; + GaugeView.type = 'gauge'; + return GaugeView; + }(ChartView); + + var GaugeSeriesModel = /** @class */function (_super) { + __extends(GaugeSeriesModel, _super); + function GaugeSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GaugeSeriesModel.type; + _this.visualStyleAccessPath = 'itemStyle'; + return _this; + } + GaugeSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesDataSimply(this, ['value']); + }; + GaugeSeriesModel.type = 'series.gauge'; + GaugeSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + colorBy: 'data', + // 默认全局居中 + center: ['50%', '50%'], + legendHoverLink: true, + radius: '75%', + startAngle: 225, + endAngle: -45, + clockwise: true, + // 最小值 + min: 0, + // 最大值 + max: 100, + // 分割段数,默认为10 + splitNumber: 10, + // 坐标轴线 + axisLine: { + // 默认显示,属性show控制显示与否 + show: true, + roundCap: false, + lineStyle: { + color: [[1, '#E6EBF8']], + width: 10 + } + }, + // 坐标轴线 + progress: { + // 默认显示,属性show控制显示与否 + show: false, + overlap: true, + width: 10, + roundCap: false, + clip: true + }, + // 分隔线 + splitLine: { + // 默认显示,属性show控制显示与否 + show: true, + // 属性length控制线长 + length: 10, + distance: 10, + // 属性lineStyle(详见lineStyle)控制线条样式 + lineStyle: { + color: '#63677A', + width: 3, + type: 'solid' + } + }, + // 坐标轴小标记 + axisTick: { + // 属性show控制显示与否,默认不显示 + show: true, + // 每份split细分多少段 + splitNumber: 5, + // 属性length控制线长 + length: 6, + distance: 10, + // 属性lineStyle控制线条样式 + lineStyle: { + color: '#63677A', + width: 1, + type: 'solid' + } + }, + axisLabel: { + show: true, + distance: 15, + // formatter: null, + color: '#464646', + fontSize: 12, + rotate: 0 + }, + pointer: { + icon: null, + offsetCenter: [0, 0], + show: true, + showAbove: true, + length: '60%', + width: 6, + keepAspect: false + }, + anchor: { + show: false, + showAbove: false, + size: 6, + icon: 'circle', + offsetCenter: [0, 0], + keepAspect: false, + itemStyle: { + color: '#fff', + borderWidth: 0, + borderColor: '#5470c6' + } + }, + title: { + show: true, + // x, y,单位px + offsetCenter: [0, '20%'], + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#464646', + fontSize: 16, + valueAnimation: false + }, + detail: { + show: true, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 0, + borderColor: '#ccc', + width: 100, + height: null, + padding: [5, 10], + // x, y,单位px + offsetCenter: [0, '40%'], + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#464646', + fontSize: 30, + fontWeight: 'bold', + lineHeight: 30, + valueAnimation: false + } + }; + return GaugeSeriesModel; + }(SeriesModel); + + function install$e(registers) { + registers.registerChartView(GaugeView); + registers.registerSeriesModel(GaugeSeriesModel); + } + + var opacityAccessPath = ['itemStyle', 'opacity']; + /** + * Piece of pie including Sector, Label, LabelLine + */ + var FunnelPiece = /** @class */function (_super) { + __extends(FunnelPiece, _super); + function FunnelPiece(data, idx) { + var _this = _super.call(this) || this; + var polygon = _this; + var labelLine = new Polyline(); + var text = new ZRText(); + polygon.setTextContent(text); + _this.setTextGuideLine(labelLine); + _this.updateData(data, idx, true); + return _this; + } + FunnelPiece.prototype.updateData = function (data, idx, firstCreate) { + var polygon = this; + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var emphasisModel = itemModel.getModel('emphasis'); + var opacity = itemModel.get(opacityAccessPath); + opacity = opacity == null ? 1 : opacity; + if (!firstCreate) { + saveOldStyle(polygon); + } + // Update common style + polygon.useStyle(data.getItemVisual(idx, 'style')); + polygon.style.lineJoin = 'round'; + if (firstCreate) { + polygon.setShape({ + points: layout.points + }); + polygon.style.opacity = 0; + initProps(polygon, { + style: { + opacity: opacity + } + }, seriesModel, idx); + } else { + updateProps(polygon, { + style: { + opacity: opacity + }, + shape: { + points: layout.points + } + }, seriesModel, idx); + } + setStatesStylesFromModel(polygon, itemModel); + this._updateLabel(data, idx); + toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }; + FunnelPiece.prototype._updateLabel = function (data, idx) { + var polygon = this; + var labelLine = this.getTextGuideLine(); + var labelText = polygon.getTextContent(); + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var labelLayout = layout.label; + var style = data.getItemVisual(idx, 'style'); + var visualColor = style.fill; + setLabelStyle( + // position will not be used in setLabelStyle + labelText, getLabelStatesModels(itemModel), { + labelFetcher: data.hostModel, + labelDataIndex: idx, + defaultOpacity: style.opacity, + defaultText: data.getName(idx) + }, { + normal: { + align: labelLayout.textAlign, + verticalAlign: labelLayout.verticalAlign + } + }); + polygon.setTextConfig({ + local: true, + inside: !!labelLayout.inside, + insideStroke: visualColor, + // insideFill: 'auto', + outsideFill: visualColor + }); + var linePoints = labelLayout.linePoints; + labelLine.setShape({ + points: linePoints + }); + polygon.textGuideLineConfig = { + anchor: linePoints ? new Point(linePoints[0][0], linePoints[0][1]) : null + }; + // Make sure update style on labelText after setLabelStyle. + // Because setLabelStyle will replace a new style on it. + updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + rotation: labelLayout.rotation, + originX: labelLayout.x, + originY: labelLayout.y, + z2: 10 + }); + setLabelLineStyle(polygon, getLabelLineStatesModels(itemModel), { + // Default use item visual color + stroke: visualColor + }); + }; + return FunnelPiece; + }(Polygon); + var FunnelView = /** @class */function (_super) { + __extends(FunnelView, _super); + function FunnelView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = FunnelView.type; + _this.ignoreLabelLineUpdate = true; + return _this; + } + FunnelView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var oldData = this._data; + var group = this.group; + data.diff(oldData).add(function (idx) { + var funnelPiece = new FunnelPiece(data, idx); + data.setItemGraphicEl(idx, funnelPiece); + group.add(funnelPiece); + }).update(function (newIdx, oldIdx) { + var piece = oldData.getItemGraphicEl(oldIdx); + piece.updateData(data, newIdx); + group.add(piece); + data.setItemGraphicEl(newIdx, piece); + }).remove(function (idx) { + var piece = oldData.getItemGraphicEl(idx); + removeElementWithFadeOut(piece, seriesModel, idx); + }).execute(); + this._data = data; + }; + FunnelView.prototype.remove = function () { + this.group.removeAll(); + this._data = null; + }; + FunnelView.prototype.dispose = function () {}; + FunnelView.type = 'funnel'; + return FunnelView; + }(ChartView); + + var FunnelSeriesModel = /** @class */function (_super) { + __extends(FunnelSeriesModel, _super); + function FunnelSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = FunnelSeriesModel.type; + return _this; + } + FunnelSeriesModel.prototype.init = function (option) { + _super.prototype.init.apply(this, arguments); + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); + // Extend labelLine emphasis + this._defaultLabelLine(option); + }; + FunnelSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesDataSimply(this, { + coordDimensions: ['value'], + encodeDefaulter: curry(makeSeriesEncodeForNameBased, this) + }); + }; + FunnelSeriesModel.prototype._defaultLabelLine = function (option) { + // Extend labelLine emphasis + defaultEmphasis(option, 'labelLine', ['show']); + var labelLineNormalOpt = option.labelLine; + var labelLineEmphasisOpt = option.emphasis.labelLine; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show; + }; + // Overwrite + FunnelSeriesModel.prototype.getDataParams = function (dataIndex) { + var data = this.getData(); + var params = _super.prototype.getDataParams.call(this, dataIndex); + var valueDim = data.mapDimension('value'); + var sum = data.getSum(valueDim); + // Percent is 0 if sum is 0 + params.percent = !sum ? 0 : +(data.get(valueDim, dataIndex) / sum * 100).toFixed(2); + params.$vars.push('percent'); + return params; + }; + FunnelSeriesModel.type = 'series.funnel'; + FunnelSeriesModel.defaultOption = { + // zlevel: 0, // 一级层叠 + z: 2, + legendHoverLink: true, + colorBy: 'data', + left: 80, + top: 60, + right: 80, + bottom: 60, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + // 默认取数据最小最大值 + // min: 0, + // max: 100, + minSize: '0%', + maxSize: '100%', + sort: 'descending', + orient: 'vertical', + gap: 0, + funnelAlign: 'center', + label: { + show: true, + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + }, + + labelLine: { + show: true, + length: 20, + lineStyle: { + // color: 各异, + width: 1 + } + }, + itemStyle: { + // color: 各异, + borderColor: '#fff', + borderWidth: 1 + }, + emphasis: { + label: { + show: true + } + }, + select: { + itemStyle: { + borderColor: '#212121' + } + } + }; + return FunnelSeriesModel; + }(SeriesModel); + + function getViewRect$3(seriesModel, api) { + return getLayoutRect(seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + } + function getSortedIndices(data, sort) { + var valueDim = data.mapDimension('value'); + var valueArr = data.mapArray(valueDim, function (val) { + return val; + }); + var indices = []; + var isAscending = sort === 'ascending'; + for (var i = 0, len = data.count(); i < len; i++) { + indices[i] = i; + } + // Add custom sortable function & none sortable opetion by "options.sort" + if (isFunction(sort)) { + indices.sort(sort); + } else if (sort !== 'none') { + indices.sort(function (a, b) { + return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a]; + }); + } + return indices; + } + function labelLayout(data) { + var seriesModel = data.hostModel; + var orient = seriesModel.get('orient'); + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label'); + var labelPosition = labelModel.get('position'); + var labelLineModel = itemModel.getModel('labelLine'); + var layout = data.getItemLayout(idx); + var points = layout.points; + var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight'; + var textAlign; + var textX; + var textY; + var linePoints; + if (isLabelInside) { + if (labelPosition === 'insideLeft') { + textX = (points[0][0] + points[3][0]) / 2 + 5; + textY = (points[0][1] + points[3][1]) / 2; + textAlign = 'left'; + } else if (labelPosition === 'insideRight') { + textX = (points[1][0] + points[2][0]) / 2 - 5; + textY = (points[1][1] + points[2][1]) / 2; + textAlign = 'right'; + } else { + textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4; + textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4; + textAlign = 'center'; + } + linePoints = [[textX, textY], [textX, textY]]; + } else { + var x1 = void 0; + var y1 = void 0; + var x2 = void 0; + var y2 = void 0; + var labelLineLen = labelLineModel.get('length'); + if ("development" !== 'production') { + if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition) > -1) { + labelPosition = 'left'; + console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.'); + } + if (orient === 'horizontal' && ['left', 'right'].indexOf(labelPosition) > -1) { + labelPosition = 'bottom'; + console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.'); + } + } + if (labelPosition === 'left') { + // Left side + x1 = (points[3][0] + points[0][0]) / 2; + y1 = (points[3][1] + points[0][1]) / 2; + x2 = x1 - labelLineLen; + textX = x2 - 5; + textAlign = 'right'; + } else if (labelPosition === 'right') { + // Right side + x1 = (points[1][0] + points[2][0]) / 2; + y1 = (points[1][1] + points[2][1]) / 2; + x2 = x1 + labelLineLen; + textX = x2 + 5; + textAlign = 'left'; + } else if (labelPosition === 'top') { + // Top side + x1 = (points[3][0] + points[0][0]) / 2; + y1 = (points[3][1] + points[0][1]) / 2; + y2 = y1 - labelLineLen; + textY = y2 - 5; + textAlign = 'center'; + } else if (labelPosition === 'bottom') { + // Bottom side + x1 = (points[1][0] + points[2][0]) / 2; + y1 = (points[1][1] + points[2][1]) / 2; + y2 = y1 + labelLineLen; + textY = y2 + 5; + textAlign = 'center'; + } else if (labelPosition === 'rightTop') { + // RightTop side + x1 = orient === 'horizontal' ? points[3][0] : points[1][0]; + y1 = orient === 'horizontal' ? points[3][1] : points[1][1]; + if (orient === 'horizontal') { + y2 = y1 - labelLineLen; + textY = y2 - 5; + textAlign = 'center'; + } else { + x2 = x1 + labelLineLen; + textX = x2 + 5; + textAlign = 'top'; + } + } else if (labelPosition === 'rightBottom') { + // RightBottom side + x1 = points[2][0]; + y1 = points[2][1]; + if (orient === 'horizontal') { + y2 = y1 + labelLineLen; + textY = y2 + 5; + textAlign = 'center'; + } else { + x2 = x1 + labelLineLen; + textX = x2 + 5; + textAlign = 'bottom'; + } + } else if (labelPosition === 'leftTop') { + // LeftTop side + x1 = points[0][0]; + y1 = orient === 'horizontal' ? points[0][1] : points[1][1]; + if (orient === 'horizontal') { + y2 = y1 - labelLineLen; + textY = y2 - 5; + textAlign = 'center'; + } else { + x2 = x1 - labelLineLen; + textX = x2 - 5; + textAlign = 'right'; + } + } else if (labelPosition === 'leftBottom') { + // LeftBottom side + x1 = orient === 'horizontal' ? points[1][0] : points[3][0]; + y1 = orient === 'horizontal' ? points[1][1] : points[2][1]; + if (orient === 'horizontal') { + y2 = y1 + labelLineLen; + textY = y2 + 5; + textAlign = 'center'; + } else { + x2 = x1 - labelLineLen; + textX = x2 - 5; + textAlign = 'right'; + } + } else { + // Right side or Bottom side + x1 = (points[1][0] + points[2][0]) / 2; + y1 = (points[1][1] + points[2][1]) / 2; + if (orient === 'horizontal') { + y2 = y1 + labelLineLen; + textY = y2 + 5; + textAlign = 'center'; + } else { + x2 = x1 + labelLineLen; + textX = x2 + 5; + textAlign = 'left'; + } + } + if (orient === 'horizontal') { + x2 = x1; + textX = x2; + } else { + y2 = y1; + textY = y2; + } + linePoints = [[x1, y1], [x2, y2]]; + } + layout.label = { + linePoints: linePoints, + x: textX, + y: textY, + verticalAlign: 'middle', + textAlign: textAlign, + inside: isLabelInside + }; + }); + } + function funnelLayout(ecModel, api) { + ecModel.eachSeriesByType('funnel', function (seriesModel) { + var data = seriesModel.getData(); + var valueDim = data.mapDimension('value'); + var sort = seriesModel.get('sort'); + var viewRect = getViewRect$3(seriesModel, api); + var orient = seriesModel.get('orient'); + var viewWidth = viewRect.width; + var viewHeight = viewRect.height; + var indices = getSortedIndices(data, sort); + var x = viewRect.x; + var y = viewRect.y; + var sizeExtent = orient === 'horizontal' ? [parsePercent$1(seriesModel.get('minSize'), viewHeight), parsePercent$1(seriesModel.get('maxSize'), viewHeight)] : [parsePercent$1(seriesModel.get('minSize'), viewWidth), parsePercent$1(seriesModel.get('maxSize'), viewWidth)]; + var dataExtent = data.getDataExtent(valueDim); + var min = seriesModel.get('min'); + var max = seriesModel.get('max'); + if (min == null) { + min = Math.min(dataExtent[0], 0); + } + if (max == null) { + max = dataExtent[1]; + } + var funnelAlign = seriesModel.get('funnelAlign'); + var gap = seriesModel.get('gap'); + var viewSize = orient === 'horizontal' ? viewWidth : viewHeight; + var itemSize = (viewSize - gap * (data.count() - 1)) / data.count(); + var getLinePoints = function (idx, offset) { + // End point index is data.count() and we assign it 0 + if (orient === 'horizontal') { + var val_1 = data.get(valueDim, idx) || 0; + var itemHeight = linearMap(val_1, [min, max], sizeExtent, true); + var y0 = void 0; + switch (funnelAlign) { + case 'top': + y0 = y; + break; + case 'center': + y0 = y + (viewHeight - itemHeight) / 2; + break; + case 'bottom': + y0 = y + (viewHeight - itemHeight); + break; + } + return [[offset, y0], [offset, y0 + itemHeight]]; + } + var val = data.get(valueDim, idx) || 0; + var itemWidth = linearMap(val, [min, max], sizeExtent, true); + var x0; + switch (funnelAlign) { + case 'left': + x0 = x; + break; + case 'center': + x0 = x + (viewWidth - itemWidth) / 2; + break; + case 'right': + x0 = x + viewWidth - itemWidth; + break; + } + return [[x0, offset], [x0 + itemWidth, offset]]; + }; + if (sort === 'ascending') { + // From bottom to top + itemSize = -itemSize; + gap = -gap; + if (orient === 'horizontal') { + x += viewWidth; + } else { + y += viewHeight; + } + indices = indices.reverse(); + } + for (var i = 0; i < indices.length; i++) { + var idx = indices[i]; + var nextIdx = indices[i + 1]; + var itemModel = data.getItemModel(idx); + if (orient === 'horizontal') { + var width = itemModel.get(['itemStyle', 'width']); + if (width == null) { + width = itemSize; + } else { + width = parsePercent$1(width, viewWidth); + if (sort === 'ascending') { + width = -width; + } + } + var start = getLinePoints(idx, x); + var end = getLinePoints(nextIdx, x + width); + x += width + gap; + data.setItemLayout(idx, { + points: start.concat(end.slice().reverse()) + }); + } else { + var height = itemModel.get(['itemStyle', 'height']); + if (height == null) { + height = itemSize; + } else { + height = parsePercent$1(height, viewHeight); + if (sort === 'ascending') { + height = -height; + } + } + var start = getLinePoints(idx, y); + var end = getLinePoints(nextIdx, y + height); + y += height + gap; + data.setItemLayout(idx, { + points: start.concat(end.slice().reverse()) + }); + } + } + labelLayout(data); + }); + } + + function install$f(registers) { + registers.registerChartView(FunnelView); + registers.registerSeriesModel(FunnelSeriesModel); + registers.registerLayout(funnelLayout); + registers.registerProcessor(dataFilter('funnel')); + } + + var DEFAULT_SMOOTH = 0.3; + var ParallelView = /** @class */function (_super) { + __extends(ParallelView, _super); + function ParallelView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ParallelView.type; + _this._dataGroup = new Group(); + _this._initialized = false; + return _this; + } + ParallelView.prototype.init = function () { + this.group.add(this._dataGroup); + }; + /** + * @override + */ + ParallelView.prototype.render = function (seriesModel, ecModel, api, payload) { + // Clear previously rendered progressive elements. + this._progressiveEls = null; + var dataGroup = this._dataGroup; + var data = seriesModel.getData(); + var oldData = this._data; + var coordSys = seriesModel.coordinateSystem; + var dimensions = coordSys.dimensions; + var seriesScope = makeSeriesScope$2(seriesModel); + data.diff(oldData).add(add).update(update).remove(remove).execute(); + function add(newDataIndex) { + var line = addEl(data, dataGroup, newDataIndex, dimensions, coordSys); + updateElCommon(line, data, newDataIndex, seriesScope); + } + function update(newDataIndex, oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + var points = createLinePoints(data, newDataIndex, dimensions, coordSys); + data.setItemGraphicEl(newDataIndex, line); + updateProps(line, { + shape: { + points: points + } + }, seriesModel, newDataIndex); + saveOldStyle(line); + updateElCommon(line, data, newDataIndex, seriesScope); + } + function remove(oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + dataGroup.remove(line); + } + // First create + if (!this._initialized) { + this._initialized = true; + var clipPath = createGridClipShape(coordSys, seriesModel, function () { + // Callback will be invoked immediately if there is no animation + setTimeout(function () { + dataGroup.removeClipPath(); + }); + }); + dataGroup.setClipPath(clipPath); + } + this._data = data; + }; + ParallelView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) { + this._initialized = true; + this._data = null; + this._dataGroup.removeAll(); + }; + ParallelView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + var dimensions = coordSys.dimensions; + var seriesScope = makeSeriesScope$2(seriesModel); + var progressiveEls = this._progressiveEls = []; + for (var dataIndex = taskParams.start; dataIndex < taskParams.end; dataIndex++) { + var line = addEl(data, this._dataGroup, dataIndex, dimensions, coordSys); + line.incremental = true; + updateElCommon(line, data, dataIndex, seriesScope); + progressiveEls.push(line); + } + }; + ParallelView.prototype.remove = function () { + this._dataGroup && this._dataGroup.removeAll(); + this._data = null; + }; + ParallelView.type = 'parallel'; + return ParallelView; + }(ChartView); + function createGridClipShape(coordSys, seriesModel, cb) { + var parallelModel = coordSys.model; + var rect = coordSys.getRect(); + var rectEl = new Rect({ + shape: { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + } + }); + var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height'; + rectEl.setShape(dim, 0); + initProps(rectEl, { + shape: { + width: rect.width, + height: rect.height + } + }, seriesModel, cb); + return rectEl; + } + function createLinePoints(data, dataIndex, dimensions, coordSys) { + var points = []; + for (var i = 0; i < dimensions.length; i++) { + var dimName = dimensions[i]; + var value = data.get(data.mapDimension(dimName), dataIndex); + if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) { + points.push(coordSys.dataToPoint(value, dimName)); + } + } + return points; + } + function addEl(data, dataGroup, dataIndex, dimensions, coordSys) { + var points = createLinePoints(data, dataIndex, dimensions, coordSys); + var line = new Polyline({ + shape: { + points: points + }, + // silent: true, + z2: 10 + }); + dataGroup.add(line); + data.setItemGraphicEl(dataIndex, line); + return line; + } + function makeSeriesScope$2(seriesModel) { + var smooth = seriesModel.get('smooth', true); + smooth === true && (smooth = DEFAULT_SMOOTH); + smooth = numericToNumber(smooth); + eqNaN(smooth) && (smooth = 0); + return { + smooth: smooth + }; + } + function updateElCommon(el, data, dataIndex, seriesScope) { + el.useStyle(data.getItemVisual(dataIndex, 'style')); + el.style.fill = null; + el.setShape('smooth', seriesScope.smooth); + var itemModel = data.getItemModel(dataIndex); + var emphasisModel = itemModel.getModel('emphasis'); + setStatesStylesFromModel(el, itemModel, 'lineStyle'); + toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + } + // function simpleDiff(oldData, newData, dimensions) { + // let oldLen; + // if (!oldData + // || !oldData.__plProgressive + // || (oldLen = oldData.count()) !== newData.count() + // ) { + // return true; + // } + // let dimLen = dimensions.length; + // for (let i = 0; i < oldLen; i++) { + // for (let j = 0; j < dimLen; j++) { + // if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) { + // return true; + // } + // } + // } + // return false; + // } + // FIXME put in common util? + function isEmptyValue(val, axisType) { + return axisType === 'category' ? val == null : val == null || isNaN(val); // axisType === 'value' + } + + var ParallelSeriesModel = /** @class */function (_super) { + __extends(ParallelSeriesModel, _super); + function ParallelSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ParallelSeriesModel.type; + _this.visualStyleAccessPath = 'lineStyle'; + _this.visualDrawType = 'stroke'; + return _this; + } + ParallelSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesData(null, this, { + useEncodeDefaulter: bind(makeDefaultEncode, null, this) + }); + }; + /** + * User can get data raw indices on 'axisAreaSelected' event received. + * + * @return Raw indices + */ + ParallelSeriesModel.prototype.getRawIndicesByActiveState = function (activeState) { + var coordSys = this.coordinateSystem; + var data = this.getData(); + var indices = []; + coordSys.eachActiveState(data, function (theActiveState, dataIndex) { + if (activeState === theActiveState) { + indices.push(data.getRawIndex(dataIndex)); + } + }); + return indices; + }; + ParallelSeriesModel.type = 'series.parallel'; + ParallelSeriesModel.dependencies = ['parallel']; + ParallelSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'parallel', + parallelIndex: 0, + label: { + show: false + }, + inactiveOpacity: 0.05, + activeOpacity: 1, + lineStyle: { + width: 1, + opacity: 0.45, + type: 'solid' + }, + emphasis: { + label: { + show: false + } + }, + progressive: 500, + smooth: false, + animationEasing: 'linear' + }; + return ParallelSeriesModel; + }(SeriesModel); + function makeDefaultEncode(seriesModel) { + // The mapping of parallelAxis dimension to data dimension can + // be specified in parallelAxis.option.dim. For example, if + // parallelAxis.option.dim is 'dim3', it mapping to the third + // dimension of data. But `data.encode` has higher priority. + // Moreover, parallelModel.dimension should not be regarded as data + // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; + var parallelModel = seriesModel.ecModel.getComponent('parallel', seriesModel.get('parallelIndex')); + if (!parallelModel) { + return; + } + var encodeDefine = {}; + each(parallelModel.dimensions, function (axisDim) { + var dataDimIndex = convertDimNameToNumber(axisDim); + encodeDefine[axisDim] = dataDimIndex; + }); + return encodeDefine; + } + function convertDimNameToNumber(dimName) { + return +dimName.replace('dim', ''); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var opacityAccessPath$1 = ['lineStyle', 'opacity']; + var parallelVisual = { + seriesType: 'parallel', + reset: function (seriesModel, ecModel) { + var coordSys = seriesModel.coordinateSystem; + var opacityMap = { + normal: seriesModel.get(['lineStyle', 'opacity']), + active: seriesModel.get('activeOpacity'), + inactive: seriesModel.get('inactiveOpacity') + }; + return { + progress: function (params, data) { + coordSys.eachActiveState(data, function (activeState, dataIndex) { + var opacity = opacityMap[activeState]; + if (activeState === 'normal' && data.hasItemOption) { + var itemOpacity = data.getItemModel(dataIndex).get(opacityAccessPath$1, true); + itemOpacity != null && (opacity = itemOpacity); + } + var existsStyle = data.ensureUniqueItemVisual(dataIndex, 'style'); + existsStyle.opacity = opacity; + }, params.start, params.end); + } + }; + } + }; + + function parallelPreprocessor(option) { + createParallelIfNeeded(option); + mergeAxisOptionFromParallel(option); + } + /** + * Create a parallel coordinate if not exists. + * @inner + */ + function createParallelIfNeeded(option) { + if (option.parallel) { + return; + } + var hasParallelSeries = false; + each(option.series, function (seriesOpt) { + if (seriesOpt && seriesOpt.type === 'parallel') { + hasParallelSeries = true; + } + }); + if (hasParallelSeries) { + option.parallel = [{}]; + } + } + /** + * Merge aixs definition from parallel option (if exists) to axis option. + * @inner + */ + function mergeAxisOptionFromParallel(option) { + var axes = normalizeToArray(option.parallelAxis); + each(axes, function (axisOption) { + if (!isObject(axisOption)) { + return; + } + var parallelIndex = axisOption.parallelIndex || 0; + var parallelOption = normalizeToArray(option.parallel)[parallelIndex]; + if (parallelOption && parallelOption.parallelAxisDefault) { + merge(axisOption, parallelOption.parallelAxisDefault, false); + } + }); + } + + var CLICK_THRESHOLD = 5; // > 4 + var ParallelView$1 = /** @class */function (_super) { + __extends(ParallelView, _super); + function ParallelView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ParallelView.type; + return _this; + } + ParallelView.prototype.render = function (parallelModel, ecModel, api) { + this._model = parallelModel; + this._api = api; + if (!this._handlers) { + this._handlers = {}; + each(handlers, function (handler, eventName) { + api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this)); + }, this); + } + createOrUpdate(this, '_throttledDispatchExpand', parallelModel.get('axisExpandRate'), 'fixRate'); + }; + ParallelView.prototype.dispose = function (ecModel, api) { + clear(this, '_throttledDispatchExpand'); + each(this._handlers, function (handler, eventName) { + api.getZr().off(eventName, handler); + }); + this._handlers = null; + }; + /** + * @internal + * @param {Object} [opt] If null, cancel the last action triggering for debounce. + */ + ParallelView.prototype._throttledDispatchExpand = function (opt) { + this._dispatchExpand(opt); + }; + /** + * @internal + */ + ParallelView.prototype._dispatchExpand = function (opt) { + opt && this._api.dispatchAction(extend({ + type: 'parallelAxisExpand' + }, opt)); + }; + ParallelView.type = 'parallel'; + return ParallelView; + }(ComponentView); + var handlers = { + mousedown: function (e) { + if (checkTrigger(this, 'click')) { + this._mouseDownPoint = [e.offsetX, e.offsetY]; + } + }, + mouseup: function (e) { + var mouseDownPoint = this._mouseDownPoint; + if (checkTrigger(this, 'click') && mouseDownPoint) { + var point = [e.offsetX, e.offsetY]; + var dist = Math.pow(mouseDownPoint[0] - point[0], 2) + Math.pow(mouseDownPoint[1] - point[1], 2); + if (dist > CLICK_THRESHOLD) { + return; + } + var result = this._model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]); + result.behavior !== 'none' && this._dispatchExpand({ + axisExpandWindow: result.axisExpandWindow + }); + } + this._mouseDownPoint = null; + }, + mousemove: function (e) { + // Should do nothing when brushing. + if (this._mouseDownPoint || !checkTrigger(this, 'mousemove')) { + return; + } + var model = this._model; + var result = model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]); + var behavior = result.behavior; + behavior === 'jump' && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce')); + this._throttledDispatchExpand(behavior === 'none' ? null // Cancel the last trigger, in case that mouse slide out of the area quickly. + : { + axisExpandWindow: result.axisExpandWindow, + // Jumping uses animation, and sliding suppresses animation. + animation: behavior === 'jump' ? null : { + duration: 0 // Disable animation. + } + }); + } + }; + + function checkTrigger(view, triggerOn) { + var model = view._model; + return model.get('axisExpandable') && model.get('axisExpandTriggerOn') === triggerOn; + } + + var ParallelModel = /** @class */function (_super) { + __extends(ParallelModel, _super); + function ParallelModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ParallelModel.type; + return _this; + } + ParallelModel.prototype.init = function () { + _super.prototype.init.apply(this, arguments); + this.mergeOption({}); + }; + ParallelModel.prototype.mergeOption = function (newOption) { + var thisOption = this.option; + newOption && merge(thisOption, newOption, true); + this._initDimensions(); + }; + /** + * Whether series or axis is in this coordinate system. + */ + ParallelModel.prototype.contains = function (model, ecModel) { + var parallelIndex = model.get('parallelIndex'); + return parallelIndex != null && ecModel.getComponent('parallel', parallelIndex) === this; + }; + ParallelModel.prototype.setAxisExpand = function (opt) { + each(['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth', 'axisExpandWindow'], function (name) { + if (opt.hasOwnProperty(name)) { + // @ts-ignore FIXME: why "never" inferred in this.option[name]? + this.option[name] = opt[name]; + } + }, this); + }; + ParallelModel.prototype._initDimensions = function () { + var dimensions = this.dimensions = []; + var parallelAxisIndex = this.parallelAxisIndex = []; + var axisModels = filter(this.ecModel.queryComponents({ + mainType: 'parallelAxis' + }), function (axisModel) { + // Can not use this.contains here, because + // initialization has not been completed yet. + return (axisModel.get('parallelIndex') || 0) === this.componentIndex; + }, this); + each(axisModels, function (axisModel) { + dimensions.push('dim' + axisModel.get('dim')); + parallelAxisIndex.push(axisModel.componentIndex); + }); + }; + ParallelModel.type = 'parallel'; + ParallelModel.dependencies = ['parallelAxis']; + ParallelModel.layoutMode = 'box'; + ParallelModel.defaultOption = { + // zlevel: 0, + z: 0, + left: 80, + top: 60, + right: 80, + bottom: 60, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + layout: 'horizontal', + // FIXME + // naming? + axisExpandable: false, + axisExpandCenter: null, + axisExpandCount: 0, + axisExpandWidth: 50, + axisExpandRate: 17, + axisExpandDebounce: 50, + // [out, in, jumpTarget]. In percentage. If use [null, 0.05], null means full. + // Do not doc to user until necessary. + axisExpandSlideTriggerArea: [-0.15, 0.05, 0.4], + axisExpandTriggerOn: 'click', + parallelAxisDefault: null + }; + return ParallelModel; + }(ComponentModel); + + var ParallelAxis = /** @class */function (_super) { + __extends(ParallelAxis, _super); + function ParallelAxis(dim, scale, coordExtent, axisType, axisIndex) { + var _this = _super.call(this, dim, scale, coordExtent) || this; + _this.type = axisType || 'value'; + _this.axisIndex = axisIndex; + return _this; + } + ParallelAxis.prototype.isHorizontal = function () { + return this.coordinateSystem.getModel().get('layout') !== 'horizontal'; + }; + return ParallelAxis; + }(Axis); + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /** + * Calculate slider move result. + * Usage: + * (1) If both handle0 and handle1 are needed to be moved, set minSpan the same as + * maxSpan and the same as `Math.abs(handleEnd[1] - handleEnds[0])`. + * (2) If handle0 is forbidden to cross handle1, set minSpan as `0`. + * + * @param delta Move length. + * @param handleEnds handleEnds[0] can be bigger then handleEnds[1]. + * handleEnds will be modified in this method. + * @param extent handleEnds is restricted by extent. + * extent[0] should less or equals than extent[1]. + * @param handleIndex Can be 'all', means that both move the two handleEnds. + * @param minSpan The range of dataZoom can not be smaller than that. + * If not set, handle0 and cross handle1. If set as a non-negative + * number (including `0`), handles will push each other when reaching + * the minSpan. + * @param maxSpan The range of dataZoom can not be larger than that. + * @return The input handleEnds. + */ + function sliderMove(delta, handleEnds, extent, handleIndex, minSpan, maxSpan) { + delta = delta || 0; + var extentSpan = extent[1] - extent[0]; + // Notice maxSpan and minSpan can be null/undefined. + if (minSpan != null) { + minSpan = restrict(minSpan, [0, extentSpan]); + } + if (maxSpan != null) { + maxSpan = Math.max(maxSpan, minSpan != null ? minSpan : 0); + } + if (handleIndex === 'all') { + var handleSpan = Math.abs(handleEnds[1] - handleEnds[0]); + handleSpan = restrict(handleSpan, [0, extentSpan]); + minSpan = maxSpan = restrict(handleSpan, [minSpan, maxSpan]); + handleIndex = 0; + } + handleEnds[0] = restrict(handleEnds[0], extent); + handleEnds[1] = restrict(handleEnds[1], extent); + var originalDistSign = getSpanSign(handleEnds, handleIndex); + handleEnds[handleIndex] += delta; + // Restrict in extent. + var extentMinSpan = minSpan || 0; + var realExtent = extent.slice(); + originalDistSign.sign < 0 ? realExtent[0] += extentMinSpan : realExtent[1] -= extentMinSpan; + handleEnds[handleIndex] = restrict(handleEnds[handleIndex], realExtent); + // Expand span. + var currDistSign; + currDistSign = getSpanSign(handleEnds, handleIndex); + if (minSpan != null && (currDistSign.sign !== originalDistSign.sign || currDistSign.span < minSpan)) { + // If minSpan exists, 'cross' is forbidden. + handleEnds[1 - handleIndex] = handleEnds[handleIndex] + originalDistSign.sign * minSpan; + } + // Shrink span. + currDistSign = getSpanSign(handleEnds, handleIndex); + if (maxSpan != null && currDistSign.span > maxSpan) { + handleEnds[1 - handleIndex] = handleEnds[handleIndex] + currDistSign.sign * maxSpan; + } + return handleEnds; + } + function getSpanSign(handleEnds, handleIndex) { + var dist = handleEnds[handleIndex] - handleEnds[1 - handleIndex]; + // If `handleEnds[0] === handleEnds[1]`, always believe that handleEnd[0] + // is at left of handleEnds[1] for non-cross case. + return { + span: Math.abs(dist), + sign: dist > 0 ? -1 : dist < 0 ? 1 : handleIndex ? -1 : 1 + }; + } + function restrict(value, extend) { + return Math.min(extend[1] != null ? extend[1] : Infinity, Math.max(extend[0] != null ? extend[0] : -Infinity, value)); + } + + var each$5 = each; + var mathMin$8 = Math.min; + var mathMax$8 = Math.max; + var mathFloor$1 = Math.floor; + var mathCeil$1 = Math.ceil; + var round$3 = round; + var PI$7 = Math.PI; + var Parallel = /** @class */function () { + function Parallel(parallelModel, ecModel, api) { + this.type = 'parallel'; + /** + * key: dimension + */ + this._axesMap = createHashMap(); + /** + * key: dimension + * value: {position: [], rotation, } + */ + this._axesLayout = {}; + this.dimensions = parallelModel.dimensions; + this._model = parallelModel; + this._init(parallelModel, ecModel, api); + } + Parallel.prototype._init = function (parallelModel, ecModel, api) { + var dimensions = parallelModel.dimensions; + var parallelAxisIndex = parallelModel.parallelAxisIndex; + each$5(dimensions, function (dim, idx) { + var axisIndex = parallelAxisIndex[idx]; + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + var axis = this._axesMap.set(dim, new ParallelAxis(dim, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisIndex)); + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + // Injection + axisModel.axis = axis; + axis.model = axisModel; + axis.coordinateSystem = axisModel.coordinateSystem = this; + }, this); + }; + /** + * Update axis scale after data processed + */ + Parallel.prototype.update = function (ecModel, api) { + this._updateAxesFromSeries(this._model, ecModel); + }; + Parallel.prototype.containPoint = function (point) { + var layoutInfo = this._makeLayoutInfo(); + var axisBase = layoutInfo.axisBase; + var layoutBase = layoutInfo.layoutBase; + var pixelDimIndex = layoutInfo.pixelDimIndex; + var pAxis = point[1 - pixelDimIndex]; + var pLayout = point[pixelDimIndex]; + return pAxis >= axisBase && pAxis <= axisBase + layoutInfo.axisLength && pLayout >= layoutBase && pLayout <= layoutBase + layoutInfo.layoutLength; + }; + Parallel.prototype.getModel = function () { + return this._model; + }; + /** + * Update properties from series + */ + Parallel.prototype._updateAxesFromSeries = function (parallelModel, ecModel) { + ecModel.eachSeries(function (seriesModel) { + if (!parallelModel.contains(seriesModel, ecModel)) { + return; + } + var data = seriesModel.getData(); + each$5(this.dimensions, function (dim) { + var axis = this._axesMap.get(dim); + axis.scale.unionExtentFromData(data, data.mapDimension(dim)); + niceScaleExtent(axis.scale, axis.model); + }, this); + }, this); + }; + /** + * Resize the parallel coordinate system. + */ + Parallel.prototype.resize = function (parallelModel, api) { + this._rect = getLayoutRect(parallelModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + this._layoutAxes(); + }; + Parallel.prototype.getRect = function () { + return this._rect; + }; + Parallel.prototype._makeLayoutInfo = function () { + var parallelModel = this._model; + var rect = this._rect; + var xy = ['x', 'y']; + var wh = ['width', 'height']; + var layout = parallelModel.get('layout'); + var pixelDimIndex = layout === 'horizontal' ? 0 : 1; + var layoutLength = rect[wh[pixelDimIndex]]; + var layoutExtent = [0, layoutLength]; + var axisCount = this.dimensions.length; + var axisExpandWidth = restrict$1(parallelModel.get('axisExpandWidth'), layoutExtent); + var axisExpandCount = restrict$1(parallelModel.get('axisExpandCount') || 0, [0, axisCount]); + var axisExpandable = parallelModel.get('axisExpandable') && axisCount > 3 && axisCount > axisExpandCount && axisExpandCount > 1 && axisExpandWidth > 0 && layoutLength > 0; + // `axisExpandWindow` is According to the coordinates of [0, axisExpandLength], + // for sake of consider the case that axisCollapseWidth is 0 (when screen is narrow), + // where collapsed axes should be overlapped. + var axisExpandWindow = parallelModel.get('axisExpandWindow'); + var winSize; + if (!axisExpandWindow) { + winSize = restrict$1(axisExpandWidth * (axisExpandCount - 1), layoutExtent); + var axisExpandCenter = parallelModel.get('axisExpandCenter') || mathFloor$1(axisCount / 2); + axisExpandWindow = [axisExpandWidth * axisExpandCenter - winSize / 2]; + axisExpandWindow[1] = axisExpandWindow[0] + winSize; + } else { + winSize = restrict$1(axisExpandWindow[1] - axisExpandWindow[0], layoutExtent); + axisExpandWindow[1] = axisExpandWindow[0] + winSize; + } + var axisCollapseWidth = (layoutLength - winSize) / (axisCount - axisExpandCount); + // Avoid axisCollapseWidth is too small. + axisCollapseWidth < 3 && (axisCollapseWidth = 0); + // Find the first and last indices > ewin[0] and < ewin[1]. + var winInnerIndices = [mathFloor$1(round$3(axisExpandWindow[0] / axisExpandWidth, 1)) + 1, mathCeil$1(round$3(axisExpandWindow[1] / axisExpandWidth, 1)) - 1]; + // Pos in ec coordinates. + var axisExpandWindow0Pos = axisCollapseWidth / axisExpandWidth * axisExpandWindow[0]; + return { + layout: layout, + pixelDimIndex: pixelDimIndex, + layoutBase: rect[xy[pixelDimIndex]], + layoutLength: layoutLength, + axisBase: rect[xy[1 - pixelDimIndex]], + axisLength: rect[wh[1 - pixelDimIndex]], + axisExpandable: axisExpandable, + axisExpandWidth: axisExpandWidth, + axisCollapseWidth: axisCollapseWidth, + axisExpandWindow: axisExpandWindow, + axisCount: axisCount, + winInnerIndices: winInnerIndices, + axisExpandWindow0Pos: axisExpandWindow0Pos + }; + }; + Parallel.prototype._layoutAxes = function () { + var rect = this._rect; + var axes = this._axesMap; + var dimensions = this.dimensions; + var layoutInfo = this._makeLayoutInfo(); + var layout = layoutInfo.layout; + axes.each(function (axis) { + var axisExtent = [0, layoutInfo.axisLength]; + var idx = axis.inverse ? 1 : 0; + axis.setExtent(axisExtent[idx], axisExtent[1 - idx]); + }); + each$5(dimensions, function (dim, idx) { + var posInfo = (layoutInfo.axisExpandable ? layoutAxisWithExpand : layoutAxisWithoutExpand)(idx, layoutInfo); + var positionTable = { + horizontal: { + x: posInfo.position, + y: layoutInfo.axisLength + }, + vertical: { + x: 0, + y: posInfo.position + } + }; + var rotationTable = { + horizontal: PI$7 / 2, + vertical: 0 + }; + var position = [positionTable[layout].x + rect.x, positionTable[layout].y + rect.y]; + var rotation = rotationTable[layout]; + var transform = create$1(); + rotate(transform, transform, rotation); + translate(transform, transform, position); + // TODO + // tick layout info + // TODO + // update dimensions info based on axis order. + this._axesLayout[dim] = { + position: position, + rotation: rotation, + transform: transform, + axisNameAvailableWidth: posInfo.axisNameAvailableWidth, + axisLabelShow: posInfo.axisLabelShow, + nameTruncateMaxWidth: posInfo.nameTruncateMaxWidth, + tickDirection: 1, + labelDirection: 1 + }; + }, this); + }; + /** + * Get axis by dim. + */ + Parallel.prototype.getAxis = function (dim) { + return this._axesMap.get(dim); + }; + /** + * Convert a dim value of a single item of series data to Point. + */ + Parallel.prototype.dataToPoint = function (value, dim) { + return this.axisCoordToPoint(this._axesMap.get(dim).dataToCoord(value), dim); + }; + /** + * Travel data for one time, get activeState of each data item. + * @param start the start dataIndex that travel from. + * @param end the next dataIndex of the last dataIndex will be travel. + */ + Parallel.prototype.eachActiveState = function (data, callback, start, end) { + start == null && (start = 0); + end == null && (end = data.count()); + var axesMap = this._axesMap; + var dimensions = this.dimensions; + var dataDimensions = []; + var axisModels = []; + each(dimensions, function (axisDim) { + dataDimensions.push(data.mapDimension(axisDim)); + axisModels.push(axesMap.get(axisDim).model); + }); + var hasActiveSet = this.hasAxisBrushed(); + for (var dataIndex = start; dataIndex < end; dataIndex++) { + var activeState = void 0; + if (!hasActiveSet) { + activeState = 'normal'; + } else { + activeState = 'active'; + var values = data.getValues(dataDimensions, dataIndex); + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + var state = axisModels[j].getActiveState(values[j]); + if (state === 'inactive') { + activeState = 'inactive'; + break; + } + } + } + callback(activeState, dataIndex); + } + }; + /** + * Whether has any activeSet. + */ + Parallel.prototype.hasAxisBrushed = function () { + var dimensions = this.dimensions; + var axesMap = this._axesMap; + var hasActiveSet = false; + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + if (axesMap.get(dimensions[j]).model.getActiveState() !== 'normal') { + hasActiveSet = true; + } + } + return hasActiveSet; + }; + /** + * Convert coords of each axis to Point. + * Return point. For example: [10, 20] + */ + Parallel.prototype.axisCoordToPoint = function (coord, dim) { + var axisLayout = this._axesLayout[dim]; + return applyTransform$1([coord, 0], axisLayout.transform); + }; + /** + * Get axis layout. + */ + Parallel.prototype.getAxisLayout = function (dim) { + return clone(this._axesLayout[dim]); + }; + /** + * @return {Object} {axisExpandWindow, delta, behavior: 'jump' | 'slide' | 'none'}. + */ + Parallel.prototype.getSlidedAxisExpandWindow = function (point) { + var layoutInfo = this._makeLayoutInfo(); + var pixelDimIndex = layoutInfo.pixelDimIndex; + var axisExpandWindow = layoutInfo.axisExpandWindow.slice(); + var winSize = axisExpandWindow[1] - axisExpandWindow[0]; + var extent = [0, layoutInfo.axisExpandWidth * (layoutInfo.axisCount - 1)]; + // Out of the area of coordinate system. + if (!this.containPoint(point)) { + return { + behavior: 'none', + axisExpandWindow: axisExpandWindow + }; + } + // Convert the point from global to expand coordinates. + var pointCoord = point[pixelDimIndex] - layoutInfo.layoutBase - layoutInfo.axisExpandWindow0Pos; + // For dragging operation convenience, the window should not be + // slided when mouse is the center area of the window. + var delta; + var behavior = 'slide'; + var axisCollapseWidth = layoutInfo.axisCollapseWidth; + var triggerArea = this._model.get('axisExpandSlideTriggerArea'); + // But consider touch device, jump is necessary. + var useJump = triggerArea[0] != null; + if (axisCollapseWidth) { + if (useJump && axisCollapseWidth && pointCoord < winSize * triggerArea[0]) { + behavior = 'jump'; + delta = pointCoord - winSize * triggerArea[2]; + } else if (useJump && axisCollapseWidth && pointCoord > winSize * (1 - triggerArea[0])) { + behavior = 'jump'; + delta = pointCoord - winSize * (1 - triggerArea[2]); + } else { + (delta = pointCoord - winSize * triggerArea[1]) >= 0 && (delta = pointCoord - winSize * (1 - triggerArea[1])) <= 0 && (delta = 0); + } + delta *= layoutInfo.axisExpandWidth / axisCollapseWidth; + delta ? sliderMove(delta, axisExpandWindow, extent, 'all') + // Avoid nonsense triger on mousemove. + : behavior = 'none'; + } + // When screen is too narrow, make it visible and slidable, although it is hard to interact. + else { + var winSize2 = axisExpandWindow[1] - axisExpandWindow[0]; + var pos = extent[1] * pointCoord / winSize2; + axisExpandWindow = [mathMax$8(0, pos - winSize2 / 2)]; + axisExpandWindow[1] = mathMin$8(extent[1], axisExpandWindow[0] + winSize2); + axisExpandWindow[0] = axisExpandWindow[1] - winSize2; + } + return { + axisExpandWindow: axisExpandWindow, + behavior: behavior + }; + }; + return Parallel; + }(); + function restrict$1(len, extent) { + return mathMin$8(mathMax$8(len, extent[0]), extent[1]); + } + function layoutAxisWithoutExpand(axisIndex, layoutInfo) { + var step = layoutInfo.layoutLength / (layoutInfo.axisCount - 1); + return { + position: step * axisIndex, + axisNameAvailableWidth: step, + axisLabelShow: true + }; + } + function layoutAxisWithExpand(axisIndex, layoutInfo) { + var layoutLength = layoutInfo.layoutLength; + var axisExpandWidth = layoutInfo.axisExpandWidth; + var axisCount = layoutInfo.axisCount; + var axisCollapseWidth = layoutInfo.axisCollapseWidth; + var winInnerIndices = layoutInfo.winInnerIndices; + var position; + var axisNameAvailableWidth = axisCollapseWidth; + var axisLabelShow = false; + var nameTruncateMaxWidth; + if (axisIndex < winInnerIndices[0]) { + position = axisIndex * axisCollapseWidth; + nameTruncateMaxWidth = axisCollapseWidth; + } else if (axisIndex <= winInnerIndices[1]) { + position = layoutInfo.axisExpandWindow0Pos + axisIndex * axisExpandWidth - layoutInfo.axisExpandWindow[0]; + axisNameAvailableWidth = axisExpandWidth; + axisLabelShow = true; + } else { + position = layoutLength - (axisCount - 1 - axisIndex) * axisCollapseWidth; + nameTruncateMaxWidth = axisCollapseWidth; + } + return { + position: position, + axisNameAvailableWidth: axisNameAvailableWidth, + axisLabelShow: axisLabelShow, + nameTruncateMaxWidth: nameTruncateMaxWidth + }; + } + + function createParallelCoordSys(ecModel, api) { + var coordSysList = []; + ecModel.eachComponent('parallel', function (parallelModel, idx) { + var coordSys = new Parallel(parallelModel, ecModel, api); + coordSys.name = 'parallel_' + idx; + coordSys.resize(parallelModel, api); + parallelModel.coordinateSystem = coordSys; + coordSys.model = parallelModel; + coordSysList.push(coordSys); + }); + // Inject the coordinateSystems into seriesModel + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'parallel') { + var parallelModel = seriesModel.getReferringComponents('parallel', SINGLE_REFERRING).models[0]; + seriesModel.coordinateSystem = parallelModel.coordinateSystem; + } + }); + return coordSysList; + } + var parallelCoordSysCreator = { + create: createParallelCoordSys + }; + + var ParallelAxisModel = /** @class */function (_super) { + __extends(ParallelAxisModel, _super); + function ParallelAxisModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ParallelAxisModel.type; + /** + * @readOnly + */ + _this.activeIntervals = []; + return _this; + } + ParallelAxisModel.prototype.getAreaSelectStyle = function () { + return makeStyleMapper([['fill', 'color'], ['lineWidth', 'borderWidth'], ['stroke', 'borderColor'], ['width', 'width'], ['opacity', 'opacity'] + // Option decal is in `DecalObject` but style.decal is in `PatternObject`. + // So do not transfer decal directly. + ])(this.getModel('areaSelectStyle')); + }; + /** + * The code of this feature is put on AxisModel but not ParallelAxis, + * because axisModel can be alive after echarts updating but instance of + * ParallelAxis having been disposed. this._activeInterval should be kept + * when action dispatched (i.e. legend click). + * + * @param intervals `interval.length === 0` means set all active. + */ + ParallelAxisModel.prototype.setActiveIntervals = function (intervals) { + var activeIntervals = this.activeIntervals = clone(intervals); + // Normalize + if (activeIntervals) { + for (var i = activeIntervals.length - 1; i >= 0; i--) { + asc(activeIntervals[i]); + } + } + }; + /** + * @param value When only attempting detect whether 'no activeIntervals set', + * `value` is not needed to be input. + */ + ParallelAxisModel.prototype.getActiveState = function (value) { + var activeIntervals = this.activeIntervals; + if (!activeIntervals.length) { + return 'normal'; + } + if (value == null || isNaN(+value)) { + return 'inactive'; + } + // Simple optimization + if (activeIntervals.length === 1) { + var interval = activeIntervals[0]; + if (interval[0] <= value && value <= interval[1]) { + return 'active'; + } + } else { + for (var i = 0, len = activeIntervals.length; i < len; i++) { + if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) { + return 'active'; + } + } + } + return 'inactive'; + }; + return ParallelAxisModel; + }(ComponentModel); + mixin(ParallelAxisModel, AxisModelCommonMixin); + + var BRUSH_PANEL_GLOBAL = true; + var mathMin$9 = Math.min; + var mathMax$9 = Math.max; + var mathPow$2 = Math.pow; + var COVER_Z = 10000; + var UNSELECT_THRESHOLD = 6; + var MIN_RESIZE_LINE_WIDTH = 6; + var MUTEX_RESOURCE_KEY = 'globalPan'; + var DIRECTION_MAP = { + w: [0, 0], + e: [0, 1], + n: [1, 0], + s: [1, 1] + }; + var CURSOR_MAP = { + w: 'ew', + e: 'ew', + n: 'ns', + s: 'ns', + ne: 'nesw', + sw: 'nesw', + nw: 'nwse', + se: 'nwse' + }; + var DEFAULT_BRUSH_OPT = { + brushStyle: { + lineWidth: 2, + stroke: 'rgba(210,219,238,0.3)', + fill: '#D2DBEE' + }, + transformable: true, + brushMode: 'single', + removeOnClick: false + }; + var baseUID = 0; + /** + * params: + * areas: Array., coord relates to container group, + * If no container specified, to global. + * opt { + * isEnd: boolean, + * removeOnClick: boolean + * } + */ + var BrushController = /** @class */function (_super) { + __extends(BrushController, _super); + function BrushController(zr) { + var _this = _super.call(this) || this; + /** + * @internal + */ + _this._track = []; + /** + * @internal + */ + _this._covers = []; + _this._handlers = {}; + if ("development" !== 'production') { + assert(zr); + } + _this._zr = zr; + _this.group = new Group(); + _this._uid = 'brushController_' + baseUID++; + each(pointerHandlers, function (handler, eventName) { + this._handlers[eventName] = bind(handler, this); + }, _this); + return _this; + } + /** + * If set to `false`, select disabled. + */ + BrushController.prototype.enableBrush = function (brushOption) { + if ("development" !== 'production') { + assert(this._mounted); + } + this._brushType && this._doDisableBrush(); + brushOption.brushType && this._doEnableBrush(brushOption); + return this; + }; + BrushController.prototype._doEnableBrush = function (brushOption) { + var zr = this._zr; + // Consider roam, which takes globalPan too. + if (!this._enableGlobalPan) { + take(zr, MUTEX_RESOURCE_KEY, this._uid); + } + each(this._handlers, function (handler, eventName) { + zr.on(eventName, handler); + }); + this._brushType = brushOption.brushType; + this._brushOption = merge(clone(DEFAULT_BRUSH_OPT), brushOption, true); + }; + BrushController.prototype._doDisableBrush = function () { + var zr = this._zr; + release(zr, MUTEX_RESOURCE_KEY, this._uid); + each(this._handlers, function (handler, eventName) { + zr.off(eventName, handler); + }); + this._brushType = this._brushOption = null; + }; + /** + * @param panelOpts If not pass, it is global brush. + */ + BrushController.prototype.setPanels = function (panelOpts) { + if (panelOpts && panelOpts.length) { + var panels_1 = this._panels = {}; + each(panelOpts, function (panelOpts) { + panels_1[panelOpts.panelId] = clone(panelOpts); + }); + } else { + this._panels = null; + } + return this; + }; + BrushController.prototype.mount = function (opt) { + opt = opt || {}; + if ("development" !== 'production') { + this._mounted = true; // should be at first. + } + + this._enableGlobalPan = opt.enableGlobalPan; + var thisGroup = this.group; + this._zr.add(thisGroup); + thisGroup.attr({ + x: opt.x || 0, + y: opt.y || 0, + rotation: opt.rotation || 0, + scaleX: opt.scaleX || 1, + scaleY: opt.scaleY || 1 + }); + this._transform = thisGroup.getLocalTransform(); + return this; + }; + // eachCover(cb, context): void { + // each(this._covers, cb, context); + // } + /** + * Update covers. + * @param coverConfigList + * If coverConfigList is null/undefined, all covers removed. + */ + BrushController.prototype.updateCovers = function (coverConfigList) { + if ("development" !== 'production') { + assert(this._mounted); + } + coverConfigList = map(coverConfigList, function (coverConfig) { + return merge(clone(DEFAULT_BRUSH_OPT), coverConfig, true); + }); + var tmpIdPrefix = '\0-brush-index-'; + var oldCovers = this._covers; + var newCovers = this._covers = []; + var controller = this; + var creatingCover = this._creatingCover; + new DataDiffer(oldCovers, coverConfigList, oldGetKey, getKey).add(addOrUpdate).update(addOrUpdate).remove(remove).execute(); + return this; + function getKey(brushOption, index) { + return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + '-' + brushOption.brushType; + } + function oldGetKey(cover, index) { + return getKey(cover.__brushOption, index); + } + function addOrUpdate(newIndex, oldIndex) { + var newBrushInternal = coverConfigList[newIndex]; + // Consider setOption in event listener of brushSelect, + // where updating cover when creating should be forbidden. + if (oldIndex != null && oldCovers[oldIndex] === creatingCover) { + newCovers[newIndex] = oldCovers[oldIndex]; + } else { + var cover = newCovers[newIndex] = oldIndex != null ? (oldCovers[oldIndex].__brushOption = newBrushInternal, oldCovers[oldIndex]) : endCreating(controller, createCover(controller, newBrushInternal)); + updateCoverAfterCreation(controller, cover); + } + } + function remove(oldIndex) { + if (oldCovers[oldIndex] !== creatingCover) { + controller.group.remove(oldCovers[oldIndex]); + } + } + }; + BrushController.prototype.unmount = function () { + if ("development" !== 'production') { + if (!this._mounted) { + return; + } + } + this.enableBrush(false); + // container may 'removeAll' outside. + clearCovers(this); + this._zr.remove(this.group); + if ("development" !== 'production') { + this._mounted = false; // should be at last. + } + + return this; + }; + BrushController.prototype.dispose = function () { + this.unmount(); + this.off(); + }; + return BrushController; + }(Eventful); + function createCover(controller, brushOption) { + var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption); + cover.__brushOption = brushOption; + updateZ(cover, brushOption); + controller.group.add(cover); + return cover; + } + function endCreating(controller, creatingCover) { + var coverRenderer = getCoverRenderer(creatingCover); + if (coverRenderer.endCreating) { + coverRenderer.endCreating(controller, creatingCover); + updateZ(creatingCover, creatingCover.__brushOption); + } + return creatingCover; + } + function updateCoverShape(controller, cover) { + var brushOption = cover.__brushOption; + getCoverRenderer(cover).updateCoverShape(controller, cover, brushOption.range, brushOption); + } + function updateZ(cover, brushOption) { + var z = brushOption.z; + z == null && (z = COVER_Z); + cover.traverse(function (el) { + el.z = z; + el.z2 = z; // Consider in given container. + }); + } + + function updateCoverAfterCreation(controller, cover) { + getCoverRenderer(cover).updateCommon(controller, cover); + updateCoverShape(controller, cover); + } + function getCoverRenderer(cover) { + return coverRenderers[cover.__brushOption.brushType]; + } + // return target panel or `true` (means global panel) + function getPanelByPoint(controller, e, localCursorPoint) { + var panels = controller._panels; + if (!panels) { + return BRUSH_PANEL_GLOBAL; // Global panel + } + + var panel; + var transform = controller._transform; + each(panels, function (pn) { + pn.isTargetByCursor(e, localCursorPoint, transform) && (panel = pn); + }); + return panel; + } + // Return a panel or true + function getPanelByCover(controller, cover) { + var panels = controller._panels; + if (!panels) { + return BRUSH_PANEL_GLOBAL; // Global panel + } + + var panelId = cover.__brushOption.panelId; + // User may give cover without coord sys info, + // which is then treated as global panel. + return panelId != null ? panels[panelId] : BRUSH_PANEL_GLOBAL; + } + function clearCovers(controller) { + var covers = controller._covers; + var originalLength = covers.length; + each(covers, function (cover) { + controller.group.remove(cover); + }, controller); + covers.length = 0; + return !!originalLength; + } + function trigger$1(controller, opt) { + var areas = map(controller._covers, function (cover) { + var brushOption = cover.__brushOption; + var range = clone(brushOption.range); + return { + brushType: brushOption.brushType, + panelId: brushOption.panelId, + range: range + }; + }); + controller.trigger('brush', { + areas: areas, + isEnd: !!opt.isEnd, + removeOnClick: !!opt.removeOnClick + }); + } + function shouldShowCover(controller) { + var track = controller._track; + if (!track.length) { + return false; + } + var p2 = track[track.length - 1]; + var p1 = track[0]; + var dx = p2[0] - p1[0]; + var dy = p2[1] - p1[1]; + var dist = mathPow$2(dx * dx + dy * dy, 0.5); + return dist > UNSELECT_THRESHOLD; + } + function getTrackEnds(track) { + var tail = track.length - 1; + tail < 0 && (tail = 0); + return [track[0], track[tail]]; + } + function createBaseRectCover(rectRangeConverter, controller, brushOption, edgeNameSequences) { + var cover = new Group(); + cover.add(new Rect({ + name: 'main', + style: makeStyle(brushOption), + silent: true, + draggable: true, + cursor: 'move', + drift: curry(driftRect, rectRangeConverter, controller, cover, ['n', 's', 'w', 'e']), + ondragend: curry(trigger$1, controller, { + isEnd: true + }) + })); + each(edgeNameSequences, function (nameSequence) { + cover.add(new Rect({ + name: nameSequence.join(''), + style: { + opacity: 0 + }, + draggable: true, + silent: true, + invisible: true, + drift: curry(driftRect, rectRangeConverter, controller, cover, nameSequence), + ondragend: curry(trigger$1, controller, { + isEnd: true + }) + })); + }); + return cover; + } + function updateBaseRect(controller, cover, localRange, brushOption) { + var lineWidth = brushOption.brushStyle.lineWidth || 0; + var handleSize = mathMax$9(lineWidth, MIN_RESIZE_LINE_WIDTH); + var x = localRange[0][0]; + var y = localRange[1][0]; + var xa = x - lineWidth / 2; + var ya = y - lineWidth / 2; + var x2 = localRange[0][1]; + var y2 = localRange[1][1]; + var x2a = x2 - handleSize + lineWidth / 2; + var y2a = y2 - handleSize + lineWidth / 2; + var width = x2 - x; + var height = y2 - y; + var widtha = width + lineWidth; + var heighta = height + lineWidth; + updateRectShape(controller, cover, 'main', x, y, width, height); + if (brushOption.transformable) { + updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta); + updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta); + updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize); + updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize); + updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize); + updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize); + updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize); + updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize); + } + } + function updateCommon(controller, cover) { + var brushOption = cover.__brushOption; + var transformable = brushOption.transformable; + var mainEl = cover.childAt(0); + mainEl.useStyle(makeStyle(brushOption)); + mainEl.attr({ + silent: !transformable, + cursor: transformable ? 'move' : 'default' + }); + each([['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']], function (nameSequence) { + var el = cover.childOfName(nameSequence.join('')); + var globalDir = nameSequence.length === 1 ? getGlobalDirection1(controller, nameSequence[0]) : getGlobalDirection2(controller, nameSequence); + el && el.attr({ + silent: !transformable, + invisible: !transformable, + cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null + }); + }); + } + function updateRectShape(controller, cover, name, x, y, w, h) { + var el = cover.childOfName(name); + el && el.setShape(pointsToRect(clipByPanel(controller, cover, [[x, y], [x + w, y + h]]))); + } + function makeStyle(brushOption) { + return defaults({ + strokeNoScale: true + }, brushOption.brushStyle); + } + function formatRectRange(x, y, x2, y2) { + var min = [mathMin$9(x, x2), mathMin$9(y, y2)]; + var max = [mathMax$9(x, x2), mathMax$9(y, y2)]; + return [[min[0], max[0]], [min[1], max[1]] // y range + ]; + } + + function getTransform$1(controller) { + return getTransform(controller.group); + } + function getGlobalDirection1(controller, localDirName) { + var map = { + w: 'left', + e: 'right', + n: 'top', + s: 'bottom' + }; + var inverseMap = { + left: 'w', + right: 'e', + top: 'n', + bottom: 's' + }; + var dir = transformDirection(map[localDirName], getTransform$1(controller)); + return inverseMap[dir]; + } + function getGlobalDirection2(controller, localDirNameSeq) { + var globalDir = [getGlobalDirection1(controller, localDirNameSeq[0]), getGlobalDirection1(controller, localDirNameSeq[1])]; + (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse(); + return globalDir.join(''); + } + function driftRect(rectRangeConverter, controller, cover, dirNameSequence, dx, dy) { + var brushOption = cover.__brushOption; + var rectRange = rectRangeConverter.toRectRange(brushOption.range); + var localDelta = toLocalDelta(controller, dx, dy); + each(dirNameSequence, function (dirName) { + var ind = DIRECTION_MAP[dirName]; + rectRange[ind[0]][ind[1]] += localDelta[ind[0]]; + }); + brushOption.range = rectRangeConverter.fromRectRange(formatRectRange(rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1])); + updateCoverAfterCreation(controller, cover); + trigger$1(controller, { + isEnd: false + }); + } + function driftPolygon(controller, cover, dx, dy) { + var range = cover.__brushOption.range; + var localDelta = toLocalDelta(controller, dx, dy); + each(range, function (point) { + point[0] += localDelta[0]; + point[1] += localDelta[1]; + }); + updateCoverAfterCreation(controller, cover); + trigger$1(controller, { + isEnd: false + }); + } + function toLocalDelta(controller, dx, dy) { + var thisGroup = controller.group; + var localD = thisGroup.transformCoordToLocal(dx, dy); + var localZero = thisGroup.transformCoordToLocal(0, 0); + return [localD[0] - localZero[0], localD[1] - localZero[1]]; + } + function clipByPanel(controller, cover, data) { + var panel = getPanelByCover(controller, cover); + return panel && panel !== BRUSH_PANEL_GLOBAL ? panel.clipPath(data, controller._transform) : clone(data); + } + function pointsToRect(points) { + var xmin = mathMin$9(points[0][0], points[1][0]); + var ymin = mathMin$9(points[0][1], points[1][1]); + var xmax = mathMax$9(points[0][0], points[1][0]); + var ymax = mathMax$9(points[0][1], points[1][1]); + return { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; + } + function resetCursor(controller, e, localCursorPoint) { + if ( + // Check active + !controller._brushType + // resetCursor should be always called when mouse is in zr area, + // but not called when mouse is out of zr area to avoid bad influence + // if `mousemove`, `mouseup` are triggered from `document` event. + || isOutsideZrArea(controller, e.offsetX, e.offsetY)) { + return; + } + var zr = controller._zr; + var covers = controller._covers; + var currPanel = getPanelByPoint(controller, e, localCursorPoint); + // Check whether in covers. + if (!controller._dragging) { + for (var i = 0; i < covers.length; i++) { + var brushOption = covers[i].__brushOption; + if (currPanel && (currPanel === BRUSH_PANEL_GLOBAL || brushOption.panelId === currPanel.panelId) && coverRenderers[brushOption.brushType].contain(covers[i], localCursorPoint[0], localCursorPoint[1])) { + // Use cursor style set on cover. + return; + } + } + } + currPanel && zr.setCursorStyle('crosshair'); + } + function preventDefault(e) { + var rawE = e.event; + rawE.preventDefault && rawE.preventDefault(); + } + function mainShapeContain(cover, x, y) { + return cover.childOfName('main').contain(x, y); + } + function updateCoverByMouse(controller, e, localCursorPoint, isEnd) { + var creatingCover = controller._creatingCover; + var panel = controller._creatingPanel; + var thisBrushOption = controller._brushOption; + var eventParams; + controller._track.push(localCursorPoint.slice()); + if (shouldShowCover(controller) || creatingCover) { + if (panel && !creatingCover) { + thisBrushOption.brushMode === 'single' && clearCovers(controller); + var brushOption = clone(thisBrushOption); + brushOption.brushType = determineBrushType(brushOption.brushType, panel); + brushOption.panelId = panel === BRUSH_PANEL_GLOBAL ? null : panel.panelId; + creatingCover = controller._creatingCover = createCover(controller, brushOption); + controller._covers.push(creatingCover); + } + if (creatingCover) { + var coverRenderer = coverRenderers[determineBrushType(controller._brushType, panel)]; + var coverBrushOption = creatingCover.__brushOption; + coverBrushOption.range = coverRenderer.getCreatingRange(clipByPanel(controller, creatingCover, controller._track)); + if (isEnd) { + endCreating(controller, creatingCover); + coverRenderer.updateCommon(controller, creatingCover); + } + updateCoverShape(controller, creatingCover); + eventParams = { + isEnd: isEnd + }; + } + } else if (isEnd && thisBrushOption.brushMode === 'single' && thisBrushOption.removeOnClick) { + // Help user to remove covers easily, only by a tiny drag, in 'single' mode. + // But a single click do not clear covers, because user may have casual + // clicks (for example, click on other component and do not expect covers + // disappear). + // Only some cover removed, trigger action, but not every click trigger action. + if (getPanelByPoint(controller, e, localCursorPoint) && clearCovers(controller)) { + eventParams = { + isEnd: isEnd, + removeOnClick: true + }; + } + } + return eventParams; + } + function determineBrushType(brushType, panel) { + if (brushType === 'auto') { + if ("development" !== 'production') { + assert(panel && panel.defaultBrushType, 'MUST have defaultBrushType when brushType is "atuo"'); + } + return panel.defaultBrushType; + } + return brushType; + } + var pointerHandlers = { + mousedown: function (e) { + if (this._dragging) { + // In case some browser do not support globalOut, + // and release mouse out side the browser. + handleDragEnd(this, e); + } else if (!e.target || !e.target.draggable) { + preventDefault(e); + var localCursorPoint = this.group.transformCoordToLocal(e.offsetX, e.offsetY); + this._creatingCover = null; + var panel = this._creatingPanel = getPanelByPoint(this, e, localCursorPoint); + if (panel) { + this._dragging = true; + this._track = [localCursorPoint.slice()]; + } + } + }, + mousemove: function (e) { + var x = e.offsetX; + var y = e.offsetY; + var localCursorPoint = this.group.transformCoordToLocal(x, y); + resetCursor(this, e, localCursorPoint); + if (this._dragging) { + preventDefault(e); + var eventParams = updateCoverByMouse(this, e, localCursorPoint, false); + eventParams && trigger$1(this, eventParams); + } + }, + mouseup: function (e) { + handleDragEnd(this, e); + } + }; + function handleDragEnd(controller, e) { + if (controller._dragging) { + preventDefault(e); + var x = e.offsetX; + var y = e.offsetY; + var localCursorPoint = controller.group.transformCoordToLocal(x, y); + var eventParams = updateCoverByMouse(controller, e, localCursorPoint, true); + controller._dragging = false; + controller._track = []; + controller._creatingCover = null; + // trigger event should be at final, after procedure will be nested. + eventParams && trigger$1(controller, eventParams); + } + } + function isOutsideZrArea(controller, x, y) { + var zr = controller._zr; + return x < 0 || x > zr.getWidth() || y < 0 || y > zr.getHeight(); + } + /** + * key: brushType + */ + var coverRenderers = { + lineX: getLineRenderer(0), + lineY: getLineRenderer(1), + rect: { + createCover: function (controller, brushOption) { + function returnInput(range) { + return range; + } + return createBaseRectCover({ + toRectRange: returnInput, + fromRectRange: returnInput + }, controller, brushOption, [['w'], ['e'], ['n'], ['s'], ['s', 'e'], ['s', 'w'], ['n', 'e'], ['n', 'w']]); + }, + getCreatingRange: function (localTrack) { + var ends = getTrackEnds(localTrack); + return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]); + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + updateBaseRect(controller, cover, localRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }, + polygon: { + createCover: function (controller, brushOption) { + var cover = new Group(); + // Do not use graphic.Polygon because graphic.Polyline do not close the + // border of the shape when drawing, which is a better experience for user. + cover.add(new Polyline({ + name: 'main', + style: makeStyle(brushOption), + silent: true + })); + return cover; + }, + getCreatingRange: function (localTrack) { + return localTrack; + }, + endCreating: function (controller, cover) { + cover.remove(cover.childAt(0)); + // Use graphic.Polygon close the shape. + cover.add(new Polygon({ + name: 'main', + draggable: true, + drift: curry(driftPolygon, controller, cover), + ondragend: curry(trigger$1, controller, { + isEnd: true + }) + })); + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + cover.childAt(0).setShape({ + points: clipByPanel(controller, cover, localRange) + }); + }, + updateCommon: updateCommon, + contain: mainShapeContain + } + }; + function getLineRenderer(xyIndex) { + return { + createCover: function (controller, brushOption) { + return createBaseRectCover({ + toRectRange: function (range) { + var rectRange = [range, [0, 100]]; + xyIndex && rectRange.reverse(); + return rectRange; + }, + fromRectRange: function (rectRange) { + return rectRange[xyIndex]; + } + }, controller, brushOption, [[['w'], ['e']], [['n'], ['s']]][xyIndex]); + }, + getCreatingRange: function (localTrack) { + var ends = getTrackEnds(localTrack); + var min = mathMin$9(ends[0][xyIndex], ends[1][xyIndex]); + var max = mathMax$9(ends[0][xyIndex], ends[1][xyIndex]); + return [min, max]; + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + var otherExtent; + // If brushWidth not specified, fit the panel. + var panel = getPanelByCover(controller, cover); + if (panel !== BRUSH_PANEL_GLOBAL && panel.getLinearBrushOtherExtent) { + otherExtent = panel.getLinearBrushOtherExtent(xyIndex); + } else { + var zr = controller._zr; + otherExtent = [0, [zr.getWidth(), zr.getHeight()][1 - xyIndex]]; + } + var rectRange = [localRange, otherExtent]; + xyIndex && rectRange.reverse(); + updateBaseRect(controller, cover, rectRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }; + } + + function makeRectPanelClipPath(rect) { + rect = normalizeRect(rect); + return function (localPoints) { + return clipPointsByRect(localPoints, rect); + }; + } + function makeLinearBrushOtherExtent(rect, specifiedXYIndex) { + rect = normalizeRect(rect); + return function (xyIndex) { + var idx = specifiedXYIndex != null ? specifiedXYIndex : xyIndex; + var brushWidth = idx ? rect.width : rect.height; + var base = idx ? rect.x : rect.y; + return [base, base + (brushWidth || 0)]; + }; + } + function makeRectIsTargetByCursor(rect, api, targetModel) { + var boundingRect = normalizeRect(rect); + return function (e, localCursorPoint) { + return boundingRect.contain(localCursorPoint[0], localCursorPoint[1]) && !onIrrelevantElement(e, api, targetModel); + }; + } + // Consider width/height is negative. + function normalizeRect(rect) { + return BoundingRect.create(rect); + } + + var elementList = ['axisLine', 'axisTickLabel', 'axisName']; + var ParallelAxisView = /** @class */function (_super) { + __extends(ParallelAxisView, _super); + function ParallelAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ParallelAxisView.type; + return _this; + } + ParallelAxisView.prototype.init = function (ecModel, api) { + _super.prototype.init.apply(this, arguments); + (this._brushController = new BrushController(api.getZr())).on('brush', bind(this._onBrush, this)); + }; + ParallelAxisView.prototype.render = function (axisModel, ecModel, api, payload) { + if (fromAxisAreaSelect(axisModel, ecModel, payload)) { + return; + } + this.axisModel = axisModel; + this.api = api; + this.group.removeAll(); + var oldAxisGroup = this._axisGroup; + this._axisGroup = new Group(); + this.group.add(this._axisGroup); + if (!axisModel.get('show')) { + return; + } + var coordSysModel = getCoordSysModel(axisModel, ecModel); + var coordSys = coordSysModel.coordinateSystem; + var areaSelectStyle = axisModel.getAreaSelectStyle(); + var areaWidth = areaSelectStyle.width; + var dim = axisModel.axis.dim; + var axisLayout = coordSys.getAxisLayout(dim); + var builderOpt = extend({ + strokeContainThreshold: areaWidth + }, axisLayout); + var axisBuilder = new AxisBuilder(axisModel, builderOpt); + each(elementList, axisBuilder.add, axisBuilder); + this._axisGroup.add(axisBuilder.getGroup()); + this._refreshBrushController(builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api); + groupTransition(oldAxisGroup, this._axisGroup, axisModel); + }; + // /** + // * @override + // */ + // updateVisual(axisModel, ecModel, api, payload) { + // this._brushController && this._brushController + // .updateCovers(getCoverInfoList(axisModel)); + // } + ParallelAxisView.prototype._refreshBrushController = function (builderOpt, areaSelectStyle, axisModel, coordSysModel, areaWidth, api) { + // After filtering, axis may change, select area needs to be update. + var extent = axisModel.axis.getExtent(); + var extentLen = extent[1] - extent[0]; + var extra = Math.min(30, Math.abs(extentLen) * 0.1); // Arbitrary value. + // width/height might be negative, which will be + // normalized in BoundingRect. + var rect = BoundingRect.create({ + x: extent[0], + y: -areaWidth / 2, + width: extentLen, + height: areaWidth + }); + rect.x -= extra; + rect.width += 2 * extra; + this._brushController.mount({ + enableGlobalPan: true, + rotation: builderOpt.rotation, + x: builderOpt.position[0], + y: builderOpt.position[1] + }).setPanels([{ + panelId: 'pl', + clipPath: makeRectPanelClipPath(rect), + isTargetByCursor: makeRectIsTargetByCursor(rect, api, coordSysModel), + getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect, 0) + }]).enableBrush({ + brushType: 'lineX', + brushStyle: areaSelectStyle, + removeOnClick: true + }).updateCovers(getCoverInfoList(axisModel)); + }; + ParallelAxisView.prototype._onBrush = function (eventParam) { + var coverInfoList = eventParam.areas; + // Do not cache these object, because the mey be changed. + var axisModel = this.axisModel; + var axis = axisModel.axis; + var intervals = map(coverInfoList, function (coverInfo) { + return [axis.coordToData(coverInfo.range[0], true), axis.coordToData(coverInfo.range[1], true)]; + }); + // If realtime is true, action is not dispatched on drag end, because + // the drag end emits the same params with the last drag move event, + // and may have some delay when using touch pad. + if (!axisModel.option.realtime === eventParam.isEnd || eventParam.removeOnClick) { + // jshint ignore:line + this.api.dispatchAction({ + type: 'axisAreaSelect', + parallelAxisId: axisModel.id, + intervals: intervals + }); + } + }; + ParallelAxisView.prototype.dispose = function () { + this._brushController.dispose(); + }; + ParallelAxisView.type = 'parallelAxis'; + return ParallelAxisView; + }(ComponentView); + function fromAxisAreaSelect(axisModel, ecModel, payload) { + return payload && payload.type === 'axisAreaSelect' && ecModel.findComponents({ + mainType: 'parallelAxis', + query: payload + })[0] === axisModel; + } + function getCoverInfoList(axisModel) { + var axis = axisModel.axis; + return map(axisModel.activeIntervals, function (interval) { + return { + brushType: 'lineX', + panelId: 'pl', + range: [axis.dataToCoord(interval[0], true), axis.dataToCoord(interval[1], true)] + }; + }); + } + function getCoordSysModel(axisModel, ecModel) { + return ecModel.getComponent('parallel', axisModel.get('parallelIndex')); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var actionInfo$1 = { + type: 'axisAreaSelect', + event: 'axisAreaSelected' + // update: 'updateVisual' + }; + + function installParallelActions(registers) { + registers.registerAction(actionInfo$1, function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'parallelAxis', + query: payload + }, function (parallelAxisModel) { + parallelAxisModel.axis.model.setActiveIntervals(payload.intervals); + }); + }); + /** + * @payload + */ + registers.registerAction('parallelAxisExpand', function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'parallel', + query: payload + }, function (parallelModel) { + parallelModel.setAxisExpand(payload); + }); + }); + } + + var defaultAxisOption = { + type: 'value', + areaSelectStyle: { + width: 20, + borderWidth: 1, + borderColor: 'rgba(160,197,232)', + color: 'rgba(160,197,232)', + opacity: 0.3 + }, + realtime: true, + z: 10 + }; + function install$g(registers) { + registers.registerComponentView(ParallelView$1); + registers.registerComponentModel(ParallelModel); + registers.registerCoordinateSystem('parallel', parallelCoordSysCreator); + registers.registerPreprocessor(parallelPreprocessor); + registers.registerComponentModel(ParallelAxisModel); + registers.registerComponentView(ParallelAxisView); + axisModelCreator(registers, 'parallel', ParallelAxisModel, defaultAxisOption); + installParallelActions(registers); + } + + function install$h(registers) { + use(install$g); + registers.registerChartView(ParallelView); + registers.registerSeriesModel(ParallelSeriesModel); + registers.registerVisual(registers.PRIORITY.VISUAL.BRUSH, parallelVisual); + } + + var SankeyPathShape = /** @class */function () { + function SankeyPathShape() { + this.x1 = 0; + this.y1 = 0; + this.x2 = 0; + this.y2 = 0; + this.cpx1 = 0; + this.cpy1 = 0; + this.cpx2 = 0; + this.cpy2 = 0; + this.extent = 0; + } + return SankeyPathShape; + }(); + var SankeyPath = /** @class */function (_super) { + __extends(SankeyPath, _super); + function SankeyPath(opts) { + return _super.call(this, opts) || this; + } + SankeyPath.prototype.getDefaultShape = function () { + return new SankeyPathShape(); + }; + SankeyPath.prototype.buildPath = function (ctx, shape) { + var extent = shape.extent; + ctx.moveTo(shape.x1, shape.y1); + ctx.bezierCurveTo(shape.cpx1, shape.cpy1, shape.cpx2, shape.cpy2, shape.x2, shape.y2); + if (shape.orient === 'vertical') { + ctx.lineTo(shape.x2 + extent, shape.y2); + ctx.bezierCurveTo(shape.cpx2 + extent, shape.cpy2, shape.cpx1 + extent, shape.cpy1, shape.x1 + extent, shape.y1); + } else { + ctx.lineTo(shape.x2, shape.y2 + extent); + ctx.bezierCurveTo(shape.cpx2, shape.cpy2 + extent, shape.cpx1, shape.cpy1 + extent, shape.x1, shape.y1 + extent); + } + ctx.closePath(); + }; + SankeyPath.prototype.highlight = function () { + enterEmphasis(this); + }; + SankeyPath.prototype.downplay = function () { + leaveEmphasis(this); + }; + return SankeyPath; + }(Path); + var SankeyView = /** @class */function (_super) { + __extends(SankeyView, _super); + function SankeyView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SankeyView.type; + _this._focusAdjacencyDisabled = false; + return _this; + } + SankeyView.prototype.render = function (seriesModel, ecModel, api) { + var sankeyView = this; + var graph = seriesModel.getGraph(); + var group = this.group; + var layoutInfo = seriesModel.layoutInfo; + // view width + var width = layoutInfo.width; + // view height + var height = layoutInfo.height; + var nodeData = seriesModel.getData(); + var edgeData = seriesModel.getData('edge'); + var orient = seriesModel.get('orient'); + this._model = seriesModel; + group.removeAll(); + group.x = layoutInfo.x; + group.y = layoutInfo.y; + // generate a bezire Curve for each edge + graph.eachEdge(function (edge) { + var curve = new SankeyPath(); + var ecData = getECData(curve); + ecData.dataIndex = edge.dataIndex; + ecData.seriesIndex = seriesModel.seriesIndex; + ecData.dataType = 'edge'; + var edgeModel = edge.getModel(); + var lineStyleModel = edgeModel.getModel('lineStyle'); + var curvature = lineStyleModel.get('curveness'); + var n1Layout = edge.node1.getLayout(); + var node1Model = edge.node1.getModel(); + var dragX1 = node1Model.get('localX'); + var dragY1 = node1Model.get('localY'); + var n2Layout = edge.node2.getLayout(); + var node2Model = edge.node2.getModel(); + var dragX2 = node2Model.get('localX'); + var dragY2 = node2Model.get('localY'); + var edgeLayout = edge.getLayout(); + var x1; + var y1; + var x2; + var y2; + var cpx1; + var cpy1; + var cpx2; + var cpy2; + curve.shape.extent = Math.max(1, edgeLayout.dy); + curve.shape.orient = orient; + if (orient === 'vertical') { + x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + edgeLayout.sy; + y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + n1Layout.dy; + x2 = (dragX2 != null ? dragX2 * width : n2Layout.x) + edgeLayout.ty; + y2 = dragY2 != null ? dragY2 * height : n2Layout.y; + cpx1 = x1; + cpy1 = y1 * (1 - curvature) + y2 * curvature; + cpx2 = x2; + cpy2 = y1 * curvature + y2 * (1 - curvature); + } else { + x1 = (dragX1 != null ? dragX1 * width : n1Layout.x) + n1Layout.dx; + y1 = (dragY1 != null ? dragY1 * height : n1Layout.y) + edgeLayout.sy; + x2 = dragX2 != null ? dragX2 * width : n2Layout.x; + y2 = (dragY2 != null ? dragY2 * height : n2Layout.y) + edgeLayout.ty; + cpx1 = x1 * (1 - curvature) + x2 * curvature; + cpy1 = y1; + cpx2 = x1 * curvature + x2 * (1 - curvature); + cpy2 = y2; + } + curve.setShape({ + x1: x1, + y1: y1, + x2: x2, + y2: y2, + cpx1: cpx1, + cpy1: cpy1, + cpx2: cpx2, + cpy2: cpy2 + }); + curve.useStyle(lineStyleModel.getItemStyle()); + // Special color, use source node color or target node color + applyCurveStyle(curve.style, orient, edge); + var defaultEdgeLabelText = "" + edgeModel.get('value'); + var edgeLabelStateModels = getLabelStatesModels(edgeModel, 'edgeLabel'); + setLabelStyle(curve, edgeLabelStateModels, { + labelFetcher: { + getFormattedLabel: function (dataIndex, stateName, dataType, labelDimIndex, formatter, extendParams) { + return seriesModel.getFormattedLabel(dataIndex, stateName, 'edge', labelDimIndex, + // ensure edgeLabel formatter is provided + // to prevent the inheritance from `label.formatter` of the series + retrieve3(formatter, edgeLabelStateModels.normal && edgeLabelStateModels.normal.get('formatter'), defaultEdgeLabelText), extendParams); + } + }, + labelDataIndex: edge.dataIndex, + defaultText: defaultEdgeLabelText + }); + curve.setTextConfig({ + position: 'inside' + }); + var emphasisModel = edgeModel.getModel('emphasis'); + setStatesStylesFromModel(curve, edgeModel, 'lineStyle', function (model) { + var style = model.getItemStyle(); + applyCurveStyle(style, orient, edge); + return style; + }); + group.add(curve); + edgeData.setItemGraphicEl(edge.dataIndex, curve); + var focus = emphasisModel.get('focus'); + toggleHoverEmphasis(curve, focus === 'adjacency' ? edge.getAdjacentDataIndices() : focus === 'trajectory' ? edge.getTrajectoryDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }); + // Generate a rect for each node + graph.eachNode(function (node) { + var layout = node.getLayout(); + var itemModel = node.getModel(); + var dragX = itemModel.get('localX'); + var dragY = itemModel.get('localY'); + var emphasisModel = itemModel.getModel('emphasis'); + var rect = new Rect({ + shape: { + x: dragX != null ? dragX * width : layout.x, + y: dragY != null ? dragY * height : layout.y, + width: layout.dx, + height: layout.dy + }, + style: itemModel.getModel('itemStyle').getItemStyle(), + z2: 10 + }); + setLabelStyle(rect, getLabelStatesModels(itemModel), { + labelFetcher: { + getFormattedLabel: function (dataIndex, stateName) { + return seriesModel.getFormattedLabel(dataIndex, stateName, 'node'); + } + }, + labelDataIndex: node.dataIndex, + defaultText: node.id + }); + rect.disableLabelAnimation = true; + rect.setStyle('fill', node.getVisual('color')); + rect.setStyle('decal', node.getVisual('style').decal); + setStatesStylesFromModel(rect, itemModel); + group.add(rect); + nodeData.setItemGraphicEl(node.dataIndex, rect); + getECData(rect).dataType = 'node'; + var focus = emphasisModel.get('focus'); + toggleHoverEmphasis(rect, focus === 'adjacency' ? node.getAdjacentDataIndices() : focus === 'trajectory' ? node.getTrajectoryDataIndices() : focus, emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }); + nodeData.eachItemGraphicEl(function (el, dataIndex) { + var itemModel = nodeData.getItemModel(dataIndex); + if (itemModel.get('draggable')) { + el.drift = function (dx, dy) { + sankeyView._focusAdjacencyDisabled = true; + this.shape.x += dx; + this.shape.y += dy; + this.dirty(); + api.dispatchAction({ + type: 'dragNode', + seriesId: seriesModel.id, + dataIndex: nodeData.getRawIndex(dataIndex), + localX: this.shape.x / width, + localY: this.shape.y / height + }); + }; + el.ondragend = function () { + sankeyView._focusAdjacencyDisabled = false; + }; + el.draggable = true; + el.cursor = 'move'; + } + }); + if (!this._data && seriesModel.isAnimationEnabled()) { + group.setClipPath(createGridClipShape$1(group.getBoundingRect(), seriesModel, function () { + group.removeClipPath(); + })); + } + this._data = seriesModel.getData(); + }; + SankeyView.prototype.dispose = function () {}; + SankeyView.type = 'sankey'; + return SankeyView; + }(ChartView); + /** + * Special color, use source node color or target node color + * @param curveProps curve's style to parse + * @param orient direction + * @param edge current curve data + */ + function applyCurveStyle(curveProps, orient, edge) { + switch (curveProps.fill) { + case 'source': + curveProps.fill = edge.node1.getVisual('color'); + curveProps.decal = edge.node1.getVisual('style').decal; + break; + case 'target': + curveProps.fill = edge.node2.getVisual('color'); + curveProps.decal = edge.node2.getVisual('style').decal; + break; + case 'gradient': + var sourceColor = edge.node1.getVisual('color'); + var targetColor = edge.node2.getVisual('color'); + if (isString(sourceColor) && isString(targetColor)) { + curveProps.fill = new LinearGradient(0, 0, +(orient === 'horizontal'), +(orient === 'vertical'), [{ + color: sourceColor, + offset: 0 + }, { + color: targetColor, + offset: 1 + }]); + } + } + } + // Add animation to the view + function createGridClipShape$1(rect, seriesModel, cb) { + var rectEl = new Rect({ + shape: { + x: rect.x - 10, + y: rect.y - 10, + width: 0, + height: rect.height + 20 + } + }); + initProps(rectEl, { + shape: { + width: rect.width + 20 + } + }, seriesModel, cb); + return rectEl; + } + + var SankeySeriesModel = /** @class */function (_super) { + __extends(SankeySeriesModel, _super); + function SankeySeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SankeySeriesModel.type; + return _this; + } + /** + * Init a graph data structure from data in option series + */ + SankeySeriesModel.prototype.getInitialData = function (option, ecModel) { + var links = option.edges || option.links; + var nodes = option.data || option.nodes; + var levels = option.levels; + this.levelModels = []; + var levelModels = this.levelModels; + for (var i = 0; i < levels.length; i++) { + if (levels[i].depth != null && levels[i].depth >= 0) { + levelModels[levels[i].depth] = new Model(levels[i], this, ecModel); + } else { + if ("development" !== 'production') { + throw new Error('levels[i].depth is mandatory and should be natural number'); + } + } + } + if (nodes && links) { + var graph = createGraphFromNodeEdge(nodes, links, this, true, beforeLink); + return graph.data; + } + function beforeLink(nodeData, edgeData) { + nodeData.wrapMethod('getItemModel', function (model, idx) { + var seriesModel = model.parentModel; + var layout = seriesModel.getData().getItemLayout(idx); + if (layout) { + var nodeDepth = layout.depth; + var levelModel = seriesModel.levelModels[nodeDepth]; + if (levelModel) { + model.parentModel = levelModel; + } + } + return model; + }); + edgeData.wrapMethod('getItemModel', function (model, idx) { + var seriesModel = model.parentModel; + var edge = seriesModel.getGraph().getEdgeByIndex(idx); + var layout = edge.node1.getLayout(); + if (layout) { + var depth = layout.depth; + var levelModel = seriesModel.levelModels[depth]; + if (levelModel) { + model.parentModel = levelModel; + } + } + return model; + }); + } + }; + SankeySeriesModel.prototype.setNodePosition = function (dataIndex, localPosition) { + var nodes = this.option.data || this.option.nodes; + var dataItem = nodes[dataIndex]; + dataItem.localX = localPosition[0]; + dataItem.localY = localPosition[1]; + }; + /** + * Return the graphic data structure + * + * @return graphic data structure + */ + SankeySeriesModel.prototype.getGraph = function () { + return this.getData().graph; + }; + /** + * Get edge data of graphic data structure + * + * @return data structure of list + */ + SankeySeriesModel.prototype.getEdgeData = function () { + return this.getGraph().edgeData; + }; + SankeySeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + function noValue(val) { + return isNaN(val) || val == null; + } + // dataType === 'node' or empty do not show tooltip by default + if (dataType === 'edge') { + var params = this.getDataParams(dataIndex, dataType); + var rawDataOpt = params.data; + var edgeValue = params.value; + var edgeName = rawDataOpt.source + ' -- ' + rawDataOpt.target; + return createTooltipMarkup('nameValue', { + name: edgeName, + value: edgeValue, + noValue: noValue(edgeValue) + }); + } + // dataType === 'node' + else { + var node = this.getGraph().getNodeByIndex(dataIndex); + var value = node.getLayout().value; + var name_1 = this.getDataParams(dataIndex, dataType).data.name; + return createTooltipMarkup('nameValue', { + name: name_1 != null ? name_1 + '' : null, + value: value, + noValue: noValue(value) + }); + } + }; + SankeySeriesModel.prototype.optionUpdated = function () {}; + // Override Series.getDataParams() + SankeySeriesModel.prototype.getDataParams = function (dataIndex, dataType) { + var params = _super.prototype.getDataParams.call(this, dataIndex, dataType); + if (params.value == null && dataType === 'node') { + var node = this.getGraph().getNodeByIndex(dataIndex); + var nodeValue = node.getLayout().value; + params.value = nodeValue; + } + return params; + }; + SankeySeriesModel.type = 'series.sankey'; + SankeySeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'view', + left: '5%', + top: '5%', + right: '20%', + bottom: '5%', + orient: 'horizontal', + nodeWidth: 20, + nodeGap: 8, + draggable: true, + layoutIterations: 32, + label: { + show: true, + position: 'right', + fontSize: 12 + }, + edgeLabel: { + show: false, + fontSize: 12 + }, + levels: [], + nodeAlign: 'justify', + lineStyle: { + color: '#314656', + opacity: 0.2, + curveness: 0.5 + }, + emphasis: { + label: { + show: true + }, + lineStyle: { + opacity: 0.5 + } + }, + select: { + itemStyle: { + borderColor: '#212121' + } + }, + animationEasing: 'linear', + animationDuration: 1000 + }; + return SankeySeriesModel; + }(SeriesModel); + + function sankeyLayout(ecModel, api) { + ecModel.eachSeriesByType('sankey', function (seriesModel) { + var nodeWidth = seriesModel.get('nodeWidth'); + var nodeGap = seriesModel.get('nodeGap'); + var layoutInfo = getViewRect$4(seriesModel, api); + seriesModel.layoutInfo = layoutInfo; + var width = layoutInfo.width; + var height = layoutInfo.height; + var graph = seriesModel.getGraph(); + var nodes = graph.nodes; + var edges = graph.edges; + computeNodeValues(nodes); + var filteredNodes = filter(nodes, function (node) { + return node.getLayout().value === 0; + }); + var iterations = filteredNodes.length !== 0 ? 0 : seriesModel.get('layoutIterations'); + var orient = seriesModel.get('orient'); + var nodeAlign = seriesModel.get('nodeAlign'); + layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign); + }); + } + /** + * Get the layout position of the whole view + */ + function getViewRect$4(seriesModel, api) { + return getLayoutRect(seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + } + function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign) { + computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign); + computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient); + computeEdgeDepths(nodes, orient); + } + /** + * Compute the value of each node by summing the associated edge's value + */ + function computeNodeValues(nodes) { + each(nodes, function (node) { + var value1 = sum(node.outEdges, getEdgeValue); + var value2 = sum(node.inEdges, getEdgeValue); + var nodeRawValue = node.getValue() || 0; + var value = Math.max(value1, value2, nodeRawValue); + node.setLayout({ + value: value + }, true); + }); + } + /** + * Compute the x-position for each node. + * + * Here we use Kahn algorithm to detect cycle when we traverse + * the node to computer the initial x position. + */ + function computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign) { + // Used to mark whether the edge is deleted. if it is deleted, + // the value is 0, otherwise it is 1. + var remainEdges = []; + // Storage each node's indegree. + var indegreeArr = []; + // Used to storage the node with indegree is equal to 0. + var zeroIndegrees = []; + var nextTargetNode = []; + var x = 0; + // let kx = 0; + for (var i = 0; i < edges.length; i++) { + remainEdges[i] = 1; + } + for (var i = 0; i < nodes.length; i++) { + indegreeArr[i] = nodes[i].inEdges.length; + if (indegreeArr[i] === 0) { + zeroIndegrees.push(nodes[i]); + } + } + var maxNodeDepth = -1; + // Traversing nodes using topological sorting to calculate the + // horizontal(if orient === 'horizontal') or vertical(if orient === 'vertical') + // position of the nodes. + while (zeroIndegrees.length) { + for (var idx = 0; idx < zeroIndegrees.length; idx++) { + var node = zeroIndegrees[idx]; + var item = node.hostGraph.data.getRawDataItem(node.dataIndex); + var isItemDepth = item.depth != null && item.depth >= 0; + if (isItemDepth && item.depth > maxNodeDepth) { + maxNodeDepth = item.depth; + } + node.setLayout({ + depth: isItemDepth ? item.depth : x + }, true); + orient === 'vertical' ? node.setLayout({ + dy: nodeWidth + }, true) : node.setLayout({ + dx: nodeWidth + }, true); + for (var edgeIdx = 0; edgeIdx < node.outEdges.length; edgeIdx++) { + var edge = node.outEdges[edgeIdx]; + var indexEdge = edges.indexOf(edge); + remainEdges[indexEdge] = 0; + var targetNode = edge.node2; + var nodeIndex = nodes.indexOf(targetNode); + if (--indegreeArr[nodeIndex] === 0 && nextTargetNode.indexOf(targetNode) < 0) { + nextTargetNode.push(targetNode); + } + } + } + ++x; + zeroIndegrees = nextTargetNode; + nextTargetNode = []; + } + for (var i = 0; i < remainEdges.length; i++) { + if (remainEdges[i] === 1) { + throw new Error('Sankey is a DAG, the original data has cycle!'); + } + } + var maxDepth = maxNodeDepth > x - 1 ? maxNodeDepth : x - 1; + if (nodeAlign && nodeAlign !== 'left') { + adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth); + } + var kx = orient === 'vertical' ? (height - nodeWidth) / maxDepth : (width - nodeWidth) / maxDepth; + scaleNodeBreadths(nodes, kx, orient); + } + function isNodeDepth(node) { + var item = node.hostGraph.data.getRawDataItem(node.dataIndex); + return item.depth != null && item.depth >= 0; + } + function adjustNodeWithNodeAlign(nodes, nodeAlign, orient, maxDepth) { + if (nodeAlign === 'right') { + var nextSourceNode = []; + var remainNodes = nodes; + var nodeHeight = 0; + while (remainNodes.length) { + for (var i = 0; i < remainNodes.length; i++) { + var node = remainNodes[i]; + node.setLayout({ + skNodeHeight: nodeHeight + }, true); + for (var j = 0; j < node.inEdges.length; j++) { + var edge = node.inEdges[j]; + if (nextSourceNode.indexOf(edge.node1) < 0) { + nextSourceNode.push(edge.node1); + } + } + } + remainNodes = nextSourceNode; + nextSourceNode = []; + ++nodeHeight; + } + each(nodes, function (node) { + if (!isNodeDepth(node)) { + node.setLayout({ + depth: Math.max(0, maxDepth - node.getLayout().skNodeHeight) + }, true); + } + }); + } else if (nodeAlign === 'justify') { + moveSinksRight(nodes, maxDepth); + } + } + /** + * All the node without outEgdes are assigned maximum x-position and + * be aligned in the last column. + * + * @param nodes. node of sankey view. + * @param maxDepth. use to assign to node without outEdges as x-position. + */ + function moveSinksRight(nodes, maxDepth) { + each(nodes, function (node) { + if (!isNodeDepth(node) && !node.outEdges.length) { + node.setLayout({ + depth: maxDepth + }, true); + } + }); + } + /** + * Scale node x-position to the width + * + * @param nodes node of sankey view + * @param kx multiple used to scale nodes + */ + function scaleNodeBreadths(nodes, kx, orient) { + each(nodes, function (node) { + var nodeDepth = node.getLayout().depth * kx; + orient === 'vertical' ? node.setLayout({ + y: nodeDepth + }, true) : node.setLayout({ + x: nodeDepth + }, true); + }); + } + /** + * Using Gauss-Seidel iterations method to compute the node depth(y-position) + * + * @param nodes node of sankey view + * @param edges edge of sankey view + * @param height the whole height of the area to draw the view + * @param nodeGap the vertical distance between two nodes + * in the same column. + * @param iterations the number of iterations for the algorithm + */ + function computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient) { + var nodesByBreadth = prepareNodesByBreadth(nodes, orient); + initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + for (var alpha = 1; iterations > 0; iterations--) { + // 0.99 is a experience parameter, ensure that each iterations of + // changes as small as possible. + alpha *= 0.99; + relaxRightToLeft(nodesByBreadth, alpha, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + relaxLeftToRight(nodesByBreadth, alpha, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + } + } + function prepareNodesByBreadth(nodes, orient) { + var nodesByBreadth = []; + var keyAttr = orient === 'vertical' ? 'y' : 'x'; + var groupResult = groupData(nodes, function (node) { + return node.getLayout()[keyAttr]; + }); + groupResult.keys.sort(function (a, b) { + return a - b; + }); + each(groupResult.keys, function (key) { + nodesByBreadth.push(groupResult.buckets.get(key)); + }); + return nodesByBreadth; + } + /** + * Compute the original y-position for each node + */ + function initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient) { + var minKy = Infinity; + each(nodesByBreadth, function (nodes) { + var n = nodes.length; + var sum = 0; + each(nodes, function (node) { + sum += node.getLayout().value; + }); + var ky = orient === 'vertical' ? (width - (n - 1) * nodeGap) / sum : (height - (n - 1) * nodeGap) / sum; + if (ky < minKy) { + minKy = ky; + } + }); + each(nodesByBreadth, function (nodes) { + each(nodes, function (node, i) { + var nodeDy = node.getLayout().value * minKy; + if (orient === 'vertical') { + node.setLayout({ + x: i + }, true); + node.setLayout({ + dx: nodeDy + }, true); + } else { + node.setLayout({ + y: i + }, true); + node.setLayout({ + dy: nodeDy + }, true); + } + }); + }); + each(edges, function (edge) { + var edgeDy = +edge.getValue() * minKy; + edge.setLayout({ + dy: edgeDy + }, true); + }); + } + /** + * Resolve the collision of initialized depth (y-position) + */ + function resolveCollisions(nodesByBreadth, nodeGap, height, width, orient) { + var keyAttr = orient === 'vertical' ? 'x' : 'y'; + each(nodesByBreadth, function (nodes) { + nodes.sort(function (a, b) { + return a.getLayout()[keyAttr] - b.getLayout()[keyAttr]; + }); + var nodeX; + var node; + var dy; + var y0 = 0; + var n = nodes.length; + var nodeDyAttr = orient === 'vertical' ? 'dx' : 'dy'; + for (var i = 0; i < n; i++) { + node = nodes[i]; + dy = y0 - node.getLayout()[keyAttr]; + if (dy > 0) { + nodeX = node.getLayout()[keyAttr] + dy; + orient === 'vertical' ? node.setLayout({ + x: nodeX + }, true) : node.setLayout({ + y: nodeX + }, true); + } + y0 = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap; + } + var viewWidth = orient === 'vertical' ? width : height; + // If the bottommost node goes outside the bounds, push it back up + dy = y0 - nodeGap - viewWidth; + if (dy > 0) { + nodeX = node.getLayout()[keyAttr] - dy; + orient === 'vertical' ? node.setLayout({ + x: nodeX + }, true) : node.setLayout({ + y: nodeX + }, true); + y0 = nodeX; + for (var i = n - 2; i >= 0; --i) { + node = nodes[i]; + dy = node.getLayout()[keyAttr] + node.getLayout()[nodeDyAttr] + nodeGap - y0; + if (dy > 0) { + nodeX = node.getLayout()[keyAttr] - dy; + orient === 'vertical' ? node.setLayout({ + x: nodeX + }, true) : node.setLayout({ + y: nodeX + }, true); + } + y0 = node.getLayout()[keyAttr]; + } + } + }); + } + /** + * Change the y-position of the nodes, except most the right side nodes + * @param nodesByBreadth + * @param alpha parameter used to adjust the nodes y-position + */ + function relaxRightToLeft(nodesByBreadth, alpha, orient) { + each(nodesByBreadth.slice().reverse(), function (nodes) { + each(nodes, function (node) { + if (node.outEdges.length) { + var y = sum(node.outEdges, weightedTarget, orient) / sum(node.outEdges, getEdgeValue); + if (isNaN(y)) { + var len = node.outEdges.length; + y = len ? sum(node.outEdges, centerTarget, orient) / len : 0; + } + if (orient === 'vertical') { + var nodeX = node.getLayout().x + (y - center$1(node, orient)) * alpha; + node.setLayout({ + x: nodeX + }, true); + } else { + var nodeY = node.getLayout().y + (y - center$1(node, orient)) * alpha; + node.setLayout({ + y: nodeY + }, true); + } + } + }); + }); + } + function weightedTarget(edge, orient) { + return center$1(edge.node2, orient) * edge.getValue(); + } + function centerTarget(edge, orient) { + return center$1(edge.node2, orient); + } + function weightedSource(edge, orient) { + return center$1(edge.node1, orient) * edge.getValue(); + } + function centerSource(edge, orient) { + return center$1(edge.node1, orient); + } + function center$1(node, orient) { + return orient === 'vertical' ? node.getLayout().x + node.getLayout().dx / 2 : node.getLayout().y + node.getLayout().dy / 2; + } + function getEdgeValue(edge) { + return edge.getValue(); + } + function sum(array, cb, orient) { + var sum = 0; + var len = array.length; + var i = -1; + while (++i < len) { + var value = +cb(array[i], orient); + if (!isNaN(value)) { + sum += value; + } + } + return sum; + } + /** + * Change the y-position of the nodes, except most the left side nodes + */ + function relaxLeftToRight(nodesByBreadth, alpha, orient) { + each(nodesByBreadth, function (nodes) { + each(nodes, function (node) { + if (node.inEdges.length) { + var y = sum(node.inEdges, weightedSource, orient) / sum(node.inEdges, getEdgeValue); + if (isNaN(y)) { + var len = node.inEdges.length; + y = len ? sum(node.inEdges, centerSource, orient) / len : 0; + } + if (orient === 'vertical') { + var nodeX = node.getLayout().x + (y - center$1(node, orient)) * alpha; + node.setLayout({ + x: nodeX + }, true); + } else { + var nodeY = node.getLayout().y + (y - center$1(node, orient)) * alpha; + node.setLayout({ + y: nodeY + }, true); + } + } + }); + }); + } + /** + * Compute the depth(y-position) of each edge + */ + function computeEdgeDepths(nodes, orient) { + var keyAttr = orient === 'vertical' ? 'x' : 'y'; + each(nodes, function (node) { + node.outEdges.sort(function (a, b) { + return a.node2.getLayout()[keyAttr] - b.node2.getLayout()[keyAttr]; + }); + node.inEdges.sort(function (a, b) { + return a.node1.getLayout()[keyAttr] - b.node1.getLayout()[keyAttr]; + }); + }); + each(nodes, function (node) { + var sy = 0; + var ty = 0; + each(node.outEdges, function (edge) { + edge.setLayout({ + sy: sy + }, true); + sy += edge.getLayout().dy; + }); + each(node.inEdges, function (edge) { + edge.setLayout({ + ty: ty + }, true); + ty += edge.getLayout().dy; + }); + }); + } + + function sankeyVisual(ecModel) { + ecModel.eachSeriesByType('sankey', function (seriesModel) { + var graph = seriesModel.getGraph(); + var nodes = graph.nodes; + var edges = graph.edges; + if (nodes.length) { + var minValue_1 = Infinity; + var maxValue_1 = -Infinity; + each(nodes, function (node) { + var nodeValue = node.getLayout().value; + if (nodeValue < minValue_1) { + minValue_1 = nodeValue; + } + if (nodeValue > maxValue_1) { + maxValue_1 = nodeValue; + } + }); + each(nodes, function (node) { + var mapping = new VisualMapping({ + type: 'color', + mappingMethod: 'linear', + dataExtent: [minValue_1, maxValue_1], + visual: seriesModel.get('color') + }); + var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value); + var customColor = node.getModel().get(['itemStyle', 'color']); + if (customColor != null) { + node.setVisual('color', customColor); + node.setVisual('style', { + fill: customColor + }); + } else { + node.setVisual('color', mapValueToColor); + node.setVisual('style', { + fill: mapValueToColor + }); + } + }); + } + if (edges.length) { + each(edges, function (edge) { + var edgeStyle = edge.getModel().get('lineStyle'); + edge.setVisual('style', edgeStyle); + }); + } + }); + } + + function install$i(registers) { + registers.registerChartView(SankeyView); + registers.registerSeriesModel(SankeySeriesModel); + registers.registerLayout(sankeyLayout); + registers.registerVisual(sankeyVisual); + registers.registerAction({ + type: 'dragNode', + event: 'dragnode', + // here can only use 'update' now, other value is not support in echarts. + update: 'update' + }, function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'series', + subType: 'sankey', + query: payload + }, function (seriesModel) { + seriesModel.setNodePosition(payload.dataIndex, [payload.localX, payload.localY]); + }); + }); + } + + var WhiskerBoxCommonMixin = /** @class */function () { + function WhiskerBoxCommonMixin() {} + /** + * @override + */ + WhiskerBoxCommonMixin.prototype.getInitialData = function (option, ecModel) { + // When both types of xAxis and yAxis are 'value', layout is + // needed to be specified by user. Otherwise, layout can be + // judged by which axis is category. + var ordinalMeta; + var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex')); + var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex')); + var xAxisType = xAxisModel.get('type'); + var yAxisType = yAxisModel.get('type'); + var addOrdinal; + // FIXME + // Consider time axis. + if (xAxisType === 'category') { + option.layout = 'horizontal'; + ordinalMeta = xAxisModel.getOrdinalMeta(); + addOrdinal = true; + } else if (yAxisType === 'category') { + option.layout = 'vertical'; + ordinalMeta = yAxisModel.getOrdinalMeta(); + addOrdinal = true; + } else { + option.layout = option.layout || 'horizontal'; + } + var coordDims = ['x', 'y']; + var baseAxisDimIndex = option.layout === 'horizontal' ? 0 : 1; + var baseAxisDim = this._baseAxisDim = coordDims[baseAxisDimIndex]; + var otherAxisDim = coordDims[1 - baseAxisDimIndex]; + var axisModels = [xAxisModel, yAxisModel]; + var baseAxisType = axisModels[baseAxisDimIndex].get('type'); + var otherAxisType = axisModels[1 - baseAxisDimIndex].get('type'); + var data = option.data; + // Clone a new data for next setOption({}) usage. + // Avoid modifying current data will affect further update. + if (data && addOrdinal) { + var newOptionData_1 = []; + each(data, function (item, index) { + var newItem; + if (isArray(item)) { + newItem = item.slice(); + // Modify current using data. + item.unshift(index); + } else if (isArray(item.value)) { + newItem = extend({}, item); + newItem.value = newItem.value.slice(); + // Modify current using data. + item.value.unshift(index); + } else { + newItem = item; + } + newOptionData_1.push(newItem); + }); + option.data = newOptionData_1; + } + var defaultValueDimensions = this.defaultValueDimensions; + var coordDimensions = [{ + name: baseAxisDim, + type: getDimensionTypeByAxis(baseAxisType), + ordinalMeta: ordinalMeta, + otherDims: { + tooltip: false, + itemName: 0 + }, + dimsDef: ['base'] + }, { + name: otherAxisDim, + type: getDimensionTypeByAxis(otherAxisType), + dimsDef: defaultValueDimensions.slice() + }]; + return createSeriesDataSimply(this, { + coordDimensions: coordDimensions, + dimensionsCount: defaultValueDimensions.length + 1, + encodeDefaulter: curry(makeSeriesEncodeForAxisCoordSys, coordDimensions, this) + }); + }; + /** + * If horizontal, base axis is x, otherwise y. + * @override + */ + WhiskerBoxCommonMixin.prototype.getBaseAxis = function () { + var dim = this._baseAxisDim; + return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis; + }; + return WhiskerBoxCommonMixin; + }(); + + var BoxplotSeriesModel = /** @class */function (_super) { + __extends(BoxplotSeriesModel, _super); + function BoxplotSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = BoxplotSeriesModel.type; + // TODO + // box width represents group size, so dimension should have 'size'. + /** + * @see + * The meanings of 'min' and 'max' depend on user, + * and echarts do not need to know it. + * @readOnly + */ + _this.defaultValueDimensions = [{ + name: 'min', + defaultTooltip: true + }, { + name: 'Q1', + defaultTooltip: true + }, { + name: 'median', + defaultTooltip: true + }, { + name: 'Q3', + defaultTooltip: true + }, { + name: 'max', + defaultTooltip: true + }]; + _this.visualDrawType = 'stroke'; + return _this; + } + BoxplotSeriesModel.type = 'series.boxplot'; + BoxplotSeriesModel.dependencies = ['xAxis', 'yAxis', 'grid']; + BoxplotSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + layout: null, + boxWidth: [7, 50], + itemStyle: { + color: '#fff', + borderWidth: 1 + }, + emphasis: { + scale: true, + itemStyle: { + borderWidth: 2, + shadowBlur: 5, + shadowOffsetX: 1, + shadowOffsetY: 1, + shadowColor: 'rgba(0,0,0,0.2)' + } + }, + animationDuration: 800 + }; + return BoxplotSeriesModel; + }(SeriesModel); + mixin(BoxplotSeriesModel, WhiskerBoxCommonMixin, true); + + var BoxplotView = /** @class */function (_super) { + __extends(BoxplotView, _super); + function BoxplotView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = BoxplotView.type; + return _this; + } + BoxplotView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var group = this.group; + var oldData = this._data; + // There is no old data only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!this._data) { + group.removeAll(); + } + var constDim = seriesModel.get('layout') === 'horizontal' ? 1 : 0; + data.diff(oldData).add(function (newIdx) { + if (data.hasValue(newIdx)) { + var itemLayout = data.getItemLayout(newIdx); + var symbolEl = createNormalBox(itemLayout, data, newIdx, constDim, true); + data.setItemGraphicEl(newIdx, symbolEl); + group.add(symbolEl); + } + }).update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + // Empty data + if (!data.hasValue(newIdx)) { + group.remove(symbolEl); + return; + } + var itemLayout = data.getItemLayout(newIdx); + if (!symbolEl) { + symbolEl = createNormalBox(itemLayout, data, newIdx, constDim); + } else { + saveOldStyle(symbolEl); + updateNormalBoxData(itemLayout, symbolEl, data, newIdx); + } + group.add(symbolEl); + data.setItemGraphicEl(newIdx, symbolEl); + }).remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }).execute(); + this._data = data; + }; + BoxplotView.prototype.remove = function (ecModel) { + var group = this.group; + var data = this._data; + this._data = null; + data && data.eachItemGraphicEl(function (el) { + el && group.remove(el); + }); + }; + BoxplotView.type = 'boxplot'; + return BoxplotView; + }(ChartView); + var BoxPathShape = /** @class */function () { + function BoxPathShape() {} + return BoxPathShape; + }(); + var BoxPath = /** @class */function (_super) { + __extends(BoxPath, _super); + function BoxPath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'boxplotBoxPath'; + return _this; + } + BoxPath.prototype.getDefaultShape = function () { + return new BoxPathShape(); + }; + BoxPath.prototype.buildPath = function (ctx, shape) { + var ends = shape.points; + var i = 0; + ctx.moveTo(ends[i][0], ends[i][1]); + i++; + for (; i < 4; i++) { + ctx.lineTo(ends[i][0], ends[i][1]); + } + ctx.closePath(); + for (; i < ends.length; i++) { + ctx.moveTo(ends[i][0], ends[i][1]); + i++; + ctx.lineTo(ends[i][0], ends[i][1]); + } + }; + return BoxPath; + }(Path); + function createNormalBox(itemLayout, data, dataIndex, constDim, isInit) { + var ends = itemLayout.ends; + var el = new BoxPath({ + shape: { + points: isInit ? transInit(ends, constDim, itemLayout) : ends + } + }); + updateNormalBoxData(itemLayout, el, data, dataIndex, isInit); + return el; + } + function updateNormalBoxData(itemLayout, el, data, dataIndex, isInit) { + var seriesModel = data.hostModel; + var updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; + updateMethod(el, { + shape: { + points: itemLayout.ends + } + }, seriesModel, dataIndex); + el.useStyle(data.getItemVisual(dataIndex, 'style')); + el.style.strokeNoScale = true; + el.z2 = 100; + var itemModel = data.getItemModel(dataIndex); + var emphasisModel = itemModel.getModel('emphasis'); + setStatesStylesFromModel(el, itemModel); + toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + } + function transInit(points, dim, itemLayout) { + return map(points, function (point) { + point = point.slice(); + point[dim] = itemLayout.initBaseline; + return point; + }); + } + + var each$6 = each; + function boxplotLayout(ecModel) { + var groupResult = groupSeriesByAxis(ecModel); + each$6(groupResult, function (groupItem) { + var seriesModels = groupItem.seriesModels; + if (!seriesModels.length) { + return; + } + calculateBase(groupItem); + each$6(seriesModels, function (seriesModel, idx) { + layoutSingleSeries(seriesModel, groupItem.boxOffsetList[idx], groupItem.boxWidthList[idx]); + }); + }); + } + /** + * Group series by axis. + */ + function groupSeriesByAxis(ecModel) { + var result = []; + var axisList = []; + ecModel.eachSeriesByType('boxplot', function (seriesModel) { + var baseAxis = seriesModel.getBaseAxis(); + var idx = indexOf(axisList, baseAxis); + if (idx < 0) { + idx = axisList.length; + axisList[idx] = baseAxis; + result[idx] = { + axis: baseAxis, + seriesModels: [] + }; + } + result[idx].seriesModels.push(seriesModel); + }); + return result; + } + /** + * Calculate offset and box width for each series. + */ + function calculateBase(groupItem) { + var baseAxis = groupItem.axis; + var seriesModels = groupItem.seriesModels; + var seriesCount = seriesModels.length; + var boxWidthList = groupItem.boxWidthList = []; + var boxOffsetList = groupItem.boxOffsetList = []; + var boundList = []; + var bandWidth; + if (baseAxis.type === 'category') { + bandWidth = baseAxis.getBandWidth(); + } else { + var maxDataCount_1 = 0; + each$6(seriesModels, function (seriesModel) { + maxDataCount_1 = Math.max(maxDataCount_1, seriesModel.getData().count()); + }); + var extent = baseAxis.getExtent(); + bandWidth = Math.abs(extent[1] - extent[0]) / maxDataCount_1; + } + each$6(seriesModels, function (seriesModel) { + var boxWidthBound = seriesModel.get('boxWidth'); + if (!isArray(boxWidthBound)) { + boxWidthBound = [boxWidthBound, boxWidthBound]; + } + boundList.push([parsePercent$1(boxWidthBound[0], bandWidth) || 0, parsePercent$1(boxWidthBound[1], bandWidth) || 0]); + }); + var availableWidth = bandWidth * 0.8 - 2; + var boxGap = availableWidth / seriesCount * 0.3; + var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount; + var base = boxWidth / 2 - availableWidth / 2; + each$6(seriesModels, function (seriesModel, idx) { + boxOffsetList.push(base); + base += boxGap + boxWidth; + boxWidthList.push(Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1])); + }); + } + /** + * Calculate points location for each series. + */ + function layoutSingleSeries(seriesModel, offset, boxWidth) { + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var halfWidth = boxWidth / 2; + var cDimIdx = seriesModel.get('layout') === 'horizontal' ? 0 : 1; + var vDimIdx = 1 - cDimIdx; + var coordDims = ['x', 'y']; + var cDim = data.mapDimension(coordDims[cDimIdx]); + var vDims = data.mapDimensionsAll(coordDims[vDimIdx]); + if (cDim == null || vDims.length < 5) { + return; + } + for (var dataIndex = 0; dataIndex < data.count(); dataIndex++) { + var axisDimVal = data.get(cDim, dataIndex); + var median = getPoint(axisDimVal, vDims[2], dataIndex); + var end1 = getPoint(axisDimVal, vDims[0], dataIndex); + var end2 = getPoint(axisDimVal, vDims[1], dataIndex); + var end4 = getPoint(axisDimVal, vDims[3], dataIndex); + var end5 = getPoint(axisDimVal, vDims[4], dataIndex); + var ends = []; + addBodyEnd(ends, end2, false); + addBodyEnd(ends, end4, true); + ends.push(end1, end2, end5, end4); + layEndLine(ends, end1); + layEndLine(ends, end5); + layEndLine(ends, median); + data.setItemLayout(dataIndex, { + initBaseline: median[vDimIdx], + ends: ends + }); + } + function getPoint(axisDimVal, dim, dataIndex) { + var val = data.get(dim, dataIndex); + var p = []; + p[cDimIdx] = axisDimVal; + p[vDimIdx] = val; + var point; + if (isNaN(axisDimVal) || isNaN(val)) { + point = [NaN, NaN]; + } else { + point = coordSys.dataToPoint(p); + point[cDimIdx] += offset; + } + return point; + } + function addBodyEnd(ends, point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[cDimIdx] += halfWidth; + point2[cDimIdx] -= halfWidth; + start ? ends.push(point1, point2) : ends.push(point2, point1); + } + function layEndLine(ends, endCenter) { + var from = endCenter.slice(); + var to = endCenter.slice(); + from[cDimIdx] -= halfWidth; + to[cDimIdx] += halfWidth; + ends.push(from, to); + } + } + + /** + * See: + * + * + * + * Helper method for preparing data. + * + * @param rawData like + * [ + * [12,232,443], (raw data set for the first box) + * [3843,5545,1232], (raw data set for the second box) + * ... + * ] + * @param opt.boundIQR=1.5 Data less than min bound is outlier. + * default 1.5, means Q1 - 1.5 * (Q3 - Q1). + * If 'none'/0 passed, min bound will not be used. + */ + function prepareBoxplotData(rawData, opt) { + opt = opt || {}; + var boxData = []; + var outliers = []; + var boundIQR = opt.boundIQR; + var useExtreme = boundIQR === 'none' || boundIQR === 0; + for (var i = 0; i < rawData.length; i++) { + var ascList = asc(rawData[i].slice()); + var Q1 = quantile(ascList, 0.25); + var Q2 = quantile(ascList, 0.5); + var Q3 = quantile(ascList, 0.75); + var min = ascList[0]; + var max = ascList[ascList.length - 1]; + var bound = (boundIQR == null ? 1.5 : boundIQR) * (Q3 - Q1); + var low = useExtreme ? min : Math.max(min, Q1 - bound); + var high = useExtreme ? max : Math.min(max, Q3 + bound); + var itemNameFormatter = opt.itemNameFormatter; + var itemName = isFunction(itemNameFormatter) ? itemNameFormatter({ + value: i + }) : isString(itemNameFormatter) ? itemNameFormatter.replace('{value}', i + '') : i + ''; + boxData.push([itemName, low, Q1, Q2, Q3, high]); + for (var j = 0; j < ascList.length; j++) { + var dataItem = ascList[j]; + if (dataItem < low || dataItem > high) { + var outlier = [itemName, dataItem]; + outliers.push(outlier); + } + } + } + return { + boxData: boxData, + outliers: outliers + }; + } + + var boxplotTransform = { + type: 'echarts:boxplot', + transform: function transform(params) { + var upstream = params.upstream; + if (upstream.sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS) { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = makePrintable('source data is not applicable for this boxplot transform. Expect number[][].'); + } + throwError(errMsg); + } + var result = prepareBoxplotData(upstream.getRawData(), params.config); + return [{ + dimensions: ['ItemName', 'Low', 'Q1', 'Q2', 'Q3', 'High'], + data: result.boxData + }, { + data: result.outliers + }]; + } + }; + + function install$j(registers) { + registers.registerSeriesModel(BoxplotSeriesModel); + registers.registerChartView(BoxplotView); + registers.registerLayout(boxplotLayout); + registers.registerTransform(boxplotTransform); + } + + var SKIP_PROPS = ['color', 'borderColor']; + var CandlestickView = /** @class */function (_super) { + __extends(CandlestickView, _super); + function CandlestickView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CandlestickView.type; + return _this; + } + CandlestickView.prototype.render = function (seriesModel, ecModel, api) { + // If there is clipPath created in large mode. Remove it. + this.group.removeClipPath(); + // Clear previously rendered progressive elements. + this._progressiveEls = null; + this._updateDrawMode(seriesModel); + this._isLargeDraw ? this._renderLarge(seriesModel) : this._renderNormal(seriesModel); + }; + CandlestickView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) { + this._clear(); + this._updateDrawMode(seriesModel); + }; + CandlestickView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) { + this._progressiveEls = []; + this._isLargeDraw ? this._incrementalRenderLarge(params, seriesModel) : this._incrementalRenderNormal(params, seriesModel); + }; + CandlestickView.prototype.eachRendered = function (cb) { + traverseElements(this._progressiveEls || this.group, cb); + }; + CandlestickView.prototype._updateDrawMode = function (seriesModel) { + var isLargeDraw = seriesModel.pipelineContext.large; + if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) { + this._isLargeDraw = isLargeDraw; + this._clear(); + } + }; + CandlestickView.prototype._renderNormal = function (seriesModel) { + var data = seriesModel.getData(); + var oldData = this._data; + var group = this.group; + var isSimpleBox = data.getLayout('isSimpleBox'); + var needsClip = seriesModel.get('clip', true); + var coord = seriesModel.coordinateSystem; + var clipArea = coord.getArea && coord.getArea(); + // There is no old data only when first rendering or switching from + // stream mode to normal mode, where previous elements should be removed. + if (!this._data) { + group.removeAll(); + } + data.diff(oldData).add(function (newIdx) { + if (data.hasValue(newIdx)) { + var itemLayout = data.getItemLayout(newIdx); + if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) { + return; + } + var el = createNormalBox$1(itemLayout, newIdx, true); + initProps(el, { + shape: { + points: itemLayout.ends + } + }, seriesModel, newIdx); + setBoxCommon(el, data, newIdx, isSimpleBox); + group.add(el); + data.setItemGraphicEl(newIdx, el); + } + }).update(function (newIdx, oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + // Empty data + if (!data.hasValue(newIdx)) { + group.remove(el); + return; + } + var itemLayout = data.getItemLayout(newIdx); + if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) { + group.remove(el); + return; + } + if (!el) { + el = createNormalBox$1(itemLayout); + } else { + updateProps(el, { + shape: { + points: itemLayout.ends + } + }, seriesModel, newIdx); + saveOldStyle(el); + } + setBoxCommon(el, data, newIdx, isSimpleBox); + group.add(el); + data.setItemGraphicEl(newIdx, el); + }).remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }).execute(); + this._data = data; + }; + CandlestickView.prototype._renderLarge = function (seriesModel) { + this._clear(); + createLarge$1(seriesModel, this.group); + var clipPath = seriesModel.get('clip', true) ? createClipPath(seriesModel.coordinateSystem, false, seriesModel) : null; + if (clipPath) { + this.group.setClipPath(clipPath); + } else { + this.group.removeClipPath(); + } + }; + CandlestickView.prototype._incrementalRenderNormal = function (params, seriesModel) { + var data = seriesModel.getData(); + var isSimpleBox = data.getLayout('isSimpleBox'); + var dataIndex; + while ((dataIndex = params.next()) != null) { + var itemLayout = data.getItemLayout(dataIndex); + var el = createNormalBox$1(itemLayout); + setBoxCommon(el, data, dataIndex, isSimpleBox); + el.incremental = true; + this.group.add(el); + this._progressiveEls.push(el); + } + }; + CandlestickView.prototype._incrementalRenderLarge = function (params, seriesModel) { + createLarge$1(seriesModel, this.group, this._progressiveEls, true); + }; + CandlestickView.prototype.remove = function (ecModel) { + this._clear(); + }; + CandlestickView.prototype._clear = function () { + this.group.removeAll(); + this._data = null; + }; + CandlestickView.type = 'candlestick'; + return CandlestickView; + }(ChartView); + var NormalBoxPathShape = /** @class */function () { + function NormalBoxPathShape() {} + return NormalBoxPathShape; + }(); + var NormalBoxPath = /** @class */function (_super) { + __extends(NormalBoxPath, _super); + function NormalBoxPath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'normalCandlestickBox'; + return _this; + } + NormalBoxPath.prototype.getDefaultShape = function () { + return new NormalBoxPathShape(); + }; + NormalBoxPath.prototype.buildPath = function (ctx, shape) { + var ends = shape.points; + if (this.__simpleBox) { + ctx.moveTo(ends[4][0], ends[4][1]); + ctx.lineTo(ends[6][0], ends[6][1]); + } else { + ctx.moveTo(ends[0][0], ends[0][1]); + ctx.lineTo(ends[1][0], ends[1][1]); + ctx.lineTo(ends[2][0], ends[2][1]); + ctx.lineTo(ends[3][0], ends[3][1]); + ctx.closePath(); + ctx.moveTo(ends[4][0], ends[4][1]); + ctx.lineTo(ends[5][0], ends[5][1]); + ctx.moveTo(ends[6][0], ends[6][1]); + ctx.lineTo(ends[7][0], ends[7][1]); + } + }; + return NormalBoxPath; + }(Path); + function createNormalBox$1(itemLayout, dataIndex, isInit) { + var ends = itemLayout.ends; + return new NormalBoxPath({ + shape: { + points: isInit ? transInit$1(ends, itemLayout) : ends + }, + z2: 100 + }); + } + function isNormalBoxClipped(clipArea, itemLayout) { + var clipped = true; + for (var i = 0; i < itemLayout.ends.length; i++) { + // If any point are in the region. + if (clipArea.contain(itemLayout.ends[i][0], itemLayout.ends[i][1])) { + clipped = false; + break; + } + } + return clipped; + } + function setBoxCommon(el, data, dataIndex, isSimpleBox) { + var itemModel = data.getItemModel(dataIndex); + el.useStyle(data.getItemVisual(dataIndex, 'style')); + el.style.strokeNoScale = true; + el.__simpleBox = isSimpleBox; + setStatesStylesFromModel(el, itemModel); + } + function transInit$1(points, itemLayout) { + return map(points, function (point) { + point = point.slice(); + point[1] = itemLayout.initBaseline; + return point; + }); + } + var LargeBoxPathShape = /** @class */function () { + function LargeBoxPathShape() {} + return LargeBoxPathShape; + }(); + var LargeBoxPath = /** @class */function (_super) { + __extends(LargeBoxPath, _super); + function LargeBoxPath(opts) { + var _this = _super.call(this, opts) || this; + _this.type = 'largeCandlestickBox'; + return _this; + } + LargeBoxPath.prototype.getDefaultShape = function () { + return new LargeBoxPathShape(); + }; + LargeBoxPath.prototype.buildPath = function (ctx, shape) { + // Drawing lines is more efficient than drawing + // a whole line or drawing rects. + var points = shape.points; + for (var i = 0; i < points.length;) { + if (this.__sign === points[i++]) { + var x = points[i++]; + ctx.moveTo(x, points[i++]); + ctx.lineTo(x, points[i++]); + } else { + i += 3; + } + } + }; + return LargeBoxPath; + }(Path); + function createLarge$1(seriesModel, group, progressiveEls, incremental) { + var data = seriesModel.getData(); + var largePoints = data.getLayout('largePoints'); + var elP = new LargeBoxPath({ + shape: { + points: largePoints + }, + __sign: 1, + ignoreCoarsePointer: true + }); + group.add(elP); + var elN = new LargeBoxPath({ + shape: { + points: largePoints + }, + __sign: -1, + ignoreCoarsePointer: true + }); + group.add(elN); + var elDoji = new LargeBoxPath({ + shape: { + points: largePoints + }, + __sign: 0, + ignoreCoarsePointer: true + }); + group.add(elDoji); + setLargeStyle(1, elP, seriesModel); + setLargeStyle(-1, elN, seriesModel); + setLargeStyle(0, elDoji, seriesModel); + if (incremental) { + elP.incremental = true; + elN.incremental = true; + } + if (progressiveEls) { + progressiveEls.push(elP, elN); + } + } + function setLargeStyle(sign, el, seriesModel, data) { + // TODO put in visual? + var borderColor = seriesModel.get(['itemStyle', sign > 0 ? 'borderColor' : 'borderColor0']) + // Use color for border color by default. + || seriesModel.get(['itemStyle', sign > 0 ? 'color' : 'color0']); + if (sign === 0) { + borderColor = seriesModel.get(['itemStyle', 'borderColorDoji']); + } + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var itemStyle = seriesModel.getModel('itemStyle').getItemStyle(SKIP_PROPS); + el.useStyle(itemStyle); + el.style.fill = null; + el.style.stroke = borderColor; + } + + var CandlestickSeriesModel = /** @class */function (_super) { + __extends(CandlestickSeriesModel, _super); + function CandlestickSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CandlestickSeriesModel.type; + _this.defaultValueDimensions = [{ + name: 'open', + defaultTooltip: true + }, { + name: 'close', + defaultTooltip: true + }, { + name: 'lowest', + defaultTooltip: true + }, { + name: 'highest', + defaultTooltip: true + }]; + return _this; + } + /** + * Get dimension for shadow in dataZoom + * @return dimension name + */ + CandlestickSeriesModel.prototype.getShadowDim = function () { + return 'open'; + }; + CandlestickSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) { + var itemLayout = data.getItemLayout(dataIndex); + return itemLayout && selectors.rect(itemLayout.brushRect); + }; + CandlestickSeriesModel.type = 'series.candlestick'; + CandlestickSeriesModel.dependencies = ['xAxis', 'yAxis', 'grid']; + CandlestickSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + // xAxisIndex: 0, + // yAxisIndex: 0, + layout: null, + clip: true, + itemStyle: { + color: '#eb5454', + color0: '#47b262', + borderColor: '#eb5454', + borderColor0: '#47b262', + borderColorDoji: null, + // borderColor: '#d24040', + // borderColor0: '#398f4f', + borderWidth: 1 + }, + emphasis: { + scale: true, + itemStyle: { + borderWidth: 2 + } + }, + barMaxWidth: null, + barMinWidth: null, + barWidth: null, + large: true, + largeThreshold: 600, + progressive: 3e3, + progressiveThreshold: 1e4, + progressiveChunkMode: 'mod', + animationEasing: 'linear', + animationDuration: 300 + }; + return CandlestickSeriesModel; + }(SeriesModel); + mixin(CandlestickSeriesModel, WhiskerBoxCommonMixin, true); + + function candlestickPreprocessor(option) { + if (!option || !isArray(option.series)) { + return; + } + // Translate 'k' to 'candlestick'. + each(option.series, function (seriesItem) { + if (isObject(seriesItem) && seriesItem.type === 'k') { + seriesItem.type = 'candlestick'; + } + }); + } + + var positiveBorderColorQuery = ['itemStyle', 'borderColor']; + var negativeBorderColorQuery = ['itemStyle', 'borderColor0']; + var dojiBorderColorQuery = ['itemStyle', 'borderColorDoji']; + var positiveColorQuery = ['itemStyle', 'color']; + var negativeColorQuery = ['itemStyle', 'color0']; + var candlestickVisual = { + seriesType: 'candlestick', + plan: createRenderPlanner(), + // For legend. + performRawSeries: true, + reset: function (seriesModel, ecModel) { + function getColor(sign, model) { + return model.get(sign > 0 ? positiveColorQuery : negativeColorQuery); + } + function getBorderColor(sign, model) { + return model.get(sign === 0 ? dojiBorderColorQuery : sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery); + } + // Only visible series has each data be visual encoded + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + var isLargeRender = seriesModel.pipelineContext.large; + return !isLargeRender && { + progress: function (params, data) { + var dataIndex; + while ((dataIndex = params.next()) != null) { + var itemModel = data.getItemModel(dataIndex); + var sign = data.getItemLayout(dataIndex).sign; + var style = itemModel.getItemStyle(); + style.fill = getColor(sign, itemModel); + style.stroke = getBorderColor(sign, itemModel) || style.fill; + var existsStyle = data.ensureUniqueItemVisual(dataIndex, 'style'); + extend(existsStyle, style); + } + } + }; + } + }; + + var candlestickLayout = { + seriesType: 'candlestick', + plan: createRenderPlanner(), + reset: function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var candleWidth = calculateCandleWidth(seriesModel, data); + var cDimIdx = 0; + var vDimIdx = 1; + var coordDims = ['x', 'y']; + var cDimI = data.getDimensionIndex(data.mapDimension(coordDims[cDimIdx])); + var vDimsI = map(data.mapDimensionsAll(coordDims[vDimIdx]), data.getDimensionIndex, data); + var openDimI = vDimsI[0]; + var closeDimI = vDimsI[1]; + var lowestDimI = vDimsI[2]; + var highestDimI = vDimsI[3]; + data.setLayout({ + candleWidth: candleWidth, + // The value is experimented visually. + isSimpleBox: candleWidth <= 1.3 + }); + if (cDimI < 0 || vDimsI.length < 4) { + return; + } + return { + progress: seriesModel.pipelineContext.large ? largeProgress : normalProgress + }; + function normalProgress(params, data) { + var dataIndex; + var store = data.getStore(); + while ((dataIndex = params.next()) != null) { + var axisDimVal = store.get(cDimI, dataIndex); + var openVal = store.get(openDimI, dataIndex); + var closeVal = store.get(closeDimI, dataIndex); + var lowestVal = store.get(lowestDimI, dataIndex); + var highestVal = store.get(highestDimI, dataIndex); + var ocLow = Math.min(openVal, closeVal); + var ocHigh = Math.max(openVal, closeVal); + var ocLowPoint = getPoint(ocLow, axisDimVal); + var ocHighPoint = getPoint(ocHigh, axisDimVal); + var lowestPoint = getPoint(lowestVal, axisDimVal); + var highestPoint = getPoint(highestVal, axisDimVal); + var ends = []; + addBodyEnd(ends, ocHighPoint, 0); + addBodyEnd(ends, ocLowPoint, 1); + ends.push(subPixelOptimizePoint(highestPoint), subPixelOptimizePoint(ocHighPoint), subPixelOptimizePoint(lowestPoint), subPixelOptimizePoint(ocLowPoint)); + var itemModel = data.getItemModel(dataIndex); + var hasDojiColor = !!itemModel.get(['itemStyle', 'borderColorDoji']); + data.setItemLayout(dataIndex, { + sign: getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor), + initBaseline: openVal > closeVal ? ocHighPoint[vDimIdx] : ocLowPoint[vDimIdx], + ends: ends, + brushRect: makeBrushRect(lowestVal, highestVal, axisDimVal) + }); + } + function getPoint(val, axisDimVal) { + var p = []; + p[cDimIdx] = axisDimVal; + p[vDimIdx] = val; + return isNaN(axisDimVal) || isNaN(val) ? [NaN, NaN] : coordSys.dataToPoint(p); + } + function addBodyEnd(ends, point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[cDimIdx] = subPixelOptimize$1(point1[cDimIdx] + candleWidth / 2, 1, false); + point2[cDimIdx] = subPixelOptimize$1(point2[cDimIdx] - candleWidth / 2, 1, true); + start ? ends.push(point1, point2) : ends.push(point2, point1); + } + function makeBrushRect(lowestVal, highestVal, axisDimVal) { + var pmin = getPoint(lowestVal, axisDimVal); + var pmax = getPoint(highestVal, axisDimVal); + pmin[cDimIdx] -= candleWidth / 2; + pmax[cDimIdx] -= candleWidth / 2; + return { + x: pmin[0], + y: pmin[1], + width: candleWidth , + height: pmax[1] - pmin[1] + }; + } + function subPixelOptimizePoint(point) { + point[cDimIdx] = subPixelOptimize$1(point[cDimIdx], 1); + return point; + } + } + function largeProgress(params, data) { + // Structure: [sign, x, yhigh, ylow, sign, x, yhigh, ylow, ...] + var points = createFloat32Array(params.count * 4); + var offset = 0; + var point; + var tmpIn = []; + var tmpOut = []; + var dataIndex; + var store = data.getStore(); + var hasDojiColor = !!seriesModel.get(['itemStyle', 'borderColorDoji']); + while ((dataIndex = params.next()) != null) { + var axisDimVal = store.get(cDimI, dataIndex); + var openVal = store.get(openDimI, dataIndex); + var closeVal = store.get(closeDimI, dataIndex); + var lowestVal = store.get(lowestDimI, dataIndex); + var highestVal = store.get(highestDimI, dataIndex); + if (isNaN(axisDimVal) || isNaN(lowestVal) || isNaN(highestVal)) { + points[offset++] = NaN; + offset += 3; + continue; + } + points[offset++] = getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor); + tmpIn[cDimIdx] = axisDimVal; + tmpIn[vDimIdx] = lowestVal; + point = coordSys.dataToPoint(tmpIn, null, tmpOut); + points[offset++] = point ? point[0] : NaN; + points[offset++] = point ? point[1] : NaN; + tmpIn[vDimIdx] = highestVal; + point = coordSys.dataToPoint(tmpIn, null, tmpOut); + points[offset++] = point ? point[1] : NaN; + } + data.setLayout('largePoints', points); + } + } + }; + /** + * Get the sign of a single data. + * + * @returns 0 for doji with hasDojiColor: true, + * 1 for positive, + * -1 for negative. + */ + function getSign(store, dataIndex, openVal, closeVal, closeDimI, hasDojiColor) { + var sign; + if (openVal > closeVal) { + sign = -1; + } else if (openVal < closeVal) { + sign = 1; + } else { + sign = hasDojiColor + // When doji color is set, use it instead of color/color0. + ? 0 : dataIndex > 0 + // If close === open, compare with close of last record + ? store.get(closeDimI, dataIndex - 1) <= closeVal ? 1 : -1 + // No record of previous, set to be positive + : 1; + } + return sign; + } + function calculateCandleWidth(seriesModel, data) { + var baseAxis = seriesModel.getBaseAxis(); + var extent; + var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : (extent = baseAxis.getExtent(), Math.abs(extent[1] - extent[0]) / data.count()); + var barMaxWidth = parsePercent$1(retrieve2(seriesModel.get('barMaxWidth'), bandWidth), bandWidth); + var barMinWidth = parsePercent$1(retrieve2(seriesModel.get('barMinWidth'), 1), bandWidth); + var barWidth = seriesModel.get('barWidth'); + return barWidth != null ? parsePercent$1(barWidth, bandWidth) + // Put max outer to ensure bar visible in spite of overlap. + : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth); + } + + function install$k(registers) { + registers.registerChartView(CandlestickView); + registers.registerSeriesModel(CandlestickSeriesModel); + registers.registerPreprocessor(candlestickPreprocessor); + registers.registerVisual(candlestickVisual); + registers.registerLayout(candlestickLayout); + } + + function updateRipplePath(rippleGroup, effectCfg) { + var color = effectCfg.rippleEffectColor || effectCfg.color; + rippleGroup.eachChild(function (ripplePath) { + ripplePath.attr({ + z: effectCfg.z, + zlevel: effectCfg.zlevel, + style: { + stroke: effectCfg.brushType === 'stroke' ? color : null, + fill: effectCfg.brushType === 'fill' ? color : null + } + }); + }); + } + var EffectSymbol = /** @class */function (_super) { + __extends(EffectSymbol, _super); + function EffectSymbol(data, idx) { + var _this = _super.call(this) || this; + var symbol = new Symbol(data, idx); + var rippleGroup = new Group(); + _this.add(symbol); + _this.add(rippleGroup); + _this.updateData(data, idx); + return _this; + } + EffectSymbol.prototype.stopEffectAnimation = function () { + this.childAt(1).removeAll(); + }; + EffectSymbol.prototype.startEffectAnimation = function (effectCfg) { + var symbolType = effectCfg.symbolType; + var color = effectCfg.color; + var rippleNumber = effectCfg.rippleNumber; + var rippleGroup = this.childAt(1); + for (var i = 0; i < rippleNumber; i++) { + // If width/height are set too small (e.g., set to 1) on ios10 + // and macOS Sierra, a circle stroke become a rect, no matter what + // the scale is set. So we set width/height as 2. See #4136. + var ripplePath = createSymbol(symbolType, -1, -1, 2, 2, color); + ripplePath.attr({ + style: { + strokeNoScale: true + }, + z2: 99, + silent: true, + scaleX: 0.5, + scaleY: 0.5 + }); + var delay = -i / rippleNumber * effectCfg.period + effectCfg.effectOffset; + ripplePath.animate('', true).when(effectCfg.period, { + scaleX: effectCfg.rippleScale / 2, + scaleY: effectCfg.rippleScale / 2 + }).delay(delay).start(); + ripplePath.animateStyle(true).when(effectCfg.period, { + opacity: 0 + }).delay(delay).start(); + rippleGroup.add(ripplePath); + } + updateRipplePath(rippleGroup, effectCfg); + }; + /** + * Update effect symbol + */ + EffectSymbol.prototype.updateEffectAnimation = function (effectCfg) { + var oldEffectCfg = this._effectCfg; + var rippleGroup = this.childAt(1); + // Must reinitialize effect if following configuration changed + var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale', 'rippleNumber']; + for (var i = 0; i < DIFFICULT_PROPS.length; i++) { + var propName = DIFFICULT_PROPS[i]; + if (oldEffectCfg[propName] !== effectCfg[propName]) { + this.stopEffectAnimation(); + this.startEffectAnimation(effectCfg); + return; + } + } + updateRipplePath(rippleGroup, effectCfg); + }; + /** + * Highlight symbol + */ + EffectSymbol.prototype.highlight = function () { + enterEmphasis(this); + }; + /** + * Downplay symbol + */ + EffectSymbol.prototype.downplay = function () { + leaveEmphasis(this); + }; + EffectSymbol.prototype.getSymbolType = function () { + var symbol = this.childAt(0); + return symbol && symbol.getSymbolType(); + }; + /** + * Update symbol properties + */ + EffectSymbol.prototype.updateData = function (data, idx) { + var _this = this; + var seriesModel = data.hostModel; + this.childAt(0).updateData(data, idx); + var rippleGroup = this.childAt(1); + var itemModel = data.getItemModel(idx); + var symbolType = data.getItemVisual(idx, 'symbol'); + var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + var symbolStyle = data.getItemVisual(idx, 'style'); + var color = symbolStyle && symbolStyle.fill; + var emphasisModel = itemModel.getModel('emphasis'); + rippleGroup.setScale(symbolSize); + rippleGroup.traverse(function (ripplePath) { + ripplePath.setStyle('fill', color); + }); + var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize); + if (symbolOffset) { + rippleGroup.x = symbolOffset[0]; + rippleGroup.y = symbolOffset[1]; + } + var symbolRotate = data.getItemVisual(idx, 'symbolRotate'); + rippleGroup.rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + var effectCfg = {}; + effectCfg.showEffectOn = seriesModel.get('showEffectOn'); + effectCfg.rippleScale = itemModel.get(['rippleEffect', 'scale']); + effectCfg.brushType = itemModel.get(['rippleEffect', 'brushType']); + effectCfg.period = itemModel.get(['rippleEffect', 'period']) * 1000; + effectCfg.effectOffset = idx / data.count(); + effectCfg.z = seriesModel.getShallow('z') || 0; + effectCfg.zlevel = seriesModel.getShallow('zlevel') || 0; + effectCfg.symbolType = symbolType; + effectCfg.color = color; + effectCfg.rippleEffectColor = itemModel.get(['rippleEffect', 'color']); + effectCfg.rippleNumber = itemModel.get(['rippleEffect', 'number']); + if (effectCfg.showEffectOn === 'render') { + this._effectCfg ? this.updateEffectAnimation(effectCfg) : this.startEffectAnimation(effectCfg); + this._effectCfg = effectCfg; + } else { + // Not keep old effect config + this._effectCfg = null; + this.stopEffectAnimation(); + this.onHoverStateChange = function (toState) { + if (toState === 'emphasis') { + if (effectCfg.showEffectOn !== 'render') { + _this.startEffectAnimation(effectCfg); + } + } else if (toState === 'normal') { + if (effectCfg.showEffectOn !== 'render') { + _this.stopEffectAnimation(); + } + } + }; + } + this._effectCfg = effectCfg; + toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }; + EffectSymbol.prototype.fadeOut = function (cb) { + cb && cb(); + }; + return EffectSymbol; + }(Group); + + var EffectScatterView = /** @class */function (_super) { + __extends(EffectScatterView, _super); + function EffectScatterView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = EffectScatterView.type; + return _this; + } + EffectScatterView.prototype.init = function () { + this._symbolDraw = new SymbolDraw(EffectSymbol); + }; + EffectScatterView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var effectSymbolDraw = this._symbolDraw; + effectSymbolDraw.updateData(data, { + clipShape: this._getClipShape(seriesModel) + }); + this.group.add(effectSymbolDraw.group); + }; + EffectScatterView.prototype._getClipShape = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var clipArea = coordSys && coordSys.getArea && coordSys.getArea(); + return seriesModel.get('clip', true) ? clipArea : null; + }; + EffectScatterView.prototype.updateTransform = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + this.group.dirty(); + var res = pointsLayout('').reset(seriesModel, ecModel, api); + if (res.progress) { + res.progress({ + start: 0, + end: data.count(), + count: data.count() + }, data); + } + this._symbolDraw.updateLayout(); + }; + EffectScatterView.prototype._updateGroupTransform = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.getRoamTransform) { + this.group.transform = clone$2(coordSys.getRoamTransform()); + this.group.decomposeTransform(); + } + }; + EffectScatterView.prototype.remove = function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(true); + }; + EffectScatterView.type = 'effectScatter'; + return EffectScatterView; + }(ChartView); + + var EffectScatterSeriesModel = /** @class */function (_super) { + __extends(EffectScatterSeriesModel, _super); + function EffectScatterSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = EffectScatterSeriesModel.type; + _this.hasSymbolVisual = true; + return _this; + } + EffectScatterSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesData(null, this, { + useEncodeDefaulter: true + }); + }; + EffectScatterSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) { + return selectors.point(data.getItemLayout(dataIndex)); + }; + EffectScatterSeriesModel.type = 'series.effectScatter'; + EffectScatterSeriesModel.dependencies = ['grid', 'polar']; + EffectScatterSeriesModel.defaultOption = { + coordinateSystem: 'cartesian2d', + // zlevel: 0, + z: 2, + legendHoverLink: true, + effectType: 'ripple', + progressive: 0, + // When to show the effect, option: 'render'|'emphasis' + showEffectOn: 'render', + clip: true, + // Ripple effect config + rippleEffect: { + period: 4, + // Scale of ripple + scale: 2.5, + // Brush type can be fill or stroke + brushType: 'fill', + // Ripple number + number: 3 + }, + universalTransition: { + divideShape: 'clone' + }, + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + // Polar coordinate system + // polarIndex: 0, + // Geo coordinate system + // geoIndex: 0, + // symbol: null, // 图形类型 + symbolSize: 10 // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2 + // symbolRotate: null, // 图形旋转控制 + // itemStyle: { + // opacity: 1 + // } + }; + + return EffectScatterSeriesModel; + }(SeriesModel); + + function install$l(registers) { + registers.registerChartView(EffectScatterView); + registers.registerSeriesModel(EffectScatterSeriesModel); + registers.registerLayout(pointsLayout('effectScatter')); + } + + var EffectLine = /** @class */function (_super) { + __extends(EffectLine, _super); + function EffectLine(lineData, idx, seriesScope) { + var _this = _super.call(this) || this; + _this.add(_this.createLine(lineData, idx, seriesScope)); + _this._updateEffectSymbol(lineData, idx); + return _this; + } + EffectLine.prototype.createLine = function (lineData, idx, seriesScope) { + return new Line$1(lineData, idx, seriesScope); + }; + EffectLine.prototype._updateEffectSymbol = function (lineData, idx) { + var itemModel = lineData.getItemModel(idx); + var effectModel = itemModel.getModel('effect'); + var size = effectModel.get('symbolSize'); + var symbolType = effectModel.get('symbol'); + if (!isArray(size)) { + size = [size, size]; + } + var lineStyle = lineData.getItemVisual(idx, 'style'); + var color = effectModel.get('color') || lineStyle && lineStyle.stroke; + var symbol = this.childAt(1); + if (this._symbolType !== symbolType) { + // Remove previous + this.remove(symbol); + symbol = createSymbol(symbolType, -0.5, -0.5, 1, 1, color); + symbol.z2 = 100; + symbol.culling = true; + this.add(symbol); + } + // Symbol may be removed if loop is false + if (!symbol) { + return; + } + // Shadow color is same with color in default + symbol.setStyle('shadowColor', color); + symbol.setStyle(effectModel.getItemStyle(['color'])); + symbol.scaleX = size[0]; + symbol.scaleY = size[1]; + symbol.setColor(color); + this._symbolType = symbolType; + this._symbolScale = size; + this._updateEffectAnimation(lineData, effectModel, idx); + }; + EffectLine.prototype._updateEffectAnimation = function (lineData, effectModel, idx) { + var symbol = this.childAt(1); + if (!symbol) { + return; + } + var points = lineData.getItemLayout(idx); + var period = effectModel.get('period') * 1000; + var loop = effectModel.get('loop'); + var roundTrip = effectModel.get('roundTrip'); + var constantSpeed = effectModel.get('constantSpeed'); + var delayExpr = retrieve(effectModel.get('delay'), function (idx) { + return idx / lineData.count() * period / 3; + }); + // Ignore when updating + symbol.ignore = true; + this._updateAnimationPoints(symbol, points); + if (constantSpeed > 0) { + period = this._getLineLength(symbol) / constantSpeed * 1000; + } + if (period !== this._period || loop !== this._loop || roundTrip !== this._roundTrip) { + symbol.stopAnimation(); + var delayNum = void 0; + if (isFunction(delayExpr)) { + delayNum = delayExpr(idx); + } else { + delayNum = delayExpr; + } + if (symbol.__t > 0) { + delayNum = -period * symbol.__t; + } + this._animateSymbol(symbol, period, delayNum, loop, roundTrip); + } + this._period = period; + this._loop = loop; + this._roundTrip = roundTrip; + }; + EffectLine.prototype._animateSymbol = function (symbol, period, delayNum, loop, roundTrip) { + if (period > 0) { + symbol.__t = 0; + var self_1 = this; + var animator = symbol.animate('', loop).when(roundTrip ? period * 2 : period, { + __t: roundTrip ? 2 : 1 + }).delay(delayNum).during(function () { + self_1._updateSymbolPosition(symbol); + }); + if (!loop) { + animator.done(function () { + self_1.remove(symbol); + }); + } + animator.start(); + } + }; + EffectLine.prototype._getLineLength = function (symbol) { + // Not so accurate + return dist(symbol.__p1, symbol.__cp1) + dist(symbol.__cp1, symbol.__p2); + }; + EffectLine.prototype._updateAnimationPoints = function (symbol, points) { + symbol.__p1 = points[0]; + symbol.__p2 = points[1]; + symbol.__cp1 = points[2] || [(points[0][0] + points[1][0]) / 2, (points[0][1] + points[1][1]) / 2]; + }; + EffectLine.prototype.updateData = function (lineData, idx, seriesScope) { + this.childAt(0).updateData(lineData, idx, seriesScope); + this._updateEffectSymbol(lineData, idx); + }; + EffectLine.prototype._updateSymbolPosition = function (symbol) { + var p1 = symbol.__p1; + var p2 = symbol.__p2; + var cp1 = symbol.__cp1; + var t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t; + var pos = [symbol.x, symbol.y]; + var lastPos = pos.slice(); + var quadraticAt$1 = quadraticAt; + var quadraticDerivativeAt$1 = quadraticDerivativeAt; + pos[0] = quadraticAt$1(p1[0], cp1[0], p2[0], t); + pos[1] = quadraticAt$1(p1[1], cp1[1], p2[1], t); + // Tangent + var tx = symbol.__t < 1 ? quadraticDerivativeAt$1(p1[0], cp1[0], p2[0], t) : quadraticDerivativeAt$1(p2[0], cp1[0], p1[0], 1 - t); + var ty = symbol.__t < 1 ? quadraticDerivativeAt$1(p1[1], cp1[1], p2[1], t) : quadraticDerivativeAt$1(p2[1], cp1[1], p1[1], 1 - t); + symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; + // enable continuity trail for 'line', 'rect', 'roundRect' symbolType + if (this._symbolType === 'line' || this._symbolType === 'rect' || this._symbolType === 'roundRect') { + if (symbol.__lastT !== undefined && symbol.__lastT < symbol.__t) { + symbol.scaleY = dist(lastPos, pos) * 1.05; + // make sure the last segment render within endPoint + if (t === 1) { + pos[0] = lastPos[0] + (pos[0] - lastPos[0]) / 2; + pos[1] = lastPos[1] + (pos[1] - lastPos[1]) / 2; + } + } else if (symbol.__lastT === 1) { + // After first loop, symbol.__t does NOT start with 0, so connect p1 to pos directly. + symbol.scaleY = 2 * dist(p1, pos); + } else { + symbol.scaleY = this._symbolScale[1]; + } + } + symbol.__lastT = symbol.__t; + symbol.ignore = false; + symbol.x = pos[0]; + symbol.y = pos[1]; + }; + EffectLine.prototype.updateLayout = function (lineData, idx) { + this.childAt(0).updateLayout(lineData, idx); + var effectModel = lineData.getItemModel(idx).getModel('effect'); + this._updateEffectAnimation(lineData, effectModel, idx); + }; + return EffectLine; + }(Group); + + var Polyline$1 = /** @class */function (_super) { + __extends(Polyline$1, _super); + function Polyline$1(lineData, idx, seriesScope) { + var _this = _super.call(this) || this; + _this._createPolyline(lineData, idx, seriesScope); + return _this; + } + Polyline$1.prototype._createPolyline = function (lineData, idx, seriesScope) { + // let seriesModel = lineData.hostModel; + var points = lineData.getItemLayout(idx); + var line = new Polyline({ + shape: { + points: points + } + }); + this.add(line); + this._updateCommonStl(lineData, idx, seriesScope); + }; + Polyline$1.prototype.updateData = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + var line = this.childAt(0); + var target = { + shape: { + points: lineData.getItemLayout(idx) + } + }; + updateProps(line, target, seriesModel, idx); + this._updateCommonStl(lineData, idx, seriesScope); + }; + Polyline$1.prototype._updateCommonStl = function (lineData, idx, seriesScope) { + var line = this.childAt(0); + var itemModel = lineData.getItemModel(idx); + var emphasisLineStyle = seriesScope && seriesScope.emphasisLineStyle; + var focus = seriesScope && seriesScope.focus; + var blurScope = seriesScope && seriesScope.blurScope; + var emphasisDisabled = seriesScope && seriesScope.emphasisDisabled; + if (!seriesScope || lineData.hasItemOption) { + var emphasisModel = itemModel.getModel('emphasis'); + emphasisLineStyle = emphasisModel.getModel('lineStyle').getLineStyle(); + emphasisDisabled = emphasisModel.get('disabled'); + focus = emphasisModel.get('focus'); + blurScope = emphasisModel.get('blurScope'); + } + line.useStyle(lineData.getItemVisual(idx, 'style')); + line.style.fill = null; + line.style.strokeNoScale = true; + var lineEmphasisState = line.ensureState('emphasis'); + lineEmphasisState.style = emphasisLineStyle; + toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled); + }; + Polyline$1.prototype.updateLayout = function (lineData, idx) { + var polyline = this.childAt(0); + polyline.setShape('points', lineData.getItemLayout(idx)); + }; + return Polyline$1; + }(Group); + + var EffectPolyline = /** @class */function (_super) { + __extends(EffectPolyline, _super); + function EffectPolyline() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this._lastFrame = 0; + _this._lastFramePercent = 0; + return _this; + } + // Override + EffectPolyline.prototype.createLine = function (lineData, idx, seriesScope) { + return new Polyline$1(lineData, idx, seriesScope); + }; + // Override + EffectPolyline.prototype._updateAnimationPoints = function (symbol, points) { + this._points = points; + var accLenArr = [0]; + var len = 0; + for (var i = 1; i < points.length; i++) { + var p1 = points[i - 1]; + var p2 = points[i]; + len += dist(p1, p2); + accLenArr.push(len); + } + if (len === 0) { + this._length = 0; + return; + } + for (var i = 0; i < accLenArr.length; i++) { + accLenArr[i] /= len; + } + this._offsets = accLenArr; + this._length = len; + }; + // Override + EffectPolyline.prototype._getLineLength = function () { + return this._length; + }; + // Override + EffectPolyline.prototype._updateSymbolPosition = function (symbol) { + var t = symbol.__t < 1 ? symbol.__t : 2 - symbol.__t; + var points = this._points; + var offsets = this._offsets; + var len = points.length; + if (!offsets) { + // Has length 0 + return; + } + var lastFrame = this._lastFrame; + var frame; + if (t < this._lastFramePercent) { + // Start from the next frame + // PENDING start from lastFrame ? + var start = Math.min(lastFrame + 1, len - 1); + for (frame = start; frame >= 0; frame--) { + if (offsets[frame] <= t) { + break; + } + } + // PENDING really need to do this ? + frame = Math.min(frame, len - 2); + } else { + for (frame = lastFrame; frame < len; frame++) { + if (offsets[frame] > t) { + break; + } + } + frame = Math.min(frame - 1, len - 2); + } + var p = (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]); + var p0 = points[frame]; + var p1 = points[frame + 1]; + symbol.x = p0[0] * (1 - p) + p * p1[0]; + symbol.y = p0[1] * (1 - p) + p * p1[1]; + var tx = symbol.__t < 1 ? p1[0] - p0[0] : p0[0] - p1[0]; + var ty = symbol.__t < 1 ? p1[1] - p0[1] : p0[1] - p1[1]; + symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; + this._lastFrame = frame; + this._lastFramePercent = t; + symbol.ignore = false; + }; + return EffectPolyline; + }(EffectLine); + + var LargeLinesPathShape = /** @class */function () { + function LargeLinesPathShape() { + this.polyline = false; + this.curveness = 0; + this.segs = []; + } + return LargeLinesPathShape; + }(); + var LargeLinesPath = /** @class */function (_super) { + __extends(LargeLinesPath, _super); + function LargeLinesPath(opts) { + var _this = _super.call(this, opts) || this; + _this._off = 0; + _this.hoverDataIdx = -1; + return _this; + } + LargeLinesPath.prototype.reset = function () { + this.notClear = false; + this._off = 0; + }; + LargeLinesPath.prototype.getDefaultStyle = function () { + return { + stroke: '#000', + fill: null + }; + }; + LargeLinesPath.prototype.getDefaultShape = function () { + return new LargeLinesPathShape(); + }; + LargeLinesPath.prototype.buildPath = function (ctx, shape) { + var segs = shape.segs; + var curveness = shape.curveness; + var i; + if (shape.polyline) { + for (i = this._off; i < segs.length;) { + var count = segs[i++]; + if (count > 0) { + ctx.moveTo(segs[i++], segs[i++]); + for (var k = 1; k < count; k++) { + ctx.lineTo(segs[i++], segs[i++]); + } + } + } + } else { + for (i = this._off; i < segs.length;) { + var x0 = segs[i++]; + var y0 = segs[i++]; + var x1 = segs[i++]; + var y1 = segs[i++]; + ctx.moveTo(x0, y0); + if (curveness > 0) { + var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness; + var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness; + ctx.quadraticCurveTo(x2, y2, x1, y1); + } else { + ctx.lineTo(x1, y1); + } + } + } + if (this.incremental) { + this._off = i; + this.notClear = true; + } + }; + LargeLinesPath.prototype.findDataIndex = function (x, y) { + var shape = this.shape; + var segs = shape.segs; + var curveness = shape.curveness; + var lineWidth = this.style.lineWidth; + if (shape.polyline) { + var dataIndex = 0; + for (var i = 0; i < segs.length;) { + var count = segs[i++]; + if (count > 0) { + var x0 = segs[i++]; + var y0 = segs[i++]; + for (var k = 1; k < count; k++) { + var x1 = segs[i++]; + var y1 = segs[i++]; + if (containStroke(x0, y0, x1, y1, lineWidth, x, y)) { + return dataIndex; + } + } + } + dataIndex++; + } + } else { + var dataIndex = 0; + for (var i = 0; i < segs.length;) { + var x0 = segs[i++]; + var y0 = segs[i++]; + var x1 = segs[i++]; + var y1 = segs[i++]; + if (curveness > 0) { + var x2 = (x0 + x1) / 2 - (y0 - y1) * curveness; + var y2 = (y0 + y1) / 2 - (x1 - x0) * curveness; + if (containStroke$2(x0, y0, x2, y2, x1, y1, lineWidth, x, y)) { + return dataIndex; + } + } else { + if (containStroke(x0, y0, x1, y1, lineWidth, x, y)) { + return dataIndex; + } + } + dataIndex++; + } + } + return -1; + }; + LargeLinesPath.prototype.contain = function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + x = localPos[0]; + y = localPos[1]; + if (rect.contain(x, y)) { + // Cache found data index. + var dataIdx = this.hoverDataIdx = this.findDataIndex(x, y); + return dataIdx >= 0; + } + this.hoverDataIdx = -1; + return false; + }; + LargeLinesPath.prototype.getBoundingRect = function () { + // Ignore stroke for large symbol draw. + var rect = this._rect; + if (!rect) { + var shape = this.shape; + var points = shape.segs; + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + for (var i = 0; i < points.length;) { + var x = points[i++]; + var y = points[i++]; + minX = Math.min(x, minX); + maxX = Math.max(x, maxX); + minY = Math.min(y, minY); + maxY = Math.max(y, maxY); + } + rect = this._rect = new BoundingRect(minX, minY, maxX, maxY); + } + return rect; + }; + return LargeLinesPath; + }(Path); + var LargeLineDraw = /** @class */function () { + function LargeLineDraw() { + this.group = new Group(); + } + /** + * Update symbols draw by new data + */ + LargeLineDraw.prototype.updateData = function (data) { + this._clear(); + var lineEl = this._create(); + lineEl.setShape({ + segs: data.getLayout('linesPoints') + }); + this._setCommon(lineEl, data); + }; + /** + * @override + */ + LargeLineDraw.prototype.incrementalPrepareUpdate = function (data) { + this.group.removeAll(); + this._clear(); + }; + /** + * @override + */ + LargeLineDraw.prototype.incrementalUpdate = function (taskParams, data) { + var lastAdded = this._newAdded[0]; + var linePoints = data.getLayout('linesPoints'); + var oldSegs = lastAdded && lastAdded.shape.segs; + // Merging the exists. Each element has 1e4 points. + // Consider the performance balance between too much elements and too much points in one shape(may affect hover optimization) + if (oldSegs && oldSegs.length < 2e4) { + var oldLen = oldSegs.length; + var newSegs = new Float32Array(oldLen + linePoints.length); + // Concat two array + newSegs.set(oldSegs); + newSegs.set(linePoints, oldLen); + lastAdded.setShape({ + segs: newSegs + }); + } else { + // Clear + this._newAdded = []; + var lineEl = this._create(); + lineEl.incremental = true; + lineEl.setShape({ + segs: linePoints + }); + this._setCommon(lineEl, data); + lineEl.__startIndex = taskParams.start; + } + }; + /** + * @override + */ + LargeLineDraw.prototype.remove = function () { + this._clear(); + }; + LargeLineDraw.prototype.eachRendered = function (cb) { + this._newAdded[0] && cb(this._newAdded[0]); + }; + LargeLineDraw.prototype._create = function () { + var lineEl = new LargeLinesPath({ + cursor: 'default', + ignoreCoarsePointer: true + }); + this._newAdded.push(lineEl); + this.group.add(lineEl); + return lineEl; + }; + LargeLineDraw.prototype._setCommon = function (lineEl, data, isIncremental) { + var hostModel = data.hostModel; + lineEl.setShape({ + polyline: hostModel.get('polyline'), + curveness: hostModel.get(['lineStyle', 'curveness']) + }); + lineEl.useStyle(hostModel.getModel('lineStyle').getLineStyle()); + lineEl.style.strokeNoScale = true; + var style = data.getVisual('style'); + if (style && style.stroke) { + lineEl.setStyle('stroke', style.stroke); + } + lineEl.setStyle('fill', null); + var ecData = getECData(lineEl); + // Enable tooltip + // PENDING May have performance issue when path is extremely large + ecData.seriesIndex = hostModel.seriesIndex; + lineEl.on('mousemove', function (e) { + ecData.dataIndex = null; + var dataIndex = lineEl.hoverDataIdx; + if (dataIndex > 0) { + // Provide dataIndex for tooltip + ecData.dataIndex = dataIndex + lineEl.__startIndex; + } + }); + }; + LargeLineDraw.prototype._clear = function () { + this._newAdded = []; + this.group.removeAll(); + }; + return LargeLineDraw; + }(); + + var linesLayout = { + seriesType: 'lines', + plan: createRenderPlanner(), + reset: function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (!coordSys) { + if ("development" !== 'production') { + error('The lines series must have a coordinate system.'); + } + return; + } + var isPolyline = seriesModel.get('polyline'); + var isLarge = seriesModel.pipelineContext.large; + return { + progress: function (params, lineData) { + var lineCoords = []; + if (isLarge) { + var points = void 0; + var segCount = params.end - params.start; + if (isPolyline) { + var totalCoordsCount = 0; + for (var i = params.start; i < params.end; i++) { + totalCoordsCount += seriesModel.getLineCoordsCount(i); + } + points = new Float32Array(segCount + totalCoordsCount * 2); + } else { + points = new Float32Array(segCount * 4); + } + var offset = 0; + var pt = []; + for (var i = params.start; i < params.end; i++) { + var len = seriesModel.getLineCoords(i, lineCoords); + if (isPolyline) { + points[offset++] = len; + } + for (var k = 0; k < len; k++) { + pt = coordSys.dataToPoint(lineCoords[k], false, pt); + points[offset++] = pt[0]; + points[offset++] = pt[1]; + } + } + lineData.setLayout('linesPoints', points); + } else { + for (var i = params.start; i < params.end; i++) { + var itemModel = lineData.getItemModel(i); + var len = seriesModel.getLineCoords(i, lineCoords); + var pts = []; + if (isPolyline) { + for (var j = 0; j < len; j++) { + pts.push(coordSys.dataToPoint(lineCoords[j])); + } + } else { + pts[0] = coordSys.dataToPoint(lineCoords[0]); + pts[1] = coordSys.dataToPoint(lineCoords[1]); + var curveness = itemModel.get(['lineStyle', 'curveness']); + if (+curveness) { + pts[2] = [(pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness, (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness]; + } + } + lineData.setItemLayout(i, pts); + } + } + } + }; + } + }; + + var LinesView = /** @class */function (_super) { + __extends(LinesView, _super); + function LinesView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = LinesView.type; + return _this; + } + LinesView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var lineDraw = this._updateLineDraw(data, seriesModel); + var zlevel = seriesModel.get('zlevel'); + var trailLength = seriesModel.get(['effect', 'trailLength']); + var zr = api.getZr(); + // Avoid the drag cause ghost shadow + // FIXME Better way ? + // SVG doesn't support + var isSvg = zr.painter.getType() === 'svg'; + if (!isSvg) { + zr.painter.getLayer(zlevel).clear(true); + } + // Config layer with motion blur + if (this._lastZlevel != null && !isSvg) { + zr.configLayer(this._lastZlevel, { + motionBlur: false + }); + } + if (this._showEffect(seriesModel) && trailLength > 0) { + if (!isSvg) { + zr.configLayer(zlevel, { + motionBlur: true, + lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0) + }); + } else if ("development" !== 'production') { + console.warn('SVG render mode doesn\'t support lines with trail effect'); + } + } + lineDraw.updateData(data); + var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel); + if (clipPath) { + this.group.setClipPath(clipPath); + } else { + this.group.removeClipPath(); + } + this._lastZlevel = zlevel; + this._finished = true; + }; + LinesView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var lineDraw = this._updateLineDraw(data, seriesModel); + lineDraw.incrementalPrepareUpdate(data); + this._clearLayer(api); + this._finished = false; + }; + LinesView.prototype.incrementalRender = function (taskParams, seriesModel, ecModel) { + this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData()); + this._finished = taskParams.end === seriesModel.getData().count(); + }; + LinesView.prototype.eachRendered = function (cb) { + this._lineDraw && this._lineDraw.eachRendered(cb); + }; + LinesView.prototype.updateTransform = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var pipelineContext = seriesModel.pipelineContext; + if (!this._finished || pipelineContext.large || pipelineContext.progressiveRender) { + // TODO Don't have to do update in large mode. Only do it when there are millions of data. + return { + update: true + }; + } else { + // TODO Use same logic with ScatterView. + // Manually update layout + var res = linesLayout.reset(seriesModel, ecModel, api); + if (res.progress) { + res.progress({ + start: 0, + end: data.count(), + count: data.count() + }, data); + } + // Not in large mode + this._lineDraw.updateLayout(); + this._clearLayer(api); + } + }; + LinesView.prototype._updateLineDraw = function (data, seriesModel) { + var lineDraw = this._lineDraw; + var hasEffect = this._showEffect(seriesModel); + var isPolyline = !!seriesModel.get('polyline'); + var pipelineContext = seriesModel.pipelineContext; + var isLargeDraw = pipelineContext.large; + if ("development" !== 'production') { + if (hasEffect && isLargeDraw) { + console.warn('Large lines not support effect'); + } + } + if (!lineDraw || hasEffect !== this._hasEffet || isPolyline !== this._isPolyline || isLargeDraw !== this._isLargeDraw) { + if (lineDraw) { + lineDraw.remove(); + } + lineDraw = this._lineDraw = isLargeDraw ? new LargeLineDraw() : new LineDraw(isPolyline ? hasEffect ? EffectPolyline : Polyline$1 : hasEffect ? EffectLine : Line$1); + this._hasEffet = hasEffect; + this._isPolyline = isPolyline; + this._isLargeDraw = isLargeDraw; + } + this.group.add(lineDraw.group); + return lineDraw; + }; + LinesView.prototype._showEffect = function (seriesModel) { + return !!seriesModel.get(['effect', 'show']); + }; + LinesView.prototype._clearLayer = function (api) { + // Not use motion when dragging or zooming + var zr = api.getZr(); + var isSvg = zr.painter.getType() === 'svg'; + if (!isSvg && this._lastZlevel != null) { + zr.painter.getLayer(this._lastZlevel).clear(true); + } + }; + LinesView.prototype.remove = function (ecModel, api) { + this._lineDraw && this._lineDraw.remove(); + this._lineDraw = null; + // Clear motion when lineDraw is removed + this._clearLayer(api); + }; + LinesView.prototype.dispose = function (ecModel, api) { + this.remove(ecModel, api); + }; + LinesView.type = 'lines'; + return LinesView; + }(ChartView); + + var Uint32Arr = typeof Uint32Array === 'undefined' ? Array : Uint32Array; + var Float64Arr = typeof Float64Array === 'undefined' ? Array : Float64Array; + function compatEc2(seriesOpt) { + var data = seriesOpt.data; + if (data && data[0] && data[0][0] && data[0][0].coord) { + if ("development" !== 'production') { + console.warn('Lines data configuration has been changed to' + ' { coords:[[1,2],[2,3]] }'); + } + seriesOpt.data = map(data, function (itemOpt) { + var coords = [itemOpt[0].coord, itemOpt[1].coord]; + var target = { + coords: coords + }; + if (itemOpt[0].name) { + target.fromName = itemOpt[0].name; + } + if (itemOpt[1].name) { + target.toName = itemOpt[1].name; + } + return mergeAll([target, itemOpt[0], itemOpt[1]]); + }); + } + } + var LinesSeriesModel = /** @class */function (_super) { + __extends(LinesSeriesModel, _super); + function LinesSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = LinesSeriesModel.type; + _this.visualStyleAccessPath = 'lineStyle'; + _this.visualDrawType = 'stroke'; + return _this; + } + LinesSeriesModel.prototype.init = function (option) { + // The input data may be null/undefined. + option.data = option.data || []; + // Not using preprocessor because mergeOption may not have series.type + compatEc2(option); + var result = this._processFlatCoordsArray(option.data); + this._flatCoords = result.flatCoords; + this._flatCoordsOffset = result.flatCoordsOffset; + if (result.flatCoords) { + option.data = new Float32Array(result.count); + } + _super.prototype.init.apply(this, arguments); + }; + LinesSeriesModel.prototype.mergeOption = function (option) { + compatEc2(option); + if (option.data) { + // Only update when have option data to merge. + var result = this._processFlatCoordsArray(option.data); + this._flatCoords = result.flatCoords; + this._flatCoordsOffset = result.flatCoordsOffset; + if (result.flatCoords) { + option.data = new Float32Array(result.count); + } + } + _super.prototype.mergeOption.apply(this, arguments); + }; + LinesSeriesModel.prototype.appendData = function (params) { + var result = this._processFlatCoordsArray(params.data); + if (result.flatCoords) { + if (!this._flatCoords) { + this._flatCoords = result.flatCoords; + this._flatCoordsOffset = result.flatCoordsOffset; + } else { + this._flatCoords = concatArray(this._flatCoords, result.flatCoords); + this._flatCoordsOffset = concatArray(this._flatCoordsOffset, result.flatCoordsOffset); + } + params.data = new Float32Array(result.count); + } + this.getRawData().appendData(params.data); + }; + LinesSeriesModel.prototype._getCoordsFromItemModel = function (idx) { + var itemModel = this.getData().getItemModel(idx); + var coords = itemModel.option instanceof Array ? itemModel.option : itemModel.getShallow('coords'); + if ("development" !== 'production') { + if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) { + throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.'); + } + } + return coords; + }; + LinesSeriesModel.prototype.getLineCoordsCount = function (idx) { + if (this._flatCoordsOffset) { + return this._flatCoordsOffset[idx * 2 + 1]; + } else { + return this._getCoordsFromItemModel(idx).length; + } + }; + LinesSeriesModel.prototype.getLineCoords = function (idx, out) { + if (this._flatCoordsOffset) { + var offset = this._flatCoordsOffset[idx * 2]; + var len = this._flatCoordsOffset[idx * 2 + 1]; + for (var i = 0; i < len; i++) { + out[i] = out[i] || []; + out[i][0] = this._flatCoords[offset + i * 2]; + out[i][1] = this._flatCoords[offset + i * 2 + 1]; + } + return len; + } else { + var coords = this._getCoordsFromItemModel(idx); + for (var i = 0; i < coords.length; i++) { + out[i] = out[i] || []; + out[i][0] = coords[i][0]; + out[i][1] = coords[i][1]; + } + return coords.length; + } + }; + LinesSeriesModel.prototype._processFlatCoordsArray = function (data) { + var startOffset = 0; + if (this._flatCoords) { + startOffset = this._flatCoords.length; + } + // Stored as a typed array. In format + // Points Count(2) | x | y | x | y | Points Count(3) | x | y | x | y | x | y | + if (isNumber(data[0])) { + var len = data.length; + // Store offset and len of each segment + var coordsOffsetAndLenStorage = new Uint32Arr(len); + var coordsStorage = new Float64Arr(len); + var coordsCursor = 0; + var offsetCursor = 0; + var dataCount = 0; + for (var i = 0; i < len;) { + dataCount++; + var count = data[i++]; + // Offset + coordsOffsetAndLenStorage[offsetCursor++] = coordsCursor + startOffset; + // Len + coordsOffsetAndLenStorage[offsetCursor++] = count; + for (var k = 0; k < count; k++) { + var x = data[i++]; + var y = data[i++]; + coordsStorage[coordsCursor++] = x; + coordsStorage[coordsCursor++] = y; + if (i > len) { + if ("development" !== 'production') { + throw new Error('Invalid data format.'); + } + } + } + } + return { + flatCoordsOffset: new Uint32Array(coordsOffsetAndLenStorage.buffer, 0, offsetCursor), + flatCoords: coordsStorage, + count: dataCount + }; + } + return { + flatCoordsOffset: null, + flatCoords: null, + count: data.length + }; + }; + LinesSeriesModel.prototype.getInitialData = function (option, ecModel) { + if ("development" !== 'production') { + var CoordSys = CoordinateSystemManager.get(option.coordinateSystem); + if (!CoordSys) { + throw new Error('Unknown coordinate system ' + option.coordinateSystem); + } + } + var lineData = new SeriesData(['value'], this); + lineData.hasItemOption = false; + lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) { + // dataItem is simply coords + if (dataItem instanceof Array) { + return NaN; + } else { + lineData.hasItemOption = true; + var value = dataItem.value; + if (value != null) { + return value instanceof Array ? value[dimIndex] : value; + } + } + }); + return lineData; + }; + LinesSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + var data = this.getData(); + var itemModel = data.getItemModel(dataIndex); + var name = itemModel.get('name'); + if (name) { + return name; + } + var fromName = itemModel.get('fromName'); + var toName = itemModel.get('toName'); + var nameArr = []; + fromName != null && nameArr.push(fromName); + toName != null && nameArr.push(toName); + return createTooltipMarkup('nameValue', { + name: nameArr.join(' > ') + }); + }; + LinesSeriesModel.prototype.preventIncremental = function () { + return !!this.get(['effect', 'show']); + }; + LinesSeriesModel.prototype.getProgressive = function () { + var progressive = this.option.progressive; + if (progressive == null) { + return this.option.large ? 1e4 : this.get('progressive'); + } + return progressive; + }; + LinesSeriesModel.prototype.getProgressiveThreshold = function () { + var progressiveThreshold = this.option.progressiveThreshold; + if (progressiveThreshold == null) { + return this.option.large ? 2e4 : this.get('progressiveThreshold'); + } + return progressiveThreshold; + }; + LinesSeriesModel.prototype.getZLevelKey = function () { + var effectModel = this.getModel('effect'); + var trailLength = effectModel.get('trailLength'); + return this.getData().count() > this.getProgressiveThreshold() + // Each progressive series has individual key. + ? this.id : effectModel.get('show') && trailLength > 0 ? trailLength + '' : ''; + }; + LinesSeriesModel.type = 'series.lines'; + LinesSeriesModel.dependencies = ['grid', 'polar', 'geo', 'calendar']; + LinesSeriesModel.defaultOption = { + coordinateSystem: 'geo', + // zlevel: 0, + z: 2, + legendHoverLink: true, + // Cartesian coordinate system + xAxisIndex: 0, + yAxisIndex: 0, + symbol: ['none', 'none'], + symbolSize: [10, 10], + // Geo coordinate system + geoIndex: 0, + effect: { + show: false, + period: 4, + constantSpeed: 0, + symbol: 'circle', + symbolSize: 3, + loop: true, + trailLength: 0.2 + }, + large: false, + // Available when large is true + largeThreshold: 2000, + polyline: false, + clip: true, + label: { + show: false, + position: 'end' + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + }, + + lineStyle: { + opacity: 0.5 + } + }; + return LinesSeriesModel; + }(SeriesModel); + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function normalize$3(a) { + if (!(a instanceof Array)) { + a = [a, a]; + } + return a; + } + var linesVisual = { + seriesType: 'lines', + reset: function (seriesModel) { + var symbolType = normalize$3(seriesModel.get('symbol')); + var symbolSize = normalize$3(seriesModel.get('symbolSize')); + var data = seriesModel.getData(); + data.setVisual('fromSymbol', symbolType && symbolType[0]); + data.setVisual('toSymbol', symbolType && symbolType[1]); + data.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); + data.setVisual('toSymbolSize', symbolSize && symbolSize[1]); + function dataEach(data, idx) { + var itemModel = data.getItemModel(idx); + var symbolType = normalize$3(itemModel.getShallow('symbol', true)); + var symbolSize = normalize$3(itemModel.getShallow('symbolSize', true)); + symbolType[0] && data.setItemVisual(idx, 'fromSymbol', symbolType[0]); + symbolType[1] && data.setItemVisual(idx, 'toSymbol', symbolType[1]); + symbolSize[0] && data.setItemVisual(idx, 'fromSymbolSize', symbolSize[0]); + symbolSize[1] && data.setItemVisual(idx, 'toSymbolSize', symbolSize[1]); + } + return { + dataEach: data.hasItemOption ? dataEach : null + }; + } + }; + + function install$m(registers) { + registers.registerChartView(LinesView); + registers.registerSeriesModel(LinesSeriesModel); + registers.registerLayout(linesLayout); + registers.registerVisual(linesVisual); + } + + var GRADIENT_LEVELS = 256; + var HeatmapLayer = /** @class */function () { + function HeatmapLayer() { + this.blurSize = 30; + this.pointSize = 20; + this.maxOpacity = 1; + this.minOpacity = 0; + this._gradientPixels = { + inRange: null, + outOfRange: null + }; + var canvas = platformApi.createCanvas(); + this.canvas = canvas; + } + /** + * Renders Heatmap and returns the rendered canvas + * @param data array of data, each has x, y, value + * @param width canvas width + * @param height canvas height + */ + HeatmapLayer.prototype.update = function (data, width, height, normalize, colorFunc, isInRange) { + var brush = this._getBrush(); + var gradientInRange = this._getGradient(colorFunc, 'inRange'); + var gradientOutOfRange = this._getGradient(colorFunc, 'outOfRange'); + var r = this.pointSize + this.blurSize; + var canvas = this.canvas; + var ctx = canvas.getContext('2d'); + var len = data.length; + canvas.width = width; + canvas.height = height; + for (var i = 0; i < len; ++i) { + var p = data[i]; + var x = p[0]; + var y = p[1]; + var value = p[2]; + // calculate alpha using value + var alpha = normalize(value); + // draw with the circle brush with alpha + ctx.globalAlpha = alpha; + ctx.drawImage(brush, x - r, y - r); + } + if (!canvas.width || !canvas.height) { + // Avoid "Uncaught DOMException: Failed to execute 'getImageData' on + // 'CanvasRenderingContext2D': The source height is 0." + return canvas; + } + // colorize the canvas using alpha value and set with gradient + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var pixels = imageData.data; + var offset = 0; + var pixelLen = pixels.length; + var minOpacity = this.minOpacity; + var maxOpacity = this.maxOpacity; + var diffOpacity = maxOpacity - minOpacity; + while (offset < pixelLen) { + var alpha = pixels[offset + 3] / 256; + var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; + // Simple optimize to ignore the empty data + if (alpha > 0) { + var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; + // Any alpha > 0 will be mapped to [minOpacity, maxOpacity] + alpha > 0 && (alpha = alpha * diffOpacity + minOpacity); + pixels[offset++] = gradient[gradientOffset]; + pixels[offset++] = gradient[gradientOffset + 1]; + pixels[offset++] = gradient[gradientOffset + 2]; + pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256; + } else { + offset += 4; + } + } + ctx.putImageData(imageData, 0, 0); + return canvas; + }; + /** + * get canvas of a black circle brush used for canvas to draw later + */ + HeatmapLayer.prototype._getBrush = function () { + var brushCanvas = this._brushCanvas || (this._brushCanvas = platformApi.createCanvas()); + // set brush size + var r = this.pointSize + this.blurSize; + var d = r * 2; + brushCanvas.width = d; + brushCanvas.height = d; + var ctx = brushCanvas.getContext('2d'); + ctx.clearRect(0, 0, d, d); + // in order to render shadow without the distinct circle, + // draw the distinct circle in an invisible place, + // and use shadowOffset to draw shadow in the center of the canvas + ctx.shadowOffsetX = d; + ctx.shadowBlur = this.blurSize; + // draw the shadow in black, and use alpha and shadow blur to generate + // color in color map + ctx.shadowColor = '#000'; + // draw circle in the left to the canvas + ctx.beginPath(); + ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fill(); + return brushCanvas; + }; + /** + * get gradient color map + * @private + */ + HeatmapLayer.prototype._getGradient = function (colorFunc, state) { + var gradientPixels = this._gradientPixels; + var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4)); + var color = [0, 0, 0, 0]; + var off = 0; + for (var i = 0; i < 256; i++) { + colorFunc[state](i / 255, true, color); + pixelsSingleState[off++] = color[0]; + pixelsSingleState[off++] = color[1]; + pixelsSingleState[off++] = color[2]; + pixelsSingleState[off++] = color[3]; + } + return pixelsSingleState; + }; + return HeatmapLayer; + }(); + + function getIsInPiecewiseRange(dataExtent, pieceList, selected) { + var dataSpan = dataExtent[1] - dataExtent[0]; + pieceList = map(pieceList, function (piece) { + return { + interval: [(piece.interval[0] - dataExtent[0]) / dataSpan, (piece.interval[1] - dataExtent[0]) / dataSpan] + }; + }); + var len = pieceList.length; + var lastIndex = 0; + return function (val) { + var i; + // Try to find in the location of the last found + for (i = lastIndex; i < len; i++) { + var interval = pieceList[i].interval; + if (interval[0] <= val && val <= interval[1]) { + lastIndex = i; + break; + } + } + if (i === len) { + // Not found, back interation + for (i = lastIndex - 1; i >= 0; i--) { + var interval = pieceList[i].interval; + if (interval[0] <= val && val <= interval[1]) { + lastIndex = i; + break; + } + } + } + return i >= 0 && i < len && selected[i]; + }; + } + function getIsInContinuousRange(dataExtent, range) { + var dataSpan = dataExtent[1] - dataExtent[0]; + range = [(range[0] - dataExtent[0]) / dataSpan, (range[1] - dataExtent[0]) / dataSpan]; + return function (val) { + return val >= range[0] && val <= range[1]; + }; + } + function isGeoCoordSys(coordSys) { + var dimensions = coordSys.dimensions; + // Not use coordSys.type === 'geo' because coordSys maybe extended + return dimensions[0] === 'lng' && dimensions[1] === 'lat'; + } + var HeatmapView = /** @class */function (_super) { + __extends(HeatmapView, _super); + function HeatmapView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = HeatmapView.type; + return _this; + } + HeatmapView.prototype.render = function (seriesModel, ecModel, api) { + var visualMapOfThisSeries; + ecModel.eachComponent('visualMap', function (visualMap) { + visualMap.eachTargetSeries(function (targetSeries) { + if (targetSeries === seriesModel) { + visualMapOfThisSeries = visualMap; + } + }); + }); + if ("development" !== 'production') { + if (!visualMapOfThisSeries) { + throw new Error('Heatmap must use with visualMap'); + } + } + // Clear previously rendered progressive elements. + this._progressiveEls = null; + this.group.removeAll(); + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type === 'cartesian2d' || coordSys.type === 'calendar') { + this._renderOnCartesianAndCalendar(seriesModel, api, 0, seriesModel.getData().count()); + } else if (isGeoCoordSys(coordSys)) { + this._renderOnGeo(coordSys, seriesModel, visualMapOfThisSeries, api); + } + }; + HeatmapView.prototype.incrementalPrepareRender = function (seriesModel, ecModel, api) { + this.group.removeAll(); + }; + HeatmapView.prototype.incrementalRender = function (params, seriesModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys) { + // geo does not support incremental rendering? + if (isGeoCoordSys(coordSys)) { + this.render(seriesModel, ecModel, api); + } else { + this._progressiveEls = []; + this._renderOnCartesianAndCalendar(seriesModel, api, params.start, params.end, true); + } + } + }; + HeatmapView.prototype.eachRendered = function (cb) { + traverseElements(this._progressiveEls || this.group, cb); + }; + HeatmapView.prototype._renderOnCartesianAndCalendar = function (seriesModel, api, start, end, incremental) { + var coordSys = seriesModel.coordinateSystem; + var isCartesian2d = isCoordinateSystemType(coordSys, 'cartesian2d'); + var width; + var height; + var xAxisExtent; + var yAxisExtent; + if (isCartesian2d) { + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + if ("development" !== 'production') { + if (!(xAxis.type === 'category' && yAxis.type === 'category')) { + throw new Error('Heatmap on cartesian must have two category axes'); + } + if (!(xAxis.onBand && yAxis.onBand)) { + throw new Error('Heatmap on cartesian must have two axes with boundaryGap true'); + } + } + // add 0.5px to avoid the gaps + width = xAxis.getBandWidth() + .5; + height = yAxis.getBandWidth() + .5; + xAxisExtent = xAxis.scale.getExtent(); + yAxisExtent = yAxis.scale.getExtent(); + } + var group = this.group; + var data = seriesModel.getData(); + var emphasisStyle = seriesModel.getModel(['emphasis', 'itemStyle']).getItemStyle(); + var blurStyle = seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(); + var selectStyle = seriesModel.getModel(['select', 'itemStyle']).getItemStyle(); + var borderRadius = seriesModel.get(['itemStyle', 'borderRadius']); + var labelStatesModels = getLabelStatesModels(seriesModel); + var emphasisModel = seriesModel.getModel('emphasis'); + var focus = emphasisModel.get('focus'); + var blurScope = emphasisModel.get('blurScope'); + var emphasisDisabled = emphasisModel.get('disabled'); + var dataDims = isCartesian2d ? [data.mapDimension('x'), data.mapDimension('y'), data.mapDimension('value')] : [data.mapDimension('time'), data.mapDimension('value')]; + for (var idx = start; idx < end; idx++) { + var rect = void 0; + var style = data.getItemVisual(idx, 'style'); + if (isCartesian2d) { + var dataDimX = data.get(dataDims[0], idx); + var dataDimY = data.get(dataDims[1], idx); + // Ignore empty data and out of extent data + if (isNaN(data.get(dataDims[2], idx)) || isNaN(dataDimX) || isNaN(dataDimY) || dataDimX < xAxisExtent[0] || dataDimX > xAxisExtent[1] || dataDimY < yAxisExtent[0] || dataDimY > yAxisExtent[1]) { + continue; + } + var point = coordSys.dataToPoint([dataDimX, dataDimY]); + rect = new Rect({ + shape: { + x: point[0] - width / 2, + y: point[1] - height / 2, + width: width, + height: height + }, + style: style + }); + } else { + // Ignore empty data + if (isNaN(data.get(dataDims[1], idx))) { + continue; + } + rect = new Rect({ + z2: 1, + shape: coordSys.dataToRect([data.get(dataDims[0], idx)]).contentShape, + style: style + }); + } + // Optimization for large dataset + if (data.hasItemOption) { + var itemModel = data.getItemModel(idx); + var emphasisModel_1 = itemModel.getModel('emphasis'); + emphasisStyle = emphasisModel_1.getModel('itemStyle').getItemStyle(); + blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle(); + selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle(); + // Each item value struct in the data would be firstly + // { + // itemStyle: { borderRadius: [30, 30] }, + // value: [2022, 02, 22] + // } + borderRadius = itemModel.get(['itemStyle', 'borderRadius']); + focus = emphasisModel_1.get('focus'); + blurScope = emphasisModel_1.get('blurScope'); + emphasisDisabled = emphasisModel_1.get('disabled'); + labelStatesModels = getLabelStatesModels(itemModel); + } + rect.shape.r = borderRadius; + var rawValue = seriesModel.getRawValue(idx); + var defaultText = '-'; + if (rawValue && rawValue[2] != null) { + defaultText = rawValue[2] + ''; + } + setLabelStyle(rect, labelStatesModels, { + labelFetcher: seriesModel, + labelDataIndex: idx, + defaultOpacity: style.opacity, + defaultText: defaultText + }); + rect.ensureState('emphasis').style = emphasisStyle; + rect.ensureState('blur').style = blurStyle; + rect.ensureState('select').style = selectStyle; + toggleHoverEmphasis(rect, focus, blurScope, emphasisDisabled); + rect.incremental = incremental; + // PENDING + if (incremental) { + // Rect must use hover layer if it's incremental. + rect.states.emphasis.hoverLayer = true; + } + group.add(rect); + data.setItemGraphicEl(idx, rect); + if (this._progressiveEls) { + this._progressiveEls.push(rect); + } + } + }; + HeatmapView.prototype._renderOnGeo = function (geo, seriesModel, visualMapModel, api) { + var inRangeVisuals = visualMapModel.targetVisuals.inRange; + var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; + // if (!visualMapping) { + // throw new Error('Data range must have color visuals'); + // } + var data = seriesModel.getData(); + var hmLayer = this._hmLayer || this._hmLayer || new HeatmapLayer(); + hmLayer.blurSize = seriesModel.get('blurSize'); + hmLayer.pointSize = seriesModel.get('pointSize'); + hmLayer.minOpacity = seriesModel.get('minOpacity'); + hmLayer.maxOpacity = seriesModel.get('maxOpacity'); + var rect = geo.getViewRect().clone(); + var roamTransform = geo.getRoamTransform(); + rect.applyTransform(roamTransform); + // Clamp on viewport + var x = Math.max(rect.x, 0); + var y = Math.max(rect.y, 0); + var x2 = Math.min(rect.width + rect.x, api.getWidth()); + var y2 = Math.min(rect.height + rect.y, api.getHeight()); + var width = x2 - x; + var height = y2 - y; + var dims = [data.mapDimension('lng'), data.mapDimension('lat'), data.mapDimension('value')]; + var points = data.mapArray(dims, function (lng, lat, value) { + var pt = geo.dataToPoint([lng, lat]); + pt[0] -= x; + pt[1] -= y; + pt.push(value); + return pt; + }); + var dataExtent = visualMapModel.getExtent(); + var isInRange = visualMapModel.type === 'visualMap.continuous' ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) : getIsInPiecewiseRange(dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected); + hmLayer.update(points, width, height, inRangeVisuals.color.getNormalizer(), { + inRange: inRangeVisuals.color.getColorMapper(), + outOfRange: outOfRangeVisuals.color.getColorMapper() + }, isInRange); + var img = new ZRImage({ + style: { + width: width, + height: height, + x: x, + y: y, + image: hmLayer.canvas + }, + silent: true + }); + this.group.add(img); + }; + HeatmapView.type = 'heatmap'; + return HeatmapView; + }(ChartView); + + var HeatmapSeriesModel = /** @class */function (_super) { + __extends(HeatmapSeriesModel, _super); + function HeatmapSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = HeatmapSeriesModel.type; + return _this; + } + HeatmapSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesData(null, this, { + generateCoord: 'value' + }); + }; + HeatmapSeriesModel.prototype.preventIncremental = function () { + var coordSysCreator = CoordinateSystemManager.get(this.get('coordinateSystem')); + if (coordSysCreator && coordSysCreator.dimensions) { + return coordSysCreator.dimensions[0] === 'lng' && coordSysCreator.dimensions[1] === 'lat'; + } + }; + HeatmapSeriesModel.type = 'series.heatmap'; + HeatmapSeriesModel.dependencies = ['grid', 'geo', 'calendar']; + HeatmapSeriesModel.defaultOption = { + coordinateSystem: 'cartesian2d', + // zlevel: 0, + z: 2, + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + // Geo coordinate system + geoIndex: 0, + blurSize: 30, + pointSize: 20, + maxOpacity: 1, + minOpacity: 0, + select: { + itemStyle: { + borderColor: '#212121' + } + } + }; + return HeatmapSeriesModel; + }(SeriesModel); + + function install$n(registers) { + registers.registerChartView(HeatmapView); + registers.registerSeriesModel(HeatmapSeriesModel); + } + + var BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'borderWidth']; + // index: +isHorizontal + var LAYOUT_ATTRS = [{ + xy: 'x', + wh: 'width', + index: 0, + posDesc: ['left', 'right'] + }, { + xy: 'y', + wh: 'height', + index: 1, + posDesc: ['top', 'bottom'] + }]; + var pathForLineWidth = new Circle(); + var PictorialBarView = /** @class */function (_super) { + __extends(PictorialBarView, _super); + function PictorialBarView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = PictorialBarView.type; + return _this; + } + PictorialBarView.prototype.render = function (seriesModel, ecModel, api) { + var group = this.group; + var data = seriesModel.getData(); + var oldData = this._data; + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var isHorizontal = baseAxis.isHorizontal(); + var coordSysRect = cartesian.master.getRect(); + var opt = { + ecSize: { + width: api.getWidth(), + height: api.getHeight() + }, + seriesModel: seriesModel, + coordSys: cartesian, + coordSysExtent: [[coordSysRect.x, coordSysRect.x + coordSysRect.width], [coordSysRect.y, coordSysRect.y + coordSysRect.height]], + isHorizontal: isHorizontal, + valueDim: LAYOUT_ATTRS[+isHorizontal], + categoryDim: LAYOUT_ATTRS[1 - +isHorizontal] + }; + data.diff(oldData).add(function (dataIndex) { + if (!data.hasValue(dataIndex)) { + return; + } + var itemModel = getItemModel(data, dataIndex); + var symbolMeta = getSymbolMeta(data, dataIndex, itemModel, opt); + var bar = createBar(data, opt, symbolMeta); + data.setItemGraphicEl(dataIndex, bar); + group.add(bar); + updateCommon$1(bar, opt, symbolMeta); + }).update(function (newIndex, oldIndex) { + var bar = oldData.getItemGraphicEl(oldIndex); + if (!data.hasValue(newIndex)) { + group.remove(bar); + return; + } + var itemModel = getItemModel(data, newIndex); + var symbolMeta = getSymbolMeta(data, newIndex, itemModel, opt); + var pictorialShapeStr = getShapeStr(data, symbolMeta); + if (bar && pictorialShapeStr !== bar.__pictorialShapeStr) { + group.remove(bar); + data.setItemGraphicEl(newIndex, null); + bar = null; + } + if (bar) { + updateBar(bar, opt, symbolMeta); + } else { + bar = createBar(data, opt, symbolMeta, true); + } + data.setItemGraphicEl(newIndex, bar); + bar.__pictorialSymbolMeta = symbolMeta; + // Add back + group.add(bar); + updateCommon$1(bar, opt, symbolMeta); + }).remove(function (dataIndex) { + var bar = oldData.getItemGraphicEl(dataIndex); + bar && removeBar(oldData, dataIndex, bar.__pictorialSymbolMeta.animationModel, bar); + }).execute(); + // Do clipping + var clipPath = seriesModel.get('clip', true) ? createClipPath(seriesModel.coordinateSystem, false, seriesModel) : null; + if (clipPath) { + group.setClipPath(clipPath); + } else { + group.removeClipPath(); + } + this._data = data; + return this.group; + }; + PictorialBarView.prototype.remove = function (ecModel, api) { + var group = this.group; + var data = this._data; + if (ecModel.get('animation')) { + if (data) { + data.eachItemGraphicEl(function (bar) { + removeBar(data, getECData(bar).dataIndex, ecModel, bar); + }); + } + } else { + group.removeAll(); + } + }; + PictorialBarView.type = 'pictorialBar'; + return PictorialBarView; + }(ChartView); + // Set or calculate default value about symbol, and calculate layout info. + function getSymbolMeta(data, dataIndex, itemModel, opt) { + var layout = data.getItemLayout(dataIndex); + var symbolRepeat = itemModel.get('symbolRepeat'); + var symbolClip = itemModel.get('symbolClip'); + var symbolPosition = itemModel.get('symbolPosition') || 'start'; + var symbolRotate = itemModel.get('symbolRotate'); + var rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + var symbolPatternSize = itemModel.get('symbolPatternSize') || 2; + var isAnimationEnabled = itemModel.isAnimationEnabled(); + var symbolMeta = { + dataIndex: dataIndex, + layout: layout, + itemModel: itemModel, + symbolType: data.getItemVisual(dataIndex, 'symbol') || 'circle', + style: data.getItemVisual(dataIndex, 'style'), + symbolClip: symbolClip, + symbolRepeat: symbolRepeat, + symbolRepeatDirection: itemModel.get('symbolRepeatDirection'), + symbolPatternSize: symbolPatternSize, + rotation: rotation, + animationModel: isAnimationEnabled ? itemModel : null, + hoverScale: isAnimationEnabled && itemModel.get(['emphasis', 'scale']), + z2: itemModel.getShallow('z', true) || 0 + }; + prepareBarLength(itemModel, symbolRepeat, layout, opt, symbolMeta); + prepareSymbolSize(data, dataIndex, layout, symbolRepeat, symbolClip, symbolMeta.boundingLength, symbolMeta.pxSign, symbolPatternSize, opt, symbolMeta); + prepareLineWidth(itemModel, symbolMeta.symbolScale, rotation, opt, symbolMeta); + var symbolSize = symbolMeta.symbolSize; + var symbolOffset = normalizeSymbolOffset(itemModel.get('symbolOffset'), symbolSize); + prepareLayoutInfo(itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, symbolPosition, symbolMeta.valueLineWidth, symbolMeta.boundingLength, symbolMeta.repeatCutLength, opt, symbolMeta); + return symbolMeta; + } + // bar length can be negative. + function prepareBarLength(itemModel, symbolRepeat, layout, opt, outputSymbolMeta) { + var valueDim = opt.valueDim; + var symbolBoundingData = itemModel.get('symbolBoundingData'); + var valueAxis = opt.coordSys.getOtherAxis(opt.coordSys.getBaseAxis()); + var zeroPx = valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)); + var pxSignIdx = 1 - +(layout[valueDim.wh] <= 0); + var boundingLength; + if (isArray(symbolBoundingData)) { + var symbolBoundingExtent = [convertToCoordOnAxis(valueAxis, symbolBoundingData[0]) - zeroPx, convertToCoordOnAxis(valueAxis, symbolBoundingData[1]) - zeroPx]; + symbolBoundingExtent[1] < symbolBoundingExtent[0] && symbolBoundingExtent.reverse(); + boundingLength = symbolBoundingExtent[pxSignIdx]; + } else if (symbolBoundingData != null) { + boundingLength = convertToCoordOnAxis(valueAxis, symbolBoundingData) - zeroPx; + } else if (symbolRepeat) { + boundingLength = opt.coordSysExtent[valueDim.index][pxSignIdx] - zeroPx; + } else { + boundingLength = layout[valueDim.wh]; + } + outputSymbolMeta.boundingLength = boundingLength; + if (symbolRepeat) { + outputSymbolMeta.repeatCutLength = layout[valueDim.wh]; + } + // if 'pxSign' means sign of pixel, it can't be zero, or symbolScale will be zero + // and when borderWidth be settled, the actual linewidth will be NaN + outputSymbolMeta.pxSign = boundingLength > 0 ? 1 : -1; + } + function convertToCoordOnAxis(axis, value) { + return axis.toGlobalCoord(axis.dataToCoord(axis.scale.parse(value))); + } + // Support ['100%', '100%'] + function prepareSymbolSize(data, dataIndex, layout, symbolRepeat, symbolClip, boundingLength, pxSign, symbolPatternSize, opt, outputSymbolMeta) { + var valueDim = opt.valueDim; + var categoryDim = opt.categoryDim; + var categorySize = Math.abs(layout[categoryDim.wh]); + var symbolSize = data.getItemVisual(dataIndex, 'symbolSize'); + var parsedSymbolSize; + if (isArray(symbolSize)) { + parsedSymbolSize = symbolSize.slice(); + } else { + if (symbolSize == null) { + // will parse to number below + parsedSymbolSize = ['100%', '100%']; + } else { + parsedSymbolSize = [symbolSize, symbolSize]; + } + } + // Note: percentage symbolSize (like '100%') do not consider lineWidth, because it is + // to complicated to calculate real percent value if considering scaled lineWidth. + // So the actual size will bigger than layout size if lineWidth is bigger than zero, + // which can be tolerated in pictorial chart. + parsedSymbolSize[categoryDim.index] = parsePercent$1(parsedSymbolSize[categoryDim.index], categorySize); + parsedSymbolSize[valueDim.index] = parsePercent$1(parsedSymbolSize[valueDim.index], symbolRepeat ? categorySize : Math.abs(boundingLength)); + outputSymbolMeta.symbolSize = parsedSymbolSize; + // If x or y is less than zero, show reversed shape. + var symbolScale = outputSymbolMeta.symbolScale = [parsedSymbolSize[0] / symbolPatternSize, parsedSymbolSize[1] / symbolPatternSize]; + // Follow convention, 'right' and 'top' is the normal scale. + symbolScale[valueDim.index] *= (opt.isHorizontal ? -1 : 1) * pxSign; + } + function prepareLineWidth(itemModel, symbolScale, rotation, opt, outputSymbolMeta) { + // In symbols are drawn with scale, so do not need to care about the case that width + // or height are too small. But symbol use strokeNoScale, where acture lineWidth should + // be calculated. + var valueLineWidth = itemModel.get(BAR_BORDER_WIDTH_QUERY) || 0; + if (valueLineWidth) { + pathForLineWidth.attr({ + scaleX: symbolScale[0], + scaleY: symbolScale[1], + rotation: rotation + }); + pathForLineWidth.updateTransform(); + valueLineWidth /= pathForLineWidth.getLineScale(); + valueLineWidth *= symbolScale[opt.valueDim.index]; + } + outputSymbolMeta.valueLineWidth = valueLineWidth || 0; + } + function prepareLayoutInfo(itemModel, symbolSize, layout, symbolRepeat, symbolClip, symbolOffset, symbolPosition, valueLineWidth, boundingLength, repeatCutLength, opt, outputSymbolMeta) { + var categoryDim = opt.categoryDim; + var valueDim = opt.valueDim; + var pxSign = outputSymbolMeta.pxSign; + var unitLength = Math.max(symbolSize[valueDim.index] + valueLineWidth, 0); + var pathLen = unitLength; + // Note: rotation will not effect the layout of symbols, because user may + // want symbols to rotate on its center, which should not be translated + // when rotating. + if (symbolRepeat) { + var absBoundingLength = Math.abs(boundingLength); + var symbolMargin = retrieve(itemModel.get('symbolMargin'), '15%') + ''; + var hasEndGap = false; + if (symbolMargin.lastIndexOf('!') === symbolMargin.length - 1) { + hasEndGap = true; + symbolMargin = symbolMargin.slice(0, symbolMargin.length - 1); + } + var symbolMarginNumeric = parsePercent$1(symbolMargin, symbolSize[valueDim.index]); + var uLenWithMargin = Math.max(unitLength + symbolMarginNumeric * 2, 0); + // When symbol margin is less than 0, margin at both ends will be subtracted + // to ensure that all of the symbols will not be overflow the given area. + var endFix = hasEndGap ? 0 : symbolMarginNumeric * 2; + // Both final repeatTimes and final symbolMarginNumeric area calculated based on + // boundingLength. + var repeatSpecified = isNumeric(symbolRepeat); + var repeatTimes = repeatSpecified ? symbolRepeat : toIntTimes((absBoundingLength + endFix) / uLenWithMargin); + // Adjust calculate margin, to ensure each symbol is displayed + // entirely in the given layout area. + var mDiff = absBoundingLength - repeatTimes * unitLength; + symbolMarginNumeric = mDiff / 2 / (hasEndGap ? repeatTimes : Math.max(repeatTimes - 1, 1)); + uLenWithMargin = unitLength + symbolMarginNumeric * 2; + endFix = hasEndGap ? 0 : symbolMarginNumeric * 2; + // Update repeatTimes when not all symbol will be shown. + if (!repeatSpecified && symbolRepeat !== 'fixed') { + repeatTimes = repeatCutLength ? toIntTimes((Math.abs(repeatCutLength) + endFix) / uLenWithMargin) : 0; + } + pathLen = repeatTimes * uLenWithMargin - endFix; + outputSymbolMeta.repeatTimes = repeatTimes; + outputSymbolMeta.symbolMargin = symbolMarginNumeric; + } + var sizeFix = pxSign * (pathLen / 2); + var pathPosition = outputSymbolMeta.pathPosition = []; + pathPosition[categoryDim.index] = layout[categoryDim.wh] / 2; + pathPosition[valueDim.index] = symbolPosition === 'start' ? sizeFix : symbolPosition === 'end' ? boundingLength - sizeFix : boundingLength / 2; // 'center' + if (symbolOffset) { + pathPosition[0] += symbolOffset[0]; + pathPosition[1] += symbolOffset[1]; + } + var bundlePosition = outputSymbolMeta.bundlePosition = []; + bundlePosition[categoryDim.index] = layout[categoryDim.xy]; + bundlePosition[valueDim.index] = layout[valueDim.xy]; + var barRectShape = outputSymbolMeta.barRectShape = extend({}, layout); + barRectShape[valueDim.wh] = pxSign * Math.max(Math.abs(layout[valueDim.wh]), Math.abs(pathPosition[valueDim.index] + sizeFix)); + barRectShape[categoryDim.wh] = layout[categoryDim.wh]; + var clipShape = outputSymbolMeta.clipShape = {}; + // Consider that symbol may be overflow layout rect. + clipShape[categoryDim.xy] = -layout[categoryDim.xy]; + clipShape[categoryDim.wh] = opt.ecSize[categoryDim.wh]; + clipShape[valueDim.xy] = 0; + clipShape[valueDim.wh] = layout[valueDim.wh]; + } + function createPath(symbolMeta) { + var symbolPatternSize = symbolMeta.symbolPatternSize; + var path = createSymbol( + // Consider texture img, make a big size. + symbolMeta.symbolType, -symbolPatternSize / 2, -symbolPatternSize / 2, symbolPatternSize, symbolPatternSize); + path.attr({ + culling: true + }); + path.type !== 'image' && path.setStyle({ + strokeNoScale: true + }); + return path; + } + function createOrUpdateRepeatSymbols(bar, opt, symbolMeta, isUpdate) { + var bundle = bar.__pictorialBundle; + var symbolSize = symbolMeta.symbolSize; + var valueLineWidth = symbolMeta.valueLineWidth; + var pathPosition = symbolMeta.pathPosition; + var valueDim = opt.valueDim; + var repeatTimes = symbolMeta.repeatTimes || 0; + var index = 0; + var unit = symbolSize[opt.valueDim.index] + valueLineWidth + symbolMeta.symbolMargin * 2; + eachPath(bar, function (path) { + path.__pictorialAnimationIndex = index; + path.__pictorialRepeatTimes = repeatTimes; + if (index < repeatTimes) { + updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate); + } else { + updateAttr(path, null, { + scaleX: 0, + scaleY: 0 + }, symbolMeta, isUpdate, function () { + bundle.remove(path); + }); + } + // updateHoverAnimation(path, symbolMeta); + index++; + }); + for (; index < repeatTimes; index++) { + var path = createPath(symbolMeta); + path.__pictorialAnimationIndex = index; + path.__pictorialRepeatTimes = repeatTimes; + bundle.add(path); + var target = makeTarget(index); + updateAttr(path, { + x: target.x, + y: target.y, + scaleX: 0, + scaleY: 0 + }, { + scaleX: target.scaleX, + scaleY: target.scaleY, + rotation: target.rotation + }, symbolMeta, isUpdate); + } + function makeTarget(index) { + var position = pathPosition.slice(); + // (start && pxSign > 0) || (end && pxSign < 0): i = repeatTimes - index + // Otherwise: i = index; + var pxSign = symbolMeta.pxSign; + var i = index; + if (symbolMeta.symbolRepeatDirection === 'start' ? pxSign > 0 : pxSign < 0) { + i = repeatTimes - 1 - index; + } + position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index]; + return { + x: position[0], + y: position[1], + scaleX: symbolMeta.symbolScale[0], + scaleY: symbolMeta.symbolScale[1], + rotation: symbolMeta.rotation + }; + } + } + function createOrUpdateSingleSymbol(bar, opt, symbolMeta, isUpdate) { + var bundle = bar.__pictorialBundle; + var mainPath = bar.__pictorialMainPath; + if (!mainPath) { + mainPath = bar.__pictorialMainPath = createPath(symbolMeta); + bundle.add(mainPath); + updateAttr(mainPath, { + x: symbolMeta.pathPosition[0], + y: symbolMeta.pathPosition[1], + scaleX: 0, + scaleY: 0, + rotation: symbolMeta.rotation + }, { + scaleX: symbolMeta.symbolScale[0], + scaleY: symbolMeta.symbolScale[1] + }, symbolMeta, isUpdate); + } else { + updateAttr(mainPath, null, { + x: symbolMeta.pathPosition[0], + y: symbolMeta.pathPosition[1], + scaleX: symbolMeta.symbolScale[0], + scaleY: symbolMeta.symbolScale[1], + rotation: symbolMeta.rotation + }, symbolMeta, isUpdate); + } + } + // bar rect is used for label. + function createOrUpdateBarRect(bar, symbolMeta, isUpdate) { + var rectShape = extend({}, symbolMeta.barRectShape); + var barRect = bar.__pictorialBarRect; + if (!barRect) { + barRect = bar.__pictorialBarRect = new Rect({ + z2: 2, + shape: rectShape, + silent: true, + style: { + stroke: 'transparent', + fill: 'transparent', + lineWidth: 0 + } + }); + barRect.disableMorphing = true; + bar.add(barRect); + } else { + updateAttr(barRect, null, { + shape: rectShape + }, symbolMeta, isUpdate); + } + } + function createOrUpdateClip(bar, opt, symbolMeta, isUpdate) { + // If not clip, symbol will be remove and rebuilt. + if (symbolMeta.symbolClip) { + var clipPath = bar.__pictorialClipPath; + var clipShape = extend({}, symbolMeta.clipShape); + var valueDim = opt.valueDim; + var animationModel = symbolMeta.animationModel; + var dataIndex = symbolMeta.dataIndex; + if (clipPath) { + updateProps(clipPath, { + shape: clipShape + }, animationModel, dataIndex); + } else { + clipShape[valueDim.wh] = 0; + clipPath = new Rect({ + shape: clipShape + }); + bar.__pictorialBundle.setClipPath(clipPath); + bar.__pictorialClipPath = clipPath; + var target = {}; + target[valueDim.wh] = symbolMeta.clipShape[valueDim.wh]; + graphic[isUpdate ? 'updateProps' : 'initProps'](clipPath, { + shape: target + }, animationModel, dataIndex); + } + } + } + function getItemModel(data, dataIndex) { + var itemModel = data.getItemModel(dataIndex); + itemModel.getAnimationDelayParams = getAnimationDelayParams; + itemModel.isAnimationEnabled = isAnimationEnabled; + return itemModel; + } + function getAnimationDelayParams(path) { + // The order is the same as the z-order, see `symbolRepeatDiretion`. + return { + index: path.__pictorialAnimationIndex, + count: path.__pictorialRepeatTimes + }; + } + function isAnimationEnabled() { + // `animation` prop can be set on itemModel in pictorial bar chart. + return this.parentModel.isAnimationEnabled() && !!this.getShallow('animation'); + } + function createBar(data, opt, symbolMeta, isUpdate) { + // bar is the main element for each data. + var bar = new Group(); + // bundle is used for location and clip. + var bundle = new Group(); + bar.add(bundle); + bar.__pictorialBundle = bundle; + bundle.x = symbolMeta.bundlePosition[0]; + bundle.y = symbolMeta.bundlePosition[1]; + if (symbolMeta.symbolRepeat) { + createOrUpdateRepeatSymbols(bar, opt, symbolMeta); + } else { + createOrUpdateSingleSymbol(bar, opt, symbolMeta); + } + createOrUpdateBarRect(bar, symbolMeta, isUpdate); + createOrUpdateClip(bar, opt, symbolMeta, isUpdate); + bar.__pictorialShapeStr = getShapeStr(data, symbolMeta); + bar.__pictorialSymbolMeta = symbolMeta; + return bar; + } + function updateBar(bar, opt, symbolMeta) { + var animationModel = symbolMeta.animationModel; + var dataIndex = symbolMeta.dataIndex; + var bundle = bar.__pictorialBundle; + updateProps(bundle, { + x: symbolMeta.bundlePosition[0], + y: symbolMeta.bundlePosition[1] + }, animationModel, dataIndex); + if (symbolMeta.symbolRepeat) { + createOrUpdateRepeatSymbols(bar, opt, symbolMeta, true); + } else { + createOrUpdateSingleSymbol(bar, opt, symbolMeta, true); + } + createOrUpdateBarRect(bar, symbolMeta, true); + createOrUpdateClip(bar, opt, symbolMeta, true); + } + function removeBar(data, dataIndex, animationModel, bar) { + // Not show text when animating + var labelRect = bar.__pictorialBarRect; + labelRect && labelRect.removeTextContent(); + var paths = []; + eachPath(bar, function (path) { + paths.push(path); + }); + bar.__pictorialMainPath && paths.push(bar.__pictorialMainPath); + // I do not find proper remove animation for clip yet. + bar.__pictorialClipPath && (animationModel = null); + each(paths, function (path) { + removeElement(path, { + scaleX: 0, + scaleY: 0 + }, animationModel, dataIndex, function () { + bar.parent && bar.parent.remove(bar); + }); + }); + data.setItemGraphicEl(dataIndex, null); + } + function getShapeStr(data, symbolMeta) { + return [data.getItemVisual(symbolMeta.dataIndex, 'symbol') || 'none', !!symbolMeta.symbolRepeat, !!symbolMeta.symbolClip].join(':'); + } + function eachPath(bar, cb, context) { + // Do not use Group#eachChild, because it do not support remove. + each(bar.__pictorialBundle.children(), function (el) { + el !== bar.__pictorialBarRect && cb.call(context, el); + }); + } + function updateAttr(el, immediateAttrs, animationAttrs, symbolMeta, isUpdate, cb) { + immediateAttrs && el.attr(immediateAttrs); + // when symbolCip used, only clip path has init animation, otherwise it would be weird effect. + if (symbolMeta.symbolClip && !isUpdate) { + animationAttrs && el.attr(animationAttrs); + } else { + animationAttrs && graphic[isUpdate ? 'updateProps' : 'initProps'](el, animationAttrs, symbolMeta.animationModel, symbolMeta.dataIndex, cb); + } + } + function updateCommon$1(bar, opt, symbolMeta) { + var dataIndex = symbolMeta.dataIndex; + var itemModel = symbolMeta.itemModel; + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var emphasisModel = itemModel.getModel('emphasis'); + var emphasisStyle = emphasisModel.getModel('itemStyle').getItemStyle(); + var blurStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle(); + var selectStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle(); + var cursorStyle = itemModel.getShallow('cursor'); + var focus = emphasisModel.get('focus'); + var blurScope = emphasisModel.get('blurScope'); + var hoverScale = emphasisModel.get('scale'); + eachPath(bar, function (path) { + if (path instanceof ZRImage) { + var pathStyle = path.style; + path.useStyle(extend({ + // TODO other properties like dx, dy ? + image: pathStyle.image, + x: pathStyle.x, + y: pathStyle.y, + width: pathStyle.width, + height: pathStyle.height + }, symbolMeta.style)); + } else { + path.useStyle(symbolMeta.style); + } + var emphasisState = path.ensureState('emphasis'); + emphasisState.style = emphasisStyle; + if (hoverScale) { + // NOTE: Must after scale is set after updateAttr + emphasisState.scaleX = path.scaleX * 1.1; + emphasisState.scaleY = path.scaleY * 1.1; + } + path.ensureState('blur').style = blurStyle; + path.ensureState('select').style = selectStyle; + cursorStyle && (path.cursor = cursorStyle); + path.z2 = symbolMeta.z2; + }); + var barPositionOutside = opt.valueDim.posDesc[+(symbolMeta.boundingLength > 0)]; + var barRect = bar.__pictorialBarRect; + barRect.ignoreClip = true; + setLabelStyle(barRect, getLabelStatesModels(itemModel), { + labelFetcher: opt.seriesModel, + labelDataIndex: dataIndex, + defaultText: getDefaultLabel(opt.seriesModel.getData(), dataIndex), + inheritColor: symbolMeta.style.fill, + defaultOpacity: symbolMeta.style.opacity, + defaultOutsidePosition: barPositionOutside + }); + toggleHoverEmphasis(bar, focus, blurScope, emphasisModel.get('disabled')); + } + function toIntTimes(times) { + var roundedTimes = Math.round(times); + // Escapse accurate error + return Math.abs(times - roundedTimes) < 1e-4 ? roundedTimes : Math.ceil(times); + } + + var PictorialBarSeriesModel = /** @class */function (_super) { + __extends(PictorialBarSeriesModel, _super); + function PictorialBarSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = PictorialBarSeriesModel.type; + _this.hasSymbolVisual = true; + _this.defaultSymbol = 'roundRect'; + return _this; + } + PictorialBarSeriesModel.prototype.getInitialData = function (option) { + // Disable stack. + option.stack = null; + return _super.prototype.getInitialData.apply(this, arguments); + }; + PictorialBarSeriesModel.type = 'series.pictorialBar'; + PictorialBarSeriesModel.dependencies = ['grid']; + PictorialBarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, { + symbol: 'circle', + symbolSize: null, + symbolRotate: null, + symbolPosition: null, + symbolOffset: null, + symbolMargin: null, + symbolRepeat: false, + symbolRepeatDirection: 'end', + symbolClip: false, + symbolBoundingData: null, + symbolPatternSize: 400, + barGap: '-100%', + // Pictorial bar do not clip by default because in many cases + // xAxis and yAxis are not displayed and it's expected not to clip + clip: false, + // z can be set in data item, which is z2 actually. + // Disable progressive + progressive: 0, + emphasis: { + // By default pictorialBar do not hover scale. Hover scale is not suitable + // for the case that both has foreground and background. + scale: false + }, + select: { + itemStyle: { + borderColor: '#212121' + } + } + }); + return PictorialBarSeriesModel; + }(BaseBarSeriesModel); + + function install$o(registers) { + registers.registerChartView(PictorialBarView); + registers.registerSeriesModel(PictorialBarSeriesModel); + registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'pictorialBar')); + // Do layout after other overall layout, which can prepare some information. + registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('pictorialBar')); + } + + var ThemeRiverView = /** @class */function (_super) { + __extends(ThemeRiverView, _super); + function ThemeRiverView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ThemeRiverView.type; + _this._layers = []; + return _this; + } + ThemeRiverView.prototype.render = function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var self = this; + var group = this.group; + var layersSeries = seriesModel.getLayerSeries(); + var layoutInfo = data.getLayout('layoutInfo'); + var rect = layoutInfo.rect; + var boundaryGap = layoutInfo.boundaryGap; + group.x = 0; + group.y = rect.y + boundaryGap[0]; + function keyGetter(item) { + return item.name; + } + var dataDiffer = new DataDiffer(this._layersSeries || [], layersSeries, keyGetter, keyGetter); + var newLayersGroups = []; + dataDiffer.add(bind(process, this, 'add')).update(bind(process, this, 'update')).remove(bind(process, this, 'remove')).execute(); + function process(status, idx, oldIdx) { + var oldLayersGroups = self._layers; + if (status === 'remove') { + group.remove(oldLayersGroups[idx]); + return; + } + var points0 = []; + var points1 = []; + var style; + var indices = layersSeries[idx].indices; + var j = 0; + for (; j < indices.length; j++) { + var layout = data.getItemLayout(indices[j]); + var x = layout.x; + var y0 = layout.y0; + var y = layout.y; + points0.push(x, y0); + points1.push(x, y0 + y); + style = data.getItemVisual(indices[j], 'style'); + } + var polygon; + var textLayout = data.getItemLayout(indices[0]); + var labelModel = seriesModel.getModel('label'); + var margin = labelModel.get('margin'); + var emphasisModel = seriesModel.getModel('emphasis'); + if (status === 'add') { + var layerGroup = newLayersGroups[idx] = new Group(); + polygon = new ECPolygon({ + shape: { + points: points0, + stackedOnPoints: points1, + smooth: 0.4, + stackedOnSmooth: 0.4, + smoothConstraint: false + }, + z2: 0 + }); + layerGroup.add(polygon); + group.add(layerGroup); + if (seriesModel.isAnimationEnabled()) { + polygon.setClipPath(createGridClipShape$2(polygon.getBoundingRect(), seriesModel, function () { + polygon.removeClipPath(); + })); + } + } else { + var layerGroup = oldLayersGroups[oldIdx]; + polygon = layerGroup.childAt(0); + group.add(layerGroup); + newLayersGroups[idx] = layerGroup; + updateProps(polygon, { + shape: { + points: points0, + stackedOnPoints: points1 + } + }, seriesModel); + saveOldStyle(polygon); + } + setLabelStyle(polygon, getLabelStatesModels(seriesModel), { + labelDataIndex: indices[j - 1], + defaultText: data.getName(indices[j - 1]), + inheritColor: style.fill + }, { + normal: { + verticalAlign: 'middle' + // align: 'right' + } + }); + + polygon.setTextConfig({ + position: null, + local: true + }); + var labelEl = polygon.getTextContent(); + // TODO More label position options. + if (labelEl) { + labelEl.x = textLayout.x - margin; + labelEl.y = textLayout.y0 + textLayout.y / 2; + } + polygon.useStyle(style); + data.setItemGraphicEl(idx, polygon); + setStatesStylesFromModel(polygon, seriesModel); + toggleHoverEmphasis(polygon, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + } + this._layersSeries = layersSeries; + this._layers = newLayersGroups; + }; + ThemeRiverView.type = 'themeRiver'; + return ThemeRiverView; + }(ChartView); + // add animation to the view + function createGridClipShape$2(rect, seriesModel, cb) { + var rectEl = new Rect({ + shape: { + x: rect.x - 10, + y: rect.y - 10, + width: 0, + height: rect.height + 20 + } + }); + initProps(rectEl, { + shape: { + x: rect.x - 50, + width: rect.width + 100, + height: rect.height + 20 + } + }, seriesModel, cb); + return rectEl; + } + + var DATA_NAME_INDEX = 2; + var ThemeRiverSeriesModel = /** @class */function (_super) { + __extends(ThemeRiverSeriesModel, _super); + function ThemeRiverSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ThemeRiverSeriesModel.type; + return _this; + } + /** + * @override + */ + ThemeRiverSeriesModel.prototype.init = function (option) { + // eslint-disable-next-line + _super.prototype.init.apply(this, arguments); + // Put this function here is for the sake of consistency of code style. + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); + }; + /** + * If there is no value of a certain point in the time for some event,set it value to 0. + * + * @param {Array} data initial data in the option + * @return {Array} + */ + ThemeRiverSeriesModel.prototype.fixData = function (data) { + var rawDataLength = data.length; + /** + * Make sure every layer data get the same keys. + * The value index tells which layer has visited. + * { + * 2014/01/01: -1 + * } + */ + var timeValueKeys = {}; + // grouped data by name + var groupResult = groupData(data, function (item) { + if (!timeValueKeys.hasOwnProperty(item[0] + '')) { + timeValueKeys[item[0] + ''] = -1; + } + return item[2]; + }); + var layerData = []; + groupResult.buckets.each(function (items, key) { + layerData.push({ + name: key, + dataList: items + }); + }); + var layerNum = layerData.length; + for (var k = 0; k < layerNum; ++k) { + var name_1 = layerData[k].name; + for (var j = 0; j < layerData[k].dataList.length; ++j) { + var timeValue = layerData[k].dataList[j][0] + ''; + timeValueKeys[timeValue] = k; + } + for (var timeValue in timeValueKeys) { + if (timeValueKeys.hasOwnProperty(timeValue) && timeValueKeys[timeValue] !== k) { + timeValueKeys[timeValue] = k; + data[rawDataLength] = [timeValue, 0, name_1]; + rawDataLength++; + } + } + } + return data; + }; + /** + * @override + * @param option the initial option that user gave + * @param ecModel the model object for themeRiver option + */ + ThemeRiverSeriesModel.prototype.getInitialData = function (option, ecModel) { + var singleAxisModel = this.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0]; + var axisType = singleAxisModel.get('type'); + // filter the data item with the value of label is undefined + var filterData = filter(option.data, function (dataItem) { + return dataItem[2] !== undefined; + }); + // ??? TODO design a stage to transfer data for themeRiver and lines? + var data = this.fixData(filterData || []); + var nameList = []; + var nameMap = this.nameMap = createHashMap(); + var count = 0; + for (var i = 0; i < data.length; ++i) { + nameList.push(data[i][DATA_NAME_INDEX]); + if (!nameMap.get(data[i][DATA_NAME_INDEX])) { + nameMap.set(data[i][DATA_NAME_INDEX], count); + count++; + } + } + var dimensions = prepareSeriesDataSchema(data, { + coordDimensions: ['single'], + dimensionsDefine: [{ + name: 'time', + type: getDimensionTypeByAxis(axisType) + }, { + name: 'value', + type: 'float' + }, { + name: 'name', + type: 'ordinal' + }], + encodeDefine: { + single: 0, + value: 1, + itemName: 2 + } + }).dimensions; + var list = new SeriesData(dimensions, this); + list.initData(data); + return list; + }; + /** + * The raw data is divided into multiple layers and each layer + * has same name. + */ + ThemeRiverSeriesModel.prototype.getLayerSeries = function () { + var data = this.getData(); + var lenCount = data.count(); + var indexArr = []; + for (var i = 0; i < lenCount; ++i) { + indexArr[i] = i; + } + var timeDim = data.mapDimension('single'); + // data group by name + var groupResult = groupData(indexArr, function (index) { + return data.get('name', index); + }); + var layerSeries = []; + groupResult.buckets.each(function (items, key) { + items.sort(function (index1, index2) { + return data.get(timeDim, index1) - data.get(timeDim, index2); + }); + layerSeries.push({ + name: key, + indices: items + }); + }); + return layerSeries; + }; + /** + * Get data indices for show tooltip content + */ + ThemeRiverSeriesModel.prototype.getAxisTooltipData = function (dim, value, baseAxis) { + if (!isArray(dim)) { + dim = dim ? [dim] : []; + } + var data = this.getData(); + var layerSeries = this.getLayerSeries(); + var indices = []; + var layerNum = layerSeries.length; + var nestestValue; + for (var i = 0; i < layerNum; ++i) { + var minDist = Number.MAX_VALUE; + var nearestIdx = -1; + var pointNum = layerSeries[i].indices.length; + for (var j = 0; j < pointNum; ++j) { + var theValue = data.get(dim[0], layerSeries[i].indices[j]); + var dist = Math.abs(theValue - value); + if (dist <= minDist) { + nestestValue = theValue; + minDist = dist; + nearestIdx = layerSeries[i].indices[j]; + } + } + indices.push(nearestIdx); + } + return { + dataIndices: indices, + nestestValue: nestestValue + }; + }; + ThemeRiverSeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + var data = this.getData(); + var name = data.getName(dataIndex); + var value = data.get(data.mapDimension('value'), dataIndex); + return createTooltipMarkup('nameValue', { + name: name, + value: value + }); + }; + ThemeRiverSeriesModel.type = 'series.themeRiver'; + ThemeRiverSeriesModel.dependencies = ['singleAxis']; + ThemeRiverSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + colorBy: 'data', + coordinateSystem: 'singleAxis', + // gap in axis's orthogonal orientation + boundaryGap: ['10%', '10%'], + // legendHoverLink: true, + singleAxisIndex: 0, + animationEasing: 'linear', + label: { + margin: 4, + show: true, + position: 'left', + fontSize: 11 + }, + emphasis: { + label: { + show: true + } + } + }; + return ThemeRiverSeriesModel; + }(SeriesModel); + + function themeRiverLayout(ecModel, api) { + ecModel.eachSeriesByType('themeRiver', function (seriesModel) { + var data = seriesModel.getData(); + var single = seriesModel.coordinateSystem; + var layoutInfo = {}; + // use the axis boundingRect for view + var rect = single.getRect(); + layoutInfo.rect = rect; + var boundaryGap = seriesModel.get('boundaryGap'); + var axis = single.getAxis(); + layoutInfo.boundaryGap = boundaryGap; + if (axis.orient === 'horizontal') { + boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.height); + boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.height); + var height = rect.height - boundaryGap[0] - boundaryGap[1]; + doThemeRiverLayout(data, seriesModel, height); + } else { + boundaryGap[0] = parsePercent$1(boundaryGap[0], rect.width); + boundaryGap[1] = parsePercent$1(boundaryGap[1], rect.width); + var width = rect.width - boundaryGap[0] - boundaryGap[1]; + doThemeRiverLayout(data, seriesModel, width); + } + data.setLayout('layoutInfo', layoutInfo); + }); + } + /** + * The layout information about themeriver + * + * @param data data in the series + * @param seriesModel the model object of themeRiver series + * @param height value used to compute every series height + */ + function doThemeRiverLayout(data, seriesModel, height) { + if (!data.count()) { + return; + } + var coordSys = seriesModel.coordinateSystem; + // the data in each layer are organized into a series. + var layerSeries = seriesModel.getLayerSeries(); + // the points in each layer. + var timeDim = data.mapDimension('single'); + var valueDim = data.mapDimension('value'); + var layerPoints = map(layerSeries, function (singleLayer) { + return map(singleLayer.indices, function (idx) { + var pt = coordSys.dataToPoint(data.get(timeDim, idx)); + pt[1] = data.get(valueDim, idx); + return pt; + }); + }); + var base = computeBaseline(layerPoints); + var baseLine = base.y0; + var ky = height / base.max; + // set layout information for each item. + var n = layerSeries.length; + var m = layerSeries[0].indices.length; + var baseY0; + for (var j = 0; j < m; ++j) { + baseY0 = baseLine[j] * ky; + data.setItemLayout(layerSeries[0].indices[j], { + layerIndex: 0, + x: layerPoints[0][j][0], + y0: baseY0, + y: layerPoints[0][j][1] * ky + }); + for (var i = 1; i < n; ++i) { + baseY0 += layerPoints[i - 1][j][1] * ky; + data.setItemLayout(layerSeries[i].indices[j], { + layerIndex: i, + x: layerPoints[i][j][0], + y0: baseY0, + y: layerPoints[i][j][1] * ky + }); + } + } + } + /** + * Compute the baseLine of the rawdata + * Inspired by Lee Byron's paper Stacked Graphs - Geometry & Aesthetics + * + * @param data the points in each layer + */ + function computeBaseline(data) { + var layerNum = data.length; + var pointNum = data[0].length; + var sums = []; + var y0 = []; + var max = 0; + for (var i = 0; i < pointNum; ++i) { + var temp = 0; + for (var j = 0; j < layerNum; ++j) { + temp += data[j][i][1]; + } + if (temp > max) { + max = temp; + } + sums.push(temp); + } + for (var k = 0; k < pointNum; ++k) { + y0[k] = (max - sums[k]) / 2; + } + max = 0; + for (var l = 0; l < pointNum; ++l) { + var sum = sums[l] + y0[l]; + if (sum > max) { + max = sum; + } + } + return { + y0: y0, + max: max + }; + } + + function install$p(registers) { + registers.registerChartView(ThemeRiverView); + registers.registerSeriesModel(ThemeRiverSeriesModel); + registers.registerLayout(themeRiverLayout); + registers.registerProcessor(dataFilter('themeRiver')); + } + + var DEFAULT_SECTOR_Z = 2; + var DEFAULT_TEXT_Z = 4; + /** + * Sunburstce of Sunburst including Sector, Label, LabelLine + */ + var SunburstPiece = /** @class */function (_super) { + __extends(SunburstPiece, _super); + function SunburstPiece(node, seriesModel, ecModel, api) { + var _this = _super.call(this) || this; + _this.z2 = DEFAULT_SECTOR_Z; + _this.textConfig = { + inside: true + }; + getECData(_this).seriesIndex = seriesModel.seriesIndex; + var text = new ZRText({ + z2: DEFAULT_TEXT_Z, + silent: node.getModel().get(['label', 'silent']) + }); + _this.setTextContent(text); + _this.updateData(true, node, seriesModel, ecModel, api); + return _this; + } + SunburstPiece.prototype.updateData = function (firstCreate, node, + // state: 'emphasis' | 'normal' | 'highlight' | 'downplay', + seriesModel, ecModel, api) { + this.node = node; + node.piece = this; + seriesModel = seriesModel || this._seriesModel; + ecModel = ecModel || this._ecModel; + var sector = this; + getECData(sector).dataIndex = node.dataIndex; + var itemModel = node.getModel(); + var emphasisModel = itemModel.getModel('emphasis'); + var layout = node.getLayout(); + var sectorShape = extend({}, layout); + sectorShape.label = null; + var normalStyle = node.getVisual('style'); + normalStyle.lineJoin = 'bevel'; + var decal = node.getVisual('decal'); + if (decal) { + normalStyle.decal = createOrUpdatePatternFromDecal(decal, api); + } + var cornerRadius = getSectorCornerRadius(itemModel.getModel('itemStyle'), sectorShape, true); + extend(sectorShape, cornerRadius); + each(SPECIAL_STATES, function (stateName) { + var state = sector.ensureState(stateName); + var itemStyleModel = itemModel.getModel([stateName, 'itemStyle']); + state.style = itemStyleModel.getItemStyle(); + // border radius + var cornerRadius = getSectorCornerRadius(itemStyleModel, sectorShape); + if (cornerRadius) { + state.shape = cornerRadius; + } + }); + if (firstCreate) { + sector.setShape(sectorShape); + sector.shape.r = layout.r0; + initProps(sector, { + shape: { + r: layout.r + } + }, seriesModel, node.dataIndex); + } else { + // Disable animation for gradient since no interpolation method + // is supported for gradient + updateProps(sector, { + shape: sectorShape + }, seriesModel); + saveOldStyle(sector); + } + sector.useStyle(normalStyle); + this._updateLabel(seriesModel); + var cursorStyle = itemModel.getShallow('cursor'); + cursorStyle && sector.attr('cursor', cursorStyle); + this._seriesModel = seriesModel || this._seriesModel; + this._ecModel = ecModel || this._ecModel; + var focus = emphasisModel.get('focus'); + var focusOrIndices = focus === 'ancestor' ? node.getAncestorsIndices() : focus === 'descendant' ? node.getDescendantIndices() : focus; + toggleHoverEmphasis(this, focusOrIndices, emphasisModel.get('blurScope'), emphasisModel.get('disabled')); + }; + SunburstPiece.prototype._updateLabel = function (seriesModel) { + var _this = this; + var itemModel = this.node.getModel(); + var normalLabelModel = itemModel.getModel('label'); + var layout = this.node.getLayout(); + var angle = layout.endAngle - layout.startAngle; + var midAngle = (layout.startAngle + layout.endAngle) / 2; + var dx = Math.cos(midAngle); + var dy = Math.sin(midAngle); + var sector = this; + var label = sector.getTextContent(); + var dataIndex = this.node.dataIndex; + var labelMinAngle = normalLabelModel.get('minAngle') / 180 * Math.PI; + var isNormalShown = normalLabelModel.get('show') && !(labelMinAngle != null && Math.abs(angle) < labelMinAngle); + label.ignore = !isNormalShown; + // TODO use setLabelStyle + each(DISPLAY_STATES, function (stateName) { + var labelStateModel = stateName === 'normal' ? itemModel.getModel('label') : itemModel.getModel([stateName, 'label']); + var isNormal = stateName === 'normal'; + var state = isNormal ? label : label.ensureState(stateName); + var text = seriesModel.getFormattedLabel(dataIndex, stateName); + if (isNormal) { + text = text || _this.node.name; + } + state.style = createTextStyle(labelStateModel, {}, null, stateName !== 'normal', true); + if (text) { + state.style.text = text; + } + // Not displaying text when angle is too small + var isShown = labelStateModel.get('show'); + if (isShown != null && !isNormal) { + state.ignore = !isShown; + } + var labelPosition = getLabelAttr(labelStateModel, 'position'); + var sectorState = isNormal ? sector : sector.states[stateName]; + var labelColor = sectorState.style.fill; + sectorState.textConfig = { + outsideFill: labelStateModel.get('color') === 'inherit' ? labelColor : null, + inside: labelPosition !== 'outside' + }; + var r; + var labelPadding = getLabelAttr(labelStateModel, 'distance') || 0; + var textAlign = getLabelAttr(labelStateModel, 'align'); + var rotateType = getLabelAttr(labelStateModel, 'rotate'); + var flipStartAngle = Math.PI * 0.5; + var flipEndAngle = Math.PI * 1.5; + var midAngleNormal = normalizeRadian(rotateType === 'tangential' ? Math.PI / 2 - midAngle : midAngle); + // For text that is up-side down, rotate 180 degrees to make sure + // it's readable + var needsFlip = midAngleNormal > flipStartAngle && !isRadianAroundZero(midAngleNormal - flipStartAngle) && midAngleNormal < flipEndAngle; + if (labelPosition === 'outside') { + r = layout.r + labelPadding; + textAlign = needsFlip ? 'right' : 'left'; + } else { + if (!textAlign || textAlign === 'center') { + // Put label in the center if it's a circle + if (angle === 2 * Math.PI && layout.r0 === 0) { + r = 0; + } else { + r = (layout.r + layout.r0) / 2; + } + textAlign = 'center'; + } else if (textAlign === 'left') { + r = layout.r0 + labelPadding; + textAlign = needsFlip ? 'right' : 'left'; + } else if (textAlign === 'right') { + r = layout.r - labelPadding; + textAlign = needsFlip ? 'left' : 'right'; + } + } + state.style.align = textAlign; + state.style.verticalAlign = getLabelAttr(labelStateModel, 'verticalAlign') || 'middle'; + state.x = r * dx + layout.cx; + state.y = r * dy + layout.cy; + var rotate = 0; + if (rotateType === 'radial') { + rotate = normalizeRadian(-midAngle) + (needsFlip ? Math.PI : 0); + } else if (rotateType === 'tangential') { + rotate = normalizeRadian(Math.PI / 2 - midAngle) + (needsFlip ? Math.PI : 0); + } else if (isNumber(rotateType)) { + rotate = rotateType * Math.PI / 180; + } + state.rotation = normalizeRadian(rotate); + }); + function getLabelAttr(model, name) { + var stateAttr = model.get(name); + if (stateAttr == null) { + return normalLabelModel.get(name); + } + return stateAttr; + } + label.dirtyStyle(); + }; + return SunburstPiece; + }(Sector); + + var ROOT_TO_NODE_ACTION = 'sunburstRootToNode'; + var HIGHLIGHT_ACTION = 'sunburstHighlight'; + var UNHIGHLIGHT_ACTION = 'sunburstUnhighlight'; + function installSunburstAction(registers) { + registers.registerAction({ + type: ROOT_TO_NODE_ACTION, + update: 'updateView' + }, function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'series', + subType: 'sunburst', + query: payload + }, handleRootToNode); + function handleRootToNode(model, index) { + var targetInfo = retrieveTargetInfo(payload, [ROOT_TO_NODE_ACTION], model); + if (targetInfo) { + var originViewRoot = model.getViewRoot(); + if (originViewRoot) { + payload.direction = aboveViewRoot(originViewRoot, targetInfo.node) ? 'rollUp' : 'drillDown'; + } + model.resetViewRoot(targetInfo.node); + } + } + }); + registers.registerAction({ + type: HIGHLIGHT_ACTION, + update: 'none' + }, function (payload, ecModel, api) { + // Clone + payload = extend({}, payload); + ecModel.eachComponent({ + mainType: 'series', + subType: 'sunburst', + query: payload + }, handleHighlight); + function handleHighlight(model) { + var targetInfo = retrieveTargetInfo(payload, [HIGHLIGHT_ACTION], model); + if (targetInfo) { + payload.dataIndex = targetInfo.node.dataIndex; + } + } + if ("development" !== 'production') { + deprecateReplaceLog('sunburstHighlight', 'highlight'); + } + // Fast forward action + api.dispatchAction(extend(payload, { + type: 'highlight' + })); + }); + registers.registerAction({ + type: UNHIGHLIGHT_ACTION, + update: 'updateView' + }, function (payload, ecModel, api) { + payload = extend({}, payload); + if ("development" !== 'production') { + deprecateReplaceLog('sunburstUnhighlight', 'downplay'); + } + api.dispatchAction(extend(payload, { + type: 'downplay' + })); + }); + } + + var SunburstView = /** @class */function (_super) { + __extends(SunburstView, _super); + function SunburstView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SunburstView.type; + return _this; + } + SunburstView.prototype.render = function (seriesModel, ecModel, api, + // @ts-ignore + payload) { + var self = this; + this.seriesModel = seriesModel; + this.api = api; + this.ecModel = ecModel; + var data = seriesModel.getData(); + var virtualRoot = data.tree.root; + var newRoot = seriesModel.getViewRoot(); + var group = this.group; + var renderLabelForZeroData = seriesModel.get('renderLabelForZeroData'); + var newChildren = []; + newRoot.eachNode(function (node) { + newChildren.push(node); + }); + var oldChildren = this._oldChildren || []; + dualTravel(newChildren, oldChildren); + renderRollUp(virtualRoot, newRoot); + this._initEvents(); + this._oldChildren = newChildren; + function dualTravel(newChildren, oldChildren) { + if (newChildren.length === 0 && oldChildren.length === 0) { + return; + } + new DataDiffer(oldChildren, newChildren, getKey, getKey).add(processNode).update(processNode).remove(curry(processNode, null)).execute(); + function getKey(node) { + return node.getId(); + } + function processNode(newIdx, oldIdx) { + var newNode = newIdx == null ? null : newChildren[newIdx]; + var oldNode = oldIdx == null ? null : oldChildren[oldIdx]; + doRenderNode(newNode, oldNode); + } + } + function doRenderNode(newNode, oldNode) { + if (!renderLabelForZeroData && newNode && !newNode.getValue()) { + // Not render data with value 0 + newNode = null; + } + if (newNode !== virtualRoot && oldNode !== virtualRoot) { + if (oldNode && oldNode.piece) { + if (newNode) { + // Update + oldNode.piece.updateData(false, newNode, seriesModel, ecModel, api); + // For tooltip + data.setItemGraphicEl(newNode.dataIndex, oldNode.piece); + } else { + // Remove + removeNode(oldNode); + } + } else if (newNode) { + // Add + var piece = new SunburstPiece(newNode, seriesModel, ecModel, api); + group.add(piece); + // For tooltip + data.setItemGraphicEl(newNode.dataIndex, piece); + } + } + } + function removeNode(node) { + if (!node) { + return; + } + if (node.piece) { + group.remove(node.piece); + node.piece = null; + } + } + function renderRollUp(virtualRoot, viewRoot) { + if (viewRoot.depth > 0) { + // Render + if (self.virtualPiece) { + // Update + self.virtualPiece.updateData(false, virtualRoot, seriesModel, ecModel, api); + } else { + // Add + self.virtualPiece = new SunburstPiece(virtualRoot, seriesModel, ecModel, api); + group.add(self.virtualPiece); + } + // TODO event scope + viewRoot.piece.off('click'); + self.virtualPiece.on('click', function (e) { + self._rootToNode(viewRoot.parentNode); + }); + } else if (self.virtualPiece) { + // Remove + group.remove(self.virtualPiece); + self.virtualPiece = null; + } + } + }; + /** + * @private + */ + SunburstView.prototype._initEvents = function () { + var _this = this; + this.group.off('click'); + this.group.on('click', function (e) { + var targetFound = false; + var viewRoot = _this.seriesModel.getViewRoot(); + viewRoot.eachNode(function (node) { + if (!targetFound && node.piece && node.piece === e.target) { + var nodeClick = node.getModel().get('nodeClick'); + if (nodeClick === 'rootToNode') { + _this._rootToNode(node); + } else if (nodeClick === 'link') { + var itemModel = node.getModel(); + var link = itemModel.get('link'); + if (link) { + var linkTarget = itemModel.get('target', true) || '_blank'; + windowOpen(link, linkTarget); + } + } + targetFound = true; + } + }); + }); + }; + /** + * @private + */ + SunburstView.prototype._rootToNode = function (node) { + if (node !== this.seriesModel.getViewRoot()) { + this.api.dispatchAction({ + type: ROOT_TO_NODE_ACTION, + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: node + }); + } + }; + /** + * @implement + */ + SunburstView.prototype.containPoint = function (point, seriesModel) { + var treeRoot = seriesModel.getData(); + var itemLayout = treeRoot.getItemLayout(0); + if (itemLayout) { + var dx = point[0] - itemLayout.cx; + var dy = point[1] - itemLayout.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + return radius <= itemLayout.r && radius >= itemLayout.r0; + } + }; + SunburstView.type = 'sunburst'; + return SunburstView; + }(ChartView); + + var SunburstSeriesModel = /** @class */function (_super) { + __extends(SunburstSeriesModel, _super); + function SunburstSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SunburstSeriesModel.type; + _this.ignoreStyleOnData = true; + return _this; + } + SunburstSeriesModel.prototype.getInitialData = function (option, ecModel) { + // Create a virtual root. + var root = { + name: option.name, + children: option.data + }; + completeTreeValue$1(root); + var levelModels = this._levelModels = map(option.levels || [], function (levelDefine) { + return new Model(levelDefine, this, ecModel); + }, this); + // Make sure always a new tree is created when setOption, + // in TreemapView, we check whether oldTree === newTree + // to choose mappings approach among old shapes and new shapes. + var tree = Tree.createTree(root, this, beforeLink); + function beforeLink(nodeData) { + nodeData.wrapMethod('getItemModel', function (model, idx) { + var node = tree.getNodeByDataIndex(idx); + var levelModel = levelModels[node.depth]; + levelModel && (model.parentModel = levelModel); + return model; + }); + } + return tree.data; + }; + SunburstSeriesModel.prototype.optionUpdated = function () { + this.resetViewRoot(); + }; + /* + * @override + */ + SunburstSeriesModel.prototype.getDataParams = function (dataIndex) { + var params = _super.prototype.getDataParams.apply(this, arguments); + var node = this.getData().tree.getNodeByDataIndex(dataIndex); + params.treePathInfo = wrapTreePathInfo(node, this); + return params; + }; + SunburstSeriesModel.prototype.getLevelModel = function (node) { + return this._levelModels && this._levelModels[node.depth]; + }; + SunburstSeriesModel.prototype.getViewRoot = function () { + return this._viewRoot; + }; + SunburstSeriesModel.prototype.resetViewRoot = function (viewRoot) { + viewRoot ? this._viewRoot = viewRoot : viewRoot = this._viewRoot; + var root = this.getRawData().tree.root; + if (!viewRoot || viewRoot !== root && !root.contains(viewRoot)) { + this._viewRoot = root; + } + }; + SunburstSeriesModel.prototype.enableAriaDecal = function () { + enableAriaDecalForTree(this); + }; + SunburstSeriesModel.type = 'series.sunburst'; + SunburstSeriesModel.defaultOption = { + // zlevel: 0, + z: 2, + // 默认全局居中 + center: ['50%', '50%'], + radius: [0, '75%'], + // 默认顺时针 + clockwise: true, + startAngle: 90, + // 最小角度改为0 + minAngle: 0, + // If still show when all data zero. + stillShowZeroSum: true, + // 'rootToNode', 'link', or false + nodeClick: 'rootToNode', + renderLabelForZeroData: false, + label: { + // could be: 'radial', 'tangential', or 'none' + rotate: 'radial', + show: true, + opacity: 1, + // 'left' is for inner side of inside, and 'right' is for outer + // side for inside + align: 'center', + position: 'inside', + distance: 5, + silent: true + }, + itemStyle: { + borderWidth: 1, + borderColor: 'white', + borderType: 'solid', + shadowBlur: 0, + shadowColor: 'rgba(0, 0, 0, 0.2)', + shadowOffsetX: 0, + shadowOffsetY: 0, + opacity: 1 + }, + emphasis: { + focus: 'descendant' + }, + blur: { + itemStyle: { + opacity: 0.2 + }, + label: { + opacity: 0.1 + } + }, + // Animation type can be expansion, scale. + animationType: 'expansion', + animationDuration: 1000, + animationDurationUpdate: 500, + data: [], + /** + * Sort order. + * + * Valid values: 'desc', 'asc', null, or callback function. + * 'desc' and 'asc' for descend and ascendant order; + * null for not sorting; + * example of callback function: + * function(nodeA, nodeB) { + * return nodeA.getValue() - nodeB.getValue(); + * } + */ + sort: 'desc' + }; + return SunburstSeriesModel; + }(SeriesModel); + function completeTreeValue$1(dataNode) { + // Postorder travel tree. + // If value of none-leaf node is not set, + // calculate it by suming up the value of all children. + var sum = 0; + each(dataNode.children, function (child) { + completeTreeValue$1(child); + var childValue = child.value; + // TODO First value of array must be a number + isArray(childValue) && (childValue = childValue[0]); + sum += childValue; + }); + var thisValue = dataNode.value; + if (isArray(thisValue)) { + thisValue = thisValue[0]; + } + if (thisValue == null || isNaN(thisValue)) { + thisValue = sum; + } + // Value should not less than 0. + if (thisValue < 0) { + thisValue = 0; + } + isArray(dataNode.value) ? dataNode.value[0] = thisValue : dataNode.value = thisValue; + } + + // let PI2 = Math.PI * 2; + var RADIAN$2 = Math.PI / 180; + function sunburstLayout(seriesType, ecModel, api) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var center = seriesModel.get('center'); + var radius = seriesModel.get('radius'); + if (!isArray(radius)) { + radius = [0, radius]; + } + if (!isArray(center)) { + center = [center, center]; + } + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent$1(center[0], width); + var cy = parsePercent$1(center[1], height); + var r0 = parsePercent$1(radius[0], size / 2); + var r = parsePercent$1(radius[1], size / 2); + var startAngle = -seriesModel.get('startAngle') * RADIAN$2; + var minAngle = seriesModel.get('minAngle') * RADIAN$2; + var virtualRoot = seriesModel.getData().tree.root; + var treeRoot = seriesModel.getViewRoot(); + var rootDepth = treeRoot.depth; + var sort = seriesModel.get('sort'); + if (sort != null) { + initChildren$1(treeRoot, sort); + } + var validDataCount = 0; + each(treeRoot.children, function (child) { + !isNaN(child.getValue()) && validDataCount++; + }); + var sum = treeRoot.getValue(); + // Sum may be 0 + var unitRadian = Math.PI / (sum || validDataCount) * 2; + var renderRollupNode = treeRoot.depth > 0; + var levels = treeRoot.height - (renderRollupNode ? -1 : 1); + var rPerLevel = (r - r0) / (levels || 1); + var clockwise = seriesModel.get('clockwise'); + var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); + // In the case some sector angle is smaller than minAngle + // let restAngle = PI2; + // let valueSumLargerThanMinAngle = 0; + var dir = clockwise ? 1 : -1; + /** + * Render a tree + * @return increased angle + */ + var renderNode = function (node, startAngle) { + if (!node) { + return; + } + var endAngle = startAngle; + // Render self + if (node !== virtualRoot) { + // Tree node is virtual, so it doesn't need to be drawn + var value = node.getValue(); + var angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian; + if (angle < minAngle) { + angle = minAngle; + // restAngle -= minAngle; + } + // else { + // valueSumLargerThanMinAngle += value; + // } + endAngle = startAngle + dir * angle; + var depth = node.depth - rootDepth - (renderRollupNode ? -1 : 1); + var rStart = r0 + rPerLevel * depth; + var rEnd = r0 + rPerLevel * (depth + 1); + var levelModel = seriesModel.getLevelModel(node); + if (levelModel) { + var r0_1 = levelModel.get('r0', true); + var r_1 = levelModel.get('r', true); + var radius_1 = levelModel.get('radius', true); + if (radius_1 != null) { + r0_1 = radius_1[0]; + r_1 = radius_1[1]; + } + r0_1 != null && (rStart = parsePercent$1(r0_1, size / 2)); + r_1 != null && (rEnd = parsePercent$1(r_1, size / 2)); + } + node.setLayout({ + angle: angle, + startAngle: startAngle, + endAngle: endAngle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: rStart, + r: rEnd + }); + } + // Render children + if (node.children && node.children.length) { + // currentAngle = startAngle; + var siblingAngle_1 = 0; + each(node.children, function (node) { + siblingAngle_1 += renderNode(node, startAngle + siblingAngle_1); + }); + } + return endAngle - startAngle; + }; + // Virtual root node for roll up + if (renderRollupNode) { + var rStart = r0; + var rEnd = r0 + rPerLevel; + var angle = Math.PI * 2; + virtualRoot.setLayout({ + angle: angle, + startAngle: startAngle, + endAngle: startAngle + angle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: rStart, + r: rEnd + }); + } + renderNode(treeRoot, startAngle); + }); + } + /** + * Init node children by order and update visual + */ + function initChildren$1(node, sortOrder) { + var children = node.children || []; + node.children = sort$2(children, sortOrder); + // Init children recursively + if (children.length) { + each(node.children, function (child) { + initChildren$1(child, sortOrder); + }); + } + } + /** + * Sort children nodes + * + * @param {TreeNode[]} children children of node to be sorted + * @param {string | function | null} sort sort method + * See SunburstSeries.js for details. + */ + function sort$2(children, sortOrder) { + if (isFunction(sortOrder)) { + var sortTargets = map(children, function (child, idx) { + var value = child.getValue(); + return { + params: { + depth: child.depth, + height: child.height, + dataIndex: child.dataIndex, + getValue: function () { + return value; + } + }, + index: idx + }; + }); + sortTargets.sort(function (a, b) { + return sortOrder(a.params, b.params); + }); + return map(sortTargets, function (target) { + return children[target.index]; + }); + } else { + var isAsc_1 = sortOrder === 'asc'; + return children.sort(function (a, b) { + var diff = (a.getValue() - b.getValue()) * (isAsc_1 ? 1 : -1); + return diff === 0 ? (a.dataIndex - b.dataIndex) * (isAsc_1 ? -1 : 1) : diff; + }); + } + } + + function sunburstVisual(ecModel) { + var paletteScope = {}; + // Default color strategy + function pickColor(node, seriesModel, treeHeight) { + // Choose color from palette based on the first level. + var current = node; + while (current && current.depth > 1) { + current = current.parentNode; + } + var color = seriesModel.getColorFromPalette(current.name || current.dataIndex + '', paletteScope); + if (node.depth > 1 && isString(color)) { + // Lighter on the deeper level. + color = lift(color, (node.depth - 1) / (treeHeight - 1) * 0.5); + } + return color; + } + ecModel.eachSeriesByType('sunburst', function (seriesModel) { + var data = seriesModel.getData(); + var tree = data.tree; + tree.eachNode(function (node) { + var model = node.getModel(); + var style = model.getModel('itemStyle').getItemStyle(); + if (!style.fill) { + style.fill = pickColor(node, seriesModel, tree.root.height); + } + var existsStyle = data.ensureUniqueItemVisual(node.dataIndex, 'style'); + extend(existsStyle, style); + }); + }); + } + + function install$q(registers) { + registers.registerChartView(SunburstView); + registers.registerSeriesModel(SunburstSeriesModel); + registers.registerLayout(curry(sunburstLayout, 'sunburst')); + registers.registerProcessor(curry(dataFilter, 'sunburst')); + registers.registerVisual(sunburstVisual); + installSunburstAction(registers); + } + + // Also compat with ec4, where + // `visual('color') visual('borderColor')` is supported. + var STYLE_VISUAL_TYPE = { + color: 'fill', + borderColor: 'stroke' + }; + var NON_STYLE_VISUAL_PROPS = { + symbol: 1, + symbolSize: 1, + symbolKeepAspect: 1, + legendIcon: 1, + visualMeta: 1, + liftZ: 1, + decal: 1 + }; + var customInnerStore = makeInner(); + var CustomSeriesModel = /** @class */function (_super) { + __extends(CustomSeriesModel, _super); + function CustomSeriesModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CustomSeriesModel.type; + return _this; + } + CustomSeriesModel.prototype.optionUpdated = function () { + this.currentZLevel = this.get('zlevel', true); + this.currentZ = this.get('z', true); + }; + CustomSeriesModel.prototype.getInitialData = function (option, ecModel) { + return createSeriesData(null, this); + }; + CustomSeriesModel.prototype.getDataParams = function (dataIndex, dataType, el) { + var params = _super.prototype.getDataParams.call(this, dataIndex, dataType); + el && (params.info = customInnerStore(el).info); + return params; + }; + CustomSeriesModel.type = 'series.custom'; + CustomSeriesModel.dependencies = ['grid', 'polar', 'geo', 'singleAxis', 'calendar']; + CustomSeriesModel.defaultOption = { + coordinateSystem: 'cartesian2d', + // zlevel: 0, + z: 2, + legendHoverLink: true, + // Custom series will not clip by default. + // Some case will use custom series to draw label + // For example https://echarts.apache.org/examples/en/editor.html?c=custom-gantt-flight + clip: false + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + // Polar coordinate system + // polarIndex: 0, + // Geo coordinate system + // geoIndex: 0, + }; + + return CustomSeriesModel; + }(SeriesModel); + + function dataToCoordSize(dataSize, dataItem) { + // dataItem is necessary in log axis. + dataItem = dataItem || [0, 0]; + return map(['x', 'y'], function (dim, dimIdx) { + var axis = this.getAxis(dim); + var val = dataItem[dimIdx]; + var halfSize = dataSize[dimIdx] / 2; + return axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); + }, this); + } + function cartesianPrepareCustom(coordSys) { + var rect = coordSys.master.getRect(); + return { + coordSys: { + // The name exposed to user is always 'cartesian2d' but not 'grid'. + type: 'cartesian2d', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + api: { + coord: function (data) { + // do not provide "out" param + return coordSys.dataToPoint(data); + }, + size: bind(dataToCoordSize, coordSys) + } + }; + } + + function dataToCoordSize$1(dataSize, dataItem) { + dataItem = dataItem || [0, 0]; + return map([0, 1], function (dimIdx) { + var val = dataItem[dimIdx]; + var halfSize = dataSize[dimIdx] / 2; + var p1 = []; + var p2 = []; + p1[dimIdx] = val - halfSize; + p2[dimIdx] = val + halfSize; + p1[1 - dimIdx] = p2[1 - dimIdx] = dataItem[1 - dimIdx]; + return Math.abs(this.dataToPoint(p1)[dimIdx] - this.dataToPoint(p2)[dimIdx]); + }, this); + } + function geoPrepareCustom(coordSys) { + var rect = coordSys.getBoundingRect(); + return { + coordSys: { + type: 'geo', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + zoom: coordSys.getZoom() + }, + api: { + coord: function (data) { + // do not provide "out" and noRoam param, + // Compatible with this usage: + // echarts.util.map(item.points, api.coord) + return coordSys.dataToPoint(data); + }, + size: bind(dataToCoordSize$1, coordSys) + } + }; + } + + function dataToCoordSize$2(dataSize, dataItem) { + // dataItem is necessary in log axis. + var axis = this.getAxis(); + var val = dataItem instanceof Array ? dataItem[0] : dataItem; + var halfSize = (dataSize instanceof Array ? dataSize[0] : dataSize) / 2; + return axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); + } + function singlePrepareCustom(coordSys) { + var rect = coordSys.getRect(); + return { + coordSys: { + type: 'singleAxis', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }, + api: { + coord: function (val) { + // do not provide "out" param + return coordSys.dataToPoint(val); + }, + size: bind(dataToCoordSize$2, coordSys) + } + }; + } + + // import AngleAxis from './AngleAxis.js'; + function dataToCoordSize$3(dataSize, dataItem) { + // dataItem is necessary in log axis. + dataItem = dataItem || [0, 0]; + return map(['Radius', 'Angle'], function (dim, dimIdx) { + var getterName = 'get' + dim + 'Axis'; + // TODO: TYPE Check Angle Axis + var axis = this[getterName](); + var val = dataItem[dimIdx]; + var halfSize = dataSize[dimIdx] / 2; + var result = axis.type === 'category' ? axis.getBandWidth() : Math.abs(axis.dataToCoord(val - halfSize) - axis.dataToCoord(val + halfSize)); + if (dim === 'Angle') { + result = result * Math.PI / 180; + } + return result; + }, this); + } + function polarPrepareCustom(coordSys) { + var radiusAxis = coordSys.getRadiusAxis(); + var angleAxis = coordSys.getAngleAxis(); + var radius = radiusAxis.getExtent(); + radius[0] > radius[1] && radius.reverse(); + return { + coordSys: { + type: 'polar', + cx: coordSys.cx, + cy: coordSys.cy, + r: radius[1], + r0: radius[0] + }, + api: { + coord: function (data) { + var radius = radiusAxis.dataToRadius(data[0]); + var angle = angleAxis.dataToAngle(data[1]); + var coord = coordSys.coordToPoint([radius, angle]); + coord.push(radius, angle * Math.PI / 180); + return coord; + }, + size: bind(dataToCoordSize$3, coordSys) + } + }; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function calendarPrepareCustom(coordSys) { + var rect = coordSys.getRect(); + var rangeInfo = coordSys.getRangeInfo(); + return { + coordSys: { + type: 'calendar', + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height, + cellWidth: coordSys.getCellWidth(), + cellHeight: coordSys.getCellHeight(), + rangeInfo: { + start: rangeInfo.start, + end: rangeInfo.end, + weeks: rangeInfo.weeks, + dayCount: rangeInfo.allDay + } + }, + api: { + coord: function (data, clamp) { + return coordSys.dataToPoint(data, clamp); + } + } + }; + } + + var deprecatedLogs = {}; + /** + * Whether need to call `convertEC4CompatibleStyle`. + */ + function isEC4CompatibleStyle(style, elType, hasOwnTextContentOption, hasOwnTextConfig) { + // Since echarts5, `RectText` is separated from its host element and style.text + // does not exist any more. The compat work brings some extra burden on performance. + // So we provide: + // `legacy: true` force make compat. + // `legacy: false`, force do not compat. + // `legacy` not set: auto detect whether legacy. + // But in this case we do not compat (difficult to detect and rare case): + // Becuse custom series and graphic component support "merge", users may firstly + // only set `textStrokeWidth` style or secondly only set `text`. + return style && (style.legacy || style.legacy !== false && !hasOwnTextContentOption && !hasOwnTextConfig && elType !== 'tspan' + // Difficult to detect whether legacy for a "text" el. + && (elType === 'text' || hasOwn(style, 'text'))); + } + /** + * `EC4CompatibleStyle` is style that might be in echarts4 format or echarts5 format. + * @param hostStyle The properties might be modified. + * @return If be text el, `textContentStyle` and `textConfig` will not be returned. + * Otherwise a `textContentStyle` and `textConfig` will be created, whose props area + * retried from the `hostStyle`. + */ + function convertFromEC4CompatibleStyle(hostStyle, elType, isNormal) { + var srcStyle = hostStyle; + var textConfig; + var textContent; + var textContentStyle; + if (elType === 'text') { + textContentStyle = srcStyle; + } else { + textContentStyle = {}; + hasOwn(srcStyle, 'text') && (textContentStyle.text = srcStyle.text); + hasOwn(srcStyle, 'rich') && (textContentStyle.rich = srcStyle.rich); + hasOwn(srcStyle, 'textFill') && (textContentStyle.fill = srcStyle.textFill); + hasOwn(srcStyle, 'textStroke') && (textContentStyle.stroke = srcStyle.textStroke); + hasOwn(srcStyle, 'fontFamily') && (textContentStyle.fontFamily = srcStyle.fontFamily); + hasOwn(srcStyle, 'fontSize') && (textContentStyle.fontSize = srcStyle.fontSize); + hasOwn(srcStyle, 'fontStyle') && (textContentStyle.fontStyle = srcStyle.fontStyle); + hasOwn(srcStyle, 'fontWeight') && (textContentStyle.fontWeight = srcStyle.fontWeight); + textContent = { + type: 'text', + style: textContentStyle, + // ec4 does not support rectText trigger. + // And when text position is different in normal and emphasis + // => hover text trigger emphasis; + // => text position changed, leave mouse pointer immediately; + // That might cause incorrect state. + silent: true + }; + textConfig = {}; + var hasOwnPos = hasOwn(srcStyle, 'textPosition'); + if (isNormal) { + textConfig.position = hasOwnPos ? srcStyle.textPosition : 'inside'; + } else { + hasOwnPos && (textConfig.position = srcStyle.textPosition); + } + hasOwn(srcStyle, 'textPosition') && (textConfig.position = srcStyle.textPosition); + hasOwn(srcStyle, 'textOffset') && (textConfig.offset = srcStyle.textOffset); + hasOwn(srcStyle, 'textRotation') && (textConfig.rotation = srcStyle.textRotation); + hasOwn(srcStyle, 'textDistance') && (textConfig.distance = srcStyle.textDistance); + } + convertEC4CompatibleRichItem(textContentStyle, hostStyle); + each(textContentStyle.rich, function (richItem) { + convertEC4CompatibleRichItem(richItem, richItem); + }); + return { + textConfig: textConfig, + textContent: textContent + }; + } + /** + * The result will be set to `out`. + */ + function convertEC4CompatibleRichItem(out, richItem) { + if (!richItem) { + return; + } + // (1) For simplicity, make textXXX properties (deprecated since ec5) has + // higher priority. For example, consider in ec4 `borderColor: 5, textBorderColor: 10` + // on a rect means `borderColor: 4` on the rect and `borderColor: 10` on an attached + // richText in ec5. + // (2) `out === richItem` if and only if `out` is text el or rich item. + // So we can overwrite existing props in `out` since textXXX has higher priority. + richItem.font = richItem.textFont || richItem.font; + hasOwn(richItem, 'textStrokeWidth') && (out.lineWidth = richItem.textStrokeWidth); + hasOwn(richItem, 'textAlign') && (out.align = richItem.textAlign); + hasOwn(richItem, 'textVerticalAlign') && (out.verticalAlign = richItem.textVerticalAlign); + hasOwn(richItem, 'textLineHeight') && (out.lineHeight = richItem.textLineHeight); + hasOwn(richItem, 'textWidth') && (out.width = richItem.textWidth); + hasOwn(richItem, 'textHeight') && (out.height = richItem.textHeight); + hasOwn(richItem, 'textBackgroundColor') && (out.backgroundColor = richItem.textBackgroundColor); + hasOwn(richItem, 'textPadding') && (out.padding = richItem.textPadding); + hasOwn(richItem, 'textBorderColor') && (out.borderColor = richItem.textBorderColor); + hasOwn(richItem, 'textBorderWidth') && (out.borderWidth = richItem.textBorderWidth); + hasOwn(richItem, 'textBorderRadius') && (out.borderRadius = richItem.textBorderRadius); + hasOwn(richItem, 'textBoxShadowColor') && (out.shadowColor = richItem.textBoxShadowColor); + hasOwn(richItem, 'textBoxShadowBlur') && (out.shadowBlur = richItem.textBoxShadowBlur); + hasOwn(richItem, 'textBoxShadowOffsetX') && (out.shadowOffsetX = richItem.textBoxShadowOffsetX); + hasOwn(richItem, 'textBoxShadowOffsetY') && (out.shadowOffsetY = richItem.textBoxShadowOffsetY); + } + /** + * Convert to pure echarts4 format style. + * `itemStyle` will be modified, added with ec4 style properties from + * `textStyle` and `textConfig`. + * + * [Caveat]: For simplicity, `insideRollback` in ec4 does not compat, where + * `styleEmphasis: {textFill: 'red'}` will remove the normal auto added stroke. + */ + function convertToEC4StyleForCustomSerise(itemStl, txStl, txCfg) { + var out = itemStl; + // See `custom.ts`, a trick to set extra `textPosition` firstly. + out.textPosition = out.textPosition || txCfg.position || 'inside'; + txCfg.offset != null && (out.textOffset = txCfg.offset); + txCfg.rotation != null && (out.textRotation = txCfg.rotation); + txCfg.distance != null && (out.textDistance = txCfg.distance); + var isInside = out.textPosition.indexOf('inside') >= 0; + var hostFill = itemStl.fill || '#000'; + convertToEC4RichItem(out, txStl); + var textFillNotSet = out.textFill == null; + if (isInside) { + if (textFillNotSet) { + out.textFill = txCfg.insideFill || '#fff'; + !out.textStroke && txCfg.insideStroke && (out.textStroke = txCfg.insideStroke); + !out.textStroke && (out.textStroke = hostFill); + out.textStrokeWidth == null && (out.textStrokeWidth = 2); + } + } else { + if (textFillNotSet) { + out.textFill = itemStl.fill || txCfg.outsideFill || '#000'; + } + !out.textStroke && txCfg.outsideStroke && (out.textStroke = txCfg.outsideStroke); + } + out.text = txStl.text; + out.rich = txStl.rich; + each(txStl.rich, function (richItem) { + convertToEC4RichItem(richItem, richItem); + }); + return out; + } + function convertToEC4RichItem(out, richItem) { + if (!richItem) { + return; + } + hasOwn(richItem, 'fill') && (out.textFill = richItem.fill); + hasOwn(richItem, 'stroke') && (out.textStroke = richItem.fill); + hasOwn(richItem, 'lineWidth') && (out.textStrokeWidth = richItem.lineWidth); + hasOwn(richItem, 'font') && (out.font = richItem.font); + hasOwn(richItem, 'fontStyle') && (out.fontStyle = richItem.fontStyle); + hasOwn(richItem, 'fontWeight') && (out.fontWeight = richItem.fontWeight); + hasOwn(richItem, 'fontSize') && (out.fontSize = richItem.fontSize); + hasOwn(richItem, 'fontFamily') && (out.fontFamily = richItem.fontFamily); + hasOwn(richItem, 'align') && (out.textAlign = richItem.align); + hasOwn(richItem, 'verticalAlign') && (out.textVerticalAlign = richItem.verticalAlign); + hasOwn(richItem, 'lineHeight') && (out.textLineHeight = richItem.lineHeight); + hasOwn(richItem, 'width') && (out.textWidth = richItem.width); + hasOwn(richItem, 'height') && (out.textHeight = richItem.height); + hasOwn(richItem, 'backgroundColor') && (out.textBackgroundColor = richItem.backgroundColor); + hasOwn(richItem, 'padding') && (out.textPadding = richItem.padding); + hasOwn(richItem, 'borderColor') && (out.textBorderColor = richItem.borderColor); + hasOwn(richItem, 'borderWidth') && (out.textBorderWidth = richItem.borderWidth); + hasOwn(richItem, 'borderRadius') && (out.textBorderRadius = richItem.borderRadius); + hasOwn(richItem, 'shadowColor') && (out.textBoxShadowColor = richItem.shadowColor); + hasOwn(richItem, 'shadowBlur') && (out.textBoxShadowBlur = richItem.shadowBlur); + hasOwn(richItem, 'shadowOffsetX') && (out.textBoxShadowOffsetX = richItem.shadowOffsetX); + hasOwn(richItem, 'shadowOffsetY') && (out.textBoxShadowOffsetY = richItem.shadowOffsetY); + hasOwn(richItem, 'textShadowColor') && (out.textShadowColor = richItem.textShadowColor); + hasOwn(richItem, 'textShadowBlur') && (out.textShadowBlur = richItem.textShadowBlur); + hasOwn(richItem, 'textShadowOffsetX') && (out.textShadowOffsetX = richItem.textShadowOffsetX); + hasOwn(richItem, 'textShadowOffsetY') && (out.textShadowOffsetY = richItem.textShadowOffsetY); + } + function warnDeprecated(deprecated, insteadApproach) { + if ("development" !== 'production') { + var key = deprecated + '^_^' + insteadApproach; + if (!deprecatedLogs[key]) { + console.warn("[ECharts] DEPRECATED: \"" + deprecated + "\" has been deprecated. " + insteadApproach); + deprecatedLogs[key] = true; + } + } + } + + var LEGACY_TRANSFORM_PROPS_MAP = { + position: ['x', 'y'], + scale: ['scaleX', 'scaleY'], + origin: ['originX', 'originY'] + }; + var LEGACY_TRANSFORM_PROPS = keys(LEGACY_TRANSFORM_PROPS_MAP); + var TRANSFORM_PROPS_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) { + obj[key] = 1; + return obj; + }, {}); + var transformPropNamesStr = TRANSFORMABLE_PROPS.join(', '); + // '' means root + var ELEMENT_ANIMATABLE_PROPS = ['', 'style', 'shape', 'extra']; + var transitionInnerStore = makeInner(); + function getElementAnimationConfig(animationType, el, elOption, parentModel, dataIndex) { + var animationProp = animationType + "Animation"; + var config = getAnimationConfig(animationType, parentModel, dataIndex) || {}; + var userDuring = transitionInnerStore(el).userDuring; + // Only set when duration is > 0 and it's need to be animated. + if (config.duration > 0) { + // For simplicity, if during not specified, the previous during will not work any more. + config.during = userDuring ? bind(duringCall, { + el: el, + userDuring: userDuring + }) : null; + config.setToFinal = true; + config.scope = animationType; + } + extend(config, elOption[animationProp]); + return config; + } + function applyUpdateTransition(el, elOption, animatableModel, opts) { + opts = opts || {}; + var dataIndex = opts.dataIndex, + isInit = opts.isInit, + clearStyle = opts.clearStyle; + var hasAnimation = animatableModel.isAnimationEnabled(); + // Save the meta info for further morphing. Like apply on the sub morphing elements. + var store = transitionInnerStore(el); + var styleOpt = elOption.style; + store.userDuring = elOption.during; + var transFromProps = {}; + var propsToSet = {}; + prepareTransformAllPropsFinal(el, elOption, propsToSet); + prepareShapeOrExtraAllPropsFinal('shape', elOption, propsToSet); + prepareShapeOrExtraAllPropsFinal('extra', elOption, propsToSet); + if (!isInit && hasAnimation) { + prepareTransformTransitionFrom(el, elOption, transFromProps); + prepareShapeOrExtraTransitionFrom('shape', el, elOption, transFromProps); + prepareShapeOrExtraTransitionFrom('extra', el, elOption, transFromProps); + prepareStyleTransitionFrom(el, elOption, styleOpt, transFromProps); + } + propsToSet.style = styleOpt; + applyPropsDirectly(el, propsToSet, clearStyle); + applyMiscProps(el, elOption); + if (hasAnimation) { + if (isInit) { + var enterFromProps_1 = {}; + each(ELEMENT_ANIMATABLE_PROPS, function (propName) { + var prop = propName ? elOption[propName] : elOption; + if (prop && prop.enterFrom) { + if (propName) { + enterFromProps_1[propName] = enterFromProps_1[propName] || {}; + } + extend(propName ? enterFromProps_1[propName] : enterFromProps_1, prop.enterFrom); + } + }); + var config = getElementAnimationConfig('enter', el, elOption, animatableModel, dataIndex); + if (config.duration > 0) { + el.animateFrom(enterFromProps_1, config); + } + } else { + applyPropsTransition(el, elOption, dataIndex || 0, animatableModel, transFromProps); + } + } + // Store leave to be used in leave transition. + updateLeaveTo(el, elOption); + styleOpt ? el.dirty() : el.markRedraw(); + } + function updateLeaveTo(el, elOption) { + // Try merge to previous set leaveTo + var leaveToProps = transitionInnerStore(el).leaveToProps; + for (var i = 0; i < ELEMENT_ANIMATABLE_PROPS.length; i++) { + var propName = ELEMENT_ANIMATABLE_PROPS[i]; + var prop = propName ? elOption[propName] : elOption; + if (prop && prop.leaveTo) { + if (!leaveToProps) { + leaveToProps = transitionInnerStore(el).leaveToProps = {}; + } + if (propName) { + leaveToProps[propName] = leaveToProps[propName] || {}; + } + extend(propName ? leaveToProps[propName] : leaveToProps, prop.leaveTo); + } + } + } + function applyLeaveTransition(el, elOption, animatableModel, onRemove) { + if (el) { + var parent_1 = el.parent; + var leaveToProps = transitionInnerStore(el).leaveToProps; + if (leaveToProps) { + // TODO TODO use leave after leaveAnimation in series is introduced + // TODO Data index? + var config = getElementAnimationConfig('update', el, elOption, animatableModel, 0); + config.done = function () { + parent_1.remove(el); + onRemove && onRemove(); + }; + el.animateTo(leaveToProps, config); + } else { + parent_1.remove(el); + onRemove && onRemove(); + } + } + } + function isTransitionAll(transition) { + return transition === 'all'; + } + function applyPropsDirectly(el, + // Can be null/undefined + allPropsFinal, clearStyle) { + var styleOpt = allPropsFinal.style; + if (!el.isGroup && styleOpt) { + if (clearStyle) { + el.useStyle({}); + // When style object changed, how to trade the existing animation? + // It is probably complicated and not needed to cover all the cases. + // But still need consider the case: + // (1) When using init animation on `style.opacity`, and before the animation + // ended users triggers an update by mousewhel. At that time the init + // animation should better be continued rather than terminated. + // So after `useStyle` called, we should change the animation target manually + // to continue the effect of the init animation. + // (2) PENDING: If the previous animation targeted at a `val1`, and currently we need + // to update the value to `val2` and no animation declared, should be terminate + // the previous animation or just modify the target of the animation? + // Therotically That will happen not only on `style` but also on `shape` and + // `transfrom` props. But we haven't handle this case at present yet. + // (3) PENDING: Is it proper to visit `animators` and `targetName`? + var animators = el.animators; + for (var i = 0; i < animators.length; i++) { + var animator = animators[i]; + // targetName is the "topKey". + if (animator.targetName === 'style') { + animator.changeTarget(el.style); + } + } + } + el.setStyle(styleOpt); + } + if (allPropsFinal) { + // Not set style here. + allPropsFinal.style = null; + // Set el to the final state firstly. + allPropsFinal && el.attr(allPropsFinal); + allPropsFinal.style = styleOpt; + } + } + function applyPropsTransition(el, elOption, dataIndex, model, + // Can be null/undefined + transFromProps) { + if (transFromProps) { + var config = getElementAnimationConfig('update', el, elOption, model, dataIndex); + if (config.duration > 0) { + el.animateFrom(transFromProps, config); + } + } + } + function applyMiscProps(el, elOption) { + // Merge by default. + hasOwn(elOption, 'silent') && (el.silent = elOption.silent); + hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore); + if (el instanceof Displayable) { + hasOwn(elOption, 'invisible') && (el.invisible = elOption.invisible); + } + if (el instanceof Path) { + hasOwn(elOption, 'autoBatch') && (el.autoBatch = elOption.autoBatch); + } + } + // Use it to avoid it be exposed to user. + var tmpDuringScope = {}; + var transitionDuringAPI = { + // Usually other props do not need to be changed in animation during. + setTransform: function (key, val) { + if ("development" !== 'production') { + assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `setTransform`.'); + } + tmpDuringScope.el[key] = val; + return this; + }, + getTransform: function (key) { + if ("development" !== 'production') { + assert(hasOwn(TRANSFORM_PROPS_MAP, key), 'Only ' + transformPropNamesStr + ' available in `getTransform`.'); + } + return tmpDuringScope.el[key]; + }, + setShape: function (key, val) { + if ("development" !== 'production') { + assertNotReserved(key); + } + var el = tmpDuringScope.el; + var shape = el.shape || (el.shape = {}); + shape[key] = val; + el.dirtyShape && el.dirtyShape(); + return this; + }, + getShape: function (key) { + if ("development" !== 'production') { + assertNotReserved(key); + } + var shape = tmpDuringScope.el.shape; + if (shape) { + return shape[key]; + } + }, + setStyle: function (key, val) { + if ("development" !== 'production') { + assertNotReserved(key); + } + var el = tmpDuringScope.el; + var style = el.style; + if (style) { + if ("development" !== 'production') { + if (eqNaN(val)) { + warn('style.' + key + ' must not be assigned with NaN.'); + } + } + style[key] = val; + el.dirtyStyle && el.dirtyStyle(); + } + return this; + }, + getStyle: function (key) { + if ("development" !== 'production') { + assertNotReserved(key); + } + var style = tmpDuringScope.el.style; + if (style) { + return style[key]; + } + }, + setExtra: function (key, val) { + if ("development" !== 'production') { + assertNotReserved(key); + } + var extra = tmpDuringScope.el.extra || (tmpDuringScope.el.extra = {}); + extra[key] = val; + return this; + }, + getExtra: function (key) { + if ("development" !== 'production') { + assertNotReserved(key); + } + var extra = tmpDuringScope.el.extra; + if (extra) { + return extra[key]; + } + } + }; + function assertNotReserved(key) { + if ("development" !== 'production') { + if (key === 'transition' || key === 'enterFrom' || key === 'leaveTo') { + throw new Error('key must not be "' + key + '"'); + } + } + } + function duringCall() { + // Do not provide "percent" until some requirements come. + // Because consider thies case: + // enterFrom: {x: 100, y: 30}, transition: 'x'. + // And enter duration is different from update duration. + // Thus it might be confused about the meaning of "percent" in during callback. + var scope = this; + var el = scope.el; + if (!el) { + return; + } + // If el is remove from zr by reason like legend, during still need to called, + // because el will be added back to zr and the prop value should not be incorrect. + var latestUserDuring = transitionInnerStore(el).userDuring; + var scopeUserDuring = scope.userDuring; + // Ensured a during is only called once in each animation frame. + // If a during is called multiple times in one frame, maybe some users' calculation logic + // might be wrong (not sure whether this usage exists). + // The case of a during might be called twice can be: by default there is a animator for + // 'x', 'y' when init. Before the init animation finished, call `setOption` to start + // another animators for 'style'/'shape'/'extra'. + if (latestUserDuring !== scopeUserDuring) { + // release + scope.el = scope.userDuring = null; + return; + } + tmpDuringScope.el = el; + // Give no `this` to user in "during" calling. + scopeUserDuring(transitionDuringAPI); + // FIXME: if in future meet the case that some prop will be both modified in `during` and `state`, + // consider the issue that the prop might be incorrect when return to "normal" state. + } + + function prepareShapeOrExtraTransitionFrom(mainAttr, fromEl, elOption, transFromProps) { + var attrOpt = elOption[mainAttr]; + if (!attrOpt) { + return; + } + var elPropsInAttr = fromEl[mainAttr]; + var transFromPropsInAttr; + if (elPropsInAttr) { + var transition = elOption.transition; + var attrTransition = attrOpt.transition; + if (attrTransition) { + !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {}); + if (isTransitionAll(attrTransition)) { + extend(transFromPropsInAttr, elPropsInAttr); + } else { + var transitionKeys = normalizeToArray(attrTransition); + for (var i = 0; i < transitionKeys.length; i++) { + var key = transitionKeys[i]; + var elVal = elPropsInAttr[key]; + transFromPropsInAttr[key] = elVal; + } + } + } else if (isTransitionAll(transition) || indexOf(transition, mainAttr) >= 0) { + !transFromPropsInAttr && (transFromPropsInAttr = transFromProps[mainAttr] = {}); + var elPropsInAttrKeys = keys(elPropsInAttr); + for (var i = 0; i < elPropsInAttrKeys.length; i++) { + var key = elPropsInAttrKeys[i]; + var elVal = elPropsInAttr[key]; + if (isNonStyleTransitionEnabled(attrOpt[key], elVal)) { + transFromPropsInAttr[key] = elVal; + } + } + } + } + } + function prepareShapeOrExtraAllPropsFinal(mainAttr, elOption, allProps) { + var attrOpt = elOption[mainAttr]; + if (!attrOpt) { + return; + } + var allPropsInAttr = allProps[mainAttr] = {}; + var keysInAttr = keys(attrOpt); + for (var i = 0; i < keysInAttr.length; i++) { + var key = keysInAttr[i]; + // To avoid share one object with different element, and + // to avoid user modify the object inexpectedly, have to clone. + allPropsInAttr[key] = cloneValue(attrOpt[key]); + } + } + function prepareTransformTransitionFrom(el, elOption, transFromProps) { + var transition = elOption.transition; + var transitionKeys = isTransitionAll(transition) ? TRANSFORMABLE_PROPS : normalizeToArray(transition || []); + for (var i = 0; i < transitionKeys.length; i++) { + var key = transitionKeys[i]; + if (key === 'style' || key === 'shape' || key === 'extra') { + continue; + } + var elVal = el[key]; + if ("development" !== 'production') { + checkTransformPropRefer(key, 'el.transition'); + } + // Do not clone, animator will perform that clone. + transFromProps[key] = elVal; + } + } + function prepareTransformAllPropsFinal(el, elOption, allProps) { + for (var i = 0; i < LEGACY_TRANSFORM_PROPS.length; i++) { + var legacyName = LEGACY_TRANSFORM_PROPS[i]; + var xyName = LEGACY_TRANSFORM_PROPS_MAP[legacyName]; + var legacyArr = elOption[legacyName]; + if (legacyArr) { + allProps[xyName[0]] = legacyArr[0]; + allProps[xyName[1]] = legacyArr[1]; + } + } + for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) { + var key = TRANSFORMABLE_PROPS[i]; + if (elOption[key] != null) { + allProps[key] = elOption[key]; + } + } + } + function prepareStyleTransitionFrom(fromEl, elOption, styleOpt, transFromProps) { + if (!styleOpt) { + return; + } + var fromElStyle = fromEl.style; + var transFromStyleProps; + if (fromElStyle) { + var styleTransition = styleOpt.transition; + var elTransition = elOption.transition; + if (styleTransition && !isTransitionAll(styleTransition)) { + var transitionKeys = normalizeToArray(styleTransition); + !transFromStyleProps && (transFromStyleProps = transFromProps.style = {}); + for (var i = 0; i < transitionKeys.length; i++) { + var key = transitionKeys[i]; + var elVal = fromElStyle[key]; + // Do not clone, see `checkNonStyleTansitionRefer`. + transFromStyleProps[key] = elVal; + } + } else if (fromEl.getAnimationStyleProps && (isTransitionAll(elTransition) || isTransitionAll(styleTransition) || indexOf(elTransition, 'style') >= 0)) { + var animationProps = fromEl.getAnimationStyleProps(); + var animationStyleProps = animationProps ? animationProps.style : null; + if (animationStyleProps) { + !transFromStyleProps && (transFromStyleProps = transFromProps.style = {}); + var styleKeys = keys(styleOpt); + for (var i = 0; i < styleKeys.length; i++) { + var key = styleKeys[i]; + if (animationStyleProps[key]) { + var elVal = fromElStyle[key]; + transFromStyleProps[key] = elVal; + } + } + } + } + } + } + function isNonStyleTransitionEnabled(optVal, elVal) { + // The same as `checkNonStyleTansitionRefer`. + return !isArrayLike(optVal) ? optVal != null && isFinite(optVal) : optVal !== elVal; + } + var checkTransformPropRefer; + if ("development" !== 'production') { + checkTransformPropRefer = function (key, usedIn) { + if (!hasOwn(TRANSFORM_PROPS_MAP, key)) { + warn('Prop `' + key + '` is not a permitted in `' + usedIn + '`. ' + 'Only `' + keys(TRANSFORM_PROPS_MAP).join('`, `') + '` are permitted.'); + } + }; + } + + var getStateToRestore = makeInner(); + var KEYFRAME_EXCLUDE_KEYS = ['percent', 'easing', 'shape', 'style', 'extra']; + /** + * Stop previous keyframe animation and restore the attributes. + * Avoid new keyframe animation starts with wrong internal state when the percent: 0 is not set. + */ + function stopPreviousKeyframeAnimationAndRestore(el) { + // Stop previous keyframe animation. + el.stopAnimation('keyframe'); + // Restore + el.attr(getStateToRestore(el)); + } + function applyKeyframeAnimation(el, animationOpts, animatableModel) { + if (!animatableModel.isAnimationEnabled() || !animationOpts) { + return; + } + if (isArray(animationOpts)) { + each(animationOpts, function (singleAnimationOpts) { + applyKeyframeAnimation(el, singleAnimationOpts, animatableModel); + }); + return; + } + var keyframes = animationOpts.keyframes; + var duration = animationOpts.duration; + if (animatableModel && duration == null) { + // Default to use duration of config. + // NOTE: animation config from payload will be ignored because they are mainly for transitions. + var config = getAnimationConfig('enter', animatableModel, 0); + duration = config && config.duration; + } + if (!keyframes || !duration) { + return; + } + var stateToRestore = getStateToRestore(el); + each(ELEMENT_ANIMATABLE_PROPS, function (targetPropName) { + if (targetPropName && !el[targetPropName]) { + return; + } + var animator; + var endFrameIsSet = false; + // Sort keyframes by percent. + keyframes.sort(function (a, b) { + return a.percent - b.percent; + }); + each(keyframes, function (kf) { + // Stop current animation. + var animators = el.animators; + var kfValues = targetPropName ? kf[targetPropName] : kf; + if ("development" !== 'production') { + if (kf.percent >= 1) { + endFrameIsSet = true; + } + } + if (!kfValues) { + return; + } + var propKeys = keys(kfValues); + if (!targetPropName) { + // PENDING performance? + propKeys = filter(propKeys, function (key) { + return indexOf(KEYFRAME_EXCLUDE_KEYS, key) < 0; + }); + } + if (!propKeys.length) { + return; + } + if (!animator) { + animator = el.animate(targetPropName, animationOpts.loop, true); + animator.scope = 'keyframe'; + } + for (var i = 0; i < animators.length; i++) { + // Stop all other animation that is not keyframe. + if (animators[i] !== animator && animators[i].targetName === animator.targetName) { + animators[i].stopTracks(propKeys); + } + } + targetPropName && (stateToRestore[targetPropName] = stateToRestore[targetPropName] || {}); + var savedTarget = targetPropName ? stateToRestore[targetPropName] : stateToRestore; + each(propKeys, function (key) { + // Save original value. + savedTarget[key] = ((targetPropName ? el[targetPropName] : el) || {})[key]; + }); + animator.whenWithKeys(duration * kf.percent, kfValues, propKeys, kf.easing); + }); + if (!animator) { + return; + } + if ("development" !== 'production') { + if (!endFrameIsSet) { + warn('End frame with percent: 1 is missing in the keyframeAnimation.', true); + } + } + animator.delay(animationOpts.delay || 0).duration(duration).start(animationOpts.easing); + }); + } + + var EMPHASIS = 'emphasis'; + var NORMAL = 'normal'; + var BLUR = 'blur'; + var SELECT = 'select'; + var STATES = [NORMAL, EMPHASIS, BLUR, SELECT]; + var PATH_ITEM_STYLE = { + normal: ['itemStyle'], + emphasis: [EMPHASIS, 'itemStyle'], + blur: [BLUR, 'itemStyle'], + select: [SELECT, 'itemStyle'] + }; + var PATH_LABEL = { + normal: ['label'], + emphasis: [EMPHASIS, 'label'], + blur: [BLUR, 'label'], + select: [SELECT, 'label'] + }; + var DEFAULT_TRANSITION = ['x', 'y']; + // Use prefix to avoid index to be the same as el.name, + // which will cause weird update animation. + var GROUP_DIFF_PREFIX = 'e\0\0'; + var attachedTxInfoTmp = { + normal: {}, + emphasis: {}, + blur: {}, + select: {} + }; + /** + * To reduce total package size of each coordinate systems, the modules `prepareCustom` + * of each coordinate systems are not required by each coordinate systems directly, but + * required by the module `custom`. + * + * prepareInfoForCustomSeries {Function}: optional + * @return {Object} {coordSys: {...}, api: { + * coord: function (data, clamp) {}, // return point in global. + * size: function (dataSize, dataItem) {} // return size of each axis in coordSys. + * }} + */ + var prepareCustoms = { + cartesian2d: cartesianPrepareCustom, + geo: geoPrepareCustom, + single: singlePrepareCustom, + polar: polarPrepareCustom, + calendar: calendarPrepareCustom + }; + function isPath$1(el) { + return el instanceof Path; + } + function isDisplayable(el) { + return el instanceof Displayable; + } + function copyElement(sourceEl, targetEl) { + targetEl.copyTransform(sourceEl); + if (isDisplayable(targetEl) && isDisplayable(sourceEl)) { + targetEl.setStyle(sourceEl.style); + targetEl.z = sourceEl.z; + targetEl.z2 = sourceEl.z2; + targetEl.zlevel = sourceEl.zlevel; + targetEl.invisible = sourceEl.invisible; + targetEl.ignore = sourceEl.ignore; + if (isPath$1(targetEl) && isPath$1(sourceEl)) { + targetEl.setShape(sourceEl.shape); + } + } + } + var CustomChartView = /** @class */function (_super) { + __extends(CustomChartView, _super); + function CustomChartView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CustomChartView.type; + return _this; + } + CustomChartView.prototype.render = function (customSeries, ecModel, api, payload) { + // Clear previously rendered progressive elements. + this._progressiveEls = null; + var oldData = this._data; + var data = customSeries.getData(); + var group = this.group; + var renderItem = makeRenderItem(customSeries, data, ecModel, api); + if (!oldData) { + // Previous render is incremental render or first render. + // Needs remove the incremental rendered elements. + group.removeAll(); + } + data.diff(oldData).add(function (newIdx) { + createOrUpdateItem(api, null, newIdx, renderItem(newIdx, payload), customSeries, group, data); + }).remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && applyLeaveTransition(el, customInnerStore(el).option, customSeries); + }).update(function (newIdx, oldIdx) { + var oldEl = oldData.getItemGraphicEl(oldIdx); + createOrUpdateItem(api, oldEl, newIdx, renderItem(newIdx, payload), customSeries, group, data); + }).execute(); + // Do clipping + var clipPath = customSeries.get('clip', true) ? createClipPath(customSeries.coordinateSystem, false, customSeries) : null; + if (clipPath) { + group.setClipPath(clipPath); + } else { + group.removeClipPath(); + } + this._data = data; + }; + CustomChartView.prototype.incrementalPrepareRender = function (customSeries, ecModel, api) { + this.group.removeAll(); + this._data = null; + }; + CustomChartView.prototype.incrementalRender = function (params, customSeries, ecModel, api, payload) { + var data = customSeries.getData(); + var renderItem = makeRenderItem(customSeries, data, ecModel, api); + var progressiveEls = this._progressiveEls = []; + function setIncrementalAndHoverLayer(el) { + if (!el.isGroup) { + el.incremental = true; + el.ensureState('emphasis').hoverLayer = true; + } + } + for (var idx = params.start; idx < params.end; idx++) { + var el = createOrUpdateItem(null, null, idx, renderItem(idx, payload), customSeries, this.group, data); + if (el) { + el.traverse(setIncrementalAndHoverLayer); + progressiveEls.push(el); + } + } + }; + CustomChartView.prototype.eachRendered = function (cb) { + traverseElements(this._progressiveEls || this.group, cb); + }; + CustomChartView.prototype.filterForExposedEvent = function (eventType, query, targetEl, packedEvent) { + var elementName = query.element; + if (elementName == null || targetEl.name === elementName) { + return true; + } + // Enable to give a name on a group made by `renderItem`, and listen + // events that are triggered by its descendents. + while ((targetEl = targetEl.__hostTarget || targetEl.parent) && targetEl !== this.group) { + if (targetEl.name === elementName) { + return true; + } + } + return false; + }; + CustomChartView.type = 'custom'; + return CustomChartView; + }(ChartView); + function createEl(elOption) { + var graphicType = elOption.type; + var el; + // Those graphic elements are not shapes. They should not be + // overwritten by users, so do them first. + if (graphicType === 'path') { + var shape = elOption.shape; + // Using pathRect brings convenience to users sacle svg path. + var pathRect = shape.width != null && shape.height != null ? { + x: shape.x || 0, + y: shape.y || 0, + width: shape.width, + height: shape.height + } : null; + var pathData = getPathData(shape); + // Path is also used for icon, so layout 'center' by default. + el = makePath(pathData, null, pathRect, shape.layout || 'center'); + customInnerStore(el).customPathData = pathData; + } else if (graphicType === 'image') { + el = new ZRImage({}); + customInnerStore(el).customImagePath = elOption.style.image; + } else if (graphicType === 'text') { + el = new ZRText({}); + // customInnerStore(el).customText = (elOption.style as TextStyleProps).text; + } else if (graphicType === 'group') { + el = new Group(); + } else if (graphicType === 'compoundPath') { + throw new Error('"compoundPath" is not supported yet.'); + } else { + var Clz = getShapeClass(graphicType); + if (!Clz) { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = 'graphic type "' + graphicType + '" can not be found.'; + } + throwError(errMsg); + } + el = new Clz(); + } + customInnerStore(el).customGraphicType = graphicType; + el.name = elOption.name; + // Compat ec4: the default z2 lift is 1. If changing the number, + // some cases probably be broken: hierarchy layout along z, like circle packing, + // where emphasis only intending to modify color/border rather than lift z2. + el.z2EmphasisLift = 1; + el.z2SelectLift = 1; + return el; + } + function updateElNormal( + // Can be null/undefined + api, el, dataIndex, elOption, attachedTxInfo, seriesModel, isInit) { + // Stop and restore before update any other attributes. + stopPreviousKeyframeAnimationAndRestore(el); + var txCfgOpt = attachedTxInfo && attachedTxInfo.normal.cfg; + if (txCfgOpt) { + // PENDING: whether use user object directly rather than clone? + // TODO:5.0 textConfig transition animation? + el.setTextConfig(txCfgOpt); + } + // Default transition ['x', 'y'] + if (elOption && elOption.transition == null) { + elOption.transition = DEFAULT_TRANSITION; + } + // Do some normalization on style. + var styleOpt = elOption && elOption.style; + if (styleOpt) { + if (el.type === 'text') { + var textOptionStyle = styleOpt; + // Compatible with ec4: if `textFill` or `textStroke` exists use them. + hasOwn(textOptionStyle, 'textFill') && (textOptionStyle.fill = textOptionStyle.textFill); + hasOwn(textOptionStyle, 'textStroke') && (textOptionStyle.stroke = textOptionStyle.textStroke); + } + var decalPattern = void 0; + var decalObj = isPath$1(el) ? styleOpt.decal : null; + if (api && decalObj) { + decalObj.dirty = true; + decalPattern = createOrUpdatePatternFromDecal(decalObj, api); + } + // Always overwrite in case user specify this prop. + styleOpt.__decalPattern = decalPattern; + } + if (isDisplayable(el)) { + if (styleOpt) { + var decalPattern = styleOpt.__decalPattern; + if (decalPattern) { + styleOpt.decal = decalPattern; + } + } + } + applyUpdateTransition(el, elOption, seriesModel, { + dataIndex: dataIndex, + isInit: isInit, + clearStyle: true + }); + applyKeyframeAnimation(el, elOption.keyframeAnimation, seriesModel); + } + function updateElOnState(state, el, elStateOpt, styleOpt, attachedTxInfo) { + var elDisplayable = el.isGroup ? null : el; + var txCfgOpt = attachedTxInfo && attachedTxInfo[state].cfg; + // PENDING:5.0 support customize scale change and transition animation? + if (elDisplayable) { + // By default support auto lift color when hover whether `emphasis` specified. + var stateObj = elDisplayable.ensureState(state); + if (styleOpt === false) { + var existingEmphasisState = elDisplayable.getState(state); + if (existingEmphasisState) { + existingEmphasisState.style = null; + } + } else { + // style is needed to enable default emphasis. + stateObj.style = styleOpt || null; + } + // If `elOption.styleEmphasis` or `elOption.emphasis.style` is `false`, + // remove hover style. + // If `elOption.textConfig` or `elOption.emphasis.textConfig` is null/undefined, it does not + // make sense. So for simplicity, we do not ditinguish `hasOwnProperty` and null/undefined. + if (txCfgOpt) { + stateObj.textConfig = txCfgOpt; + } + setDefaultStateProxy(elDisplayable); + } + } + function updateZ$1(el, elOption, seriesModel) { + // Group not support textContent and not support z yet. + if (el.isGroup) { + return; + } + var elDisplayable = el; + var currentZ = seriesModel.currentZ; + var currentZLevel = seriesModel.currentZLevel; + // Always erase. + elDisplayable.z = currentZ; + elDisplayable.zlevel = currentZLevel; + // z2 must not be null/undefined, otherwise sort error may occur. + var optZ2 = elOption.z2; + optZ2 != null && (elDisplayable.z2 = optZ2 || 0); + for (var i = 0; i < STATES.length; i++) { + updateZForEachState(elDisplayable, elOption, STATES[i]); + } + } + function updateZForEachState(elDisplayable, elOption, state) { + var isNormal = state === NORMAL; + var elStateOpt = isNormal ? elOption : retrieveStateOption(elOption, state); + var optZ2 = elStateOpt ? elStateOpt.z2 : null; + var stateObj; + if (optZ2 != null) { + // Do not `ensureState` until required. + stateObj = isNormal ? elDisplayable : elDisplayable.ensureState(state); + stateObj.z2 = optZ2 || 0; + } + } + function makeRenderItem(customSeries, data, ecModel, api) { + var renderItem = customSeries.get('renderItem'); + var coordSys = customSeries.coordinateSystem; + var prepareResult = {}; + if (coordSys) { + if ("development" !== 'production') { + assert(renderItem, 'series.render is required.'); + assert(coordSys.prepareCustoms || prepareCustoms[coordSys.type], 'This coordSys does not support custom series.'); + } + // `coordSys.prepareCustoms` is used for external coord sys like bmap. + prepareResult = coordSys.prepareCustoms ? coordSys.prepareCustoms(coordSys) : prepareCustoms[coordSys.type](coordSys); + } + var userAPI = defaults({ + getWidth: api.getWidth, + getHeight: api.getHeight, + getZr: api.getZr, + getDevicePixelRatio: api.getDevicePixelRatio, + value: value, + style: style, + ordinalRawValue: ordinalRawValue, + styleEmphasis: styleEmphasis, + visual: visual, + barLayout: barLayout, + currentSeriesIndices: currentSeriesIndices, + font: font + }, prepareResult.api || {}); + var userParams = { + // The life cycle of context: current round of rendering. + // The global life cycle is probably not necessary, because + // user can store global status by themselves. + context: {}, + seriesId: customSeries.id, + seriesName: customSeries.name, + seriesIndex: customSeries.seriesIndex, + coordSys: prepareResult.coordSys, + dataInsideLength: data.count(), + encode: wrapEncodeDef(customSeries.getData()) + }; + // If someday intending to refactor them to a class, should consider do not + // break change: currently these attribute member are encapsulated in a closure + // so that do not need to force user to call these method with a scope. + // Do not support call `api` asynchronously without dataIndexInside input. + var currDataIndexInside; + var currItemModel; + var currItemStyleModels = {}; + var currLabelModels = {}; + var seriesItemStyleModels = {}; + var seriesLabelModels = {}; + for (var i = 0; i < STATES.length; i++) { + var stateName = STATES[i]; + seriesItemStyleModels[stateName] = customSeries.getModel(PATH_ITEM_STYLE[stateName]); + seriesLabelModels[stateName] = customSeries.getModel(PATH_LABEL[stateName]); + } + function getItemModel(dataIndexInside) { + return dataIndexInside === currDataIndexInside ? currItemModel || (currItemModel = data.getItemModel(dataIndexInside)) : data.getItemModel(dataIndexInside); + } + function getItemStyleModel(dataIndexInside, state) { + return !data.hasItemOption ? seriesItemStyleModels[state] : dataIndexInside === currDataIndexInside ? currItemStyleModels[state] || (currItemStyleModels[state] = getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state])) : getItemModel(dataIndexInside).getModel(PATH_ITEM_STYLE[state]); + } + function getLabelModel(dataIndexInside, state) { + return !data.hasItemOption ? seriesLabelModels[state] : dataIndexInside === currDataIndexInside ? currLabelModels[state] || (currLabelModels[state] = getItemModel(dataIndexInside).getModel(PATH_LABEL[state])) : getItemModel(dataIndexInside).getModel(PATH_LABEL[state]); + } + return function (dataIndexInside, payload) { + currDataIndexInside = dataIndexInside; + currItemModel = null; + currItemStyleModels = {}; + currLabelModels = {}; + return renderItem && renderItem(defaults({ + dataIndexInside: dataIndexInside, + dataIndex: data.getRawIndex(dataIndexInside), + // Can be used for optimization when zoom or roam. + actionType: payload ? payload.type : null + }, userParams), userAPI); + }; + /** + * @public + * @param dim by default 0. + * @param dataIndexInside by default `currDataIndexInside`. + */ + function value(dim, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + return data.getStore().get(data.getDimensionIndex(dim || 0), dataIndexInside); + } + /** + * @public + * @param dim by default 0. + * @param dataIndexInside by default `currDataIndexInside`. + */ + function ordinalRawValue(dim, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + dim = dim || 0; + var dimInfo = data.getDimensionInfo(dim); + if (!dimInfo) { + var dimIndex = data.getDimensionIndex(dim); + return dimIndex >= 0 ? data.getStore().get(dimIndex, dataIndexInside) : undefined; + } + var val = data.get(dimInfo.name, dataIndexInside); + var ordinalMeta = dimInfo && dimInfo.ordinalMeta; + return ordinalMeta ? ordinalMeta.categories[val] : val; + } + /** + * @deprecated The original intention of `api.style` is enable to set itemStyle + * like other series. But it is not necessary and not easy to give a strict definition + * of what it returns. And since echarts5 it needs to be make compat work. So + * deprecates it since echarts5. + * + * By default, `visual` is applied to style (to support visualMap). + * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`, + * it can be implemented as: + * `api.style({stroke: api.visual('color'), fill: null})`; + * + * [Compat]: since ec5, RectText has been separated from its hosts el. + * so `api.style()` will only return the style from `itemStyle` but not handle `label` + * any more. But `series.label` config is never published in doc. + * We still compat it in `api.style()`. But not encourage to use it and will still not + * to pulish it to doc. + * @public + * @param dataIndexInside by default `currDataIndexInside`. + */ + function style(userProps, dataIndexInside) { + if ("development" !== 'production') { + warnDeprecated('api.style', 'Please write literal style directly instead.'); + } + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + var style = data.getItemVisual(dataIndexInside, 'style'); + var visualColor = style && style.fill; + var opacity = style && style.opacity; + var itemStyle = getItemStyleModel(dataIndexInside, NORMAL).getItemStyle(); + visualColor != null && (itemStyle.fill = visualColor); + opacity != null && (itemStyle.opacity = opacity); + var opt = { + inheritColor: isString(visualColor) ? visualColor : '#000' + }; + var labelModel = getLabelModel(dataIndexInside, NORMAL); + // Now that the feature of "auto adjust text fill/stroke" has been migrated to zrender + // since ec5, we should set `isAttached` as `false` here and make compat in + // `convertToEC4StyleForCustomSerise`. + var textStyle = createTextStyle(labelModel, null, opt, false, true); + textStyle.text = labelModel.getShallow('show') ? retrieve2(customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null; + var textConfig = createTextConfig(labelModel, opt, false); + preFetchFromExtra(userProps, itemStyle); + itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig); + userProps && applyUserPropsAfter(itemStyle, userProps); + itemStyle.legacy = true; + return itemStyle; + } + /** + * @deprecated The reason see `api.style()` + * @public + * @param dataIndexInside by default `currDataIndexInside`. + */ + function styleEmphasis(userProps, dataIndexInside) { + if ("development" !== 'production') { + warnDeprecated('api.styleEmphasis', 'Please write literal style directly instead.'); + } + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + var itemStyle = getItemStyleModel(dataIndexInside, EMPHASIS).getItemStyle(); + var labelModel = getLabelModel(dataIndexInside, EMPHASIS); + var textStyle = createTextStyle(labelModel, null, null, true, true); + textStyle.text = labelModel.getShallow('show') ? retrieve3(customSeries.getFormattedLabel(dataIndexInside, EMPHASIS), customSeries.getFormattedLabel(dataIndexInside, NORMAL), getDefaultLabel(data, dataIndexInside)) : null; + var textConfig = createTextConfig(labelModel, null, true); + preFetchFromExtra(userProps, itemStyle); + itemStyle = convertToEC4StyleForCustomSerise(itemStyle, textStyle, textConfig); + userProps && applyUserPropsAfter(itemStyle, userProps); + itemStyle.legacy = true; + return itemStyle; + } + function applyUserPropsAfter(itemStyle, extra) { + for (var key in extra) { + if (hasOwn(extra, key)) { + itemStyle[key] = extra[key]; + } + } + } + function preFetchFromExtra(extra, itemStyle) { + // A trick to retrieve those props firstly, which are used to + // apply auto inside fill/stroke in `convertToEC4StyleForCustomSerise`. + // (It's not reasonable but only for a degree of compat) + if (extra) { + extra.textFill && (itemStyle.textFill = extra.textFill); + extra.textPosition && (itemStyle.textPosition = extra.textPosition); + } + } + /** + * @public + * @param dataIndexInside by default `currDataIndexInside`. + */ + function visual(visualType, dataIndexInside) { + dataIndexInside == null && (dataIndexInside = currDataIndexInside); + if (hasOwn(STYLE_VISUAL_TYPE, visualType)) { + var style_1 = data.getItemVisual(dataIndexInside, 'style'); + return style_1 ? style_1[STYLE_VISUAL_TYPE[visualType]] : null; + } + // Only support these visuals. Other visual might be inner tricky + // for performance (like `style`), do not expose to users. + if (hasOwn(NON_STYLE_VISUAL_PROPS, visualType)) { + return data.getItemVisual(dataIndexInside, visualType); + } + } + /** + * @public + * @return If not support, return undefined. + */ + function barLayout(opt) { + if (coordSys.type === 'cartesian2d') { + var baseAxis = coordSys.getBaseAxis(); + return getLayoutOnAxis(defaults({ + axis: baseAxis + }, opt)); + } + } + /** + * @public + */ + function currentSeriesIndices() { + return ecModel.getCurrentSeriesIndices(); + } + /** + * @public + * @return font string + */ + function font(opt) { + return getFont(opt, ecModel); + } + } + function wrapEncodeDef(data) { + var encodeDef = {}; + each(data.dimensions, function (dimName) { + var dimInfo = data.getDimensionInfo(dimName); + if (!dimInfo.isExtraCoord) { + var coordDim = dimInfo.coordDim; + var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || []; + dataDims[dimInfo.coordDimIndex] = data.getDimensionIndex(dimName); + } + }); + return encodeDef; + } + function createOrUpdateItem(api, existsEl, dataIndex, elOption, seriesModel, group, data) { + // [Rule] + // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing. + // (It seems that violate the "merge" principle, but most of users probably intuitively + // regard "return;" as "show nothing element whatever", so make a exception to meet the + // most cases.) + // The rule or "merge" see [STRATEGY_MERGE]. + // If `elOption` is `null`/`undefined`/`false` (when `renderItem` returns nothing). + if (!elOption) { + group.remove(existsEl); + return; + } + var el = doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group); + el && data.setItemGraphicEl(dataIndex, el); + el && toggleHoverEmphasis(el, elOption.focus, elOption.blurScope, elOption.emphasisDisabled); + return el; + } + function doCreateOrUpdateEl(api, existsEl, dataIndex, elOption, seriesModel, group) { + if ("development" !== 'production') { + assert(elOption, 'should not have an null/undefined element setting'); + } + var toBeReplacedIdx = -1; + var oldEl = existsEl; + if (existsEl && doesElNeedRecreate(existsEl, elOption, seriesModel) + // || ( + // // PENDING: even in one-to-one mapping case, if el is marked as morph, + // // do not sure whether the el will be mapped to another el with different + // // hierarchy in Group tree. So always recreate el rather than reuse the el. + // morphHelper && morphHelper.isOneToOneFrom(el) + // ) + ) { + // Should keep at the original index, otherwise "merge by index" will be incorrect. + toBeReplacedIdx = indexOf(group.childrenRef(), existsEl); + existsEl = null; + } + var isInit = !existsEl; + var el = existsEl; + if (!el) { + el = createEl(elOption); + if (oldEl) { + copyElement(oldEl, el); + } + } else { + // FIMXE:NEXT unified clearState? + // If in some case the performance issue arised, consider + // do not clearState but update cached normal state directly. + el.clearStates(); + } + // Need to set morph: false explictly to disable automatically morphing. + if (elOption.morph === false) { + el.disableMorphing = true; + } else if (el.disableMorphing) { + el.disableMorphing = false; + } + attachedTxInfoTmp.normal.cfg = attachedTxInfoTmp.normal.conOpt = attachedTxInfoTmp.emphasis.cfg = attachedTxInfoTmp.emphasis.conOpt = attachedTxInfoTmp.blur.cfg = attachedTxInfoTmp.blur.conOpt = attachedTxInfoTmp.select.cfg = attachedTxInfoTmp.select.conOpt = null; + attachedTxInfoTmp.isLegacy = false; + doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfoTmp); + doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit); + updateElNormal(api, el, dataIndex, elOption, attachedTxInfoTmp, seriesModel, isInit); + // `elOption.info` enables user to mount some info on + // elements and use them in event handlers. + // Update them only when user specified, otherwise, remain. + hasOwn(elOption, 'info') && (customInnerStore(el).info = elOption.info); + for (var i = 0; i < STATES.length; i++) { + var stateName = STATES[i]; + if (stateName !== NORMAL) { + var otherStateOpt = retrieveStateOption(elOption, stateName); + var otherStyleOpt = retrieveStyleOptionOnState(elOption, otherStateOpt, stateName); + updateElOnState(stateName, el, otherStateOpt, otherStyleOpt, attachedTxInfoTmp); + } + } + updateZ$1(el, elOption, seriesModel); + if (elOption.type === 'group') { + mergeChildren(api, el, dataIndex, elOption, seriesModel); + } + if (toBeReplacedIdx >= 0) { + group.replaceAt(el, toBeReplacedIdx); + } else { + group.add(el); + } + return el; + } + // `el` must not be null/undefined. + function doesElNeedRecreate(el, elOption, seriesModel) { + var elInner = customInnerStore(el); + var elOptionType = elOption.type; + var elOptionShape = elOption.shape; + var elOptionStyle = elOption.style; + return ( + // Always create new if universal transition is enabled. + // Because we do transition after render. It needs to know what old element is. Replacement will loose it. + seriesModel.isUniversalTransitionEnabled() + // If `elOptionType` is `null`, follow the merge principle. + || elOptionType != null && elOptionType !== elInner.customGraphicType || elOptionType === 'path' && hasOwnPathData(elOptionShape) && getPathData(elOptionShape) !== elInner.customPathData || elOptionType === 'image' && hasOwn(elOptionStyle, 'image') && elOptionStyle.image !== elInner.customImagePath + // // FIXME test and remove this restriction? + // || (elOptionType === 'text' + // && hasOwn(elOptionStyle, 'text') + // && (elOptionStyle as TextStyleProps).text !== elInner.customText + // ) + ); + } + + function doCreateOrUpdateClipPath(el, dataIndex, elOption, seriesModel, isInit) { + // Based on the "merge" principle, if no clipPath provided, + // do nothing. The exists clip will be totally removed only if + // `el.clipPath` is `false`. Otherwise it will be merged/replaced. + var clipPathOpt = elOption.clipPath; + if (clipPathOpt === false) { + if (el && el.getClipPath()) { + el.removeClipPath(); + } + } else if (clipPathOpt) { + var clipPath = el.getClipPath(); + if (clipPath && doesElNeedRecreate(clipPath, clipPathOpt, seriesModel)) { + clipPath = null; + } + if (!clipPath) { + clipPath = createEl(clipPathOpt); + if ("development" !== 'production') { + assert(isPath$1(clipPath), 'Only any type of `path` can be used in `clipPath`, rather than ' + clipPath.type + '.'); + } + el.setClipPath(clipPath); + } + updateElNormal(null, clipPath, dataIndex, clipPathOpt, null, seriesModel, isInit); + } + // If not define `clipPath` in option, do nothing unnecessary. + } + + function doCreateOrUpdateAttachedTx(el, dataIndex, elOption, seriesModel, isInit, attachedTxInfo) { + // Group does not support textContent temporarily until necessary. + if (el.isGroup) { + return; + } + // Normal must be called before emphasis, for `isLegacy` detection. + processTxInfo(elOption, null, attachedTxInfo); + processTxInfo(elOption, EMPHASIS, attachedTxInfo); + // If `elOption.textConfig` or `elOption.textContent` is null/undefined, it does not make sense. + // So for simplicity, if "elOption hasOwnProperty of them but be null/undefined", we do not + // trade them as set to null to el. + // Especially: + // `elOption.textContent: false` means remove textContent. + // `elOption.textContent.emphasis.style: false` means remove the style from emphasis state. + var txConOptNormal = attachedTxInfo.normal.conOpt; + var txConOptEmphasis = attachedTxInfo.emphasis.conOpt; + var txConOptBlur = attachedTxInfo.blur.conOpt; + var txConOptSelect = attachedTxInfo.select.conOpt; + if (txConOptNormal != null || txConOptEmphasis != null || txConOptSelect != null || txConOptBlur != null) { + var textContent = el.getTextContent(); + if (txConOptNormal === false) { + textContent && el.removeTextContent(); + } else { + txConOptNormal = attachedTxInfo.normal.conOpt = txConOptNormal || { + type: 'text' + }; + if (!textContent) { + textContent = createEl(txConOptNormal); + el.setTextContent(textContent); + } else { + // If in some case the performance issue arised, consider + // do not clearState but update cached normal state directly. + textContent.clearStates(); + } + updateElNormal(null, textContent, dataIndex, txConOptNormal, null, seriesModel, isInit); + var txConStlOptNormal = txConOptNormal && txConOptNormal.style; + for (var i = 0; i < STATES.length; i++) { + var stateName = STATES[i]; + if (stateName !== NORMAL) { + var txConOptOtherState = attachedTxInfo[stateName].conOpt; + updateElOnState(stateName, textContent, txConOptOtherState, retrieveStyleOptionOnState(txConOptNormal, txConOptOtherState, stateName), null); + } + } + txConStlOptNormal ? textContent.dirty() : textContent.markRedraw(); + } + } + } + function processTxInfo(elOption, state, attachedTxInfo) { + var stateOpt = !state ? elOption : retrieveStateOption(elOption, state); + var styleOpt = !state ? elOption.style : retrieveStyleOptionOnState(elOption, stateOpt, EMPHASIS); + var elType = elOption.type; + var txCfg = stateOpt ? stateOpt.textConfig : null; + var txConOptNormal = elOption.textContent; + var txConOpt = !txConOptNormal ? null : !state ? txConOptNormal : retrieveStateOption(txConOptNormal, state); + if (styleOpt && ( + // Because emphasis style has little info to detect legacy, + // if normal is legacy, emphasis is trade as legacy. + attachedTxInfo.isLegacy || isEC4CompatibleStyle(styleOpt, elType, !!txCfg, !!txConOpt))) { + attachedTxInfo.isLegacy = true; + var convertResult = convertFromEC4CompatibleStyle(styleOpt, elType, !state); + // Explicitly specified `textConfig` and `textContent` has higher priority than + // the ones generated by legacy style. Otherwise if users use them and `api.style` + // at the same time, they not both work and hardly to known why. + if (!txCfg && convertResult.textConfig) { + txCfg = convertResult.textConfig; + } + if (!txConOpt && convertResult.textContent) { + txConOpt = convertResult.textContent; + } + } + if (!state && txConOpt) { + var txConOptNormal_1 = txConOpt; + // `textContent: {type: 'text'}`, the "type" is easy to be missing. So we tolerate it. + !txConOptNormal_1.type && (txConOptNormal_1.type = 'text'); + if ("development" !== 'production') { + // Do not tolerate incorrcet type for forward compat. + assert(txConOptNormal_1.type === 'text', 'textContent.type must be "text"'); + } + } + var info = !state ? attachedTxInfo.normal : attachedTxInfo[state]; + info.cfg = txCfg; + info.conOpt = txConOpt; + } + function retrieveStateOption(elOption, state) { + return !state ? elOption : elOption ? elOption[state] : null; + } + function retrieveStyleOptionOnState(stateOptionNormal, stateOption, state) { + var style = stateOption && stateOption.style; + if (style == null && state === EMPHASIS && stateOptionNormal) { + style = stateOptionNormal.styleEmphasis; + } + return style; + } + // Usage: + // (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates + // that the existing children will not be removed, and enables the feature + // that update some of the props of some of the children simply by construct + // the returned children of `renderItem` like: + // `var children = group.children = []; children[3] = {opacity: 0.5};` + // (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children + // by child.name. But that might be lower performance. + // (3) If `elOption.$mergeChildren` is `false`, the existing children will be + // replaced totally. + // (4) If `!elOption.children`, following the "merge" principle, nothing will + // happen. + // (5) If `elOption.$mergeChildren` is not `false` neither `'byName'` and the + // `el` is a group, and if any of the new child is null, it means to remove + // the element at the same index, if exists. On the other hand, if the new + // child is and empty object `{}`, it means to keep the element not changed. + // + // For implementation simpleness, do not provide a direct way to remove single + // child (otherwise the total indices of the children array have to be modified). + // User can remove a single child by setting its `ignore` to `true`. + function mergeChildren(api, el, dataIndex, elOption, seriesModel) { + var newChildren = elOption.children; + var newLen = newChildren ? newChildren.length : 0; + var mergeChildren = elOption.$mergeChildren; + // `diffChildrenByName` has been deprecated. + var byName = mergeChildren === 'byName' || elOption.diffChildrenByName; + var notMerge = mergeChildren === false; + // For better performance on roam update, only enter if necessary. + if (!newLen && !byName && !notMerge) { + return; + } + if (byName) { + diffGroupChildren({ + api: api, + oldChildren: el.children() || [], + newChildren: newChildren || [], + dataIndex: dataIndex, + seriesModel: seriesModel, + group: el + }); + return; + } + notMerge && el.removeAll(); + // Mapping children of a group simply by index, which + // might be better performance. + var index = 0; + for (; index < newLen; index++) { + var newChild = newChildren[index]; + var oldChild = el.childAt(index); + if (newChild) { + if (newChild.ignore == null) { + // The old child is set to be ignored if null (see comments + // below). So we need to set ignore to be false back. + newChild.ignore = false; + } + doCreateOrUpdateEl(api, oldChild, dataIndex, newChild, seriesModel, el); + } else { + if ("development" !== 'production') { + assert(oldChild, 'renderItem should not return a group containing elements' + ' as null/undefined/{} if they do not exist before.'); + } + // If the new element option is null, it means to remove the old + // element. But we cannot really remove the element from the group + // directly, because the element order may not be stable when this + // element is added back. So we set the element to be ignored. + oldChild.ignore = true; + } + } + for (var i = el.childCount() - 1; i >= index; i--) { + var child = el.childAt(i); + removeChildFromGroup(el, child, seriesModel); + } + } + function removeChildFromGroup(group, child, seriesModel) { + // Do not support leave elements that are not mentioned in the latest + // `renderItem` return. Otherwise users may not have a clear and simple + // concept that how to control all of the elements. + child && applyLeaveTransition(child, customInnerStore(group).option, seriesModel); + } + function diffGroupChildren(context) { + new DataDiffer(context.oldChildren, context.newChildren, getKey, getKey, context).add(processAddUpdate).update(processAddUpdate).remove(processRemove).execute(); + } + function getKey(item, idx) { + var name = item && item.name; + return name != null ? name : GROUP_DIFF_PREFIX + idx; + } + function processAddUpdate(newIndex, oldIndex) { + var context = this.context; + var childOption = newIndex != null ? context.newChildren[newIndex] : null; + var child = oldIndex != null ? context.oldChildren[oldIndex] : null; + doCreateOrUpdateEl(context.api, child, context.dataIndex, childOption, context.seriesModel, context.group); + } + function processRemove(oldIndex) { + var context = this.context; + var child = context.oldChildren[oldIndex]; + child && applyLeaveTransition(child, customInnerStore(child).option, context.seriesModel); + } + /** + * @return SVG Path data. + */ + function getPathData(shape) { + // "d" follows the SVG convention. + return shape && (shape.pathData || shape.d); + } + function hasOwnPathData(shape) { + return shape && (hasOwn(shape, 'pathData') || hasOwn(shape, 'd')); + } + + function install$r(registers) { + registers.registerChartView(CustomChartView); + registers.registerSeriesModel(CustomSeriesModel); + } + + var inner$a = makeInner(); + var clone$3 = clone; + var bind$1 = bind; + /** + * Base axis pointer class in 2D. + */ + var BaseAxisPointer = /** @class */function () { + function BaseAxisPointer() { + this._dragging = false; + /** + * In px, arbitrary value. Do not set too small, + * no animation is ok for most cases. + */ + this.animationThreshold = 15; + } + /** + * @implement + */ + BaseAxisPointer.prototype.render = function (axisModel, axisPointerModel, api, forceRender) { + var value = axisPointerModel.get('value'); + var status = axisPointerModel.get('status'); + // Bind them to `this`, not in closure, otherwise they will not + // be replaced when user calling setOption in not merge mode. + this._axisModel = axisModel; + this._axisPointerModel = axisPointerModel; + this._api = api; + // Optimize: `render` will be called repeatedly during mouse move. + // So it is power consuming if performing `render` each time, + // especially on mobile device. + if (!forceRender && this._lastValue === value && this._lastStatus === status) { + return; + } + this._lastValue = value; + this._lastStatus = status; + var group = this._group; + var handle = this._handle; + if (!status || status === 'hide') { + // Do not clear here, for animation better. + group && group.hide(); + handle && handle.hide(); + return; + } + group && group.show(); + handle && handle.show(); + // Otherwise status is 'show' + var elOption = {}; + this.makeElOption(elOption, value, axisModel, axisPointerModel, api); + // Enable change axis pointer type. + var graphicKey = elOption.graphicKey; + if (graphicKey !== this._lastGraphicKey) { + this.clear(api); + } + this._lastGraphicKey = graphicKey; + var moveAnimation = this._moveAnimation = this.determineAnimation(axisModel, axisPointerModel); + if (!group) { + group = this._group = new Group(); + this.createPointerEl(group, elOption, axisModel, axisPointerModel); + this.createLabelEl(group, elOption, axisModel, axisPointerModel); + api.getZr().add(group); + } else { + var doUpdateProps = curry(updateProps$1, axisPointerModel, moveAnimation); + this.updatePointerEl(group, elOption, doUpdateProps); + this.updateLabelEl(group, elOption, doUpdateProps, axisPointerModel); + } + updateMandatoryProps(group, axisPointerModel, true); + this._renderHandle(value); + }; + /** + * @implement + */ + BaseAxisPointer.prototype.remove = function (api) { + this.clear(api); + }; + /** + * @implement + */ + BaseAxisPointer.prototype.dispose = function (api) { + this.clear(api); + }; + /** + * @protected + */ + BaseAxisPointer.prototype.determineAnimation = function (axisModel, axisPointerModel) { + var animation = axisPointerModel.get('animation'); + var axis = axisModel.axis; + var isCategoryAxis = axis.type === 'category'; + var useSnap = axisPointerModel.get('snap'); + // Value axis without snap always do not snap. + if (!useSnap && !isCategoryAxis) { + return false; + } + if (animation === 'auto' || animation == null) { + var animationThreshold = this.animationThreshold; + if (isCategoryAxis && axis.getBandWidth() > animationThreshold) { + return true; + } + // It is important to auto animation when snap used. Consider if there is + // a dataZoom, animation will be disabled when too many points exist, while + // it will be enabled for better visual effect when little points exist. + if (useSnap) { + var seriesDataCount = getAxisInfo(axisModel).seriesDataCount; + var axisExtent = axis.getExtent(); + // Approximate band width + return Math.abs(axisExtent[0] - axisExtent[1]) / seriesDataCount > animationThreshold; + } + return false; + } + return animation === true; + }; + /** + * add {pointer, label, graphicKey} to elOption + * @protected + */ + BaseAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) { + // Should be implemenented by sub-class. + }; + /** + * @protected + */ + BaseAxisPointer.prototype.createPointerEl = function (group, elOption, axisModel, axisPointerModel) { + var pointerOption = elOption.pointer; + if (pointerOption) { + var pointerEl = inner$a(group).pointerEl = new graphic[pointerOption.type](clone$3(elOption.pointer)); + group.add(pointerEl); + } + }; + /** + * @protected + */ + BaseAxisPointer.prototype.createLabelEl = function (group, elOption, axisModel, axisPointerModel) { + if (elOption.label) { + var labelEl = inner$a(group).labelEl = new ZRText(clone$3(elOption.label)); + group.add(labelEl); + updateLabelShowHide(labelEl, axisPointerModel); + } + }; + /** + * @protected + */ + BaseAxisPointer.prototype.updatePointerEl = function (group, elOption, updateProps) { + var pointerEl = inner$a(group).pointerEl; + if (pointerEl && elOption.pointer) { + pointerEl.setStyle(elOption.pointer.style); + updateProps(pointerEl, { + shape: elOption.pointer.shape + }); + } + }; + /** + * @protected + */ + BaseAxisPointer.prototype.updateLabelEl = function (group, elOption, updateProps, axisPointerModel) { + var labelEl = inner$a(group).labelEl; + if (labelEl) { + labelEl.setStyle(elOption.label.style); + updateProps(labelEl, { + // Consider text length change in vertical axis, animation should + // be used on shape, otherwise the effect will be weird. + // TODOTODO + // shape: elOption.label.shape, + x: elOption.label.x, + y: elOption.label.y + }); + updateLabelShowHide(labelEl, axisPointerModel); + } + }; + /** + * @private + */ + BaseAxisPointer.prototype._renderHandle = function (value) { + if (this._dragging || !this.updateHandleTransform) { + return; + } + var axisPointerModel = this._axisPointerModel; + var zr = this._api.getZr(); + var handle = this._handle; + var handleModel = axisPointerModel.getModel('handle'); + var status = axisPointerModel.get('status'); + if (!handleModel.get('show') || !status || status === 'hide') { + handle && zr.remove(handle); + this._handle = null; + return; + } + var isInit; + if (!this._handle) { + isInit = true; + handle = this._handle = createIcon(handleModel.get('icon'), { + cursor: 'move', + draggable: true, + onmousemove: function (e) { + // For mobile device, prevent screen slider on the button. + stop(e.event); + }, + onmousedown: bind$1(this._onHandleDragMove, this, 0, 0), + drift: bind$1(this._onHandleDragMove, this), + ondragend: bind$1(this._onHandleDragEnd, this) + }); + zr.add(handle); + } + updateMandatoryProps(handle, axisPointerModel, false); + // update style + handle.setStyle(handleModel.getItemStyle(null, ['color', 'borderColor', 'borderWidth', 'opacity', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'])); + // update position + var handleSize = handleModel.get('size'); + if (!isArray(handleSize)) { + handleSize = [handleSize, handleSize]; + } + handle.scaleX = handleSize[0] / 2; + handle.scaleY = handleSize[1] / 2; + createOrUpdate(this, '_doDispatchAxisPointer', handleModel.get('throttle') || 0, 'fixRate'); + this._moveHandleToValue(value, isInit); + }; + BaseAxisPointer.prototype._moveHandleToValue = function (value, isInit) { + updateProps$1(this._axisPointerModel, !isInit && this._moveAnimation, this._handle, getHandleTransProps(this.getHandleTransform(value, this._axisModel, this._axisPointerModel))); + }; + BaseAxisPointer.prototype._onHandleDragMove = function (dx, dy) { + var handle = this._handle; + if (!handle) { + return; + } + this._dragging = true; + // Persistent for throttle. + var trans = this.updateHandleTransform(getHandleTransProps(handle), [dx, dy], this._axisModel, this._axisPointerModel); + this._payloadInfo = trans; + handle.stopAnimation(); + handle.attr(getHandleTransProps(trans)); + inner$a(handle).lastProp = null; + this._doDispatchAxisPointer(); + }; + /** + * Throttled method. + */ + BaseAxisPointer.prototype._doDispatchAxisPointer = function () { + var handle = this._handle; + if (!handle) { + return; + } + var payloadInfo = this._payloadInfo; + var axisModel = this._axisModel; + this._api.dispatchAction({ + type: 'updateAxisPointer', + x: payloadInfo.cursorPoint[0], + y: payloadInfo.cursorPoint[1], + tooltipOption: payloadInfo.tooltipOption, + axesInfo: [{ + axisDim: axisModel.axis.dim, + axisIndex: axisModel.componentIndex + }] + }); + }; + BaseAxisPointer.prototype._onHandleDragEnd = function () { + this._dragging = false; + var handle = this._handle; + if (!handle) { + return; + } + var value = this._axisPointerModel.get('value'); + // Consider snap or categroy axis, handle may be not consistent with + // axisPointer. So move handle to align the exact value position when + // drag ended. + this._moveHandleToValue(value); + // For the effect: tooltip will be shown when finger holding on handle + // button, and will be hidden after finger left handle button. + this._api.dispatchAction({ + type: 'hideTip' + }); + }; + /** + * @private + */ + BaseAxisPointer.prototype.clear = function (api) { + this._lastValue = null; + this._lastStatus = null; + var zr = api.getZr(); + var group = this._group; + var handle = this._handle; + if (zr && group) { + this._lastGraphicKey = null; + group && zr.remove(group); + handle && zr.remove(handle); + this._group = null; + this._handle = null; + this._payloadInfo = null; + } + clear(this, '_doDispatchAxisPointer'); + }; + /** + * @protected + */ + BaseAxisPointer.prototype.doClear = function () { + // Implemented by sub-class if necessary. + }; + BaseAxisPointer.prototype.buildLabel = function (xy, wh, xDimIndex) { + xDimIndex = xDimIndex || 0; + return { + x: xy[xDimIndex], + y: xy[1 - xDimIndex], + width: wh[xDimIndex], + height: wh[1 - xDimIndex] + }; + }; + return BaseAxisPointer; + }(); + function updateProps$1(animationModel, moveAnimation, el, props) { + // Animation optimize. + if (!propsEqual(inner$a(el).lastProp, props)) { + inner$a(el).lastProp = props; + moveAnimation ? updateProps(el, props, animationModel) : (el.stopAnimation(), el.attr(props)); + } + } + function propsEqual(lastProps, newProps) { + if (isObject(lastProps) && isObject(newProps)) { + var equals_1 = true; + each(newProps, function (item, key) { + equals_1 = equals_1 && propsEqual(lastProps[key], item); + }); + return !!equals_1; + } else { + return lastProps === newProps; + } + } + function updateLabelShowHide(labelEl, axisPointerModel) { + labelEl[axisPointerModel.get(['label', 'show']) ? 'show' : 'hide'](); + } + function getHandleTransProps(trans) { + return { + x: trans.x || 0, + y: trans.y || 0, + rotation: trans.rotation || 0 + }; + } + function updateMandatoryProps(group, axisPointerModel, silent) { + var z = axisPointerModel.get('z'); + var zlevel = axisPointerModel.get('zlevel'); + group && group.traverse(function (el) { + if (el.type !== 'group') { + z != null && (el.z = z); + zlevel != null && (el.zlevel = zlevel); + el.silent = silent; + } + }); + } + + function buildElStyle(axisPointerModel) { + var axisPointerType = axisPointerModel.get('type'); + var styleModel = axisPointerModel.getModel(axisPointerType + 'Style'); + var style; + if (axisPointerType === 'line') { + style = styleModel.getLineStyle(); + style.fill = null; + } else if (axisPointerType === 'shadow') { + style = styleModel.getAreaStyle(); + style.stroke = null; + } + return style; + } + /** + * @param {Function} labelPos {align, verticalAlign, position} + */ + function buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos) { + var value = axisPointerModel.get('value'); + var text = getValueLabel(value, axisModel.axis, axisModel.ecModel, axisPointerModel.get('seriesDataIndices'), { + precision: axisPointerModel.get(['label', 'precision']), + formatter: axisPointerModel.get(['label', 'formatter']) + }); + var labelModel = axisPointerModel.getModel('label'); + var paddings = normalizeCssArray$1(labelModel.get('padding') || 0); + var font = labelModel.getFont(); + var textRect = getBoundingRect(text, font); + var position = labelPos.position; + var width = textRect.width + paddings[1] + paddings[3]; + var height = textRect.height + paddings[0] + paddings[2]; + // Adjust by align. + var align = labelPos.align; + align === 'right' && (position[0] -= width); + align === 'center' && (position[0] -= width / 2); + var verticalAlign = labelPos.verticalAlign; + verticalAlign === 'bottom' && (position[1] -= height); + verticalAlign === 'middle' && (position[1] -= height / 2); + // Not overflow ec container + confineInContainer(position, width, height, api); + var bgColor = labelModel.get('backgroundColor'); + if (!bgColor || bgColor === 'auto') { + bgColor = axisModel.get(['axisLine', 'lineStyle', 'color']); + } + elOption.label = { + // shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')}, + x: position[0], + y: position[1], + style: createTextStyle(labelModel, { + text: text, + font: font, + fill: labelModel.getTextColor(), + padding: paddings, + backgroundColor: bgColor + }), + // Label should be over axisPointer. + z2: 10 + }; + } + // Do not overflow ec container + function confineInContainer(position, width, height, api) { + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + position[0] = Math.min(position[0] + width, viewWidth) - width; + position[1] = Math.min(position[1] + height, viewHeight) - height; + position[0] = Math.max(position[0], 0); + position[1] = Math.max(position[1], 0); + } + function getValueLabel(value, axis, ecModel, seriesDataIndices, opt) { + value = axis.scale.parse(value); + var text = axis.scale.getLabel({ + value: value + }, { + // If `precision` is set, width can be fixed (like '12.00500'), which + // helps to debounce when when moving label. + precision: opt.precision + }); + var formatter = opt.formatter; + if (formatter) { + var params_1 = { + value: getAxisRawValue(axis, { + value: value + }), + axisDimension: axis.dim, + axisIndex: axis.index, + seriesData: [] + }; + each(seriesDataIndices, function (idxItem) { + var series = ecModel.getSeriesByIndex(idxItem.seriesIndex); + var dataIndex = idxItem.dataIndexInside; + var dataParams = series && series.getDataParams(dataIndex); + dataParams && params_1.seriesData.push(dataParams); + }); + if (isString(formatter)) { + text = formatter.replace('{value}', text); + } else if (isFunction(formatter)) { + text = formatter(params_1); + } + } + return text; + } + function getTransformedPosition(axis, value, layoutInfo) { + var transform = create$1(); + rotate(transform, transform, layoutInfo.rotation); + translate(transform, transform, layoutInfo.position); + return applyTransform$1([axis.dataToCoord(value), (layoutInfo.labelOffset || 0) + (layoutInfo.labelDirection || 1) * (layoutInfo.labelMargin || 0)], transform); + } + function buildCartesianSingleLabelElOption(value, elOption, layoutInfo, axisModel, axisPointerModel, api) { + // @ts-ignore + var textLayout = AxisBuilder.innerTextLayout(layoutInfo.rotation, 0, layoutInfo.labelDirection); + layoutInfo.labelMargin = axisPointerModel.get(['label', 'margin']); + buildLabelElOption(elOption, axisModel, axisPointerModel, api, { + position: getTransformedPosition(axisModel.axis, value, layoutInfo), + align: textLayout.textAlign, + verticalAlign: textLayout.textVerticalAlign + }); + } + function makeLineShape(p1, p2, xDimIndex) { + xDimIndex = xDimIndex || 0; + return { + x1: p1[xDimIndex], + y1: p1[1 - xDimIndex], + x2: p2[xDimIndex], + y2: p2[1 - xDimIndex] + }; + } + function makeRectShape(xy, wh, xDimIndex) { + xDimIndex = xDimIndex || 0; + return { + x: xy[xDimIndex], + y: xy[1 - xDimIndex], + width: wh[xDimIndex], + height: wh[1 - xDimIndex] + }; + } + function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) { + return { + cx: cx, + cy: cy, + r0: r0, + r: r, + startAngle: startAngle, + endAngle: endAngle, + clockwise: true + }; + } + + var CartesianAxisPointer = /** @class */function (_super) { + __extends(CartesianAxisPointer, _super); + function CartesianAxisPointer() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * @override + */ + CartesianAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) { + var axis = axisModel.axis; + var grid = axis.grid; + var axisPointerType = axisPointerModel.get('type'); + var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent(); + var pixelValue = axis.toGlobalCoord(axis.dataToCoord(value, true)); + if (axisPointerType && axisPointerType !== 'none') { + var elStyle = buildElStyle(axisPointerModel); + var pointerOption = pointerShapeBuilder[axisPointerType](axis, pixelValue, otherExtent); + pointerOption.style = elStyle; + elOption.graphicKey = pointerOption.type; + elOption.pointer = pointerOption; + } + var layoutInfo = layout$1(grid.model, axisModel); + buildCartesianSingleLabelElOption( + // @ts-ignore + value, elOption, layoutInfo, axisModel, axisPointerModel, api); + }; + /** + * @override + */ + CartesianAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) { + var layoutInfo = layout$1(axisModel.axis.grid.model, axisModel, { + labelInside: false + }); + // @ts-ignore + layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']); + var pos = getTransformedPosition(axisModel.axis, value, layoutInfo); + return { + x: pos[0], + y: pos[1], + rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0) + }; + }; + /** + * @override + */ + CartesianAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) { + var axis = axisModel.axis; + var grid = axis.grid; + var axisExtent = axis.getGlobalExtent(true); + var otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent(); + var dimIndex = axis.dim === 'x' ? 0 : 1; + var currPosition = [transform.x, transform.y]; + currPosition[dimIndex] += delta[dimIndex]; + currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]); + currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]); + var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2; + var cursorPoint = [cursorOtherValue, cursorOtherValue]; + cursorPoint[dimIndex] = currPosition[dimIndex]; + // Make tooltip do not overlap axisPointer and in the middle of the grid. + var tooltipOptions = [{ + verticalAlign: 'middle' + }, { + align: 'center' + }]; + return { + x: currPosition[0], + y: currPosition[1], + rotation: transform.rotation, + cursorPoint: cursorPoint, + tooltipOption: tooltipOptions[dimIndex] + }; + }; + return CartesianAxisPointer; + }(BaseAxisPointer); + function getCartesian(grid, axis) { + var opt = {}; + opt[axis.dim + 'AxisIndex'] = axis.index; + return grid.getCartesian(opt); + } + var pointerShapeBuilder = { + line: function (axis, pixelValue, otherExtent) { + var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getAxisDimIndex(axis)); + return { + type: 'Line', + subPixelOptimize: true, + shape: targetShape + }; + }, + shadow: function (axis, pixelValue, otherExtent) { + var bandWidth = Math.max(1, axis.getBandWidth()); + var span = otherExtent[1] - otherExtent[0]; + return { + type: 'Rect', + shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getAxisDimIndex(axis)) + }; + } + }; + function getAxisDimIndex(axis) { + return axis.dim === 'x' ? 0 : 1; + } + + var AxisPointerModel = /** @class */function (_super) { + __extends(AxisPointerModel, _super); + function AxisPointerModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = AxisPointerModel.type; + return _this; + } + AxisPointerModel.type = 'axisPointer'; + AxisPointerModel.defaultOption = { + // 'auto' means that show when triggered by tooltip or handle. + show: 'auto', + // zlevel: 0, + z: 50, + type: 'line', + // axispointer triggered by tootip determine snap automatically, + // see `modelHelper`. + snap: false, + triggerTooltip: true, + triggerEmphasis: true, + value: null, + status: null, + link: [], + // Do not set 'auto' here, otherwise global animation: false + // will not effect at this axispointer. + animation: null, + animationDurationUpdate: 200, + lineStyle: { + color: '#B9BEC9', + width: 1, + type: 'dashed' + }, + shadowStyle: { + color: 'rgba(210,219,238,0.2)' + }, + label: { + show: true, + formatter: null, + precision: 'auto', + margin: 3, + color: '#fff', + padding: [5, 7, 5, 7], + backgroundColor: 'auto', + borderColor: null, + borderWidth: 0, + borderRadius: 3 + }, + handle: { + show: false, + // eslint-disable-next-line + icon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z', + size: 45, + // handle margin is from symbol center to axis, which is stable when circular move. + margin: 50, + // color: '#1b8bbd' + // color: '#2f4554' + color: '#333', + shadowBlur: 3, + shadowColor: '#aaa', + shadowOffsetX: 0, + shadowOffsetY: 2, + // For mobile performance + throttle: 40 + } + }; + return AxisPointerModel; + }(ComponentModel); + + var inner$b = makeInner(); + var each$7 = each; + /** + * @param {string} key + * @param {module:echarts/ExtensionAPI} api + * @param {Function} handler + * param: {string} currTrigger + * param: {Array.} point + */ + function register(key, api, handler) { + if (env.node) { + return; + } + var zr = api.getZr(); + inner$b(zr).records || (inner$b(zr).records = {}); + initGlobalListeners(zr, api); + var record = inner$b(zr).records[key] || (inner$b(zr).records[key] = {}); + record.handler = handler; + } + function initGlobalListeners(zr, api) { + if (inner$b(zr).initialized) { + return; + } + inner$b(zr).initialized = true; + useHandler('click', curry(doEnter, 'click')); + useHandler('mousemove', curry(doEnter, 'mousemove')); + // useHandler('mouseout', onLeave); + useHandler('globalout', onLeave); + function useHandler(eventType, cb) { + zr.on(eventType, function (e) { + var dis = makeDispatchAction(api); + each$7(inner$b(zr).records, function (record) { + record && cb(record, e, dis.dispatchAction); + }); + dispatchTooltipFinally(dis.pendings, api); + }); + } + } + function dispatchTooltipFinally(pendings, api) { + var showLen = pendings.showTip.length; + var hideLen = pendings.hideTip.length; + var actuallyPayload; + if (showLen) { + actuallyPayload = pendings.showTip[showLen - 1]; + } else if (hideLen) { + actuallyPayload = pendings.hideTip[hideLen - 1]; + } + if (actuallyPayload) { + actuallyPayload.dispatchAction = null; + api.dispatchAction(actuallyPayload); + } + } + function onLeave(record, e, dispatchAction) { + record.handler('leave', null, dispatchAction); + } + function doEnter(currTrigger, record, e, dispatchAction) { + record.handler(currTrigger, e, dispatchAction); + } + function makeDispatchAction(api) { + var pendings = { + showTip: [], + hideTip: [] + }; + // FIXME + // better approach? + // 'showTip' and 'hideTip' can be triggered by axisPointer and tooltip, + // which may be conflict, (axisPointer call showTip but tooltip call hideTip); + // So we have to add "final stage" to merge those dispatched actions. + var dispatchAction = function (payload) { + var pendingList = pendings[payload.type]; + if (pendingList) { + pendingList.push(payload); + } else { + payload.dispatchAction = dispatchAction; + api.dispatchAction(payload); + } + }; + return { + dispatchAction: dispatchAction, + pendings: pendings + }; + } + function unregister(key, api) { + if (env.node) { + return; + } + var zr = api.getZr(); + var record = (inner$b(zr).records || {})[key]; + if (record) { + inner$b(zr).records[key] = null; + } + } + + var AxisPointerView = /** @class */function (_super) { + __extends(AxisPointerView, _super); + function AxisPointerView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = AxisPointerView.type; + return _this; + } + AxisPointerView.prototype.render = function (globalAxisPointerModel, ecModel, api) { + var globalTooltipModel = ecModel.getComponent('tooltip'); + var triggerOn = globalAxisPointerModel.get('triggerOn') || globalTooltipModel && globalTooltipModel.get('triggerOn') || 'mousemove|click'; + // Register global listener in AxisPointerView to enable + // AxisPointerView to be independent to Tooltip. + register('axisPointer', api, function (currTrigger, e, dispatchAction) { + // If 'none', it is not controlled by mouse totally. + if (triggerOn !== 'none' && (currTrigger === 'leave' || triggerOn.indexOf(currTrigger) >= 0)) { + dispatchAction({ + type: 'updateAxisPointer', + currTrigger: currTrigger, + x: e && e.offsetX, + y: e && e.offsetY + }); + } + }); + }; + AxisPointerView.prototype.remove = function (ecModel, api) { + unregister('axisPointer', api); + }; + AxisPointerView.prototype.dispose = function (ecModel, api) { + unregister('axisPointer', api); + }; + AxisPointerView.type = 'axisPointer'; + return AxisPointerView; + }(ComponentView); + + /** + * @param finder contains {seriesIndex, dataIndex, dataIndexInside} + * @param ecModel + * @return {point: [x, y], el: ...} point Will not be null. + */ + function findPointFromSeries(finder, ecModel) { + var point = []; + var seriesIndex = finder.seriesIndex; + var seriesModel; + if (seriesIndex == null || !(seriesModel = ecModel.getSeriesByIndex(seriesIndex))) { + return { + point: [] + }; + } + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, finder); + if (dataIndex == null || dataIndex < 0 || isArray(dataIndex)) { + return { + point: [] + }; + } + var el = data.getItemGraphicEl(dataIndex); + var coordSys = seriesModel.coordinateSystem; + if (seriesModel.getTooltipPosition) { + point = seriesModel.getTooltipPosition(dataIndex) || []; + } else if (coordSys && coordSys.dataToPoint) { + if (finder.isStacked) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueAxisDim = valueAxis.dim; + var baseAxisDim = baseAxis.dim; + var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0; + var baseDim = data.mapDimension(baseAxisDim); + var stackedData = []; + stackedData[baseDataOffset] = data.get(baseDim, dataIndex); + stackedData[1 - baseDataOffset] = data.get(data.getCalculationInfo('stackResultDimension'), dataIndex); + point = coordSys.dataToPoint(stackedData) || []; + } else { + point = coordSys.dataToPoint(data.getValues(map(coordSys.dimensions, function (dim) { + return data.mapDimension(dim); + }), dataIndex)) || []; + } + } else if (el) { + // Use graphic bounding rect + var rect = el.getBoundingRect().clone(); + rect.applyTransform(el.transform); + point = [rect.x + rect.width / 2, rect.y + rect.height / 2]; + } + return { + point: point, + el: el + }; + } + + var inner$c = makeInner(); + /** + * Basic logic: check all axis, if they do not demand show/highlight, + * then hide/downplay them. + * + * @return content of event obj for echarts.connect. + */ + function axisTrigger(payload, ecModel, api) { + var currTrigger = payload.currTrigger; + var point = [payload.x, payload.y]; + var finder = payload; + var dispatchAction = payload.dispatchAction || bind(api.dispatchAction, api); + var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; + // Pending + // See #6121. But we are not able to reproduce it yet. + if (!coordSysAxesInfo) { + return; + } + if (illegalPoint(point)) { + // Used in the default behavior of `connection`: use the sample seriesIndex + // and dataIndex. And also used in the tooltipView trigger. + point = findPointFromSeries({ + seriesIndex: finder.seriesIndex, + // Do not use dataIndexInside from other ec instance. + // FIXME: auto detect it? + dataIndex: finder.dataIndex + }, ecModel).point; + } + var isIllegalPoint = illegalPoint(point); + // Axis and value can be specified when calling dispatchAction({type: 'updateAxisPointer'}). + // Notice: In this case, it is difficult to get the `point` (which is necessary to show + // tooltip, so if point is not given, we just use the point found by sample seriesIndex + // and dataIndex. + var inputAxesInfo = finder.axesInfo; + var axesInfo = coordSysAxesInfo.axesInfo; + var shouldHide = currTrigger === 'leave' || illegalPoint(point); + var outputPayload = {}; + var showValueMap = {}; + var dataByCoordSys = { + list: [], + map: {} + }; + var updaters = { + showPointer: curry(showPointer, showValueMap), + showTooltip: curry(showTooltip, dataByCoordSys) + }; + // Process for triggered axes. + each(coordSysAxesInfo.coordSysMap, function (coordSys, coordSysKey) { + // If a point given, it must be contained by the coordinate system. + var coordSysContainsPoint = isIllegalPoint || coordSys.containPoint(point); + each(coordSysAxesInfo.coordSysAxesInfo[coordSysKey], function (axisInfo, key) { + var axis = axisInfo.axis; + var inputAxisInfo = findInputAxisInfo(inputAxesInfo, axisInfo); + // If no inputAxesInfo, no axis is restricted. + if (!shouldHide && coordSysContainsPoint && (!inputAxesInfo || inputAxisInfo)) { + var val = inputAxisInfo && inputAxisInfo.value; + if (val == null && !isIllegalPoint) { + val = axis.pointToData(point); + } + val != null && processOnAxis(axisInfo, val, updaters, false, outputPayload); + } + }); + }); + // Process for linked axes. + var linkTriggers = {}; + each(axesInfo, function (tarAxisInfo, tarKey) { + var linkGroup = tarAxisInfo.linkGroup; + // If axis has been triggered in the previous stage, it should not be triggered by link. + if (linkGroup && !showValueMap[tarKey]) { + each(linkGroup.axesInfo, function (srcAxisInfo, srcKey) { + var srcValItem = showValueMap[srcKey]; + // If srcValItem exist, source axis is triggered, so link to target axis. + if (srcAxisInfo !== tarAxisInfo && srcValItem) { + var val = srcValItem.value; + linkGroup.mapper && (val = tarAxisInfo.axis.scale.parse(linkGroup.mapper(val, makeMapperParam(srcAxisInfo), makeMapperParam(tarAxisInfo)))); + linkTriggers[tarAxisInfo.key] = val; + } + }); + } + }); + each(linkTriggers, function (val, tarKey) { + processOnAxis(axesInfo[tarKey], val, updaters, true, outputPayload); + }); + updateModelActually(showValueMap, axesInfo, outputPayload); + dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction); + dispatchHighDownActually(axesInfo, dispatchAction, api); + return outputPayload; + } + function processOnAxis(axisInfo, newValue, updaters, noSnap, outputFinder) { + var axis = axisInfo.axis; + if (axis.scale.isBlank() || !axis.containData(newValue)) { + return; + } + if (!axisInfo.involveSeries) { + updaters.showPointer(axisInfo, newValue); + return; + } + // Heavy calculation. So put it after axis.containData checking. + var payloadInfo = buildPayloadsBySeries(newValue, axisInfo); + var payloadBatch = payloadInfo.payloadBatch; + var snapToValue = payloadInfo.snapToValue; + // Fill content of event obj for echarts.connect. + // By default use the first involved series data as a sample to connect. + if (payloadBatch[0] && outputFinder.seriesIndex == null) { + extend(outputFinder, payloadBatch[0]); + } + // If no linkSource input, this process is for collecting link + // target, where snap should not be accepted. + if (!noSnap && axisInfo.snap) { + if (axis.containData(snapToValue) && snapToValue != null) { + newValue = snapToValue; + } + } + updaters.showPointer(axisInfo, newValue, payloadBatch); + // Tooltip should always be snapToValue, otherwise there will be + // incorrect "axis value ~ series value" mapping displayed in tooltip. + updaters.showTooltip(axisInfo, payloadInfo, snapToValue); + } + function buildPayloadsBySeries(value, axisInfo) { + var axis = axisInfo.axis; + var dim = axis.dim; + var snapToValue = value; + var payloadBatch = []; + var minDist = Number.MAX_VALUE; + var minDiff = -1; + each(axisInfo.seriesModels, function (series, idx) { + var dataDim = series.getData().mapDimensionsAll(dim); + var seriesNestestValue; + var dataIndices; + if (series.getAxisTooltipData) { + var result = series.getAxisTooltipData(dataDim, value, axis); + dataIndices = result.dataIndices; + seriesNestestValue = result.nestestValue; + } else { + dataIndices = series.getData().indicesOfNearest(dataDim[0], value, + // Add a threshold to avoid find the wrong dataIndex + // when data length is not same. + // false, + axis.type === 'category' ? 0.5 : null); + if (!dataIndices.length) { + return; + } + seriesNestestValue = series.getData().get(dataDim[0], dataIndices[0]); + } + if (seriesNestestValue == null || !isFinite(seriesNestestValue)) { + return; + } + var diff = value - seriesNestestValue; + var dist = Math.abs(diff); + // Consider category case + if (dist <= minDist) { + if (dist < minDist || diff >= 0 && minDiff < 0) { + minDist = dist; + minDiff = diff; + snapToValue = seriesNestestValue; + payloadBatch.length = 0; + } + each(dataIndices, function (dataIndex) { + payloadBatch.push({ + seriesIndex: series.seriesIndex, + dataIndexInside: dataIndex, + dataIndex: series.getData().getRawIndex(dataIndex) + }); + }); + } + }); + return { + payloadBatch: payloadBatch, + snapToValue: snapToValue + }; + } + function showPointer(showValueMap, axisInfo, value, payloadBatch) { + showValueMap[axisInfo.key] = { + value: value, + payloadBatch: payloadBatch + }; + } + function showTooltip(dataByCoordSys, axisInfo, payloadInfo, value) { + var payloadBatch = payloadInfo.payloadBatch; + var axis = axisInfo.axis; + var axisModel = axis.model; + var axisPointerModel = axisInfo.axisPointerModel; + // If no data, do not create anything in dataByCoordSys, + // whose length will be used to judge whether dispatch action. + if (!axisInfo.triggerTooltip || !payloadBatch.length) { + return; + } + var coordSysModel = axisInfo.coordSys.model; + var coordSysKey = makeKey(coordSysModel); + var coordSysItem = dataByCoordSys.map[coordSysKey]; + if (!coordSysItem) { + coordSysItem = dataByCoordSys.map[coordSysKey] = { + coordSysId: coordSysModel.id, + coordSysIndex: coordSysModel.componentIndex, + coordSysType: coordSysModel.type, + coordSysMainType: coordSysModel.mainType, + dataByAxis: [] + }; + dataByCoordSys.list.push(coordSysItem); + } + coordSysItem.dataByAxis.push({ + axisDim: axis.dim, + axisIndex: axisModel.componentIndex, + axisType: axisModel.type, + axisId: axisModel.id, + value: value, + // Caustion: viewHelper.getValueLabel is actually on "view stage", which + // depends that all models have been updated. So it should not be performed + // here. Considering axisPointerModel used here is volatile, which is hard + // to be retrieve in TooltipView, we prepare parameters here. + valueLabelOpt: { + precision: axisPointerModel.get(['label', 'precision']), + formatter: axisPointerModel.get(['label', 'formatter']) + }, + seriesDataIndices: payloadBatch.slice() + }); + } + function updateModelActually(showValueMap, axesInfo, outputPayload) { + var outputAxesInfo = outputPayload.axesInfo = []; + // Basic logic: If no 'show' required, 'hide' this axisPointer. + each(axesInfo, function (axisInfo, key) { + var option = axisInfo.axisPointerModel.option; + var valItem = showValueMap[key]; + if (valItem) { + !axisInfo.useHandle && (option.status = 'show'); + option.value = valItem.value; + // For label formatter param and highlight. + option.seriesDataIndices = (valItem.payloadBatch || []).slice(); + } + // When always show (e.g., handle used), remain + // original value and status. + else { + // If hide, value still need to be set, consider + // click legend to toggle axis blank. + !axisInfo.useHandle && (option.status = 'hide'); + } + // If status is 'hide', should be no info in payload. + option.status === 'show' && outputAxesInfo.push({ + axisDim: axisInfo.axis.dim, + axisIndex: axisInfo.axis.model.componentIndex, + value: option.value + }); + }); + } + function dispatchTooltipActually(dataByCoordSys, point, payload, dispatchAction) { + // Basic logic: If no showTip required, hideTip will be dispatched. + if (illegalPoint(point) || !dataByCoordSys.list.length) { + dispatchAction({ + type: 'hideTip' + }); + return; + } + // In most case only one axis (or event one series is used). It is + // convenient to fetch payload.seriesIndex and payload.dataIndex + // directly. So put the first seriesIndex and dataIndex of the first + // axis on the payload. + var sampleItem = ((dataByCoordSys.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {}; + dispatchAction({ + type: 'showTip', + escapeConnect: true, + x: point[0], + y: point[1], + tooltipOption: payload.tooltipOption, + position: payload.position, + dataIndexInside: sampleItem.dataIndexInside, + dataIndex: sampleItem.dataIndex, + seriesIndex: sampleItem.seriesIndex, + dataByCoordSys: dataByCoordSys.list + }); + } + function dispatchHighDownActually(axesInfo, dispatchAction, api) { + // FIXME + // highlight status modification should be a stage of main process? + // (Consider confilct (e.g., legend and axisPointer) and setOption) + var zr = api.getZr(); + var highDownKey = 'axisPointerLastHighlights'; + var lastHighlights = inner$c(zr)[highDownKey] || {}; + var newHighlights = inner$c(zr)[highDownKey] = {}; + // Update highlight/downplay status according to axisPointer model. + // Build hash map and remove duplicate incidentally. + each(axesInfo, function (axisInfo, key) { + var option = axisInfo.axisPointerModel.option; + option.status === 'show' && axisInfo.triggerEmphasis && each(option.seriesDataIndices, function (batchItem) { + var key = batchItem.seriesIndex + ' | ' + batchItem.dataIndex; + newHighlights[key] = batchItem; + }); + }); + // Diff. + var toHighlight = []; + var toDownplay = []; + each(lastHighlights, function (batchItem, key) { + !newHighlights[key] && toDownplay.push(batchItem); + }); + each(newHighlights, function (batchItem, key) { + !lastHighlights[key] && toHighlight.push(batchItem); + }); + toDownplay.length && api.dispatchAction({ + type: 'downplay', + escapeConnect: true, + // Not blur others when highlight in axisPointer. + notBlur: true, + batch: toDownplay + }); + toHighlight.length && api.dispatchAction({ + type: 'highlight', + escapeConnect: true, + // Not blur others when highlight in axisPointer. + notBlur: true, + batch: toHighlight + }); + } + function findInputAxisInfo(inputAxesInfo, axisInfo) { + for (var i = 0; i < (inputAxesInfo || []).length; i++) { + var inputAxisInfo = inputAxesInfo[i]; + if (axisInfo.axis.dim === inputAxisInfo.axisDim && axisInfo.axis.model.componentIndex === inputAxisInfo.axisIndex) { + return inputAxisInfo; + } + } + } + function makeMapperParam(axisInfo) { + var axisModel = axisInfo.axis.model; + var item = {}; + var dim = item.axisDim = axisInfo.axis.dim; + item.axisIndex = item[dim + 'AxisIndex'] = axisModel.componentIndex; + item.axisName = item[dim + 'AxisName'] = axisModel.name; + item.axisId = item[dim + 'AxisId'] = axisModel.id; + return item; + } + function illegalPoint(point) { + return !point || point[0] == null || isNaN(point[0]) || point[1] == null || isNaN(point[1]); + } + + function install$s(registers) { + // CartesianAxisPointer is not supposed to be required here. But consider + // echarts.simple.js and online build tooltip, which only require gridSimple, + // CartesianAxisPointer should be able to required somewhere. + AxisView.registerAxisPointerClass('CartesianAxisPointer', CartesianAxisPointer); + registers.registerComponentModel(AxisPointerModel); + registers.registerComponentView(AxisPointerView); + registers.registerPreprocessor(function (option) { + // Always has a global axisPointerModel for default setting. + if (option) { + (!option.axisPointer || option.axisPointer.length === 0) && (option.axisPointer = {}); + var link = option.axisPointer.link; + // Normalize to array to avoid object mergin. But if link + // is not set, remain null/undefined, otherwise it will + // override existent link setting. + if (link && !isArray(link)) { + option.axisPointer.link = [link]; + } + } + }); + // This process should proformed after coordinate systems created + // and series data processed. So put it on statistic processing stage. + registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, function (ecModel, api) { + // Build axisPointerModel, mergin tooltip.axisPointer model for each axis. + // allAxesInfo should be updated when setOption performed. + ecModel.getComponent('axisPointer').coordSysAxesInfo = collect(ecModel, api); + }); + // Broadcast to all views. + registers.registerAction({ + type: 'updateAxisPointer', + event: 'updateAxisPointer', + update: ':updateAxisPointer' + }, axisTrigger); + } + + function install$t(registers) { + use(install$5); + use(install$s); + } + + var PolarAxisPointer = /** @class */function (_super) { + __extends(PolarAxisPointer, _super); + function PolarAxisPointer() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * @override + */ + PolarAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) { + var axis = axisModel.axis; + if (axis.dim === 'angle') { + this.animationThreshold = Math.PI / 18; + } + var polar = axis.polar; + var otherAxis = polar.getOtherAxis(axis); + var otherExtent = otherAxis.getExtent(); + var coordValue = axis.dataToCoord(value); + var axisPointerType = axisPointerModel.get('type'); + if (axisPointerType && axisPointerType !== 'none') { + var elStyle = buildElStyle(axisPointerModel); + var pointerOption = pointerShapeBuilder$1[axisPointerType](axis, polar, coordValue, otherExtent); + pointerOption.style = elStyle; + elOption.graphicKey = pointerOption.type; + elOption.pointer = pointerOption; + } + var labelMargin = axisPointerModel.get(['label', 'margin']); + var labelPos = getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin); + buildLabelElOption(elOption, axisModel, axisPointerModel, api, labelPos); + }; + return PolarAxisPointer; + }(BaseAxisPointer); + function getLabelPosition(value, axisModel, axisPointerModel, polar, labelMargin) { + var axis = axisModel.axis; + var coord = axis.dataToCoord(value); + var axisAngle = polar.getAngleAxis().getExtent()[0]; + axisAngle = axisAngle / 180 * Math.PI; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var position; + var align; + var verticalAlign; + if (axis.dim === 'radius') { + var transform = create$1(); + rotate(transform, transform, axisAngle); + translate(transform, transform, [polar.cx, polar.cy]); + position = applyTransform$1([coord, -labelMargin], transform); + var labelRotation = axisModel.getModel('axisLabel').get('rotate') || 0; + // @ts-ignore + var labelLayout = AxisBuilder.innerTextLayout(axisAngle, labelRotation * Math.PI / 180, -1); + align = labelLayout.textAlign; + verticalAlign = labelLayout.textVerticalAlign; + } else { + // angle axis + var r = radiusExtent[1]; + position = polar.coordToPoint([r + labelMargin, coord]); + var cx = polar.cx; + var cy = polar.cy; + align = Math.abs(position[0] - cx) / r < 0.3 ? 'center' : position[0] > cx ? 'left' : 'right'; + verticalAlign = Math.abs(position[1] - cy) / r < 0.3 ? 'middle' : position[1] > cy ? 'top' : 'bottom'; + } + return { + position: position, + align: align, + verticalAlign: verticalAlign + }; + } + var pointerShapeBuilder$1 = { + line: function (axis, polar, coordValue, otherExtent) { + return axis.dim === 'angle' ? { + type: 'Line', + shape: makeLineShape(polar.coordToPoint([otherExtent[0], coordValue]), polar.coordToPoint([otherExtent[1], coordValue])) + } : { + type: 'Circle', + shape: { + cx: polar.cx, + cy: polar.cy, + r: coordValue + } + }; + }, + shadow: function (axis, polar, coordValue, otherExtent) { + var bandWidth = Math.max(1, axis.getBandWidth()); + var radian = Math.PI / 180; + return axis.dim === 'angle' ? { + type: 'Sector', + shape: makeSectorShape(polar.cx, polar.cy, otherExtent[0], otherExtent[1], + // In ECharts y is negative if angle is positive + (-coordValue - bandWidth / 2) * radian, (-coordValue + bandWidth / 2) * radian) + } : { + type: 'Sector', + shape: makeSectorShape(polar.cx, polar.cy, coordValue - bandWidth / 2, coordValue + bandWidth / 2, 0, Math.PI * 2) + }; + } + }; + + var PolarModel = /** @class */function (_super) { + __extends(PolarModel, _super); + function PolarModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = PolarModel.type; + return _this; + } + PolarModel.prototype.findAxisModel = function (axisType) { + var foundAxisModel; + var ecModel = this.ecModel; + ecModel.eachComponent(axisType, function (axisModel) { + if (axisModel.getCoordSysModel() === this) { + foundAxisModel = axisModel; + } + }, this); + return foundAxisModel; + }; + PolarModel.type = 'polar'; + PolarModel.dependencies = ['radiusAxis', 'angleAxis']; + PolarModel.defaultOption = { + // zlevel: 0, + z: 0, + center: ['50%', '50%'], + radius: '80%' + }; + return PolarModel; + }(ComponentModel); + + var PolarAxisModel = /** @class */function (_super) { + __extends(PolarAxisModel, _super); + function PolarAxisModel() { + return _super !== null && _super.apply(this, arguments) || this; + } + PolarAxisModel.prototype.getCoordSysModel = function () { + return this.getReferringComponents('polar', SINGLE_REFERRING).models[0]; + }; + PolarAxisModel.type = 'polarAxis'; + return PolarAxisModel; + }(ComponentModel); + mixin(PolarAxisModel, AxisModelCommonMixin); + var AngleAxisModel = /** @class */function (_super) { + __extends(AngleAxisModel, _super); + function AngleAxisModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = AngleAxisModel.type; + return _this; + } + AngleAxisModel.type = 'angleAxis'; + return AngleAxisModel; + }(PolarAxisModel); + var RadiusAxisModel = /** @class */function (_super) { + __extends(RadiusAxisModel, _super); + function RadiusAxisModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = RadiusAxisModel.type; + return _this; + } + RadiusAxisModel.type = 'radiusAxis'; + return RadiusAxisModel; + }(PolarAxisModel); + + var RadiusAxis = /** @class */function (_super) { + __extends(RadiusAxis, _super); + function RadiusAxis(scale, radiusExtent) { + return _super.call(this, 'radius', scale, radiusExtent) || this; + } + RadiusAxis.prototype.pointToData = function (point, clamp) { + return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1]; + }; + return RadiusAxis; + }(Axis); + RadiusAxis.prototype.dataToRadius = Axis.prototype.dataToCoord; + RadiusAxis.prototype.radiusToData = Axis.prototype.coordToData; + + var inner$d = makeInner(); + var AngleAxis = /** @class */function (_super) { + __extends(AngleAxis, _super); + function AngleAxis(scale, angleExtent) { + return _super.call(this, 'angle', scale, angleExtent || [0, 360]) || this; + } + AngleAxis.prototype.pointToData = function (point, clamp) { + return this.polar.pointToData(point, clamp)[this.dim === 'radius' ? 0 : 1]; + }; + /** + * Only be called in category axis. + * Angle axis uses text height to decide interval + * + * @override + * @return {number} Auto interval for cateogry axis tick and label + */ + AngleAxis.prototype.calculateCategoryInterval = function () { + var axis = this; + var labelModel = axis.getLabelModel(); + var ordinalScale = axis.scale; + var ordinalExtent = ordinalScale.getExtent(); + // Providing this method is for optimization: + // avoid generating a long array by `getTicks` + // in large category data case. + var tickCount = ordinalScale.count(); + if (ordinalExtent[1] - ordinalExtent[0] < 1) { + return 0; + } + var tickValue = ordinalExtent[0]; + var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue); + var unitH = Math.abs(unitSpan); + // Not precise, just use height as text width + // and each distance from axis line yet. + var rect = getBoundingRect(tickValue == null ? '' : tickValue + '', labelModel.getFont(), 'center', 'top'); + var maxH = Math.max(rect.height, 7); + var dh = maxH / unitH; + // 0/0 is NaN, 1/0 is Infinity. + isNaN(dh) && (dh = Infinity); + var interval = Math.max(0, Math.floor(dh)); + var cache = inner$d(axis.model); + var lastAutoInterval = cache.lastAutoInterval; + var lastTickCount = cache.lastTickCount; + // Use cache to keep interval stable while moving zoom window, + // otherwise the calculated interval might jitter when the zoom + // window size is close to the interval-changing size. + if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 + // Always choose the bigger one, otherwise the critical + // point is not the same when zooming in or zooming out. + && lastAutoInterval > interval) { + interval = lastAutoInterval; + } + // Only update cache if cache not used, otherwise the + // changing of interval is too insensitive. + else { + cache.lastTickCount = tickCount; + cache.lastAutoInterval = interval; + } + return interval; + }; + return AngleAxis; + }(Axis); + AngleAxis.prototype.dataToAngle = Axis.prototype.dataToCoord; + AngleAxis.prototype.angleToData = Axis.prototype.coordToData; + + var polarDimensions = ['radius', 'angle']; + var Polar = /** @class */function () { + function Polar(name) { + this.dimensions = polarDimensions; + this.type = 'polar'; + /** + * x of polar center + */ + this.cx = 0; + /** + * y of polar center + */ + this.cy = 0; + this._radiusAxis = new RadiusAxis(); + this._angleAxis = new AngleAxis(); + this.axisPointerEnabled = true; + this.name = name || ''; + this._radiusAxis.polar = this._angleAxis.polar = this; + } + /** + * If contain coord + */ + Polar.prototype.containPoint = function (point) { + var coord = this.pointToCoord(point); + return this._radiusAxis.contain(coord[0]) && this._angleAxis.contain(coord[1]); + }; + /** + * If contain data + */ + Polar.prototype.containData = function (data) { + return this._radiusAxis.containData(data[0]) && this._angleAxis.containData(data[1]); + }; + Polar.prototype.getAxis = function (dim) { + var key = '_' + dim + 'Axis'; + return this[key]; + }; + Polar.prototype.getAxes = function () { + return [this._radiusAxis, this._angleAxis]; + }; + /** + * Get axes by type of scale + */ + Polar.prototype.getAxesByScale = function (scaleType) { + var axes = []; + var angleAxis = this._angleAxis; + var radiusAxis = this._radiusAxis; + angleAxis.scale.type === scaleType && axes.push(angleAxis); + radiusAxis.scale.type === scaleType && axes.push(radiusAxis); + return axes; + }; + Polar.prototype.getAngleAxis = function () { + return this._angleAxis; + }; + Polar.prototype.getRadiusAxis = function () { + return this._radiusAxis; + }; + Polar.prototype.getOtherAxis = function (axis) { + var angleAxis = this._angleAxis; + return axis === angleAxis ? this._radiusAxis : angleAxis; + }; + /** + * Base axis will be used on stacking. + * + */ + Polar.prototype.getBaseAxis = function () { + return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAngleAxis(); + }; + Polar.prototype.getTooltipAxes = function (dim) { + var baseAxis = dim != null && dim !== 'auto' ? this.getAxis(dim) : this.getBaseAxis(); + return { + baseAxes: [baseAxis], + otherAxes: [this.getOtherAxis(baseAxis)] + }; + }; + /** + * Convert a single data item to (x, y) point. + * Parameter data is an array which the first element is radius and the second is angle + */ + Polar.prototype.dataToPoint = function (data, clamp) { + return this.coordToPoint([this._radiusAxis.dataToRadius(data[0], clamp), this._angleAxis.dataToAngle(data[1], clamp)]); + }; + /** + * Convert a (x, y) point to data + */ + Polar.prototype.pointToData = function (point, clamp) { + var coord = this.pointToCoord(point); + return [this._radiusAxis.radiusToData(coord[0], clamp), this._angleAxis.angleToData(coord[1], clamp)]; + }; + /** + * Convert a (x, y) point to (radius, angle) coord + */ + Polar.prototype.pointToCoord = function (point) { + var dx = point[0] - this.cx; + var dy = point[1] - this.cy; + var angleAxis = this.getAngleAxis(); + var extent = angleAxis.getExtent(); + var minAngle = Math.min(extent[0], extent[1]); + var maxAngle = Math.max(extent[0], extent[1]); + // Fix fixed extent in polarCreator + // FIXME + angleAxis.inverse ? minAngle = maxAngle - 360 : maxAngle = minAngle + 360; + var radius = Math.sqrt(dx * dx + dy * dy); + dx /= radius; + dy /= radius; + var radian = Math.atan2(-dy, dx) / Math.PI * 180; + // move to angleExtent + var dir = radian < minAngle ? 1 : -1; + while (radian < minAngle || radian > maxAngle) { + radian += dir * 360; + } + return [radius, radian]; + }; + /** + * Convert a (radius, angle) coord to (x, y) point + */ + Polar.prototype.coordToPoint = function (coord) { + var radius = coord[0]; + var radian = coord[1] / 180 * Math.PI; + var x = Math.cos(radian) * radius + this.cx; + // Inverse the y + var y = -Math.sin(radian) * radius + this.cy; + return [x, y]; + }; + /** + * Get ring area of cartesian. + * Area will have a contain function to determine if a point is in the coordinate system. + */ + Polar.prototype.getArea = function () { + var angleAxis = this.getAngleAxis(); + var radiusAxis = this.getRadiusAxis(); + var radiusExtent = radiusAxis.getExtent().slice(); + radiusExtent[0] > radiusExtent[1] && radiusExtent.reverse(); + var angleExtent = angleAxis.getExtent(); + var RADIAN = Math.PI / 180; + return { + cx: this.cx, + cy: this.cy, + r0: radiusExtent[0], + r: radiusExtent[1], + startAngle: -angleExtent[0] * RADIAN, + endAngle: -angleExtent[1] * RADIAN, + clockwise: angleAxis.inverse, + contain: function (x, y) { + // It's a ring shape. + // Start angle and end angle don't matter + var dx = x - this.cx; + var dy = y - this.cy; + // minus a tiny value 1e-4 to avoid being clipped unexpectedly + var d2 = dx * dx + dy * dy - 1e-4; + var r = this.r; + var r0 = this.r0; + return d2 <= r * r && d2 >= r0 * r0; + } + }; + }; + Polar.prototype.convertToPixel = function (ecModel, finder, value) { + var coordSys = getCoordSys$2(finder); + return coordSys === this ? this.dataToPoint(value) : null; + }; + Polar.prototype.convertFromPixel = function (ecModel, finder, pixel) { + var coordSys = getCoordSys$2(finder); + return coordSys === this ? this.pointToData(pixel) : null; + }; + return Polar; + }(); + function getCoordSys$2(finder) { + var seriesModel = finder.seriesModel; + var polarModel = finder.polarModel; + return polarModel && polarModel.coordinateSystem || seriesModel && seriesModel.coordinateSystem; + } + + /** + * Resize method bound to the polar + */ + function resizePolar(polar, polarModel, api) { + var center = polarModel.get('center'); + var width = api.getWidth(); + var height = api.getHeight(); + polar.cx = parsePercent$1(center[0], width); + polar.cy = parsePercent$1(center[1], height); + var radiusAxis = polar.getRadiusAxis(); + var size = Math.min(width, height) / 2; + var radius = polarModel.get('radius'); + if (radius == null) { + radius = [0, '100%']; + } else if (!isArray(radius)) { + // r0 = 0 + radius = [0, radius]; + } + var parsedRadius = [parsePercent$1(radius[0], size), parsePercent$1(radius[1], size)]; + radiusAxis.inverse ? radiusAxis.setExtent(parsedRadius[1], parsedRadius[0]) : radiusAxis.setExtent(parsedRadius[0], parsedRadius[1]); + } + /** + * Update polar + */ + function updatePolarScale(ecModel, api) { + var polar = this; + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + // Reset scale + angleAxis.scale.setExtent(Infinity, -Infinity); + radiusAxis.scale.setExtent(Infinity, -Infinity); + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.coordinateSystem === polar) { + var data_1 = seriesModel.getData(); + each(getDataDimensionsOnAxis(data_1, 'radius'), function (dim) { + radiusAxis.scale.unionExtentFromData(data_1, dim); + }); + each(getDataDimensionsOnAxis(data_1, 'angle'), function (dim) { + angleAxis.scale.unionExtentFromData(data_1, dim); + }); + } + }); + niceScaleExtent(angleAxis.scale, angleAxis.model); + niceScaleExtent(radiusAxis.scale, radiusAxis.model); + // Fix extent of category angle axis + if (angleAxis.type === 'category' && !angleAxis.onBand) { + var extent = angleAxis.getExtent(); + var diff = 360 / angleAxis.scale.count(); + angleAxis.inverse ? extent[1] += diff : extent[1] -= diff; + angleAxis.setExtent(extent[0], extent[1]); + } + } + function isAngleAxisModel(axisModel) { + return axisModel.mainType === 'angleAxis'; + } + /** + * Set common axis properties + */ + function setAxis(axis, axisModel) { + var _a; + axis.type = axisModel.get('type'); + axis.scale = createScaleByModel(axisModel); + axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category'; + axis.inverse = axisModel.get('inverse'); + if (isAngleAxisModel(axisModel)) { + axis.inverse = axis.inverse !== axisModel.get('clockwise'); + var startAngle = axisModel.get('startAngle'); + var endAngle = (_a = axisModel.get('endAngle')) !== null && _a !== void 0 ? _a : startAngle + (axis.inverse ? -360 : 360); + axis.setExtent(startAngle, endAngle); + } + // Inject axis instance + axisModel.axis = axis; + axis.model = axisModel; + } + var polarCreator = { + dimensions: polarDimensions, + create: function (ecModel, api) { + var polarList = []; + ecModel.eachComponent('polar', function (polarModel, idx) { + var polar = new Polar(idx + ''); + // Inject resize and update method + polar.update = updatePolarScale; + var radiusAxis = polar.getRadiusAxis(); + var angleAxis = polar.getAngleAxis(); + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + setAxis(radiusAxis, radiusAxisModel); + setAxis(angleAxis, angleAxisModel); + resizePolar(polar, polarModel, api); + polarList.push(polar); + polarModel.coordinateSystem = polar; + polar.model = polarModel; + }); + // Inject coordinateSystem to series + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'polar') { + var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0]; + if ("development" !== 'production') { + if (!polarModel) { + throw new Error('Polar "' + retrieve(seriesModel.get('polarIndex'), seriesModel.get('polarId'), 0) + '" not found'); + } + } + seriesModel.coordinateSystem = polarModel.coordinateSystem; + } + }); + return polarList; + } + }; + + var elementList$1 = ['axisLine', 'axisLabel', 'axisTick', 'minorTick', 'splitLine', 'minorSplitLine', 'splitArea']; + function getAxisLineShape(polar, rExtent, angle) { + rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse()); + var start = polar.coordToPoint([rExtent[0], angle]); + var end = polar.coordToPoint([rExtent[1], angle]); + return { + x1: start[0], + y1: start[1], + x2: end[0], + y2: end[1] + }; + } + function getRadiusIdx(polar) { + var radiusAxis = polar.getRadiusAxis(); + return radiusAxis.inverse ? 0 : 1; + } + // Remove the last tick which will overlap the first tick + function fixAngleOverlap(list) { + var firstItem = list[0]; + var lastItem = list[list.length - 1]; + if (firstItem && lastItem && Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4) { + list.pop(); + } + } + var AngleAxisView = /** @class */function (_super) { + __extends(AngleAxisView, _super); + function AngleAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = AngleAxisView.type; + _this.axisPointerClass = 'PolarAxisPointer'; + return _this; + } + AngleAxisView.prototype.render = function (angleAxisModel, ecModel) { + this.group.removeAll(); + if (!angleAxisModel.get('show')) { + return; + } + var angleAxis = angleAxisModel.axis; + var polar = angleAxis.polar; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var ticksAngles = angleAxis.getTicksCoords(); + var minorTickAngles = angleAxis.getMinorTicksCoords(); + var labels = map(angleAxis.getViewLabels(), function (labelItem) { + labelItem = clone(labelItem); + var scale = angleAxis.scale; + var tickValue = scale.type === 'ordinal' ? scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue; + labelItem.coord = angleAxis.dataToCoord(tickValue); + return labelItem; + }); + fixAngleOverlap(labels); + fixAngleOverlap(ticksAngles); + each(elementList$1, function (name) { + if (angleAxisModel.get([name, 'show']) && (!angleAxis.scale.isBlank() || name === 'axisLine')) { + angelAxisElementsBuilders[name](this.group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels); + } + }, this); + }; + AngleAxisView.type = 'angleAxis'; + return AngleAxisView; + }(AxisView); + var angelAxisElementsBuilders = { + axisLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) { + var lineStyleModel = angleAxisModel.getModel(['axisLine', 'lineStyle']); + var angleAxis = polar.getAngleAxis(); + var RADIAN = Math.PI / 180; + var angleExtent = angleAxis.getExtent(); + // extent id of the axis radius (r0 and r) + var rId = getRadiusIdx(polar); + var r0Id = rId ? 0 : 1; + var shape; + var shapeType = Math.abs(angleExtent[1] - angleExtent[0]) === 360 ? 'Circle' : 'Arc'; + if (radiusExtent[r0Id] === 0) { + shape = new graphic[shapeType]({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: radiusExtent[rId], + startAngle: -angleExtent[0] * RADIAN, + endAngle: -angleExtent[1] * RADIAN, + clockwise: angleAxis.inverse + }, + style: lineStyleModel.getLineStyle(), + z2: 1, + silent: true + }); + } else { + shape = new Ring({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: radiusExtent[rId], + r0: radiusExtent[r0Id] + }, + style: lineStyleModel.getLineStyle(), + z2: 1, + silent: true + }); + } + shape.style.fill = null; + group.add(shape); + }, + axisTick: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) { + var tickModel = angleAxisModel.getModel('axisTick'); + var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length'); + var radius = radiusExtent[getRadiusIdx(polar)]; + var lines = map(ticksAngles, function (tickAngleItem) { + return new Line({ + shape: getAxisLineShape(polar, [radius, radius + tickLen], tickAngleItem.coord) + }); + }); + group.add(mergePath$1(lines, { + style: defaults(tickModel.getModel('lineStyle').getLineStyle(), { + stroke: angleAxisModel.get(['axisLine', 'lineStyle', 'color']) + }) + })); + }, + minorTick: function (group, angleAxisModel, polar, tickAngles, minorTickAngles, radiusExtent) { + if (!minorTickAngles.length) { + return; + } + var tickModel = angleAxisModel.getModel('axisTick'); + var minorTickModel = angleAxisModel.getModel('minorTick'); + var tickLen = (tickModel.get('inside') ? -1 : 1) * minorTickModel.get('length'); + var radius = radiusExtent[getRadiusIdx(polar)]; + var lines = []; + for (var i = 0; i < minorTickAngles.length; i++) { + for (var k = 0; k < minorTickAngles[i].length; k++) { + lines.push(new Line({ + shape: getAxisLineShape(polar, [radius, radius + tickLen], minorTickAngles[i][k].coord) + })); + } + } + group.add(mergePath$1(lines, { + style: defaults(minorTickModel.getModel('lineStyle').getLineStyle(), defaults(tickModel.getLineStyle(), { + stroke: angleAxisModel.get(['axisLine', 'lineStyle', 'color']) + })) + })); + }, + axisLabel: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent, labels) { + var rawCategoryData = angleAxisModel.getCategories(true); + var commonLabelModel = angleAxisModel.getModel('axisLabel'); + var labelMargin = commonLabelModel.get('margin'); + var triggerEvent = angleAxisModel.get('triggerEvent'); + // Use length of ticksAngles because it may remove the last tick to avoid overlapping + each(labels, function (labelItem, idx) { + var labelModel = commonLabelModel; + var tickValue = labelItem.tickValue; + var r = radiusExtent[getRadiusIdx(polar)]; + var p = polar.coordToPoint([r + labelMargin, labelItem.coord]); + var cx = polar.cx; + var cy = polar.cy; + var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3 ? 'center' : p[0] > cx ? 'left' : 'right'; + var labelTextVerticalAlign = Math.abs(p[1] - cy) / r < 0.3 ? 'middle' : p[1] > cy ? 'top' : 'bottom'; + if (rawCategoryData && rawCategoryData[tickValue]) { + var rawCategoryItem = rawCategoryData[tickValue]; + if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) { + labelModel = new Model(rawCategoryItem.textStyle, commonLabelModel, commonLabelModel.ecModel); + } + } + var textEl = new ZRText({ + silent: AxisBuilder.isLabelSilent(angleAxisModel), + style: createTextStyle(labelModel, { + x: p[0], + y: p[1], + fill: labelModel.getTextColor() || angleAxisModel.get(['axisLine', 'lineStyle', 'color']), + text: labelItem.formattedLabel, + align: labelTextAlign, + verticalAlign: labelTextVerticalAlign + }) + }); + group.add(textEl); + // Pack data for mouse event + if (triggerEvent) { + var eventData = AxisBuilder.makeAxisEventDataBase(angleAxisModel); + eventData.targetType = 'axisLabel'; + eventData.value = labelItem.rawLabel; + getECData(textEl).eventData = eventData; + } + }, this); + }, + splitLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) { + var splitLineModel = angleAxisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + var lineCount = 0; + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + var splitLines = []; + for (var i = 0; i < ticksAngles.length; i++) { + var colorIndex = lineCount++ % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new Line({ + shape: getAxisLineShape(polar, radiusExtent, ticksAngles[i].coord) + })); + } + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + group.add(mergePath$1(splitLines[i], { + style: defaults({ + stroke: lineColors[i % lineColors.length] + }, lineStyleModel.getLineStyle()), + silent: true, + z: angleAxisModel.get('z') + })); + } + }, + minorSplitLine: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) { + if (!minorTickAngles.length) { + return; + } + var minorSplitLineModel = angleAxisModel.getModel('minorSplitLine'); + var lineStyleModel = minorSplitLineModel.getModel('lineStyle'); + var lines = []; + for (var i = 0; i < minorTickAngles.length; i++) { + for (var k = 0; k < minorTickAngles[i].length; k++) { + lines.push(new Line({ + shape: getAxisLineShape(polar, radiusExtent, minorTickAngles[i][k].coord) + })); + } + } + group.add(mergePath$1(lines, { + style: lineStyleModel.getLineStyle(), + silent: true, + z: angleAxisModel.get('z') + })); + }, + splitArea: function (group, angleAxisModel, polar, ticksAngles, minorTickAngles, radiusExtent) { + if (!ticksAngles.length) { + return; + } + var splitAreaModel = angleAxisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var lineCount = 0; + areaColors = areaColors instanceof Array ? areaColors : [areaColors]; + var splitAreas = []; + var RADIAN = Math.PI / 180; + var prevAngle = -ticksAngles[0].coord * RADIAN; + var r0 = Math.min(radiusExtent[0], radiusExtent[1]); + var r1 = Math.max(radiusExtent[0], radiusExtent[1]); + var clockwise = angleAxisModel.get('clockwise'); + for (var i = 1, len = ticksAngles.length; i <= len; i++) { + var coord = i === len ? ticksAngles[0].coord : ticksAngles[i].coord; + var colorIndex = lineCount++ % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: r0, + r: r1, + startAngle: prevAngle, + endAngle: -coord * RADIAN, + clockwise: clockwise + }, + silent: true + })); + prevAngle = -coord * RADIAN; + } + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + group.add(mergePath$1(splitAreas[i], { + style: defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } + }; + + var axisBuilderAttrs$2 = ['axisLine', 'axisTickLabel', 'axisName']; + var selfBuilderAttrs$1 = ['splitLine', 'splitArea', 'minorSplitLine']; + var RadiusAxisView = /** @class */function (_super) { + __extends(RadiusAxisView, _super); + function RadiusAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = RadiusAxisView.type; + _this.axisPointerClass = 'PolarAxisPointer'; + return _this; + } + RadiusAxisView.prototype.render = function (radiusAxisModel, ecModel) { + this.group.removeAll(); + if (!radiusAxisModel.get('show')) { + return; + } + var oldAxisGroup = this._axisGroup; + var newAxisGroup = this._axisGroup = new Group(); + this.group.add(newAxisGroup); + var radiusAxis = radiusAxisModel.axis; + var polar = radiusAxis.polar; + var angleAxis = polar.getAngleAxis(); + var ticksCoords = radiusAxis.getTicksCoords(); + var minorTicksCoords = radiusAxis.getMinorTicksCoords(); + var axisAngle = angleAxis.getExtent()[0]; + var radiusExtent = radiusAxis.getExtent(); + var layout = layoutAxis(polar, radiusAxisModel, axisAngle); + var axisBuilder = new AxisBuilder(radiusAxisModel, layout); + each(axisBuilderAttrs$2, axisBuilder.add, axisBuilder); + newAxisGroup.add(axisBuilder.getGroup()); + groupTransition(oldAxisGroup, newAxisGroup, radiusAxisModel); + each(selfBuilderAttrs$1, function (name) { + if (radiusAxisModel.get([name, 'show']) && !radiusAxis.scale.isBlank()) { + axisElementBuilders$1[name](this.group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords, minorTicksCoords); + } + }, this); + }; + RadiusAxisView.type = 'radiusAxis'; + return RadiusAxisView; + }(AxisView); + var axisElementBuilders$1 = { + splitLine: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + var splitLineModel = radiusAxisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + var lineCount = 0; + var angleAxis = polar.getAngleAxis(); + var RADIAN = Math.PI / 180; + var angleExtent = angleAxis.getExtent(); + var shapeType = Math.abs(angleExtent[1] - angleExtent[0]) === 360 ? 'Circle' : 'Arc'; + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + var splitLines = []; + for (var i = 0; i < ticksCoords.length; i++) { + var colorIndex = lineCount++ % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic[shapeType]({ + shape: { + cx: polar.cx, + cy: polar.cy, + // ensure circle radius >= 0 + r: Math.max(ticksCoords[i].coord, 0), + startAngle: -angleExtent[0] * RADIAN, + endAngle: -angleExtent[1] * RADIAN, + clockwise: angleAxis.inverse + } + })); + } + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + group.add(mergePath$1(splitLines[i], { + style: defaults({ + stroke: lineColors[i % lineColors.length], + fill: null + }, lineStyleModel.getLineStyle()), + silent: true + })); + } + }, + minorSplitLine: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords, minorTicksCoords) { + if (!minorTicksCoords.length) { + return; + } + var minorSplitLineModel = radiusAxisModel.getModel('minorSplitLine'); + var lineStyleModel = minorSplitLineModel.getModel('lineStyle'); + var lines = []; + for (var i = 0; i < minorTicksCoords.length; i++) { + for (var k = 0; k < minorTicksCoords[i].length; k++) { + lines.push(new Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: minorTicksCoords[i][k].coord + } + })); + } + } + group.add(mergePath$1(lines, { + style: defaults({ + fill: null + }, lineStyleModel.getLineStyle()), + silent: true + })); + }, + splitArea: function (group, radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + if (!ticksCoords.length) { + return; + } + var splitAreaModel = radiusAxisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var lineCount = 0; + areaColors = areaColors instanceof Array ? areaColors : [areaColors]; + var splitAreas = []; + var prevRadius = ticksCoords[0].coord; + for (var i = 1; i < ticksCoords.length; i++) { + var colorIndex = lineCount++ % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: prevRadius, + r: ticksCoords[i].coord, + startAngle: 0, + endAngle: Math.PI * 2 + }, + silent: true + })); + prevRadius = ticksCoords[i].coord; + } + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + group.add(mergePath$1(splitAreas[i], { + style: defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } + }; + /** + * @inner + */ + function layoutAxis(polar, radiusAxisModel, axisAngle) { + return { + position: [polar.cx, polar.cy], + rotation: axisAngle / 180 * Math.PI, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1, + labelRotate: radiusAxisModel.getModel('axisLabel').get('rotate'), + // Over splitLine and splitArea + z2: 1 + }; + } + + function getSeriesStackId$1(seriesModel) { + return seriesModel.get('stack') || '__ec_stack_' + seriesModel.seriesIndex; + } + function getAxisKey$1(polar, axis) { + return axis.dim + polar.model.componentIndex; + } + function barLayoutPolar(seriesType, ecModel, api) { + var lastStackCoords = {}; + var barWidthAndOffset = calRadialBar(filter(ecModel.getSeriesByType(seriesType), function (seriesModel) { + return !ecModel.isSeriesFiltered(seriesModel) && seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'polar'; + })); + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + // Check series coordinate, do layout for polar only + if (seriesModel.coordinateSystem.type !== 'polar') { + return; + } + var data = seriesModel.getData(); + var polar = seriesModel.coordinateSystem; + var baseAxis = polar.getBaseAxis(); + var axisKey = getAxisKey$1(polar, baseAxis); + var stackId = getSeriesStackId$1(seriesModel); + var columnLayoutInfo = barWidthAndOffset[axisKey][stackId]; + var columnOffset = columnLayoutInfo.offset; + var columnWidth = columnLayoutInfo.width; + var valueAxis = polar.getOtherAxis(baseAxis); + var cx = seriesModel.coordinateSystem.cx; + var cy = seriesModel.coordinateSystem.cy; + var barMinHeight = seriesModel.get('barMinHeight') || 0; + var barMinAngle = seriesModel.get('barMinAngle') || 0; + lastStackCoords[stackId] = lastStackCoords[stackId] || []; + var valueDim = data.mapDimension(valueAxis.dim); + var baseDim = data.mapDimension(baseAxis.dim); + var stacked = isDimensionStacked(data, valueDim /* , baseDim */); + var clampLayout = baseAxis.dim !== 'radius' || !seriesModel.get('roundCap', true); + var valueAxisStart = valueAxis.dataToCoord(0); + for (var idx = 0, len = data.count(); idx < len; idx++) { + var value = data.get(valueDim, idx); + var baseValue = data.get(baseDim, idx); + var sign = value >= 0 ? 'p' : 'n'; + var baseCoord = valueAxisStart; + // Because of the barMinHeight, we can not use the value in + // stackResultDimension directly. + // Only ordinal axis can be stacked. + if (stacked) { + if (!lastStackCoords[stackId][baseValue]) { + lastStackCoords[stackId][baseValue] = { + p: valueAxisStart, + n: valueAxisStart // Negative stack + }; + } + // Should also consider #4243 + baseCoord = lastStackCoords[stackId][baseValue][sign]; + } + var r0 = void 0; + var r = void 0; + var startAngle = void 0; + var endAngle = void 0; + // radial sector + if (valueAxis.dim === 'radius') { + var radiusSpan = valueAxis.dataToCoord(value) - valueAxisStart; + var angle = baseAxis.dataToCoord(baseValue); + if (Math.abs(radiusSpan) < barMinHeight) { + radiusSpan = (radiusSpan < 0 ? -1 : 1) * barMinHeight; + } + r0 = baseCoord; + r = baseCoord + radiusSpan; + startAngle = angle - columnOffset; + endAngle = startAngle - columnWidth; + stacked && (lastStackCoords[stackId][baseValue][sign] = r); + } + // tangential sector + else { + var angleSpan = valueAxis.dataToCoord(value, clampLayout) - valueAxisStart; + var radius = baseAxis.dataToCoord(baseValue); + if (Math.abs(angleSpan) < barMinAngle) { + angleSpan = (angleSpan < 0 ? -1 : 1) * barMinAngle; + } + r0 = radius + columnOffset; + r = r0 + columnWidth; + startAngle = baseCoord; + endAngle = baseCoord + angleSpan; + // if the previous stack is at the end of the ring, + // add a round to differentiate it from origin + // let extent = angleAxis.getExtent(); + // let stackCoord = angle; + // if (stackCoord === extent[0] && value > 0) { + // stackCoord = extent[1]; + // } + // else if (stackCoord === extent[1] && value < 0) { + // stackCoord = extent[0]; + // } + stacked && (lastStackCoords[stackId][baseValue][sign] = endAngle); + } + data.setItemLayout(idx, { + cx: cx, + cy: cy, + r0: r0, + r: r, + // Consider that positive angle is anti-clockwise, + // while positive radian of sector is clockwise + startAngle: -startAngle * Math.PI / 180, + endAngle: -endAngle * Math.PI / 180, + /** + * Keep the same logic with bar in catesion: use end value to + * control direction. Notice that if clockwise is true (by + * default), the sector will always draw clockwisely, no matter + * whether endAngle is greater or less than startAngle. + */ + clockwise: startAngle >= endAngle + }); + } + }); + } + /** + * Calculate bar width and offset for radial bar charts + */ + function calRadialBar(barSeries) { + // Columns info on each category axis. Key is polar name + var columnsMap = {}; + each(barSeries, function (seriesModel, idx) { + var data = seriesModel.getData(); + var polar = seriesModel.coordinateSystem; + var baseAxis = polar.getBaseAxis(); + var axisKey = getAxisKey$1(polar, baseAxis); + var axisExtent = baseAxis.getExtent(); + var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : Math.abs(axisExtent[1] - axisExtent[0]) / data.count(); + var columnsOnAxis = columnsMap[axisKey] || { + bandWidth: bandWidth, + remainedWidth: bandWidth, + autoWidthCount: 0, + categoryGap: '20%', + gap: '30%', + stacks: {} + }; + var stacks = columnsOnAxis.stacks; + columnsMap[axisKey] = columnsOnAxis; + var stackId = getSeriesStackId$1(seriesModel); + if (!stacks[stackId]) { + columnsOnAxis.autoWidthCount++; + } + stacks[stackId] = stacks[stackId] || { + width: 0, + maxWidth: 0 + }; + var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth); + var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth); + var barGap = seriesModel.get('barGap'); + var barCategoryGap = seriesModel.get('barCategoryGap'); + if (barWidth && !stacks[stackId].width) { + barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); + stacks[stackId].width = barWidth; + columnsOnAxis.remainedWidth -= barWidth; + } + barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); + barGap != null && (columnsOnAxis.gap = barGap); + barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap); + }); + var result = {}; + each(columnsMap, function (columnsOnAxis, coordSysName) { + result[coordSysName] = {}; + var stacks = columnsOnAxis.stacks; + var bandWidth = columnsOnAxis.bandWidth; + var categoryGap = parsePercent$1(columnsOnAxis.categoryGap, bandWidth); + var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1); + var remainedWidth = columnsOnAxis.remainedWidth; + var autoWidthCount = columnsOnAxis.autoWidthCount; + var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + // Find if any auto calculated bar exceeded maxBarWidth + each(stacks, function (column, stack) { + var maxWidth = column.maxWidth; + if (maxWidth && maxWidth < autoWidth) { + maxWidth = Math.min(maxWidth, remainedWidth); + if (column.width) { + maxWidth = Math.min(maxWidth, column.width); + } + remainedWidth -= maxWidth; + column.width = maxWidth; + autoWidthCount--; + } + }); + // Recalculate width again + autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + var widthSum = 0; + var lastColumn; + each(stacks, function (column, idx) { + if (!column.width) { + column.width = autoWidth; + } + lastColumn = column; + widthSum += column.width * (1 + barGapPercent); + }); + if (lastColumn) { + widthSum -= lastColumn.width * barGapPercent; + } + var offset = -widthSum / 2; + each(stacks, function (column, stackId) { + result[coordSysName][stackId] = result[coordSysName][stackId] || { + offset: offset, + width: column.width + }; + offset += column.width * (1 + barGapPercent); + }); + }); + return result; + } + + var angleAxisExtraOption = { + startAngle: 90, + clockwise: true, + splitNumber: 12, + axisLabel: { + rotate: 0 + } + }; + var radiusAxisExtraOption = { + splitNumber: 5 + }; + var PolarView = /** @class */function (_super) { + __extends(PolarView, _super); + function PolarView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = PolarView.type; + return _this; + } + PolarView.type = 'polar'; + return PolarView; + }(ComponentView); + function install$u(registers) { + use(install$s); + AxisView.registerAxisPointerClass('PolarAxisPointer', PolarAxisPointer); + registers.registerCoordinateSystem('polar', polarCreator); + registers.registerComponentModel(PolarModel); + registers.registerComponentView(PolarView); + // Model and view for angleAxis and radiusAxis + axisModelCreator(registers, 'angle', AngleAxisModel, angleAxisExtraOption); + axisModelCreator(registers, 'radius', RadiusAxisModel, radiusAxisExtraOption); + registers.registerComponentView(AngleAxisView); + registers.registerComponentView(RadiusAxisView); + registers.registerLayout(curry(barLayoutPolar, 'bar')); + } + + function layout$2(axisModel, opt) { + opt = opt || {}; + var single = axisModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + var axisPosition = axis.position; + var orient = axis.orient; + var rect = single.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + var positionMap = { + horizontal: { + top: rectBound[2], + bottom: rectBound[3] + }, + vertical: { + left: rectBound[0], + right: rectBound[1] + } + }; + layout.position = [orient === 'vertical' ? positionMap.vertical[axisPosition] : rectBound[0], orient === 'horizontal' ? positionMap.horizontal[axisPosition] : rectBound[3]]; + var r = { + horizontal: 0, + vertical: 1 + }; + layout.rotation = Math.PI / 2 * r[orient]; + var directionMap = { + top: -1, + bottom: 1, + right: 1, + left: -1 + }; + layout.labelDirection = layout.tickDirection = layout.nameDirection = directionMap[axisPosition]; + if (axisModel.get(['axisTick', 'inside'])) { + layout.tickDirection = -layout.tickDirection; + } + if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) { + layout.labelDirection = -layout.labelDirection; + } + var labelRotation = opt.rotate; + labelRotation == null && (labelRotation = axisModel.get(['axisLabel', 'rotate'])); + layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; + layout.z2 = 1; + return layout; + } + + var axisBuilderAttrs$3 = ['axisLine', 'axisTickLabel', 'axisName']; + var selfBuilderAttrs$2 = ['splitArea', 'splitLine']; + var SingleAxisView = /** @class */function (_super) { + __extends(SingleAxisView, _super); + function SingleAxisView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SingleAxisView.type; + _this.axisPointerClass = 'SingleAxisPointer'; + return _this; + } + SingleAxisView.prototype.render = function (axisModel, ecModel, api, payload) { + var group = this.group; + group.removeAll(); + var oldAxisGroup = this._axisGroup; + this._axisGroup = new Group(); + var layout = layout$2(axisModel); + var axisBuilder = new AxisBuilder(axisModel, layout); + each(axisBuilderAttrs$3, axisBuilder.add, axisBuilder); + group.add(this._axisGroup); + group.add(axisBuilder.getGroup()); + each(selfBuilderAttrs$2, function (name) { + if (axisModel.get([name, 'show'])) { + axisElementBuilders$2[name](this, this.group, this._axisGroup, axisModel); + } + }, this); + groupTransition(oldAxisGroup, this._axisGroup, axisModel); + _super.prototype.render.call(this, axisModel, ecModel, api, payload); + }; + SingleAxisView.prototype.remove = function () { + rectCoordAxisHandleRemove(this); + }; + SingleAxisView.type = 'singleAxis'; + return SingleAxisView; + }(AxisView); + var axisElementBuilders$2 = { + splitLine: function (axisView, group, axisGroup, axisModel) { + var axis = axisModel.axis; + if (axis.scale.isBlank()) { + return; + } + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + var lineWidth = lineStyleModel.get('width'); + var gridRect = axisModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + var splitLines = []; + var lineCount = 0; + var ticksCoords = axis.getTicksCoords({ + tickModel: splitLineModel + }); + var p1 = []; + var p2 = []; + for (var i = 0; i < ticksCoords.length; ++i) { + var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + var line = new Line({ + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + silent: true + }); + subPixelOptimizeLine$1(line.shape, lineWidth); + var colorIndex = lineCount++ % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(line); + } + var lineStyle = lineStyleModel.getLineStyle(['color']); + for (var i = 0; i < splitLines.length; ++i) { + group.add(mergePath$1(splitLines[i], { + style: defaults({ + stroke: lineColors[i % lineColors.length] + }, lineStyle), + silent: true + })); + } + }, + splitArea: function (axisView, group, axisGroup, axisModel) { + rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, axisModel); + } + }; + + var SingleAxisModel = /** @class */function (_super) { + __extends(SingleAxisModel, _super); + function SingleAxisModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SingleAxisModel.type; + return _this; + } + SingleAxisModel.prototype.getCoordSysModel = function () { + return this; + }; + SingleAxisModel.type = 'singleAxis'; + SingleAxisModel.layoutMode = 'box'; + SingleAxisModel.defaultOption = { + left: '5%', + top: '5%', + right: '5%', + bottom: '5%', + type: 'value', + position: 'bottom', + orient: 'horizontal', + axisLine: { + show: true, + lineStyle: { + width: 1, + type: 'solid' + } + }, + // Single coordinate system and single axis is the, + // which is used as the parent tooltip model. + // same model, so we set default tooltip show as true. + tooltip: { + show: true + }, + axisTick: { + show: true, + length: 6, + lineStyle: { + width: 1 + } + }, + axisLabel: { + show: true, + interval: 'auto' + }, + splitLine: { + show: true, + lineStyle: { + type: 'dashed', + opacity: 0.2 + } + } + }; + return SingleAxisModel; + }(ComponentModel); + mixin(SingleAxisModel, AxisModelCommonMixin.prototype); + + var SingleAxis = /** @class */function (_super) { + __extends(SingleAxis, _super); + function SingleAxis(dim, scale, coordExtent, axisType, position) { + var _this = _super.call(this, dim, scale, coordExtent) || this; + _this.type = axisType || 'value'; + _this.position = position || 'bottom'; + return _this; + } + /** + * Judge the orient of the axis. + */ + SingleAxis.prototype.isHorizontal = function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + }; + SingleAxis.prototype.pointToData = function (point, clamp) { + return this.coordinateSystem.pointToData(point)[0]; + }; + return SingleAxis; + }(Axis); + + var singleDimensions = ['single']; + /** + * Create a single coordinates system. + */ + var Single = /** @class */function () { + function Single(axisModel, ecModel, api) { + this.type = 'single'; + this.dimension = 'single'; + /** + * Add it just for draw tooltip. + */ + this.dimensions = singleDimensions; + this.axisPointerEnabled = true; + this.model = axisModel; + this._init(axisModel, ecModel, api); + } + /** + * Initialize single coordinate system. + */ + Single.prototype._init = function (axisModel, ecModel, api) { + var dim = this.dimension; + var axis = new SingleAxis(dim, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisModel.get('position')); + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + axis.orient = axisModel.get('orient'); + axisModel.axis = axis; + axis.model = axisModel; + axis.coordinateSystem = this; + this._axis = axis; + }; + /** + * Update axis scale after data processed + */ + Single.prototype.update = function (ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.coordinateSystem === this) { + var data_1 = seriesModel.getData(); + each(data_1.mapDimensionsAll(this.dimension), function (dim) { + this._axis.scale.unionExtentFromData(data_1, dim); + }, this); + niceScaleExtent(this._axis.scale, this._axis.model); + } + }, this); + }; + /** + * Resize the single coordinate system. + */ + Single.prototype.resize = function (axisModel, api) { + this._rect = getLayoutRect({ + left: axisModel.get('left'), + top: axisModel.get('top'), + right: axisModel.get('right'), + bottom: axisModel.get('bottom'), + width: axisModel.get('width'), + height: axisModel.get('height') + }, { + width: api.getWidth(), + height: api.getHeight() + }); + this._adjustAxis(); + }; + Single.prototype.getRect = function () { + return this._rect; + }; + Single.prototype._adjustAxis = function () { + var rect = this._rect; + var axis = this._axis; + var isHorizontal = axis.isHorizontal(); + var extent = isHorizontal ? [0, rect.width] : [0, rect.height]; + var idx = axis.inverse ? 1 : 0; + axis.setExtent(extent[idx], extent[1 - idx]); + this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y); + }; + Single.prototype._updateAxisTransform = function (axis, coordBase) { + var axisExtent = axis.getExtent(); + var extentSum = axisExtent[0] + axisExtent[1]; + var isHorizontal = axis.isHorizontal(); + axis.toGlobalCoord = isHorizontal ? function (coord) { + return coord + coordBase; + } : function (coord) { + return extentSum - coord + coordBase; + }; + axis.toLocalCoord = isHorizontal ? function (coord) { + return coord - coordBase; + } : function (coord) { + return extentSum - coord + coordBase; + }; + }; + /** + * Get axis. + */ + Single.prototype.getAxis = function () { + return this._axis; + }; + /** + * Get axis, add it just for draw tooltip. + */ + Single.prototype.getBaseAxis = function () { + return this._axis; + }; + Single.prototype.getAxes = function () { + return [this._axis]; + }; + Single.prototype.getTooltipAxes = function () { + return { + baseAxes: [this.getAxis()], + // Empty otherAxes + otherAxes: [] + }; + }; + /** + * If contain point. + */ + Single.prototype.containPoint = function (point) { + var rect = this.getRect(); + var axis = this.getAxis(); + var orient = axis.orient; + if (orient === 'horizontal') { + return axis.contain(axis.toLocalCoord(point[0])) && point[1] >= rect.y && point[1] <= rect.y + rect.height; + } else { + return axis.contain(axis.toLocalCoord(point[1])) && point[0] >= rect.y && point[0] <= rect.y + rect.height; + } + }; + Single.prototype.pointToData = function (point) { + var axis = this.getAxis(); + return [axis.coordToData(axis.toLocalCoord(point[axis.orient === 'horizontal' ? 0 : 1]))]; + }; + /** + * Convert the series data to concrete point. + * Can be [val] | val + */ + Single.prototype.dataToPoint = function (val) { + var axis = this.getAxis(); + var rect = this.getRect(); + var pt = []; + var idx = axis.orient === 'horizontal' ? 0 : 1; + if (val instanceof Array) { + val = val[0]; + } + pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val)); + pt[1 - idx] = idx === 0 ? rect.y + rect.height / 2 : rect.x + rect.width / 2; + return pt; + }; + Single.prototype.convertToPixel = function (ecModel, finder, value) { + var coordSys = getCoordSys$3(finder); + return coordSys === this ? this.dataToPoint(value) : null; + }; + Single.prototype.convertFromPixel = function (ecModel, finder, pixel) { + var coordSys = getCoordSys$3(finder); + return coordSys === this ? this.pointToData(pixel) : null; + }; + return Single; + }(); + function getCoordSys$3(finder) { + var seriesModel = finder.seriesModel; + var singleModel = finder.singleAxisModel; + return singleModel && singleModel.coordinateSystem || seriesModel && seriesModel.coordinateSystem; + } + + /** + * Create single coordinate system and inject it into seriesModel. + */ + function create$2(ecModel, api) { + var singles = []; + ecModel.eachComponent('singleAxis', function (axisModel, idx) { + var single = new Single(axisModel, ecModel, api); + single.name = 'single_' + idx; + single.resize(axisModel, api); + axisModel.coordinateSystem = single; + singles.push(single); + }); + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'singleAxis') { + var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0]; + seriesModel.coordinateSystem = singleAxisModel && singleAxisModel.coordinateSystem; + } + }); + return singles; + } + var singleCreator = { + create: create$2, + dimensions: singleDimensions + }; + + var XY = ['x', 'y']; + var WH = ['width', 'height']; + var SingleAxisPointer = /** @class */function (_super) { + __extends(SingleAxisPointer, _super); + function SingleAxisPointer() { + return _super !== null && _super.apply(this, arguments) || this; + } + /** + * @override + */ + SingleAxisPointer.prototype.makeElOption = function (elOption, value, axisModel, axisPointerModel, api) { + var axis = axisModel.axis; + var coordSys = axis.coordinateSystem; + var otherExtent = getGlobalExtent(coordSys, 1 - getPointDimIndex(axis)); + var pixelValue = coordSys.dataToPoint(value)[0]; + var axisPointerType = axisPointerModel.get('type'); + if (axisPointerType && axisPointerType !== 'none') { + var elStyle = buildElStyle(axisPointerModel); + var pointerOption = pointerShapeBuilder$2[axisPointerType](axis, pixelValue, otherExtent); + pointerOption.style = elStyle; + elOption.graphicKey = pointerOption.type; + elOption.pointer = pointerOption; + } + var layoutInfo = layout$2(axisModel); + buildCartesianSingleLabelElOption( + // @ts-ignore + value, elOption, layoutInfo, axisModel, axisPointerModel, api); + }; + /** + * @override + */ + SingleAxisPointer.prototype.getHandleTransform = function (value, axisModel, axisPointerModel) { + var layoutInfo = layout$2(axisModel, { + labelInside: false + }); + // @ts-ignore + layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']); + var position = getTransformedPosition(axisModel.axis, value, layoutInfo); + return { + x: position[0], + y: position[1], + rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0) + }; + }; + /** + * @override + */ + SingleAxisPointer.prototype.updateHandleTransform = function (transform, delta, axisModel, axisPointerModel) { + var axis = axisModel.axis; + var coordSys = axis.coordinateSystem; + var dimIndex = getPointDimIndex(axis); + var axisExtent = getGlobalExtent(coordSys, dimIndex); + var currPosition = [transform.x, transform.y]; + currPosition[dimIndex] += delta[dimIndex]; + currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]); + currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]); + var otherExtent = getGlobalExtent(coordSys, 1 - dimIndex); + var cursorOtherValue = (otherExtent[1] + otherExtent[0]) / 2; + var cursorPoint = [cursorOtherValue, cursorOtherValue]; + cursorPoint[dimIndex] = currPosition[dimIndex]; + return { + x: currPosition[0], + y: currPosition[1], + rotation: transform.rotation, + cursorPoint: cursorPoint, + tooltipOption: { + verticalAlign: 'middle' + } + }; + }; + return SingleAxisPointer; + }(BaseAxisPointer); + var pointerShapeBuilder$2 = { + line: function (axis, pixelValue, otherExtent) { + var targetShape = makeLineShape([pixelValue, otherExtent[0]], [pixelValue, otherExtent[1]], getPointDimIndex(axis)); + return { + type: 'Line', + subPixelOptimize: true, + shape: targetShape + }; + }, + shadow: function (axis, pixelValue, otherExtent) { + var bandWidth = axis.getBandWidth(); + var span = otherExtent[1] - otherExtent[0]; + return { + type: 'Rect', + shape: makeRectShape([pixelValue - bandWidth / 2, otherExtent[0]], [bandWidth, span], getPointDimIndex(axis)) + }; + } + }; + function getPointDimIndex(axis) { + return axis.isHorizontal() ? 0 : 1; + } + function getGlobalExtent(coordSys, dimIndex) { + var rect = coordSys.getRect(); + return [rect[XY[dimIndex]], rect[XY[dimIndex]] + rect[WH[dimIndex]]]; + } + + var SingleView = /** @class */function (_super) { + __extends(SingleView, _super); + function SingleView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SingleView.type; + return _this; + } + SingleView.type = 'single'; + return SingleView; + }(ComponentView); + function install$v(registers) { + use(install$s); + AxisView.registerAxisPointerClass('SingleAxisPointer', SingleAxisPointer); + registers.registerComponentView(SingleView); + // Axis + registers.registerComponentView(SingleAxisView); + registers.registerComponentModel(SingleAxisModel); + axisModelCreator(registers, 'single', SingleAxisModel, SingleAxisModel.defaultOption); + registers.registerCoordinateSystem('single', singleCreator); + } + + var CalendarModel = /** @class */function (_super) { + __extends(CalendarModel, _super); + function CalendarModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CalendarModel.type; + return _this; + } + /** + * @override + */ + CalendarModel.prototype.init = function (option, parentModel, ecModel) { + var inputPositionParams = getLayoutParams(option); + _super.prototype.init.apply(this, arguments); + mergeAndNormalizeLayoutParams(option, inputPositionParams); + }; + /** + * @override + */ + CalendarModel.prototype.mergeOption = function (option) { + _super.prototype.mergeOption.apply(this, arguments); + mergeAndNormalizeLayoutParams(this.option, option); + }; + CalendarModel.prototype.getCellSize = function () { + // Has been normalized + return this.option.cellSize; + }; + CalendarModel.type = 'calendar'; + CalendarModel.defaultOption = { + // zlevel: 0, + z: 2, + left: 80, + top: 60, + cellSize: 20, + // horizontal vertical + orient: 'horizontal', + // month separate line style + splitLine: { + show: true, + lineStyle: { + color: '#000', + width: 1, + type: 'solid' + } + }, + // rect style temporarily unused emphasis + itemStyle: { + color: '#fff', + borderWidth: 1, + borderColor: '#ccc' + }, + // week text style + dayLabel: { + show: true, + firstDay: 0, + // start end + position: 'start', + margin: '50%', + color: '#000' + }, + // month text style + monthLabel: { + show: true, + // start end + position: 'start', + margin: 5, + // center or left + align: 'center', + formatter: null, + color: '#000' + }, + // year text style + yearLabel: { + show: true, + // top bottom left right + position: null, + margin: 30, + formatter: null, + color: '#ccc', + fontFamily: 'sans-serif', + fontWeight: 'bolder', + fontSize: 20 + } + }; + return CalendarModel; + }(ComponentModel); + function mergeAndNormalizeLayoutParams(target, raw) { + // Normalize cellSize + var cellSize = target.cellSize; + var cellSizeArr; + if (!isArray(cellSize)) { + cellSizeArr = target.cellSize = [cellSize, cellSize]; + } else { + cellSizeArr = cellSize; + } + if (cellSizeArr.length === 1) { + cellSizeArr[1] = cellSizeArr[0]; + } + var ignoreSize = map([0, 1], function (hvIdx) { + // If user have set `width` or both `left` and `right`, cellSizeArr + // will be automatically set to 'auto', otherwise the default + // setting of cellSizeArr will make `width` setting not work. + if (sizeCalculable(raw, hvIdx)) { + cellSizeArr[hvIdx] = 'auto'; + } + return cellSizeArr[hvIdx] != null && cellSizeArr[hvIdx] !== 'auto'; + }); + mergeLayoutParam(target, raw, { + type: 'box', + ignoreSize: ignoreSize + }); + } + + var CalendarView = /** @class */function (_super) { + __extends(CalendarView, _super); + function CalendarView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = CalendarView.type; + return _this; + } + CalendarView.prototype.render = function (calendarModel, ecModel, api) { + var group = this.group; + group.removeAll(); + var coordSys = calendarModel.coordinateSystem; + // range info + var rangeData = coordSys.getRangeInfo(); + var orient = coordSys.getOrient(); + // locale + var localeModel = ecModel.getLocaleModel(); + this._renderDayRect(calendarModel, rangeData, group); + // _renderLines must be called prior to following function + this._renderLines(calendarModel, rangeData, orient, group); + this._renderYearText(calendarModel, rangeData, orient, group); + this._renderMonthText(calendarModel, localeModel, orient, group); + this._renderWeekText(calendarModel, localeModel, rangeData, orient, group); + }; + // render day rect + CalendarView.prototype._renderDayRect = function (calendarModel, rangeData, group) { + var coordSys = calendarModel.coordinateSystem; + var itemRectStyleModel = calendarModel.getModel('itemStyle').getItemStyle(); + var sw = coordSys.getCellWidth(); + var sh = coordSys.getCellHeight(); + for (var i = rangeData.start.time; i <= rangeData.end.time; i = coordSys.getNextNDay(i, 1).time) { + var point = coordSys.dataToRect([i], false).tl; + // every rect + var rect = new Rect({ + shape: { + x: point[0], + y: point[1], + width: sw, + height: sh + }, + cursor: 'default', + style: itemRectStyleModel + }); + group.add(rect); + } + }; + // render separate line + CalendarView.prototype._renderLines = function (calendarModel, rangeData, orient, group) { + var self = this; + var coordSys = calendarModel.coordinateSystem; + var lineStyleModel = calendarModel.getModel(['splitLine', 'lineStyle']).getLineStyle(); + var show = calendarModel.get(['splitLine', 'show']); + var lineWidth = lineStyleModel.lineWidth; + this._tlpoints = []; + this._blpoints = []; + this._firstDayOfMonth = []; + this._firstDayPoints = []; + var firstDay = rangeData.start; + for (var i = 0; firstDay.time <= rangeData.end.time; i++) { + addPoints(firstDay.formatedDate); + if (i === 0) { + firstDay = coordSys.getDateInfo(rangeData.start.y + '-' + rangeData.start.m); + } + var date = firstDay.date; + date.setMonth(date.getMonth() + 1); + firstDay = coordSys.getDateInfo(date); + } + addPoints(coordSys.getNextNDay(rangeData.end.time, 1).formatedDate); + function addPoints(date) { + self._firstDayOfMonth.push(coordSys.getDateInfo(date)); + self._firstDayPoints.push(coordSys.dataToRect([date], false).tl); + var points = self._getLinePointsOfOneWeek(calendarModel, date, orient); + self._tlpoints.push(points[0]); + self._blpoints.push(points[points.length - 1]); + show && self._drawSplitline(points, lineStyleModel, group); + } + // render top/left line + show && this._drawSplitline(self._getEdgesPoints(self._tlpoints, lineWidth, orient), lineStyleModel, group); + // render bottom/right line + show && this._drawSplitline(self._getEdgesPoints(self._blpoints, lineWidth, orient), lineStyleModel, group); + }; + // get points at both ends + CalendarView.prototype._getEdgesPoints = function (points, lineWidth, orient) { + var rs = [points[0].slice(), points[points.length - 1].slice()]; + var idx = orient === 'horizontal' ? 0 : 1; + // both ends of the line are extend half lineWidth + rs[0][idx] = rs[0][idx] - lineWidth / 2; + rs[1][idx] = rs[1][idx] + lineWidth / 2; + return rs; + }; + // render split line + CalendarView.prototype._drawSplitline = function (points, lineStyle, group) { + var poyline = new Polyline({ + z2: 20, + shape: { + points: points + }, + style: lineStyle + }); + group.add(poyline); + }; + // render month line of one week points + CalendarView.prototype._getLinePointsOfOneWeek = function (calendarModel, date, orient) { + var coordSys = calendarModel.coordinateSystem; + var parsedDate = coordSys.getDateInfo(date); + var points = []; + for (var i = 0; i < 7; i++) { + var tmpD = coordSys.getNextNDay(parsedDate.time, i); + var point = coordSys.dataToRect([tmpD.time], false); + points[2 * tmpD.day] = point.tl; + points[2 * tmpD.day + 1] = point[orient === 'horizontal' ? 'bl' : 'tr']; + } + return points; + }; + CalendarView.prototype._formatterLabel = function (formatter, params) { + if (isString(formatter) && formatter) { + return formatTplSimple(formatter, params); + } + if (isFunction(formatter)) { + return formatter(params); + } + return params.nameMap; + }; + CalendarView.prototype._yearTextPositionControl = function (textEl, point, orient, position, margin) { + var x = point[0]; + var y = point[1]; + var aligns = ['center', 'bottom']; + if (position === 'bottom') { + y += margin; + aligns = ['center', 'top']; + } else if (position === 'left') { + x -= margin; + } else if (position === 'right') { + x += margin; + aligns = ['center', 'top']; + } else { + // top + y -= margin; + } + var rotate = 0; + if (position === 'left' || position === 'right') { + rotate = Math.PI / 2; + } + return { + rotation: rotate, + x: x, + y: y, + style: { + align: aligns[0], + verticalAlign: aligns[1] + } + }; + }; + // render year + CalendarView.prototype._renderYearText = function (calendarModel, rangeData, orient, group) { + var yearLabel = calendarModel.getModel('yearLabel'); + if (!yearLabel.get('show')) { + return; + } + var margin = yearLabel.get('margin'); + var pos = yearLabel.get('position'); + if (!pos) { + pos = orient !== 'horizontal' ? 'top' : 'left'; + } + var points = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]]; + var xc = (points[0][0] + points[1][0]) / 2; + var yc = (points[0][1] + points[1][1]) / 2; + var idx = orient === 'horizontal' ? 0 : 1; + var posPoints = { + top: [xc, points[idx][1]], + bottom: [xc, points[1 - idx][1]], + left: [points[1 - idx][0], yc], + right: [points[idx][0], yc] + }; + var name = rangeData.start.y; + if (+rangeData.end.y > +rangeData.start.y) { + name = name + '-' + rangeData.end.y; + } + var formatter = yearLabel.get('formatter'); + var params = { + start: rangeData.start.y, + end: rangeData.end.y, + nameMap: name + }; + var content = this._formatterLabel(formatter, params); + var yearText = new ZRText({ + z2: 30, + style: createTextStyle(yearLabel, { + text: content + }) + }); + yearText.attr(this._yearTextPositionControl(yearText, posPoints[pos], orient, pos, margin)); + group.add(yearText); + }; + CalendarView.prototype._monthTextPositionControl = function (point, isCenter, orient, position, margin) { + var align = 'left'; + var vAlign = 'top'; + var x = point[0]; + var y = point[1]; + if (orient === 'horizontal') { + y = y + margin; + if (isCenter) { + align = 'center'; + } + if (position === 'start') { + vAlign = 'bottom'; + } + } else { + x = x + margin; + if (isCenter) { + vAlign = 'middle'; + } + if (position === 'start') { + align = 'right'; + } + } + return { + x: x, + y: y, + align: align, + verticalAlign: vAlign + }; + }; + // render month and year text + CalendarView.prototype._renderMonthText = function (calendarModel, localeModel, orient, group) { + var monthLabel = calendarModel.getModel('monthLabel'); + if (!monthLabel.get('show')) { + return; + } + var nameMap = monthLabel.get('nameMap'); + var margin = monthLabel.get('margin'); + var pos = monthLabel.get('position'); + var align = monthLabel.get('align'); + var termPoints = [this._tlpoints, this._blpoints]; + if (!nameMap || isString(nameMap)) { + if (nameMap) { + // case-sensitive + localeModel = getLocaleModel(nameMap) || localeModel; + } + // PENDING + // for ZH locale, original form is `一月` but current form is `1月` + nameMap = localeModel.get(['time', 'monthAbbr']) || []; + } + var idx = pos === 'start' ? 0 : 1; + var axis = orient === 'horizontal' ? 0 : 1; + margin = pos === 'start' ? -margin : margin; + var isCenter = align === 'center'; + for (var i = 0; i < termPoints[idx].length - 1; i++) { + var tmp = termPoints[idx][i].slice(); + var firstDay = this._firstDayOfMonth[i]; + if (isCenter) { + var firstDayPoints = this._firstDayPoints[i]; + tmp[axis] = (firstDayPoints[axis] + termPoints[0][i + 1][axis]) / 2; + } + var formatter = monthLabel.get('formatter'); + var name_1 = nameMap[+firstDay.m - 1]; + var params = { + yyyy: firstDay.y, + yy: (firstDay.y + '').slice(2), + MM: firstDay.m, + M: +firstDay.m, + nameMap: name_1 + }; + var content = this._formatterLabel(formatter, params); + var monthText = new ZRText({ + z2: 30, + style: extend(createTextStyle(monthLabel, { + text: content + }), this._monthTextPositionControl(tmp, isCenter, orient, pos, margin)) + }); + group.add(monthText); + } + }; + CalendarView.prototype._weekTextPositionControl = function (point, orient, position, margin, cellSize) { + var align = 'center'; + var vAlign = 'middle'; + var x = point[0]; + var y = point[1]; + var isStart = position === 'start'; + if (orient === 'horizontal') { + x = x + margin + (isStart ? 1 : -1) * cellSize[0] / 2; + align = isStart ? 'right' : 'left'; + } else { + y = y + margin + (isStart ? 1 : -1) * cellSize[1] / 2; + vAlign = isStart ? 'bottom' : 'top'; + } + return { + x: x, + y: y, + align: align, + verticalAlign: vAlign + }; + }; + // render weeks + CalendarView.prototype._renderWeekText = function (calendarModel, localeModel, rangeData, orient, group) { + var dayLabel = calendarModel.getModel('dayLabel'); + if (!dayLabel.get('show')) { + return; + } + var coordSys = calendarModel.coordinateSystem; + var pos = dayLabel.get('position'); + var nameMap = dayLabel.get('nameMap'); + var margin = dayLabel.get('margin'); + var firstDayOfWeek = coordSys.getFirstDayOfWeek(); + if (!nameMap || isString(nameMap)) { + if (nameMap) { + // case-sensitive + localeModel = getLocaleModel(nameMap) || localeModel; + } + // Use the first letter of `dayOfWeekAbbr` if `dayOfWeekShort` doesn't exist in the locale file + var dayOfWeekShort = localeModel.get(['time', 'dayOfWeekShort']); + nameMap = dayOfWeekShort || map(localeModel.get(['time', 'dayOfWeekAbbr']), function (val) { + return val[0]; + }); + } + var start = coordSys.getNextNDay(rangeData.end.time, 7 - rangeData.lweek).time; + var cellSize = [coordSys.getCellWidth(), coordSys.getCellHeight()]; + margin = parsePercent$1(margin, Math.min(cellSize[1], cellSize[0])); + if (pos === 'start') { + start = coordSys.getNextNDay(rangeData.start.time, -(7 + rangeData.fweek)).time; + margin = -margin; + } + for (var i = 0; i < 7; i++) { + var tmpD = coordSys.getNextNDay(start, i); + var point = coordSys.dataToRect([tmpD.time], false).center; + var day = i; + day = Math.abs((i + firstDayOfWeek) % 7); + var weekText = new ZRText({ + z2: 30, + style: extend(createTextStyle(dayLabel, { + text: nameMap[day] + }), this._weekTextPositionControl(point, orient, pos, margin, cellSize)) + }); + group.add(weekText); + } + }; + CalendarView.type = 'calendar'; + return CalendarView; + }(ComponentView); + + // (24*60*60*1000) + var PROXIMATE_ONE_DAY = 86400000; + var Calendar = /** @class */function () { + function Calendar(calendarModel, ecModel, api) { + this.type = 'calendar'; + this.dimensions = Calendar.dimensions; + // Required in createListFromData + this.getDimensionsInfo = Calendar.getDimensionsInfo; + this._model = calendarModel; + } + Calendar.getDimensionsInfo = function () { + return [{ + name: 'time', + type: 'time' + }, 'value']; + }; + Calendar.prototype.getRangeInfo = function () { + return this._rangeInfo; + }; + Calendar.prototype.getModel = function () { + return this._model; + }; + Calendar.prototype.getRect = function () { + return this._rect; + }; + Calendar.prototype.getCellWidth = function () { + return this._sw; + }; + Calendar.prototype.getCellHeight = function () { + return this._sh; + }; + Calendar.prototype.getOrient = function () { + return this._orient; + }; + /** + * getFirstDayOfWeek + * + * @example + * 0 : start at Sunday + * 1 : start at Monday + * + * @return {number} + */ + Calendar.prototype.getFirstDayOfWeek = function () { + return this._firstDayOfWeek; + }; + /** + * get date info + * } + */ + Calendar.prototype.getDateInfo = function (date) { + date = parseDate(date); + var y = date.getFullYear(); + var m = date.getMonth() + 1; + var mStr = m < 10 ? '0' + m : '' + m; + var d = date.getDate(); + var dStr = d < 10 ? '0' + d : '' + d; + var day = date.getDay(); + day = Math.abs((day + 7 - this.getFirstDayOfWeek()) % 7); + return { + y: y + '', + m: mStr, + d: dStr, + day: day, + time: date.getTime(), + formatedDate: y + '-' + mStr + '-' + dStr, + date: date + }; + }; + Calendar.prototype.getNextNDay = function (date, n) { + n = n || 0; + if (n === 0) { + return this.getDateInfo(date); + } + date = new Date(this.getDateInfo(date).time); + date.setDate(date.getDate() + n); + return this.getDateInfo(date); + }; + Calendar.prototype.update = function (ecModel, api) { + this._firstDayOfWeek = +this._model.getModel('dayLabel').get('firstDay'); + this._orient = this._model.get('orient'); + this._lineWidth = this._model.getModel('itemStyle').getItemStyle().lineWidth || 0; + this._rangeInfo = this._getRangeInfo(this._initRangeOption()); + var weeks = this._rangeInfo.weeks || 1; + var whNames = ['width', 'height']; + var cellSize = this._model.getCellSize().slice(); + var layoutParams = this._model.getBoxLayoutParams(); + var cellNumbers = this._orient === 'horizontal' ? [weeks, 7] : [7, weeks]; + each([0, 1], function (idx) { + if (cellSizeSpecified(cellSize, idx)) { + layoutParams[whNames[idx]] = cellSize[idx] * cellNumbers[idx]; + } + }); + var whGlobal = { + width: api.getWidth(), + height: api.getHeight() + }; + var calendarRect = this._rect = getLayoutRect(layoutParams, whGlobal); + each([0, 1], function (idx) { + if (!cellSizeSpecified(cellSize, idx)) { + cellSize[idx] = calendarRect[whNames[idx]] / cellNumbers[idx]; + } + }); + function cellSizeSpecified(cellSize, idx) { + return cellSize[idx] != null && cellSize[idx] !== 'auto'; + } + // Has been calculated out number. + this._sw = cellSize[0]; + this._sh = cellSize[1]; + }; + /** + * Convert a time data(time, value) item to (x, y) point. + */ + // TODO Clamp of calendar is not same with cartesian coordinate systems. + // It will return NaN if data exceeds. + Calendar.prototype.dataToPoint = function (data, clamp) { + isArray(data) && (data = data[0]); + clamp == null && (clamp = true); + var dayInfo = this.getDateInfo(data); + var range = this._rangeInfo; + var date = dayInfo.formatedDate; + // if not in range return [NaN, NaN] + if (clamp && !(dayInfo.time >= range.start.time && dayInfo.time < range.end.time + PROXIMATE_ONE_DAY)) { + return [NaN, NaN]; + } + var week = dayInfo.day; + var nthWeek = this._getRangeInfo([range.start.time, date]).nthWeek; + if (this._orient === 'vertical') { + return [this._rect.x + week * this._sw + this._sw / 2, this._rect.y + nthWeek * this._sh + this._sh / 2]; + } + return [this._rect.x + nthWeek * this._sw + this._sw / 2, this._rect.y + week * this._sh + this._sh / 2]; + }; + /** + * Convert a (x, y) point to time data + */ + Calendar.prototype.pointToData = function (point) { + var date = this.pointToDate(point); + return date && date.time; + }; + /** + * Convert a time date item to (x, y) four point. + */ + Calendar.prototype.dataToRect = function (data, clamp) { + var point = this.dataToPoint(data, clamp); + return { + contentShape: { + x: point[0] - (this._sw - this._lineWidth) / 2, + y: point[1] - (this._sh - this._lineWidth) / 2, + width: this._sw - this._lineWidth, + height: this._sh - this._lineWidth + }, + center: point, + tl: [point[0] - this._sw / 2, point[1] - this._sh / 2], + tr: [point[0] + this._sw / 2, point[1] - this._sh / 2], + br: [point[0] + this._sw / 2, point[1] + this._sh / 2], + bl: [point[0] - this._sw / 2, point[1] + this._sh / 2] + }; + }; + /** + * Convert a (x, y) point to time date + * + * @param {Array} point point + * @return {Object} date + */ + Calendar.prototype.pointToDate = function (point) { + var nthX = Math.floor((point[0] - this._rect.x) / this._sw) + 1; + var nthY = Math.floor((point[1] - this._rect.y) / this._sh) + 1; + var range = this._rangeInfo.range; + if (this._orient === 'vertical') { + return this._getDateByWeeksAndDay(nthY, nthX - 1, range); + } + return this._getDateByWeeksAndDay(nthX, nthY - 1, range); + }; + Calendar.prototype.convertToPixel = function (ecModel, finder, value) { + var coordSys = getCoordSys$4(finder); + return coordSys === this ? coordSys.dataToPoint(value) : null; + }; + Calendar.prototype.convertFromPixel = function (ecModel, finder, pixel) { + var coordSys = getCoordSys$4(finder); + return coordSys === this ? coordSys.pointToData(pixel) : null; + }; + Calendar.prototype.containPoint = function (point) { + console.warn('Not implemented.'); + return false; + }; + /** + * initRange + * Normalize to an [start, end] array + */ + Calendar.prototype._initRangeOption = function () { + var range = this._model.get('range'); + var normalizedRange; + // Convert [1990] to 1990 + if (isArray(range) && range.length === 1) { + range = range[0]; + } + if (!isArray(range)) { + var rangeStr = range.toString(); + // One year. + if (/^\d{4}$/.test(rangeStr)) { + normalizedRange = [rangeStr + '-01-01', rangeStr + '-12-31']; + } + // One month + if (/^\d{4}[\/|-]\d{1,2}$/.test(rangeStr)) { + var start = this.getDateInfo(rangeStr); + var firstDay = start.date; + firstDay.setMonth(firstDay.getMonth() + 1); + var end = this.getNextNDay(firstDay, -1); + normalizedRange = [start.formatedDate, end.formatedDate]; + } + // One day + if (/^\d{4}[\/|-]\d{1,2}[\/|-]\d{1,2}$/.test(rangeStr)) { + normalizedRange = [rangeStr, rangeStr]; + } + } else { + normalizedRange = range; + } + if (!normalizedRange) { + if ("development" !== 'production') { + logError('Invalid date range.'); + } + // Not handling it. + return range; + } + var tmp = this._getRangeInfo(normalizedRange); + if (tmp.start.time > tmp.end.time) { + normalizedRange.reverse(); + } + return normalizedRange; + }; + /** + * range info + * + * @private + * @param {Array} range range ['2017-01-01', '2017-07-08'] + * If range[0] > range[1], they will not be reversed. + * @return {Object} obj + */ + Calendar.prototype._getRangeInfo = function (range) { + var parsedRange = [this.getDateInfo(range[0]), this.getDateInfo(range[1])]; + var reversed; + if (parsedRange[0].time > parsedRange[1].time) { + reversed = true; + parsedRange.reverse(); + } + var allDay = Math.floor(parsedRange[1].time / PROXIMATE_ONE_DAY) - Math.floor(parsedRange[0].time / PROXIMATE_ONE_DAY) + 1; + // Consider case1 (#11677 #10430): + // Set the system timezone as "UK", set the range to `['2016-07-01', '2016-12-31']` + // Consider case2: + // Firstly set system timezone as "Time Zone: America/Toronto", + // ``` + // let first = new Date(1478412000000 - 3600 * 1000 * 2.5); + // let second = new Date(1478412000000); + // let allDays = Math.floor(second / ONE_DAY) - Math.floor(first / ONE_DAY) + 1; + // ``` + // will get wrong result because of DST. So we should fix it. + var date = new Date(parsedRange[0].time); + var startDateNum = date.getDate(); + var endDateNum = parsedRange[1].date.getDate(); + date.setDate(startDateNum + allDay - 1); + // The bias can not over a month, so just compare date. + var dateNum = date.getDate(); + if (dateNum !== endDateNum) { + var sign = date.getTime() - parsedRange[1].time > 0 ? 1 : -1; + while ((dateNum = date.getDate()) !== endDateNum && (date.getTime() - parsedRange[1].time) * sign > 0) { + allDay -= sign; + date.setDate(dateNum - sign); + } + } + var weeks = Math.floor((allDay + parsedRange[0].day + 6) / 7); + var nthWeek = reversed ? -weeks + 1 : weeks - 1; + reversed && parsedRange.reverse(); + return { + range: [parsedRange[0].formatedDate, parsedRange[1].formatedDate], + start: parsedRange[0], + end: parsedRange[1], + allDay: allDay, + weeks: weeks, + // From 0. + nthWeek: nthWeek, + fweek: parsedRange[0].day, + lweek: parsedRange[1].day + }; + }; + /** + * get date by nthWeeks and week day in range + * + * @private + * @param {number} nthWeek the week + * @param {number} day the week day + * @param {Array} range [d1, d2] + * @return {Object} + */ + Calendar.prototype._getDateByWeeksAndDay = function (nthWeek, day, range) { + var rangeInfo = this._getRangeInfo(range); + if (nthWeek > rangeInfo.weeks || nthWeek === 0 && day < rangeInfo.fweek || nthWeek === rangeInfo.weeks && day > rangeInfo.lweek) { + return null; + } + var nthDay = (nthWeek - 1) * 7 - rangeInfo.fweek + day; + var date = new Date(rangeInfo.start.time); + date.setDate(+rangeInfo.start.d + nthDay); + return this.getDateInfo(date); + }; + Calendar.create = function (ecModel, api) { + var calendarList = []; + ecModel.eachComponent('calendar', function (calendarModel) { + var calendar = new Calendar(calendarModel, ecModel, api); + calendarList.push(calendar); + calendarModel.coordinateSystem = calendar; + }); + ecModel.eachSeries(function (calendarSeries) { + if (calendarSeries.get('coordinateSystem') === 'calendar') { + // Inject coordinate system + calendarSeries.coordinateSystem = calendarList[calendarSeries.get('calendarIndex') || 0]; + } + }); + return calendarList; + }; + Calendar.dimensions = ['time', 'value']; + return Calendar; + }(); + function getCoordSys$4(finder) { + var calendarModel = finder.calendarModel; + var seriesModel = finder.seriesModel; + var coordSys = calendarModel ? calendarModel.coordinateSystem : seriesModel ? seriesModel.coordinateSystem : null; + return coordSys; + } + + function install$w(registers) { + registers.registerComponentModel(CalendarModel); + registers.registerComponentView(CalendarView); + registers.registerCoordinateSystem('calendar', Calendar); + } + + function setKeyInfoToNewElOption(resultItem, newElOption) { + var existElOption = resultItem.existing; + // Set id and type after id assigned. + newElOption.id = resultItem.keyInfo.id; + !newElOption.type && existElOption && (newElOption.type = existElOption.type); + // Set parent id if not specified + if (newElOption.parentId == null) { + var newElParentOption = newElOption.parentOption; + if (newElParentOption) { + newElOption.parentId = newElParentOption.id; + } else if (existElOption) { + newElOption.parentId = existElOption.parentId; + } + } + // Clear + newElOption.parentOption = null; + } + function isSetLoc(obj, props) { + var isSet; + each(props, function (prop) { + obj[prop] != null && obj[prop] !== 'auto' && (isSet = true); + }); + return isSet; + } + function mergeNewElOptionToExist(existList, index, newElOption) { + // Update existing options, for `getOption` feature. + var newElOptCopy = extend({}, newElOption); + var existElOption = existList[index]; + var $action = newElOption.$action || 'merge'; + if ($action === 'merge') { + if (existElOption) { + if ("development" !== 'production') { + var newType = newElOption.type; + assert(!newType || existElOption.type === newType, 'Please set $action: "replace" to change `type`'); + } + // We can ensure that newElOptCopy and existElOption are not + // the same object, so `merge` will not change newElOptCopy. + merge(existElOption, newElOptCopy, true); + // Rigid body, use ignoreSize. + mergeLayoutParam(existElOption, newElOptCopy, { + ignoreSize: true + }); + // Will be used in render. + copyLayoutParams(newElOption, existElOption); + // Copy transition info to new option so it can be used in the transition. + // DO IT AFTER merge + copyTransitionInfo(newElOption, existElOption); + copyTransitionInfo(newElOption, existElOption, 'shape'); + copyTransitionInfo(newElOption, existElOption, 'style'); + copyTransitionInfo(newElOption, existElOption, 'extra'); + // Copy clipPath + newElOption.clipPath = existElOption.clipPath; + } else { + existList[index] = newElOptCopy; + } + } else if ($action === 'replace') { + existList[index] = newElOptCopy; + } else if ($action === 'remove') { + // null will be cleaned later. + existElOption && (existList[index] = null); + } + } + var TRANSITION_PROPS_TO_COPY = ['transition', 'enterFrom', 'leaveTo']; + var ROOT_TRANSITION_PROPS_TO_COPY = TRANSITION_PROPS_TO_COPY.concat(['enterAnimation', 'updateAnimation', 'leaveAnimation']); + function copyTransitionInfo(target, source, targetProp) { + if (targetProp) { + if (!target[targetProp] && source[targetProp]) { + // TODO avoid creating this empty object when there is no transition configuration. + target[targetProp] = {}; + } + target = target[targetProp]; + source = source[targetProp]; + } + if (!target || !source) { + return; + } + var props = targetProp ? TRANSITION_PROPS_TO_COPY : ROOT_TRANSITION_PROPS_TO_COPY; + for (var i = 0; i < props.length; i++) { + var prop = props[i]; + if (target[prop] == null && source[prop] != null) { + target[prop] = source[prop]; + } + } + } + function setLayoutInfoToExist(existItem, newElOption) { + if (!existItem) { + return; + } + existItem.hv = newElOption.hv = [ + // Rigid body, don't care about `width`. + isSetLoc(newElOption, ['left', 'right']), + // Rigid body, don't care about `height`. + isSetLoc(newElOption, ['top', 'bottom'])]; + // Give default group size. Otherwise layout error may occur. + if (existItem.type === 'group') { + var existingGroupOpt = existItem; + var newGroupOpt = newElOption; + existingGroupOpt.width == null && (existingGroupOpt.width = newGroupOpt.width = 0); + existingGroupOpt.height == null && (existingGroupOpt.height = newGroupOpt.height = 0); + } + } + var GraphicComponentModel = /** @class */function (_super) { + __extends(GraphicComponentModel, _super); + function GraphicComponentModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GraphicComponentModel.type; + _this.preventAutoZ = true; + return _this; + } + GraphicComponentModel.prototype.mergeOption = function (option, ecModel) { + // Prevent default merge to elements + var elements = this.option.elements; + this.option.elements = null; + _super.prototype.mergeOption.call(this, option, ecModel); + this.option.elements = elements; + }; + GraphicComponentModel.prototype.optionUpdated = function (newOption, isInit) { + var thisOption = this.option; + var newList = (isInit ? thisOption : newOption).elements; + var existList = thisOption.elements = isInit ? [] : thisOption.elements; + var flattenedList = []; + this._flatten(newList, flattenedList, null); + var mappingResult = mappingToExists(existList, flattenedList, 'normalMerge'); + // Clear elOptionsToUpdate + var elOptionsToUpdate = this._elOptionsToUpdate = []; + each(mappingResult, function (resultItem, index) { + var newElOption = resultItem.newOption; + if ("development" !== 'production') { + assert(isObject(newElOption) || resultItem.existing, 'Empty graphic option definition'); + } + if (!newElOption) { + return; + } + elOptionsToUpdate.push(newElOption); + setKeyInfoToNewElOption(resultItem, newElOption); + mergeNewElOptionToExist(existList, index, newElOption); + setLayoutInfoToExist(existList[index], newElOption); + }, this); + // Clean + thisOption.elements = filter(existList, function (item) { + // $action should be volatile, otherwise option gotten from + // `getOption` will contain unexpected $action. + item && delete item.$action; + return item != null; + }); + }; + /** + * Convert + * [{ + * type: 'group', + * id: 'xx', + * children: [{type: 'circle'}, {type: 'polygon'}] + * }] + * to + * [ + * {type: 'group', id: 'xx'}, + * {type: 'circle', parentId: 'xx'}, + * {type: 'polygon', parentId: 'xx'} + * ] + */ + GraphicComponentModel.prototype._flatten = function (optionList, result, parentOption) { + each(optionList, function (option) { + if (!option) { + return; + } + if (parentOption) { + option.parentOption = parentOption; + } + result.push(option); + var children = option.children; + // here we don't judge if option.type is `group` + // when new option doesn't provide `type`, it will cause that the children can't be updated. + if (children && children.length) { + this._flatten(children, result, option); + } + // Deleting for JSON output, and for not affecting group creation. + delete option.children; + }, this); + }; + // FIXME + // Pass to view using payload? setOption has a payload? + GraphicComponentModel.prototype.useElOptionsToUpdate = function () { + var els = this._elOptionsToUpdate; + // Clear to avoid render duplicately when zooming. + this._elOptionsToUpdate = null; + return els; + }; + GraphicComponentModel.type = 'graphic'; + GraphicComponentModel.defaultOption = { + elements: [] + // parentId: null + }; + + return GraphicComponentModel; + }(ComponentModel); + + var nonShapeGraphicElements = { + // Reserved but not supported in graphic component. + path: null, + compoundPath: null, + // Supported in graphic component. + group: Group, + image: ZRImage, + text: ZRText + }; + var inner$e = makeInner(); + // ------------------------ + // View + // ------------------------ + var GraphicComponentView = /** @class */function (_super) { + __extends(GraphicComponentView, _super); + function GraphicComponentView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = GraphicComponentView.type; + return _this; + } + GraphicComponentView.prototype.init = function () { + this._elMap = createHashMap(); + }; + GraphicComponentView.prototype.render = function (graphicModel, ecModel, api) { + // Having leveraged between use cases and algorithm complexity, a very + // simple layout mechanism is used: + // The size(width/height) can be determined by itself or its parent (not + // implemented yet), but can not by its children. (Top-down travel) + // The location(x/y) can be determined by the bounding rect of itself + // (can including its descendants or not) and the size of its parent. + // (Bottom-up travel) + // When `chart.clear()` or `chart.setOption({...}, true)` with the same id, + // view will be reused. + if (graphicModel !== this._lastGraphicModel) { + this._clear(); + } + this._lastGraphicModel = graphicModel; + this._updateElements(graphicModel); + this._relocate(graphicModel, api); + }; + /** + * Update graphic elements. + */ + GraphicComponentView.prototype._updateElements = function (graphicModel) { + var elOptionsToUpdate = graphicModel.useElOptionsToUpdate(); + if (!elOptionsToUpdate) { + return; + } + var elMap = this._elMap; + var rootGroup = this.group; + var globalZ = graphicModel.get('z'); + var globalZLevel = graphicModel.get('zlevel'); + // Top-down tranverse to assign graphic settings to each elements. + each(elOptionsToUpdate, function (elOption) { + var id = convertOptionIdName(elOption.id, null); + var elExisting = id != null ? elMap.get(id) : null; + var parentId = convertOptionIdName(elOption.parentId, null); + var targetElParent = parentId != null ? elMap.get(parentId) : rootGroup; + var elType = elOption.type; + var elOptionStyle = elOption.style; + if (elType === 'text' && elOptionStyle) { + // In top/bottom mode, textVerticalAlign should not be used, which cause + // inaccurately locating. + if (elOption.hv && elOption.hv[1]) { + elOptionStyle.textVerticalAlign = elOptionStyle.textBaseline = elOptionStyle.verticalAlign = elOptionStyle.align = null; + } + } + var textContentOption = elOption.textContent; + var textConfig = elOption.textConfig; + if (elOptionStyle && isEC4CompatibleStyle(elOptionStyle, elType, !!textConfig, !!textContentOption)) { + var convertResult = convertFromEC4CompatibleStyle(elOptionStyle, elType, true); + if (!textConfig && convertResult.textConfig) { + textConfig = elOption.textConfig = convertResult.textConfig; + } + if (!textContentOption && convertResult.textContent) { + textContentOption = convertResult.textContent; + } + } + // Remove unnecessary props to avoid potential problems. + var elOptionCleaned = getCleanedElOption(elOption); + // For simple, do not support parent change, otherwise reorder is needed. + if ("development" !== 'production') { + elExisting && assert(targetElParent === elExisting.parent, 'Changing parent is not supported.'); + } + var $action = elOption.$action || 'merge'; + var isMerge = $action === 'merge'; + var isReplace = $action === 'replace'; + if (isMerge) { + var isInit = !elExisting; + var el_1 = elExisting; + if (isInit) { + el_1 = createEl$1(id, targetElParent, elOption.type, elMap); + } else { + el_1 && (inner$e(el_1).isNew = false); + // Stop and restore before update any other attributes. + stopPreviousKeyframeAnimationAndRestore(el_1); + } + if (el_1) { + applyUpdateTransition(el_1, elOptionCleaned, graphicModel, { + isInit: isInit + }); + updateCommonAttrs(el_1, elOption, globalZ, globalZLevel); + } + } else if (isReplace) { + removeEl(elExisting, elOption, elMap, graphicModel); + var el_2 = createEl$1(id, targetElParent, elOption.type, elMap); + if (el_2) { + applyUpdateTransition(el_2, elOptionCleaned, graphicModel, { + isInit: true + }); + updateCommonAttrs(el_2, elOption, globalZ, globalZLevel); + } + } else if ($action === 'remove') { + updateLeaveTo(elExisting, elOption); + removeEl(elExisting, elOption, elMap, graphicModel); + } + var el = elMap.get(id); + if (el && textContentOption) { + if (isMerge) { + var textContentExisting = el.getTextContent(); + textContentExisting ? textContentExisting.attr(textContentOption) : el.setTextContent(new ZRText(textContentOption)); + } else if (isReplace) { + el.setTextContent(new ZRText(textContentOption)); + } + } + if (el) { + var clipPathOption = elOption.clipPath; + if (clipPathOption) { + var clipPathType = clipPathOption.type; + var clipPath = void 0; + var isInit = false; + if (isMerge) { + var oldClipPath = el.getClipPath(); + isInit = !oldClipPath || inner$e(oldClipPath).type !== clipPathType; + clipPath = isInit ? newEl(clipPathType) : oldClipPath; + } else if (isReplace) { + isInit = true; + clipPath = newEl(clipPathType); + } + el.setClipPath(clipPath); + applyUpdateTransition(clipPath, clipPathOption, graphicModel, { + isInit: isInit + }); + applyKeyframeAnimation(clipPath, clipPathOption.keyframeAnimation, graphicModel); + } + var elInner = inner$e(el); + el.setTextConfig(textConfig); + elInner.option = elOption; + setEventData(el, graphicModel, elOption); + setTooltipConfig({ + el: el, + componentModel: graphicModel, + itemName: el.name, + itemTooltipOption: elOption.tooltip + }); + applyKeyframeAnimation(el, elOption.keyframeAnimation, graphicModel); + } + }); + }; + /** + * Locate graphic elements. + */ + GraphicComponentView.prototype._relocate = function (graphicModel, api) { + var elOptions = graphicModel.option.elements; + var rootGroup = this.group; + var elMap = this._elMap; + var apiWidth = api.getWidth(); + var apiHeight = api.getHeight(); + var xy = ['x', 'y']; + // Top-down to calculate percentage width/height of group + for (var i = 0; i < elOptions.length; i++) { + var elOption = elOptions[i]; + var id = convertOptionIdName(elOption.id, null); + var el = id != null ? elMap.get(id) : null; + if (!el || !el.isGroup) { + continue; + } + var parentEl = el.parent; + var isParentRoot = parentEl === rootGroup; + // Like 'position:absolut' in css, default 0. + var elInner = inner$e(el); + var parentElInner = inner$e(parentEl); + elInner.width = parsePercent$1(elInner.option.width, isParentRoot ? apiWidth : parentElInner.width) || 0; + elInner.height = parsePercent$1(elInner.option.height, isParentRoot ? apiHeight : parentElInner.height) || 0; + } + // Bottom-up tranvese all elements (consider ec resize) to locate elements. + for (var i = elOptions.length - 1; i >= 0; i--) { + var elOption = elOptions[i]; + var id = convertOptionIdName(elOption.id, null); + var el = id != null ? elMap.get(id) : null; + if (!el) { + continue; + } + var parentEl = el.parent; + var parentElInner = inner$e(parentEl); + var containerInfo = parentEl === rootGroup ? { + width: apiWidth, + height: apiHeight + } : { + width: parentElInner.width, + height: parentElInner.height + }; + // PENDING + // Currently, when `bounding: 'all'`, the union bounding rect of the group + // does not include the rect of [0, 0, group.width, group.height], which + // is probably weird for users. Should we make a break change for it? + var layoutPos = {}; + var layouted = positionElement(el, elOption, containerInfo, null, { + hv: elOption.hv, + boundingMode: elOption.bounding + }, layoutPos); + if (!inner$e(el).isNew && layouted) { + var transition = elOption.transition; + var animatePos = {}; + for (var k = 0; k < xy.length; k++) { + var key = xy[k]; + var val = layoutPos[key]; + if (transition && (isTransitionAll(transition) || indexOf(transition, key) >= 0)) { + animatePos[key] = val; + } else { + el[key] = val; + } + } + updateProps(el, animatePos, graphicModel, 0); + } else { + el.attr(layoutPos); + } + } + }; + /** + * Clear all elements. + */ + GraphicComponentView.prototype._clear = function () { + var _this = this; + var elMap = this._elMap; + elMap.each(function (el) { + removeEl(el, inner$e(el).option, elMap, _this._lastGraphicModel); + }); + this._elMap = createHashMap(); + }; + GraphicComponentView.prototype.dispose = function () { + this._clear(); + }; + GraphicComponentView.type = 'graphic'; + return GraphicComponentView; + }(ComponentView); + function newEl(graphicType) { + if ("development" !== 'production') { + assert(graphicType, 'graphic type MUST be set'); + } + var Clz = hasOwn(nonShapeGraphicElements, graphicType) + // Those graphic elements are not shapes. They should not be + // overwritten by users, so do them first. + ? nonShapeGraphicElements[graphicType] : getShapeClass(graphicType); + if ("development" !== 'production') { + assert(Clz, "graphic type " + graphicType + " can not be found"); + } + var el = new Clz({}); + inner$e(el).type = graphicType; + return el; + } + function createEl$1(id, targetElParent, graphicType, elMap) { + var el = newEl(graphicType); + targetElParent.add(el); + elMap.set(id, el); + inner$e(el).id = id; + inner$e(el).isNew = true; + return el; + } + function removeEl(elExisting, elOption, elMap, graphicModel) { + var existElParent = elExisting && elExisting.parent; + if (existElParent) { + elExisting.type === 'group' && elExisting.traverse(function (el) { + removeEl(el, elOption, elMap, graphicModel); + }); + applyLeaveTransition(elExisting, elOption, graphicModel); + elMap.removeKey(inner$e(elExisting).id); + } + } + function updateCommonAttrs(el, elOption, defaultZ, defaultZlevel) { + if (!el.isGroup) { + each([['cursor', Displayable.prototype.cursor], + // We should not support configure z and zlevel in the element level. + // But seems we didn't limit it previously. So here still use it to avoid breaking. + ['zlevel', defaultZlevel || 0], ['z', defaultZ || 0], + // z2 must not be null/undefined, otherwise sort error may occur. + ['z2', 0]], function (item) { + var prop = item[0]; + if (hasOwn(elOption, prop)) { + el[prop] = retrieve2(elOption[prop], item[1]); + } else if (el[prop] == null) { + el[prop] = item[1]; + } + }); + } + each(keys(elOption), function (key) { + // Assign event handlers. + // PENDING: should enumerate all event names or use pattern matching? + if (key.indexOf('on') === 0) { + var val = elOption[key]; + el[key] = isFunction(val) ? val : null; + } + }); + if (hasOwn(elOption, 'draggable')) { + el.draggable = elOption.draggable; + } + // Other attributes + elOption.name != null && (el.name = elOption.name); + elOption.id != null && (el.id = elOption.id); + } + // Remove unnecessary props to avoid potential problems. + function getCleanedElOption(elOption) { + elOption = extend({}, elOption); + each(['id', 'parentId', '$action', 'hv', 'bounding', 'textContent', 'clipPath'].concat(LOCATION_PARAMS), function (name) { + delete elOption[name]; + }); + return elOption; + } + function setEventData(el, graphicModel, elOption) { + var eventData = getECData(el).eventData; + // Simple optimize for large amount of elements that no need event. + if (!el.silent && !el.ignore && !eventData) { + eventData = getECData(el).eventData = { + componentType: 'graphic', + componentIndex: graphicModel.componentIndex, + name: el.name + }; + } + // `elOption.info` enables user to mount some info on + // elements and use them in event handlers. + if (eventData) { + eventData.info = elOption.info; + } + } + + function install$x(registers) { + registers.registerComponentModel(GraphicComponentModel); + registers.registerComponentView(GraphicComponentView); + registers.registerPreprocessor(function (option) { + var graphicOption = option.graphic; + // Convert + // {graphic: [{left: 10, type: 'circle'}, ...]} + // or + // {graphic: {left: 10, type: 'circle'}} + // to + // {graphic: [{elements: [{left: 10, type: 'circle'}, ...]}]} + if (isArray(graphicOption)) { + if (!graphicOption[0] || !graphicOption[0].elements) { + option.graphic = [{ + elements: graphicOption + }]; + } else { + // Only one graphic instance can be instantiated. (We don't + // want that too many views are created in echarts._viewMap.) + option.graphic = [option.graphic[0]]; + } + } else if (graphicOption && !graphicOption.elements) { + option.graphic = [{ + elements: [graphicOption] + }]; + } + }); + } + + var DATA_ZOOM_AXIS_DIMENSIONS = ['x', 'y', 'radius', 'angle', 'single']; + // Supported coords. + // FIXME: polar has been broken (but rarely used). + var SERIES_COORDS = ['cartesian2d', 'polar', 'singleAxis']; + function isCoordSupported(seriesModel) { + var coordType = seriesModel.get('coordinateSystem'); + return indexOf(SERIES_COORDS, coordType) >= 0; + } + function getAxisMainType(axisDim) { + if ("development" !== 'production') { + assert(axisDim); + } + return axisDim + 'Axis'; + } + /** + * If two dataZoomModels has the same axis controlled, we say that they are 'linked'. + * This function finds all linked dataZoomModels start from the given payload. + */ + function findEffectedDataZooms(ecModel, payload) { + // Key: `DataZoomAxisDimension` + var axisRecords = createHashMap(); + var effectedModels = []; + // Key: uid of dataZoomModel + var effectedModelMap = createHashMap(); + // Find the dataZooms specified by payload. + ecModel.eachComponent({ + mainType: 'dataZoom', + query: payload + }, function (dataZoomModel) { + if (!effectedModelMap.get(dataZoomModel.uid)) { + addToEffected(dataZoomModel); + } + }); + // Start from the given dataZoomModels, travel the graph to find + // all of the linked dataZoom models. + var foundNewLink; + do { + foundNewLink = false; + ecModel.eachComponent('dataZoom', processSingle); + } while (foundNewLink); + function processSingle(dataZoomModel) { + if (!effectedModelMap.get(dataZoomModel.uid) && isLinked(dataZoomModel)) { + addToEffected(dataZoomModel); + foundNewLink = true; + } + } + function addToEffected(dataZoom) { + effectedModelMap.set(dataZoom.uid, true); + effectedModels.push(dataZoom); + markAxisControlled(dataZoom); + } + function isLinked(dataZoomModel) { + var isLink = false; + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + var axisIdxArr = axisRecords.get(axisDim); + if (axisIdxArr && axisIdxArr[axisIndex]) { + isLink = true; + } + }); + return isLink; + } + function markAxisControlled(dataZoomModel) { + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + (axisRecords.get(axisDim) || axisRecords.set(axisDim, []))[axisIndex] = true; + }); + } + return effectedModels; + } + /** + * Find the first target coordinate system. + * Available after model built. + * + * @return Like { + * grid: [ + * {model: coord0, axisModels: [axis1, axis3], coordIndex: 1}, + * {model: coord1, axisModels: [axis0, axis2], coordIndex: 0}, + * ... + * ], // cartesians must not be null/undefined. + * polar: [ + * {model: coord0, axisModels: [axis4], coordIndex: 0}, + * ... + * ], // polars must not be null/undefined. + * singleAxis: [ + * {model: coord0, axisModels: [], coordIndex: 0} + * ] + * } + */ + function collectReferCoordSysModelInfo(dataZoomModel) { + var ecModel = dataZoomModel.ecModel; + var coordSysInfoWrap = { + infoList: [], + infoMap: createHashMap() + }; + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex); + if (!axisModel) { + return; + } + var coordSysModel = axisModel.getCoordSysModel(); + if (!coordSysModel) { + return; + } + var coordSysUid = coordSysModel.uid; + var coordSysInfo = coordSysInfoWrap.infoMap.get(coordSysUid); + if (!coordSysInfo) { + coordSysInfo = { + model: coordSysModel, + axisModels: [] + }; + coordSysInfoWrap.infoList.push(coordSysInfo); + coordSysInfoWrap.infoMap.set(coordSysUid, coordSysInfo); + } + coordSysInfo.axisModels.push(axisModel); + }); + return coordSysInfoWrap; + } + + var DataZoomAxisInfo = /** @class */function () { + function DataZoomAxisInfo() { + this.indexList = []; + this.indexMap = []; + } + DataZoomAxisInfo.prototype.add = function (axisCmptIdx) { + // Remove duplication. + if (!this.indexMap[axisCmptIdx]) { + this.indexList.push(axisCmptIdx); + this.indexMap[axisCmptIdx] = true; + } + }; + return DataZoomAxisInfo; + }(); + var DataZoomModel = /** @class */function (_super) { + __extends(DataZoomModel, _super); + function DataZoomModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = DataZoomModel.type; + _this._autoThrottle = true; + _this._noTarget = true; + /** + * It is `[rangeModeForMin, rangeModeForMax]`. + * The optional values for `rangeMode`: + * + `'value'` mode: the axis extent will always be determined by + * `dataZoom.startValue` and `dataZoom.endValue`, despite + * how data like and how `axis.min` and `axis.max` are. + * + `'percent'` mode: `100` represents 100% of the `[dMin, dMax]`, + * where `dMin` is `axis.min` if `axis.min` specified, otherwise `data.extent[0]`, + * and `dMax` is `axis.max` if `axis.max` specified, otherwise `data.extent[1]`. + * Axis extent will be determined by the result of the percent of `[dMin, dMax]`. + * + * For example, when users are using dynamic data (update data periodically via `setOption`), + * if in `'value`' mode, the window will be kept in a fixed value range despite how + * data are appended, while if in `'percent'` mode, whe window range will be changed alone with + * the appended data (suppose `axis.min` and `axis.max` are not specified). + */ + _this._rangePropMode = ['percent', 'percent']; + return _this; + } + DataZoomModel.prototype.init = function (option, parentModel, ecModel) { + var inputRawOption = retrieveRawOption(option); + /** + * Suppose a "main process" start at the point that model prepared (that is, + * model initialized or merged or method called in `action`). + * We should keep the `main process` idempotent, that is, given a set of values + * on `option`, we get the same result. + * + * But sometimes, values on `option` will be updated for providing users + * a "final calculated value" (`dataZoomProcessor` will do that). Those value + * should not be the base/input of the `main process`. + * + * So in that case we should save and keep the input of the `main process` + * separately, called `settledOption`. + * + * For example, consider the case: + * (Step_1) brush zoom the grid by `toolbox.dataZoom`, + * where the original input `option.startValue`, `option.endValue` are earsed by + * calculated value. + * (Step)2) click the legend to hide and show a series, + * where the new range is calculated by the earsed `startValue` and `endValue`, + * which brings incorrect result. + */ + this.settledOption = inputRawOption; + this.mergeDefaultAndTheme(option, ecModel); + this._doInit(inputRawOption); + }; + DataZoomModel.prototype.mergeOption = function (newOption) { + var inputRawOption = retrieveRawOption(newOption); + // FIX #2591 + merge(this.option, newOption, true); + merge(this.settledOption, inputRawOption, true); + this._doInit(inputRawOption); + }; + DataZoomModel.prototype._doInit = function (inputRawOption) { + var thisOption = this.option; + this._setDefaultThrottle(inputRawOption); + this._updateRangeUse(inputRawOption); + var settledOption = this.settledOption; + each([['start', 'startValue'], ['end', 'endValue']], function (names, index) { + // start/end has higher priority over startValue/endValue if they + // both set, but we should make chart.setOption({endValue: 1000}) + // effective, rather than chart.setOption({endValue: 1000, end: null}). + if (this._rangePropMode[index] === 'value') { + thisOption[names[0]] = settledOption[names[0]] = null; + } + // Otherwise do nothing and use the merge result. + }, this); + this._resetTarget(); + }; + DataZoomModel.prototype._resetTarget = function () { + var optionOrient = this.get('orient', true); + var targetAxisIndexMap = this._targetAxisInfoMap = createHashMap(); + var hasAxisSpecified = this._fillSpecifiedTargetAxis(targetAxisIndexMap); + if (hasAxisSpecified) { + this._orient = optionOrient || this._makeAutoOrientByTargetAxis(); + } else { + this._orient = optionOrient || 'horizontal'; + this._fillAutoTargetAxisByOrient(targetAxisIndexMap, this._orient); + } + this._noTarget = true; + targetAxisIndexMap.each(function (axisInfo) { + if (axisInfo.indexList.length) { + this._noTarget = false; + } + }, this); + }; + DataZoomModel.prototype._fillSpecifiedTargetAxis = function (targetAxisIndexMap) { + var hasAxisSpecified = false; + each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) { + var refering = this.getReferringComponents(getAxisMainType(axisDim), MULTIPLE_REFERRING); + // When user set axisIndex as a empty array, we think that user specify axisIndex + // but do not want use auto mode. Because empty array may be encountered when + // some error occurred. + if (!refering.specified) { + return; + } + hasAxisSpecified = true; + var axisInfo = new DataZoomAxisInfo(); + each(refering.models, function (axisModel) { + axisInfo.add(axisModel.componentIndex); + }); + targetAxisIndexMap.set(axisDim, axisInfo); + }, this); + return hasAxisSpecified; + }; + DataZoomModel.prototype._fillAutoTargetAxisByOrient = function (targetAxisIndexMap, orient) { + var ecModel = this.ecModel; + var needAuto = true; + // Find axis that parallel to dataZoom as default. + if (needAuto) { + var axisDim = orient === 'vertical' ? 'y' : 'x'; + var axisModels = ecModel.findComponents({ + mainType: axisDim + 'Axis' + }); + setParallelAxis(axisModels, axisDim); + } + // Find axis that parallel to dataZoom as default. + if (needAuto) { + var axisModels = ecModel.findComponents({ + mainType: 'singleAxis', + filter: function (axisModel) { + return axisModel.get('orient', true) === orient; + } + }); + setParallelAxis(axisModels, 'single'); + } + function setParallelAxis(axisModels, axisDim) { + // At least use the first parallel axis as the target axis. + var axisModel = axisModels[0]; + if (!axisModel) { + return; + } + var axisInfo = new DataZoomAxisInfo(); + axisInfo.add(axisModel.componentIndex); + targetAxisIndexMap.set(axisDim, axisInfo); + needAuto = false; + // Find parallel axes in the same grid. + if (axisDim === 'x' || axisDim === 'y') { + var gridModel_1 = axisModel.getReferringComponents('grid', SINGLE_REFERRING).models[0]; + gridModel_1 && each(axisModels, function (axModel) { + if (axisModel.componentIndex !== axModel.componentIndex && gridModel_1 === axModel.getReferringComponents('grid', SINGLE_REFERRING).models[0]) { + axisInfo.add(axModel.componentIndex); + } + }); + } + } + if (needAuto) { + // If no parallel axis, find the first category axis as default. (Also consider polar). + each(DATA_ZOOM_AXIS_DIMENSIONS, function (axisDim) { + if (!needAuto) { + return; + } + var axisModels = ecModel.findComponents({ + mainType: getAxisMainType(axisDim), + filter: function (axisModel) { + return axisModel.get('type', true) === 'category'; + } + }); + if (axisModels[0]) { + var axisInfo = new DataZoomAxisInfo(); + axisInfo.add(axisModels[0].componentIndex); + targetAxisIndexMap.set(axisDim, axisInfo); + needAuto = false; + } + }, this); + } + }; + DataZoomModel.prototype._makeAutoOrientByTargetAxis = function () { + var dim; + // Find the first axis + this.eachTargetAxis(function (axisDim) { + !dim && (dim = axisDim); + }, this); + return dim === 'y' ? 'vertical' : 'horizontal'; + }; + DataZoomModel.prototype._setDefaultThrottle = function (inputRawOption) { + // When first time user set throttle, auto throttle ends. + if (inputRawOption.hasOwnProperty('throttle')) { + this._autoThrottle = false; + } + if (this._autoThrottle) { + var globalOption = this.ecModel.option; + this.option.throttle = globalOption.animation && globalOption.animationDurationUpdate > 0 ? 100 : 20; + } + }; + DataZoomModel.prototype._updateRangeUse = function (inputRawOption) { + var rangePropMode = this._rangePropMode; + var rangeModeInOption = this.get('rangeMode'); + each([['start', 'startValue'], ['end', 'endValue']], function (names, index) { + var percentSpecified = inputRawOption[names[0]] != null; + var valueSpecified = inputRawOption[names[1]] != null; + if (percentSpecified && !valueSpecified) { + rangePropMode[index] = 'percent'; + } else if (!percentSpecified && valueSpecified) { + rangePropMode[index] = 'value'; + } else if (rangeModeInOption) { + rangePropMode[index] = rangeModeInOption[index]; + } else if (percentSpecified) { + // percentSpecified && valueSpecified + rangePropMode[index] = 'percent'; + } + // else remain its original setting. + }); + }; + + DataZoomModel.prototype.noTarget = function () { + return this._noTarget; + }; + DataZoomModel.prototype.getFirstTargetAxisModel = function () { + var firstAxisModel; + this.eachTargetAxis(function (axisDim, axisIndex) { + if (firstAxisModel == null) { + firstAxisModel = this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex); + } + }, this); + return firstAxisModel; + }; + /** + * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel + */ + DataZoomModel.prototype.eachTargetAxis = function (callback, context) { + this._targetAxisInfoMap.each(function (axisInfo, axisDim) { + each(axisInfo.indexList, function (axisIndex) { + callback.call(context, axisDim, axisIndex); + }); + }); + }; + /** + * @return If not found, return null/undefined. + */ + DataZoomModel.prototype.getAxisProxy = function (axisDim, axisIndex) { + var axisModel = this.getAxisModel(axisDim, axisIndex); + if (axisModel) { + return axisModel.__dzAxisProxy; + } + }; + /** + * @return If not found, return null/undefined. + */ + DataZoomModel.prototype.getAxisModel = function (axisDim, axisIndex) { + if ("development" !== 'production') { + assert(axisDim && axisIndex != null); + } + var axisInfo = this._targetAxisInfoMap.get(axisDim); + if (axisInfo && axisInfo.indexMap[axisIndex]) { + return this.ecModel.getComponent(getAxisMainType(axisDim), axisIndex); + } + }; + /** + * If not specified, set to undefined. + */ + DataZoomModel.prototype.setRawRange = function (opt) { + var thisOption = this.option; + var settledOption = this.settledOption; + each([['start', 'startValue'], ['end', 'endValue']], function (names) { + // Consider the pair : + // If one has value and the other one is `null/undefined`, we both set them + // to `settledOption`. This strategy enables the feature to clear the original + // value in `settledOption` to `null/undefined`. + // But if both of them are `null/undefined`, we do not set them to `settledOption` + // and keep `settledOption` with the original value. This strategy enables users to + // only set but not set when calling + // `dispatchAction`. + // The pair is treated in the same way. + if (opt[names[0]] != null || opt[names[1]] != null) { + thisOption[names[0]] = settledOption[names[0]] = opt[names[0]]; + thisOption[names[1]] = settledOption[names[1]] = opt[names[1]]; + } + }, this); + this._updateRangeUse(opt); + }; + DataZoomModel.prototype.setCalculatedRange = function (opt) { + var option = this.option; + each(['start', 'startValue', 'end', 'endValue'], function (name) { + option[name] = opt[name]; + }); + }; + DataZoomModel.prototype.getPercentRange = function () { + var axisProxy = this.findRepresentativeAxisProxy(); + if (axisProxy) { + return axisProxy.getDataPercentWindow(); + } + }; + /** + * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0); + * + * @return [startValue, endValue] value can only be '-' or finite number. + */ + DataZoomModel.prototype.getValueRange = function (axisDim, axisIndex) { + if (axisDim == null && axisIndex == null) { + var axisProxy = this.findRepresentativeAxisProxy(); + if (axisProxy) { + return axisProxy.getDataValueWindow(); + } + } else { + return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow(); + } + }; + /** + * @param axisModel If axisModel given, find axisProxy + * corresponding to the axisModel + */ + DataZoomModel.prototype.findRepresentativeAxisProxy = function (axisModel) { + if (axisModel) { + return axisModel.__dzAxisProxy; + } + // Find the first hosted axisProxy + var firstProxy; + var axisDimList = this._targetAxisInfoMap.keys(); + for (var i = 0; i < axisDimList.length; i++) { + var axisDim = axisDimList[i]; + var axisInfo = this._targetAxisInfoMap.get(axisDim); + for (var j = 0; j < axisInfo.indexList.length; j++) { + var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]); + if (proxy.hostedBy(this)) { + return proxy; + } + if (!firstProxy) { + firstProxy = proxy; + } + } + } + // If no hosted proxy found, still need to return a proxy. + // This case always happens in toolbox dataZoom, where axes are all hosted by + // other dataZooms. + return firstProxy; + }; + DataZoomModel.prototype.getRangePropMode = function () { + return this._rangePropMode.slice(); + }; + DataZoomModel.prototype.getOrient = function () { + if ("development" !== 'production') { + // Should not be called before initialized. + assert(this._orient); + } + return this._orient; + }; + DataZoomModel.type = 'dataZoom'; + DataZoomModel.dependencies = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'series', 'toolbox']; + DataZoomModel.defaultOption = { + // zlevel: 0, + z: 4, + filterMode: 'filter', + start: 0, + end: 100 + }; + return DataZoomModel; + }(ComponentModel); + /** + * Retrieve those raw params from option, which will be cached separately, + * because they will be overwritten by normalized/calculated values in the main + * process. + */ + function retrieveRawOption(option) { + var ret = {}; + each(['start', 'end', 'startValue', 'endValue', 'throttle'], function (name) { + option.hasOwnProperty(name) && (ret[name] = option[name]); + }); + return ret; + } + + var SelectDataZoomModel = /** @class */function (_super) { + __extends(SelectDataZoomModel, _super); + function SelectDataZoomModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SelectDataZoomModel.type; + return _this; + } + SelectDataZoomModel.type = 'dataZoom.select'; + return SelectDataZoomModel; + }(DataZoomModel); + + var DataZoomView = /** @class */function (_super) { + __extends(DataZoomView, _super); + function DataZoomView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = DataZoomView.type; + return _this; + } + DataZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) { + this.dataZoomModel = dataZoomModel; + this.ecModel = ecModel; + this.api = api; + }; + DataZoomView.type = 'dataZoom'; + return DataZoomView; + }(ComponentView); + + var SelectDataZoomView = /** @class */function (_super) { + __extends(SelectDataZoomView, _super); + function SelectDataZoomView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SelectDataZoomView.type; + return _this; + } + SelectDataZoomView.type = 'dataZoom.select'; + return SelectDataZoomView; + }(DataZoomView); + + var each$8 = each; + var asc$1 = asc; + /** + * Operate single axis. + * One axis can only operated by one axis operator. + * Different dataZoomModels may be defined to operate the same axis. + * (i.e. 'inside' data zoom and 'slider' data zoom components) + * So dataZoomModels share one axisProxy in that case. + */ + var AxisProxy = /** @class */function () { + function AxisProxy(dimName, axisIndex, dataZoomModel, ecModel) { + this._dimName = dimName; + this._axisIndex = axisIndex; + this.ecModel = ecModel; + this._dataZoomModel = dataZoomModel; + // /** + // * @readOnly + // * @private + // */ + // this.hasSeriesStacked; + } + /** + * Whether the axisProxy is hosted by dataZoomModel. + */ + AxisProxy.prototype.hostedBy = function (dataZoomModel) { + return this._dataZoomModel === dataZoomModel; + }; + /** + * @return Value can only be NaN or finite value. + */ + AxisProxy.prototype.getDataValueWindow = function () { + return this._valueWindow.slice(); + }; + /** + * @return {Array.} + */ + AxisProxy.prototype.getDataPercentWindow = function () { + return this._percentWindow.slice(); + }; + AxisProxy.prototype.getTargetSeriesModels = function () { + var seriesModels = []; + this.ecModel.eachSeries(function (seriesModel) { + if (isCoordSupported(seriesModel)) { + var axisMainType = getAxisMainType(this._dimName); + var axisModel = seriesModel.getReferringComponents(axisMainType, SINGLE_REFERRING).models[0]; + if (axisModel && this._axisIndex === axisModel.componentIndex) { + seriesModels.push(seriesModel); + } + } + }, this); + return seriesModels; + }; + AxisProxy.prototype.getAxisModel = function () { + return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex); + }; + AxisProxy.prototype.getMinMaxSpan = function () { + return clone(this._minMaxSpan); + }; + /** + * Only calculate by given range and this._dataExtent, do not change anything. + */ + AxisProxy.prototype.calculateDataWindow = function (opt) { + var dataExtent = this._dataExtent; + var axisModel = this.getAxisModel(); + var scale = axisModel.axis.scale; + var rangePropMode = this._dataZoomModel.getRangePropMode(); + var percentExtent = [0, 100]; + var percentWindow = []; + var valueWindow = []; + var hasPropModeValue; + each$8(['start', 'end'], function (prop, idx) { + var boundPercent = opt[prop]; + var boundValue = opt[prop + 'Value']; + // Notice: dataZoom is based either on `percentProp` ('start', 'end') or + // on `valueProp` ('startValue', 'endValue'). (They are based on the data extent + // but not min/max of axis, which will be calculated by data window then). + // The former one is suitable for cases that a dataZoom component controls multiple + // axes with different unit or extent, and the latter one is suitable for accurate + // zoom by pixel (e.g., in dataZoomSelect). + // we use `getRangePropMode()` to mark which prop is used. `rangePropMode` is updated + // only when setOption or dispatchAction, otherwise it remains its original value. + // (Why not only record `percentProp` and always map to `valueProp`? Because + // the map `valueProp` -> `percentProp` -> `valueProp` probably not the original + // `valueProp`. consider two axes constrolled by one dataZoom. They have different + // data extent. All of values that are overflow the `dataExtent` will be calculated + // to percent '100%'). + if (rangePropMode[idx] === 'percent') { + boundPercent == null && (boundPercent = percentExtent[idx]); + // Use scale.parse to math round for category or time axis. + boundValue = scale.parse(linearMap(boundPercent, percentExtent, dataExtent)); + } else { + hasPropModeValue = true; + boundValue = boundValue == null ? dataExtent[idx] : scale.parse(boundValue); + // Calculating `percent` from `value` may be not accurate, because + // This calculation can not be inversed, because all of values that + // are overflow the `dataExtent` will be calculated to percent '100%' + boundPercent = linearMap(boundValue, dataExtent, percentExtent); + } + // valueWindow[idx] = round(boundValue); + // percentWindow[idx] = round(boundPercent); + // fallback to extent start/end when parsed value or percent is invalid + valueWindow[idx] = boundValue == null || isNaN(boundValue) ? dataExtent[idx] : boundValue; + percentWindow[idx] = boundPercent == null || isNaN(boundPercent) ? percentExtent[idx] : boundPercent; + }); + asc$1(valueWindow); + asc$1(percentWindow); + // The windows from user calling of `dispatchAction` might be out of the extent, + // or do not obey the `min/maxSpan`, `min/maxValueSpan`. But we don't restrict window + // by `zoomLock` here, because we see `zoomLock` just as a interaction constraint, + // where API is able to initialize/modify the window size even though `zoomLock` + // specified. + var spans = this._minMaxSpan; + hasPropModeValue ? restrictSet(valueWindow, percentWindow, dataExtent, percentExtent, false) : restrictSet(percentWindow, valueWindow, percentExtent, dataExtent, true); + function restrictSet(fromWindow, toWindow, fromExtent, toExtent, toValue) { + var suffix = toValue ? 'Span' : 'ValueSpan'; + sliderMove(0, fromWindow, fromExtent, 'all', spans['min' + suffix], spans['max' + suffix]); + for (var i = 0; i < 2; i++) { + toWindow[i] = linearMap(fromWindow[i], fromExtent, toExtent, true); + toValue && (toWindow[i] = scale.parse(toWindow[i])); + } + } + return { + valueWindow: valueWindow, + percentWindow: percentWindow + }; + }; + /** + * Notice: reset should not be called before series.restoreData() is called, + * so it is recommended to be called in "process stage" but not "model init + * stage". + */ + AxisProxy.prototype.reset = function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + var targetSeries = this.getTargetSeriesModels(); + // Culculate data window and data extent, and record them. + this._dataExtent = calculateDataExtent(this, this._dimName, targetSeries); + // `calculateDataWindow` uses min/maxSpan. + this._updateMinMaxSpan(); + var dataWindow = this.calculateDataWindow(dataZoomModel.settledOption); + this._valueWindow = dataWindow.valueWindow; + this._percentWindow = dataWindow.percentWindow; + // Update axis setting then. + this._setAxisModel(); + }; + AxisProxy.prototype.filterData = function (dataZoomModel, api) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + var axisDim = this._dimName; + var seriesModels = this.getTargetSeriesModels(); + var filterMode = dataZoomModel.get('filterMode'); + var valueWindow = this._valueWindow; + if (filterMode === 'none') { + return; + } + // FIXME + // Toolbox may has dataZoom injected. And if there are stacked bar chart + // with NaN data, NaN will be filtered and stack will be wrong. + // So we need to force the mode to be set empty. + // In fect, it is not a big deal that do not support filterMode-'filter' + // when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis + // selection" some day, which might need "adapt to data extent on the + // otherAxis", which is disabled by filterMode-'empty'. + // But currently, stack has been fixed to based on value but not index, + // so this is not an issue any more. + // let otherAxisModel = this.getOtherAxisModel(); + // if (dataZoomModel.get('$fromToolbox') + // && otherAxisModel + // && otherAxisModel.hasSeriesStacked + // ) { + // filterMode = 'empty'; + // } + // TODO + // filterMode 'weakFilter' and 'empty' is not optimized for huge data yet. + each$8(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + var dataDims = seriesData.mapDimensionsAll(axisDim); + if (!dataDims.length) { + return; + } + if (filterMode === 'weakFilter') { + var store_1 = seriesData.getStore(); + var dataDimIndices_1 = map(dataDims, function (dim) { + return seriesData.getDimensionIndex(dim); + }, seriesData); + seriesData.filterSelf(function (dataIndex) { + var leftOut; + var rightOut; + var hasValue; + for (var i = 0; i < dataDims.length; i++) { + var value = store_1.get(dataDimIndices_1[i], dataIndex); + var thisHasValue = !isNaN(value); + var thisLeftOut = value < valueWindow[0]; + var thisRightOut = value > valueWindow[1]; + if (thisHasValue && !thisLeftOut && !thisRightOut) { + return true; + } + thisHasValue && (hasValue = true); + thisLeftOut && (leftOut = true); + thisRightOut && (rightOut = true); + } + // If both left out and right out, do not filter. + return hasValue && leftOut && rightOut; + }); + } else { + each$8(dataDims, function (dim) { + if (filterMode === 'empty') { + seriesModel.setData(seriesData = seriesData.map(dim, function (value) { + return !isInWindow(value) ? NaN : value; + })); + } else { + var range = {}; + range[dim] = valueWindow; + // console.time('select'); + seriesData.selectRange(range); + // console.timeEnd('select'); + } + }); + } + + each$8(dataDims, function (dim) { + seriesData.setApproximateExtent(valueWindow, dim); + }); + }); + function isInWindow(value) { + return value >= valueWindow[0] && value <= valueWindow[1]; + } + }; + AxisProxy.prototype._updateMinMaxSpan = function () { + var minMaxSpan = this._minMaxSpan = {}; + var dataZoomModel = this._dataZoomModel; + var dataExtent = this._dataExtent; + each$8(['min', 'max'], function (minMax) { + var percentSpan = dataZoomModel.get(minMax + 'Span'); + var valueSpan = dataZoomModel.get(minMax + 'ValueSpan'); + valueSpan != null && (valueSpan = this.getAxisModel().axis.scale.parse(valueSpan)); + // minValueSpan and maxValueSpan has higher priority than minSpan and maxSpan + if (valueSpan != null) { + percentSpan = linearMap(dataExtent[0] + valueSpan, dataExtent, [0, 100], true); + } else if (percentSpan != null) { + valueSpan = linearMap(percentSpan, [0, 100], dataExtent, true) - dataExtent[0]; + } + minMaxSpan[minMax + 'Span'] = percentSpan; + minMaxSpan[minMax + 'ValueSpan'] = valueSpan; + }, this); + }; + AxisProxy.prototype._setAxisModel = function () { + var axisModel = this.getAxisModel(); + var percentWindow = this._percentWindow; + var valueWindow = this._valueWindow; + if (!percentWindow) { + return; + } + // [0, 500]: arbitrary value, guess axis extent. + var precision = getPixelPrecision(valueWindow, [0, 500]); + precision = Math.min(precision, 20); + // For value axis, if min/max/scale are not set, we just use the extent obtained + // by series data, which may be a little different from the extent calculated by + // `axisHelper.getScaleExtent`. But the different just affects the experience a + // little when zooming. So it will not be fixed until some users require it strongly. + var rawExtentInfo = axisModel.axis.scale.rawExtentInfo; + if (percentWindow[0] !== 0) { + rawExtentInfo.setDeterminedMinMax('min', +valueWindow[0].toFixed(precision)); + } + if (percentWindow[1] !== 100) { + rawExtentInfo.setDeterminedMinMax('max', +valueWindow[1].toFixed(precision)); + } + rawExtentInfo.freeze(); + }; + return AxisProxy; + }(); + function calculateDataExtent(axisProxy, axisDim, seriesModels) { + var dataExtent = [Infinity, -Infinity]; + each$8(seriesModels, function (seriesModel) { + unionAxisExtentFromData(dataExtent, seriesModel.getData(), axisDim); + }); + // It is important to get "consistent" extent when more then one axes is + // controlled by a `dataZoom`, otherwise those axes will not be synchronized + // when zooming. But it is difficult to know what is "consistent", considering + // axes have different type or even different meanings (For example, two + // time axes are used to compare data of the same date in different years). + // So basically dataZoom just obtains extent by series.data (in category axis + // extent can be obtained from axis.data). + // Nevertheless, user can set min/max/scale on axes to make extent of axes + // consistent. + var axisModel = axisProxy.getAxisModel(); + var rawExtentResult = ensureScaleRawExtentInfo(axisModel.axis.scale, axisModel, dataExtent).calculate(); + return [rawExtentResult.min, rawExtentResult.max]; + } + + var dataZoomProcessor = { + // `dataZoomProcessor` will only be performed in needed series. Consider if + // there is a line series and a pie series, it is better not to update the + // line series if only pie series is needed to be updated. + getTargetSeries: function (ecModel) { + function eachAxisModel(cb) { + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + var axisModel = ecModel.getComponent(getAxisMainType(axisDim), axisIndex); + cb(axisDim, axisIndex, axisModel, dataZoomModel); + }); + }); + } + // FIXME: it brings side-effect to `getTargetSeries`. + // Prepare axis proxies. + eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) { + // dispose all last axis proxy, in case that some axis are deleted. + axisModel.__dzAxisProxy = null; + }); + var proxyList = []; + eachAxisModel(function (axisDim, axisIndex, axisModel, dataZoomModel) { + // Different dataZooms may constrol the same axis. In that case, + // an axisProxy serves both of them. + if (!axisModel.__dzAxisProxy) { + // Use the first dataZoomModel as the main model of axisProxy. + axisModel.__dzAxisProxy = new AxisProxy(axisDim, axisIndex, dataZoomModel, ecModel); + proxyList.push(axisModel.__dzAxisProxy); + } + }); + var seriesModelMap = createHashMap(); + each(proxyList, function (axisProxy) { + each(axisProxy.getTargetSeriesModels(), function (seriesModel) { + seriesModelMap.set(seriesModel.uid, seriesModel); + }); + }); + return seriesModelMap; + }, + // Consider appendData, where filter should be performed. Because data process is + // in block mode currently, it is not need to worry about that the overallProgress + // execute every frame. + overallReset: function (ecModel, api) { + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + // We calculate window and reset axis here but not in model + // init stage and not after action dispatch handler, because + // reset should be called after seriesData.restoreData. + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel); + }); + // Caution: data zoom filtering is order sensitive when using + // percent range and no min/max/scale set on axis. + // For example, we have dataZoom definition: + // [ + // {xAxisIndex: 0, start: 30, end: 70}, + // {yAxisIndex: 0, start: 20, end: 80} + // ] + // In this case, [20, 80] of y-dataZoom should be based on data + // that have filtered by x-dataZoom using range of [30, 70], + // but should not be based on full raw data. Thus sliding + // x-dataZoom will change both ranges of xAxis and yAxis, + // while sliding y-dataZoom will only change the range of yAxis. + // So we should filter x-axis after reset x-axis immediately, + // and then reset y-axis and filter y-axis. + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api); + }); + }); + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + // Fullfill all of the range props so that user + // is able to get them from chart.getOption(). + var axisProxy = dataZoomModel.findRepresentativeAxisProxy(); + if (axisProxy) { + var percentRange = axisProxy.getDataPercentWindow(); + var valueRange = axisProxy.getDataValueWindow(); + dataZoomModel.setCalculatedRange({ + start: percentRange[0], + end: percentRange[1], + startValue: valueRange[0], + endValue: valueRange[1] + }); + } + }); + } + }; + + function installDataZoomAction(registers) { + registers.registerAction('dataZoom', function (payload, ecModel) { + var effectedModels = findEffectedDataZooms(ecModel, payload); + each(effectedModels, function (dataZoomModel) { + dataZoomModel.setRawRange({ + start: payload.start, + end: payload.end, + startValue: payload.startValue, + endValue: payload.endValue + }); + }); + }); + } + + var installed = false; + function installCommon(registers) { + if (installed) { + return; + } + installed = true; + registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, dataZoomProcessor); + installDataZoomAction(registers); + registers.registerSubTypeDefaulter('dataZoom', function () { + // Default 'slider' when no type specified. + return 'slider'; + }); + } + + function install$y(registers) { + registers.registerComponentModel(SelectDataZoomModel); + registers.registerComponentView(SelectDataZoomView); + installCommon(registers); + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + var ToolboxFeature = /** @class */function () { + function ToolboxFeature() {} + return ToolboxFeature; + }(); + var features = {}; + function registerFeature(name, ctor) { + features[name] = ctor; + } + function getFeature(name) { + return features[name]; + } + + var ToolboxModel = /** @class */function (_super) { + __extends(ToolboxModel, _super); + function ToolboxModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ToolboxModel.type; + return _this; + } + ToolboxModel.prototype.optionUpdated = function () { + _super.prototype.optionUpdated.apply(this, arguments); + var ecModel = this.ecModel; + each(this.option.feature, function (featureOpt, featureName) { + var Feature = getFeature(featureName); + if (Feature) { + if (Feature.getDefaultOption) { + Feature.defaultOption = Feature.getDefaultOption(ecModel); + } + merge(featureOpt, Feature.defaultOption); + } + }); + }; + ToolboxModel.type = 'toolbox'; + ToolboxModel.layoutMode = { + type: 'box', + ignoreSize: true + }; + ToolboxModel.defaultOption = { + show: true, + z: 6, + // zlevel: 0, + orient: 'horizontal', + left: 'right', + top: 'top', + // right + // bottom + backgroundColor: 'transparent', + borderColor: '#ccc', + borderRadius: 0, + borderWidth: 0, + padding: 5, + itemSize: 15, + itemGap: 8, + showTitle: true, + iconStyle: { + borderColor: '#666', + color: 'none' + }, + emphasis: { + iconStyle: { + borderColor: '#3E98C5' + } + }, + // textStyle: {}, + // feature + tooltip: { + show: false, + position: 'bottom' + } + }; + return ToolboxModel; + }(ComponentModel); + + /** + * Layout list like component. + * It will box layout each items in group of component and then position the whole group in the viewport + * @param {module:zrender/group/Group} group + * @param {module:echarts/model/Component} componentModel + * @param {module:echarts/ExtensionAPI} + */ + function layout$3(group, componentModel, api) { + var boxLayoutParams = componentModel.getBoxLayoutParams(); + var padding = componentModel.get('padding'); + var viewportSize = { + width: api.getWidth(), + height: api.getHeight() + }; + var rect = getLayoutRect(boxLayoutParams, viewportSize, padding); + box(componentModel.get('orient'), group, componentModel.get('itemGap'), rect.width, rect.height); + positionElement(group, boxLayoutParams, viewportSize, padding); + } + function makeBackground(rect, componentModel) { + var padding = normalizeCssArray$1(componentModel.get('padding')); + var style = componentModel.getItemStyle(['color', 'opacity']); + style.fill = componentModel.get('backgroundColor'); + rect = new Rect({ + shape: { + x: rect.x - padding[3], + y: rect.y - padding[0], + width: rect.width + padding[1] + padding[3], + height: rect.height + padding[0] + padding[2], + r: componentModel.get('borderRadius') + }, + style: style, + silent: true, + z2: -1 + }); + // FIXME + // `subPixelOptimizeRect` may bring some gap between edge of viewpart + // and background rect when setting like `left: 0`, `top: 0`. + // graphic.subPixelOptimizeRect(rect); + return rect; + } + + var ToolboxView = /** @class */function (_super) { + __extends(ToolboxView, _super); + function ToolboxView() { + return _super !== null && _super.apply(this, arguments) || this; + } + ToolboxView.prototype.render = function (toolboxModel, ecModel, api, payload) { + var group = this.group; + group.removeAll(); + if (!toolboxModel.get('show')) { + return; + } + var itemSize = +toolboxModel.get('itemSize'); + var isVertical = toolboxModel.get('orient') === 'vertical'; + var featureOpts = toolboxModel.get('feature') || {}; + var features = this._features || (this._features = {}); + var featureNames = []; + each(featureOpts, function (opt, name) { + featureNames.push(name); + }); + new DataDiffer(this._featureNames || [], featureNames).add(processFeature).update(processFeature).remove(curry(processFeature, null)).execute(); + // Keep for diff. + this._featureNames = featureNames; + function processFeature(newIndex, oldIndex) { + var featureName = featureNames[newIndex]; + var oldName = featureNames[oldIndex]; + var featureOpt = featureOpts[featureName]; + var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel); + var feature; + // FIX#11236, merge feature title from MagicType newOption. TODO: consider seriesIndex ? + if (payload && payload.newTitle != null && payload.featureName === featureName) { + featureOpt.title = payload.newTitle; + } + if (featureName && !oldName) { + // Create + if (isUserFeatureName(featureName)) { + feature = { + onclick: featureModel.option.onclick, + featureName: featureName + }; + } else { + var Feature = getFeature(featureName); + if (!Feature) { + return; + } + feature = new Feature(); + } + features[featureName] = feature; + } else { + feature = features[oldName]; + // If feature does not exist. + if (!feature) { + return; + } + } + feature.uid = getUID('toolbox-feature'); + feature.model = featureModel; + feature.ecModel = ecModel; + feature.api = api; + var isToolboxFeature = feature instanceof ToolboxFeature; + if (!featureName && oldName) { + isToolboxFeature && feature.dispose && feature.dispose(ecModel, api); + return; + } + if (!featureModel.get('show') || isToolboxFeature && feature.unusable) { + isToolboxFeature && feature.remove && feature.remove(ecModel, api); + return; + } + createIconPaths(featureModel, feature, featureName); + featureModel.setIconStatus = function (iconName, status) { + var option = this.option; + var iconPaths = this.iconPaths; + option.iconStatus = option.iconStatus || {}; + option.iconStatus[iconName] = status; + if (iconPaths[iconName]) { + (status === 'emphasis' ? enterEmphasis : leaveEmphasis)(iconPaths[iconName]); + } + }; + if (feature instanceof ToolboxFeature) { + if (feature.render) { + feature.render(featureModel, ecModel, api, payload); + } + } + } + function createIconPaths(featureModel, feature, featureName) { + var iconStyleModel = featureModel.getModel('iconStyle'); + var iconStyleEmphasisModel = featureModel.getModel(['emphasis', 'iconStyle']); + // If one feature has multiple icons, they are organized as + // { + // icon: { + // foo: '', + // bar: '' + // }, + // title: { + // foo: '', + // bar: '' + // } + // } + var icons = feature instanceof ToolboxFeature && feature.getIcons ? feature.getIcons() : featureModel.get('icon'); + var titles = featureModel.get('title') || {}; + var iconsMap; + var titlesMap; + if (isString(icons)) { + iconsMap = {}; + iconsMap[featureName] = icons; + } else { + iconsMap = icons; + } + if (isString(titles)) { + titlesMap = {}; + titlesMap[featureName] = titles; + } else { + titlesMap = titles; + } + var iconPaths = featureModel.iconPaths = {}; + each(iconsMap, function (iconStr, iconName) { + var path = createIcon(iconStr, {}, { + x: -itemSize / 2, + y: -itemSize / 2, + width: itemSize, + height: itemSize + }); // TODO handling image + path.setStyle(iconStyleModel.getItemStyle()); + var pathEmphasisState = path.ensureState('emphasis'); + pathEmphasisState.style = iconStyleEmphasisModel.getItemStyle(); + // Text position calculation + // TODO: extract `textStyle` from `iconStyle` and use `createTextStyle` + var textContent = new ZRText({ + style: { + text: titlesMap[iconName], + align: iconStyleEmphasisModel.get('textAlign'), + borderRadius: iconStyleEmphasisModel.get('textBorderRadius'), + padding: iconStyleEmphasisModel.get('textPadding'), + fill: null, + font: getFont({ + fontStyle: iconStyleEmphasisModel.get('textFontStyle'), + fontFamily: iconStyleEmphasisModel.get('textFontFamily'), + fontSize: iconStyleEmphasisModel.get('textFontSize'), + fontWeight: iconStyleEmphasisModel.get('textFontWeight') + }, ecModel) + }, + ignore: true + }); + path.setTextContent(textContent); + setTooltipConfig({ + el: path, + componentModel: toolboxModel, + itemName: iconName, + formatterParamsExtra: { + title: titlesMap[iconName] + } + }); + path.__title = titlesMap[iconName]; + path.on('mouseover', function () { + // Should not reuse above hoverStyle, which might be modified. + var hoverStyle = iconStyleEmphasisModel.getItemStyle(); + var defaultTextPosition = isVertical ? toolboxModel.get('right') == null && toolboxModel.get('left') !== 'right' ? 'right' : 'left' : toolboxModel.get('bottom') == null && toolboxModel.get('top') !== 'bottom' ? 'bottom' : 'top'; + textContent.setStyle({ + fill: iconStyleEmphasisModel.get('textFill') || hoverStyle.fill || hoverStyle.stroke || '#000', + backgroundColor: iconStyleEmphasisModel.get('textBackgroundColor') + }); + path.setTextConfig({ + position: iconStyleEmphasisModel.get('textPosition') || defaultTextPosition + }); + textContent.ignore = !toolboxModel.get('showTitle'); + // Use enterEmphasis and leaveEmphasis provide by ec. + // There are flags managed by the echarts. + api.enterEmphasis(this); + }).on('mouseout', function () { + if (featureModel.get(['iconStatus', iconName]) !== 'emphasis') { + api.leaveEmphasis(this); + } + textContent.hide(); + }); + (featureModel.get(['iconStatus', iconName]) === 'emphasis' ? enterEmphasis : leaveEmphasis)(path); + group.add(path); + path.on('click', bind(feature.onclick, feature, ecModel, api, iconName)); + iconPaths[iconName] = path; + }); + } + layout$3(group, toolboxModel, api); + // Render background after group is layout + // FIXME + group.add(makeBackground(group.getBoundingRect(), toolboxModel)); + // Adjust icon title positions to avoid them out of screen + isVertical || group.eachChild(function (icon) { + var titleText = icon.__title; + // const hoverStyle = icon.hoverStyle; + // TODO simplify code? + var emphasisState = icon.ensureState('emphasis'); + var emphasisTextConfig = emphasisState.textConfig || (emphasisState.textConfig = {}); + var textContent = icon.getTextContent(); + var emphasisTextState = textContent && textContent.ensureState('emphasis'); + // May be background element + if (emphasisTextState && !isFunction(emphasisTextState) && titleText) { + var emphasisTextStyle = emphasisTextState.style || (emphasisTextState.style = {}); + var rect = getBoundingRect(titleText, ZRText.makeFont(emphasisTextStyle)); + var offsetX = icon.x + group.x; + var offsetY = icon.y + group.y + itemSize; + var needPutOnTop = false; + if (offsetY + rect.height > api.getHeight()) { + emphasisTextConfig.position = 'top'; + needPutOnTop = true; + } + var topOffset = needPutOnTop ? -5 - rect.height : itemSize + 10; + if (offsetX + rect.width / 2 > api.getWidth()) { + emphasisTextConfig.position = ['100%', topOffset]; + emphasisTextStyle.align = 'right'; + } else if (offsetX - rect.width / 2 < 0) { + emphasisTextConfig.position = [0, topOffset]; + emphasisTextStyle.align = 'left'; + } + } + }); + }; + ToolboxView.prototype.updateView = function (toolboxModel, ecModel, api, payload) { + each(this._features, function (feature) { + feature instanceof ToolboxFeature && feature.updateView && feature.updateView(feature.model, ecModel, api, payload); + }); + }; + // updateLayout(toolboxModel, ecModel, api, payload) { + // zrUtil.each(this._features, function (feature) { + // feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload); + // }); + // }, + ToolboxView.prototype.remove = function (ecModel, api) { + each(this._features, function (feature) { + feature instanceof ToolboxFeature && feature.remove && feature.remove(ecModel, api); + }); + this.group.removeAll(); + }; + ToolboxView.prototype.dispose = function (ecModel, api) { + each(this._features, function (feature) { + feature instanceof ToolboxFeature && feature.dispose && feature.dispose(ecModel, api); + }); + }; + ToolboxView.type = 'toolbox'; + return ToolboxView; + }(ComponentView); + function isUserFeatureName(featureName) { + return featureName.indexOf('my') === 0; + } + + /* global window, document */ + var SaveAsImage = /** @class */function (_super) { + __extends(SaveAsImage, _super); + function SaveAsImage() { + return _super !== null && _super.apply(this, arguments) || this; + } + SaveAsImage.prototype.onclick = function (ecModel, api) { + var model = this.model; + var title = model.get('name') || ecModel.get('title.0.text') || 'echarts'; + var isSvg = api.getZr().painter.getType() === 'svg'; + var type = isSvg ? 'svg' : model.get('type', true) || 'png'; + var url = api.getConnectedDataURL({ + type: type, + backgroundColor: model.get('backgroundColor', true) || ecModel.get('backgroundColor') || '#fff', + connectedBackgroundColor: model.get('connectedBackgroundColor'), + excludeComponents: model.get('excludeComponents'), + pixelRatio: model.get('pixelRatio') + }); + var browser = env.browser; + // Chrome, Firefox, New Edge + if (isFunction(MouseEvent) && (browser.newEdge || !browser.ie && !browser.edge)) { + var $a = document.createElement('a'); + $a.download = title + '.' + type; + $a.target = '_blank'; + $a.href = url; + var evt = new MouseEvent('click', { + // some micro front-end framework, window maybe is a Proxy + view: document.defaultView, + bubbles: true, + cancelable: false + }); + $a.dispatchEvent(evt); + } + // IE or old Edge + else { + // @ts-ignore + if (window.navigator.msSaveOrOpenBlob || isSvg) { + var parts = url.split(','); + // data:[][;charset=][;base64], + var base64Encoded = parts[0].indexOf('base64') > -1; + var bstr = isSvg + // should decode the svg data uri first + ? decodeURIComponent(parts[1]) : parts[1]; + // only `atob` when the data uri is encoded with base64 + // otherwise, like `svg` data uri exported by zrender, + // there will be an error, for it's not encoded with base64. + // (just a url-encoded string through `encodeURIComponent`) + base64Encoded && (bstr = window.atob(bstr)); + var filename = title + '.' + type; + // @ts-ignore + if (window.navigator.msSaveOrOpenBlob) { + var n = bstr.length; + var u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + var blob = new Blob([u8arr]); // @ts-ignore + window.navigator.msSaveOrOpenBlob(blob, filename); + } else { + var frame = document.createElement('iframe'); + document.body.appendChild(frame); + var cw = frame.contentWindow; + var doc = cw.document; + doc.open('image/svg+xml', 'replace'); + doc.write(bstr); + doc.close(); + cw.focus(); + doc.execCommand('SaveAs', true, filename); + document.body.removeChild(frame); + } + } else { + var lang = model.get('lang'); + var html = '' + '' + '' + ''; + var tab = window.open(); + tab.document.write(html); + tab.document.title = title; + } + } + }; + SaveAsImage.getDefaultOption = function (ecModel) { + var defaultOption = { + show: true, + icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0', + title: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'title']), + type: 'png', + // Default use option.backgroundColor + // backgroundColor: '#fff', + connectedBackgroundColor: '#fff', + name: '', + excludeComponents: ['toolbox'], + // use current pixel ratio of device by default + // pixelRatio: 1, + lang: ecModel.getLocaleModel().get(['toolbox', 'saveAsImage', 'lang']) + }; + return defaultOption; + }; + return SaveAsImage; + }(ToolboxFeature); + + var INNER_STACK_KEYWORD = '__ec_magicType_stack__'; + var radioTypes = [['line', 'bar'], ['stack']]; + var MagicType = /** @class */function (_super) { + __extends(MagicType, _super); + function MagicType() { + return _super !== null && _super.apply(this, arguments) || this; + } + MagicType.prototype.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon'); + var icons = {}; + each(model.get('type'), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; + }; + MagicType.getDefaultOption = function (ecModel) { + var defaultOption = { + show: true, + type: [], + // Icon group + icon: { + line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4', + bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7', + // eslint-disable-next-line + stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z' // jshint ignore:line + }, + + // `line`, `bar`, `stack`, `tiled` + title: ecModel.getLocaleModel().get(['toolbox', 'magicType', 'title']), + option: {}, + seriesIndex: {} + }; + return defaultOption; + }; + MagicType.prototype.onclick = function (ecModel, api, type) { + var model = this.model; + var seriesIndex = model.get(['seriesIndex', type]); + // Not supported magicType + if (!seriesOptGenreator[type]) { + return; + } + var newOption = { + series: [] + }; + var generateNewSeriesTypes = function (seriesModel) { + var seriesType = seriesModel.subType; + var seriesId = seriesModel.id; + var newSeriesOpt = seriesOptGenreator[type](seriesType, seriesId, seriesModel, model); + if (newSeriesOpt) { + // PENDING If merge original option? + defaults(newSeriesOpt, seriesModel.option); + newOption.series.push(newSeriesOpt); + } + // Modify boundaryGap + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) { + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + if (categoryAxis) { + var axisDim = categoryAxis.dim; + var axisType = axisDim + 'Axis'; + var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0]; + var axisIndex = axisModel.componentIndex; + newOption[axisType] = newOption[axisType] || []; + for (var i = 0; i <= axisIndex; i++) { + newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {}; + } + newOption[axisType][axisIndex].boundaryGap = type === 'bar'; + } + } + }; + each(radioTypes, function (radio) { + if (indexOf(radio, type) >= 0) { + each(radio, function (item) { + model.setIconStatus(item, 'normal'); + }); + } + }); + model.setIconStatus(type, 'emphasis'); + ecModel.eachComponent({ + mainType: 'series', + query: seriesIndex == null ? null : { + seriesIndex: seriesIndex + } + }, generateNewSeriesTypes); + var newTitle; + var currentType = type; + // Change title of stack + if (type === 'stack') { + // use titles in model instead of ecModel + // as stack and tiled appears in pair, just flip them + // no need of checking stack state + newTitle = merge({ + stack: model.option.title.tiled, + tiled: model.option.title.stack + }, model.option.title); + if (model.get(['iconStatus', type]) !== 'emphasis') { + currentType = 'tiled'; + } + } + api.dispatchAction({ + type: 'changeMagicType', + currentType: currentType, + newOption: newOption, + newTitle: newTitle, + featureName: 'magicType' + }); + }; + return MagicType; + }(ToolboxFeature); + var seriesOptGenreator = { + 'line': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'bar') { + return merge({ + id: seriesId, + type: 'line', + // Preserve data related option + data: seriesModel.get('data'), + stack: seriesModel.get('stack'), + markPoint: seriesModel.get('markPoint'), + markLine: seriesModel.get('markLine') + }, model.get(['option', 'line']) || {}, true); + } + }, + 'bar': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line') { + return merge({ + id: seriesId, + type: 'bar', + // Preserve data related option + data: seriesModel.get('data'), + stack: seriesModel.get('stack'), + markPoint: seriesModel.get('markPoint'), + markLine: seriesModel.get('markLine') + }, model.get(['option', 'bar']) || {}, true); + } + }, + 'stack': function (seriesType, seriesId, seriesModel, model) { + var isStack = seriesModel.get('stack') === INNER_STACK_KEYWORD; + if (seriesType === 'line' || seriesType === 'bar') { + model.setIconStatus('stack', isStack ? 'normal' : 'emphasis'); + return merge({ + id: seriesId, + stack: isStack ? '' : INNER_STACK_KEYWORD + }, model.get(['option', 'stack']) || {}, true); + } + } + }; + // TODO: SELF REGISTERED. + registerAction({ + type: 'changeMagicType', + event: 'magicTypeChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + ecModel.mergeOption(payload.newOption); + }); + + /* global document */ + var BLOCK_SPLITER = new Array(60).join('-'); + var ITEM_SPLITER = '\t'; + /** + * Group series into two types + * 1. on category axis, like line, bar + * 2. others, like scatter, pie + */ + function groupSeries(ecModel) { + var seriesGroupByCategoryAxis = {}; + var otherSeries = []; + var meta = []; + ecModel.eachRawSeries(function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) { + // TODO: TYPE Consider polar? Include polar may increase unecessary bundle size. + var baseAxis = coordSys.getBaseAxis(); + if (baseAxis.type === 'category') { + var key = baseAxis.dim + '_' + baseAxis.index; + if (!seriesGroupByCategoryAxis[key]) { + seriesGroupByCategoryAxis[key] = { + categoryAxis: baseAxis, + valueAxis: coordSys.getOtherAxis(baseAxis), + series: [] + }; + meta.push({ + axisDim: baseAxis.dim, + axisIndex: baseAxis.index + }); + } + seriesGroupByCategoryAxis[key].series.push(seriesModel); + } else { + otherSeries.push(seriesModel); + } + } else { + otherSeries.push(seriesModel); + } + }); + return { + seriesGroupByCategoryAxis: seriesGroupByCategoryAxis, + other: otherSeries, + meta: meta + }; + } + /** + * Assemble content of series on cateogory axis + * @inner + */ + function assembleSeriesWithCategoryAxis(groups) { + var tables = []; + each(groups, function (group, key) { + var categoryAxis = group.categoryAxis; + var valueAxis = group.valueAxis; + var valueAxisDim = valueAxis.dim; + var headers = [' '].concat(map(group.series, function (series) { + return series.name; + })); + // @ts-ignore TODO Polar + var columns = [categoryAxis.model.getCategories()]; + each(group.series, function (series) { + var rawData = series.getRawData(); + columns.push(series.getRawData().mapArray(rawData.mapDimension(valueAxisDim), function (val) { + return val; + })); + }); + // Assemble table content + var lines = [headers.join(ITEM_SPLITER)]; + for (var i = 0; i < columns[0].length; i++) { + var items = []; + for (var j = 0; j < columns.length; j++) { + items.push(columns[j][i]); + } + lines.push(items.join(ITEM_SPLITER)); + } + tables.push(lines.join('\n')); + }); + return tables.join('\n\n' + BLOCK_SPLITER + '\n\n'); + } + /** + * Assemble content of other series + */ + function assembleOtherSeries(series) { + return map(series, function (series) { + var data = series.getRawData(); + var lines = [series.name]; + var vals = []; + data.each(data.dimensions, function () { + var argLen = arguments.length; + var dataIndex = arguments[argLen - 1]; + var name = data.getName(dataIndex); + for (var i = 0; i < argLen - 1; i++) { + vals[i] = arguments[i]; + } + lines.push((name ? name + ITEM_SPLITER : '') + vals.join(ITEM_SPLITER)); + }); + return lines.join('\n'); + }).join('\n\n' + BLOCK_SPLITER + '\n\n'); + } + function getContentFromModel(ecModel) { + var result = groupSeries(ecModel); + return { + value: filter([assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), assembleOtherSeries(result.other)], function (str) { + return !!str.replace(/[\n\t\s]/g, ''); + }).join('\n\n' + BLOCK_SPLITER + '\n\n'), + meta: result.meta + }; + } + function trim$1(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + /** + * If a block is tsv format + */ + function isTSVFormat(block) { + // Simple method to find out if a block is tsv format + var firstLine = block.slice(0, block.indexOf('\n')); + if (firstLine.indexOf(ITEM_SPLITER) >= 0) { + return true; + } + } + var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g'); + /** + * @param {string} tsv + * @return {Object} + */ + function parseTSVContents(tsv) { + var tsvLines = tsv.split(/\n+/g); + var headers = trim$1(tsvLines.shift()).split(itemSplitRegex); + var categories = []; + var series = map(headers, function (header) { + return { + name: header, + data: [] + }; + }); + for (var i = 0; i < tsvLines.length; i++) { + var items = trim$1(tsvLines[i]).split(itemSplitRegex); + categories.push(items.shift()); + for (var j = 0; j < items.length; j++) { + series[j] && (series[j].data[i] = items[j]); + } + } + return { + series: series, + categories: categories + }; + } + function parseListContents(str) { + var lines = str.split(/\n+/g); + var seriesName = trim$1(lines.shift()); + var data = []; + for (var i = 0; i < lines.length; i++) { + // if line is empty, ignore it. + // there is a case that a user forgot to delete `\n`. + var line = trim$1(lines[i]); + if (!line) { + continue; + } + var items = line.split(itemSplitRegex); + var name_1 = ''; + var value = void 0; + var hasName = false; + if (isNaN(items[0])) { + // First item is name + hasName = true; + name_1 = items[0]; + items = items.slice(1); + data[i] = { + name: name_1, + value: [] + }; + value = data[i].value; + } else { + value = data[i] = []; + } + for (var j = 0; j < items.length; j++) { + value.push(+items[j]); + } + if (value.length === 1) { + hasName ? data[i].value = value[0] : data[i] = value[0]; + } + } + return { + name: seriesName, + data: data + }; + } + function parseContents(str, blockMetaList) { + var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g')); + var newOption = { + series: [] + }; + each(blocks, function (block, idx) { + if (isTSVFormat(block)) { + var result = parseTSVContents(block); + var blockMeta = blockMetaList[idx]; + var axisKey = blockMeta.axisDim + 'Axis'; + if (blockMeta) { + newOption[axisKey] = newOption[axisKey] || []; + newOption[axisKey][blockMeta.axisIndex] = { + data: result.categories + }; + newOption.series = newOption.series.concat(result.series); + } + } else { + var result = parseListContents(block); + newOption.series.push(result); + } + }); + return newOption; + } + var DataView = /** @class */function (_super) { + __extends(DataView, _super); + function DataView() { + return _super !== null && _super.apply(this, arguments) || this; + } + DataView.prototype.onclick = function (ecModel, api) { + // FIXME: better way? + setTimeout(function () { + api.dispatchAction({ + type: 'hideTip' + }); + }); + var container = api.getDom(); + var model = this.model; + if (this._dom) { + container.removeChild(this._dom); + } + var root = document.createElement('div'); + // use padding to avoid 5px whitespace + root.style.cssText = 'position:absolute;top:0;bottom:0;left:0;right:0;padding:5px'; + root.style.backgroundColor = model.get('backgroundColor') || '#fff'; + // Create elements + var header = document.createElement('h4'); + var lang = model.get('lang') || []; + header.innerHTML = lang[0] || model.get('title'); + header.style.cssText = 'margin:10px 20px'; + header.style.color = model.get('textColor'); + var viewMain = document.createElement('div'); + var textarea = document.createElement('textarea'); + viewMain.style.cssText = 'overflow:auto'; + var optionToContent = model.get('optionToContent'); + var contentToOption = model.get('contentToOption'); + var result = getContentFromModel(ecModel); + if (isFunction(optionToContent)) { + var htmlOrDom = optionToContent(api.getOption()); + if (isString(htmlOrDom)) { + viewMain.innerHTML = htmlOrDom; + } else if (isDom(htmlOrDom)) { + viewMain.appendChild(htmlOrDom); + } + } else { + // Use default textarea + textarea.readOnly = model.get('readOnly'); + var style = textarea.style; + // eslint-disable-next-line max-len + style.cssText = 'display:block;width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;resize:none;box-sizing:border-box;outline:none'; + style.color = model.get('textColor'); + style.borderColor = model.get('textareaBorderColor'); + style.backgroundColor = model.get('textareaColor'); + textarea.value = result.value; + viewMain.appendChild(textarea); + } + var blockMetaList = result.meta; + var buttonContainer = document.createElement('div'); + buttonContainer.style.cssText = 'position:absolute;bottom:5px;left:0;right:0'; + // eslint-disable-next-line max-len + var buttonStyle = 'float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px'; + var closeButton = document.createElement('div'); + var refreshButton = document.createElement('div'); + buttonStyle += ';background-color:' + model.get('buttonColor'); + buttonStyle += ';color:' + model.get('buttonTextColor'); + var self = this; + function close() { + container.removeChild(root); + self._dom = null; + } + addEventListener(closeButton, 'click', close); + addEventListener(refreshButton, 'click', function () { + if (contentToOption == null && optionToContent != null || contentToOption != null && optionToContent == null) { + if ("development" !== 'production') { + // eslint-disable-next-line + warn('It seems you have just provided one of `contentToOption` and `optionToContent` functions but missed the other one. Data change is ignored.'); + } + close(); + return; + } + var newOption; + try { + if (isFunction(contentToOption)) { + newOption = contentToOption(viewMain, api.getOption()); + } else { + newOption = parseContents(textarea.value, blockMetaList); + } + } catch (e) { + close(); + throw new Error('Data view format error ' + e); + } + if (newOption) { + api.dispatchAction({ + type: 'changeDataView', + newOption: newOption + }); + } + close(); + }); + closeButton.innerHTML = lang[1]; + refreshButton.innerHTML = lang[2]; + refreshButton.style.cssText = closeButton.style.cssText = buttonStyle; + !model.get('readOnly') && buttonContainer.appendChild(refreshButton); + buttonContainer.appendChild(closeButton); + root.appendChild(header); + root.appendChild(viewMain); + root.appendChild(buttonContainer); + viewMain.style.height = container.clientHeight - 80 + 'px'; + container.appendChild(root); + this._dom = root; + }; + DataView.prototype.remove = function (ecModel, api) { + this._dom && api.getDom().removeChild(this._dom); + }; + DataView.prototype.dispose = function (ecModel, api) { + this.remove(ecModel, api); + }; + DataView.getDefaultOption = function (ecModel) { + var defaultOption = { + show: true, + readOnly: false, + optionToContent: null, + contentToOption: null, + // eslint-disable-next-line + icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28', + title: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'title']), + lang: ecModel.getLocaleModel().get(['toolbox', 'dataView', 'lang']), + backgroundColor: '#fff', + textColor: '#000', + textareaColor: '#fff', + textareaBorderColor: '#333', + buttonColor: '#c23531', + buttonTextColor: '#fff' + }; + return defaultOption; + }; + return DataView; + }(ToolboxFeature); + /** + * @inner + */ + function tryMergeDataOption(newData, originalData) { + return map(newData, function (newVal, idx) { + var original = originalData && originalData[idx]; + if (isObject(original) && !isArray(original)) { + var newValIsObject = isObject(newVal) && !isArray(newVal); + if (!newValIsObject) { + newVal = { + value: newVal + }; + } + // original data has name but new data has no name + var shouldDeleteName = original.name != null && newVal.name == null; + // Original data has option + newVal = defaults(newVal, original); + shouldDeleteName && delete newVal.name; + return newVal; + } else { + return newVal; + } + }); + } + // TODO: SELF REGISTERED. + registerAction({ + type: 'changeDataView', + event: 'dataViewChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + var newSeriesOptList = []; + each(payload.newOption.series, function (seriesOpt) { + var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0]; + if (!seriesModel) { + // New created series + // Geuss the series type + newSeriesOptList.push(extend({ + // Default is scatter + type: 'scatter' + }, seriesOpt)); + } else { + var originalData = seriesModel.get('data'); + newSeriesOptList.push({ + name: seriesOpt.name, + data: tryMergeDataOption(seriesOpt.data, originalData) + }); + } + }); + ecModel.mergeOption(defaults({ + series: newSeriesOptList + }, payload.newOption)); + }); + + var each$9 = each; + var inner$f = makeInner(); + /** + * @param ecModel + * @param newSnapshot key is dataZoomId + */ + function push(ecModel, newSnapshot) { + var storedSnapshots = getStoreSnapshots(ecModel); + // If previous dataZoom can not be found, + // complete an range with current range. + each$9(newSnapshot, function (batchItem, dataZoomId) { + var i = storedSnapshots.length - 1; + for (; i >= 0; i--) { + var snapshot = storedSnapshots[i]; + if (snapshot[dataZoomId]) { + break; + } + } + if (i < 0) { + // No origin range set, create one by current range. + var dataZoomModel = ecModel.queryComponents({ + mainType: 'dataZoom', + subType: 'select', + id: dataZoomId + })[0]; + if (dataZoomModel) { + var percentRange = dataZoomModel.getPercentRange(); + storedSnapshots[0][dataZoomId] = { + dataZoomId: dataZoomId, + start: percentRange[0], + end: percentRange[1] + }; + } + } + }); + storedSnapshots.push(newSnapshot); + } + function pop(ecModel) { + var storedSnapshots = getStoreSnapshots(ecModel); + var head = storedSnapshots[storedSnapshots.length - 1]; + storedSnapshots.length > 1 && storedSnapshots.pop(); + // Find top for all dataZoom. + var snapshot = {}; + each$9(head, function (batchItem, dataZoomId) { + for (var i = storedSnapshots.length - 1; i >= 0; i--) { + batchItem = storedSnapshots[i][dataZoomId]; + if (batchItem) { + snapshot[dataZoomId] = batchItem; + break; + } + } + }); + return snapshot; + } + function clear$1(ecModel) { + inner$f(ecModel).snapshots = null; + } + function count(ecModel) { + return getStoreSnapshots(ecModel).length; + } + /** + * History length of each dataZoom may be different. + * this._history[0] is used to store origin range. + */ + function getStoreSnapshots(ecModel) { + var store = inner$f(ecModel); + if (!store.snapshots) { + store.snapshots = [{}]; + } + return store.snapshots; + } + + var RestoreOption = /** @class */function (_super) { + __extends(RestoreOption, _super); + function RestoreOption() { + return _super !== null && _super.apply(this, arguments) || this; + } + RestoreOption.prototype.onclick = function (ecModel, api) { + clear$1(ecModel); + api.dispatchAction({ + type: 'restore', + from: this.uid + }); + }; + RestoreOption.getDefaultOption = function (ecModel) { + var defaultOption = { + show: true, + // eslint-disable-next-line + icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5', + title: ecModel.getLocaleModel().get(['toolbox', 'restore', 'title']) + }; + return defaultOption; + }; + return RestoreOption; + }(ToolboxFeature); + // TODO: SELF REGISTERED. + registerAction({ + type: 'restore', + event: 'restore', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + ecModel.resetOption('recreate'); + }); + + // FIXME + // how to genarialize to more coordinate systems. + var INCLUDE_FINDER_MAIN_TYPES = ['grid', 'xAxis', 'yAxis', 'geo', 'graph', 'polar', 'radiusAxis', 'angleAxis', 'bmap']; + var BrushTargetManager = /** @class */function () { + /** + * @param finder contains Index/Id/Name of xAxis/yAxis/geo/grid + * Each can be {number|Array.}. like: {xAxisIndex: [3, 4]} + * @param opt.include include coordinate system types. + */ + function BrushTargetManager(finder, ecModel, opt) { + var _this = this; + this._targetInfoList = []; + var foundCpts = parseFinder$1(ecModel, finder); + each(targetInfoBuilders, function (builder, type) { + if (!opt || !opt.include || indexOf(opt.include, type) >= 0) { + builder(foundCpts, _this._targetInfoList); + } + }); + } + BrushTargetManager.prototype.setOutputRanges = function (areas, ecModel) { + this.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) { + (area.coordRanges || (area.coordRanges = [])).push(coordRange); + // area.coordRange is the first of area.coordRanges + if (!area.coordRange) { + area.coordRange = coordRange; + // In 'category' axis, coord to pixel is not reversible, so we can not + // rebuild range by coordRange accrately, which may bring trouble when + // brushing only one item. So we use __rangeOffset to rebuilding range + // by coordRange. And this it only used in brush component so it is no + // need to be adapted to coordRanges. + var result = coordConvert[area.brushType](0, coordSys, coordRange); + area.__rangeOffset = { + offset: diffProcessor[area.brushType](result.values, area.range, [1, 1]), + xyMinMax: result.xyMinMax + }; + } + }); + return areas; + }; + BrushTargetManager.prototype.matchOutputRanges = function (areas, ecModel, cb) { + each(areas, function (area) { + var targetInfo = this.findTargetInfo(area, ecModel); + if (targetInfo && targetInfo !== true) { + each(targetInfo.coordSyses, function (coordSys) { + var result = coordConvert[area.brushType](1, coordSys, area.range, true); + cb(area, result.values, coordSys, ecModel); + }); + } + }, this); + }; + /** + * the `areas` is `BrushModel.areas`. + * Called in layout stage. + * convert `area.coordRange` to global range and set panelId to `area.range`. + */ + BrushTargetManager.prototype.setInputRanges = function (areas, ecModel) { + each(areas, function (area) { + var targetInfo = this.findTargetInfo(area, ecModel); + if ("development" !== 'production') { + assert(!targetInfo || targetInfo === true || area.coordRange, 'coordRange must be specified when coord index specified.'); + assert(!targetInfo || targetInfo !== true || area.range, 'range must be specified in global brush.'); + } + area.range = area.range || []; + // convert coordRange to global range and set panelId. + if (targetInfo && targetInfo !== true) { + area.panelId = targetInfo.panelId; + // (1) area.range should always be calculate from coordRange but does + // not keep its original value, for the sake of the dataZoom scenario, + // where area.coordRange remains unchanged but area.range may be changed. + // (2) Only support converting one coordRange to pixel range in brush + // component. So do not consider `coordRanges`. + // (3) About __rangeOffset, see comment above. + var result = coordConvert[area.brushType](0, targetInfo.coordSys, area.coordRange); + var rangeOffset = area.__rangeOffset; + area.range = rangeOffset ? diffProcessor[area.brushType](result.values, rangeOffset.offset, getScales(result.xyMinMax, rangeOffset.xyMinMax)) : result.values; + } + }, this); + }; + BrushTargetManager.prototype.makePanelOpts = function (api, getDefaultBrushType) { + return map(this._targetInfoList, function (targetInfo) { + var rect = targetInfo.getPanelRect(); + return { + panelId: targetInfo.panelId, + defaultBrushType: getDefaultBrushType ? getDefaultBrushType(targetInfo) : null, + clipPath: makeRectPanelClipPath(rect), + isTargetByCursor: makeRectIsTargetByCursor(rect, api, targetInfo.coordSysModel), + getLinearBrushOtherExtent: makeLinearBrushOtherExtent(rect) + }; + }); + }; + BrushTargetManager.prototype.controlSeries = function (area, seriesModel, ecModel) { + // Check whether area is bound in coord, and series do not belong to that coord. + // If do not do this check, some brush (like lineX) will controll all axes. + var targetInfo = this.findTargetInfo(area, ecModel); + return targetInfo === true || targetInfo && indexOf(targetInfo.coordSyses, seriesModel.coordinateSystem) >= 0; + }; + /** + * If return Object, a coord found. + * If return true, global found. + * Otherwise nothing found. + */ + BrushTargetManager.prototype.findTargetInfo = function (area, ecModel) { + var targetInfoList = this._targetInfoList; + var foundCpts = parseFinder$1(ecModel, area); + for (var i = 0; i < targetInfoList.length; i++) { + var targetInfo = targetInfoList[i]; + var areaPanelId = area.panelId; + if (areaPanelId) { + if (targetInfo.panelId === areaPanelId) { + return targetInfo; + } + } else { + for (var j = 0; j < targetInfoMatchers.length; j++) { + if (targetInfoMatchers[j](foundCpts, targetInfo)) { + return targetInfo; + } + } + } + } + return true; + }; + return BrushTargetManager; + }(); + function formatMinMax(minMax) { + minMax[0] > minMax[1] && minMax.reverse(); + return minMax; + } + function parseFinder$1(ecModel, finder) { + return parseFinder(ecModel, finder, { + includeMainTypes: INCLUDE_FINDER_MAIN_TYPES + }); + } + var targetInfoBuilders = { + grid: function (foundCpts, targetInfoList) { + var xAxisModels = foundCpts.xAxisModels; + var yAxisModels = foundCpts.yAxisModels; + var gridModels = foundCpts.gridModels; + // Remove duplicated. + var gridModelMap = createHashMap(); + var xAxesHas = {}; + var yAxesHas = {}; + if (!xAxisModels && !yAxisModels && !gridModels) { + return; + } + each(xAxisModels, function (axisModel) { + var gridModel = axisModel.axis.grid.model; + gridModelMap.set(gridModel.id, gridModel); + xAxesHas[gridModel.id] = true; + }); + each(yAxisModels, function (axisModel) { + var gridModel = axisModel.axis.grid.model; + gridModelMap.set(gridModel.id, gridModel); + yAxesHas[gridModel.id] = true; + }); + each(gridModels, function (gridModel) { + gridModelMap.set(gridModel.id, gridModel); + xAxesHas[gridModel.id] = true; + yAxesHas[gridModel.id] = true; + }); + gridModelMap.each(function (gridModel) { + var grid = gridModel.coordinateSystem; + var cartesians = []; + each(grid.getCartesians(), function (cartesian, index) { + if (indexOf(xAxisModels, cartesian.getAxis('x').model) >= 0 || indexOf(yAxisModels, cartesian.getAxis('y').model) >= 0) { + cartesians.push(cartesian); + } + }); + targetInfoList.push({ + panelId: 'grid--' + gridModel.id, + gridModel: gridModel, + coordSysModel: gridModel, + // Use the first one as the representitive coordSys. + coordSys: cartesians[0], + coordSyses: cartesians, + getPanelRect: panelRectBuilders.grid, + xAxisDeclared: xAxesHas[gridModel.id], + yAxisDeclared: yAxesHas[gridModel.id] + }); + }); + }, + geo: function (foundCpts, targetInfoList) { + each(foundCpts.geoModels, function (geoModel) { + var coordSys = geoModel.coordinateSystem; + targetInfoList.push({ + panelId: 'geo--' + geoModel.id, + geoModel: geoModel, + coordSysModel: geoModel, + coordSys: coordSys, + coordSyses: [coordSys], + getPanelRect: panelRectBuilders.geo + }); + }); + } + }; + var targetInfoMatchers = [ + // grid + function (foundCpts, targetInfo) { + var xAxisModel = foundCpts.xAxisModel; + var yAxisModel = foundCpts.yAxisModel; + var gridModel = foundCpts.gridModel; + !gridModel && xAxisModel && (gridModel = xAxisModel.axis.grid.model); + !gridModel && yAxisModel && (gridModel = yAxisModel.axis.grid.model); + return gridModel && gridModel === targetInfo.gridModel; + }, + // geo + function (foundCpts, targetInfo) { + var geoModel = foundCpts.geoModel; + return geoModel && geoModel === targetInfo.geoModel; + }]; + var panelRectBuilders = { + grid: function () { + // grid is not Transformable. + return this.coordSys.master.getRect().clone(); + }, + geo: function () { + var coordSys = this.coordSys; + var rect = coordSys.getBoundingRect().clone(); + // geo roam and zoom transform + rect.applyTransform(getTransform(coordSys)); + return rect; + } + }; + var coordConvert = { + lineX: curry(axisConvert, 0), + lineY: curry(axisConvert, 1), + rect: function (to, coordSys, rangeOrCoordRange, clamp) { + var xminymin = to ? coordSys.pointToData([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][0], rangeOrCoordRange[1][0]], clamp); + var xmaxymax = to ? coordSys.pointToData([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp) : coordSys.dataToPoint([rangeOrCoordRange[0][1], rangeOrCoordRange[1][1]], clamp); + var values = [formatMinMax([xminymin[0], xmaxymax[0]]), formatMinMax([xminymin[1], xmaxymax[1]])]; + return { + values: values, + xyMinMax: values + }; + }, + polygon: function (to, coordSys, rangeOrCoordRange, clamp) { + var xyMinMax = [[Infinity, -Infinity], [Infinity, -Infinity]]; + var values = map(rangeOrCoordRange, function (item) { + var p = to ? coordSys.pointToData(item, clamp) : coordSys.dataToPoint(item, clamp); + xyMinMax[0][0] = Math.min(xyMinMax[0][0], p[0]); + xyMinMax[1][0] = Math.min(xyMinMax[1][0], p[1]); + xyMinMax[0][1] = Math.max(xyMinMax[0][1], p[0]); + xyMinMax[1][1] = Math.max(xyMinMax[1][1], p[1]); + return p; + }); + return { + values: values, + xyMinMax: xyMinMax + }; + } + }; + function axisConvert(axisNameIndex, to, coordSys, rangeOrCoordRange) { + if ("development" !== 'production') { + assert(coordSys.type === 'cartesian2d', 'lineX/lineY brush is available only in cartesian2d.'); + } + var axis = coordSys.getAxis(['x', 'y'][axisNameIndex]); + var values = formatMinMax(map([0, 1], function (i) { + return to ? axis.coordToData(axis.toLocalCoord(rangeOrCoordRange[i]), true) : axis.toGlobalCoord(axis.dataToCoord(rangeOrCoordRange[i])); + })); + var xyMinMax = []; + xyMinMax[axisNameIndex] = values; + xyMinMax[1 - axisNameIndex] = [NaN, NaN]; + return { + values: values, + xyMinMax: xyMinMax + }; + } + var diffProcessor = { + lineX: curry(axisDiffProcessor, 0), + lineY: curry(axisDiffProcessor, 1), + rect: function (values, refer, scales) { + return [[values[0][0] - scales[0] * refer[0][0], values[0][1] - scales[0] * refer[0][1]], [values[1][0] - scales[1] * refer[1][0], values[1][1] - scales[1] * refer[1][1]]]; + }, + polygon: function (values, refer, scales) { + return map(values, function (item, idx) { + return [item[0] - scales[0] * refer[idx][0], item[1] - scales[1] * refer[idx][1]]; + }); + } + }; + function axisDiffProcessor(axisNameIndex, values, refer, scales) { + return [values[0] - scales[axisNameIndex] * refer[0], values[1] - scales[axisNameIndex] * refer[1]]; + } + // We have to process scale caused by dataZoom manually, + // although it might be not accurate. + // Return [0~1, 0~1] + function getScales(xyMinMaxCurr, xyMinMaxOrigin) { + var sizeCurr = getSize$1(xyMinMaxCurr); + var sizeOrigin = getSize$1(xyMinMaxOrigin); + var scales = [sizeCurr[0] / sizeOrigin[0], sizeCurr[1] / sizeOrigin[1]]; + isNaN(scales[0]) && (scales[0] = 1); + isNaN(scales[1]) && (scales[1] = 1); + return scales; + } + function getSize$1(xyMinMax) { + return xyMinMax ? [xyMinMax[0][1] - xyMinMax[0][0], xyMinMax[1][1] - xyMinMax[1][0]] : [NaN, NaN]; + } + + var each$a = each; + var DATA_ZOOM_ID_BASE = makeInternalComponentId('toolbox-dataZoom_'); + var DataZoomFeature = /** @class */function (_super) { + __extends(DataZoomFeature, _super); + function DataZoomFeature() { + return _super !== null && _super.apply(this, arguments) || this; + } + DataZoomFeature.prototype.render = function (featureModel, ecModel, api, payload) { + if (!this._brushController) { + this._brushController = new BrushController(api.getZr()); + this._brushController.on('brush', bind(this._onBrush, this)).mount(); + } + updateZoomBtnStatus(featureModel, ecModel, this, payload, api); + updateBackBtnStatus(featureModel, ecModel); + }; + DataZoomFeature.prototype.onclick = function (ecModel, api, type) { + handlers$1[type].call(this); + }; + DataZoomFeature.prototype.remove = function (ecModel, api) { + this._brushController && this._brushController.unmount(); + }; + DataZoomFeature.prototype.dispose = function (ecModel, api) { + this._brushController && this._brushController.dispose(); + }; + DataZoomFeature.prototype._onBrush = function (eventParam) { + var areas = eventParam.areas; + if (!eventParam.isEnd || !areas.length) { + return; + } + var snapshot = {}; + var ecModel = this.ecModel; + this._brushController.updateCovers([]); // remove cover + var brushTargetManager = new BrushTargetManager(makeAxisFinder(this.model), ecModel, { + include: ['grid'] + }); + brushTargetManager.matchOutputRanges(areas, ecModel, function (area, coordRange, coordSys) { + if (coordSys.type !== 'cartesian2d') { + return; + } + var brushType = area.brushType; + if (brushType === 'rect') { + setBatch('x', coordSys, coordRange[0]); + setBatch('y', coordSys, coordRange[1]); + } else { + setBatch({ + lineX: 'x', + lineY: 'y' + }[brushType], coordSys, coordRange); + } + }); + push(ecModel, snapshot); + this._dispatchZoomAction(snapshot); + function setBatch(dimName, coordSys, minMax) { + var axis = coordSys.getAxis(dimName); + var axisModel = axis.model; + var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); + // Restrict range. + var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); + if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { + minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); + } + dataZoomModel && (snapshot[dataZoomModel.id] = { + dataZoomId: dataZoomModel.id, + startValue: minMax[0], + endValue: minMax[1] + }); + } + function findDataZoom(dimName, axisModel, ecModel) { + var found; + ecModel.eachComponent({ + mainType: 'dataZoom', + subType: 'select' + }, function (dzModel) { + var has = dzModel.getAxisModel(dimName, axisModel.componentIndex); + has && (found = dzModel); + }); + return found; + } + }; + DataZoomFeature.prototype._dispatchZoomAction = function (snapshot) { + var batch = []; + // Convert from hash map to array. + each$a(snapshot, function (batchItem, dataZoomId) { + batch.push(clone(batchItem)); + }); + batch.length && this.api.dispatchAction({ + type: 'dataZoom', + from: this.uid, + batch: batch + }); + }; + DataZoomFeature.getDefaultOption = function (ecModel) { + var defaultOption = { + show: true, + filterMode: 'filter', + // Icon group + icon: { + zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1', + back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26' + }, + // `zoom`, `back` + title: ecModel.getLocaleModel().get(['toolbox', 'dataZoom', 'title']), + brushStyle: { + borderWidth: 0, + color: 'rgba(210,219,238,0.2)' + } + }; + return defaultOption; + }; + return DataZoomFeature; + }(ToolboxFeature); + var handlers$1 = { + zoom: function () { + var nextActive = !this._isZoomActive; + this.api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'dataZoomSelect', + dataZoomSelectActive: nextActive + }); + }, + back: function () { + this._dispatchZoomAction(pop(this.ecModel)); + } + }; + function makeAxisFinder(dzFeatureModel) { + var setting = { + xAxisIndex: dzFeatureModel.get('xAxisIndex', true), + yAxisIndex: dzFeatureModel.get('yAxisIndex', true), + xAxisId: dzFeatureModel.get('xAxisId', true), + yAxisId: dzFeatureModel.get('yAxisId', true) + }; + // If both `xAxisIndex` `xAxisId` not set, it means 'all'. + // If both `yAxisIndex` `yAxisId` not set, it means 'all'. + // Some old cases set like this below to close yAxis control but leave xAxis control: + // `{ feature: { dataZoom: { yAxisIndex: false } }`. + if (setting.xAxisIndex == null && setting.xAxisId == null) { + setting.xAxisIndex = 'all'; + } + if (setting.yAxisIndex == null && setting.yAxisId == null) { + setting.yAxisIndex = 'all'; + } + return setting; + } + function updateBackBtnStatus(featureModel, ecModel) { + featureModel.setIconStatus('back', count(ecModel) > 1 ? 'emphasis' : 'normal'); + } + function updateZoomBtnStatus(featureModel, ecModel, view, payload, api) { + var zoomActive = view._isZoomActive; + if (payload && payload.type === 'takeGlobalCursor') { + zoomActive = payload.key === 'dataZoomSelect' ? payload.dataZoomSelectActive : false; + } + view._isZoomActive = zoomActive; + featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal'); + var brushTargetManager = new BrushTargetManager(makeAxisFinder(featureModel), ecModel, { + include: ['grid'] + }); + var panels = brushTargetManager.makePanelOpts(api, function (targetInfo) { + return targetInfo.xAxisDeclared && !targetInfo.yAxisDeclared ? 'lineX' : !targetInfo.xAxisDeclared && targetInfo.yAxisDeclared ? 'lineY' : 'rect'; + }); + view._brushController.setPanels(panels).enableBrush(zoomActive && panels.length ? { + brushType: 'auto', + brushStyle: featureModel.getModel('brushStyle').getItemStyle() + } : false); + } + registerInternalOptionCreator('dataZoom', function (ecModel) { + var toolboxModel = ecModel.getComponent('toolbox', 0); + var featureDataZoomPath = ['feature', 'dataZoom']; + if (!toolboxModel || toolboxModel.get(featureDataZoomPath) == null) { + return; + } + var dzFeatureModel = toolboxModel.getModel(featureDataZoomPath); + var dzOptions = []; + var finder = makeAxisFinder(dzFeatureModel); + var finderResult = parseFinder(ecModel, finder); + each$a(finderResult.xAxisModels, function (axisModel) { + return buildInternalOptions(axisModel, 'xAxis', 'xAxisIndex'); + }); + each$a(finderResult.yAxisModels, function (axisModel) { + return buildInternalOptions(axisModel, 'yAxis', 'yAxisIndex'); + }); + function buildInternalOptions(axisModel, axisMainType, axisIndexPropName) { + var axisIndex = axisModel.componentIndex; + var newOpt = { + type: 'select', + $fromToolbox: true, + // Default to be filter + filterMode: dzFeatureModel.get('filterMode', true) || 'filter', + // Id for merge mapping. + id: DATA_ZOOM_ID_BASE + axisMainType + axisIndex + }; + newOpt[axisIndexPropName] = axisIndex; + dzOptions.push(newOpt); + } + return dzOptions; + }); + + function install$z(registers) { + registers.registerComponentModel(ToolboxModel); + registers.registerComponentView(ToolboxView); + registerFeature('saveAsImage', SaveAsImage); + registerFeature('magicType', MagicType); + registerFeature('dataView', DataView); + registerFeature('dataZoom', DataZoomFeature); + registerFeature('restore', RestoreOption); + use(install$y); + } + + var TooltipModel = /** @class */function (_super) { + __extends(TooltipModel, _super); + function TooltipModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TooltipModel.type; + return _this; + } + TooltipModel.type = 'tooltip'; + TooltipModel.dependencies = ['axisPointer']; + TooltipModel.defaultOption = { + // zlevel: 0, + z: 60, + show: true, + // tooltip main content + showContent: true, + // 'trigger' only works on coordinate system. + // 'item' | 'axis' | 'none' + trigger: 'item', + // 'click' | 'mousemove' | 'none' + triggerOn: 'mousemove|click', + alwaysShowContent: false, + displayMode: 'single', + renderMode: 'auto', + // whether restraint content inside viewRect. + // If renderMode: 'richText', default true. + // If renderMode: 'html', defaut false (for backward compat). + confine: null, + showDelay: 0, + hideDelay: 100, + // Animation transition time, unit is second + transitionDuration: 0.4, + enterable: false, + backgroundColor: '#fff', + // box shadow + shadowBlur: 10, + shadowColor: 'rgba(0, 0, 0, .2)', + shadowOffsetX: 1, + shadowOffsetY: 2, + // tooltip border radius, unit is px, default is 4 + borderRadius: 4, + // tooltip border width, unit is px, default is 0 (no border) + borderWidth: 1, + // Tooltip inside padding, default is 5 for all direction + // Array is allowed to set up, right, bottom, left, same with css + // The default value: See `tooltip/tooltipMarkup.ts#getPaddingFromTooltipModel`. + padding: null, + // Extra css text + extraCssText: '', + // axis indicator, trigger by axis + axisPointer: { + // default is line + // legal values: 'line' | 'shadow' | 'cross' + type: 'line', + // Valid when type is line, appoint tooltip line locate on which line. Optional + // legal values: 'x' | 'y' | 'angle' | 'radius' | 'auto' + // default is 'auto', chose the axis which type is category. + // for multiply y axis, cartesian coord chose x axis, polar chose angle axis + axis: 'auto', + animation: 'auto', + animationDurationUpdate: 200, + animationEasingUpdate: 'exponentialOut', + crossStyle: { + color: '#999', + width: 1, + type: 'dashed', + // TODO formatter + textStyle: {} + } + // lineStyle and shadowStyle should not be specified here, + // otherwise it will always override those styles on option.axisPointer. + }, + + textStyle: { + color: '#666', + fontSize: 14 + } + }; + return TooltipModel; + }(ComponentModel); + + /* global document */ + function shouldTooltipConfine(tooltipModel) { + var confineOption = tooltipModel.get('confine'); + return confineOption != null ? !!confineOption + // In richText mode, the outside part can not be visible. + : tooltipModel.get('renderMode') === 'richText'; + } + function testStyle(styleProps) { + if (!env.domSupported) { + return; + } + var style = document.documentElement.style; + for (var i = 0, len = styleProps.length; i < len; i++) { + if (styleProps[i] in style) { + return styleProps[i]; + } + } + } + var TRANSFORM_VENDOR = testStyle(['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']); + var TRANSITION_VENDOR = testStyle(['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); + function toCSSVendorPrefix(styleVendor, styleProp) { + if (!styleVendor) { + return styleProp; + } + styleProp = toCamelCase(styleProp, true); + var idx = styleVendor.indexOf(styleProp); + styleVendor = idx === -1 ? styleProp : "-" + styleVendor.slice(0, idx) + "-" + styleProp; + return styleVendor.toLowerCase(); + } + function getComputedStyle(el, style) { + var stl = el.currentStyle || document.defaultView && document.defaultView.getComputedStyle(el); + return stl ? style ? stl[style] : stl : null; + } + + /* global document, window */ + var CSS_TRANSITION_VENDOR = toCSSVendorPrefix(TRANSITION_VENDOR, 'transition'); + var CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform'); + // eslint-disable-next-line + var gCssText = "position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;" + (env.transform3dSupported ? 'will-change:transform;' : ''); + function mirrorPos(pos) { + pos = pos === 'left' ? 'right' : pos === 'right' ? 'left' : pos === 'top' ? 'bottom' : 'top'; + return pos; + } + function assembleArrow(tooltipModel, borderColor, arrowPosition) { + if (!isString(arrowPosition) || arrowPosition === 'inside') { + return ''; + } + var backgroundColor = tooltipModel.get('backgroundColor'); + var borderWidth = tooltipModel.get('borderWidth'); + borderColor = convertToColorString(borderColor); + var arrowPos = mirrorPos(arrowPosition); + var arrowSize = Math.max(Math.round(borderWidth) * 1.5, 6); + var positionStyle = ''; + var transformStyle = CSS_TRANSFORM_VENDOR + ':'; + var rotateDeg; + if (indexOf(['left', 'right'], arrowPos) > -1) { + positionStyle += 'top:50%'; + transformStyle += "translateY(-50%) rotate(" + (rotateDeg = arrowPos === 'left' ? -225 : -45) + "deg)"; + } else { + positionStyle += 'left:50%'; + transformStyle += "translateX(-50%) rotate(" + (rotateDeg = arrowPos === 'top' ? 225 : 45) + "deg)"; + } + var rotateRadian = rotateDeg * Math.PI / 180; + var arrowWH = arrowSize + borderWidth; + var rotatedWH = arrowWH * Math.abs(Math.cos(rotateRadian)) + arrowWH * Math.abs(Math.sin(rotateRadian)); + var arrowOffset = Math.round(((rotatedWH - Math.SQRT2 * borderWidth) / 2 + Math.SQRT2 * borderWidth - (rotatedWH - arrowWH) / 2) * 100) / 100; + positionStyle += ";" + arrowPos + ":-" + arrowOffset + "px"; + var borderStyle = borderColor + " solid " + borderWidth + "px;"; + var styleCss = ["position:absolute;width:" + arrowSize + "px;height:" + arrowSize + "px;z-index:-1;", positionStyle + ";" + transformStyle + ";", "border-bottom:" + borderStyle, "border-right:" + borderStyle, "background-color:" + backgroundColor + ";"]; + return "
"; + } + function assembleTransition(duration, onlyFade) { + var transitionCurve = 'cubic-bezier(0.23,1,0.32,1)'; + var transitionOption = " " + duration / 2 + "s " + transitionCurve; + var transitionText = "opacity" + transitionOption + ",visibility" + transitionOption; + if (!onlyFade) { + transitionOption = " " + duration + "s " + transitionCurve; + transitionText += env.transformSupported ? "," + CSS_TRANSFORM_VENDOR + transitionOption : ",left" + transitionOption + ",top" + transitionOption; + } + return CSS_TRANSITION_VENDOR + ':' + transitionText; + } + function assembleTransform(x, y, toString) { + // If using float on style, the final width of the dom might + // keep changing slightly while mouse move. So `toFixed(0)` them. + var x0 = x.toFixed(0) + 'px'; + var y0 = y.toFixed(0) + 'px'; + // not support transform, use `left` and `top` instead. + if (!env.transformSupported) { + return toString ? "top:" + y0 + ";left:" + x0 + ";" : [['top', y0], ['left', x0]]; + } + // support transform + var is3d = env.transform3dSupported; + var translate = "translate" + (is3d ? '3d' : '') + "(" + x0 + "," + y0 + (is3d ? ',0' : '') + ")"; + return toString ? 'top:0;left:0;' + CSS_TRANSFORM_VENDOR + ':' + translate + ';' : [['top', 0], ['left', 0], [TRANSFORM_VENDOR, translate]]; + } + /** + * @param {Object} textStyle + * @return {string} + * @inner + */ + function assembleFont(textStyleModel) { + var cssText = []; + var fontSize = textStyleModel.get('fontSize'); + var color = textStyleModel.getTextColor(); + color && cssText.push('color:' + color); + cssText.push('font:' + textStyleModel.getFont()); + fontSize + // @ts-ignore, leave it to the tooltip refactor. + && cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px'); + var shadowColor = textStyleModel.get('textShadowColor'); + var shadowBlur = textStyleModel.get('textShadowBlur') || 0; + var shadowOffsetX = textStyleModel.get('textShadowOffsetX') || 0; + var shadowOffsetY = textStyleModel.get('textShadowOffsetY') || 0; + shadowColor && shadowBlur && cssText.push('text-shadow:' + shadowOffsetX + 'px ' + shadowOffsetY + 'px ' + shadowBlur + 'px ' + shadowColor); + each(['decoration', 'align'], function (name) { + var val = textStyleModel.get(name); + val && cssText.push('text-' + name + ':' + val); + }); + return cssText.join(';'); + } + function assembleCssText(tooltipModel, enableTransition, onlyFade) { + var cssText = []; + var transitionDuration = tooltipModel.get('transitionDuration'); + var backgroundColor = tooltipModel.get('backgroundColor'); + var shadowBlur = tooltipModel.get('shadowBlur'); + var shadowColor = tooltipModel.get('shadowColor'); + var shadowOffsetX = tooltipModel.get('shadowOffsetX'); + var shadowOffsetY = tooltipModel.get('shadowOffsetY'); + var textStyleModel = tooltipModel.getModel('textStyle'); + var padding = getPaddingFromTooltipModel(tooltipModel, 'html'); + var boxShadow = shadowOffsetX + "px " + shadowOffsetY + "px " + shadowBlur + "px " + shadowColor; + cssText.push('box-shadow:' + boxShadow); + // Animation transition. Do not animate when transitionDuration is 0. + enableTransition && transitionDuration && cssText.push(assembleTransition(transitionDuration, onlyFade)); + if (backgroundColor) { + cssText.push('background-color:' + backgroundColor); + } + // Border style + each(['width', 'color', 'radius'], function (name) { + var borderName = 'border-' + name; + var camelCase = toCamelCase(borderName); + var val = tooltipModel.get(camelCase); + val != null && cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px')); + }); + // Text style + cssText.push(assembleFont(textStyleModel)); + // Padding + if (padding != null) { + cssText.push('padding:' + normalizeCssArray$1(padding).join('px ') + 'px'); + } + return cssText.join(';') + ';'; + } + // If not able to make, do not modify the input `out`. + function makeStyleCoord(out, zr, container, zrX, zrY) { + var zrPainter = zr && zr.painter; + if (container) { + var zrViewportRoot = zrPainter && zrPainter.getViewportRoot(); + if (zrViewportRoot) { + // Some APPs might use scale on body, so we support CSS transform here. + transformLocalCoord(out, zrViewportRoot, container, zrX, zrY); + } + } else { + out[0] = zrX; + out[1] = zrY; + // xy should be based on canvas root. But tooltipContent is + // the sibling of canvas root. So padding of ec container + // should be considered here. + var viewportRootOffset = zrPainter && zrPainter.getViewportRootOffset(); + if (viewportRootOffset) { + out[0] += viewportRootOffset.offsetLeft; + out[1] += viewportRootOffset.offsetTop; + } + } + out[2] = out[0] / zr.getWidth(); + out[3] = out[1] / zr.getHeight(); + } + var TooltipHTMLContent = /** @class */function () { + function TooltipHTMLContent(api, opt) { + this._show = false; + this._styleCoord = [0, 0, 0, 0]; + this._enterable = true; + this._alwaysShowContent = false; + this._firstShow = true; + this._longHide = true; + if (env.wxa) { + return null; + } + var el = document.createElement('div'); + // TODO: TYPE + el.domBelongToZr = true; + this.el = el; + var zr = this._zr = api.getZr(); + var appendTo = opt.appendTo; + var container = appendTo && (isString(appendTo) ? document.querySelector(appendTo) : isDom(appendTo) ? appendTo : isFunction(appendTo) && appendTo(api.getDom())); + makeStyleCoord(this._styleCoord, zr, container, api.getWidth() / 2, api.getHeight() / 2); + (container || api.getDom()).appendChild(el); + this._api = api; + this._container = container; + // FIXME + // Is it needed to trigger zr event manually if + // the browser do not support `pointer-events: none`. + var self = this; + el.onmouseenter = function () { + // clear the timeout in hideLater and keep showing tooltip + if (self._enterable) { + clearTimeout(self._hideTimeout); + self._show = true; + } + self._inContent = true; + }; + el.onmousemove = function (e) { + e = e || window.event; + if (!self._enterable) { + // `pointer-events: none` is set to tooltip content div + // if `enterable` is set as `false`, and `el.onmousemove` + // can not be triggered. But in browser that do not + // support `pointer-events`, we need to do this: + // Try trigger zrender event to avoid mouse + // in and out shape too frequently + var handler = zr.handler; + var zrViewportRoot = zr.painter.getViewportRoot(); + normalizeEvent(zrViewportRoot, e, true); + handler.dispatch('mousemove', e); + } + }; + el.onmouseleave = function () { + // set `_inContent` to `false` before `hideLater` + self._inContent = false; + if (self._enterable) { + if (self._show) { + self.hideLater(self._hideDelay); + } + } + }; + } + /** + * Update when tooltip is rendered + */ + TooltipHTMLContent.prototype.update = function (tooltipModel) { + // FIXME + // Move this logic to ec main? + if (!this._container) { + var container = this._api.getDom(); + var position = getComputedStyle(container, 'position'); + var domStyle = container.style; + if (domStyle.position !== 'absolute' && position !== 'absolute') { + domStyle.position = 'relative'; + } + } + // move tooltip if chart resized + var alwaysShowContent = tooltipModel.get('alwaysShowContent'); + alwaysShowContent && this._moveIfResized(); + // update alwaysShowContent + this._alwaysShowContent = alwaysShowContent; + // update className + this.el.className = tooltipModel.get('className') || ''; + // Hide the tooltip + // PENDING + // this.hide(); + }; + + TooltipHTMLContent.prototype.show = function (tooltipModel, nearPointColor) { + clearTimeout(this._hideTimeout); + clearTimeout(this._longHideTimeout); + var el = this.el; + var style = el.style; + var styleCoord = this._styleCoord; + if (!el.innerHTML) { + style.display = 'none'; + } else { + style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide) + // initial transform + + assembleTransform(styleCoord[0], styleCoord[1], true) + ("border-color:" + convertToColorString(nearPointColor) + ";") + (tooltipModel.get('extraCssText') || '') + // If mouse occasionally move over the tooltip, a mouseout event will be + // triggered by canvas, and cause some unexpectable result like dragging + // stop, "unfocusAdjacency". Here `pointer-events: none` is used to solve + // it. Although it is not supported by IE8~IE10, fortunately it is a rare + // scenario. + + (";pointer-events:" + (this._enterable ? 'auto' : 'none')); + } + this._show = true; + this._firstShow = false; + this._longHide = false; + }; + TooltipHTMLContent.prototype.setContent = function (content, markers, tooltipModel, borderColor, arrowPosition) { + var el = this.el; + if (content == null) { + el.innerHTML = ''; + return; + } + var arrow = ''; + if (isString(arrowPosition) && tooltipModel.get('trigger') === 'item' && !shouldTooltipConfine(tooltipModel)) { + arrow = assembleArrow(tooltipModel, borderColor, arrowPosition); + } + if (isString(content)) { + el.innerHTML = content + arrow; + } else if (content) { + // Clear previous + el.innerHTML = ''; + if (!isArray(content)) { + content = [content]; + } + for (var i = 0; i < content.length; i++) { + if (isDom(content[i]) && content[i].parentNode !== el) { + el.appendChild(content[i]); + } + } + // no arrow if empty + if (arrow && el.childNodes.length) { + // no need to create a new parent element, but it's not supported by IE 10 and older. + // const arrowEl = document.createRange().createContextualFragment(arrow); + var arrowEl = document.createElement('div'); + arrowEl.innerHTML = arrow; + el.appendChild(arrowEl); + } + } + }; + TooltipHTMLContent.prototype.setEnterable = function (enterable) { + this._enterable = enterable; + }; + TooltipHTMLContent.prototype.getSize = function () { + var el = this.el; + return [el.offsetWidth, el.offsetHeight]; + }; + TooltipHTMLContent.prototype.moveTo = function (zrX, zrY) { + var styleCoord = this._styleCoord; + makeStyleCoord(styleCoord, this._zr, this._container, zrX, zrY); + if (styleCoord[0] != null && styleCoord[1] != null) { + var style_1 = this.el.style; + var transforms = assembleTransform(styleCoord[0], styleCoord[1]); + each(transforms, function (transform) { + style_1[transform[0]] = transform[1]; + }); + } + }; + /** + * when `alwaysShowContent` is true, + * move the tooltip after chart resized + */ + TooltipHTMLContent.prototype._moveIfResized = function () { + // The ratio of left to width + var ratioX = this._styleCoord[2]; + // The ratio of top to height + var ratioY = this._styleCoord[3]; + this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight()); + }; + TooltipHTMLContent.prototype.hide = function () { + var _this = this; + var style = this.el.style; + style.visibility = 'hidden'; + style.opacity = '0'; + env.transform3dSupported && (style.willChange = ''); + this._show = false; + this._longHideTimeout = setTimeout(function () { + return _this._longHide = true; + }, 500); + }; + TooltipHTMLContent.prototype.hideLater = function (time) { + if (this._show && !(this._inContent && this._enterable) && !this._alwaysShowContent) { + if (time) { + this._hideDelay = time; + // Set show false to avoid invoke hideLater multiple times + this._show = false; + this._hideTimeout = setTimeout(bind(this.hide, this), time); + } else { + this.hide(); + } + } + }; + TooltipHTMLContent.prototype.isShow = function () { + return this._show; + }; + TooltipHTMLContent.prototype.dispose = function () { + clearTimeout(this._hideTimeout); + clearTimeout(this._longHideTimeout); + var parentNode = this.el.parentNode; + parentNode && parentNode.removeChild(this.el); + this.el = this._container = null; + }; + return TooltipHTMLContent; + }(); + + var TooltipRichContent = /** @class */function () { + function TooltipRichContent(api) { + this._show = false; + this._styleCoord = [0, 0, 0, 0]; + this._alwaysShowContent = false; + this._enterable = true; + this._zr = api.getZr(); + makeStyleCoord$1(this._styleCoord, this._zr, api.getWidth() / 2, api.getHeight() / 2); + } + /** + * Update when tooltip is rendered + */ + TooltipRichContent.prototype.update = function (tooltipModel) { + var alwaysShowContent = tooltipModel.get('alwaysShowContent'); + alwaysShowContent && this._moveIfResized(); + // update alwaysShowContent + this._alwaysShowContent = alwaysShowContent; + }; + TooltipRichContent.prototype.show = function () { + if (this._hideTimeout) { + clearTimeout(this._hideTimeout); + } + this.el.show(); + this._show = true; + }; + /** + * Set tooltip content + */ + TooltipRichContent.prototype.setContent = function (content, markupStyleCreator, tooltipModel, borderColor, arrowPosition) { + var _this = this; + if (isObject(content)) { + throwError("development" !== 'production' ? 'Passing DOM nodes as content is not supported in richText tooltip!' : ''); + } + if (this.el) { + this._zr.remove(this.el); + } + var textStyleModel = tooltipModel.getModel('textStyle'); + this.el = new ZRText({ + style: { + rich: markupStyleCreator.richTextStyles, + text: content, + lineHeight: 22, + borderWidth: 1, + borderColor: borderColor, + textShadowColor: textStyleModel.get('textShadowColor'), + fill: tooltipModel.get(['textStyle', 'color']), + padding: getPaddingFromTooltipModel(tooltipModel, 'richText'), + verticalAlign: 'top', + align: 'left' + }, + z: tooltipModel.get('z') + }); + each(['backgroundColor', 'borderRadius', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY'], function (propName) { + _this.el.style[propName] = tooltipModel.get(propName); + }); + each(['textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY'], function (propName) { + _this.el.style[propName] = textStyleModel.get(propName) || 0; + }); + this._zr.add(this.el); + var self = this; + this.el.on('mouseover', function () { + // clear the timeout in hideLater and keep showing tooltip + if (self._enterable) { + clearTimeout(self._hideTimeout); + self._show = true; + } + self._inContent = true; + }); + this.el.on('mouseout', function () { + if (self._enterable) { + if (self._show) { + self.hideLater(self._hideDelay); + } + } + self._inContent = false; + }); + }; + TooltipRichContent.prototype.setEnterable = function (enterable) { + this._enterable = enterable; + }; + TooltipRichContent.prototype.getSize = function () { + var el = this.el; + var bounding = this.el.getBoundingRect(); + // bounding rect does not include shadow. For renderMode richText, + // if overflow, it will be cut. So calculate them accurately. + var shadowOuterSize = calcShadowOuterSize(el.style); + return [bounding.width + shadowOuterSize.left + shadowOuterSize.right, bounding.height + shadowOuterSize.top + shadowOuterSize.bottom]; + }; + TooltipRichContent.prototype.moveTo = function (x, y) { + var el = this.el; + if (el) { + var styleCoord = this._styleCoord; + makeStyleCoord$1(styleCoord, this._zr, x, y); + x = styleCoord[0]; + y = styleCoord[1]; + var style = el.style; + var borderWidth = mathMaxWith0(style.borderWidth || 0); + var shadowOuterSize = calcShadowOuterSize(style); + // rich text x, y do not include border. + el.x = x + borderWidth + shadowOuterSize.left; + el.y = y + borderWidth + shadowOuterSize.top; + el.markRedraw(); + } + }; + /** + * when `alwaysShowContent` is true, + * move the tooltip after chart resized + */ + TooltipRichContent.prototype._moveIfResized = function () { + // The ratio of left to width + var ratioX = this._styleCoord[2]; + // The ratio of top to height + var ratioY = this._styleCoord[3]; + this.moveTo(ratioX * this._zr.getWidth(), ratioY * this._zr.getHeight()); + }; + TooltipRichContent.prototype.hide = function () { + if (this.el) { + this.el.hide(); + } + this._show = false; + }; + TooltipRichContent.prototype.hideLater = function (time) { + if (this._show && !(this._inContent && this._enterable) && !this._alwaysShowContent) { + if (time) { + this._hideDelay = time; + // Set show false to avoid invoke hideLater multiple times + this._show = false; + this._hideTimeout = setTimeout(bind(this.hide, this), time); + } else { + this.hide(); + } + } + }; + TooltipRichContent.prototype.isShow = function () { + return this._show; + }; + TooltipRichContent.prototype.dispose = function () { + this._zr.remove(this.el); + }; + return TooltipRichContent; + }(); + function mathMaxWith0(val) { + return Math.max(0, val); + } + function calcShadowOuterSize(style) { + var shadowBlur = mathMaxWith0(style.shadowBlur || 0); + var shadowOffsetX = mathMaxWith0(style.shadowOffsetX || 0); + var shadowOffsetY = mathMaxWith0(style.shadowOffsetY || 0); + return { + left: mathMaxWith0(shadowBlur - shadowOffsetX), + right: mathMaxWith0(shadowBlur + shadowOffsetX), + top: mathMaxWith0(shadowBlur - shadowOffsetY), + bottom: mathMaxWith0(shadowBlur + shadowOffsetY) + }; + } + function makeStyleCoord$1(out, zr, zrX, zrY) { + out[0] = zrX; + out[1] = zrY; + out[2] = out[0] / zr.getWidth(); + out[3] = out[1] / zr.getHeight(); + } + + var proxyRect = new Rect({ + shape: { + x: -1, + y: -1, + width: 2, + height: 2 + } + }); + var TooltipView = /** @class */function (_super) { + __extends(TooltipView, _super); + function TooltipView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TooltipView.type; + return _this; + } + TooltipView.prototype.init = function (ecModel, api) { + if (env.node || !api.getDom()) { + return; + } + var tooltipModel = ecModel.getComponent('tooltip'); + var renderMode = this._renderMode = getTooltipRenderMode(tooltipModel.get('renderMode')); + this._tooltipContent = renderMode === 'richText' ? new TooltipRichContent(api) : new TooltipHTMLContent(api, { + appendTo: tooltipModel.get('appendToBody', true) ? 'body' : tooltipModel.get('appendTo', true) + }); + }; + TooltipView.prototype.render = function (tooltipModel, ecModel, api) { + if (env.node || !api.getDom()) { + return; + } + // Reset + this.group.removeAll(); + this._tooltipModel = tooltipModel; + this._ecModel = ecModel; + this._api = api; + var tooltipContent = this._tooltipContent; + tooltipContent.update(tooltipModel); + tooltipContent.setEnterable(tooltipModel.get('enterable')); + this._initGlobalListener(); + this._keepShow(); + // PENDING + // `mousemove` event will be triggered very frequently when the mouse moves fast, + // which causes that the `updatePosition` function was also called frequently. + // In Chrome with devtools open and Firefox, tooltip looks laggy and shakes. See #14695 #16101 + // To avoid frequent triggering, + // consider throttling it in 50ms when transition is enabled + if (this._renderMode !== 'richText' && tooltipModel.get('transitionDuration')) { + createOrUpdate(this, '_updatePosition', 50, 'fixRate'); + } else { + clear(this, '_updatePosition'); + } + }; + TooltipView.prototype._initGlobalListener = function () { + var tooltipModel = this._tooltipModel; + var triggerOn = tooltipModel.get('triggerOn'); + register('itemTooltip', this._api, bind(function (currTrigger, e, dispatchAction) { + // If 'none', it is not controlled by mouse totally. + if (triggerOn !== 'none') { + if (triggerOn.indexOf(currTrigger) >= 0) { + this._tryShow(e, dispatchAction); + } else if (currTrigger === 'leave') { + this._hide(dispatchAction); + } + } + }, this)); + }; + TooltipView.prototype._keepShow = function () { + var tooltipModel = this._tooltipModel; + var ecModel = this._ecModel; + var api = this._api; + var triggerOn = tooltipModel.get('triggerOn'); + // Try to keep the tooltip show when refreshing + if (this._lastX != null && this._lastY != null + // When user is willing to control tooltip totally using API, + // self.manuallyShowTip({x, y}) might cause tooltip hide, + // which is not expected. + && triggerOn !== 'none' && triggerOn !== 'click') { + var self_1 = this; + clearTimeout(this._refreshUpdateTimeout); + this._refreshUpdateTimeout = setTimeout(function () { + // Show tip next tick after other charts are rendered + // In case highlight action has wrong result + // FIXME + !api.isDisposed() && self_1.manuallyShowTip(tooltipModel, ecModel, api, { + x: self_1._lastX, + y: self_1._lastY, + dataByCoordSys: self_1._lastDataByCoordSys + }); + }); + } + }; + /** + * Show tip manually by + * dispatchAction({ + * type: 'showTip', + * x: 10, + * y: 10 + * }); + * Or + * dispatchAction({ + * type: 'showTip', + * seriesIndex: 0, + * dataIndex or dataIndexInside or name + * }); + * + * TODO Batch + */ + TooltipView.prototype.manuallyShowTip = function (tooltipModel, ecModel, api, payload) { + if (payload.from === this.uid || env.node || !api.getDom()) { + return; + } + var dispatchAction = makeDispatchAction$1(payload, api); + // Reset ticket + this._ticket = ''; + // When triggered from axisPointer. + var dataByCoordSys = payload.dataByCoordSys; + var cmptRef = findComponentReference(payload, ecModel, api); + if (cmptRef) { + var rect = cmptRef.el.getBoundingRect().clone(); + rect.applyTransform(cmptRef.el.transform); + this._tryShow({ + offsetX: rect.x + rect.width / 2, + offsetY: rect.y + rect.height / 2, + target: cmptRef.el, + position: payload.position, + // When manully trigger, the mouse is not on the el, so we'd better to + // position tooltip on the bottom of the el and display arrow is possible. + positionDefault: 'bottom' + }, dispatchAction); + } else if (payload.tooltip && payload.x != null && payload.y != null) { + var el = proxyRect; + el.x = payload.x; + el.y = payload.y; + el.update(); + getECData(el).tooltipConfig = { + name: null, + option: payload.tooltip + }; + // Manually show tooltip while view is not using zrender elements. + this._tryShow({ + offsetX: payload.x, + offsetY: payload.y, + target: el + }, dispatchAction); + } else if (dataByCoordSys) { + this._tryShow({ + offsetX: payload.x, + offsetY: payload.y, + position: payload.position, + dataByCoordSys: dataByCoordSys, + tooltipOption: payload.tooltipOption + }, dispatchAction); + } else if (payload.seriesIndex != null) { + if (this._manuallyAxisShowTip(tooltipModel, ecModel, api, payload)) { + return; + } + var pointInfo = findPointFromSeries(payload, ecModel); + var cx = pointInfo.point[0]; + var cy = pointInfo.point[1]; + if (cx != null && cy != null) { + this._tryShow({ + offsetX: cx, + offsetY: cy, + target: pointInfo.el, + position: payload.position, + // When manully trigger, the mouse is not on the el, so we'd better to + // position tooltip on the bottom of the el and display arrow is possible. + positionDefault: 'bottom' + }, dispatchAction); + } + } else if (payload.x != null && payload.y != null) { + // FIXME + // should wrap dispatchAction like `axisPointer/globalListener` ? + api.dispatchAction({ + type: 'updateAxisPointer', + x: payload.x, + y: payload.y + }); + this._tryShow({ + offsetX: payload.x, + offsetY: payload.y, + position: payload.position, + target: api.getZr().findHover(payload.x, payload.y).target + }, dispatchAction); + } + }; + TooltipView.prototype.manuallyHideTip = function (tooltipModel, ecModel, api, payload) { + var tooltipContent = this._tooltipContent; + if (this._tooltipModel) { + tooltipContent.hideLater(this._tooltipModel.get('hideDelay')); + } + this._lastX = this._lastY = this._lastDataByCoordSys = null; + if (payload.from !== this.uid) { + this._hide(makeDispatchAction$1(payload, api)); + } + }; + // Be compatible with previous design, that is, when tooltip.type is 'axis' and + // dispatchAction 'showTip' with seriesIndex and dataIndex will trigger axis pointer + // and tooltip. + TooltipView.prototype._manuallyAxisShowTip = function (tooltipModel, ecModel, api, payload) { + var seriesIndex = payload.seriesIndex; + var dataIndex = payload.dataIndex; + // @ts-ignore + var coordSysAxesInfo = ecModel.getComponent('axisPointer').coordSysAxesInfo; + if (seriesIndex == null || dataIndex == null || coordSysAxesInfo == null) { + return; + } + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + if (!seriesModel) { + return; + } + var data = seriesModel.getData(); + var tooltipCascadedModel = buildTooltipModel([data.getItemModel(dataIndex), seriesModel, (seriesModel.coordinateSystem || {}).model], this._tooltipModel); + if (tooltipCascadedModel.get('trigger') !== 'axis') { + return; + } + api.dispatchAction({ + type: 'updateAxisPointer', + seriesIndex: seriesIndex, + dataIndex: dataIndex, + position: payload.position + }); + return true; + }; + TooltipView.prototype._tryShow = function (e, dispatchAction) { + var el = e.target; + var tooltipModel = this._tooltipModel; + if (!tooltipModel) { + return; + } + // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed + this._lastX = e.offsetX; + this._lastY = e.offsetY; + var dataByCoordSys = e.dataByCoordSys; + if (dataByCoordSys && dataByCoordSys.length) { + this._showAxisTooltip(dataByCoordSys, e); + } else if (el) { + var ecData = getECData(el); + if (ecData.ssrType === 'legend') { + // Don't trigger tooltip for legend tooltip item + return; + } + this._lastDataByCoordSys = null; + var seriesDispatcher_1; + var cmptDispatcher_1; + findEventDispatcher(el, function (target) { + // Always show item tooltip if mouse is on the element with dataIndex + if (getECData(target).dataIndex != null) { + seriesDispatcher_1 = target; + return true; + } + // Tooltip provided directly. Like legend. + if (getECData(target).tooltipConfig != null) { + cmptDispatcher_1 = target; + return true; + } + }, true); + if (seriesDispatcher_1) { + this._showSeriesItemTooltip(e, seriesDispatcher_1, dispatchAction); + } else if (cmptDispatcher_1) { + this._showComponentItemTooltip(e, cmptDispatcher_1, dispatchAction); + } else { + this._hide(dispatchAction); + } + } else { + this._lastDataByCoordSys = null; + this._hide(dispatchAction); + } + }; + TooltipView.prototype._showOrMove = function (tooltipModel, cb) { + // showDelay is used in this case: tooltip.enterable is set + // as true. User intent to move mouse into tooltip and click + // something. `showDelay` makes it easier to enter the content + // but tooltip do not move immediately. + var delay = tooltipModel.get('showDelay'); + cb = bind(cb, this); + clearTimeout(this._showTimout); + delay > 0 ? this._showTimout = setTimeout(cb, delay) : cb(); + }; + TooltipView.prototype._showAxisTooltip = function (dataByCoordSys, e) { + var ecModel = this._ecModel; + var globalTooltipModel = this._tooltipModel; + var point = [e.offsetX, e.offsetY]; + var singleTooltipModel = buildTooltipModel([e.tooltipOption], globalTooltipModel); + var renderMode = this._renderMode; + var cbParamsList = []; + var articleMarkup = createTooltipMarkup('section', { + blocks: [], + noHeader: true + }); + // Only for legacy: `Serise['formatTooltip']` returns a string. + var markupTextArrLegacy = []; + var markupStyleCreator = new TooltipMarkupStyleCreator(); + each(dataByCoordSys, function (itemCoordSys) { + each(itemCoordSys.dataByAxis, function (axisItem) { + var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex); + var axisValue = axisItem.value; + if (!axisModel || axisValue == null) { + return; + } + var axisValueLabel = getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt); + var axisSectionMarkup = createTooltipMarkup('section', { + header: axisValueLabel, + noHeader: !trim(axisValueLabel), + sortBlocks: true, + blocks: [] + }); + articleMarkup.blocks.push(axisSectionMarkup); + each(axisItem.seriesDataIndices, function (idxItem) { + var series = ecModel.getSeriesByIndex(idxItem.seriesIndex); + var dataIndex = idxItem.dataIndexInside; + var cbParams = series.getDataParams(dataIndex); + // Can't find data. + if (cbParams.dataIndex < 0) { + return; + } + cbParams.axisDim = axisItem.axisDim; + cbParams.axisIndex = axisItem.axisIndex; + cbParams.axisType = axisItem.axisType; + cbParams.axisId = axisItem.axisId; + cbParams.axisValue = getAxisRawValue(axisModel.axis, { + value: axisValue + }); + cbParams.axisValueLabel = axisValueLabel; + // Pre-create marker style for makers. Users can assemble richText + // text in `formatter` callback and use those markers style. + cbParams.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(cbParams.color), renderMode); + var seriesTooltipResult = normalizeTooltipFormatResult(series.formatTooltip(dataIndex, true, null)); + var frag = seriesTooltipResult.frag; + if (frag) { + var valueFormatter = buildTooltipModel([series], globalTooltipModel).get('valueFormatter'); + axisSectionMarkup.blocks.push(valueFormatter ? extend({ + valueFormatter: valueFormatter + }, frag) : frag); + } + if (seriesTooltipResult.text) { + markupTextArrLegacy.push(seriesTooltipResult.text); + } + cbParamsList.push(cbParams); + }); + }); + }); + // In most cases, the second axis is displays upper on the first one. + // So we reverse it to look better. + articleMarkup.blocks.reverse(); + markupTextArrLegacy.reverse(); + var positionExpr = e.position; + var orderMode = singleTooltipModel.get('order'); + var builtMarkupText = buildTooltipMarkup(articleMarkup, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), singleTooltipModel.get('textStyle')); + builtMarkupText && markupTextArrLegacy.unshift(builtMarkupText); + var blockBreak = renderMode === 'richText' ? '\n\n' : '
'; + var allMarkupText = markupTextArrLegacy.join(blockBreak); + this._showOrMove(singleTooltipModel, function () { + if (this._updateContentNotChangedOnAxis(dataByCoordSys, cbParamsList)) { + this._updatePosition(singleTooltipModel, positionExpr, point[0], point[1], this._tooltipContent, cbParamsList); + } else { + this._showTooltipContent(singleTooltipModel, allMarkupText, cbParamsList, Math.random() + '', point[0], point[1], positionExpr, null, markupStyleCreator); + } + }); + // Do not trigger events here, because this branch only be entered + // from dispatchAction. + }; + + TooltipView.prototype._showSeriesItemTooltip = function (e, dispatcher, dispatchAction) { + var ecModel = this._ecModel; + var ecData = getECData(dispatcher); + // Use dataModel in element if possible + // Used when mouseover on a element like markPoint or edge + // In which case, the data is not main data in series. + var seriesIndex = ecData.seriesIndex; + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + // For example, graph link. + var dataModel = ecData.dataModel || seriesModel; + var dataIndex = ecData.dataIndex; + var dataType = ecData.dataType; + var data = dataModel.getData(dataType); + var renderMode = this._renderMode; + var positionDefault = e.positionDefault; + var tooltipModel = buildTooltipModel([data.getItemModel(dataIndex), dataModel, seriesModel && (seriesModel.coordinateSystem || {}).model], this._tooltipModel, positionDefault ? { + position: positionDefault + } : null); + var tooltipTrigger = tooltipModel.get('trigger'); + if (tooltipTrigger != null && tooltipTrigger !== 'item') { + return; + } + var params = dataModel.getDataParams(dataIndex, dataType); + var markupStyleCreator = new TooltipMarkupStyleCreator(); + // Pre-create marker style for makers. Users can assemble richText + // text in `formatter` callback and use those markers style. + params.marker = markupStyleCreator.makeTooltipMarker('item', convertToColorString(params.color), renderMode); + var seriesTooltipResult = normalizeTooltipFormatResult(dataModel.formatTooltip(dataIndex, false, dataType)); + var orderMode = tooltipModel.get('order'); + var valueFormatter = tooltipModel.get('valueFormatter'); + var frag = seriesTooltipResult.frag; + var markupText = frag ? buildTooltipMarkup(valueFormatter ? extend({ + valueFormatter: valueFormatter + }, frag) : frag, markupStyleCreator, renderMode, orderMode, ecModel.get('useUTC'), tooltipModel.get('textStyle')) : seriesTooltipResult.text; + var asyncTicket = 'item_' + dataModel.name + '_' + dataIndex; + this._showOrMove(tooltipModel, function () { + this._showTooltipContent(tooltipModel, markupText, params, asyncTicket, e.offsetX, e.offsetY, e.position, e.target, markupStyleCreator); + }); + // FIXME + // duplicated showtip if manuallyShowTip is called from dispatchAction. + dispatchAction({ + type: 'showTip', + dataIndexInside: dataIndex, + dataIndex: data.getRawIndex(dataIndex), + seriesIndex: seriesIndex, + from: this.uid + }); + }; + TooltipView.prototype._showComponentItemTooltip = function (e, el, dispatchAction) { + var ecData = getECData(el); + var tooltipConfig = ecData.tooltipConfig; + var tooltipOpt = tooltipConfig.option || {}; + if (isString(tooltipOpt)) { + var content = tooltipOpt; + tooltipOpt = { + content: content, + // Fixed formatter + formatter: content + }; + } + var tooltipModelCascade = [tooltipOpt]; + var cmpt = this._ecModel.getComponent(ecData.componentMainType, ecData.componentIndex); + if (cmpt) { + tooltipModelCascade.push(cmpt); + } + // In most cases, component tooltip formatter has different params with series tooltip formatter, + // so that they cannot share the same formatter. Since the global tooltip formatter is used for series + // by convention, we do not use it as the default formatter for component. + tooltipModelCascade.push({ + formatter: tooltipOpt.content + }); + var positionDefault = e.positionDefault; + var subTooltipModel = buildTooltipModel(tooltipModelCascade, this._tooltipModel, positionDefault ? { + position: positionDefault + } : null); + var defaultHtml = subTooltipModel.get('content'); + var asyncTicket = Math.random() + ''; + // PENDING: this case do not support richText style yet. + var markupStyleCreator = new TooltipMarkupStyleCreator(); + // Do not check whether `trigger` is 'none' here, because `trigger` + // only works on coordinate system. In fact, we have not found case + // that requires setting `trigger` nothing on component yet. + this._showOrMove(subTooltipModel, function () { + // Use formatterParams from element defined in component + // Avoid users modify it. + var formatterParams = clone(subTooltipModel.get('formatterParams') || {}); + this._showTooltipContent(subTooltipModel, defaultHtml, formatterParams, asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator); + }); + // If not dispatch showTip, tip may be hide triggered by axis. + dispatchAction({ + type: 'showTip', + from: this.uid + }); + }; + TooltipView.prototype._showTooltipContent = function ( + // Use Model insteadof TooltipModel because this model may be from series or other options. + // Instead of top level tooltip. + tooltipModel, defaultHtml, params, asyncTicket, x, y, positionExpr, el, markupStyleCreator) { + // Reset ticket + this._ticket = ''; + if (!tooltipModel.get('showContent') || !tooltipModel.get('show')) { + return; + } + var tooltipContent = this._tooltipContent; + tooltipContent.setEnterable(tooltipModel.get('enterable')); + var formatter = tooltipModel.get('formatter'); + positionExpr = positionExpr || tooltipModel.get('position'); + var html = defaultHtml; + var nearPoint = this._getNearestPoint([x, y], params, tooltipModel.get('trigger'), tooltipModel.get('borderColor')); + var nearPointColor = nearPoint.color; + if (formatter) { + if (isString(formatter)) { + var useUTC = tooltipModel.ecModel.get('useUTC'); + var params0 = isArray(params) ? params[0] : params; + var isTimeAxis = params0 && params0.axisType && params0.axisType.indexOf('time') >= 0; + html = formatter; + if (isTimeAxis) { + html = format(params0.axisValue, html, useUTC); + } + html = formatTpl(html, params, true); + } else if (isFunction(formatter)) { + var callback = bind(function (cbTicket, html) { + if (cbTicket === this._ticket) { + tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr); + this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el); + } + }, this); + this._ticket = asyncTicket; + html = formatter(params, asyncTicket, callback); + } else { + html = formatter; + } + } + tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr); + tooltipContent.show(tooltipModel, nearPointColor); + this._updatePosition(tooltipModel, positionExpr, x, y, tooltipContent, params, el); + }; + TooltipView.prototype._getNearestPoint = function (point, tooltipDataParams, trigger, borderColor) { + if (trigger === 'axis' || isArray(tooltipDataParams)) { + return { + color: borderColor || (this._renderMode === 'html' ? '#fff' : 'none') + }; + } + if (!isArray(tooltipDataParams)) { + return { + color: borderColor || tooltipDataParams.color || tooltipDataParams.borderColor + }; + } + }; + TooltipView.prototype._updatePosition = function (tooltipModel, positionExpr, x, + // Mouse x + y, + // Mouse y + content, params, el) { + var viewWidth = this._api.getWidth(); + var viewHeight = this._api.getHeight(); + positionExpr = positionExpr || tooltipModel.get('position'); + var contentSize = content.getSize(); + var align = tooltipModel.get('align'); + var vAlign = tooltipModel.get('verticalAlign'); + var rect = el && el.getBoundingRect().clone(); + el && rect.applyTransform(el.transform); + if (isFunction(positionExpr)) { + // Callback of position can be an array or a string specify the position + positionExpr = positionExpr([x, y], params, content.el, rect, { + viewSize: [viewWidth, viewHeight], + contentSize: contentSize.slice() + }); + } + if (isArray(positionExpr)) { + x = parsePercent$1(positionExpr[0], viewWidth); + y = parsePercent$1(positionExpr[1], viewHeight); + } else if (isObject(positionExpr)) { + var boxLayoutPosition = positionExpr; + boxLayoutPosition.width = contentSize[0]; + boxLayoutPosition.height = contentSize[1]; + var layoutRect = getLayoutRect(boxLayoutPosition, { + width: viewWidth, + height: viewHeight + }); + x = layoutRect.x; + y = layoutRect.y; + align = null; + // When positionExpr is left/top/right/bottom, + // align and verticalAlign will not work. + vAlign = null; + } + // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element + else if (isString(positionExpr) && el) { + var pos = calcTooltipPosition(positionExpr, rect, contentSize, tooltipModel.get('borderWidth')); + x = pos[0]; + y = pos[1]; + } else { + var pos = refixTooltipPosition(x, y, content, viewWidth, viewHeight, align ? null : 20, vAlign ? null : 20); + x = pos[0]; + y = pos[1]; + } + align && (x -= isCenterAlign(align) ? contentSize[0] / 2 : align === 'right' ? contentSize[0] : 0); + vAlign && (y -= isCenterAlign(vAlign) ? contentSize[1] / 2 : vAlign === 'bottom' ? contentSize[1] : 0); + if (shouldTooltipConfine(tooltipModel)) { + var pos = confineTooltipPosition(x, y, content, viewWidth, viewHeight); + x = pos[0]; + y = pos[1]; + } + content.moveTo(x, y); + }; + // FIXME + // Should we remove this but leave this to user? + TooltipView.prototype._updateContentNotChangedOnAxis = function (dataByCoordSys, cbParamsList) { + var lastCoordSys = this._lastDataByCoordSys; + var lastCbParamsList = this._cbParamsList; + var contentNotChanged = !!lastCoordSys && lastCoordSys.length === dataByCoordSys.length; + contentNotChanged && each(lastCoordSys, function (lastItemCoordSys, indexCoordSys) { + var lastDataByAxis = lastItemCoordSys.dataByAxis || []; + var thisItemCoordSys = dataByCoordSys[indexCoordSys] || {}; + var thisDataByAxis = thisItemCoordSys.dataByAxis || []; + contentNotChanged = contentNotChanged && lastDataByAxis.length === thisDataByAxis.length; + contentNotChanged && each(lastDataByAxis, function (lastItem, indexAxis) { + var thisItem = thisDataByAxis[indexAxis] || {}; + var lastIndices = lastItem.seriesDataIndices || []; + var newIndices = thisItem.seriesDataIndices || []; + contentNotChanged = contentNotChanged && lastItem.value === thisItem.value && lastItem.axisType === thisItem.axisType && lastItem.axisId === thisItem.axisId && lastIndices.length === newIndices.length; + contentNotChanged && each(lastIndices, function (lastIdxItem, j) { + var newIdxItem = newIndices[j]; + contentNotChanged = contentNotChanged && lastIdxItem.seriesIndex === newIdxItem.seriesIndex && lastIdxItem.dataIndex === newIdxItem.dataIndex; + }); + // check is cbParams data value changed + lastCbParamsList && each(lastItem.seriesDataIndices, function (idxItem) { + var seriesIdx = idxItem.seriesIndex; + var cbParams = cbParamsList[seriesIdx]; + var lastCbParams = lastCbParamsList[seriesIdx]; + if (cbParams && lastCbParams && lastCbParams.data !== cbParams.data) { + contentNotChanged = false; + } + }); + }); + }); + this._lastDataByCoordSys = dataByCoordSys; + this._cbParamsList = cbParamsList; + return !!contentNotChanged; + }; + TooltipView.prototype._hide = function (dispatchAction) { + // Do not directly hideLater here, because this behavior may be prevented + // in dispatchAction when showTip is dispatched. + // FIXME + // duplicated hideTip if manuallyHideTip is called from dispatchAction. + this._lastDataByCoordSys = null; + dispatchAction({ + type: 'hideTip', + from: this.uid + }); + }; + TooltipView.prototype.dispose = function (ecModel, api) { + if (env.node || !api.getDom()) { + return; + } + clear(this, '_updatePosition'); + this._tooltipContent.dispose(); + unregister('itemTooltip', api); + }; + TooltipView.type = 'tooltip'; + return TooltipView; + }(ComponentView); + /** + * From top to bottom. (the last one should be globalTooltipModel); + */ + function buildTooltipModel(modelCascade, globalTooltipModel, defaultTooltipOption) { + // Last is always tooltip model. + var ecModel = globalTooltipModel.ecModel; + var resultModel; + if (defaultTooltipOption) { + resultModel = new Model(defaultTooltipOption, ecModel, ecModel); + resultModel = new Model(globalTooltipModel.option, resultModel, ecModel); + } else { + resultModel = globalTooltipModel; + } + for (var i = modelCascade.length - 1; i >= 0; i--) { + var tooltipOpt = modelCascade[i]; + if (tooltipOpt) { + if (tooltipOpt instanceof Model) { + tooltipOpt = tooltipOpt.get('tooltip', true); + } + // In each data item tooltip can be simply write: + // { + // value: 10, + // tooltip: 'Something you need to know' + // } + if (isString(tooltipOpt)) { + tooltipOpt = { + formatter: tooltipOpt + }; + } + if (tooltipOpt) { + resultModel = new Model(tooltipOpt, resultModel, ecModel); + } + } + } + return resultModel; + } + function makeDispatchAction$1(payload, api) { + return payload.dispatchAction || bind(api.dispatchAction, api); + } + function refixTooltipPosition(x, y, content, viewWidth, viewHeight, gapH, gapV) { + var size = content.getSize(); + var width = size[0]; + var height = size[1]; + if (gapH != null) { + // Add extra 2 pixels for this case: + // At present the "values" in default tooltip are using CSS `float: right`. + // When the right edge of the tooltip box is on the right side of the + // viewport, the `float` layout might push the "values" to the second line. + if (x + width + gapH + 2 > viewWidth) { + x -= width + gapH; + } else { + x += gapH; + } + } + if (gapV != null) { + if (y + height + gapV > viewHeight) { + y -= height + gapV; + } else { + y += gapV; + } + } + return [x, y]; + } + function confineTooltipPosition(x, y, content, viewWidth, viewHeight) { + var size = content.getSize(); + var width = size[0]; + var height = size[1]; + x = Math.min(x + width, viewWidth) - width; + y = Math.min(y + height, viewHeight) - height; + x = Math.max(x, 0); + y = Math.max(y, 0); + return [x, y]; + } + function calcTooltipPosition(position, rect, contentSize, borderWidth) { + var domWidth = contentSize[0]; + var domHeight = contentSize[1]; + var offset = Math.ceil(Math.SQRT2 * borderWidth) + 8; + var x = 0; + var y = 0; + var rectWidth = rect.width; + var rectHeight = rect.height; + switch (position) { + case 'inside': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y + rectHeight / 2 - domHeight / 2; + break; + case 'top': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y - domHeight - offset; + break; + case 'bottom': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y + rectHeight + offset; + break; + case 'left': + x = rect.x - domWidth - offset; + y = rect.y + rectHeight / 2 - domHeight / 2; + break; + case 'right': + x = rect.x + rectWidth + offset; + y = rect.y + rectHeight / 2 - domHeight / 2; + } + return [x, y]; + } + function isCenterAlign(align) { + return align === 'center' || align === 'middle'; + } + /** + * Find target component by payload like: + * ```js + * { legendId: 'some_id', name: 'xxx' } + * { toolboxIndex: 1, name: 'xxx' } + * { geoName: 'some_name', name: 'xxx' } + * ``` + * PENDING: at present only + * + * If not found, return null/undefined. + */ + function findComponentReference(payload, ecModel, api) { + var queryOptionMap = preParseFinder(payload).queryOptionMap; + var componentMainType = queryOptionMap.keys()[0]; + if (!componentMainType || componentMainType === 'series') { + return; + } + var queryResult = queryReferringComponents(ecModel, componentMainType, queryOptionMap.get(componentMainType), { + useDefault: false, + enableAll: false, + enableNone: false + }); + var model = queryResult.models[0]; + if (!model) { + return; + } + var view = api.getViewOfComponentModel(model); + var el; + view.group.traverse(function (subEl) { + var tooltipConfig = getECData(subEl).tooltipConfig; + if (tooltipConfig && tooltipConfig.name === payload.name) { + el = subEl; + return true; // stop + } + }); + + if (el) { + return { + componentMainType: componentMainType, + componentIndex: model.componentIndex, + el: el + }; + } + } + + function install$A(registers) { + use(install$s); + registers.registerComponentModel(TooltipModel); + registers.registerComponentView(TooltipView); + /** + * @action + * @property {string} type + * @property {number} seriesIndex + * @property {number} dataIndex + * @property {number} [x] + * @property {number} [y] + */ + registers.registerAction({ + type: 'showTip', + event: 'showTip', + update: 'tooltip:manuallyShowTip' + }, noop); + registers.registerAction({ + type: 'hideTip', + event: 'hideTip', + update: 'tooltip:manuallyHideTip' + }, noop); + } + + var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear']; + function brushPreprocessor(option, isNew) { + var brushComponents = normalizeToArray(option ? option.brush : []); + if (!brushComponents.length) { + return; + } + var brushComponentSpecifiedBtns = []; + each(brushComponents, function (brushOpt) { + var tbs = brushOpt.hasOwnProperty('toolbox') ? brushOpt.toolbox : []; + if (tbs instanceof Array) { + brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs); + } + }); + var toolbox = option && option.toolbox; + if (isArray(toolbox)) { + toolbox = toolbox[0]; + } + if (!toolbox) { + toolbox = { + feature: {} + }; + option.toolbox = [toolbox]; + } + var toolboxFeature = toolbox.feature || (toolbox.feature = {}); + var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {}); + var brushTypes = toolboxBrush.type || (toolboxBrush.type = []); + brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns); + removeDuplicate(brushTypes); + if (isNew && !brushTypes.length) { + brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS); + } + } + function removeDuplicate(arr) { + var map = {}; + each(arr, function (val) { + map[val] = 1; + }); + arr.length = 0; + each(map, function (flag, val) { + arr.push(val); + }); + } + + var each$b = each; + function hasKeys(obj) { + if (obj) { + for (var name_1 in obj) { + if (obj.hasOwnProperty(name_1)) { + return true; + } + } + } + } + function createVisualMappings(option, stateList, supplementVisualOption) { + var visualMappings = {}; + each$b(stateList, function (state) { + var mappings = visualMappings[state] = createMappings(); + each$b(option[state], function (visualData, visualType) { + if (!VisualMapping.isValidType(visualType)) { + return; + } + var mappingOption = { + type: visualType, + visual: visualData + }; + supplementVisualOption && supplementVisualOption(mappingOption, state); + mappings[visualType] = new VisualMapping(mappingOption); + // Prepare a alpha for opacity, for some case that opacity + // is not supported, such as rendering using gradient color. + if (visualType === 'opacity') { + mappingOption = clone(mappingOption); + mappingOption.type = 'colorAlpha'; + mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption); + } + }); + }); + return visualMappings; + function createMappings() { + var Creater = function () {}; + // Make sure hidden fields will not be visited by + // object iteration (with hasOwnProperty checking). + Creater.prototype.__hidden = Creater.prototype; + var obj = new Creater(); + return obj; + } + } + function replaceVisualOption(thisOption, newOption, keys) { + // Visual attributes merge is not supported, otherwise it + // brings overcomplicated merge logic. See #2853. So if + // newOption has anyone of these keys, all of these keys + // will be reset. Otherwise, all keys remain. + var has; + each(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + has = true; + } + }); + has && each(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + thisOption[key] = clone(newOption[key]); + } else { + delete thisOption[key]; + } + }); + } + /** + * @param stateList + * @param visualMappings + * @param list + * @param getValueState param: valueOrIndex, return: state. + * @param scope Scope for getValueState + * @param dimension Concrete dimension, if used. + */ + // ???! handle brush? + function applyVisual(stateList, visualMappings, data, getValueState, scope, dimension) { + var visualTypesMap = {}; + each(stateList, function (state) { + var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); + visualTypesMap[state] = visualTypes; + }); + var dataIndex; + function getVisual(key) { + return getItemVisualFromData(data, dataIndex, key); + } + function setVisual(key, value) { + setItemVisualFromData(data, dataIndex, key, value); + } + if (dimension == null) { + data.each(eachItem); + } else { + data.each([dimension], eachItem); + } + function eachItem(valueOrIndex, index) { + dataIndex = dimension == null ? valueOrIndex // First argument is index + : index; + var rawDataItem = data.getRawDataItem(dataIndex); + // Consider performance + // @ts-ignore + if (rawDataItem && rawDataItem.visualMap === false) { + return; + } + var valueState = getValueState.call(scope, valueOrIndex); + var mappings = visualMappings[valueState]; + var visualTypes = visualTypesMap[valueState]; + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + mappings[type] && mappings[type].applyVisual(valueOrIndex, getVisual, setVisual); + } + } + } + /** + * @param data + * @param stateList + * @param visualMappings > + * @param getValueState param: valueOrIndex, return: state. + * @param dim dimension or dimension index. + */ + function incrementalApplyVisual(stateList, visualMappings, getValueState, dim) { + var visualTypesMap = {}; + each(stateList, function (state) { + var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); + visualTypesMap[state] = visualTypes; + }); + return { + progress: function progress(params, data) { + var dimIndex; + if (dim != null) { + dimIndex = data.getDimensionIndex(dim); + } + function getVisual(key) { + return getItemVisualFromData(data, dataIndex, key); + } + function setVisual(key, value) { + setItemVisualFromData(data, dataIndex, key, value); + } + var dataIndex; + var store = data.getStore(); + while ((dataIndex = params.next()) != null) { + var rawDataItem = data.getRawDataItem(dataIndex); + // Consider performance + // @ts-ignore + if (rawDataItem && rawDataItem.visualMap === false) { + continue; + } + var value = dim != null ? store.get(dimIndex, dataIndex) : dataIndex; + var valueState = getValueState(value); + var mappings = visualMappings[valueState]; + var visualTypes = visualTypesMap[valueState]; + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + mappings[type] && mappings[type].applyVisual(value, getVisual, setVisual); + } + } + } + }; + } + + function makeBrushCommonSelectorForSeries(area) { + var brushType = area.brushType; + // Do not use function binding or curry for performance. + var selectors = { + point: function (itemLayout) { + return selector[brushType].point(itemLayout, selectors, area); + }, + rect: function (itemLayout) { + return selector[brushType].rect(itemLayout, selectors, area); + } + }; + return selectors; + } + var selector = { + lineX: getLineSelectors(0), + lineY: getLineSelectors(1), + rect: { + point: function (itemLayout, selectors, area) { + return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + return itemLayout && area.boundingRect.intersect(itemLayout); + } + }, + polygon: { + point: function (itemLayout, selectors, area) { + return itemLayout && area.boundingRect.contain(itemLayout[0], itemLayout[1]) && contain$2(area.range, itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + var points = area.range; + if (!itemLayout || points.length <= 1) { + return false; + } + var x = itemLayout.x; + var y = itemLayout.y; + var width = itemLayout.width; + var height = itemLayout.height; + var p = points[0]; + if (contain$2(points, x, y) || contain$2(points, x + width, y) || contain$2(points, x, y + height) || contain$2(points, x + width, y + height) || BoundingRect.create(itemLayout).contain(p[0], p[1]) || linePolygonIntersect(x, y, x + width, y, points) || linePolygonIntersect(x, y, x, y + height, points) || linePolygonIntersect(x + width, y, x + width, y + height, points) || linePolygonIntersect(x, y + height, x + width, y + height, points)) { + return true; + } + } + } + }; + function getLineSelectors(xyIndex) { + var xy = ['x', 'y']; + var wh = ['width', 'height']; + return { + point: function (itemLayout, selectors, area) { + if (itemLayout) { + var range = area.range; + var p = itemLayout[xyIndex]; + return inLineRange(p, range); + } + }, + rect: function (itemLayout, selectors, area) { + if (itemLayout) { + var range = area.range; + var layoutRange = [itemLayout[xy[xyIndex]], itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]]]; + layoutRange[1] < layoutRange[0] && layoutRange.reverse(); + return inLineRange(layoutRange[0], range) || inLineRange(layoutRange[1], range) || inLineRange(range[0], layoutRange) || inLineRange(range[1], layoutRange); + } + } + }; + } + function inLineRange(p, range) { + return range[0] <= p && p <= range[1]; + } + + var STATE_LIST = ['inBrush', 'outOfBrush']; + var DISPATCH_METHOD = '__ecBrushSelect'; + var DISPATCH_FLAG = '__ecInBrushSelectEvent'; + function layoutCovers(ecModel) { + ecModel.eachComponent({ + mainType: 'brush' + }, function (brushModel) { + var brushTargetManager = brushModel.brushTargetManager = new BrushTargetManager(brushModel.option, ecModel); + brushTargetManager.setInputRanges(brushModel.areas, ecModel); + }); + } + /** + * Register the visual encoding if this modules required. + */ + function brushVisual(ecModel, api, payload) { + var brushSelected = []; + var throttleType; + var throttleDelay; + ecModel.eachComponent({ + mainType: 'brush' + }, function (brushModel) { + payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption(payload.key === 'brush' ? payload.brushOption : { + brushType: false + }); + }); + layoutCovers(ecModel); + ecModel.eachComponent({ + mainType: 'brush' + }, function (brushModel, brushIndex) { + var thisBrushSelected = { + brushId: brushModel.id, + brushIndex: brushIndex, + brushName: brushModel.name, + areas: clone(brushModel.areas), + selected: [] + }; + // Every brush component exists in event params, convenient + // for user to find by index. + brushSelected.push(thisBrushSelected); + var brushOption = brushModel.option; + var brushLink = brushOption.brushLink; + var linkedSeriesMap = []; + var selectedDataIndexForLink = []; + var rangeInfoBySeries = []; + var hasBrushExists = false; + if (!brushIndex) { + // Only the first throttle setting works. + throttleType = brushOption.throttleType; + throttleDelay = brushOption.throttleDelay; + } + // Add boundingRect and selectors to range. + var areas = map(brushModel.areas, function (area) { + var builder = boundingRectBuilders[area.brushType]; + var selectableArea = defaults({ + boundingRect: builder ? builder(area) : void 0 + }, area); + selectableArea.selectors = makeBrushCommonSelectorForSeries(selectableArea); + return selectableArea; + }); + var visualMappings = createVisualMappings(brushModel.option, STATE_LIST, function (mappingOption) { + mappingOption.mappingMethod = 'fixed'; + }); + isArray(brushLink) && each(brushLink, function (seriesIndex) { + linkedSeriesMap[seriesIndex] = 1; + }); + function linkOthers(seriesIndex) { + return brushLink === 'all' || !!linkedSeriesMap[seriesIndex]; + } + // If no supported brush or no brush on the series, + // all visuals should be in original state. + function brushed(rangeInfoList) { + return !!rangeInfoList.length; + } + /** + * Logic for each series: (If the logic has to be modified one day, do it carefully!) + * + * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers ) => StepA: ┬record, ┬ StepB: ┬visualByRecord. + * !brushed┘ ├hasBrushExist ┤ └nothing,┘ ├visualByRecord. + * └!hasBrushExist┘ └nothing. + * ( !brushed && ┬hasBrushExist ┬ && linkOthers ) => StepA: nothing, StepB: ┬visualByRecord. + * └!hasBrushExist┘ └nothing. + * ( brushed ┬ && !linkOthers ) => StepA: nothing, StepB: ┬visualByCheck. + * !brushed┘ └nothing. + * ( !brushed && !linkOthers ) => StepA: nothing, StepB: nothing. + */ + // Step A + ecModel.eachSeries(function (seriesModel, seriesIndex) { + var rangeInfoList = rangeInfoBySeries[seriesIndex] = []; + seriesModel.subType === 'parallel' ? stepAParallel(seriesModel, seriesIndex) : stepAOthers(seriesModel, seriesIndex, rangeInfoList); + }); + function stepAParallel(seriesModel, seriesIndex) { + var coordSys = seriesModel.coordinateSystem; + hasBrushExists = hasBrushExists || coordSys.hasAxisBrushed(); + linkOthers(seriesIndex) && coordSys.eachActiveState(seriesModel.getData(), function (activeState, dataIndex) { + activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1); + }); + } + function stepAOthers(seriesModel, seriesIndex, rangeInfoList) { + if (!seriesModel.brushSelector || brushModelNotControll(brushModel, seriesIndex)) { + return; + } + each(areas, function (area) { + if (brushModel.brushTargetManager.controlSeries(area, seriesModel, ecModel)) { + rangeInfoList.push(area); + } + hasBrushExists = hasBrushExists || brushed(rangeInfoList); + }); + if (linkOthers(seriesIndex) && brushed(rangeInfoList)) { + var data_1 = seriesModel.getData(); + data_1.each(function (dataIndex) { + if (checkInRange(seriesModel, rangeInfoList, data_1, dataIndex)) { + selectedDataIndexForLink[dataIndex] = 1; + } + }); + } + } + // Step B + ecModel.eachSeries(function (seriesModel, seriesIndex) { + var seriesBrushSelected = { + seriesId: seriesModel.id, + seriesIndex: seriesIndex, + seriesName: seriesModel.name, + dataIndex: [] + }; + // Every series exists in event params, convenient + // for user to find series by seriesIndex. + thisBrushSelected.selected.push(seriesBrushSelected); + var rangeInfoList = rangeInfoBySeries[seriesIndex]; + var data = seriesModel.getData(); + var getValueState = linkOthers(seriesIndex) ? function (dataIndex) { + return selectedDataIndexForLink[dataIndex] ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush'; + } : function (dataIndex) { + return checkInRange(seriesModel, rangeInfoList, data, dataIndex) ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') : 'outOfBrush'; + }; + // If no supported brush or no brush, all visuals are in original state. + (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) && applyVisual(STATE_LIST, visualMappings, data, getValueState); + }); + }); + dispatchAction(api, throttleType, throttleDelay, brushSelected, payload); + } + function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) { + // This event will not be triggered when `setOpion`, otherwise dead lock may + // triggered when do `setOption` in event listener, which we do not find + // satisfactory way to solve yet. Some considered resolutions: + // (a) Diff with prevoius selected data ant only trigger event when changed. + // But store previous data and diff precisely (i.e., not only by dataIndex, but + // also detect value changes in selected data) might bring complexity or fragility. + // (b) Use spectial param like `silent` to suppress event triggering. + // But such kind of volatile param may be weird in `setOption`. + if (!payload) { + return; + } + var zr = api.getZr(); + if (zr[DISPATCH_FLAG]) { + return; + } + if (!zr[DISPATCH_METHOD]) { + zr[DISPATCH_METHOD] = doDispatch; + } + var fn = createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType); + fn(api, brushSelected); + } + function doDispatch(api, brushSelected) { + if (!api.isDisposed()) { + var zr = api.getZr(); + zr[DISPATCH_FLAG] = true; + api.dispatchAction({ + type: 'brushSelect', + batch: brushSelected + }); + zr[DISPATCH_FLAG] = false; + } + } + function checkInRange(seriesModel, rangeInfoList, data, dataIndex) { + for (var i = 0, len = rangeInfoList.length; i < len; i++) { + var area = rangeInfoList[i]; + if (seriesModel.brushSelector(dataIndex, data, area.selectors, area)) { + return true; + } + } + } + function brushModelNotControll(brushModel, seriesIndex) { + var seriesIndices = brushModel.option.seriesIndex; + return seriesIndices != null && seriesIndices !== 'all' && (isArray(seriesIndices) ? indexOf(seriesIndices, seriesIndex) < 0 : seriesIndex !== seriesIndices); + } + var boundingRectBuilders = { + rect: function (area) { + return getBoundingRectFromMinMax(area.range); + }, + polygon: function (area) { + var minMax; + var range = area.range; + for (var i = 0, len = range.length; i < len; i++) { + minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]]; + var rg = range[i]; + rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]); + rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]); + rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]); + rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]); + } + return minMax && getBoundingRectFromMinMax(minMax); + } + }; + function getBoundingRectFromMinMax(minMax) { + return new BoundingRect(minMax[0][0], minMax[1][0], minMax[0][1] - minMax[0][0], minMax[1][1] - minMax[1][0]); + } + + var BrushView = /** @class */function (_super) { + __extends(BrushView, _super); + function BrushView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = BrushView.type; + return _this; + } + BrushView.prototype.init = function (ecModel, api) { + this.ecModel = ecModel; + this.api = api; + this.model; + (this._brushController = new BrushController(api.getZr())).on('brush', bind(this._onBrush, this)).mount(); + }; + BrushView.prototype.render = function (brushModel, ecModel, api, payload) { + this.model = brushModel; + this._updateController(brushModel, ecModel, api, payload); + }; + BrushView.prototype.updateTransform = function (brushModel, ecModel, api, payload) { + // PENDING: `updateTransform` is a little tricky, whose layout need + // to be calculate mandatorily and other stages will not be performed. + // Take care the correctness of the logic. See #11754 . + layoutCovers(ecModel); + this._updateController(brushModel, ecModel, api, payload); + }; + BrushView.prototype.updateVisual = function (brushModel, ecModel, api, payload) { + this.updateTransform(brushModel, ecModel, api, payload); + }; + BrushView.prototype.updateView = function (brushModel, ecModel, api, payload) { + this._updateController(brushModel, ecModel, api, payload); + }; + BrushView.prototype._updateController = function (brushModel, ecModel, api, payload) { + // Do not update controller when drawing. + (!payload || payload.$from !== brushModel.id) && this._brushController.setPanels(brushModel.brushTargetManager.makePanelOpts(api)).enableBrush(brushModel.brushOption).updateCovers(brushModel.areas.slice()); + }; + // updateLayout: updateController, + // updateVisual: updateController, + BrushView.prototype.dispose = function () { + this._brushController.dispose(); + }; + BrushView.prototype._onBrush = function (eventParam) { + var modelId = this.model.id; + var areas = this.model.brushTargetManager.setOutputRanges(eventParam.areas, this.ecModel); + // Action is not dispatched on drag end, because the drag end + // emits the same params with the last drag move event, and + // may have some delay when using touch pad, which makes + // animation not smooth (when using debounce). + (!eventParam.isEnd || eventParam.removeOnClick) && this.api.dispatchAction({ + type: 'brush', + brushId: modelId, + areas: clone(areas), + $from: modelId + }); + eventParam.isEnd && this.api.dispatchAction({ + type: 'brushEnd', + brushId: modelId, + areas: clone(areas), + $from: modelId + }); + }; + BrushView.type = 'brush'; + return BrushView; + }(ComponentView); + + var DEFAULT_OUT_OF_BRUSH_COLOR = '#ddd'; + var BrushModel = /** @class */function (_super) { + __extends(BrushModel, _super); + function BrushModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = BrushModel.type; + /** + * @readOnly + */ + _this.areas = []; + /** + * Current brush painting area settings. + * @readOnly + */ + _this.brushOption = {}; + return _this; + } + BrushModel.prototype.optionUpdated = function (newOption, isInit) { + var thisOption = this.option; + !isInit && replaceVisualOption(thisOption, newOption, ['inBrush', 'outOfBrush']); + var inBrush = thisOption.inBrush = thisOption.inBrush || {}; + // Always give default visual, consider setOption at the second time. + thisOption.outOfBrush = thisOption.outOfBrush || { + color: DEFAULT_OUT_OF_BRUSH_COLOR + }; + if (!inBrush.hasOwnProperty('liftZ')) { + // Bigger than the highlight z lift, otherwise it will + // be effected by the highlight z when brush. + inBrush.liftZ = 5; + } + }; + /** + * If `areas` is null/undefined, range state remain. + */ + BrushModel.prototype.setAreas = function (areas) { + if ("development" !== 'production') { + assert(isArray(areas)); + each(areas, function (area) { + assert(area.brushType, 'Illegal areas'); + }); + } + // If areas is null/undefined, range state remain. + // This helps user to dispatchAction({type: 'brush'}) with no areas + // set but just want to get the current brush select info from a `brush` event. + if (!areas) { + return; + } + this.areas = map(areas, function (area) { + return generateBrushOption(this.option, area); + }, this); + }; + /** + * Set the current painting brush option. + */ + BrushModel.prototype.setBrushOption = function (brushOption) { + this.brushOption = generateBrushOption(this.option, brushOption); + this.brushType = this.brushOption.brushType; + }; + BrushModel.type = 'brush'; + BrushModel.dependencies = ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series']; + BrushModel.defaultOption = { + seriesIndex: 'all', + brushType: 'rect', + brushMode: 'single', + transformable: true, + brushStyle: { + borderWidth: 1, + color: 'rgba(210,219,238,0.3)', + borderColor: '#D2DBEE' + }, + throttleType: 'fixRate', + throttleDelay: 0, + removeOnClick: true, + z: 10000 + }; + return BrushModel; + }(ComponentModel); + function generateBrushOption(option, brushOption) { + return merge({ + brushType: option.brushType, + brushMode: option.brushMode, + transformable: option.transformable, + brushStyle: new Model(option.brushStyle).getItemStyle(), + removeOnClick: option.removeOnClick, + z: option.z + }, brushOption, true); + } + + var ICON_TYPES = ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear']; + var BrushFeature = /** @class */function (_super) { + __extends(BrushFeature, _super); + function BrushFeature() { + return _super !== null && _super.apply(this, arguments) || this; + } + BrushFeature.prototype.render = function (featureModel, ecModel, api) { + var brushType; + var brushMode; + var isBrushed; + ecModel.eachComponent({ + mainType: 'brush' + }, function (brushModel) { + brushType = brushModel.brushType; + brushMode = brushModel.brushOption.brushMode || 'single'; + isBrushed = isBrushed || !!brushModel.areas.length; + }); + this._brushType = brushType; + this._brushMode = brushMode; + each(featureModel.get('type', true), function (type) { + featureModel.setIconStatus(type, (type === 'keep' ? brushMode === 'multiple' : type === 'clear' ? isBrushed : type === brushType) ? 'emphasis' : 'normal'); + }); + }; + BrushFeature.prototype.updateView = function (featureModel, ecModel, api) { + this.render(featureModel, ecModel, api); + }; + BrushFeature.prototype.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon', true); + var icons = {}; + each(model.get('type', true), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; + }; + BrushFeature.prototype.onclick = function (ecModel, api, type) { + var brushType = this._brushType; + var brushMode = this._brushMode; + if (type === 'clear') { + // Trigger parallel action firstly + api.dispatchAction({ + type: 'axisAreaSelect', + intervals: [] + }); + api.dispatchAction({ + type: 'brush', + command: 'clear', + // Clear all areas of all brush components. + areas: [] + }); + } else { + api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'brush', + brushOption: { + brushType: type === 'keep' ? brushType : brushType === type ? false : type, + brushMode: type === 'keep' ? brushMode === 'multiple' ? 'single' : 'multiple' : brushMode + } + }); + } + }; + BrushFeature.getDefaultOption = function (ecModel) { + var defaultOption = { + show: true, + type: ICON_TYPES.slice(), + icon: { + /* eslint-disable */ + rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13', + polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2', + lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4', + lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4', + keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z', + clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line + /* eslint-enable */ + }, + + // `rect`, `polygon`, `lineX`, `lineY`, `keep`, `clear` + title: ecModel.getLocaleModel().get(['toolbox', 'brush', 'title']) + }; + return defaultOption; + }; + return BrushFeature; + }(ToolboxFeature); + + function install$B(registers) { + registers.registerComponentView(BrushView); + registers.registerComponentModel(BrushModel); + registers.registerPreprocessor(brushPreprocessor); + registers.registerVisual(registers.PRIORITY.VISUAL.BRUSH, brushVisual); + registers.registerAction({ + type: 'brush', + event: 'brush', + update: 'updateVisual' + }, function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'brush', + query: payload + }, function (brushModel) { + brushModel.setAreas(payload.areas); + }); + }); + /** + * payload: { + * brushComponents: [ + * { + * brushId, + * brushIndex, + * brushName, + * series: [ + * { + * seriesId, + * seriesIndex, + * seriesName, + * rawIndices: [21, 34, ...] + * }, + * ... + * ] + * }, + * ... + * ] + * } + */ + registers.registerAction({ + type: 'brushSelect', + event: 'brushSelected', + update: 'none' + }, noop); + registers.registerAction({ + type: 'brushEnd', + event: 'brushEnd', + update: 'none' + }, noop); + registerFeature('brush', BrushFeature); + } + + var TitleModel = /** @class */function (_super) { + __extends(TitleModel, _super); + function TitleModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TitleModel.type; + _this.layoutMode = { + type: 'box', + ignoreSize: true + }; + return _this; + } + TitleModel.type = 'title'; + TitleModel.defaultOption = { + // zlevel: 0, + z: 6, + show: true, + text: '', + target: 'blank', + subtext: '', + subtarget: 'blank', + left: 0, + top: 0, + backgroundColor: 'rgba(0,0,0,0)', + borderColor: '#ccc', + borderWidth: 0, + padding: 5, + itemGap: 10, + textStyle: { + fontSize: 18, + fontWeight: 'bold', + color: '#464646' + }, + subtextStyle: { + fontSize: 12, + color: '#6E7079' + } + }; + return TitleModel; + }(ComponentModel); + // View + var TitleView = /** @class */function (_super) { + __extends(TitleView, _super); + function TitleView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TitleView.type; + return _this; + } + TitleView.prototype.render = function (titleModel, ecModel, api) { + this.group.removeAll(); + if (!titleModel.get('show')) { + return; + } + var group = this.group; + var textStyleModel = titleModel.getModel('textStyle'); + var subtextStyleModel = titleModel.getModel('subtextStyle'); + var textAlign = titleModel.get('textAlign'); + var textVerticalAlign = retrieve2(titleModel.get('textBaseline'), titleModel.get('textVerticalAlign')); + var textEl = new ZRText({ + style: createTextStyle(textStyleModel, { + text: titleModel.get('text'), + fill: textStyleModel.getTextColor() + }, { + disableBox: true + }), + z2: 10 + }); + var textRect = textEl.getBoundingRect(); + var subText = titleModel.get('subtext'); + var subTextEl = new ZRText({ + style: createTextStyle(subtextStyleModel, { + text: subText, + fill: subtextStyleModel.getTextColor(), + y: textRect.height + titleModel.get('itemGap'), + verticalAlign: 'top' + }, { + disableBox: true + }), + z2: 10 + }); + var link = titleModel.get('link'); + var sublink = titleModel.get('sublink'); + var triggerEvent = titleModel.get('triggerEvent', true); + textEl.silent = !link && !triggerEvent; + subTextEl.silent = !sublink && !triggerEvent; + if (link) { + textEl.on('click', function () { + windowOpen(link, '_' + titleModel.get('target')); + }); + } + if (sublink) { + subTextEl.on('click', function () { + windowOpen(sublink, '_' + titleModel.get('subtarget')); + }); + } + getECData(textEl).eventData = getECData(subTextEl).eventData = triggerEvent ? { + componentType: 'title', + componentIndex: titleModel.componentIndex + } : null; + group.add(textEl); + subText && group.add(subTextEl); + // If no subText, but add subTextEl, there will be an empty line. + var groupRect = group.getBoundingRect(); + var layoutOption = titleModel.getBoxLayoutParams(); + layoutOption.width = groupRect.width; + layoutOption.height = groupRect.height; + var layoutRect = getLayoutRect(layoutOption, { + width: api.getWidth(), + height: api.getHeight() + }, titleModel.get('padding')); + // Adjust text align based on position + if (!textAlign) { + // Align left if title is on the left. center and right is same + textAlign = titleModel.get('left') || titleModel.get('right'); + // @ts-ignore + if (textAlign === 'middle') { + textAlign = 'center'; + } + // Adjust layout by text align + if (textAlign === 'right') { + layoutRect.x += layoutRect.width; + } else if (textAlign === 'center') { + layoutRect.x += layoutRect.width / 2; + } + } + if (!textVerticalAlign) { + textVerticalAlign = titleModel.get('top') || titleModel.get('bottom'); + // @ts-ignore + if (textVerticalAlign === 'center') { + textVerticalAlign = 'middle'; + } + if (textVerticalAlign === 'bottom') { + layoutRect.y += layoutRect.height; + } else if (textVerticalAlign === 'middle') { + layoutRect.y += layoutRect.height / 2; + } + textVerticalAlign = textVerticalAlign || 'top'; + } + group.x = layoutRect.x; + group.y = layoutRect.y; + group.markRedraw(); + var alignStyle = { + align: textAlign, + verticalAlign: textVerticalAlign + }; + textEl.setStyle(alignStyle); + subTextEl.setStyle(alignStyle); + // Render background + // Get groupRect again because textAlign has been changed + groupRect = group.getBoundingRect(); + var padding = layoutRect.margin; + var style = titleModel.getItemStyle(['color', 'opacity']); + style.fill = titleModel.get('backgroundColor'); + var rect = new Rect({ + shape: { + x: groupRect.x - padding[3], + y: groupRect.y - padding[0], + width: groupRect.width + padding[1] + padding[3], + height: groupRect.height + padding[0] + padding[2], + r: titleModel.get('borderRadius') + }, + style: style, + subPixelOptimize: true, + silent: true + }); + group.add(rect); + }; + TitleView.type = 'title'; + return TitleView; + }(ComponentView); + function install$C(registers) { + registers.registerComponentModel(TitleModel); + registers.registerComponentView(TitleView); + } + + var TimelineModel = /** @class */function (_super) { + __extends(TimelineModel, _super); + function TimelineModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TimelineModel.type; + _this.layoutMode = 'box'; + return _this; + } + /** + * @override + */ + TimelineModel.prototype.init = function (option, parentModel, ecModel) { + this.mergeDefaultAndTheme(option, ecModel); + this._initData(); + }; + /** + * @override + */ + TimelineModel.prototype.mergeOption = function (option) { + _super.prototype.mergeOption.apply(this, arguments); + this._initData(); + }; + TimelineModel.prototype.setCurrentIndex = function (currentIndex) { + if (currentIndex == null) { + currentIndex = this.option.currentIndex; + } + var count = this._data.count(); + if (this.option.loop) { + currentIndex = (currentIndex % count + count) % count; + } else { + currentIndex >= count && (currentIndex = count - 1); + currentIndex < 0 && (currentIndex = 0); + } + this.option.currentIndex = currentIndex; + }; + /** + * @return {number} currentIndex + */ + TimelineModel.prototype.getCurrentIndex = function () { + return this.option.currentIndex; + }; + /** + * @return {boolean} + */ + TimelineModel.prototype.isIndexMax = function () { + return this.getCurrentIndex() >= this._data.count() - 1; + }; + /** + * @param {boolean} state true: play, false: stop + */ + TimelineModel.prototype.setPlayState = function (state) { + this.option.autoPlay = !!state; + }; + /** + * @return {boolean} true: play, false: stop + */ + TimelineModel.prototype.getPlayState = function () { + return !!this.option.autoPlay; + }; + /** + * @private + */ + TimelineModel.prototype._initData = function () { + var thisOption = this.option; + var dataArr = thisOption.data || []; + var axisType = thisOption.axisType; + var names = this._names = []; + var processedDataArr; + if (axisType === 'category') { + processedDataArr = []; + each(dataArr, function (item, index) { + var value = convertOptionIdName(getDataItemValue(item), ''); + var newItem; + if (isObject(item)) { + newItem = clone(item); + newItem.value = index; + } else { + newItem = index; + } + processedDataArr.push(newItem); + names.push(value); + }); + } else { + processedDataArr = dataArr; + } + var dimType = { + category: 'ordinal', + time: 'time', + value: 'number' + }[axisType] || 'number'; + var data = this._data = new SeriesData([{ + name: 'value', + type: dimType + }], this); + data.initData(processedDataArr, names); + }; + TimelineModel.prototype.getData = function () { + return this._data; + }; + /** + * @public + * @return {Array.} categoreis + */ + TimelineModel.prototype.getCategories = function () { + if (this.get('axisType') === 'category') { + return this._names.slice(); + } + }; + TimelineModel.type = 'timeline'; + /** + * @protected + */ + TimelineModel.defaultOption = { + // zlevel: 0, // 一级层叠 + z: 4, + show: true, + axisType: 'time', + realtime: true, + left: '20%', + top: null, + right: '20%', + bottom: 0, + width: null, + height: 40, + padding: 5, + controlPosition: 'left', + autoPlay: false, + rewind: false, + loop: true, + playInterval: 2000, + currentIndex: 0, + itemStyle: {}, + label: { + color: '#000' + }, + data: [] + }; + return TimelineModel; + }(ComponentModel); + + var SliderTimelineModel = /** @class */function (_super) { + __extends(SliderTimelineModel, _super); + function SliderTimelineModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SliderTimelineModel.type; + return _this; + } + SliderTimelineModel.type = 'timeline.slider'; + /** + * @protected + */ + SliderTimelineModel.defaultOption = inheritDefaultOption(TimelineModel.defaultOption, { + backgroundColor: 'rgba(0,0,0,0)', + borderColor: '#ccc', + borderWidth: 0, + orient: 'horizontal', + inverse: false, + tooltip: { + trigger: 'item' // data item may also have tootip attr. + }, + + symbol: 'circle', + symbolSize: 12, + lineStyle: { + show: true, + width: 2, + color: '#DAE1F5' + }, + label: { + position: 'auto', + // When using number, label position is not + // restricted by viewRect. + // positive: right/bottom, negative: left/top + show: true, + interval: 'auto', + rotate: 0, + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#A4B1D7' + }, + itemStyle: { + color: '#A4B1D7', + borderWidth: 1 + }, + checkpointStyle: { + symbol: 'circle', + symbolSize: 15, + color: '#316bf3', + borderColor: '#fff', + borderWidth: 2, + shadowBlur: 2, + shadowOffsetX: 1, + shadowOffsetY: 1, + shadowColor: 'rgba(0, 0, 0, 0.3)', + // borderColor: 'rgba(194,53,49, 0.5)', + animation: true, + animationDuration: 300, + animationEasing: 'quinticInOut' + }, + controlStyle: { + show: true, + showPlayBtn: true, + showPrevBtn: true, + showNextBtn: true, + itemSize: 24, + itemGap: 12, + position: 'left', + playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z', + stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z', + // eslint-disable-next-line max-len + nextIcon: 'M2,18.5A1.52,1.52,0,0,1,.92,18a1.49,1.49,0,0,1,0-2.12L7.81,9.36,1,3.11A1.5,1.5,0,1,1,3,.89l8,7.34a1.48,1.48,0,0,1,.49,1.09,1.51,1.51,0,0,1-.46,1.1L3,18.08A1.5,1.5,0,0,1,2,18.5Z', + // eslint-disable-next-line max-len + prevIcon: 'M10,.5A1.52,1.52,0,0,1,11.08,1a1.49,1.49,0,0,1,0,2.12L4.19,9.64,11,15.89a1.5,1.5,0,1,1-2,2.22L1,10.77A1.48,1.48,0,0,1,.5,9.68,1.51,1.51,0,0,1,1,8.58L9,.92A1.5,1.5,0,0,1,10,.5Z', + prevBtnSize: 18, + nextBtnSize: 18, + color: '#A4B1D7', + borderColor: '#A4B1D7', + borderWidth: 1 + }, + emphasis: { + label: { + show: true, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#6f778d' + }, + itemStyle: { + color: '#316BF3' + }, + controlStyle: { + color: '#316BF3', + borderColor: '#316BF3', + borderWidth: 2 + } + }, + progress: { + lineStyle: { + color: '#316BF3' + }, + itemStyle: { + color: '#316BF3' + }, + label: { + color: '#6f778d' + } + }, + data: [] + }); + return SliderTimelineModel; + }(TimelineModel); + mixin(SliderTimelineModel, DataFormatMixin.prototype); + + var TimelineView = /** @class */function (_super) { + __extends(TimelineView, _super); + function TimelineView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = TimelineView.type; + return _this; + } + TimelineView.type = 'timeline'; + return TimelineView; + }(ComponentView); + + /** + * Extend axis 2d + */ + var TimelineAxis = /** @class */function (_super) { + __extends(TimelineAxis, _super); + function TimelineAxis(dim, scale, coordExtent, axisType) { + var _this = _super.call(this, dim, scale, coordExtent) || this; + _this.type = axisType || 'value'; + return _this; + } + /** + * @override + */ + TimelineAxis.prototype.getLabelModel = function () { + // Force override + return this.model.getModel('label'); + }; + /** + * @override + */ + TimelineAxis.prototype.isHorizontal = function () { + return this.model.get('orient') === 'horizontal'; + }; + return TimelineAxis; + }(Axis); + + var PI$8 = Math.PI; + var labelDataIndexStore = makeInner(); + var SliderTimelineView = /** @class */function (_super) { + __extends(SliderTimelineView, _super); + function SliderTimelineView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SliderTimelineView.type; + return _this; + } + SliderTimelineView.prototype.init = function (ecModel, api) { + this.api = api; + }; + /** + * @override + */ + SliderTimelineView.prototype.render = function (timelineModel, ecModel, api) { + this.model = timelineModel; + this.api = api; + this.ecModel = ecModel; + this.group.removeAll(); + if (timelineModel.get('show', true)) { + var layoutInfo_1 = this._layout(timelineModel, api); + var mainGroup_1 = this._createGroup('_mainGroup'); + var labelGroup = this._createGroup('_labelGroup'); + var axis_1 = this._axis = this._createAxis(layoutInfo_1, timelineModel); + timelineModel.formatTooltip = function (dataIndex) { + var name = axis_1.scale.getLabel({ + value: dataIndex + }); + return createTooltipMarkup('nameValue', { + noName: true, + value: name + }); + }; + each(['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], function (name) { + this['_render' + name](layoutInfo_1, mainGroup_1, axis_1, timelineModel); + }, this); + this._renderAxisLabel(layoutInfo_1, labelGroup, axis_1, timelineModel); + this._position(layoutInfo_1, timelineModel); + } + this._doPlayStop(); + this._updateTicksStatus(); + }; + /** + * @override + */ + SliderTimelineView.prototype.remove = function () { + this._clearTimer(); + this.group.removeAll(); + }; + /** + * @override + */ + SliderTimelineView.prototype.dispose = function () { + this._clearTimer(); + }; + SliderTimelineView.prototype._layout = function (timelineModel, api) { + var labelPosOpt = timelineModel.get(['label', 'position']); + var orient = timelineModel.get('orient'); + var viewRect = getViewRect$5(timelineModel, api); + var parsedLabelPos; + // Auto label offset. + if (labelPosOpt == null || labelPosOpt === 'auto') { + parsedLabelPos = orient === 'horizontal' ? viewRect.y + viewRect.height / 2 < api.getHeight() / 2 ? '-' : '+' : viewRect.x + viewRect.width / 2 < api.getWidth() / 2 ? '+' : '-'; + } else if (isString(labelPosOpt)) { + parsedLabelPos = { + horizontal: { + top: '-', + bottom: '+' + }, + vertical: { + left: '-', + right: '+' + } + }[orient][labelPosOpt]; + } else { + // is number + parsedLabelPos = labelPosOpt; + } + var labelAlignMap = { + horizontal: 'center', + vertical: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'left' : 'right' + }; + var labelBaselineMap = { + horizontal: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'top' : 'bottom', + vertical: 'middle' + }; + var rotationMap = { + horizontal: 0, + vertical: PI$8 / 2 + }; + // Position + var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width; + var controlModel = timelineModel.getModel('controlStyle'); + var showControl = controlModel.get('show', true); + var controlSize = showControl ? controlModel.get('itemSize') : 0; + var controlGap = showControl ? controlModel.get('itemGap') : 0; + var sizePlusGap = controlSize + controlGap; + // Special label rotate. + var labelRotation = timelineModel.get(['label', 'rotate']) || 0; + labelRotation = labelRotation * PI$8 / 180; // To radian. + var playPosition; + var prevBtnPosition; + var nextBtnPosition; + var controlPosition = controlModel.get('position', true); + var showPlayBtn = showControl && controlModel.get('showPlayBtn', true); + var showPrevBtn = showControl && controlModel.get('showPrevBtn', true); + var showNextBtn = showControl && controlModel.get('showNextBtn', true); + var xLeft = 0; + var xRight = mainLength; + // position[0] means left, position[1] means middle. + if (controlPosition === 'left' || controlPosition === 'bottom') { + showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap); + showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap); + showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + } else { + // 'top' 'right' + showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap); + showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + } + var axisExtent = [xLeft, xRight]; + if (timelineModel.get('inverse')) { + axisExtent.reverse(); + } + return { + viewRect: viewRect, + mainLength: mainLength, + orient: orient, + rotation: rotationMap[orient], + labelRotation: labelRotation, + labelPosOpt: parsedLabelPos, + labelAlign: timelineModel.get(['label', 'align']) || labelAlignMap[orient], + labelBaseline: timelineModel.get(['label', 'verticalAlign']) || timelineModel.get(['label', 'baseline']) || labelBaselineMap[orient], + // Based on mainGroup. + playPosition: playPosition, + prevBtnPosition: prevBtnPosition, + nextBtnPosition: nextBtnPosition, + axisExtent: axisExtent, + controlSize: controlSize, + controlGap: controlGap + }; + }; + SliderTimelineView.prototype._position = function (layoutInfo, timelineModel) { + // Position is be called finally, because bounding rect is needed for + // adapt content to fill viewRect (auto adapt offset). + // Timeline may be not all in the viewRect when 'offset' is specified + // as a number, because it is more appropriate that label aligns at + // 'offset' but not the other edge defined by viewRect. + var mainGroup = this._mainGroup; + var labelGroup = this._labelGroup; + var viewRect = layoutInfo.viewRect; + if (layoutInfo.orient === 'vertical') { + // transform to horizontal, inverse rotate by left-top point. + var m = create$1(); + var rotateOriginX = viewRect.x; + var rotateOriginY = viewRect.y + viewRect.height; + translate(m, m, [-rotateOriginX, -rotateOriginY]); + rotate(m, m, -PI$8 / 2); + translate(m, m, [rotateOriginX, rotateOriginY]); + viewRect = viewRect.clone(); + viewRect.applyTransform(m); + } + var viewBound = getBound(viewRect); + var mainBound = getBound(mainGroup.getBoundingRect()); + var labelBound = getBound(labelGroup.getBoundingRect()); + var mainPosition = [mainGroup.x, mainGroup.y]; + var labelsPosition = [labelGroup.x, labelGroup.y]; + labelsPosition[0] = mainPosition[0] = viewBound[0][0]; + var labelPosOpt = layoutInfo.labelPosOpt; + if (labelPosOpt == null || isString(labelPosOpt)) { + // '+' or '-' + var mainBoundIdx = labelPosOpt === '+' ? 0 : 1; + toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); + toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx); + } else { + var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1; + toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); + labelsPosition[1] = mainPosition[1] + labelPosOpt; + } + mainGroup.setPosition(mainPosition); + labelGroup.setPosition(labelsPosition); + mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation; + setOrigin(mainGroup); + setOrigin(labelGroup); + function setOrigin(targetGroup) { + targetGroup.originX = viewBound[0][0] - targetGroup.x; + targetGroup.originY = viewBound[1][0] - targetGroup.y; + } + function getBound(rect) { + // [[xmin, xmax], [ymin, ymax]] + return [[rect.x, rect.x + rect.width], [rect.y, rect.y + rect.height]]; + } + function toBound(fromPos, from, to, dimIdx, boundIdx) { + fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx]; + } + }; + SliderTimelineView.prototype._createAxis = function (layoutInfo, timelineModel) { + var data = timelineModel.getData(); + var axisType = timelineModel.get('axisType'); + var scale = createScaleByModel$1(timelineModel, axisType); + // Customize scale. The `tickValue` is `dataIndex`. + scale.getTicks = function () { + return data.mapArray(['value'], function (value) { + return { + value: value + }; + }); + }; + var dataExtent = data.getDataExtent('value'); + scale.setExtent(dataExtent[0], dataExtent[1]); + scale.calcNiceTicks(); + var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType); + axis.model = timelineModel; + return axis; + }; + SliderTimelineView.prototype._createGroup = function (key) { + var newGroup = this[key] = new Group(); + this.group.add(newGroup); + return newGroup; + }; + SliderTimelineView.prototype._renderAxisLine = function (layoutInfo, group, axis, timelineModel) { + var axisExtent = axis.getExtent(); + if (!timelineModel.get(['lineStyle', 'show'])) { + return; + } + var line = new Line({ + shape: { + x1: axisExtent[0], + y1: 0, + x2: axisExtent[1], + y2: 0 + }, + style: extend({ + lineCap: 'round' + }, timelineModel.getModel('lineStyle').getLineStyle()), + silent: true, + z2: 1 + }); + group.add(line); + var progressLine = this._progressLine = new Line({ + shape: { + x1: axisExtent[0], + x2: this._currentPointer ? this._currentPointer.x : axisExtent[0], + y1: 0, + y2: 0 + }, + style: defaults({ + lineCap: 'round', + lineWidth: line.style.lineWidth + }, timelineModel.getModel(['progress', 'lineStyle']).getLineStyle()), + silent: true, + z2: 1 + }); + group.add(progressLine); + }; + SliderTimelineView.prototype._renderAxisTick = function (layoutInfo, group, axis, timelineModel) { + var _this = this; + var data = timelineModel.getData(); + // Show all ticks, despite ignoring strategy. + var ticks = axis.scale.getTicks(); + this._tickSymbols = []; + // The value is dataIndex, see the customized scale. + each(ticks, function (tick) { + var tickCoord = axis.dataToCoord(tick.value); + var itemModel = data.getItemModel(tick.value); + var itemStyleModel = itemModel.getModel('itemStyle'); + var hoverStyleModel = itemModel.getModel(['emphasis', 'itemStyle']); + var progressStyleModel = itemModel.getModel(['progress', 'itemStyle']); + var symbolOpt = { + x: tickCoord, + y: 0, + onclick: bind(_this._changeTimeline, _this, tick.value) + }; + var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt); + el.ensureState('emphasis').style = hoverStyleModel.getItemStyle(); + el.ensureState('progress').style = progressStyleModel.getItemStyle(); + enableHoverEmphasis(el); + var ecData = getECData(el); + if (itemModel.get('tooltip')) { + ecData.dataIndex = tick.value; + ecData.dataModel = timelineModel; + } else { + ecData.dataIndex = ecData.dataModel = null; + } + _this._tickSymbols.push(el); + }); + }; + SliderTimelineView.prototype._renderAxisLabel = function (layoutInfo, group, axis, timelineModel) { + var _this = this; + var labelModel = axis.getLabelModel(); + if (!labelModel.get('show')) { + return; + } + var data = timelineModel.getData(); + var labels = axis.getViewLabels(); + this._tickLabels = []; + each(labels, function (labelItem) { + // The tickValue is dataIndex, see the customized scale. + var dataIndex = labelItem.tickValue; + var itemModel = data.getItemModel(dataIndex); + var normalLabelModel = itemModel.getModel('label'); + var hoverLabelModel = itemModel.getModel(['emphasis', 'label']); + var progressLabelModel = itemModel.getModel(['progress', 'label']); + var tickCoord = axis.dataToCoord(labelItem.tickValue); + var textEl = new ZRText({ + x: tickCoord, + y: 0, + rotation: layoutInfo.labelRotation - layoutInfo.rotation, + onclick: bind(_this._changeTimeline, _this, dataIndex), + silent: false, + style: createTextStyle(normalLabelModel, { + text: labelItem.formattedLabel, + align: layoutInfo.labelAlign, + verticalAlign: layoutInfo.labelBaseline + }) + }); + textEl.ensureState('emphasis').style = createTextStyle(hoverLabelModel); + textEl.ensureState('progress').style = createTextStyle(progressLabelModel); + group.add(textEl); + enableHoverEmphasis(textEl); + labelDataIndexStore(textEl).dataIndex = dataIndex; + _this._tickLabels.push(textEl); + }); + }; + SliderTimelineView.prototype._renderControl = function (layoutInfo, group, axis, timelineModel) { + var controlSize = layoutInfo.controlSize; + var rotation = layoutInfo.rotation; + var itemStyle = timelineModel.getModel('controlStyle').getItemStyle(); + var hoverStyle = timelineModel.getModel(['emphasis', 'controlStyle']).getItemStyle(); + var playState = timelineModel.getPlayState(); + var inverse = timelineModel.get('inverse', true); + makeBtn(layoutInfo.nextBtnPosition, 'next', bind(this._changeTimeline, this, inverse ? '-' : '+')); + makeBtn(layoutInfo.prevBtnPosition, 'prev', bind(this._changeTimeline, this, inverse ? '+' : '-')); + makeBtn(layoutInfo.playPosition, playState ? 'stop' : 'play', bind(this._handlePlayClick, this, !playState), true); + function makeBtn(position, iconName, onclick, willRotate) { + if (!position) { + return; + } + var iconSize = parsePercent(retrieve2(timelineModel.get(['controlStyle', iconName + 'BtnSize']), controlSize), controlSize); + var rect = [0, -iconSize / 2, iconSize, iconSize]; + var btn = makeControlIcon(timelineModel, iconName + 'Icon', rect, { + x: position[0], + y: position[1], + originX: controlSize / 2, + originY: 0, + rotation: willRotate ? -rotation : 0, + rectHover: true, + style: itemStyle, + onclick: onclick + }); + btn.ensureState('emphasis').style = hoverStyle; + group.add(btn); + enableHoverEmphasis(btn); + } + }; + SliderTimelineView.prototype._renderCurrentPointer = function (layoutInfo, group, axis, timelineModel) { + var data = timelineModel.getData(); + var currentIndex = timelineModel.getCurrentIndex(); + var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle'); + var me = this; + var callback = { + onCreate: function (pointer) { + pointer.draggable = true; + pointer.drift = bind(me._handlePointerDrag, me); + pointer.ondragend = bind(me._handlePointerDragend, me); + pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel, true); + }, + onUpdate: function (pointer) { + pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel); + } + }; + // Reuse when exists, for animation and drag. + this._currentPointer = giveSymbol(pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback); + }; + SliderTimelineView.prototype._handlePlayClick = function (nextState) { + this._clearTimer(); + this.api.dispatchAction({ + type: 'timelinePlayChange', + playState: nextState, + from: this.uid + }); + }; + SliderTimelineView.prototype._handlePointerDrag = function (dx, dy, e) { + this._clearTimer(); + this._pointerChangeTimeline([e.offsetX, e.offsetY]); + }; + SliderTimelineView.prototype._handlePointerDragend = function (e) { + this._pointerChangeTimeline([e.offsetX, e.offsetY], true); + }; + SliderTimelineView.prototype._pointerChangeTimeline = function (mousePos, trigger) { + var toCoord = this._toAxisCoord(mousePos)[0]; + var axis = this._axis; + var axisExtent = asc(axis.getExtent().slice()); + toCoord > axisExtent[1] && (toCoord = axisExtent[1]); + toCoord < axisExtent[0] && (toCoord = axisExtent[0]); + this._currentPointer.x = toCoord; + this._currentPointer.markRedraw(); + var progressLine = this._progressLine; + if (progressLine) { + progressLine.shape.x2 = toCoord; + progressLine.dirty(); + } + var targetDataIndex = this._findNearestTick(toCoord); + var timelineModel = this.model; + if (trigger || targetDataIndex !== timelineModel.getCurrentIndex() && timelineModel.get('realtime')) { + this._changeTimeline(targetDataIndex); + } + }; + SliderTimelineView.prototype._doPlayStop = function () { + var _this = this; + this._clearTimer(); + if (this.model.getPlayState()) { + this._timer = setTimeout(function () { + // Do not cache + var timelineModel = _this.model; + _this._changeTimeline(timelineModel.getCurrentIndex() + (timelineModel.get('rewind', true) ? -1 : 1)); + }, this.model.get('playInterval')); + } + }; + SliderTimelineView.prototype._toAxisCoord = function (vertex) { + var trans = this._mainGroup.getLocalTransform(); + return applyTransform$1(vertex, trans, true); + }; + SliderTimelineView.prototype._findNearestTick = function (axisCoord) { + var data = this.model.getData(); + var dist = Infinity; + var targetDataIndex; + var axis = this._axis; + data.each(['value'], function (value, dataIndex) { + var coord = axis.dataToCoord(value); + var d = Math.abs(coord - axisCoord); + if (d < dist) { + dist = d; + targetDataIndex = dataIndex; + } + }); + return targetDataIndex; + }; + SliderTimelineView.prototype._clearTimer = function () { + if (this._timer) { + clearTimeout(this._timer); + this._timer = null; + } + }; + SliderTimelineView.prototype._changeTimeline = function (nextIndex) { + var currentIndex = this.model.getCurrentIndex(); + if (nextIndex === '+') { + nextIndex = currentIndex + 1; + } else if (nextIndex === '-') { + nextIndex = currentIndex - 1; + } + this.api.dispatchAction({ + type: 'timelineChange', + currentIndex: nextIndex, + from: this.uid + }); + }; + SliderTimelineView.prototype._updateTicksStatus = function () { + var currentIndex = this.model.getCurrentIndex(); + var tickSymbols = this._tickSymbols; + var tickLabels = this._tickLabels; + if (tickSymbols) { + for (var i = 0; i < tickSymbols.length; i++) { + tickSymbols && tickSymbols[i] && tickSymbols[i].toggleState('progress', i < currentIndex); + } + } + if (tickLabels) { + for (var i = 0; i < tickLabels.length; i++) { + tickLabels && tickLabels[i] && tickLabels[i].toggleState('progress', labelDataIndexStore(tickLabels[i]).dataIndex <= currentIndex); + } + } + }; + SliderTimelineView.type = 'timeline.slider'; + return SliderTimelineView; + }(TimelineView); + function createScaleByModel$1(model, axisType) { + axisType = axisType || model.get('type'); + if (axisType) { + switch (axisType) { + // Buildin scale + case 'category': + return new OrdinalScale({ + ordinalMeta: model.getCategories(), + extent: [Infinity, -Infinity] + }); + case 'time': + return new TimeScale({ + locale: model.ecModel.getLocaleModel(), + useUTC: model.ecModel.get('useUTC') + }); + default: + // default to be value + return new IntervalScale(); + } + } + } + function getViewRect$5(model, api) { + return getLayoutRect(model.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }, model.get('padding')); + } + function makeControlIcon(timelineModel, objPath, rect, opts) { + var style = opts.style; + var icon = createIcon(timelineModel.get(['controlStyle', objPath]), opts || {}, new BoundingRect(rect[0], rect[1], rect[2], rect[3])); + // TODO createIcon won't use style in opt. + if (style) { + icon.setStyle(style); + } + return icon; + } + /** + * Create symbol or update symbol + * opt: basic position and event handlers + */ + function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) { + var color = itemStyleModel.get('color'); + if (!symbol) { + var symbolType = hostModel.get('symbol'); + symbol = createSymbol(symbolType, -1, -1, 2, 2, color); + symbol.setStyle('strokeNoScale', true); + group.add(symbol); + callback && callback.onCreate(symbol); + } else { + symbol.setColor(color); + group.add(symbol); // Group may be new, also need to add. + callback && callback.onUpdate(symbol); + } + // Style + var itemStyle = itemStyleModel.getItemStyle(['color']); + symbol.setStyle(itemStyle); + // Transform and events. + opt = merge({ + rectHover: true, + z2: 100 + }, opt, true); + var symbolSize = normalizeSymbolSize(hostModel.get('symbolSize')); + opt.scaleX = symbolSize[0] / 2; + opt.scaleY = symbolSize[1] / 2; + var symbolOffset = normalizeSymbolOffset(hostModel.get('symbolOffset'), symbolSize); + if (symbolOffset) { + opt.x = (opt.x || 0) + symbolOffset[0]; + opt.y = (opt.y || 0) + symbolOffset[1]; + } + var symbolRotate = hostModel.get('symbolRotate'); + opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + symbol.attr(opt); + // FIXME + // (1) When symbol.style.strokeNoScale is true and updateTransform is not performed, + // getBoundingRect will return wrong result. + // (This is supposed to be resolved in zrender, but it is a little difficult to + // leverage performance and auto updateTransform) + // (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol. + symbol.updateTransform(); + return symbol; + } + function pointerMoveTo(pointer, progressLine, dataIndex, axis, timelineModel, noAnimation) { + if (pointer.dragging) { + return; + } + var pointerModel = timelineModel.getModel('checkpointStyle'); + var toCoord = axis.dataToCoord(timelineModel.getData().get('value', dataIndex)); + if (noAnimation || !pointerModel.get('animation', true)) { + pointer.attr({ + x: toCoord, + y: 0 + }); + progressLine && progressLine.attr({ + shape: { + x2: toCoord + } + }); + } else { + var animationCfg = { + duration: pointerModel.get('animationDuration', true), + easing: pointerModel.get('animationEasing', true) + }; + pointer.stopAnimation(null, true); + pointer.animateTo({ + x: toCoord, + y: 0 + }, animationCfg); + progressLine && progressLine.animateTo({ + shape: { + x2: toCoord + } + }, animationCfg); + } + } + + function installTimelineAction(registers) { + registers.registerAction({ + type: 'timelineChange', + event: 'timelineChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel, api) { + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.currentIndex != null) { + timelineModel.setCurrentIndex(payload.currentIndex); + if (!timelineModel.get('loop', true) && timelineModel.isIndexMax() && timelineModel.getPlayState()) { + timelineModel.setPlayState(false); + // The timeline has played to the end, trigger event + api.dispatchAction({ + type: 'timelinePlayChange', + playState: false, + from: payload.from + }); + } + } + // Set normalized currentIndex to payload. + ecModel.resetOption('timeline', { + replaceMerge: timelineModel.get('replaceMerge', true) + }); + return defaults({ + currentIndex: timelineModel.option.currentIndex + }, payload); + }); + registers.registerAction({ + type: 'timelinePlayChange', + event: 'timelinePlayChanged', + update: 'update' + }, function (payload, ecModel) { + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.playState != null) { + timelineModel.setPlayState(payload.playState); + } + }); + } + + function timelinePreprocessor(option) { + var timelineOpt = option && option.timeline; + if (!isArray(timelineOpt)) { + timelineOpt = timelineOpt ? [timelineOpt] : []; + } + each(timelineOpt, function (opt) { + if (!opt) { + return; + } + compatibleEC2(opt); + }); + } + function compatibleEC2(opt) { + var type = opt.type; + var ec2Types = { + 'number': 'value', + 'time': 'time' + }; + // Compatible with ec2 + if (ec2Types[type]) { + opt.axisType = ec2Types[type]; + delete opt.type; + } + transferItem(opt); + if (has(opt, 'controlPosition')) { + var controlStyle = opt.controlStyle || (opt.controlStyle = {}); + if (!has(controlStyle, 'position')) { + controlStyle.position = opt.controlPosition; + } + if (controlStyle.position === 'none' && !has(controlStyle, 'show')) { + controlStyle.show = false; + delete controlStyle.position; + } + delete opt.controlPosition; + } + each(opt.data || [], function (dataItem) { + if (isObject(dataItem) && !isArray(dataItem)) { + if (!has(dataItem, 'value') && has(dataItem, 'name')) { + // In ec2, using name as value. + dataItem.value = dataItem.name; + } + transferItem(dataItem); + } + }); + } + function transferItem(opt) { + var itemStyle = opt.itemStyle || (opt.itemStyle = {}); + var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {}); + // Transfer label out + var label = opt.label || opt.label || {}; + var labelNormal = label.normal || (label.normal = {}); + var excludeLabelAttr = { + normal: 1, + emphasis: 1 + }; + each(label, function (value, name) { + if (!excludeLabelAttr[name] && !has(labelNormal, name)) { + labelNormal[name] = value; + } + }); + if (itemStyleEmphasis.label && !has(label, 'emphasis')) { + label.emphasis = itemStyleEmphasis.label; + delete itemStyleEmphasis.label; + } + } + function has(obj, attr) { + return obj.hasOwnProperty(attr); + } + + function install$D(registers) { + registers.registerComponentModel(SliderTimelineModel); + registers.registerComponentView(SliderTimelineView); + registers.registerSubTypeDefaulter('timeline', function () { + // Only slider now. + return 'slider'; + }); + installTimelineAction(registers); + registers.registerPreprocessor(timelinePreprocessor); + } + + function checkMarkerInSeries(seriesOpts, markerType) { + if (!seriesOpts) { + return false; + } + var seriesOptArr = isArray(seriesOpts) ? seriesOpts : [seriesOpts]; + for (var idx = 0; idx < seriesOptArr.length; idx++) { + if (seriesOptArr[idx] && seriesOptArr[idx][markerType]) { + return true; + } + } + return false; + } + + function fillLabel(opt) { + defaultEmphasis(opt, 'label', ['show']); + } + // { [componentType]: MarkerModel } + var inner$g = makeInner(); + var MarkerModel = /** @class */function (_super) { + __extends(MarkerModel, _super); + function MarkerModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkerModel.type; + /** + * If marker model is created by self from series + */ + _this.createdBySelf = false; + return _this; + } + /** + * @overrite + */ + MarkerModel.prototype.init = function (option, parentModel, ecModel) { + if ("development" !== 'production') { + if (this.type === 'marker') { + throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.'); + } + } + this.mergeDefaultAndTheme(option, ecModel); + this._mergeOption(option, ecModel, false, true); + }; + MarkerModel.prototype.isAnimationEnabled = function () { + if (env.node) { + return false; + } + var hostSeries = this.__hostSeries; + return this.getShallow('animation') && hostSeries && hostSeries.isAnimationEnabled(); + }; + /** + * @overrite + */ + MarkerModel.prototype.mergeOption = function (newOpt, ecModel) { + this._mergeOption(newOpt, ecModel, false, false); + }; + MarkerModel.prototype._mergeOption = function (newOpt, ecModel, createdBySelf, isInit) { + var componentType = this.mainType; + if (!createdBySelf) { + ecModel.eachSeries(function (seriesModel) { + // mainType can be markPoint, markLine, markArea + var markerOpt = seriesModel.get(this.mainType, true); + var markerModel = inner$g(seriesModel)[componentType]; + if (!markerOpt || !markerOpt.data) { + inner$g(seriesModel)[componentType] = null; + return; + } + if (!markerModel) { + if (isInit) { + // Default label emphasis `position` and `show` + fillLabel(markerOpt); + } + each(markerOpt.data, function (item) { + // FIXME Overwrite fillLabel method ? + if (item instanceof Array) { + fillLabel(item[0]); + fillLabel(item[1]); + } else { + fillLabel(item); + } + }); + markerModel = this.createMarkerModelFromSeries(markerOpt, this, ecModel); + // markerModel = new ImplementedMarkerModel( + // markerOpt, this, ecModel + // ); + extend(markerModel, { + mainType: this.mainType, + // Use the same series index and name + seriesIndex: seriesModel.seriesIndex, + name: seriesModel.name, + createdBySelf: true + }); + markerModel.__hostSeries = seriesModel; + } else { + markerModel._mergeOption(markerOpt, ecModel, true); + } + inner$g(seriesModel)[componentType] = markerModel; + }, this); + } + }; + MarkerModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var itemName = data.getName(dataIndex); + return createTooltipMarkup('section', { + header: this.name, + blocks: [createTooltipMarkup('nameValue', { + name: itemName, + value: value, + noName: !itemName, + noValue: value == null + })] + }); + }; + MarkerModel.prototype.getData = function () { + return this._data; + }; + MarkerModel.prototype.setData = function (data) { + this._data = data; + }; + MarkerModel.getMarkerModelFromSeries = function (seriesModel, + // Support three types of markers. Strict check. + componentType) { + return inner$g(seriesModel)[componentType]; + }; + MarkerModel.type = 'marker'; + MarkerModel.dependencies = ['series', 'grid', 'polar', 'geo']; + return MarkerModel; + }(ComponentModel); + mixin(MarkerModel, DataFormatMixin.prototype); + + var MarkPointModel = /** @class */function (_super) { + __extends(MarkPointModel, _super); + function MarkPointModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkPointModel.type; + return _this; + } + MarkPointModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) { + return new MarkPointModel(markerOpt, masterMarkerModel, ecModel); + }; + MarkPointModel.type = 'markPoint'; + MarkPointModel.defaultOption = { + // zlevel: 0, + z: 5, + symbol: 'pin', + symbolSize: 50, + // symbolRotate: 0, + // symbolOffset: [0, 0] + tooltip: { + trigger: 'item' + }, + label: { + show: true, + position: 'inside' + }, + itemStyle: { + borderWidth: 2 + }, + emphasis: { + label: { + show: true + } + } + }; + return MarkPointModel; + }(MarkerModel); + + function hasXOrY(item) { + return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y))); + } + function hasXAndY(item) { + return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y)); + } + function markerTypeCalculatorWithExtent(markerType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex) { + var coordArr = []; + var stacked = isDimensionStacked(data, targetDataDim /* , otherDataDim */); + var calcDataDim = stacked ? data.getCalculationInfo('stackResultDimension') : targetDataDim; + var value = numCalculate(data, calcDataDim, markerType); + var dataIndex = data.indicesOfNearest(calcDataDim, value)[0]; + coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex); + coordArr[targetCoordIndex] = data.get(calcDataDim, dataIndex); + var coordArrValue = data.get(targetDataDim, dataIndex); + // Make it simple, do not visit all stacked value to count precision. + var precision = getPrecision(data.get(targetDataDim, dataIndex)); + precision = Math.min(precision, 20); + if (precision >= 0) { + coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision); + } + return [coordArr, coordArrValue]; + } + // TODO Specified percent + var markerTypeCalculator = { + min: curry(markerTypeCalculatorWithExtent, 'min'), + max: curry(markerTypeCalculatorWithExtent, 'max'), + average: curry(markerTypeCalculatorWithExtent, 'average'), + median: curry(markerTypeCalculatorWithExtent, 'median') + }; + /** + * Transform markPoint data item to format used in List by do the following + * 1. Calculate statistic like `max`, `min`, `average` + * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array + */ + function dataTransform(seriesModel, item) { + if (!item) { + return; + } + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + var dims = coordSys && coordSys.dimensions; + // 1. If not specify the position with pixel directly + // 2. If `coord` is not a data array. Which uses `xAxis`, + // `yAxis` to specify the coord on each dimension + // parseFloat first because item.x and item.y can be percent string like '20%' + if (!hasXAndY(item) && !isArray(item.coord) && isArray(dims)) { + var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); + // Clone the option + // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value + item = clone(item); + if (item.type && markerTypeCalculator[item.type] && axisInfo.baseAxis && axisInfo.valueAxis) { + var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim); + var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim); + var coordInfo = markerTypeCalculator[item.type](data, axisInfo.baseDataDim, axisInfo.valueDataDim, otherCoordIndex, targetCoordIndex); + item.coord = coordInfo[0]; + // Force to use the value of calculated value. + // let item use the value without stack. + item.value = coordInfo[1]; + } else { + // FIXME Only has one of xAxis and yAxis. + item.coord = [item.xAxis != null ? item.xAxis : item.radiusAxis, item.yAxis != null ? item.yAxis : item.angleAxis]; + } + } + // x y is provided + if (item.coord == null || !isArray(dims)) { + item.coord = []; + } else { + // Each coord support max, min, average + var coord = item.coord; + for (var i = 0; i < 2; i++) { + if (markerTypeCalculator[coord[i]]) { + coord[i] = numCalculate(data, data.mapDimension(dims[i]), coord[i]); + } + } + } + return item; + } + function getAxisInfo$1(item, data, coordSys, seriesModel) { + var ret = {}; + if (item.valueIndex != null || item.valueDim != null) { + ret.valueDataDim = item.valueIndex != null ? data.getDimension(item.valueIndex) : item.valueDim; + ret.valueAxis = coordSys.getAxis(dataDimToCoordDim(seriesModel, ret.valueDataDim)); + ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis); + ret.baseDataDim = data.mapDimension(ret.baseAxis.dim); + } else { + ret.baseAxis = seriesModel.getBaseAxis(); + ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis); + ret.baseDataDim = data.mapDimension(ret.baseAxis.dim); + ret.valueDataDim = data.mapDimension(ret.valueAxis.dim); + } + return ret; + } + function dataDimToCoordDim(seriesModel, dataDim) { + var dimItem = seriesModel.getData().getDimensionInfo(dataDim); + return dimItem && dimItem.coordDim; + } + /** + * Filter data which is out of coordinateSystem range + * [dataFilter description] + */ + function dataFilter$1( + // Currently only polar and cartesian has containData. + coordSys, item) { + // Always return true if there is no coordSys + return coordSys && coordSys.containData && item.coord && !hasXOrY(item) ? coordSys.containData(item.coord) : true; + } + function zoneFilter( + // Currently only polar and cartesian has containData. + coordSys, item1, item2) { + // Always return true if there is no coordSys + return coordSys && coordSys.containZone && item1.coord && item2.coord && !hasXOrY(item1) && !hasXOrY(item2) ? coordSys.containZone(item1.coord, item2.coord) : true; + } + function createMarkerDimValueGetter(inCoordSys, dims) { + return inCoordSys ? function (item, dimName, dataIndex, dimIndex) { + var rawVal = dimIndex < 2 + // x, y, radius, angle + ? item.coord && item.coord[dimIndex] : item.value; + return parseDataValue(rawVal, dims[dimIndex]); + } : function (item, dimName, dataIndex, dimIndex) { + return parseDataValue(item.value, dims[dimIndex]); + }; + } + function numCalculate(data, valueDataDim, type) { + if (type === 'average') { + var sum_1 = 0; + var count_1 = 0; + data.each(valueDataDim, function (val, idx) { + if (!isNaN(val)) { + sum_1 += val; + count_1++; + } + }); + return sum_1 / count_1; + } else if (type === 'median') { + return data.getMedian(valueDataDim); + } else { + // max & min + return data.getDataExtent(valueDataDim)[type === 'max' ? 1 : 0]; + } + } + + var inner$h = makeInner(); + var MarkerView = /** @class */function (_super) { + __extends(MarkerView, _super); + function MarkerView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkerView.type; + return _this; + } + MarkerView.prototype.init = function () { + this.markerGroupMap = createHashMap(); + }; + MarkerView.prototype.render = function (markerModel, ecModel, api) { + var _this = this; + var markerGroupMap = this.markerGroupMap; + markerGroupMap.each(function (item) { + inner$h(item).keep = false; + }); + ecModel.eachSeries(function (seriesModel) { + var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type); + markerModel && _this.renderSeries(seriesModel, markerModel, ecModel, api); + }); + markerGroupMap.each(function (item) { + !inner$h(item).keep && _this.group.remove(item.group); + }); + }; + MarkerView.prototype.markKeep = function (drawGroup) { + inner$h(drawGroup).keep = true; + }; + MarkerView.prototype.toggleBlurSeries = function (seriesModelList, isBlur) { + var _this = this; + each(seriesModelList, function (seriesModel) { + var markerModel = MarkerModel.getMarkerModelFromSeries(seriesModel, _this.type); + if (markerModel) { + var data = markerModel.getData(); + data.eachItemGraphicEl(function (el) { + if (el) { + isBlur ? enterBlur(el) : leaveBlur(el); + } + }); + } + }); + }; + MarkerView.type = 'marker'; + return MarkerView; + }(ComponentView); + + function updateMarkerLayout(mpData, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var point; + var xPx = parsePercent$1(itemModel.get('x'), api.getWidth()); + var yPx = parsePercent$1(itemModel.get('y'), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + // Chart like bar may have there own marker positioning logic + else if (seriesModel.getMarkerPosition) { + // Use the getMarkerPosition + point = seriesModel.getMarkerPosition(mpData.getValues(mpData.dimensions, idx)); + } else if (coordSys) { + var x = mpData.get(coordSys.dimensions[0], idx); + var y = mpData.get(coordSys.dimensions[1], idx); + point = coordSys.dataToPoint([x, y]); + } + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + mpData.setItemLayout(idx, point); + }); + } + var MarkPointView = /** @class */function (_super) { + __extends(MarkPointView, _super); + function MarkPointView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkPointView.type; + return _this; + } + MarkPointView.prototype.updateTransform = function (markPointModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mpModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markPoint'); + if (mpModel) { + updateMarkerLayout(mpModel.getData(), seriesModel, api); + this.markerGroupMap.get(seriesModel.id).updateLayout(); + } + }, this); + }; + MarkPointView.prototype.renderSeries = function (seriesModel, mpModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesId = seriesModel.id; + var seriesData = seriesModel.getData(); + var symbolDrawMap = this.markerGroupMap; + var symbolDraw = symbolDrawMap.get(seriesId) || symbolDrawMap.set(seriesId, new SymbolDraw()); + var mpData = createData(coordSys, seriesModel, mpModel); + // FIXME + mpModel.setData(mpData); + updateMarkerLayout(mpModel.getData(), seriesModel, api); + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var symbol = itemModel.getShallow('symbol'); + var symbolSize = itemModel.getShallow('symbolSize'); + var symbolRotate = itemModel.getShallow('symbolRotate'); + var symbolOffset = itemModel.getShallow('symbolOffset'); + var symbolKeepAspect = itemModel.getShallow('symbolKeepAspect'); + // TODO: refactor needed: single data item should not support callback function + if (isFunction(symbol) || isFunction(symbolSize) || isFunction(symbolRotate) || isFunction(symbolOffset)) { + var rawIdx = mpModel.getRawValue(idx); + var dataParams = mpModel.getDataParams(idx); + if (isFunction(symbol)) { + symbol = symbol(rawIdx, dataParams); + } + if (isFunction(symbolSize)) { + // FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据? + symbolSize = symbolSize(rawIdx, dataParams); + } + if (isFunction(symbolRotate)) { + symbolRotate = symbolRotate(rawIdx, dataParams); + } + if (isFunction(symbolOffset)) { + symbolOffset = symbolOffset(rawIdx, dataParams); + } + } + var style = itemModel.getModel('itemStyle').getItemStyle(); + var color = getVisualFromData(seriesData, 'color'); + if (!style.fill) { + style.fill = color; + } + mpData.setItemVisual(idx, { + symbol: symbol, + symbolSize: symbolSize, + symbolRotate: symbolRotate, + symbolOffset: symbolOffset, + symbolKeepAspect: symbolKeepAspect, + style: style + }); + }); + // TODO Text are wrong + symbolDraw.updateData(mpData); + this.group.add(symbolDraw.group); + // Set host model for tooltip + // FIXME + mpData.eachItemGraphicEl(function (el) { + el.traverse(function (child) { + getECData(child).dataModel = mpModel; + }); + }); + this.markKeep(symbolDraw); + symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent'); + }; + MarkPointView.type = 'markPoint'; + return MarkPointView; + }(MarkerView); + function createData(coordSys, seriesModel, mpModel) { + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; + // In map series data don't have lng and lat dimension. Fallback to same with coordSys + return extend(extend({}, info), { + name: coordDim, + // DON'T use ordinalMeta to parse and collect ordinal. + ordinalMeta: null + }); + }); + } else { + coordDimsInfos = [{ + name: 'value', + type: 'float' + }]; + } + var mpData = new SeriesData(coordDimsInfos, mpModel); + var dataOpt = map(mpModel.get('data'), curry(dataTransform, seriesModel)); + if (coordSys) { + dataOpt = filter(dataOpt, curry(dataFilter$1, coordSys)); + } + var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos); + mpData.initData(dataOpt, null, dimValueGetter); + return mpData; + } + + function install$E(registers) { + registers.registerComponentModel(MarkPointModel); + registers.registerComponentView(MarkPointView); + registers.registerPreprocessor(function (opt) { + if (checkMarkerInSeries(opt.series, 'markPoint')) { + // Make sure markPoint component is enabled + opt.markPoint = opt.markPoint || {}; + } + }); + } + + var MarkLineModel = /** @class */function (_super) { + __extends(MarkLineModel, _super); + function MarkLineModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkLineModel.type; + return _this; + } + MarkLineModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) { + return new MarkLineModel(markerOpt, masterMarkerModel, ecModel); + }; + MarkLineModel.type = 'markLine'; + MarkLineModel.defaultOption = { + // zlevel: 0, + z: 5, + symbol: ['circle', 'arrow'], + symbolSize: [8, 16], + // symbolRotate: 0, + symbolOffset: 0, + precision: 2, + tooltip: { + trigger: 'item' + }, + label: { + show: true, + position: 'end', + distance: 5 + }, + lineStyle: { + type: 'dashed' + }, + emphasis: { + label: { + show: true + }, + lineStyle: { + width: 3 + } + }, + animationEasing: 'linear' + }; + return MarkLineModel; + }(MarkerModel); + + var inner$i = makeInner(); + var markLineTransform = function (seriesModel, coordSys, mlModel, item) { + var data = seriesModel.getData(); + var itemArray; + if (!isArray(item)) { + // Special type markLine like 'min', 'max', 'average', 'median' + var mlType = item.type; + if (mlType === 'min' || mlType === 'max' || mlType === 'average' || mlType === 'median' + // In case + // data: [{ + // yAxis: 10 + // }] + || item.xAxis != null || item.yAxis != null) { + var valueAxis = void 0; + var value = void 0; + if (item.yAxis != null || item.xAxis != null) { + valueAxis = coordSys.getAxis(item.yAxis != null ? 'y' : 'x'); + value = retrieve(item.yAxis, item.xAxis); + } else { + var axisInfo = getAxisInfo$1(item, data, coordSys, seriesModel); + valueAxis = axisInfo.valueAxis; + var valueDataDim = getStackedDimension(data, axisInfo.valueDataDim); + value = numCalculate(data, valueDataDim, mlType); + } + var valueIndex = valueAxis.dim === 'x' ? 0 : 1; + var baseIndex = 1 - valueIndex; + // Normized to 2d data with start and end point + var mlFrom = clone(item); + var mlTo = { + coord: [] + }; + mlFrom.type = null; + mlFrom.coord = []; + mlFrom.coord[baseIndex] = -Infinity; + mlTo.coord[baseIndex] = Infinity; + var precision = mlModel.get('precision'); + if (precision >= 0 && isNumber(value)) { + value = +value.toFixed(Math.min(precision, 20)); + } + mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value; + itemArray = [mlFrom, mlTo, { + type: mlType, + valueIndex: item.valueIndex, + // Force to use the value of calculated value. + value: value + }]; + } else { + // Invalid data + if ("development" !== 'production') { + logError('Invalid markLine data.'); + } + itemArray = []; + } + } else { + itemArray = item; + } + var normalizedItem = [dataTransform(seriesModel, itemArray[0]), dataTransform(seriesModel, itemArray[1]), extend({}, itemArray[2])]; + // Avoid line data type is extended by from(to) data type + normalizedItem[2].type = normalizedItem[2].type || null; + // Merge from option and to option into line option + merge(normalizedItem[2], normalizedItem[0]); + merge(normalizedItem[2], normalizedItem[1]); + return normalizedItem; + }; + function isInfinity(val) { + return !isNaN(val) && !isFinite(val); + } + // If a markLine has one dim + function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + var dimName = coordSys.dimensions[dimIndex]; + return isInfinity(fromCoord[otherDimIndex]) && isInfinity(toCoord[otherDimIndex]) && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]); + } + function markLineFilter(coordSys, item) { + if (coordSys.type === 'cartesian2d') { + var fromCoord = item[0].coord; + var toCoord = item[1].coord; + // In case + // { + // markLine: { + // data: [{ yAxis: 2 }] + // } + // } + if (fromCoord && toCoord && (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys))) { + return true; + } + } + return dataFilter$1(coordSys, item[0]) && dataFilter$1(coordSys, item[1]); + } + function updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + var point; + var xPx = parsePercent$1(itemModel.get('x'), api.getWidth()); + var yPx = parsePercent$1(itemModel.get('y'), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } else { + // Chart like bar may have there own marker positioning logic + if (seriesModel.getMarkerPosition) { + // Use the getMarkerPosition + point = seriesModel.getMarkerPosition(data.getValues(data.dimensions, idx)); + } else { + var dims = coordSys.dimensions; + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + point = coordSys.dataToPoint([x, y]); + } + // Expand line to the edge of grid if value on one axis is Inifnity + // In case + // markLine: { + // data: [{ + // yAxis: 2 + // // or + // type: 'average' + // }] + // } + if (isCoordinateSystemType(coordSys, 'cartesian2d')) { + // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + var dims = coordSys.dimensions; + if (isInfinity(data.get(dims[0], idx))) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]); + } else if (isInfinity(data.get(dims[1], idx))) { + point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]); + } + } + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + } + data.setItemLayout(idx, point); + } + var MarkLineView = /** @class */function (_super) { + __extends(MarkLineView, _super); + function MarkLineView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkLineView.type; + return _this; + } + MarkLineView.prototype.updateTransform = function (markLineModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mlModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markLine'); + if (mlModel) { + var mlData_1 = mlModel.getData(); + var fromData_1 = inner$i(mlModel).from; + var toData_1 = inner$i(mlModel).to; + // Update visual and layout of from symbol and to symbol + fromData_1.each(function (idx) { + updateSingleMarkerEndLayout(fromData_1, idx, true, seriesModel, api); + updateSingleMarkerEndLayout(toData_1, idx, false, seriesModel, api); + }); + // Update layout of line + mlData_1.each(function (idx) { + mlData_1.setItemLayout(idx, [fromData_1.getItemLayout(idx), toData_1.getItemLayout(idx)]); + }); + this.markerGroupMap.get(seriesModel.id).updateLayout(); + } + }, this); + }; + MarkLineView.prototype.renderSeries = function (seriesModel, mlModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesId = seriesModel.id; + var seriesData = seriesModel.getData(); + var lineDrawMap = this.markerGroupMap; + var lineDraw = lineDrawMap.get(seriesId) || lineDrawMap.set(seriesId, new LineDraw()); + this.group.add(lineDraw.group); + var mlData = createList$1(coordSys, seriesModel, mlModel); + var fromData = mlData.from; + var toData = mlData.to; + var lineData = mlData.line; + inner$i(mlModel).from = fromData; + inner$i(mlModel).to = toData; + // Line data for tooltip and formatter + mlModel.setData(lineData); + // TODO + // Functionally, `symbolSize` & `symbolOffset` can also be 2D array now. + // But the related logic and type definition are not finished yet. + // Finish it if required + var symbolType = mlModel.get('symbol'); + var symbolSize = mlModel.get('symbolSize'); + var symbolRotate = mlModel.get('symbolRotate'); + var symbolOffset = mlModel.get('symbolOffset'); + // TODO: support callback function like markPoint + if (!isArray(symbolType)) { + symbolType = [symbolType, symbolType]; + } + if (!isArray(symbolSize)) { + symbolSize = [symbolSize, symbolSize]; + } + if (!isArray(symbolRotate)) { + symbolRotate = [symbolRotate, symbolRotate]; + } + if (!isArray(symbolOffset)) { + symbolOffset = [symbolOffset, symbolOffset]; + } + // Update visual and layout of from symbol and to symbol + mlData.from.each(function (idx) { + updateDataVisualAndLayout(fromData, idx, true); + updateDataVisualAndLayout(toData, idx, false); + }); + // Update visual and layout of line + lineData.each(function (idx) { + var lineStyle = lineData.getItemModel(idx).getModel('lineStyle').getLineStyle(); + // lineData.setItemVisual(idx, { + // color: lineColor || fromData.getItemVisual(idx, 'color') + // }); + lineData.setItemLayout(idx, [fromData.getItemLayout(idx), toData.getItemLayout(idx)]); + if (lineStyle.stroke == null) { + lineStyle.stroke = fromData.getItemVisual(idx, 'style').fill; + } + lineData.setItemVisual(idx, { + fromSymbolKeepAspect: fromData.getItemVisual(idx, 'symbolKeepAspect'), + fromSymbolOffset: fromData.getItemVisual(idx, 'symbolOffset'), + fromSymbolRotate: fromData.getItemVisual(idx, 'symbolRotate'), + fromSymbolSize: fromData.getItemVisual(idx, 'symbolSize'), + fromSymbol: fromData.getItemVisual(idx, 'symbol'), + toSymbolKeepAspect: toData.getItemVisual(idx, 'symbolKeepAspect'), + toSymbolOffset: toData.getItemVisual(idx, 'symbolOffset'), + toSymbolRotate: toData.getItemVisual(idx, 'symbolRotate'), + toSymbolSize: toData.getItemVisual(idx, 'symbolSize'), + toSymbol: toData.getItemVisual(idx, 'symbol'), + style: lineStyle + }); + }); + lineDraw.updateData(lineData); + // Set host model for tooltip + // FIXME + mlData.line.eachItemGraphicEl(function (el) { + getECData(el).dataModel = mlModel; + el.traverse(function (child) { + getECData(child).dataModel = mlModel; + }); + }); + function updateDataVisualAndLayout(data, idx, isFrom) { + var itemModel = data.getItemModel(idx); + updateSingleMarkerEndLayout(data, idx, isFrom, seriesModel, api); + var style = itemModel.getModel('itemStyle').getItemStyle(); + if (style.fill == null) { + style.fill = getVisualFromData(seriesData, 'color'); + } + data.setItemVisual(idx, { + symbolKeepAspect: itemModel.get('symbolKeepAspect'), + // `0` should be considered as a valid value, so use `retrieve2` instead of `||` + symbolOffset: retrieve2(itemModel.get('symbolOffset', true), symbolOffset[isFrom ? 0 : 1]), + symbolRotate: retrieve2(itemModel.get('symbolRotate', true), symbolRotate[isFrom ? 0 : 1]), + // TODO: when 2d array is supported, it should ignore parent + symbolSize: retrieve2(itemModel.get('symbolSize'), symbolSize[isFrom ? 0 : 1]), + symbol: retrieve2(itemModel.get('symbol', true), symbolType[isFrom ? 0 : 1]), + style: style + }); + } + this.markKeep(lineDraw); + lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent'); + }; + MarkLineView.type = 'markLine'; + return MarkLineView; + }(MarkerView); + function createList$1(coordSys, seriesModel, mlModel) { + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo(seriesModel.getData().mapDimension(coordDim)) || {}; + // In map series data don't have lng and lat dimension. Fallback to same with coordSys + return extend(extend({}, info), { + name: coordDim, + // DON'T use ordinalMeta to parse and collect ordinal. + ordinalMeta: null + }); + }); + } else { + coordDimsInfos = [{ + name: 'value', + type: 'float' + }]; + } + var fromData = new SeriesData(coordDimsInfos, mlModel); + var toData = new SeriesData(coordDimsInfos, mlModel); + // No dimensions + var lineData = new SeriesData([], mlModel); + var optData = map(mlModel.get('data'), curry(markLineTransform, seriesModel, coordSys, mlModel)); + if (coordSys) { + optData = filter(optData, curry(markLineFilter, coordSys)); + } + var dimValueGetter = createMarkerDimValueGetter(!!coordSys, coordDimsInfos); + fromData.initData(map(optData, function (item) { + return item[0]; + }), null, dimValueGetter); + toData.initData(map(optData, function (item) { + return item[1]; + }), null, dimValueGetter); + lineData.initData(map(optData, function (item) { + return item[2]; + })); + lineData.hasItemOption = true; + return { + from: fromData, + to: toData, + line: lineData + }; + } + + function install$F(registers) { + registers.registerComponentModel(MarkLineModel); + registers.registerComponentView(MarkLineView); + registers.registerPreprocessor(function (opt) { + if (checkMarkerInSeries(opt.series, 'markLine')) { + // Make sure markLine component is enabled + opt.markLine = opt.markLine || {}; + } + }); + } + + var MarkAreaModel = /** @class */function (_super) { + __extends(MarkAreaModel, _super); + function MarkAreaModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkAreaModel.type; + return _this; + } + MarkAreaModel.prototype.createMarkerModelFromSeries = function (markerOpt, masterMarkerModel, ecModel) { + return new MarkAreaModel(markerOpt, masterMarkerModel, ecModel); + }; + MarkAreaModel.type = 'markArea'; + MarkAreaModel.defaultOption = { + // zlevel: 0, + // PENDING + z: 1, + tooltip: { + trigger: 'item' + }, + // markArea should fixed on the coordinate system + animation: false, + label: { + show: true, + position: 'top' + }, + itemStyle: { + // color and borderColor default to use color from series + // color: 'auto' + // borderColor: 'auto' + borderWidth: 0 + }, + emphasis: { + label: { + show: true, + position: 'top' + } + } + }; + return MarkAreaModel; + }(MarkerModel); + + var inner$j = makeInner(); + var markAreaTransform = function (seriesModel, coordSys, maModel, item) { + // item may be null + var item0 = item[0]; + var item1 = item[1]; + if (!item0 || !item1) { + return; + } + var lt = dataTransform(seriesModel, item0); + var rb = dataTransform(seriesModel, item1); + // FIXME make sure lt is less than rb + var ltCoord = lt.coord; + var rbCoord = rb.coord; + ltCoord[0] = retrieve(ltCoord[0], -Infinity); + ltCoord[1] = retrieve(ltCoord[1], -Infinity); + rbCoord[0] = retrieve(rbCoord[0], Infinity); + rbCoord[1] = retrieve(rbCoord[1], Infinity); + // Merge option into one + var result = mergeAll([{}, lt, rb]); + result.coord = [lt.coord, rb.coord]; + result.x0 = lt.x; + result.y0 = lt.y; + result.x1 = rb.x; + result.y1 = rb.y; + return result; + }; + function isInfinity$1(val) { + return !isNaN(val) && !isFinite(val); + } + // If a markArea has one dim + function ifMarkAreaHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + return isInfinity$1(fromCoord[otherDimIndex]) && isInfinity$1(toCoord[otherDimIndex]); + } + function markAreaFilter(coordSys, item) { + var fromCoord = item.coord[0]; + var toCoord = item.coord[1]; + var item0 = { + coord: fromCoord, + x: item.x0, + y: item.y0 + }; + var item1 = { + coord: toCoord, + x: item.x1, + y: item.y1 + }; + if (isCoordinateSystemType(coordSys, 'cartesian2d')) { + // In case + // { + // markArea: { + // data: [{ yAxis: 2 }] + // } + // } + if (fromCoord && toCoord && (ifMarkAreaHasOnlyDim(1, fromCoord, toCoord) || ifMarkAreaHasOnlyDim(0, fromCoord, toCoord))) { + return true; + } + // Directly returning true may also do the work, + // because markArea will not be shown automatically + // when it's not included in coordinate system. + // But filtering ahead can avoid keeping rendering markArea + // when there are too many of them. + return zoneFilter(coordSys, item0, item1); + } + return dataFilter$1(coordSys, item0) || dataFilter$1(coordSys, item1); + } + // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0'] + function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + var point; + var xPx = parsePercent$1(itemModel.get(dims[0]), api.getWidth()); + var yPx = parsePercent$1(itemModel.get(dims[1]), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } else { + // Chart like bar may have there own marker positioning logic + if (seriesModel.getMarkerPosition) { + // Consider the case that user input the right-bottom point first + // Pick the larger x and y as 'x1' and 'y1' + var pointValue0 = data.getValues(['x0', 'y0'], idx); + var pointValue1 = data.getValues(['x1', 'y1'], idx); + var clampPointValue0 = coordSys.clampData(pointValue0); + var clampPointValue1 = coordSys.clampData(pointValue1); + var pointValue = []; + if (dims[0] === 'x0') { + pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue1[0] : pointValue0[0]; + } else { + pointValue[0] = clampPointValue0[0] > clampPointValue1[0] ? pointValue0[0] : pointValue1[0]; + } + if (dims[1] === 'y0') { + pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue1[1] : pointValue0[1]; + } else { + pointValue[1] = clampPointValue0[1] > clampPointValue1[1] ? pointValue0[1] : pointValue1[1]; + } + // Use the getMarkerPosition + point = seriesModel.getMarkerPosition(pointValue, dims, true); + } else { + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + var pt = [x, y]; + coordSys.clampData && coordSys.clampData(pt, pt); + point = coordSys.dataToPoint(pt, true); + } + if (isCoordinateSystemType(coordSys, 'cartesian2d')) { + // TODO: TYPE ts@4.1 may still infer it as Axis instead of Axis2D. Not sure if it's a bug + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + if (isInfinity$1(x)) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]); + } else if (isInfinity$1(y)) { + point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]); + } + } + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + } + return point; + } + var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']]; + var MarkAreaView = /** @class */function (_super) { + __extends(MarkAreaView, _super); + function MarkAreaView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = MarkAreaView.type; + return _this; + } + MarkAreaView.prototype.updateTransform = function (markAreaModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var maModel = MarkerModel.getMarkerModelFromSeries(seriesModel, 'markArea'); + if (maModel) { + var areaData_1 = maModel.getData(); + areaData_1.each(function (idx) { + var points = map(dimPermutations, function (dim) { + return getSingleMarkerEndPoint(areaData_1, idx, dim, seriesModel, api); + }); + // Layout + areaData_1.setItemLayout(idx, points); + var el = areaData_1.getItemGraphicEl(idx); + el.setShape('points', points); + }); + } + }, this); + }; + MarkAreaView.prototype.renderSeries = function (seriesModel, maModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesId = seriesModel.id; + var seriesData = seriesModel.getData(); + var areaGroupMap = this.markerGroupMap; + var polygonGroup = areaGroupMap.get(seriesId) || areaGroupMap.set(seriesId, { + group: new Group() + }); + this.group.add(polygonGroup.group); + this.markKeep(polygonGroup); + var areaData = createList$2(coordSys, seriesModel, maModel); + // Line data for tooltip and formatter + maModel.setData(areaData); + // Update visual and layout of line + areaData.each(function (idx) { + // Layout + var points = map(dimPermutations, function (dim) { + return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); + }); + var xAxisScale = coordSys.getAxis('x').scale; + var yAxisScale = coordSys.getAxis('y').scale; + var xAxisExtent = xAxisScale.getExtent(); + var yAxisExtent = yAxisScale.getExtent(); + var xPointExtent = [xAxisScale.parse(areaData.get('x0', idx)), xAxisScale.parse(areaData.get('x1', idx))]; + var yPointExtent = [yAxisScale.parse(areaData.get('y0', idx)), yAxisScale.parse(areaData.get('y1', idx))]; + asc(xPointExtent); + asc(yPointExtent); + var overlapped = !(xAxisExtent[0] > xPointExtent[1] || xAxisExtent[1] < xPointExtent[0] || yAxisExtent[0] > yPointExtent[1] || yAxisExtent[1] < yPointExtent[0]); + // If none of the area is inside coordSys, allClipped is set to be true + // in layout so that label will not be displayed. See #12591 + var allClipped = !overlapped; + areaData.setItemLayout(idx, { + points: points, + allClipped: allClipped + }); + var style = areaData.getItemModel(idx).getModel('itemStyle').getItemStyle(); + var color$1 = getVisualFromData(seriesData, 'color'); + if (!style.fill) { + style.fill = color$1; + if (isString(style.fill)) { + style.fill = modifyAlpha(style.fill, 0.4); + } + } + if (!style.stroke) { + style.stroke = color$1; + } + // Visual + areaData.setItemVisual(idx, 'style', style); + }); + areaData.diff(inner$j(polygonGroup).data).add(function (idx) { + var layout = areaData.getItemLayout(idx); + if (!layout.allClipped) { + var polygon = new Polygon({ + shape: { + points: layout.points + } + }); + areaData.setItemGraphicEl(idx, polygon); + polygonGroup.group.add(polygon); + } + }).update(function (newIdx, oldIdx) { + var polygon = inner$j(polygonGroup).data.getItemGraphicEl(oldIdx); + var layout = areaData.getItemLayout(newIdx); + if (!layout.allClipped) { + if (polygon) { + updateProps(polygon, { + shape: { + points: layout.points + } + }, maModel, newIdx); + } else { + polygon = new Polygon({ + shape: { + points: layout.points + } + }); + } + areaData.setItemGraphicEl(newIdx, polygon); + polygonGroup.group.add(polygon); + } else if (polygon) { + polygonGroup.group.remove(polygon); + } + }).remove(function (idx) { + var polygon = inner$j(polygonGroup).data.getItemGraphicEl(idx); + polygonGroup.group.remove(polygon); + }).execute(); + areaData.eachItemGraphicEl(function (polygon, idx) { + var itemModel = areaData.getItemModel(idx); + var style = areaData.getItemVisual(idx, 'style'); + polygon.useStyle(areaData.getItemVisual(idx, 'style')); + setLabelStyle(polygon, getLabelStatesModels(itemModel), { + labelFetcher: maModel, + labelDataIndex: idx, + defaultText: areaData.getName(idx) || '', + inheritColor: isString(style.fill) ? modifyAlpha(style.fill, 1) : '#000' + }); + setStatesStylesFromModel(polygon, itemModel); + toggleHoverEmphasis(polygon, null, null, itemModel.get(['emphasis', 'disabled'])); + getECData(polygon).dataModel = maModel; + }); + inner$j(polygonGroup).data = areaData; + polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent'); + }; + MarkAreaView.type = 'markArea'; + return MarkAreaView; + }(MarkerView); + function createList$2(coordSys, seriesModel, maModel) { + var areaData; + var dataDims; + var dims = ['x0', 'y0', 'x1', 'y1']; + if (coordSys) { + var coordDimsInfos_1 = map(coordSys && coordSys.dimensions, function (coordDim) { + var data = seriesModel.getData(); + var info = data.getDimensionInfo(data.mapDimension(coordDim)) || {}; + // In map series data don't have lng and lat dimension. Fallback to same with coordSys + return extend(extend({}, info), { + name: coordDim, + // DON'T use ordinalMeta to parse and collect ordinal. + ordinalMeta: null + }); + }); + dataDims = map(dims, function (dim, idx) { + return { + name: dim, + type: coordDimsInfos_1[idx % 2].type + }; + }); + areaData = new SeriesData(dataDims, maModel); + } else { + dataDims = [{ + name: 'value', + type: 'float' + }]; + areaData = new SeriesData(dataDims, maModel); + } + var optData = map(maModel.get('data'), curry(markAreaTransform, seriesModel, coordSys, maModel)); + if (coordSys) { + optData = filter(optData, curry(markAreaFilter, coordSys)); + } + var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) { + // TODO should convert to ParsedValue? + var rawVal = item.coord[Math.floor(dimIndex / 2)][dimIndex % 2]; + return parseDataValue(rawVal, dataDims[dimIndex]); + } : function (item, dimName, dataIndex, dimIndex) { + return parseDataValue(item.value, dataDims[dimIndex]); + }; + areaData.initData(optData, null, dimValueGetter); + areaData.hasItemOption = true; + return areaData; + } + + function install$G(registers) { + registers.registerComponentModel(MarkAreaModel); + registers.registerComponentView(MarkAreaView); + registers.registerPreprocessor(function (opt) { + if (checkMarkerInSeries(opt.series, 'markArea')) { + // Make sure markArea component is enabled + opt.markArea = opt.markArea || {}; + } + }); + } + + var getDefaultSelectorOptions = function (ecModel, type) { + if (type === 'all') { + return { + type: 'all', + title: ecModel.getLocaleModel().get(['legend', 'selector', 'all']) + }; + } else if (type === 'inverse') { + return { + type: 'inverse', + title: ecModel.getLocaleModel().get(['legend', 'selector', 'inverse']) + }; + } + }; + var LegendModel = /** @class */function (_super) { + __extends(LegendModel, _super); + function LegendModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = LegendModel.type; + _this.layoutMode = { + type: 'box', + // legend.width/height are maxWidth/maxHeight actually, + // whereas real width/height is calculated by its content. + // (Setting {left: 10, right: 10} does not make sense). + // So consider the case: + // `setOption({legend: {left: 10});` + // then `setOption({legend: {right: 10});` + // The previous `left` should be cleared by setting `ignoreSize`. + ignoreSize: true + }; + return _this; + } + LegendModel.prototype.init = function (option, parentModel, ecModel) { + this.mergeDefaultAndTheme(option, ecModel); + option.selected = option.selected || {}; + this._updateSelector(option); + }; + LegendModel.prototype.mergeOption = function (option, ecModel) { + _super.prototype.mergeOption.call(this, option, ecModel); + this._updateSelector(option); + }; + LegendModel.prototype._updateSelector = function (option) { + var selector = option.selector; + var ecModel = this.ecModel; + if (selector === true) { + selector = option.selector = ['all', 'inverse']; + } + if (isArray(selector)) { + each(selector, function (item, index) { + isString(item) && (item = { + type: item + }); + selector[index] = merge(item, getDefaultSelectorOptions(ecModel, item.type)); + }); + } + }; + LegendModel.prototype.optionUpdated = function () { + this._updateData(this.ecModel); + var legendData = this._data; + // If selectedMode is single, try to select one + if (legendData[0] && this.get('selectedMode') === 'single') { + var hasSelected = false; + // If has any selected in option.selected + for (var i = 0; i < legendData.length; i++) { + var name_1 = legendData[i].get('name'); + if (this.isSelected(name_1)) { + // Force to unselect others + this.select(name_1); + hasSelected = true; + break; + } + } + // Try select the first if selectedMode is single + !hasSelected && this.select(legendData[0].get('name')); + } + }; + LegendModel.prototype._updateData = function (ecModel) { + var potentialData = []; + var availableNames = []; + ecModel.eachRawSeries(function (seriesModel) { + var seriesName = seriesModel.name; + availableNames.push(seriesName); + var isPotential; + if (seriesModel.legendVisualProvider) { + var provider = seriesModel.legendVisualProvider; + var names = provider.getAllNames(); + if (!ecModel.isSeriesFiltered(seriesModel)) { + availableNames = availableNames.concat(names); + } + if (names.length) { + potentialData = potentialData.concat(names); + } else { + isPotential = true; + } + } else { + isPotential = true; + } + if (isPotential && isNameSpecified(seriesModel)) { + potentialData.push(seriesModel.name); + } + }); + /** + * @type {Array.} + * @private + */ + this._availableNames = availableNames; + // If legend.data is not specified in option, use availableNames as data, + // which is convenient for user preparing option. + var rawData = this.get('data') || potentialData; + var legendNameMap = createHashMap(); + var legendData = map(rawData, function (dataItem) { + // Can be string or number + if (isString(dataItem) || isNumber(dataItem)) { + dataItem = { + name: dataItem + }; + } + if (legendNameMap.get(dataItem.name)) { + // remove legend name duplicate + return null; + } + legendNameMap.set(dataItem.name, true); + return new Model(dataItem, this, this.ecModel); + }, this); + /** + * @type {Array.} + * @private + */ + this._data = filter(legendData, function (item) { + return !!item; + }); + }; + LegendModel.prototype.getData = function () { + return this._data; + }; + LegendModel.prototype.select = function (name) { + var selected = this.option.selected; + var selectedMode = this.get('selectedMode'); + if (selectedMode === 'single') { + var data = this._data; + each(data, function (dataItem) { + selected[dataItem.get('name')] = false; + }); + } + selected[name] = true; + }; + LegendModel.prototype.unSelect = function (name) { + if (this.get('selectedMode') !== 'single') { + this.option.selected[name] = false; + } + }; + LegendModel.prototype.toggleSelected = function (name) { + var selected = this.option.selected; + // Default is true + if (!selected.hasOwnProperty(name)) { + selected[name] = true; + } + this[selected[name] ? 'unSelect' : 'select'](name); + }; + LegendModel.prototype.allSelect = function () { + var data = this._data; + var selected = this.option.selected; + each(data, function (dataItem) { + selected[dataItem.get('name', true)] = true; + }); + }; + LegendModel.prototype.inverseSelect = function () { + var data = this._data; + var selected = this.option.selected; + each(data, function (dataItem) { + var name = dataItem.get('name', true); + // Initially, default value is true + if (!selected.hasOwnProperty(name)) { + selected[name] = true; + } + selected[name] = !selected[name]; + }); + }; + LegendModel.prototype.isSelected = function (name) { + var selected = this.option.selected; + return !(selected.hasOwnProperty(name) && !selected[name]) && indexOf(this._availableNames, name) >= 0; + }; + LegendModel.prototype.getOrient = function () { + return this.get('orient') === 'vertical' ? { + index: 1, + name: 'vertical' + } : { + index: 0, + name: 'horizontal' + }; + }; + LegendModel.type = 'legend.plain'; + LegendModel.dependencies = ['series']; + LegendModel.defaultOption = { + // zlevel: 0, + z: 4, + show: true, + orient: 'horizontal', + left: 'center', + // right: 'center', + top: 0, + // bottom: null, + align: 'auto', + backgroundColor: 'rgba(0,0,0,0)', + borderColor: '#ccc', + borderRadius: 0, + borderWidth: 0, + padding: 5, + itemGap: 10, + itemWidth: 25, + itemHeight: 14, + symbolRotate: 'inherit', + symbolKeepAspect: true, + inactiveColor: '#ccc', + inactiveBorderColor: '#ccc', + inactiveBorderWidth: 'auto', + itemStyle: { + color: 'inherit', + opacity: 'inherit', + borderColor: 'inherit', + borderWidth: 'auto', + borderCap: 'inherit', + borderJoin: 'inherit', + borderDashOffset: 'inherit', + borderMiterLimit: 'inherit' + }, + lineStyle: { + width: 'auto', + color: 'inherit', + inactiveColor: '#ccc', + inactiveWidth: 2, + opacity: 'inherit', + type: 'inherit', + cap: 'inherit', + join: 'inherit', + dashOffset: 'inherit', + miterLimit: 'inherit' + }, + textStyle: { + color: '#333' + }, + selectedMode: true, + selector: false, + selectorLabel: { + show: true, + borderRadius: 10, + padding: [3, 5, 3, 5], + fontSize: 12, + fontFamily: 'sans-serif', + color: '#666', + borderWidth: 1, + borderColor: '#666' + }, + emphasis: { + selectorLabel: { + show: true, + color: '#eee', + backgroundColor: '#666' + } + }, + selectorPosition: 'auto', + selectorItemGap: 7, + selectorButtonGap: 10, + tooltip: { + show: false + } + }; + return LegendModel; + }(ComponentModel); + + var curry$1 = curry; + var each$c = each; + var Group$2 = Group; + var LegendView = /** @class */function (_super) { + __extends(LegendView, _super); + function LegendView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = LegendView.type; + _this.newlineDisabled = false; + return _this; + } + LegendView.prototype.init = function () { + this.group.add(this._contentGroup = new Group$2()); + this.group.add(this._selectorGroup = new Group$2()); + this._isFirstRender = true; + }; + /** + * @protected + */ + LegendView.prototype.getContentGroup = function () { + return this._contentGroup; + }; + /** + * @protected + */ + LegendView.prototype.getSelectorGroup = function () { + return this._selectorGroup; + }; + /** + * @override + */ + LegendView.prototype.render = function (legendModel, ecModel, api) { + var isFirstRender = this._isFirstRender; + this._isFirstRender = false; + this.resetInner(); + if (!legendModel.get('show', true)) { + return; + } + var itemAlign = legendModel.get('align'); + var orient = legendModel.get('orient'); + if (!itemAlign || itemAlign === 'auto') { + itemAlign = legendModel.get('left') === 'right' && orient === 'vertical' ? 'right' : 'left'; + } + // selector has been normalized to an array in model + var selector = legendModel.get('selector', true); + var selectorPosition = legendModel.get('selectorPosition', true); + if (selector && (!selectorPosition || selectorPosition === 'auto')) { + selectorPosition = orient === 'horizontal' ? 'end' : 'start'; + } + this.renderInner(itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); + // Perform layout. + var positionInfo = legendModel.getBoxLayoutParams(); + var viewportSize = { + width: api.getWidth(), + height: api.getHeight() + }; + var padding = legendModel.get('padding'); + var maxSize = getLayoutRect(positionInfo, viewportSize, padding); + var mainRect = this.layoutInner(legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition); + // Place mainGroup, based on the calculated `mainRect`. + var layoutRect = getLayoutRect(defaults({ + width: mainRect.width, + height: mainRect.height + }, positionInfo), viewportSize, padding); + this.group.x = layoutRect.x - mainRect.x; + this.group.y = layoutRect.y - mainRect.y; + this.group.markRedraw(); + // Render background after group is layout. + this.group.add(this._backgroundEl = makeBackground(mainRect, legendModel)); + }; + LegendView.prototype.resetInner = function () { + this.getContentGroup().removeAll(); + this._backgroundEl && this.group.remove(this._backgroundEl); + this.getSelectorGroup().removeAll(); + }; + LegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) { + var contentGroup = this.getContentGroup(); + var legendDrawnMap = createHashMap(); + var selectMode = legendModel.get('selectedMode'); + var excludeSeriesId = []; + ecModel.eachRawSeries(function (seriesModel) { + !seriesModel.get('legendHoverLink') && excludeSeriesId.push(seriesModel.id); + }); + each$c(legendModel.getData(), function (legendItemModel, dataIndex) { + var name = legendItemModel.get('name'); + // Use empty string or \n as a newline string + if (!this.newlineDisabled && (name === '' || name === '\n')) { + var g = new Group$2(); + // @ts-ignore + g.newline = true; + contentGroup.add(g); + return; + } + // Representitive series. + var seriesModel = ecModel.getSeriesByName(name)[0]; + if (legendDrawnMap.get(name)) { + // Have been drawn + return; + } + // Legend to control series. + if (seriesModel) { + var data = seriesModel.getData(); + var lineVisualStyle = data.getVisual('legendLineStyle') || {}; + var legendIcon = data.getVisual('legendIcon'); + /** + * `data.getVisual('style')` may be the color from the register + * in series. For example, for line series, + */ + var style = data.getVisual('style'); + var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, style, legendIcon, selectMode, api); + itemGroup.on('click', curry$1(dispatchSelectAction, name, null, api, excludeSeriesId)).on('mouseover', curry$1(dispatchHighlightAction, seriesModel.name, null, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, seriesModel.name, null, api, excludeSeriesId)); + if (ecModel.ssr) { + itemGroup.eachChild(function (child) { + var ecData = getECData(child); + ecData.seriesIndex = seriesModel.seriesIndex; + ecData.dataIndex = dataIndex; + ecData.ssrType = 'legend'; + }); + } + legendDrawnMap.set(name, true); + } else { + // Legend to control data. In pie and funnel. + ecModel.eachRawSeries(function (seriesModel) { + // In case multiple series has same data name + if (legendDrawnMap.get(name)) { + return; + } + if (seriesModel.legendVisualProvider) { + var provider = seriesModel.legendVisualProvider; + if (!provider.containName(name)) { + return; + } + var idx = provider.indexOfName(name); + var style = provider.getItemVisual(idx, 'style'); + var legendIcon = provider.getItemVisual(idx, 'legendIcon'); + var colorArr = parse(style.fill); + // Color may be set to transparent in visualMap when data is out of range. + // Do not show nothing. + if (colorArr && colorArr[3] === 0) { + colorArr[3] = 0.2; + // TODO color is set to 0, 0, 0, 0. Should show correct RGBA + style = extend(extend({}, style), { + fill: stringify(colorArr, 'rgba') + }); + } + var itemGroup = this._createItem(seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, {}, style, legendIcon, selectMode, api); + // FIXME: consider different series has items with the same name. + itemGroup.on('click', curry$1(dispatchSelectAction, null, name, api, excludeSeriesId)) + // Should not specify the series name, consider legend controls + // more than one pie series. + .on('mouseover', curry$1(dispatchHighlightAction, null, name, api, excludeSeriesId)).on('mouseout', curry$1(dispatchDownplayAction, null, name, api, excludeSeriesId)); + if (ecModel.ssr) { + itemGroup.eachChild(function (child) { + var ecData = getECData(child); + ecData.seriesIndex = seriesModel.seriesIndex; + ecData.dataIndex = dataIndex; + ecData.ssrType = 'legend'; + }); + } + legendDrawnMap.set(name, true); + } + }, this); + } + if ("development" !== 'production') { + if (!legendDrawnMap.get(name)) { + console.warn(name + ' series not exists. Legend data should be same with series name or data name.'); + } + } + }, this); + if (selector) { + this._createSelector(selector, legendModel, api, orient, selectorPosition); + } + }; + LegendView.prototype._createSelector = function (selector, legendModel, api, orient, selectorPosition) { + var selectorGroup = this.getSelectorGroup(); + each$c(selector, function createSelectorButton(selectorItem) { + var type = selectorItem.type; + var labelText = new ZRText({ + style: { + x: 0, + y: 0, + align: 'center', + verticalAlign: 'middle' + }, + onclick: function () { + api.dispatchAction({ + type: type === 'all' ? 'legendAllSelect' : 'legendInverseSelect' + }); + } + }); + selectorGroup.add(labelText); + var labelModel = legendModel.getModel('selectorLabel'); + var emphasisLabelModel = legendModel.getModel(['emphasis', 'selectorLabel']); + setLabelStyle(labelText, { + normal: labelModel, + emphasis: emphasisLabelModel + }, { + defaultText: selectorItem.title + }); + enableHoverEmphasis(labelText); + }); + }; + LegendView.prototype._createItem = function (seriesModel, name, dataIndex, legendItemModel, legendModel, itemAlign, lineVisualStyle, itemVisualStyle, legendIcon, selectMode, api) { + var drawType = seriesModel.visualDrawType; + var itemWidth = legendModel.get('itemWidth'); + var itemHeight = legendModel.get('itemHeight'); + var isSelected = legendModel.isSelected(name); + var iconRotate = legendItemModel.get('symbolRotate'); + var symbolKeepAspect = legendItemModel.get('symbolKeepAspect'); + var legendIconType = legendItemModel.get('icon'); + legendIcon = legendIconType || legendIcon || 'roundRect'; + var style = getLegendStyle(legendIcon, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api); + var itemGroup = new Group$2(); + var textStyleModel = legendItemModel.getModel('textStyle'); + if (isFunction(seriesModel.getLegendIcon) && (!legendIconType || legendIconType === 'inherit')) { + // Series has specific way to define legend icon + itemGroup.add(seriesModel.getLegendIcon({ + itemWidth: itemWidth, + itemHeight: itemHeight, + icon: legendIcon, + iconRotate: iconRotate, + itemStyle: style.itemStyle, + lineStyle: style.lineStyle, + symbolKeepAspect: symbolKeepAspect + })); + } else { + // Use default legend icon policy for most series + var rotate = legendIconType === 'inherit' && seriesModel.getData().getVisual('symbol') ? iconRotate === 'inherit' ? seriesModel.getData().getVisual('symbolRotate') : iconRotate : 0; // No rotation for no icon + itemGroup.add(getDefaultLegendIcon({ + itemWidth: itemWidth, + itemHeight: itemHeight, + icon: legendIcon, + iconRotate: rotate, + itemStyle: style.itemStyle, + lineStyle: style.lineStyle, + symbolKeepAspect: symbolKeepAspect + })); + } + var textX = itemAlign === 'left' ? itemWidth + 5 : -5; + var textAlign = itemAlign; + var formatter = legendModel.get('formatter'); + var content = name; + if (isString(formatter) && formatter) { + content = formatter.replace('{name}', name != null ? name : ''); + } else if (isFunction(formatter)) { + content = formatter(name); + } + var textColor = isSelected ? textStyleModel.getTextColor() : legendItemModel.get('inactiveColor'); + itemGroup.add(new ZRText({ + style: createTextStyle(textStyleModel, { + text: content, + x: textX, + y: itemHeight / 2, + fill: textColor, + align: textAlign, + verticalAlign: 'middle' + }, { + inheritColor: textColor + }) + })); + // Add a invisible rect to increase the area of mouse hover + var hitRect = new Rect({ + shape: itemGroup.getBoundingRect(), + style: { + // Cannot use 'invisible' because SVG SSR will miss the node + fill: 'transparent' + } + }); + var tooltipModel = legendItemModel.getModel('tooltip'); + if (tooltipModel.get('show')) { + setTooltipConfig({ + el: hitRect, + componentModel: legendModel, + itemName: name, + itemTooltipOption: tooltipModel.option + }); + } + itemGroup.add(hitRect); + itemGroup.eachChild(function (child) { + child.silent = true; + }); + hitRect.silent = !selectMode; + this.getContentGroup().add(itemGroup); + enableHoverEmphasis(itemGroup); + // @ts-ignore + itemGroup.__legendDataIndex = dataIndex; + return itemGroup; + }; + LegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) { + var contentGroup = this.getContentGroup(); + var selectorGroup = this.getSelectorGroup(); + // Place items in contentGroup. + box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), maxSize.width, maxSize.height); + var contentRect = contentGroup.getBoundingRect(); + var contentPos = [-contentRect.x, -contentRect.y]; + selectorGroup.markRedraw(); + contentGroup.markRedraw(); + if (selector) { + // Place buttons in selectorGroup + box( + // Buttons in selectorGroup always layout horizontally + 'horizontal', selectorGroup, legendModel.get('selectorItemGap', true)); + var selectorRect = selectorGroup.getBoundingRect(); + var selectorPos = [-selectorRect.x, -selectorRect.y]; + var selectorButtonGap = legendModel.get('selectorButtonGap', true); + var orientIdx = legendModel.getOrient().index; + var wh = orientIdx === 0 ? 'width' : 'height'; + var hw = orientIdx === 0 ? 'height' : 'width'; + var yx = orientIdx === 0 ? 'y' : 'x'; + if (selectorPosition === 'end') { + selectorPos[orientIdx] += contentRect[wh] + selectorButtonGap; + } else { + contentPos[orientIdx] += selectorRect[wh] + selectorButtonGap; + } + // Always align selector to content as 'middle' + selectorPos[1 - orientIdx] += contentRect[hw] / 2 - selectorRect[hw] / 2; + selectorGroup.x = selectorPos[0]; + selectorGroup.y = selectorPos[1]; + contentGroup.x = contentPos[0]; + contentGroup.y = contentPos[1]; + var mainRect = { + x: 0, + y: 0 + }; + mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh]; + mainRect[hw] = Math.max(contentRect[hw], selectorRect[hw]); + mainRect[yx] = Math.min(0, selectorRect[yx] + selectorPos[1 - orientIdx]); + return mainRect; + } else { + contentGroup.x = contentPos[0]; + contentGroup.y = contentPos[1]; + return this.group.getBoundingRect(); + } + }; + /** + * @protected + */ + LegendView.prototype.remove = function () { + this.getContentGroup().removeAll(); + this._isFirstRender = true; + }; + LegendView.type = 'legend.plain'; + return LegendView; + }(ComponentView); + function getLegendStyle(iconType, legendItemModel, lineVisualStyle, itemVisualStyle, drawType, isSelected, api) { + /** + * Use series style if is inherit; + * elsewise, use legend style + */ + function handleCommonProps(style, visualStyle) { + // If lineStyle.width is 'auto', it is set to be 2 if series has border + if (style.lineWidth === 'auto') { + style.lineWidth = visualStyle.lineWidth > 0 ? 2 : 0; + } + each$c(style, function (propVal, propName) { + style[propName] === 'inherit' && (style[propName] = visualStyle[propName]); + }); + } + // itemStyle + var itemStyleModel = legendItemModel.getModel('itemStyle'); + var itemStyle = itemStyleModel.getItemStyle(); + var iconBrushType = iconType.lastIndexOf('empty', 0) === 0 ? 'fill' : 'stroke'; + var decalStyle = itemStyleModel.getShallow('decal'); + itemStyle.decal = !decalStyle || decalStyle === 'inherit' ? itemVisualStyle.decal : createOrUpdatePatternFromDecal(decalStyle, api); + if (itemStyle.fill === 'inherit') { + /** + * Series with visualDrawType as 'stroke' should have + * series stroke as legend fill + */ + itemStyle.fill = itemVisualStyle[drawType]; + } + if (itemStyle.stroke === 'inherit') { + /** + * icon type with "emptyXXX" should use fill color + * in visual style + */ + itemStyle.stroke = itemVisualStyle[iconBrushType]; + } + if (itemStyle.opacity === 'inherit') { + /** + * Use lineStyle.opacity if drawType is stroke + */ + itemStyle.opacity = (drawType === 'fill' ? itemVisualStyle : lineVisualStyle).opacity; + } + handleCommonProps(itemStyle, itemVisualStyle); + // lineStyle + var legendLineModel = legendItemModel.getModel('lineStyle'); + var lineStyle = legendLineModel.getLineStyle(); + handleCommonProps(lineStyle, lineVisualStyle); + // Fix auto color to real color + itemStyle.fill === 'auto' && (itemStyle.fill = itemVisualStyle.fill); + itemStyle.stroke === 'auto' && (itemStyle.stroke = itemVisualStyle.fill); + lineStyle.stroke === 'auto' && (lineStyle.stroke = itemVisualStyle.fill); + if (!isSelected) { + var borderWidth = legendItemModel.get('inactiveBorderWidth'); + /** + * Since stroke is set to be inactiveBorderColor, it may occur that + * there is no border in series but border in legend, so we need to + * use border only when series has border if is set to be auto + */ + var visualHasBorder = itemStyle[iconBrushType]; + itemStyle.lineWidth = borderWidth === 'auto' ? itemVisualStyle.lineWidth > 0 && visualHasBorder ? 2 : 0 : itemStyle.lineWidth; + itemStyle.fill = legendItemModel.get('inactiveColor'); + itemStyle.stroke = legendItemModel.get('inactiveBorderColor'); + lineStyle.stroke = legendLineModel.get('inactiveColor'); + lineStyle.lineWidth = legendLineModel.get('inactiveWidth'); + } + return { + itemStyle: itemStyle, + lineStyle: lineStyle + }; + } + function getDefaultLegendIcon(opt) { + var symboType = opt.icon || 'roundRect'; + var icon = createSymbol(symboType, 0, 0, opt.itemWidth, opt.itemHeight, opt.itemStyle.fill, opt.symbolKeepAspect); + icon.setStyle(opt.itemStyle); + icon.rotation = (opt.iconRotate || 0) * Math.PI / 180; + icon.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]); + if (symboType.indexOf('empty') > -1) { + icon.style.stroke = icon.style.fill; + icon.style.fill = '#fff'; + icon.style.lineWidth = 2; + } + return icon; + } + function dispatchSelectAction(seriesName, dataName, api, excludeSeriesId) { + // downplay before unselect + dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId); + api.dispatchAction({ + type: 'legendToggleSelect', + name: seriesName != null ? seriesName : dataName + }); + // highlight after select + // TODO highlight immediately may cause animation loss. + dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId); + } + function isUseHoverLayer(api) { + var list = api.getZr().storage.getDisplayList(); + var emphasisState; + var i = 0; + var len = list.length; + while (i < len && !(emphasisState = list[i].states.emphasis)) { + i++; + } + return emphasisState && emphasisState.hoverLayer; + } + function dispatchHighlightAction(seriesName, dataName, api, excludeSeriesId) { + // If element hover will move to a hoverLayer. + if (!isUseHoverLayer(api)) { + api.dispatchAction({ + type: 'highlight', + seriesName: seriesName, + name: dataName, + excludeSeriesId: excludeSeriesId + }); + } + } + function dispatchDownplayAction(seriesName, dataName, api, excludeSeriesId) { + // If element hover will move to a hoverLayer. + if (!isUseHoverLayer(api)) { + api.dispatchAction({ + type: 'downplay', + seriesName: seriesName, + name: dataName, + excludeSeriesId: excludeSeriesId + }); + } + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function legendFilter(ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (legendModels && legendModels.length) { + ecModel.filterSeries(function (series) { + // If in any legend component the status is not selected. + // Because in legend series is assumed selected when it is not in the legend data. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(series.name)) { + return false; + } + } + return true; + }); + } + } + + function legendSelectActionHandler(methodName, payload, ecModel) { + var selectedMap = {}; + var isToggleSelect = methodName === 'toggleSelected'; + var isSelected; + // Update all legend components + ecModel.eachComponent('legend', function (legendModel) { + if (isToggleSelect && isSelected != null) { + // Force other legend has same selected status + // Or the first is toggled to true and other are toggled to false + // In the case one legend has some item unSelected in option. And if other legend + // doesn't has the item, they will assume it is selected. + legendModel[isSelected ? 'select' : 'unSelect'](payload.name); + } else if (methodName === 'allSelect' || methodName === 'inverseSelect') { + legendModel[methodName](); + } else { + legendModel[methodName](payload.name); + isSelected = legendModel.isSelected(payload.name); + } + var legendData = legendModel.getData(); + each(legendData, function (model) { + var name = model.get('name'); + // Wrap element + if (name === '\n' || name === '') { + return; + } + var isItemSelected = legendModel.isSelected(name); + if (selectedMap.hasOwnProperty(name)) { + // Unselected if any legend is unselected + selectedMap[name] = selectedMap[name] && isItemSelected; + } else { + selectedMap[name] = isItemSelected; + } + }); + }); + // Return the event explicitly + return methodName === 'allSelect' || methodName === 'inverseSelect' ? { + selected: selectedMap + } : { + name: payload.name, + selected: selectedMap + }; + } + function installLegendAction(registers) { + /** + * @event legendToggleSelect + * @type {Object} + * @property {string} type 'legendToggleSelect' + * @property {string} [from] + * @property {string} name Series name or data item name + */ + registers.registerAction('legendToggleSelect', 'legendselectchanged', curry(legendSelectActionHandler, 'toggleSelected')); + registers.registerAction('legendAllSelect', 'legendselectall', curry(legendSelectActionHandler, 'allSelect')); + registers.registerAction('legendInverseSelect', 'legendinverseselect', curry(legendSelectActionHandler, 'inverseSelect')); + /** + * @event legendSelect + * @type {Object} + * @property {string} type 'legendSelect' + * @property {string} name Series name or data item name + */ + registers.registerAction('legendSelect', 'legendselected', curry(legendSelectActionHandler, 'select')); + /** + * @event legendUnSelect + * @type {Object} + * @property {string} type 'legendUnSelect' + * @property {string} name Series name or data item name + */ + registers.registerAction('legendUnSelect', 'legendunselected', curry(legendSelectActionHandler, 'unSelect')); + } + + function install$H(registers) { + registers.registerComponentModel(LegendModel); + registers.registerComponentView(LegendView); + registers.registerProcessor(registers.PRIORITY.PROCESSOR.SERIES_FILTER, legendFilter); + registers.registerSubTypeDefaulter('legend', function () { + return 'plain'; + }); + installLegendAction(registers); + } + + var ScrollableLegendModel = /** @class */function (_super) { + __extends(ScrollableLegendModel, _super); + function ScrollableLegendModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ScrollableLegendModel.type; + return _this; + } + /** + * @param {number} scrollDataIndex + */ + ScrollableLegendModel.prototype.setScrollDataIndex = function (scrollDataIndex) { + this.option.scrollDataIndex = scrollDataIndex; + }; + ScrollableLegendModel.prototype.init = function (option, parentModel, ecModel) { + var inputPositionParams = getLayoutParams(option); + _super.prototype.init.call(this, option, parentModel, ecModel); + mergeAndNormalizeLayoutParams$1(this, option, inputPositionParams); + }; + /** + * @override + */ + ScrollableLegendModel.prototype.mergeOption = function (option, ecModel) { + _super.prototype.mergeOption.call(this, option, ecModel); + mergeAndNormalizeLayoutParams$1(this, this.option, option); + }; + ScrollableLegendModel.type = 'legend.scroll'; + ScrollableLegendModel.defaultOption = inheritDefaultOption(LegendModel.defaultOption, { + scrollDataIndex: 0, + pageButtonItemGap: 5, + pageButtonGap: null, + pageButtonPosition: 'end', + pageFormatter: '{current}/{total}', + pageIcons: { + horizontal: ['M0,0L12,-10L12,10z', 'M0,0L-12,-10L-12,10z'], + vertical: ['M0,0L20,0L10,-20z', 'M0,0L20,0L10,20z'] + }, + pageIconColor: '#2f4554', + pageIconInactiveColor: '#aaa', + pageIconSize: 15, + pageTextStyle: { + color: '#333' + }, + animationDurationUpdate: 800 + }); + return ScrollableLegendModel; + }(LegendModel); + // Do not `ignoreSize` to enable setting {left: 10, right: 10}. + function mergeAndNormalizeLayoutParams$1(legendModel, target, raw) { + var orient = legendModel.getOrient(); + var ignoreSize = [1, 1]; + ignoreSize[orient.index] = 0; + mergeLayoutParam(target, raw, { + type: 'box', + ignoreSize: !!ignoreSize + }); + } + + var Group$3 = Group; + var WH$1 = ['width', 'height']; + var XY$1 = ['x', 'y']; + var ScrollableLegendView = /** @class */function (_super) { + __extends(ScrollableLegendView, _super); + function ScrollableLegendView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ScrollableLegendView.type; + _this.newlineDisabled = true; + _this._currentIndex = 0; + return _this; + } + ScrollableLegendView.prototype.init = function () { + _super.prototype.init.call(this); + this.group.add(this._containerGroup = new Group$3()); + this._containerGroup.add(this.getContentGroup()); + this.group.add(this._controllerGroup = new Group$3()); + }; + /** + * @override + */ + ScrollableLegendView.prototype.resetInner = function () { + _super.prototype.resetInner.call(this); + this._controllerGroup.removeAll(); + this._containerGroup.removeClipPath(); + this._containerGroup.__rectSize = null; + }; + /** + * @override + */ + ScrollableLegendView.prototype.renderInner = function (itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition) { + var self = this; + // Render content items. + _super.prototype.renderInner.call(this, itemAlign, legendModel, ecModel, api, selector, orient, selectorPosition); + var controllerGroup = this._controllerGroup; + // FIXME: support be 'auto' adapt to size number text length, + // e.g., '3/12345' should not overlap with the control arrow button. + var pageIconSize = legendModel.get('pageIconSize', true); + var pageIconSizeArr = isArray(pageIconSize) ? pageIconSize : [pageIconSize, pageIconSize]; + createPageButton('pagePrev', 0); + var pageTextStyleModel = legendModel.getModel('pageTextStyle'); + controllerGroup.add(new ZRText({ + name: 'pageText', + style: { + // Placeholder to calculate a proper layout. + text: 'xx/xx', + fill: pageTextStyleModel.getTextColor(), + font: pageTextStyleModel.getFont(), + verticalAlign: 'middle', + align: 'center' + }, + silent: true + })); + createPageButton('pageNext', 1); + function createPageButton(name, iconIdx) { + var pageDataIndexName = name + 'DataIndex'; + var icon = createIcon(legendModel.get('pageIcons', true)[legendModel.getOrient().name][iconIdx], { + // Buttons will be created in each render, so we do not need + // to worry about avoiding using legendModel kept in scope. + onclick: bind(self._pageGo, self, pageDataIndexName, legendModel, api) + }, { + x: -pageIconSizeArr[0] / 2, + y: -pageIconSizeArr[1] / 2, + width: pageIconSizeArr[0], + height: pageIconSizeArr[1] + }); + icon.name = name; + controllerGroup.add(icon); + } + }; + /** + * @override + */ + ScrollableLegendView.prototype.layoutInner = function (legendModel, itemAlign, maxSize, isFirstRender, selector, selectorPosition) { + var selectorGroup = this.getSelectorGroup(); + var orientIdx = legendModel.getOrient().index; + var wh = WH$1[orientIdx]; + var xy = XY$1[orientIdx]; + var hw = WH$1[1 - orientIdx]; + var yx = XY$1[1 - orientIdx]; + selector && box( + // Buttons in selectorGroup always layout horizontally + 'horizontal', selectorGroup, legendModel.get('selectorItemGap', true)); + var selectorButtonGap = legendModel.get('selectorButtonGap', true); + var selectorRect = selectorGroup.getBoundingRect(); + var selectorPos = [-selectorRect.x, -selectorRect.y]; + var processMaxSize = clone(maxSize); + selector && (processMaxSize[wh] = maxSize[wh] - selectorRect[wh] - selectorButtonGap); + var mainRect = this._layoutContentAndController(legendModel, isFirstRender, processMaxSize, orientIdx, wh, hw, yx, xy); + if (selector) { + if (selectorPosition === 'end') { + selectorPos[orientIdx] += mainRect[wh] + selectorButtonGap; + } else { + var offset = selectorRect[wh] + selectorButtonGap; + selectorPos[orientIdx] -= offset; + mainRect[xy] -= offset; + } + mainRect[wh] += selectorRect[wh] + selectorButtonGap; + selectorPos[1 - orientIdx] += mainRect[yx] + mainRect[hw] / 2 - selectorRect[hw] / 2; + mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]); + mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]); + selectorGroup.x = selectorPos[0]; + selectorGroup.y = selectorPos[1]; + selectorGroup.markRedraw(); + } + return mainRect; + }; + ScrollableLegendView.prototype._layoutContentAndController = function (legendModel, isFirstRender, maxSize, orientIdx, wh, hw, yx, xy) { + var contentGroup = this.getContentGroup(); + var containerGroup = this._containerGroup; + var controllerGroup = this._controllerGroup; + // Place items in contentGroup. + box(legendModel.get('orient'), contentGroup, legendModel.get('itemGap'), !orientIdx ? null : maxSize.width, orientIdx ? null : maxSize.height); + box( + // Buttons in controller are layout always horizontally. + 'horizontal', controllerGroup, legendModel.get('pageButtonItemGap', true)); + var contentRect = contentGroup.getBoundingRect(); + var controllerRect = controllerGroup.getBoundingRect(); + var showController = this._showController = contentRect[wh] > maxSize[wh]; + // In case that the inner elements of contentGroup layout do not based on [0, 0] + var contentPos = [-contentRect.x, -contentRect.y]; + // Remain contentPos when scroll animation perfroming. + // If first rendering, `contentGroup.position` is [0, 0], which + // does not make sense and may cause unexepcted animation if adopted. + if (!isFirstRender) { + contentPos[orientIdx] = contentGroup[xy]; + } + // Layout container group based on 0. + var containerPos = [0, 0]; + var controllerPos = [-controllerRect.x, -controllerRect.y]; + var pageButtonGap = retrieve2(legendModel.get('pageButtonGap', true), legendModel.get('itemGap', true)); + // Place containerGroup and controllerGroup and contentGroup. + if (showController) { + var pageButtonPosition = legendModel.get('pageButtonPosition', true); + // controller is on the right / bottom. + if (pageButtonPosition === 'end') { + controllerPos[orientIdx] += maxSize[wh] - controllerRect[wh]; + } + // controller is on the left / top. + else { + containerPos[orientIdx] += controllerRect[wh] + pageButtonGap; + } + } + // Always align controller to content as 'middle'. + controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2; + contentGroup.setPosition(contentPos); + containerGroup.setPosition(containerPos); + controllerGroup.setPosition(controllerPos); + // Calculate `mainRect` and set `clipPath`. + // mainRect should not be calculated by `this.group.getBoundingRect()` + // for sake of the overflow. + var mainRect = { + x: 0, + y: 0 + }; + // Consider content may be overflow (should be clipped). + mainRect[wh] = showController ? maxSize[wh] : contentRect[wh]; + mainRect[hw] = Math.max(contentRect[hw], controllerRect[hw]); + // `containerRect[yx] + containerPos[1 - orientIdx]` is 0. + mainRect[yx] = Math.min(0, controllerRect[yx] + controllerPos[1 - orientIdx]); + containerGroup.__rectSize = maxSize[wh]; + if (showController) { + var clipShape = { + x: 0, + y: 0 + }; + clipShape[wh] = Math.max(maxSize[wh] - controllerRect[wh] - pageButtonGap, 0); + clipShape[hw] = mainRect[hw]; + containerGroup.setClipPath(new Rect({ + shape: clipShape + })); + // Consider content may be larger than container, container rect + // can not be obtained from `containerGroup.getBoundingRect()`. + containerGroup.__rectSize = clipShape[wh]; + } else { + // Do not remove or ignore controller. Keep them set as placeholders. + controllerGroup.eachChild(function (child) { + child.attr({ + invisible: true, + silent: true + }); + }); + } + // Content translate animation. + var pageInfo = this._getPageInfo(legendModel); + pageInfo.pageIndex != null && updateProps(contentGroup, { + x: pageInfo.contentPosition[0], + y: pageInfo.contentPosition[1] + }, + // When switch from "show controller" to "not show controller", view should be + // updated immediately without animation, otherwise causes weird effect. + showController ? legendModel : null); + this._updatePageInfoView(legendModel, pageInfo); + return mainRect; + }; + ScrollableLegendView.prototype._pageGo = function (to, legendModel, api) { + var scrollDataIndex = this._getPageInfo(legendModel)[to]; + scrollDataIndex != null && api.dispatchAction({ + type: 'legendScroll', + scrollDataIndex: scrollDataIndex, + legendId: legendModel.id + }); + }; + ScrollableLegendView.prototype._updatePageInfoView = function (legendModel, pageInfo) { + var controllerGroup = this._controllerGroup; + each(['pagePrev', 'pageNext'], function (name) { + var key = name + 'DataIndex'; + var canJump = pageInfo[key] != null; + var icon = controllerGroup.childOfName(name); + if (icon) { + icon.setStyle('fill', canJump ? legendModel.get('pageIconColor', true) : legendModel.get('pageIconInactiveColor', true)); + icon.cursor = canJump ? 'pointer' : 'default'; + } + }); + var pageText = controllerGroup.childOfName('pageText'); + var pageFormatter = legendModel.get('pageFormatter'); + var pageIndex = pageInfo.pageIndex; + var current = pageIndex != null ? pageIndex + 1 : 0; + var total = pageInfo.pageCount; + pageText && pageFormatter && pageText.setStyle('text', isString(pageFormatter) ? pageFormatter.replace('{current}', current == null ? '' : current + '').replace('{total}', total == null ? '' : total + '') : pageFormatter({ + current: current, + total: total + })); + }; + /** + * contentPosition: Array., null when data item not found. + * pageIndex: number, null when data item not found. + * pageCount: number, always be a number, can be 0. + * pagePrevDataIndex: number, null when no previous page. + * pageNextDataIndex: number, null when no next page. + * } + */ + ScrollableLegendView.prototype._getPageInfo = function (legendModel) { + var scrollDataIndex = legendModel.get('scrollDataIndex', true); + var contentGroup = this.getContentGroup(); + var containerRectSize = this._containerGroup.__rectSize; + var orientIdx = legendModel.getOrient().index; + var wh = WH$1[orientIdx]; + var xy = XY$1[orientIdx]; + var targetItemIndex = this._findTargetItemIndex(scrollDataIndex); + var children = contentGroup.children(); + var targetItem = children[targetItemIndex]; + var itemCount = children.length; + var pCount = !itemCount ? 0 : 1; + var result = { + contentPosition: [contentGroup.x, contentGroup.y], + pageCount: pCount, + pageIndex: pCount - 1, + pagePrevDataIndex: null, + pageNextDataIndex: null + }; + if (!targetItem) { + return result; + } + var targetItemInfo = getItemInfo(targetItem); + result.contentPosition[orientIdx] = -targetItemInfo.s; + // Strategy: + // (1) Always align based on the left/top most item. + // (2) It is user-friendly that the last item shown in the + // current window is shown at the begining of next window. + // Otherwise if half of the last item is cut by the window, + // it will have no chance to display entirely. + // (3) Consider that item size probably be different, we + // have calculate pageIndex by size rather than item index, + // and we can not get page index directly by division. + // (4) The window is to narrow to contain more than + // one item, we should make sure that the page can be fliped. + for (var i = targetItemIndex + 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i <= itemCount; ++i) { + currItemInfo = getItemInfo(children[i]); + if ( + // Half of the last item is out of the window. + !currItemInfo && winEndItemInfo.e > winStartItemInfo.s + containerRectSize + // If the current item does not intersect with the window, the new page + // can be started at the current item or the last item. + || currItemInfo && !intersect(currItemInfo, winStartItemInfo.s)) { + if (winEndItemInfo.i > winStartItemInfo.i) { + winStartItemInfo = winEndItemInfo; + } else { + // e.g., when page size is smaller than item size. + winStartItemInfo = currItemInfo; + } + if (winStartItemInfo) { + if (result.pageNextDataIndex == null) { + result.pageNextDataIndex = winStartItemInfo.i; + } + ++result.pageCount; + } + } + winEndItemInfo = currItemInfo; + } + for (var i = targetItemIndex - 1, winStartItemInfo = targetItemInfo, winEndItemInfo = targetItemInfo, currItemInfo = null; i >= -1; --i) { + currItemInfo = getItemInfo(children[i]); + if ( + // If the the end item does not intersect with the window started + // from the current item, a page can be settled. + (!currItemInfo || !intersect(winEndItemInfo, currItemInfo.s) + // e.g., when page size is smaller than item size. + ) && winStartItemInfo.i < winEndItemInfo.i) { + winEndItemInfo = winStartItemInfo; + if (result.pagePrevDataIndex == null) { + result.pagePrevDataIndex = winStartItemInfo.i; + } + ++result.pageCount; + ++result.pageIndex; + } + winStartItemInfo = currItemInfo; + } + return result; + function getItemInfo(el) { + if (el) { + var itemRect = el.getBoundingRect(); + var start = itemRect[xy] + el[xy]; + return { + s: start, + e: start + itemRect[wh], + i: el.__legendDataIndex + }; + } + } + function intersect(itemInfo, winStart) { + return itemInfo.e >= winStart && itemInfo.s <= winStart + containerRectSize; + } + }; + ScrollableLegendView.prototype._findTargetItemIndex = function (targetDataIndex) { + if (!this._showController) { + return 0; + } + var index; + var contentGroup = this.getContentGroup(); + var defaultIndex; + contentGroup.eachChild(function (child, idx) { + var legendDataIdx = child.__legendDataIndex; + // FIXME + // If the given targetDataIndex (from model) is illegal, + // we use defaultIndex. But the index on the legend model and + // action payload is still illegal. That case will not be + // changed until some scenario requires. + if (defaultIndex == null && legendDataIdx != null) { + defaultIndex = idx; + } + if (legendDataIdx === targetDataIndex) { + index = idx; + } + }); + return index != null ? index : defaultIndex; + }; + ScrollableLegendView.type = 'legend.scroll'; + return ScrollableLegendView; + }(LegendView); + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + function installScrollableLegendAction(registers) { + /** + * @event legendScroll + * @type {Object} + * @property {string} type 'legendScroll' + * @property {string} scrollDataIndex + */ + registers.registerAction('legendScroll', 'legendscroll', function (payload, ecModel) { + var scrollDataIndex = payload.scrollDataIndex; + scrollDataIndex != null && ecModel.eachComponent({ + mainType: 'legend', + subType: 'scroll', + query: payload + }, function (legendModel) { + legendModel.setScrollDataIndex(scrollDataIndex); + }); + }); + } + + function install$I(registers) { + use(install$H); + registers.registerComponentModel(ScrollableLegendModel); + registers.registerComponentView(ScrollableLegendView); + installScrollableLegendAction(registers); + } + + function install$J(registers) { + use(install$H); + use(install$I); + } + + var InsideZoomModel = /** @class */function (_super) { + __extends(InsideZoomModel, _super); + function InsideZoomModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = InsideZoomModel.type; + return _this; + } + InsideZoomModel.type = 'dataZoom.inside'; + InsideZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, { + disabled: false, + zoomLock: false, + zoomOnMouseWheel: true, + moveOnMouseMove: true, + moveOnMouseWheel: false, + preventDefaultMouseMove: true + }); + return InsideZoomModel; + }(DataZoomModel); + + var inner$k = makeInner(); + function setViewInfoToCoordSysRecord(api, dataZoomModel, getRange) { + inner$k(api).coordSysRecordMap.each(function (coordSysRecord) { + var dzInfo = coordSysRecord.dataZoomInfoMap.get(dataZoomModel.uid); + if (dzInfo) { + dzInfo.getRange = getRange; + } + }); + } + function disposeCoordSysRecordIfNeeded(api, dataZoomModel) { + var coordSysRecordMap = inner$k(api).coordSysRecordMap; + var coordSysKeyArr = coordSysRecordMap.keys(); + for (var i = 0; i < coordSysKeyArr.length; i++) { + var coordSysKey = coordSysKeyArr[i]; + var coordSysRecord = coordSysRecordMap.get(coordSysKey); + var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap; + if (dataZoomInfoMap) { + var dzUid = dataZoomModel.uid; + var dzInfo = dataZoomInfoMap.get(dzUid); + if (dzInfo) { + dataZoomInfoMap.removeKey(dzUid); + if (!dataZoomInfoMap.keys().length) { + disposeCoordSysRecord(coordSysRecordMap, coordSysRecord); + } + } + } + } + } + function disposeCoordSysRecord(coordSysRecordMap, coordSysRecord) { + if (coordSysRecord) { + coordSysRecordMap.removeKey(coordSysRecord.model.uid); + var controller = coordSysRecord.controller; + controller && controller.dispose(); + } + } + function createCoordSysRecord(api, coordSysModel) { + // These init props will never change after record created. + var coordSysRecord = { + model: coordSysModel, + containsPoint: curry(containsPoint, coordSysModel), + dispatchAction: curry(dispatchAction$1, api), + dataZoomInfoMap: null, + controller: null + }; + // Must not do anything depends on coordSysRecord outside the event handler here, + // because coordSysRecord not completed yet. + var controller = coordSysRecord.controller = new RoamController(api.getZr()); + each(['pan', 'zoom', 'scrollMove'], function (eventName) { + controller.on(eventName, function (event) { + var batch = []; + coordSysRecord.dataZoomInfoMap.each(function (dzInfo) { + // Check whether the behaviors (zoomOnMouseWheel, moveOnMouseMove, + // moveOnMouseWheel, ...) enabled. + if (!event.isAvailableBehavior(dzInfo.model.option)) { + return; + } + var method = (dzInfo.getRange || {})[eventName]; + var range = method && method(dzInfo.dzReferCoordSysInfo, coordSysRecord.model.mainType, coordSysRecord.controller, event); + !dzInfo.model.get('disabled', true) && range && batch.push({ + dataZoomId: dzInfo.model.id, + start: range[0], + end: range[1] + }); + }); + batch.length && coordSysRecord.dispatchAction(batch); + }); + }); + return coordSysRecord; + } + /** + * This action will be throttled. + */ + function dispatchAction$1(api, batch) { + if (!api.isDisposed()) { + api.dispatchAction({ + type: 'dataZoom', + animation: { + easing: 'cubicOut', + duration: 100 + }, + batch: batch + }); + } + } + function containsPoint(coordSysModel, e, x, y) { + return coordSysModel.coordinateSystem.containPoint([x, y]); + } + /** + * Merge roamController settings when multiple dataZooms share one roamController. + */ + function mergeControllerParams(dataZoomInfoMap) { + var controlType; + // DO NOT use reserved word (true, false, undefined) as key literally. Even if encapsulated + // as string, it is probably revert to reserved word by compress tool. See #7411. + var prefix = 'type_'; + var typePriority = { + 'type_true': 2, + 'type_move': 1, + 'type_false': 0, + 'type_undefined': -1 + }; + var preventDefaultMouseMove = true; + dataZoomInfoMap.each(function (dataZoomInfo) { + var dataZoomModel = dataZoomInfo.model; + var oneType = dataZoomModel.get('disabled', true) ? false : dataZoomModel.get('zoomLock', true) ? 'move' : true; + if (typePriority[prefix + oneType] > typePriority[prefix + controlType]) { + controlType = oneType; + } + // Prevent default move event by default. If one false, do not prevent. Otherwise + // users may be confused why it does not work when multiple insideZooms exist. + preventDefaultMouseMove = preventDefaultMouseMove && dataZoomModel.get('preventDefaultMouseMove', true); + }); + return { + controlType: controlType, + opt: { + // RoamController will enable all of these functionalities, + // and the final behavior is determined by its event listener + // provided by each inside zoom. + zoomOnMouseWheel: true, + moveOnMouseMove: true, + moveOnMouseWheel: true, + preventDefaultMouseMove: !!preventDefaultMouseMove + } + }; + } + function installDataZoomRoamProcessor(registers) { + registers.registerProcessor(registers.PRIORITY.PROCESSOR.FILTER, function (ecModel, api) { + var apiInner = inner$k(api); + var coordSysRecordMap = apiInner.coordSysRecordMap || (apiInner.coordSysRecordMap = createHashMap()); + coordSysRecordMap.each(function (coordSysRecord) { + // `coordSysRecordMap` always exists (because it holds the `roam controller`, which should + // better not re-create each time), but clear `dataZoomInfoMap` each round of the workflow. + coordSysRecord.dataZoomInfoMap = null; + }); + ecModel.eachComponent({ + mainType: 'dataZoom', + subType: 'inside' + }, function (dataZoomModel) { + var dzReferCoordSysWrap = collectReferCoordSysModelInfo(dataZoomModel); + each(dzReferCoordSysWrap.infoList, function (dzCoordSysInfo) { + var coordSysUid = dzCoordSysInfo.model.uid; + var coordSysRecord = coordSysRecordMap.get(coordSysUid) || coordSysRecordMap.set(coordSysUid, createCoordSysRecord(api, dzCoordSysInfo.model)); + var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap || (coordSysRecord.dataZoomInfoMap = createHashMap()); + // Notice these props might be changed each time for a single dataZoomModel. + dataZoomInfoMap.set(dataZoomModel.uid, { + dzReferCoordSysInfo: dzCoordSysInfo, + model: dataZoomModel, + getRange: null + }); + }); + }); + // (1) Merge dataZoom settings for each coord sys and set to the roam controller. + // (2) Clear coord sys if not refered by any dataZoom. + coordSysRecordMap.each(function (coordSysRecord) { + var controller = coordSysRecord.controller; + var firstDzInfo; + var dataZoomInfoMap = coordSysRecord.dataZoomInfoMap; + if (dataZoomInfoMap) { + var firstDzKey = dataZoomInfoMap.keys()[0]; + if (firstDzKey != null) { + firstDzInfo = dataZoomInfoMap.get(firstDzKey); + } + } + if (!firstDzInfo) { + disposeCoordSysRecord(coordSysRecordMap, coordSysRecord); + return; + } + var controllerParams = mergeControllerParams(dataZoomInfoMap); + controller.enable(controllerParams.controlType, controllerParams.opt); + controller.setPointerChecker(coordSysRecord.containsPoint); + createOrUpdate(coordSysRecord, 'dispatchAction', firstDzInfo.model.get('throttle', true), 'fixRate'); + }); + }); + } + + var InsideZoomView = /** @class */function (_super) { + __extends(InsideZoomView, _super); + function InsideZoomView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'dataZoom.inside'; + return _this; + } + InsideZoomView.prototype.render = function (dataZoomModel, ecModel, api) { + _super.prototype.render.apply(this, arguments); + if (dataZoomModel.noTarget()) { + this._clear(); + return; + } + // Hence the `throttle` util ensures to preserve command order, + // here simply updating range all the time will not cause missing + // any of the the roam change. + this.range = dataZoomModel.getPercentRange(); + // Reset controllers. + setViewInfoToCoordSysRecord(api, dataZoomModel, { + pan: bind(getRangeHandlers.pan, this), + zoom: bind(getRangeHandlers.zoom, this), + scrollMove: bind(getRangeHandlers.scrollMove, this) + }); + }; + InsideZoomView.prototype.dispose = function () { + this._clear(); + _super.prototype.dispose.apply(this, arguments); + }; + InsideZoomView.prototype._clear = function () { + disposeCoordSysRecordIfNeeded(this.api, this.dataZoomModel); + this.range = null; + }; + InsideZoomView.type = 'dataZoom.inside'; + return InsideZoomView; + }(DataZoomView); + var getRangeHandlers = { + zoom: function (coordSysInfo, coordSysMainType, controller, e) { + var lastRange = this.range; + var range = lastRange.slice(); + // Calculate transform by the first axis. + var axisModel = coordSysInfo.axisModels[0]; + if (!axisModel) { + return; + } + var directionInfo = getDirectionInfo[coordSysMainType](null, [e.originX, e.originY], axisModel, controller, coordSysInfo); + var percentPoint = (directionInfo.signal > 0 ? directionInfo.pixelStart + directionInfo.pixelLength - directionInfo.pixel : directionInfo.pixel - directionInfo.pixelStart) / directionInfo.pixelLength * (range[1] - range[0]) + range[0]; + var scale = Math.max(1 / e.scale, 0); + range[0] = (range[0] - percentPoint) * scale + percentPoint; + range[1] = (range[1] - percentPoint) * scale + percentPoint; + // Restrict range. + var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); + this.range = range; + if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { + return range; + } + }, + pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) { + var directionInfo = getDirectionInfo[coordSysMainType]([e.oldX, e.oldY], [e.newX, e.newY], axisModel, controller, coordSysInfo); + return directionInfo.signal * (range[1] - range[0]) * directionInfo.pixel / directionInfo.pixelLength; + }), + scrollMove: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) { + var directionInfo = getDirectionInfo[coordSysMainType]([0, 0], [e.scrollDelta, e.scrollDelta], axisModel, controller, coordSysInfo); + return directionInfo.signal * (range[1] - range[0]) * e.scrollDelta; + }) + }; + function makeMover(getPercentDelta) { + return function (coordSysInfo, coordSysMainType, controller, e) { + var lastRange = this.range; + var range = lastRange.slice(); + // Calculate transform by the first axis. + var axisModel = coordSysInfo.axisModels[0]; + if (!axisModel) { + return; + } + var percentDelta = getPercentDelta(range, axisModel, coordSysInfo, coordSysMainType, controller, e); + sliderMove(percentDelta, range, [0, 100], 'all'); + this.range = range; + if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { + return range; + } + }; + } + var getDirectionInfo = { + grid: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) { + var axis = axisModel.axis; + var ret = {}; + var rect = coordSysInfo.model.coordinateSystem.getRect(); + oldPoint = oldPoint || [0, 0]; + if (axis.dim === 'x') { + ret.pixel = newPoint[0] - oldPoint[0]; + ret.pixelLength = rect.width; + ret.pixelStart = rect.x; + ret.signal = axis.inverse ? 1 : -1; + } else { + // axis.dim === 'y' + ret.pixel = newPoint[1] - oldPoint[1]; + ret.pixelLength = rect.height; + ret.pixelStart = rect.y; + ret.signal = axis.inverse ? -1 : 1; + } + return ret; + }, + polar: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) { + var axis = axisModel.axis; + var ret = {}; + var polar = coordSysInfo.model.coordinateSystem; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var angleExtent = polar.getAngleAxis().getExtent(); + oldPoint = oldPoint ? polar.pointToCoord(oldPoint) : [0, 0]; + newPoint = polar.pointToCoord(newPoint); + if (axisModel.mainType === 'radiusAxis') { + ret.pixel = newPoint[0] - oldPoint[0]; + // ret.pixelLength = Math.abs(radiusExtent[1] - radiusExtent[0]); + // ret.pixelStart = Math.min(radiusExtent[0], radiusExtent[1]); + ret.pixelLength = radiusExtent[1] - radiusExtent[0]; + ret.pixelStart = radiusExtent[0]; + ret.signal = axis.inverse ? 1 : -1; + } else { + // 'angleAxis' + ret.pixel = newPoint[1] - oldPoint[1]; + // ret.pixelLength = Math.abs(angleExtent[1] - angleExtent[0]); + // ret.pixelStart = Math.min(angleExtent[0], angleExtent[1]); + ret.pixelLength = angleExtent[1] - angleExtent[0]; + ret.pixelStart = angleExtent[0]; + ret.signal = axis.inverse ? -1 : 1; + } + return ret; + }, + singleAxis: function (oldPoint, newPoint, axisModel, controller, coordSysInfo) { + var axis = axisModel.axis; + var rect = coordSysInfo.model.coordinateSystem.getRect(); + var ret = {}; + oldPoint = oldPoint || [0, 0]; + if (axis.orient === 'horizontal') { + ret.pixel = newPoint[0] - oldPoint[0]; + ret.pixelLength = rect.width; + ret.pixelStart = rect.x; + ret.signal = axis.inverse ? 1 : -1; + } else { + // 'vertical' + ret.pixel = newPoint[1] - oldPoint[1]; + ret.pixelLength = rect.height; + ret.pixelStart = rect.y; + ret.signal = axis.inverse ? -1 : 1; + } + return ret; + } + }; + + function install$K(registers) { + installCommon(registers); + registers.registerComponentModel(InsideZoomModel); + registers.registerComponentView(InsideZoomView); + installDataZoomRoamProcessor(registers); + } + + var SliderZoomModel = /** @class */function (_super) { + __extends(SliderZoomModel, _super); + function SliderZoomModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SliderZoomModel.type; + return _this; + } + SliderZoomModel.type = 'dataZoom.slider'; + SliderZoomModel.layoutMode = 'box'; + SliderZoomModel.defaultOption = inheritDefaultOption(DataZoomModel.defaultOption, { + show: true, + // deault value can only be drived in view stage. + right: 'ph', + top: 'ph', + width: 'ph', + height: 'ph', + left: null, + bottom: null, + borderColor: '#d2dbee', + borderRadius: 3, + backgroundColor: 'rgba(47,69,84,0)', + // dataBackgroundColor: '#ddd', + dataBackground: { + lineStyle: { + color: '#d2dbee', + width: 0.5 + }, + areaStyle: { + color: '#d2dbee', + opacity: 0.2 + } + }, + selectedDataBackground: { + lineStyle: { + color: '#8fb0f7', + width: 0.5 + }, + areaStyle: { + color: '#8fb0f7', + opacity: 0.2 + } + }, + // Color of selected window. + fillerColor: 'rgba(135,175,274,0.2)', + handleIcon: 'path://M-9.35,34.56V42m0-40V9.5m-2,0h4a2,2,0,0,1,2,2v21a2,2,0,0,1-2,2h-4a2,2,0,0,1-2-2v-21A2,2,0,0,1-11.35,9.5Z', + // Percent of the slider height + handleSize: '100%', + handleStyle: { + color: '#fff', + borderColor: '#ACB8D1' + }, + moveHandleSize: 7, + moveHandleIcon: 'path://M-320.9-50L-320.9-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-348-41-339-50-320.9-50z M-212.3-50L-212.3-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-239.4-41-230.4-50-212.3-50z M-103.7-50L-103.7-50c18.1,0,27.1,9,27.1,27.1V85.7c0,18.1-9,27.1-27.1,27.1l0,0c-18.1,0-27.1-9-27.1-27.1V-22.9C-130.9-41-121.8-50-103.7-50z', + moveHandleStyle: { + color: '#D2DBEE', + opacity: 0.7 + }, + showDetail: true, + showDataShadow: 'auto', + realtime: true, + zoomLock: false, + textStyle: { + color: '#6E7079' + }, + brushSelect: true, + brushStyle: { + color: 'rgba(135,175,274,0.15)' + }, + emphasis: { + handleStyle: { + borderColor: '#8FB0F7' + }, + moveHandleStyle: { + color: '#8FB0F7' + } + } + }); + return SliderZoomModel; + }(DataZoomModel); + + var Rect$2 = Rect; + // Constants + var DEFAULT_LOCATION_EDGE_GAP = 7; + var DEFAULT_FRAME_BORDER_WIDTH = 1; + var DEFAULT_FILLER_SIZE = 30; + var DEFAULT_MOVE_HANDLE_SIZE = 7; + var HORIZONTAL = 'horizontal'; + var VERTICAL = 'vertical'; + var LABEL_GAP = 5; + var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; + var REALTIME_ANIMATION_CONFIG = { + easing: 'cubicOut', + duration: 100, + delay: 0 + }; + var SliderZoomView = /** @class */function (_super) { + __extends(SliderZoomView, _super); + function SliderZoomView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = SliderZoomView.type; + _this._displayables = {}; + return _this; + } + SliderZoomView.prototype.init = function (ecModel, api) { + this.api = api; + // A unique handler for each dataZoom component + this._onBrush = bind(this._onBrush, this); + this._onBrushEnd = bind(this._onBrushEnd, this); + }; + SliderZoomView.prototype.render = function (dataZoomModel, ecModel, api, payload) { + _super.prototype.render.apply(this, arguments); + createOrUpdate(this, '_dispatchZoomAction', dataZoomModel.get('throttle'), 'fixRate'); + this._orient = dataZoomModel.getOrient(); + if (dataZoomModel.get('show') === false) { + this.group.removeAll(); + return; + } + if (dataZoomModel.noTarget()) { + this._clear(); + this.group.removeAll(); + return; + } + // Notice: this._resetInterval() should not be executed when payload.type + // is 'dataZoom', origin this._range should be maintained, otherwise 'pan' + // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction, + if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) { + this._buildView(); + } + this._updateView(); + }; + SliderZoomView.prototype.dispose = function () { + this._clear(); + _super.prototype.dispose.apply(this, arguments); + }; + SliderZoomView.prototype._clear = function () { + clear(this, '_dispatchZoomAction'); + var zr = this.api.getZr(); + zr.off('mousemove', this._onBrush); + zr.off('mouseup', this._onBrushEnd); + }; + SliderZoomView.prototype._buildView = function () { + var thisGroup = this.group; + thisGroup.removeAll(); + this._brushing = false; + this._displayables.brushRect = null; + this._resetLocation(); + this._resetInterval(); + var barGroup = this._displayables.sliderGroup = new Group(); + this._renderBackground(); + this._renderHandle(); + this._renderDataShadow(); + thisGroup.add(barGroup); + this._positionGroup(); + }; + SliderZoomView.prototype._resetLocation = function () { + var dataZoomModel = this.dataZoomModel; + var api = this.api; + var showMoveHandle = dataZoomModel.get('brushSelect'); + var moveHandleSize = showMoveHandle ? DEFAULT_MOVE_HANDLE_SIZE : 0; + // If some of x/y/width/height are not specified, + // auto-adapt according to target grid. + var coordRect = this._findCoordRect(); + var ecSize = { + width: api.getWidth(), + height: api.getHeight() + }; + // Default align by coordinate system rect. + var positionInfo = this._orient === HORIZONTAL ? { + // Why using 'right', because right should be used in vertical, + // and it is better to be consistent for dealing with position param merge. + right: ecSize.width - coordRect.x - coordRect.width, + top: ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP - moveHandleSize, + width: coordRect.width, + height: DEFAULT_FILLER_SIZE + } : { + right: DEFAULT_LOCATION_EDGE_GAP, + top: coordRect.y, + width: DEFAULT_FILLER_SIZE, + height: coordRect.height + }; + // Do not write back to option and replace value 'ph', because + // the 'ph' value should be recalculated when resize. + var layoutParams = getLayoutParams(dataZoomModel.option); + // Replace the placeholder value. + each(['right', 'top', 'width', 'height'], function (name) { + if (layoutParams[name] === 'ph') { + layoutParams[name] = positionInfo[name]; + } + }); + var layoutRect = getLayoutRect(layoutParams, ecSize); + this._location = { + x: layoutRect.x, + y: layoutRect.y + }; + this._size = [layoutRect.width, layoutRect.height]; + this._orient === VERTICAL && this._size.reverse(); + }; + SliderZoomView.prototype._positionGroup = function () { + var thisGroup = this.group; + var location = this._location; + var orient = this._orient; + // Just use the first axis to determine mapping. + var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel(); + var inverse = targetAxisModel && targetAxisModel.get('inverse'); + var sliderGroup = this._displayables.sliderGroup; + var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; + // Transform barGroup. + sliderGroup.attr(orient === HORIZONTAL && !inverse ? { + scaleY: otherAxisInverse ? 1 : -1, + scaleX: 1 + } : orient === HORIZONTAL && inverse ? { + scaleY: otherAxisInverse ? 1 : -1, + scaleX: -1 + } : orient === VERTICAL && !inverse ? { + scaleY: otherAxisInverse ? -1 : 1, + scaleX: 1, + rotation: Math.PI / 2 + } + // Don't use Math.PI, considering shadow direction. + : { + scaleY: otherAxisInverse ? -1 : 1, + scaleX: -1, + rotation: Math.PI / 2 + }); + // Position barGroup + var rect = thisGroup.getBoundingRect([sliderGroup]); + thisGroup.x = location.x - rect.x; + thisGroup.y = location.y - rect.y; + thisGroup.markRedraw(); + }; + SliderZoomView.prototype._getViewExtent = function () { + return [0, this._size[0]]; + }; + SliderZoomView.prototype._renderBackground = function () { + var dataZoomModel = this.dataZoomModel; + var size = this._size; + var barGroup = this._displayables.sliderGroup; + var brushSelect = dataZoomModel.get('brushSelect'); + barGroup.add(new Rect$2({ + silent: true, + shape: { + x: 0, + y: 0, + width: size[0], + height: size[1] + }, + style: { + fill: dataZoomModel.get('backgroundColor') + }, + z2: -40 + })); + // Click panel, over shadow, below handles. + var clickPanel = new Rect$2({ + shape: { + x: 0, + y: 0, + width: size[0], + height: size[1] + }, + style: { + fill: 'transparent' + }, + z2: 0, + onclick: bind(this._onClickPanel, this) + }); + var zr = this.api.getZr(); + if (brushSelect) { + clickPanel.on('mousedown', this._onBrushStart, this); + clickPanel.cursor = 'crosshair'; + zr.on('mousemove', this._onBrush); + zr.on('mouseup', this._onBrushEnd); + } else { + zr.off('mousemove', this._onBrush); + zr.off('mouseup', this._onBrushEnd); + } + barGroup.add(clickPanel); + }; + SliderZoomView.prototype._renderDataShadow = function () { + var info = this._dataShadowInfo = this._prepareDataShadowInfo(); + this._displayables.dataShadowSegs = []; + if (!info) { + return; + } + var size = this._size; + var oldSize = this._shadowSize || []; + var seriesModel = info.series; + var data = seriesModel.getRawData(); + var candlestickDim = seriesModel.getShadowDim && seriesModel.getShadowDim(); + var otherDim = candlestickDim && data.getDimensionInfo(candlestickDim) ? seriesModel.getShadowDim() // @see candlestick + : info.otherDim; + if (otherDim == null) { + return; + } + var polygonPts = this._shadowPolygonPts; + var polylinePts = this._shadowPolylinePts; + // Not re-render if data doesn't change. + if (data !== this._shadowData || otherDim !== this._shadowDim || size[0] !== oldSize[0] || size[1] !== oldSize[1]) { + var otherDataExtent_1 = data.getDataExtent(otherDim); + // Nice extent. + var otherOffset = (otherDataExtent_1[1] - otherDataExtent_1[0]) * 0.3; + otherDataExtent_1 = [otherDataExtent_1[0] - otherOffset, otherDataExtent_1[1] + otherOffset]; + var otherShadowExtent_1 = [0, size[1]]; + var thisShadowExtent = [0, size[0]]; + var areaPoints_1 = [[size[0], 0], [0, 0]]; + var linePoints_1 = []; + var step_1 = thisShadowExtent[1] / (data.count() - 1); + var thisCoord_1 = 0; + // Optimize for large data shadow + var stride_1 = Math.round(data.count() / size[0]); + var lastIsEmpty_1; + data.each([otherDim], function (value, index) { + if (stride_1 > 0 && index % stride_1) { + thisCoord_1 += step_1; + return; + } + // FIXME + // Should consider axis.min/axis.max when drawing dataShadow. + // FIXME + // 应该使用统一的空判断?还是在list里进行空判断? + var isEmpty = value == null || isNaN(value) || value === ''; + // See #4235. + var otherCoord = isEmpty ? 0 : linearMap(value, otherDataExtent_1, otherShadowExtent_1, true); + // Attempt to draw data shadow precisely when there are empty value. + if (isEmpty && !lastIsEmpty_1 && index) { + areaPoints_1.push([areaPoints_1[areaPoints_1.length - 1][0], 0]); + linePoints_1.push([linePoints_1[linePoints_1.length - 1][0], 0]); + } else if (!isEmpty && lastIsEmpty_1) { + areaPoints_1.push([thisCoord_1, 0]); + linePoints_1.push([thisCoord_1, 0]); + } + areaPoints_1.push([thisCoord_1, otherCoord]); + linePoints_1.push([thisCoord_1, otherCoord]); + thisCoord_1 += step_1; + lastIsEmpty_1 = isEmpty; + }); + polygonPts = this._shadowPolygonPts = areaPoints_1; + polylinePts = this._shadowPolylinePts = linePoints_1; + } + this._shadowData = data; + this._shadowDim = otherDim; + this._shadowSize = [size[0], size[1]]; + var dataZoomModel = this.dataZoomModel; + function createDataShadowGroup(isSelectedArea) { + var model = dataZoomModel.getModel(isSelectedArea ? 'selectedDataBackground' : 'dataBackground'); + var group = new Group(); + var polygon = new Polygon({ + shape: { + points: polygonPts + }, + segmentIgnoreThreshold: 1, + style: model.getModel('areaStyle').getAreaStyle(), + silent: true, + z2: -20 + }); + var polyline = new Polyline({ + shape: { + points: polylinePts + }, + segmentIgnoreThreshold: 1, + style: model.getModel('lineStyle').getLineStyle(), + silent: true, + z2: -19 + }); + group.add(polygon); + group.add(polyline); + return group; + } + // let dataBackgroundModel = dataZoomModel.getModel('dataBackground'); + for (var i = 0; i < 3; i++) { + var group = createDataShadowGroup(i === 1); + this._displayables.sliderGroup.add(group); + this._displayables.dataShadowSegs.push(group); + } + }; + SliderZoomView.prototype._prepareDataShadowInfo = function () { + var dataZoomModel = this.dataZoomModel; + var showDataShadow = dataZoomModel.get('showDataShadow'); + if (showDataShadow === false) { + return; + } + // Find a representative series. + var result; + var ecModel = this.ecModel; + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { + var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels(); + each(seriesModels, function (seriesModel) { + if (result) { + return; + } + if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { + return; + } + var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; + var otherDim = getOtherDim(axisDim); + var otherAxisInverse; + var coordSys = seriesModel.coordinateSystem; + if (otherDim != null && coordSys.getOtherAxis) { + otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; + } + otherDim = seriesModel.getData().mapDimension(otherDim); + result = { + thisAxis: thisAxis, + series: seriesModel, + thisDim: axisDim, + otherDim: otherDim, + otherAxisInverse: otherAxisInverse + }; + }, this); + }, this); + return result; + }; + SliderZoomView.prototype._renderHandle = function () { + var thisGroup = this.group; + var displayables = this._displayables; + var handles = displayables.handles = [null, null]; + var handleLabels = displayables.handleLabels = [null, null]; + var sliderGroup = this._displayables.sliderGroup; + var size = this._size; + var dataZoomModel = this.dataZoomModel; + var api = this.api; + var borderRadius = dataZoomModel.get('borderRadius') || 0; + var brushSelect = dataZoomModel.get('brushSelect'); + var filler = displayables.filler = new Rect$2({ + silent: brushSelect, + style: { + fill: dataZoomModel.get('fillerColor') + }, + textConfig: { + position: 'inside' + } + }); + sliderGroup.add(filler); + // Frame border. + sliderGroup.add(new Rect$2({ + silent: true, + subPixelOptimize: true, + shape: { + x: 0, + y: 0, + width: size[0], + height: size[1], + r: borderRadius + }, + style: { + // deprecated option + stroke: dataZoomModel.get('dataBackgroundColor') || dataZoomModel.get('borderColor'), + lineWidth: DEFAULT_FRAME_BORDER_WIDTH, + fill: 'rgba(0,0,0,0)' + } + })); + // Left and right handle to resize + each([0, 1], function (handleIndex) { + var iconStr = dataZoomModel.get('handleIcon'); + if (!symbolBuildProxies[iconStr] && iconStr.indexOf('path://') < 0 && iconStr.indexOf('image://') < 0) { + // Compatitable with the old icon parsers. Which can use a path string without path:// + iconStr = 'path://' + iconStr; + if ("development" !== 'production') { + deprecateLog('handleIcon now needs \'path://\' prefix when using a path string'); + } + } + var path = createSymbol(iconStr, -1, 0, 2, 2, null, true); + path.attr({ + cursor: getCursor(this._orient), + draggable: true, + drift: bind(this._onDragMove, this, handleIndex), + ondragend: bind(this._onDragEnd, this), + onmouseover: bind(this._showDataInfo, this, true), + onmouseout: bind(this._showDataInfo, this, false), + z2: 5 + }); + var bRect = path.getBoundingRect(); + var handleSize = dataZoomModel.get('handleSize'); + this._handleHeight = parsePercent$1(handleSize, this._size[1]); + this._handleWidth = bRect.width / bRect.height * this._handleHeight; + path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle()); + path.style.strokeNoScale = true; + path.rectHover = true; + path.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'handleStyle']).getItemStyle(); + enableHoverEmphasis(path); + var handleColor = dataZoomModel.get('handleColor'); // deprecated option + // Compatitable with previous version + if (handleColor != null) { + path.style.fill = handleColor; + } + sliderGroup.add(handles[handleIndex] = path); + var textStyleModel = dataZoomModel.getModel('textStyle'); + thisGroup.add(handleLabels[handleIndex] = new ZRText({ + silent: true, + invisible: true, + style: createTextStyle(textStyleModel, { + x: 0, + y: 0, + text: '', + verticalAlign: 'middle', + align: 'center', + fill: textStyleModel.getTextColor(), + font: textStyleModel.getFont() + }), + z2: 10 + })); + }, this); + // Handle to move. Only visible when brushSelect is set true. + var actualMoveZone = filler; + if (brushSelect) { + var moveHandleHeight = parsePercent$1(dataZoomModel.get('moveHandleSize'), size[1]); + var moveHandle_1 = displayables.moveHandle = new Rect({ + style: dataZoomModel.getModel('moveHandleStyle').getItemStyle(), + silent: true, + shape: { + r: [0, 0, 2, 2], + y: size[1] - 0.5, + height: moveHandleHeight + } + }); + var iconSize = moveHandleHeight * 0.8; + var moveHandleIcon = displayables.moveHandleIcon = createSymbol(dataZoomModel.get('moveHandleIcon'), -iconSize / 2, -iconSize / 2, iconSize, iconSize, '#fff', true); + moveHandleIcon.silent = true; + moveHandleIcon.y = size[1] + moveHandleHeight / 2 - 0.5; + moveHandle_1.ensureState('emphasis').style = dataZoomModel.getModel(['emphasis', 'moveHandleStyle']).getItemStyle(); + var moveZoneExpandSize = Math.min(size[1] / 2, Math.max(moveHandleHeight, 10)); + actualMoveZone = displayables.moveZone = new Rect({ + invisible: true, + shape: { + y: size[1] - moveZoneExpandSize, + height: moveHandleHeight + moveZoneExpandSize + } + }); + actualMoveZone.on('mouseover', function () { + api.enterEmphasis(moveHandle_1); + }).on('mouseout', function () { + api.leaveEmphasis(moveHandle_1); + }); + sliderGroup.add(moveHandle_1); + sliderGroup.add(moveHandleIcon); + sliderGroup.add(actualMoveZone); + } + actualMoveZone.attr({ + draggable: true, + cursor: getCursor(this._orient), + drift: bind(this._onDragMove, this, 'all'), + ondragstart: bind(this._showDataInfo, this, true), + ondragend: bind(this._onDragEnd, this), + onmouseover: bind(this._showDataInfo, this, true), + onmouseout: bind(this._showDataInfo, this, false) + }); + }; + SliderZoomView.prototype._resetInterval = function () { + var range = this._range = this.dataZoomModel.getPercentRange(); + var viewExtent = this._getViewExtent(); + this._handleEnds = [linearMap(range[0], [0, 100], viewExtent, true), linearMap(range[1], [0, 100], viewExtent, true)]; + }; + SliderZoomView.prototype._updateInterval = function (handleIndex, delta) { + var dataZoomModel = this.dataZoomModel; + var handleEnds = this._handleEnds; + var viewExtend = this._getViewExtent(); + var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + var percentExtent = [0, 100]; + sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); + var lastRange = this._range; + var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); + return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; + }; + SliderZoomView.prototype._updateView = function (nonRealtime) { + var displaybles = this._displayables; + var handleEnds = this._handleEnds; + var handleInterval = asc(handleEnds.slice()); + var size = this._size; + each([0, 1], function (handleIndex) { + // Handles + var handle = displaybles.handles[handleIndex]; + var handleHeight = this._handleHeight; + handle.attr({ + scaleX: handleHeight / 2, + scaleY: handleHeight / 2, + // This is a trick, by adding an extra tiny offset to let the default handle's end point align to the drag window. + // NOTE: It may affect some custom shapes a bit. But we prefer to have better result by default. + x: handleEnds[handleIndex] + (handleIndex ? -1 : 1), + y: size[1] / 2 - handleHeight / 2 + }); + }, this); + // Filler + displaybles.filler.setShape({ + x: handleInterval[0], + y: 0, + width: handleInterval[1] - handleInterval[0], + height: size[1] + }); + var viewExtent = { + x: handleInterval[0], + width: handleInterval[1] - handleInterval[0] + }; + // Move handle + if (displaybles.moveHandle) { + displaybles.moveHandle.setShape(viewExtent); + displaybles.moveZone.setShape(viewExtent); + // Force update path on the invisible object + displaybles.moveZone.getBoundingRect(); + displaybles.moveHandleIcon && displaybles.moveHandleIcon.attr('x', viewExtent.x + viewExtent.width / 2); + } + // update clip path of shadow. + var dataShadowSegs = displaybles.dataShadowSegs; + var segIntervals = [0, handleInterval[0], handleInterval[1], size[0]]; + for (var i = 0; i < dataShadowSegs.length; i++) { + var segGroup = dataShadowSegs[i]; + var clipPath = segGroup.getClipPath(); + if (!clipPath) { + clipPath = new Rect(); + segGroup.setClipPath(clipPath); + } + clipPath.setShape({ + x: segIntervals[i], + y: 0, + width: segIntervals[i + 1] - segIntervals[i], + height: size[1] + }); + } + this._updateDataInfo(nonRealtime); + }; + SliderZoomView.prototype._updateDataInfo = function (nonRealtime) { + var dataZoomModel = this.dataZoomModel; + var displaybles = this._displayables; + var handleLabels = displaybles.handleLabels; + var orient = this._orient; + var labelTexts = ['', '']; + // FIXME + // date型,支持formatter,autoformatter(ec2 date.getAutoFormatter) + if (dataZoomModel.get('showDetail')) { + var axisProxy = dataZoomModel.findRepresentativeAxisProxy(); + if (axisProxy) { + var axis = axisProxy.getAxisModel().axis; + var range = this._range; + var dataInterval = nonRealtime + // See #4434, data and axis are not processed and reset yet in non-realtime mode. + ? axisProxy.calculateDataWindow({ + start: range[0], + end: range[1] + }).valueWindow : axisProxy.getDataValueWindow(); + labelTexts = [this._formatLabel(dataInterval[0], axis), this._formatLabel(dataInterval[1], axis)]; + } + } + var orderedHandleEnds = asc(this._handleEnds.slice()); + setLabel.call(this, 0); + setLabel.call(this, 1); + function setLabel(handleIndex) { + // Label + // Text should not transform by barGroup. + // Ignore handlers transform + var barTransform = getTransform(displaybles.handles[handleIndex].parent, this.group); + var direction = transformDirection(handleIndex === 0 ? 'right' : 'left', barTransform); + var offset = this._handleWidth / 2 + LABEL_GAP; + var textPoint = applyTransform$1([orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), this._size[1] / 2], barTransform); + handleLabels[handleIndex].setStyle({ + x: textPoint[0], + y: textPoint[1], + verticalAlign: orient === HORIZONTAL ? 'middle' : direction, + align: orient === HORIZONTAL ? direction : 'center', + text: labelTexts[handleIndex] + }); + } + }; + SliderZoomView.prototype._formatLabel = function (value, axis) { + var dataZoomModel = this.dataZoomModel; + var labelFormatter = dataZoomModel.get('labelFormatter'); + var labelPrecision = dataZoomModel.get('labelPrecision'); + if (labelPrecision == null || labelPrecision === 'auto') { + labelPrecision = axis.getPixelPrecision(); + } + var valueStr = value == null || isNaN(value) ? '' + // FIXME Glue code + : axis.type === 'category' || axis.type === 'time' ? axis.scale.getLabel({ + value: Math.round(value) + }) + // param of toFixed should less then 20. + : value.toFixed(Math.min(labelPrecision, 20)); + return isFunction(labelFormatter) ? labelFormatter(value, valueStr) : isString(labelFormatter) ? labelFormatter.replace('{value}', valueStr) : valueStr; + }; + /** + * @param showOrHide true: show, false: hide + */ + SliderZoomView.prototype._showDataInfo = function (showOrHide) { + // Always show when drgging. + showOrHide = this._dragging || showOrHide; + var displayables = this._displayables; + var handleLabels = displayables.handleLabels; + handleLabels[0].attr('invisible', !showOrHide); + handleLabels[1].attr('invisible', !showOrHide); + // Highlight move handle + displayables.moveHandle && this.api[showOrHide ? 'enterEmphasis' : 'leaveEmphasis'](displayables.moveHandle, 1); + }; + SliderZoomView.prototype._onDragMove = function (handleIndex, dx, dy, event) { + this._dragging = true; + // For mobile device, prevent screen slider on the button. + stop(event.event); + // Transform dx, dy to bar coordination. + var barTransform = this._displayables.sliderGroup.getLocalTransform(); + var vertex = applyTransform$1([dx, dy], barTransform, true); + var changed = this._updateInterval(handleIndex, vertex[0]); + var realtime = this.dataZoomModel.get('realtime'); + this._updateView(!realtime); + // Avoid dispatch dataZoom repeatly but range not changed, + // which cause bad visual effect when progressive enabled. + changed && realtime && this._dispatchZoomAction(true); + }; + SliderZoomView.prototype._onDragEnd = function () { + this._dragging = false; + this._showDataInfo(false); + // While in realtime mode and stream mode, dispatch action when + // drag end will cause the whole view rerender, which is unnecessary. + var realtime = this.dataZoomModel.get('realtime'); + !realtime && this._dispatchZoomAction(false); + }; + SliderZoomView.prototype._onClickPanel = function (e) { + var size = this._size; + var localPoint = this._displayables.sliderGroup.transformCoordToLocal(e.offsetX, e.offsetY); + if (localPoint[0] < 0 || localPoint[0] > size[0] || localPoint[1] < 0 || localPoint[1] > size[1]) { + return; + } + var handleEnds = this._handleEnds; + var center = (handleEnds[0] + handleEnds[1]) / 2; + var changed = this._updateInterval('all', localPoint[0] - center); + this._updateView(); + changed && this._dispatchZoomAction(false); + }; + SliderZoomView.prototype._onBrushStart = function (e) { + var x = e.offsetX; + var y = e.offsetY; + this._brushStart = new Point(x, y); + this._brushing = true; + this._brushStartTime = +new Date(); + // this._updateBrushRect(x, y); + }; + + SliderZoomView.prototype._onBrushEnd = function (e) { + if (!this._brushing) { + return; + } + var brushRect = this._displayables.brushRect; + this._brushing = false; + if (!brushRect) { + return; + } + brushRect.attr('ignore', true); + var brushShape = brushRect.shape; + var brushEndTime = +new Date(); + // console.log(brushEndTime - this._brushStartTime); + if (brushEndTime - this._brushStartTime < 200 && Math.abs(brushShape.width) < 5) { + // Will treat it as a click + return; + } + var viewExtend = this._getViewExtent(); + var percentExtent = [0, 100]; + this._range = asc([linearMap(brushShape.x, viewExtend, percentExtent, true), linearMap(brushShape.x + brushShape.width, viewExtend, percentExtent, true)]); + this._handleEnds = [brushShape.x, brushShape.x + brushShape.width]; + this._updateView(); + this._dispatchZoomAction(false); + }; + SliderZoomView.prototype._onBrush = function (e) { + if (this._brushing) { + // For mobile device, prevent screen slider on the button. + stop(e.event); + this._updateBrushRect(e.offsetX, e.offsetY); + } + }; + SliderZoomView.prototype._updateBrushRect = function (mouseX, mouseY) { + var displayables = this._displayables; + var dataZoomModel = this.dataZoomModel; + var brushRect = displayables.brushRect; + if (!brushRect) { + brushRect = displayables.brushRect = new Rect$2({ + silent: true, + style: dataZoomModel.getModel('brushStyle').getItemStyle() + }); + displayables.sliderGroup.add(brushRect); + } + brushRect.attr('ignore', false); + var brushStart = this._brushStart; + var sliderGroup = this._displayables.sliderGroup; + var endPoint = sliderGroup.transformCoordToLocal(mouseX, mouseY); + var startPoint = sliderGroup.transformCoordToLocal(brushStart.x, brushStart.y); + var size = this._size; + endPoint[0] = Math.max(Math.min(size[0], endPoint[0]), 0); + brushRect.setShape({ + x: startPoint[0], + y: 0, + width: endPoint[0] - startPoint[0], + height: size[1] + }); + }; + /** + * This action will be throttled. + */ + SliderZoomView.prototype._dispatchZoomAction = function (realtime) { + var range = this._range; + this.api.dispatchAction({ + type: 'dataZoom', + from: this.uid, + dataZoomId: this.dataZoomModel.id, + animation: realtime ? REALTIME_ANIMATION_CONFIG : null, + start: range[0], + end: range[1] + }); + }; + SliderZoomView.prototype._findCoordRect = function () { + // Find the grid corresponding to the first axis referred by dataZoom. + var rect; + var coordSysInfoList = collectReferCoordSysModelInfo(this.dataZoomModel).infoList; + if (!rect && coordSysInfoList.length) { + var coordSys = coordSysInfoList[0].model.coordinateSystem; + rect = coordSys.getRect && coordSys.getRect(); + } + if (!rect) { + var width = this.api.getWidth(); + var height = this.api.getHeight(); + rect = { + x: width * 0.2, + y: height * 0.2, + width: width * 0.6, + height: height * 0.6 + }; + } + return rect; + }; + SliderZoomView.type = 'dataZoom.slider'; + return SliderZoomView; + }(DataZoomView); + function getOtherDim(thisDim) { + // FIXME + // 这个逻辑和getOtherAxis里一致,但是写在这里是否不好 + var map = { + x: 'y', + y: 'x', + radius: 'angle', + angle: 'radius' + }; + return map[thisDim]; + } + function getCursor(orient) { + return orient === 'vertical' ? 'ns-resize' : 'ew-resize'; + } + + function install$L(registers) { + registers.registerComponentModel(SliderZoomModel); + registers.registerComponentView(SliderZoomView); + installCommon(registers); + } + + function install$M(registers) { + use(install$K); + use(install$L); + // Do not install './dataZoomSelect', + // since it only work for toolbox dataZoom. + } + + var visualDefault = { + /** + * @public + */ + get: function (visualType, key, isCategory) { + var value = clone((defaultOption$1[visualType] || {})[key]); + return isCategory ? isArray(value) ? value[value.length - 1] : value : value; + } + }; + var defaultOption$1 = { + color: { + active: ['#006edd', '#e0ffff'], + inactive: ['rgba(0,0,0,0)'] + }, + colorHue: { + active: [0, 360], + inactive: [0, 0] + }, + colorSaturation: { + active: [0.3, 1], + inactive: [0, 0] + }, + colorLightness: { + active: [0.9, 0.5], + inactive: [0, 0] + }, + colorAlpha: { + active: [0.3, 1], + inactive: [0, 0] + }, + opacity: { + active: [0.3, 1], + inactive: [0, 0] + }, + symbol: { + active: ['circle', 'roundRect', 'diamond'], + inactive: ['none'] + }, + symbolSize: { + active: [10, 50], + inactive: [0, 0] + } + }; + + var mapVisual$1 = VisualMapping.mapVisual; + var eachVisual = VisualMapping.eachVisual; + var isArray$1 = isArray; + var each$d = each; + var asc$2 = asc; + var linearMap$1 = linearMap; + var VisualMapModel = /** @class */function (_super) { + __extends(VisualMapModel, _super); + function VisualMapModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = VisualMapModel.type; + _this.stateList = ['inRange', 'outOfRange']; + _this.replacableOptionKeys = ['inRange', 'outOfRange', 'target', 'controller', 'color']; + _this.layoutMode = { + type: 'box', + ignoreSize: true + }; + /** + * [lowerBound, upperBound] + */ + _this.dataBound = [-Infinity, Infinity]; + _this.targetVisuals = {}; + _this.controllerVisuals = {}; + return _this; + } + VisualMapModel.prototype.init = function (option, parentModel, ecModel) { + this.mergeDefaultAndTheme(option, ecModel); + }; + /** + * @protected + */ + VisualMapModel.prototype.optionUpdated = function (newOption, isInit) { + var thisOption = this.option; + !isInit && replaceVisualOption(thisOption, newOption, this.replacableOptionKeys); + this.textStyleModel = this.getModel('textStyle'); + this.resetItemSize(); + this.completeVisualOption(); + }; + /** + * @protected + */ + VisualMapModel.prototype.resetVisual = function (supplementVisualOption) { + var stateList = this.stateList; + supplementVisualOption = bind(supplementVisualOption, this); + this.controllerVisuals = createVisualMappings(this.option.controller, stateList, supplementVisualOption); + this.targetVisuals = createVisualMappings(this.option.target, stateList, supplementVisualOption); + }; + /** + * @public + */ + VisualMapModel.prototype.getItemSymbol = function () { + return null; + }; + /** + * @protected + * @return {Array.} An array of series indices. + */ + VisualMapModel.prototype.getTargetSeriesIndices = function () { + var optionSeriesIndex = this.option.seriesIndex; + var seriesIndices = []; + if (optionSeriesIndex == null || optionSeriesIndex === 'all') { + this.ecModel.eachSeries(function (seriesModel, index) { + seriesIndices.push(index); + }); + } else { + seriesIndices = normalizeToArray(optionSeriesIndex); + } + return seriesIndices; + }; + /** + * @public + */ + VisualMapModel.prototype.eachTargetSeries = function (callback, context) { + each(this.getTargetSeriesIndices(), function (seriesIndex) { + var seriesModel = this.ecModel.getSeriesByIndex(seriesIndex); + if (seriesModel) { + callback.call(context, seriesModel); + } + }, this); + }; + /** + * @pubilc + */ + VisualMapModel.prototype.isTargetSeries = function (seriesModel) { + var is = false; + this.eachTargetSeries(function (model) { + model === seriesModel && (is = true); + }); + return is; + }; + /** + * @example + * this.formatValueText(someVal); // format single numeric value to text. + * this.formatValueText(someVal, true); // format single category value to text. + * this.formatValueText([min, max]); // format numeric min-max to text. + * this.formatValueText([this.dataBound[0], max]); // using data lower bound. + * this.formatValueText([min, this.dataBound[1]]); // using data upper bound. + * + * @param value Real value, or this.dataBound[0 or 1]. + * @param isCategory Only available when value is number. + * @param edgeSymbols Open-close symbol when value is interval. + * @protected + */ + VisualMapModel.prototype.formatValueText = function (value, isCategory, edgeSymbols) { + var option = this.option; + var precision = option.precision; + var dataBound = this.dataBound; + var formatter = option.formatter; + var isMinMax; + edgeSymbols = edgeSymbols || ['<', '>']; + if (isArray(value)) { + value = value.slice(); + isMinMax = true; + } + var textValue = isCategory ? value // Value is string when isCategory + : isMinMax ? [toFixed(value[0]), toFixed(value[1])] : toFixed(value); + if (isString(formatter)) { + return formatter.replace('{value}', isMinMax ? textValue[0] : textValue).replace('{value2}', isMinMax ? textValue[1] : textValue); + } else if (isFunction(formatter)) { + return isMinMax ? formatter(value[0], value[1]) : formatter(value); + } + if (isMinMax) { + if (value[0] === dataBound[0]) { + return edgeSymbols[0] + ' ' + textValue[1]; + } else if (value[1] === dataBound[1]) { + return edgeSymbols[1] + ' ' + textValue[0]; + } else { + return textValue[0] + ' - ' + textValue[1]; + } + } else { + // Format single value (includes category case). + return textValue; + } + function toFixed(val) { + return val === dataBound[0] ? 'min' : val === dataBound[1] ? 'max' : (+val).toFixed(Math.min(precision, 20)); + } + }; + /** + * @protected + */ + VisualMapModel.prototype.resetExtent = function () { + var thisOption = this.option; + // Can not calculate data extent by data here. + // Because series and data may be modified in processing stage. + // So we do not support the feature "auto min/max". + var extent = asc$2([thisOption.min, thisOption.max]); + this._dataExtent = extent; + }; + /** + * PENDING: + * delete this method if no outer usage. + * + * Return Concrete dimension. If null/undefined is returned, no dimension is used. + */ + // getDataDimension(data: SeriesData) { + // const optDim = this.option.dimension; + // if (optDim != null) { + // return data.getDimension(optDim); + // } + // const dimNames = data.dimensions; + // for (let i = dimNames.length - 1; i >= 0; i--) { + // const dimName = dimNames[i]; + // const dimInfo = data.getDimensionInfo(dimName); + // if (!dimInfo.isCalculationCoord) { + // return dimName; + // } + // } + // } + VisualMapModel.prototype.getDataDimensionIndex = function (data) { + var optDim = this.option.dimension; + if (optDim != null) { + return data.getDimensionIndex(optDim); + } + var dimNames = data.dimensions; + for (var i = dimNames.length - 1; i >= 0; i--) { + var dimName = dimNames[i]; + var dimInfo = data.getDimensionInfo(dimName); + if (!dimInfo.isCalculationCoord) { + return dimInfo.storeDimIndex; + } + } + }; + VisualMapModel.prototype.getExtent = function () { + return this._dataExtent.slice(); + }; + VisualMapModel.prototype.completeVisualOption = function () { + var ecModel = this.ecModel; + var thisOption = this.option; + var base = { + inRange: thisOption.inRange, + outOfRange: thisOption.outOfRange + }; + var target = thisOption.target || (thisOption.target = {}); + var controller = thisOption.controller || (thisOption.controller = {}); + merge(target, base); // Do not override + merge(controller, base); // Do not override + var isCategory = this.isCategory(); + completeSingle.call(this, target); + completeSingle.call(this, controller); + completeInactive.call(this, target, 'inRange', 'outOfRange'); + // completeInactive.call(this, target, 'outOfRange', 'inRange'); + completeController.call(this, controller); + function completeSingle(base) { + // Compatible with ec2 dataRange.color. + // The mapping order of dataRange.color is: [high value, ..., low value] + // whereas inRange.color and outOfRange.color is [low value, ..., high value] + // Notice: ec2 has no inverse. + if (isArray$1(thisOption.color) + // If there has been inRange: {symbol: ...}, adding color is a mistake. + // So adding color only when no inRange defined. + && !base.inRange) { + base.inRange = { + color: thisOption.color.slice().reverse() + }; + } + // Compatible with previous logic, always give a default color, otherwise + // simple config with no inRange and outOfRange will not work. + // Originally we use visualMap.color as the default color, but setOption at + // the second time the default color will be erased. So we change to use + // constant DEFAULT_COLOR. + // If user do not want the default color, set inRange: {color: null}. + base.inRange = base.inRange || { + color: ecModel.get('gradientColor') + }; + } + function completeInactive(base, stateExist, stateAbsent) { + var optExist = base[stateExist]; + var optAbsent = base[stateAbsent]; + if (optExist && !optAbsent) { + optAbsent = base[stateAbsent] = {}; + each$d(optExist, function (visualData, visualType) { + if (!VisualMapping.isValidType(visualType)) { + return; + } + var defa = visualDefault.get(visualType, 'inactive', isCategory); + if (defa != null) { + optAbsent[visualType] = defa; + // Compatibable with ec2: + // Only inactive color to rgba(0,0,0,0) can not + // make label transparent, so use opacity also. + if (visualType === 'color' && !optAbsent.hasOwnProperty('opacity') && !optAbsent.hasOwnProperty('colorAlpha')) { + optAbsent.opacity = [0, 0]; + } + } + }); + } + } + function completeController(controller) { + var symbolExists = (controller.inRange || {}).symbol || (controller.outOfRange || {}).symbol; + var symbolSizeExists = (controller.inRange || {}).symbolSize || (controller.outOfRange || {}).symbolSize; + var inactiveColor = this.get('inactiveColor'); + var itemSymbol = this.getItemSymbol(); + var defaultSymbol = itemSymbol || 'roundRect'; + each$d(this.stateList, function (state) { + var itemSize = this.itemSize; + var visuals = controller[state]; + // Set inactive color for controller if no other color + // attr (like colorAlpha) specified. + if (!visuals) { + visuals = controller[state] = { + color: isCategory ? inactiveColor : [inactiveColor] + }; + } + // Consistent symbol and symbolSize if not specified. + if (visuals.symbol == null) { + visuals.symbol = symbolExists && clone(symbolExists) || (isCategory ? defaultSymbol : [defaultSymbol]); + } + if (visuals.symbolSize == null) { + visuals.symbolSize = symbolSizeExists && clone(symbolSizeExists) || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]); + } + // Filter none + visuals.symbol = mapVisual$1(visuals.symbol, function (symbol) { + return symbol === 'none' ? defaultSymbol : symbol; + }); + // Normalize symbolSize + var symbolSize = visuals.symbolSize; + if (symbolSize != null) { + var max_1 = -Infinity; + // symbolSize can be object when categories defined. + eachVisual(symbolSize, function (value) { + value > max_1 && (max_1 = value); + }); + visuals.symbolSize = mapVisual$1(symbolSize, function (value) { + return linearMap$1(value, [0, max_1], [0, itemSize[0]], true); + }); + } + }, this); + } + }; + VisualMapModel.prototype.resetItemSize = function () { + this.itemSize = [parseFloat(this.get('itemWidth')), parseFloat(this.get('itemHeight'))]; + }; + VisualMapModel.prototype.isCategory = function () { + return !!this.option.categories; + }; + /** + * @public + * @abstract + */ + VisualMapModel.prototype.setSelected = function (selected) {}; + VisualMapModel.prototype.getSelected = function () { + return null; + }; + /** + * @public + * @abstract + */ + VisualMapModel.prototype.getValueState = function (value) { + return null; + }; + /** + * FIXME + * Do not publish to thirt-part-dev temporarily + * util the interface is stable. (Should it return + * a function but not visual meta?) + * + * @pubilc + * @abstract + * @param getColorVisual + * params: value, valueState + * return: color + * @return {Object} visualMeta + * should includes {stops, outerColors} + * outerColor means [colorBeyondMinValue, colorBeyondMaxValue] + */ + VisualMapModel.prototype.getVisualMeta = function (getColorVisual) { + return null; + }; + VisualMapModel.type = 'visualMap'; + VisualMapModel.dependencies = ['series']; + VisualMapModel.defaultOption = { + show: true, + // zlevel: 0, + z: 4, + seriesIndex: 'all', + min: 0, + max: 200, + left: 0, + right: null, + top: null, + bottom: 0, + itemWidth: null, + itemHeight: null, + inverse: false, + orient: 'vertical', + backgroundColor: 'rgba(0,0,0,0)', + borderColor: '#ccc', + contentColor: '#5793f3', + inactiveColor: '#aaa', + borderWidth: 0, + padding: 5, + // 接受数组分别设定上右下左边距,同css + textGap: 10, + precision: 0, + textStyle: { + color: '#333' // 值域文字颜色 + } + }; + + return VisualMapModel; + }(ComponentModel); + + // Constant + var DEFAULT_BAR_BOUND = [20, 140]; + var ContinuousModel = /** @class */function (_super) { + __extends(ContinuousModel, _super); + function ContinuousModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ContinuousModel.type; + return _this; + } + /** + * @override + */ + ContinuousModel.prototype.optionUpdated = function (newOption, isInit) { + _super.prototype.optionUpdated.apply(this, arguments); + this.resetExtent(); + this.resetVisual(function (mappingOption) { + mappingOption.mappingMethod = 'linear'; + mappingOption.dataExtent = this.getExtent(); + }); + this._resetRange(); + }; + /** + * @protected + * @override + */ + ContinuousModel.prototype.resetItemSize = function () { + _super.prototype.resetItemSize.apply(this, arguments); + var itemSize = this.itemSize; + (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]); + (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]); + }; + /** + * @private + */ + ContinuousModel.prototype._resetRange = function () { + var dataExtent = this.getExtent(); + var range = this.option.range; + if (!range || range.auto) { + // `range` should always be array (so we don't use other + // value like 'auto') for user-friend. (consider getOption). + dataExtent.auto = 1; + this.option.range = dataExtent; + } else if (isArray(range)) { + if (range[0] > range[1]) { + range.reverse(); + } + range[0] = Math.max(range[0], dataExtent[0]); + range[1] = Math.min(range[1], dataExtent[1]); + } + }; + /** + * @protected + * @override + */ + ContinuousModel.prototype.completeVisualOption = function () { + _super.prototype.completeVisualOption.apply(this, arguments); + each(this.stateList, function (state) { + var symbolSize = this.option.controller[state].symbolSize; + if (symbolSize && symbolSize[0] !== symbolSize[1]) { + symbolSize[0] = symbolSize[1] / 3; // For good looking. + } + }, this); + }; + /** + * @override + */ + ContinuousModel.prototype.setSelected = function (selected) { + this.option.range = selected.slice(); + this._resetRange(); + }; + /** + * @public + */ + ContinuousModel.prototype.getSelected = function () { + var dataExtent = this.getExtent(); + var dataInterval = asc((this.get('range') || []).slice()); + // Clamp + dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]); + dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]); + dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]); + dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]); + return dataInterval; + }; + /** + * @override + */ + ContinuousModel.prototype.getValueState = function (value) { + var range = this.option.range; + var dataExtent = this.getExtent(); + // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'. + // range[1] is processed likewise. + return (range[0] <= dataExtent[0] || range[0] <= value) && (range[1] >= dataExtent[1] || value <= range[1]) ? 'inRange' : 'outOfRange'; + }; + ContinuousModel.prototype.findTargetDataIndices = function (range) { + var result = []; + this.eachTargetSeries(function (seriesModel) { + var dataIndices = []; + var data = seriesModel.getData(); + data.each(this.getDataDimensionIndex(data), function (value, dataIndex) { + range[0] <= value && value <= range[1] && dataIndices.push(dataIndex); + }, this); + result.push({ + seriesId: seriesModel.id, + dataIndex: dataIndices + }); + }, this); + return result; + }; + /** + * @implement + */ + ContinuousModel.prototype.getVisualMeta = function (getColorVisual) { + var oVals = getColorStopValues(this, 'outOfRange', this.getExtent()); + var iVals = getColorStopValues(this, 'inRange', this.option.range.slice()); + var stops = []; + function setStop(value, valueState) { + stops.push({ + value: value, + color: getColorVisual(value, valueState) + }); + } + // Format to: outOfRange -- inRange -- outOfRange. + var iIdx = 0; + var oIdx = 0; + var iLen = iVals.length; + var oLen = oVals.length; + for (; oIdx < oLen && (!iVals.length || oVals[oIdx] <= iVals[0]); oIdx++) { + // If oVal[oIdx] === iVals[iIdx], oVal[oIdx] should be ignored. + if (oVals[oIdx] < iVals[iIdx]) { + setStop(oVals[oIdx], 'outOfRange'); + } + } + for (var first = 1; iIdx < iLen; iIdx++, first = 0) { + // If range is full, value beyond min, max will be clamped. + // make a singularity + first && stops.length && setStop(iVals[iIdx], 'outOfRange'); + setStop(iVals[iIdx], 'inRange'); + } + for (var first = 1; oIdx < oLen; oIdx++) { + if (!iVals.length || iVals[iVals.length - 1] < oVals[oIdx]) { + // make a singularity + if (first) { + stops.length && setStop(stops[stops.length - 1].value, 'outOfRange'); + first = 0; + } + setStop(oVals[oIdx], 'outOfRange'); + } + } + var stopsLen = stops.length; + return { + stops: stops, + outerColors: [stopsLen ? stops[0].color : 'transparent', stopsLen ? stops[stopsLen - 1].color : 'transparent'] + }; + }; + ContinuousModel.type = 'visualMap.continuous'; + ContinuousModel.defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, { + align: 'auto', + calculable: false, + hoverLink: true, + realtime: true, + handleIcon: 'path://M-11.39,9.77h0a3.5,3.5,0,0,1-3.5,3.5h-22a3.5,3.5,0,0,1-3.5-3.5h0a3.5,3.5,0,0,1,3.5-3.5h22A3.5,3.5,0,0,1-11.39,9.77Z', + handleSize: '120%', + handleStyle: { + borderColor: '#fff', + borderWidth: 1 + }, + indicatorIcon: 'circle', + indicatorSize: '50%', + indicatorStyle: { + borderColor: '#fff', + borderWidth: 2, + shadowBlur: 2, + shadowOffsetX: 1, + shadowOffsetY: 1, + shadowColor: 'rgba(0,0,0,0.2)' + } + // emphasis: { + // handleStyle: { + // shadowBlur: 3, + // shadowOffsetX: 1, + // shadowOffsetY: 1, + // shadowColor: 'rgba(0,0,0,0.2)' + // } + // } + }); + + return ContinuousModel; + }(VisualMapModel); + function getColorStopValues(visualMapModel, valueState, dataExtent) { + if (dataExtent[0] === dataExtent[1]) { + return dataExtent.slice(); + } + // When using colorHue mapping, it is not linear color any more. + // Moreover, canvas gradient seems not to be accurate linear. + // FIXME + // Should be arbitrary value 100? or based on pixel size? + var count = 200; + var step = (dataExtent[1] - dataExtent[0]) / count; + var value = dataExtent[0]; + var stopValues = []; + for (var i = 0; i <= count && value < dataExtent[1]; i++) { + stopValues.push(value); + value += step; + } + stopValues.push(dataExtent[1]); + return stopValues; + } + + var VisualMapView = /** @class */function (_super) { + __extends(VisualMapView, _super); + function VisualMapView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = VisualMapView.type; + _this.autoPositionValues = { + left: 1, + right: 1, + top: 1, + bottom: 1 + }; + return _this; + } + VisualMapView.prototype.init = function (ecModel, api) { + this.ecModel = ecModel; + this.api = api; + }; + /** + * @protected + */ + VisualMapView.prototype.render = function (visualMapModel, ecModel, api, payload // TODO: TYPE + ) { + this.visualMapModel = visualMapModel; + if (visualMapModel.get('show') === false) { + this.group.removeAll(); + return; + } + this.doRender(visualMapModel, ecModel, api, payload); + }; + /** + * @protected + */ + VisualMapView.prototype.renderBackground = function (group) { + var visualMapModel = this.visualMapModel; + var padding = normalizeCssArray$1(visualMapModel.get('padding') || 0); + var rect = group.getBoundingRect(); + group.add(new Rect({ + z2: -1, + silent: true, + shape: { + x: rect.x - padding[3], + y: rect.y - padding[0], + width: rect.width + padding[3] + padding[1], + height: rect.height + padding[0] + padding[2] + }, + style: { + fill: visualMapModel.get('backgroundColor'), + stroke: visualMapModel.get('borderColor'), + lineWidth: visualMapModel.get('borderWidth') + } + })); + }; + /** + * @protected + * @param targetValue can be Infinity or -Infinity + * @param visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize' + * @param opts + * @param opts.forceState Specify state, instead of using getValueState method. + * @param opts.convertOpacityToAlpha For color gradient in controller widget. + * @return {*} Visual value. + */ + VisualMapView.prototype.getControllerVisual = function (targetValue, visualCluster, opts) { + opts = opts || {}; + var forceState = opts.forceState; + var visualMapModel = this.visualMapModel; + var visualObj = {}; + // Default values. + if (visualCluster === 'color') { + var defaultColor = visualMapModel.get('contentColor'); + visualObj.color = defaultColor; + } + function getter(key) { + return visualObj[key]; + } + function setter(key, value) { + visualObj[key] = value; + } + var mappings = visualMapModel.controllerVisuals[forceState || visualMapModel.getValueState(targetValue)]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + each(visualTypes, function (type) { + var visualMapping = mappings[type]; + if (opts.convertOpacityToAlpha && type === 'opacity') { + type = 'colorAlpha'; + visualMapping = mappings.__alphaForOpacity; + } + if (VisualMapping.dependsOn(type, visualCluster)) { + visualMapping && visualMapping.applyVisual(targetValue, getter, setter); + } + }); + return visualObj[visualCluster]; + }; + VisualMapView.prototype.positionGroup = function (group) { + var model = this.visualMapModel; + var api = this.api; + positionElement(group, model.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + }; + VisualMapView.prototype.doRender = function (visualMapModel, ecModel, api, payload) {}; + VisualMapView.type = 'visualMap'; + return VisualMapView; + }(ComponentView); + + var paramsSet = [['left', 'right', 'width'], ['top', 'bottom', 'height']]; + /** + * @param visualMapModel + * @param api + * @param itemSize always [short, long] + * @return {string} 'left' or 'right' or 'top' or 'bottom' + */ + function getItemAlign(visualMapModel, api, itemSize) { + var modelOption = visualMapModel.option; + var itemAlign = modelOption.align; + if (itemAlign != null && itemAlign !== 'auto') { + return itemAlign; + } + // Auto decision align. + var ecSize = { + width: api.getWidth(), + height: api.getHeight() + }; + var realIndex = modelOption.orient === 'horizontal' ? 1 : 0; + var reals = paramsSet[realIndex]; + var fakeValue = [0, null, 10]; + var layoutInput = {}; + for (var i = 0; i < 3; i++) { + layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i]; + layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]]; + } + var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex]; + var rect = getLayoutRect(layoutInput, ecSize, modelOption.padding); + return reals[(rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5 < ecSize[rParam[1]] * 0.5 ? 0 : 1]; + } + /** + * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and + * dataIndexInside means filtered index. + */ + // TODO: TYPE more specified payload types. + function makeHighDownBatch(batch, visualMapModel) { + each(batch || [], function (batchItem) { + if (batchItem.dataIndex != null) { + batchItem.dataIndexInside = batchItem.dataIndex; + batchItem.dataIndex = null; + } + batchItem.highlightKey = 'visualMap' + (visualMapModel ? visualMapModel.componentIndex : ''); + }); + return batch; + } + + var linearMap$2 = linearMap; + var each$e = each; + var mathMin$a = Math.min; + var mathMax$a = Math.max; + // Arbitrary value + var HOVER_LINK_SIZE = 12; + var HOVER_LINK_OUT = 6; + // Notice: + // Any "interval" should be by the order of [low, high]. + // "handle0" (handleIndex === 0) maps to + // low data value: this._dataInterval[0] and has low coord. + // "handle1" (handleIndex === 1) maps to + // high data value: this._dataInterval[1] and has high coord. + // The logic of transform is implemented in this._createBarGroup. + var ContinuousView = /** @class */function (_super) { + __extends(ContinuousView, _super); + function ContinuousView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = ContinuousView.type; + _this._shapes = {}; + _this._dataInterval = []; + _this._handleEnds = []; + _this._hoverLinkDataIndices = []; + return _this; + } + ContinuousView.prototype.init = function (ecModel, api) { + _super.prototype.init.call(this, ecModel, api); + this._hoverLinkFromSeriesMouseOver = bind(this._hoverLinkFromSeriesMouseOver, this); + this._hideIndicator = bind(this._hideIndicator, this); + }; + ContinuousView.prototype.doRender = function (visualMapModel, ecModel, api, payload) { + if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) { + this._buildView(); + } + }; + ContinuousView.prototype._buildView = function () { + this.group.removeAll(); + var visualMapModel = this.visualMapModel; + var thisGroup = this.group; + this._orient = visualMapModel.get('orient'); + this._useHandle = visualMapModel.get('calculable'); + this._resetInterval(); + this._renderBar(thisGroup); + var dataRangeText = visualMapModel.get('text'); + this._renderEndsText(thisGroup, dataRangeText, 0); + this._renderEndsText(thisGroup, dataRangeText, 1); + // Do this for background size calculation. + this._updateView(true); + // After updating view, inner shapes is built completely, + // and then background can be rendered. + this.renderBackground(thisGroup); + // Real update view + this._updateView(); + this._enableHoverLinkToSeries(); + this._enableHoverLinkFromSeries(); + this.positionGroup(thisGroup); + }; + ContinuousView.prototype._renderEndsText = function (group, dataRangeText, endsIndex) { + if (!dataRangeText) { + return; + } + // Compatible with ec2, text[0] map to high value, text[1] map low value. + var text = dataRangeText[1 - endsIndex]; + text = text != null ? text + '' : ''; + var visualMapModel = this.visualMapModel; + var textGap = visualMapModel.get('textGap'); + var itemSize = visualMapModel.itemSize; + var barGroup = this._shapes.mainGroup; + var position = this._applyTransform([itemSize[0] / 2, endsIndex === 0 ? -textGap : itemSize[1] + textGap], barGroup); + var align = this._applyTransform(endsIndex === 0 ? 'bottom' : 'top', barGroup); + var orient = this._orient; + var textStyleModel = this.visualMapModel.textStyleModel; + this.group.add(new ZRText({ + style: createTextStyle(textStyleModel, { + x: position[0], + y: position[1], + verticalAlign: orient === 'horizontal' ? 'middle' : align, + align: orient === 'horizontal' ? align : 'center', + text: text + }) + })); + }; + ContinuousView.prototype._renderBar = function (targetGroup) { + var visualMapModel = this.visualMapModel; + var shapes = this._shapes; + var itemSize = visualMapModel.itemSize; + var orient = this._orient; + var useHandle = this._useHandle; + var itemAlign = getItemAlign(visualMapModel, this.api, itemSize); + var mainGroup = shapes.mainGroup = this._createBarGroup(itemAlign); + var gradientBarGroup = new Group(); + mainGroup.add(gradientBarGroup); + // Bar + gradientBarGroup.add(shapes.outOfRange = createPolygon()); + gradientBarGroup.add(shapes.inRange = createPolygon(null, useHandle ? getCursor$1(this._orient) : null, bind(this._dragHandle, this, 'all', false), bind(this._dragHandle, this, 'all', true))); + // A border radius clip. + gradientBarGroup.setClipPath(new Rect({ + shape: { + x: 0, + y: 0, + width: itemSize[0], + height: itemSize[1], + r: 3 + } + })); + var textRect = visualMapModel.textStyleModel.getTextRect('国'); + var textSize = mathMax$a(textRect.width, textRect.height); + // Handle + if (useHandle) { + shapes.handleThumbs = []; + shapes.handleLabels = []; + shapes.handleLabelPoints = []; + this._createHandle(visualMapModel, mainGroup, 0, itemSize, textSize, orient); + this._createHandle(visualMapModel, mainGroup, 1, itemSize, textSize, orient); + } + this._createIndicator(visualMapModel, mainGroup, itemSize, textSize, orient); + targetGroup.add(mainGroup); + }; + ContinuousView.prototype._createHandle = function (visualMapModel, mainGroup, handleIndex, itemSize, textSize, orient) { + var onDrift = bind(this._dragHandle, this, handleIndex, false); + var onDragEnd = bind(this._dragHandle, this, handleIndex, true); + var handleSize = parsePercent(visualMapModel.get('handleSize'), itemSize[0]); + var handleThumb = createSymbol(visualMapModel.get('handleIcon'), -handleSize / 2, -handleSize / 2, handleSize, handleSize, null, true); + var cursor = getCursor$1(this._orient); + handleThumb.attr({ + cursor: cursor, + draggable: true, + drift: onDrift, + ondragend: onDragEnd, + onmousemove: function (e) { + stop(e.event); + } + }); + handleThumb.x = itemSize[0] / 2; + handleThumb.useStyle(visualMapModel.getModel('handleStyle').getItemStyle()); + handleThumb.setStyle({ + strokeNoScale: true, + strokeFirst: true + }); + handleThumb.style.lineWidth *= 2; + handleThumb.ensureState('emphasis').style = visualMapModel.getModel(['emphasis', 'handleStyle']).getItemStyle(); + setAsHighDownDispatcher(handleThumb, true); + mainGroup.add(handleThumb); + // Text is always horizontal layout but should not be effected by + // transform (orient/inverse). So label is built separately but not + // use zrender/graphic/helper/RectText, and is located based on view + // group (according to handleLabelPoint) but not barGroup. + var textStyleModel = this.visualMapModel.textStyleModel; + var handleLabel = new ZRText({ + cursor: cursor, + draggable: true, + drift: onDrift, + onmousemove: function (e) { + // For mobile device, prevent screen slider on the button. + stop(e.event); + }, + ondragend: onDragEnd, + style: createTextStyle(textStyleModel, { + x: 0, + y: 0, + text: '' + }) + }); + handleLabel.ensureState('blur').style = { + opacity: 0.1 + }; + handleLabel.stateTransition = { + duration: 200 + }; + this.group.add(handleLabel); + var handleLabelPoint = [handleSize, 0]; + var shapes = this._shapes; + shapes.handleThumbs[handleIndex] = handleThumb; + shapes.handleLabelPoints[handleIndex] = handleLabelPoint; + shapes.handleLabels[handleIndex] = handleLabel; + }; + ContinuousView.prototype._createIndicator = function (visualMapModel, mainGroup, itemSize, textSize, orient) { + var scale = parsePercent(visualMapModel.get('indicatorSize'), itemSize[0]); + var indicator = createSymbol(visualMapModel.get('indicatorIcon'), -scale / 2, -scale / 2, scale, scale, null, true); + indicator.attr({ + cursor: 'move', + invisible: true, + silent: true, + x: itemSize[0] / 2 + }); + var indicatorStyle = visualMapModel.getModel('indicatorStyle').getItemStyle(); + if (indicator instanceof ZRImage) { + var pathStyle = indicator.style; + indicator.useStyle(extend({ + // TODO other properties like x, y ? + image: pathStyle.image, + x: pathStyle.x, + y: pathStyle.y, + width: pathStyle.width, + height: pathStyle.height + }, indicatorStyle)); + } else { + indicator.useStyle(indicatorStyle); + } + mainGroup.add(indicator); + var textStyleModel = this.visualMapModel.textStyleModel; + var indicatorLabel = new ZRText({ + silent: true, + invisible: true, + style: createTextStyle(textStyleModel, { + x: 0, + y: 0, + text: '' + }) + }); + this.group.add(indicatorLabel); + var indicatorLabelPoint = [(orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT) + itemSize[0] / 2, 0]; + var shapes = this._shapes; + shapes.indicator = indicator; + shapes.indicatorLabel = indicatorLabel; + shapes.indicatorLabelPoint = indicatorLabelPoint; + this._firstShowIndicator = true; + }; + ContinuousView.prototype._dragHandle = function (handleIndex, isEnd, + // dx is event from ondragend if isEnd is true. It's not used + dx, dy) { + if (!this._useHandle) { + return; + } + this._dragging = !isEnd; + if (!isEnd) { + // Transform dx, dy to bar coordination. + var vertex = this._applyTransform([dx, dy], this._shapes.mainGroup, true); + this._updateInterval(handleIndex, vertex[1]); + this._hideIndicator(); + // Considering realtime, update view should be executed + // before dispatch action. + this._updateView(); + } + // dragEnd do not dispatch action when realtime. + if (isEnd === !this.visualMapModel.get('realtime')) { + // jshint ignore:line + this.api.dispatchAction({ + type: 'selectDataRange', + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: this._dataInterval.slice() + }); + } + if (isEnd) { + !this._hovering && this._clearHoverLinkToSeries(); + } else if (useHoverLinkOnHandle(this.visualMapModel)) { + this._doHoverLinkToSeries(this._handleEnds[handleIndex], false); + } + }; + ContinuousView.prototype._resetInterval = function () { + var visualMapModel = this.visualMapModel; + var dataInterval = this._dataInterval = visualMapModel.getSelected(); + var dataExtent = visualMapModel.getExtent(); + var sizeExtent = [0, visualMapModel.itemSize[1]]; + this._handleEnds = [linearMap$2(dataInterval[0], dataExtent, sizeExtent, true), linearMap$2(dataInterval[1], dataExtent, sizeExtent, true)]; + }; + /** + * @private + * @param {(number|string)} handleIndex 0 or 1 or 'all' + * @param {number} dx + * @param {number} dy + */ + ContinuousView.prototype._updateInterval = function (handleIndex, delta) { + delta = delta || 0; + var visualMapModel = this.visualMapModel; + var handleEnds = this._handleEnds; + var sizeExtent = [0, visualMapModel.itemSize[1]]; + sliderMove(delta, handleEnds, sizeExtent, handleIndex, + // cross is forbidden + 0); + var dataExtent = visualMapModel.getExtent(); + // Update data interval. + this._dataInterval = [linearMap$2(handleEnds[0], sizeExtent, dataExtent, true), linearMap$2(handleEnds[1], sizeExtent, dataExtent, true)]; + }; + ContinuousView.prototype._updateView = function (forSketch) { + var visualMapModel = this.visualMapModel; + var dataExtent = visualMapModel.getExtent(); + var shapes = this._shapes; + var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]]; + var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds; + var visualInRange = this._createBarVisual(this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange'); + var visualOutOfRange = this._createBarVisual(dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange'); + shapes.inRange.setStyle({ + fill: visualInRange.barColor + // opacity: visualInRange.opacity + }).setShape('points', visualInRange.barPoints); + shapes.outOfRange.setStyle({ + fill: visualOutOfRange.barColor + // opacity: visualOutOfRange.opacity + }).setShape('points', visualOutOfRange.barPoints); + this._updateHandle(inRangeHandleEnds, visualInRange); + }; + ContinuousView.prototype._createBarVisual = function (dataInterval, dataExtent, handleEnds, forceState) { + var opts = { + forceState: forceState, + convertOpacityToAlpha: true + }; + var colorStops = this._makeColorGradient(dataInterval, opts); + var symbolSizes = [this.getControllerVisual(dataInterval[0], 'symbolSize', opts), this.getControllerVisual(dataInterval[1], 'symbolSize', opts)]; + var barPoints = this._createBarPoints(handleEnds, symbolSizes); + return { + barColor: new LinearGradient(0, 0, 0, 1, colorStops), + barPoints: barPoints, + handlesColor: [colorStops[0].color, colorStops[colorStops.length - 1].color] + }; + }; + ContinuousView.prototype._makeColorGradient = function (dataInterval, opts) { + // Considering colorHue, which is not linear, so we have to sample + // to calculate gradient color stops, but not only calculate head + // and tail. + var sampleNumber = 100; // Arbitrary value. + var colorStops = []; + var step = (dataInterval[1] - dataInterval[0]) / sampleNumber; + colorStops.push({ + color: this.getControllerVisual(dataInterval[0], 'color', opts), + offset: 0 + }); + for (var i = 1; i < sampleNumber; i++) { + var currValue = dataInterval[0] + step * i; + if (currValue > dataInterval[1]) { + break; + } + colorStops.push({ + color: this.getControllerVisual(currValue, 'color', opts), + offset: i / sampleNumber + }); + } + colorStops.push({ + color: this.getControllerVisual(dataInterval[1], 'color', opts), + offset: 1 + }); + return colorStops; + }; + ContinuousView.prototype._createBarPoints = function (handleEnds, symbolSizes) { + var itemSize = this.visualMapModel.itemSize; + return [[itemSize[0] - symbolSizes[0], handleEnds[0]], [itemSize[0], handleEnds[0]], [itemSize[0], handleEnds[1]], [itemSize[0] - symbolSizes[1], handleEnds[1]]]; + }; + ContinuousView.prototype._createBarGroup = function (itemAlign) { + var orient = this._orient; + var inverse = this.visualMapModel.get('inverse'); + return new Group(orient === 'horizontal' && !inverse ? { + scaleX: itemAlign === 'bottom' ? 1 : -1, + rotation: Math.PI / 2 + } : orient === 'horizontal' && inverse ? { + scaleX: itemAlign === 'bottom' ? -1 : 1, + rotation: -Math.PI / 2 + } : orient === 'vertical' && !inverse ? { + scaleX: itemAlign === 'left' ? 1 : -1, + scaleY: -1 + } : { + scaleX: itemAlign === 'left' ? 1 : -1 + }); + }; + ContinuousView.prototype._updateHandle = function (handleEnds, visualInRange) { + if (!this._useHandle) { + return; + } + var shapes = this._shapes; + var visualMapModel = this.visualMapModel; + var handleThumbs = shapes.handleThumbs; + var handleLabels = shapes.handleLabels; + var itemSize = visualMapModel.itemSize; + var dataExtent = visualMapModel.getExtent(); + each$e([0, 1], function (handleIndex) { + var handleThumb = handleThumbs[handleIndex]; + handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]); + handleThumb.y = handleEnds[handleIndex]; + var val = linearMap$2(handleEnds[handleIndex], [0, itemSize[1]], dataExtent, true); + var symbolSize = this.getControllerVisual(val, 'symbolSize'); + handleThumb.scaleX = handleThumb.scaleY = symbolSize / itemSize[0]; + handleThumb.x = itemSize[0] - symbolSize / 2; + // Update handle label position. + var textPoint = applyTransform$1(shapes.handleLabelPoints[handleIndex], getTransform(handleThumb, this.group)); + handleLabels[handleIndex].setStyle({ + x: textPoint[0], + y: textPoint[1], + text: visualMapModel.formatValueText(this._dataInterval[handleIndex]), + verticalAlign: 'middle', + align: this._orient === 'vertical' ? this._applyTransform('left', shapes.mainGroup) : 'center' + }); + }, this); + }; + ContinuousView.prototype._showIndicator = function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) { + var visualMapModel = this.visualMapModel; + var dataExtent = visualMapModel.getExtent(); + var itemSize = visualMapModel.itemSize; + var sizeExtent = [0, itemSize[1]]; + var shapes = this._shapes; + var indicator = shapes.indicator; + if (!indicator) { + return; + } + indicator.attr('invisible', false); + var opts = { + convertOpacityToAlpha: true + }; + var color = this.getControllerVisual(cursorValue, 'color', opts); + var symbolSize = this.getControllerVisual(cursorValue, 'symbolSize'); + var y = linearMap$2(cursorValue, dataExtent, sizeExtent, true); + var x = itemSize[0] - symbolSize / 2; + var oldIndicatorPos = { + x: indicator.x, + y: indicator.y + }; + // Update handle label position. + indicator.y = y; + indicator.x = x; + var textPoint = applyTransform$1(shapes.indicatorLabelPoint, getTransform(indicator, this.group)); + var indicatorLabel = shapes.indicatorLabel; + indicatorLabel.attr('invisible', false); + var align = this._applyTransform('left', shapes.mainGroup); + var orient = this._orient; + var isHorizontal = orient === 'horizontal'; + indicatorLabel.setStyle({ + text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue), + verticalAlign: isHorizontal ? align : 'middle', + align: isHorizontal ? 'center' : align + }); + var indicatorNewProps = { + x: x, + y: y, + style: { + fill: color + } + }; + var labelNewProps = { + style: { + x: textPoint[0], + y: textPoint[1] + } + }; + if (visualMapModel.ecModel.isAnimationEnabled() && !this._firstShowIndicator) { + var animationCfg = { + duration: 100, + easing: 'cubicInOut', + additive: true + }; + indicator.x = oldIndicatorPos.x; + indicator.y = oldIndicatorPos.y; + indicator.animateTo(indicatorNewProps, animationCfg); + indicatorLabel.animateTo(labelNewProps, animationCfg); + } else { + indicator.attr(indicatorNewProps); + indicatorLabel.attr(labelNewProps); + } + this._firstShowIndicator = false; + var handleLabels = this._shapes.handleLabels; + if (handleLabels) { + for (var i = 0; i < handleLabels.length; i++) { + // Fade out handle labels. + // NOTE: Must use api enter/leave on emphasis/blur/select state. Or the global states manager will change it. + this.api.enterBlur(handleLabels[i]); + } + } + }; + ContinuousView.prototype._enableHoverLinkToSeries = function () { + var self = this; + this._shapes.mainGroup.on('mousemove', function (e) { + self._hovering = true; + if (!self._dragging) { + var itemSize = self.visualMapModel.itemSize; + var pos = self._applyTransform([e.offsetX, e.offsetY], self._shapes.mainGroup, true, true); + // For hover link show when hover handle, which might be + // below or upper than sizeExtent. + pos[1] = mathMin$a(mathMax$a(0, pos[1]), itemSize[1]); + self._doHoverLinkToSeries(pos[1], 0 <= pos[0] && pos[0] <= itemSize[0]); + } + }).on('mouseout', function () { + // When mouse is out of handle, hoverLink still need + // to be displayed when realtime is set as false. + self._hovering = false; + !self._dragging && self._clearHoverLinkToSeries(); + }); + }; + ContinuousView.prototype._enableHoverLinkFromSeries = function () { + var zr = this.api.getZr(); + if (this.visualMapModel.option.hoverLink) { + zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this); + zr.on('mouseout', this._hideIndicator, this); + } else { + this._clearHoverLinkFromSeries(); + } + }; + ContinuousView.prototype._doHoverLinkToSeries = function (cursorPos, hoverOnBar) { + var visualMapModel = this.visualMapModel; + var itemSize = visualMapModel.itemSize; + if (!visualMapModel.option.hoverLink) { + return; + } + var sizeExtent = [0, itemSize[1]]; + var dataExtent = visualMapModel.getExtent(); + // For hover link show when hover handle, which might be below or upper than sizeExtent. + cursorPos = mathMin$a(mathMax$a(sizeExtent[0], cursorPos), sizeExtent[1]); + var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent); + var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize]; + var cursorValue = linearMap$2(cursorPos, sizeExtent, dataExtent, true); + var valueRange = [linearMap$2(hoverRange[0], sizeExtent, dataExtent, true), linearMap$2(hoverRange[1], sizeExtent, dataExtent, true)]; + // Consider data range is out of visualMap range, see test/visualMap-continuous.html, + // where china and india has very large population. + hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity); + hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity); + // Do not show indicator when mouse is over handle, + // otherwise labels overlap, especially when dragging. + if (hoverOnBar) { + if (valueRange[0] === -Infinity) { + this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize); + } else if (valueRange[1] === Infinity) { + this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize); + } else { + this._showIndicator(cursorValue, cursorValue, '≈ ', halfHoverLinkSize); + } + } + // When realtime is set as false, handles, which are in barGroup, + // also trigger hoverLink, which help user to realize where they + // focus on when dragging. (see test/heatmap-large.html) + // When realtime is set as true, highlight will not show when hover + // handle, because the label on handle, which displays a exact value + // but not range, might mislead users. + var oldBatch = this._hoverLinkDataIndices; + var newBatch = []; + if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) { + newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange); + } + var resultBatches = compressBatches(oldBatch, newBatch); + this._dispatchHighDown('downplay', makeHighDownBatch(resultBatches[0], visualMapModel)); + this._dispatchHighDown('highlight', makeHighDownBatch(resultBatches[1], visualMapModel)); + }; + ContinuousView.prototype._hoverLinkFromSeriesMouseOver = function (e) { + var ecData; + findEventDispatcher(e.target, function (target) { + var currECData = getECData(target); + if (currECData.dataIndex != null) { + ecData = currECData; + return true; + } + }, true); + if (!ecData) { + return; + } + var dataModel = this.ecModel.getSeriesByIndex(ecData.seriesIndex); + var visualMapModel = this.visualMapModel; + if (!visualMapModel.isTargetSeries(dataModel)) { + return; + } + var data = dataModel.getData(ecData.dataType); + var value = data.getStore().get(visualMapModel.getDataDimensionIndex(data), ecData.dataIndex); + if (!isNaN(value)) { + this._showIndicator(value, value); + } + }; + ContinuousView.prototype._hideIndicator = function () { + var shapes = this._shapes; + shapes.indicator && shapes.indicator.attr('invisible', true); + shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true); + var handleLabels = this._shapes.handleLabels; + if (handleLabels) { + for (var i = 0; i < handleLabels.length; i++) { + // Fade out handle labels. + // NOTE: Must use api enter/leave on emphasis/blur/select state. Or the global states manager will change it. + this.api.leaveBlur(handleLabels[i]); + } + } + }; + ContinuousView.prototype._clearHoverLinkToSeries = function () { + this._hideIndicator(); + var indices = this._hoverLinkDataIndices; + this._dispatchHighDown('downplay', makeHighDownBatch(indices, this.visualMapModel)); + indices.length = 0; + }; + ContinuousView.prototype._clearHoverLinkFromSeries = function () { + this._hideIndicator(); + var zr = this.api.getZr(); + zr.off('mouseover', this._hoverLinkFromSeriesMouseOver); + zr.off('mouseout', this._hideIndicator); + }; + ContinuousView.prototype._applyTransform = function (vertex, element, inverse, global) { + var transform = getTransform(element, global ? null : this.group); + return isArray(vertex) ? applyTransform$1(vertex, transform, inverse) : transformDirection(vertex, transform, inverse); + }; + // TODO: TYPE more specified payload types. + ContinuousView.prototype._dispatchHighDown = function (type, batch) { + batch && batch.length && this.api.dispatchAction({ + type: type, + batch: batch + }); + }; + /** + * @override + */ + ContinuousView.prototype.dispose = function () { + this._clearHoverLinkFromSeries(); + this._clearHoverLinkToSeries(); + }; + ContinuousView.type = 'visualMap.continuous'; + return ContinuousView; + }(VisualMapView); + function createPolygon(points, cursor, onDrift, onDragEnd) { + return new Polygon({ + shape: { + points: points + }, + draggable: !!onDrift, + cursor: cursor, + drift: onDrift, + onmousemove: function (e) { + // For mobile device, prevent screen slider on the button. + stop(e.event); + }, + ondragend: onDragEnd + }); + } + function getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) { + var halfHoverLinkSize = HOVER_LINK_SIZE / 2; + var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize'); + if (hoverLinkDataSize) { + halfHoverLinkSize = linearMap$2(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2; + } + return halfHoverLinkSize; + } + function useHoverLinkOnHandle(visualMapModel) { + var hoverLinkOnHandle = visualMapModel.get('hoverLinkOnHandle'); + return !!(hoverLinkOnHandle == null ? visualMapModel.get('realtime') : hoverLinkOnHandle); + } + function getCursor$1(orient) { + return orient === 'vertical' ? 'ns-resize' : 'ew-resize'; + } + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + /** + * AUTO-GENERATED FILE. DO NOT MODIFY. + */ + + /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + var visualMapActionInfo = { + type: 'selectDataRange', + event: 'dataRangeSelected', + // FIXME use updateView appears wrong + update: 'update' + }; + var visualMapActionHander = function (payload, ecModel) { + ecModel.eachComponent({ + mainType: 'visualMap', + query: payload + }, function (model) { + model.setSelected(payload.selected); + }); + }; + + var visualMapEncodingHandlers = [{ + createOnAllSeries: true, + reset: function (seriesModel, ecModel) { + var resetDefines = []; + ecModel.eachComponent('visualMap', function (visualMapModel) { + var pipelineContext = seriesModel.pipelineContext; + if (!visualMapModel.isTargetSeries(seriesModel) || pipelineContext && pipelineContext.large) { + return; + } + resetDefines.push(incrementalApplyVisual(visualMapModel.stateList, visualMapModel.targetVisuals, bind(visualMapModel.getValueState, visualMapModel), visualMapModel.getDataDimensionIndex(seriesModel.getData()))); + }); + return resetDefines; + } + }, + // Only support color. + { + createOnAllSeries: true, + reset: function (seriesModel, ecModel) { + var data = seriesModel.getData(); + var visualMetaList = []; + ecModel.eachComponent('visualMap', function (visualMapModel) { + if (visualMapModel.isTargetSeries(seriesModel)) { + var visualMeta = visualMapModel.getVisualMeta(bind(getColorVisual, null, seriesModel, visualMapModel)) || { + stops: [], + outerColors: [] + }; + var dimIdx = visualMapModel.getDataDimensionIndex(data); + if (dimIdx >= 0) { + // visualMeta.dimension should be dimension index, but not concrete dimension. + visualMeta.dimension = dimIdx; + visualMetaList.push(visualMeta); + } + } + }); + // console.log(JSON.stringify(visualMetaList.map(a => a.stops))); + seriesModel.getData().setVisual('visualMeta', visualMetaList); + } + }]; + // FIXME + // performance and export for heatmap? + // value can be Infinity or -Infinity + function getColorVisual(seriesModel, visualMapModel, value, valueState) { + var mappings = visualMapModel.targetVisuals[valueState]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + var resultVisual = { + color: getVisualFromData(seriesModel.getData(), 'color') // default color. + }; + + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + var mapping = mappings[type === 'opacity' ? '__alphaForOpacity' : type]; + mapping && mapping.applyVisual(value, getVisual, setVisual); + } + return resultVisual.color; + function getVisual(key) { + return resultVisual[key]; + } + function setVisual(key, value) { + resultVisual[key] = value; + } + } + + var each$f = each; + function visualMapPreprocessor(option) { + var visualMap = option && option.visualMap; + if (!isArray(visualMap)) { + visualMap = visualMap ? [visualMap] : []; + } + each$f(visualMap, function (opt) { + if (!opt) { + return; + } + // rename splitList to pieces + if (has$1(opt, 'splitList') && !has$1(opt, 'pieces')) { + opt.pieces = opt.splitList; + delete opt.splitList; + } + var pieces = opt.pieces; + if (pieces && isArray(pieces)) { + each$f(pieces, function (piece) { + if (isObject(piece)) { + if (has$1(piece, 'start') && !has$1(piece, 'min')) { + piece.min = piece.start; + } + if (has$1(piece, 'end') && !has$1(piece, 'max')) { + piece.max = piece.end; + } + } + }); + } + }); + } + function has$1(obj, name) { + return obj && obj.hasOwnProperty && obj.hasOwnProperty(name); + } + + var installed$1 = false; + function installCommon$1(registers) { + if (installed$1) { + return; + } + installed$1 = true; + registers.registerSubTypeDefaulter('visualMap', function (option) { + // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used. + return !option.categories && (!(option.pieces ? option.pieces.length > 0 : option.splitNumber > 0) || option.calculable) ? 'continuous' : 'piecewise'; + }); + registers.registerAction(visualMapActionInfo, visualMapActionHander); + each(visualMapEncodingHandlers, function (handler) { + registers.registerVisual(registers.PRIORITY.VISUAL.COMPONENT, handler); + }); + registers.registerPreprocessor(visualMapPreprocessor); + } + + function install$N(registers) { + registers.registerComponentModel(ContinuousModel); + registers.registerComponentView(ContinuousView); + installCommon$1(registers); + } + + var PiecewiseModel = /** @class */function (_super) { + __extends(PiecewiseModel, _super); + function PiecewiseModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = PiecewiseModel.type; + /** + * The order is always [low, ..., high]. + * [{text: string, interval: Array.}, ...] + */ + _this._pieceList = []; + return _this; + } + PiecewiseModel.prototype.optionUpdated = function (newOption, isInit) { + _super.prototype.optionUpdated.apply(this, arguments); + this.resetExtent(); + var mode = this._mode = this._determineMode(); + this._pieceList = []; + resetMethods[this._mode].call(this, this._pieceList); + this._resetSelected(newOption, isInit); + var categories = this.option.categories; + this.resetVisual(function (mappingOption, state) { + if (mode === 'categories') { + mappingOption.mappingMethod = 'category'; + mappingOption.categories = clone(categories); + } else { + mappingOption.dataExtent = this.getExtent(); + mappingOption.mappingMethod = 'piecewise'; + mappingOption.pieceList = map(this._pieceList, function (piece) { + piece = clone(piece); + if (state !== 'inRange') { + // FIXME + // outOfRange do not support special visual in pieces. + piece.visual = null; + } + return piece; + }); + } + }); + }; + /** + * @protected + * @override + */ + PiecewiseModel.prototype.completeVisualOption = function () { + // Consider this case: + // visualMap: { + // pieces: [{symbol: 'circle', lt: 0}, {symbol: 'rect', gte: 0}] + // } + // where no inRange/outOfRange set but only pieces. So we should make + // default inRange/outOfRange for this case, otherwise visuals that only + // appear in `pieces` will not be taken into account in visual encoding. + var option = this.option; + var visualTypesInPieces = {}; + var visualTypes = VisualMapping.listVisualTypes(); + var isCategory = this.isCategory(); + each(option.pieces, function (piece) { + each(visualTypes, function (visualType) { + if (piece.hasOwnProperty(visualType)) { + visualTypesInPieces[visualType] = 1; + } + }); + }); + each(visualTypesInPieces, function (v, visualType) { + var exists = false; + each(this.stateList, function (state) { + exists = exists || has(option, state, visualType) || has(option.target, state, visualType); + }, this); + !exists && each(this.stateList, function (state) { + (option[state] || (option[state] = {}))[visualType] = visualDefault.get(visualType, state === 'inRange' ? 'active' : 'inactive', isCategory); + }); + }, this); + function has(obj, state, visualType) { + return obj && obj[state] && obj[state].hasOwnProperty(visualType); + } + _super.prototype.completeVisualOption.apply(this, arguments); + }; + PiecewiseModel.prototype._resetSelected = function (newOption, isInit) { + var thisOption = this.option; + var pieceList = this._pieceList; + // Selected do not merge but all override. + var selected = (isInit ? thisOption : newOption).selected || {}; + thisOption.selected = selected; + // Consider 'not specified' means true. + each(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (!selected.hasOwnProperty(key)) { + selected[key] = true; + } + }, this); + if (thisOption.selectedMode === 'single') { + // Ensure there is only one selected. + var hasSel_1 = false; + each(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (selected[key]) { + hasSel_1 ? selected[key] = false : hasSel_1 = true; + } + }, this); + } + // thisOption.selectedMode === 'multiple', default: all selected. + }; + /** + * @public + */ + PiecewiseModel.prototype.getItemSymbol = function () { + return this.get('itemSymbol'); + }; + /** + * @public + */ + PiecewiseModel.prototype.getSelectedMapKey = function (piece) { + return this._mode === 'categories' ? piece.value + '' : piece.index + ''; + }; + /** + * @public + */ + PiecewiseModel.prototype.getPieceList = function () { + return this._pieceList; + }; + /** + * @return {string} + */ + PiecewiseModel.prototype._determineMode = function () { + var option = this.option; + return option.pieces && option.pieces.length > 0 ? 'pieces' : this.option.categories ? 'categories' : 'splitNumber'; + }; + /** + * @override + */ + PiecewiseModel.prototype.setSelected = function (selected) { + this.option.selected = clone(selected); + }; + /** + * @override + */ + PiecewiseModel.prototype.getValueState = function (value) { + var index = VisualMapping.findPieceIndex(value, this._pieceList); + return index != null ? this.option.selected[this.getSelectedMapKey(this._pieceList[index])] ? 'inRange' : 'outOfRange' : 'outOfRange'; + }; + /** + * @public + * @param pieceIndex piece index in visualMapModel.getPieceList() + */ + PiecewiseModel.prototype.findTargetDataIndices = function (pieceIndex) { + var result = []; + var pieceList = this._pieceList; + this.eachTargetSeries(function (seriesModel) { + var dataIndices = []; + var data = seriesModel.getData(); + data.each(this.getDataDimensionIndex(data), function (value, dataIndex) { + // Should always base on model pieceList, because it is order sensitive. + var pIdx = VisualMapping.findPieceIndex(value, pieceList); + pIdx === pieceIndex && dataIndices.push(dataIndex); + }, this); + result.push({ + seriesId: seriesModel.id, + dataIndex: dataIndices + }); + }, this); + return result; + }; + /** + * @private + * @param piece piece.value or piece.interval is required. + * @return Can be Infinity or -Infinity + */ + PiecewiseModel.prototype.getRepresentValue = function (piece) { + var representValue; + if (this.isCategory()) { + representValue = piece.value; + } else { + if (piece.value != null) { + representValue = piece.value; + } else { + var pieceInterval = piece.interval || []; + representValue = pieceInterval[0] === -Infinity && pieceInterval[1] === Infinity ? 0 : (pieceInterval[0] + pieceInterval[1]) / 2; + } + } + return representValue; + }; + PiecewiseModel.prototype.getVisualMeta = function (getColorVisual) { + // Do not support category. (category axis is ordinal, numerical) + if (this.isCategory()) { + return; + } + var stops = []; + var outerColors = ['', '']; + var visualMapModel = this; + function setStop(interval, valueState) { + var representValue = visualMapModel.getRepresentValue({ + interval: interval + }); // Not category + if (!valueState) { + valueState = visualMapModel.getValueState(representValue); + } + var color = getColorVisual(representValue, valueState); + if (interval[0] === -Infinity) { + outerColors[0] = color; + } else if (interval[1] === Infinity) { + outerColors[1] = color; + } else { + stops.push({ + value: interval[0], + color: color + }, { + value: interval[1], + color: color + }); + } + } + // Suplement + var pieceList = this._pieceList.slice(); + if (!pieceList.length) { + pieceList.push({ + interval: [-Infinity, Infinity] + }); + } else { + var edge = pieceList[0].interval[0]; + edge !== -Infinity && pieceList.unshift({ + interval: [-Infinity, edge] + }); + edge = pieceList[pieceList.length - 1].interval[1]; + edge !== Infinity && pieceList.push({ + interval: [edge, Infinity] + }); + } + var curr = -Infinity; + each(pieceList, function (piece) { + var interval = piece.interval; + if (interval) { + // Fulfill gap. + interval[0] > curr && setStop([curr, interval[0]], 'outOfRange'); + setStop(interval.slice()); + curr = interval[1]; + } + }, this); + return { + stops: stops, + outerColors: outerColors + }; + }; + PiecewiseModel.type = 'visualMap.piecewise'; + PiecewiseModel.defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, { + selected: null, + minOpen: false, + maxOpen: false, + align: 'auto', + itemWidth: 20, + itemHeight: 14, + itemSymbol: 'roundRect', + pieces: null, + categories: null, + splitNumber: 5, + selectedMode: 'multiple', + itemGap: 10, + hoverLink: true // Enable hover highlight. + }); + + return PiecewiseModel; + }(VisualMapModel); + /** + * Key is this._mode + * @type {Object} + * @this {module:echarts/component/viusalMap/PiecewiseMode} + */ + var resetMethods = { + splitNumber: function (outPieceList) { + var thisOption = this.option; + var precision = Math.min(thisOption.precision, 20); + var dataExtent = this.getExtent(); + var splitNumber = thisOption.splitNumber; + splitNumber = Math.max(parseInt(splitNumber, 10), 1); + thisOption.splitNumber = splitNumber; + var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; + // Precision auto-adaption + while (+splitStep.toFixed(precision) !== splitStep && precision < 5) { + precision++; + } + thisOption.precision = precision; + splitStep = +splitStep.toFixed(precision); + if (thisOption.minOpen) { + outPieceList.push({ + interval: [-Infinity, dataExtent[0]], + close: [0, 0] + }); + } + for (var index = 0, curr = dataExtent[0]; index < splitNumber; curr += splitStep, index++) { + var max = index === splitNumber - 1 ? dataExtent[1] : curr + splitStep; + outPieceList.push({ + interval: [curr, max], + close: [1, 1] + }); + } + if (thisOption.maxOpen) { + outPieceList.push({ + interval: [dataExtent[1], Infinity], + close: [0, 0] + }); + } + reformIntervals(outPieceList); + each(outPieceList, function (piece, index) { + piece.index = index; + piece.text = this.formatValueText(piece.interval); + }, this); + }, + categories: function (outPieceList) { + var thisOption = this.option; + each(thisOption.categories, function (cate) { + // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。 + // 是否改一致。 + outPieceList.push({ + text: this.formatValueText(cate, true), + value: cate + }); + }, this); + // See "Order Rule". + normalizeReverse(thisOption, outPieceList); + }, + pieces: function (outPieceList) { + var thisOption = this.option; + each(thisOption.pieces, function (pieceListItem, index) { + if (!isObject(pieceListItem)) { + pieceListItem = { + value: pieceListItem + }; + } + var item = { + text: '', + index: index + }; + if (pieceListItem.label != null) { + item.text = pieceListItem.label; + } + if (pieceListItem.hasOwnProperty('value')) { + var value = item.value = pieceListItem.value; + item.interval = [value, value]; + item.close = [1, 1]; + } else { + // `min` `max` is legacy option. + // `lt` `gt` `lte` `gte` is recommended. + var interval = item.interval = []; + var close_1 = item.close = [0, 0]; + var closeList = [1, 0, 1]; + var infinityList = [-Infinity, Infinity]; + var useMinMax = []; + for (var lg = 0; lg < 2; lg++) { + var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg]; + for (var i = 0; i < 3 && interval[lg] == null; i++) { + interval[lg] = pieceListItem[names[i]]; + close_1[lg] = closeList[i]; + useMinMax[lg] = i === 2; + } + interval[lg] == null && (interval[lg] = infinityList[lg]); + } + useMinMax[0] && interval[1] === Infinity && (close_1[0] = 0); + useMinMax[1] && interval[0] === -Infinity && (close_1[1] = 0); + if ("development" !== 'production') { + if (interval[0] > interval[1]) { + console.warn('Piece ' + index + 'is illegal: ' + interval + ' lower bound should not greater then uppper bound.'); + } + } + if (interval[0] === interval[1] && close_1[0] && close_1[1]) { + // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}], + // we use value to lift the priority when min === max + item.value = interval[0]; + } + } + item.visual = VisualMapping.retrieveVisuals(pieceListItem); + outPieceList.push(item); + }, this); + // See "Order Rule". + normalizeReverse(thisOption, outPieceList); + // Only pieces + reformIntervals(outPieceList); + each(outPieceList, function (piece) { + var close = piece.close; + var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]]; + piece.text = piece.text || this.formatValueText(piece.value != null ? piece.value : piece.interval, false, edgeSymbols); + }, this); + } + }; + function normalizeReverse(thisOption, pieceList) { + var inverse = thisOption.inverse; + if (thisOption.orient === 'vertical' ? !inverse : inverse) { + pieceList.reverse(); + } + } + + var PiecewiseVisualMapView = /** @class */function (_super) { + __extends(PiecewiseVisualMapView, _super); + function PiecewiseVisualMapView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = PiecewiseVisualMapView.type; + return _this; + } + PiecewiseVisualMapView.prototype.doRender = function () { + var thisGroup = this.group; + thisGroup.removeAll(); + var visualMapModel = this.visualMapModel; + var textGap = visualMapModel.get('textGap'); + var textStyleModel = visualMapModel.textStyleModel; + var textFont = textStyleModel.getFont(); + var textFill = textStyleModel.getTextColor(); + var itemAlign = this._getItemAlign(); + var itemSize = visualMapModel.itemSize; + var viewData = this._getViewData(); + var endsText = viewData.endsText; + var showLabel = retrieve(visualMapModel.get('showLabel', true), !endsText); + endsText && this._renderEndsText(thisGroup, endsText[0], itemSize, showLabel, itemAlign); + each(viewData.viewPieceList, function (item) { + var piece = item.piece; + var itemGroup = new Group(); + itemGroup.onclick = bind(this._onItemClick, this, piece); + this._enableHoverLink(itemGroup, item.indexInModelPieceList); + // TODO Category + var representValue = visualMapModel.getRepresentValue(piece); + this._createItemSymbol(itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]]); + if (showLabel) { + var visualState = this.visualMapModel.getValueState(representValue); + itemGroup.add(new ZRText({ + style: { + x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap, + y: itemSize[1] / 2, + text: piece.text, + verticalAlign: 'middle', + align: itemAlign, + font: textFont, + fill: textFill, + opacity: visualState === 'outOfRange' ? 0.5 : 1 + } + })); + } + thisGroup.add(itemGroup); + }, this); + endsText && this._renderEndsText(thisGroup, endsText[1], itemSize, showLabel, itemAlign); + box(visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap')); + this.renderBackground(thisGroup); + this.positionGroup(thisGroup); + }; + PiecewiseVisualMapView.prototype._enableHoverLink = function (itemGroup, pieceIndex) { + var _this = this; + itemGroup.on('mouseover', function () { + return onHoverLink('highlight'); + }).on('mouseout', function () { + return onHoverLink('downplay'); + }); + var onHoverLink = function (method) { + var visualMapModel = _this.visualMapModel; + // TODO: TYPE More detailed action types + visualMapModel.option.hoverLink && _this.api.dispatchAction({ + type: method, + batch: makeHighDownBatch(visualMapModel.findTargetDataIndices(pieceIndex), visualMapModel) + }); + }; + }; + PiecewiseVisualMapView.prototype._getItemAlign = function () { + var visualMapModel = this.visualMapModel; + var modelOption = visualMapModel.option; + if (modelOption.orient === 'vertical') { + return getItemAlign(visualMapModel, this.api, visualMapModel.itemSize); + } else { + // horizontal, most case left unless specifying right. + var align = modelOption.align; + if (!align || align === 'auto') { + align = 'left'; + } + return align; + } + }; + PiecewiseVisualMapView.prototype._renderEndsText = function (group, text, itemSize, showLabel, itemAlign) { + if (!text) { + return; + } + var itemGroup = new Group(); + var textStyleModel = this.visualMapModel.textStyleModel; + itemGroup.add(new ZRText({ + style: createTextStyle(textStyleModel, { + x: showLabel ? itemAlign === 'right' ? itemSize[0] : 0 : itemSize[0] / 2, + y: itemSize[1] / 2, + verticalAlign: 'middle', + align: showLabel ? itemAlign : 'center', + text: text + }) + })); + group.add(itemGroup); + }; + /** + * @private + * @return {Object} {peiceList, endsText} The order is the same as screen pixel order. + */ + PiecewiseVisualMapView.prototype._getViewData = function () { + var visualMapModel = this.visualMapModel; + var viewPieceList = map(visualMapModel.getPieceList(), function (piece, index) { + return { + piece: piece, + indexInModelPieceList: index + }; + }); + var endsText = visualMapModel.get('text'); + // Consider orient and inverse. + var orient = visualMapModel.get('orient'); + var inverse = visualMapModel.get('inverse'); + // Order of model pieceList is always [low, ..., high] + if (orient === 'horizontal' ? inverse : !inverse) { + viewPieceList.reverse(); + } + // Origin order of endsText is [high, low] + else if (endsText) { + endsText = endsText.slice().reverse(); + } + return { + viewPieceList: viewPieceList, + endsText: endsText + }; + }; + PiecewiseVisualMapView.prototype._createItemSymbol = function (group, representValue, shapeParam) { + group.add(createSymbol( + // symbol will be string + this.getControllerVisual(representValue, 'symbol'), shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3], + // color will be string + this.getControllerVisual(representValue, 'color'))); + }; + PiecewiseVisualMapView.prototype._onItemClick = function (piece) { + var visualMapModel = this.visualMapModel; + var option = visualMapModel.option; + var selectedMode = option.selectedMode; + if (!selectedMode) { + return; + } + var selected = clone(option.selected); + var newKey = visualMapModel.getSelectedMapKey(piece); + if (selectedMode === 'single' || selectedMode === true) { + selected[newKey] = true; + each(selected, function (o, key) { + selected[key] = key === newKey; + }); + } else { + selected[newKey] = !selected[newKey]; + } + this.api.dispatchAction({ + type: 'selectDataRange', + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: selected + }); + }; + PiecewiseVisualMapView.type = 'visualMap.piecewise'; + return PiecewiseVisualMapView; + }(VisualMapView); + + function install$O(registers) { + registers.registerComponentModel(PiecewiseModel); + registers.registerComponentView(PiecewiseVisualMapView); + installCommon$1(registers); + } + + function install$P(registers) { + use(install$N); + use(install$O); + // Do not install './dataZoomSelect', + // since it only work for toolbox dataZoom. + } + + var DEFAULT_OPTION = { + label: { + enabled: true + }, + decal: { + show: false + } + }; + var inner$l = makeInner(); + var decalPaletteScope = {}; + function ariaVisual(ecModel, api) { + var ariaModel = ecModel.getModel('aria'); + // See "area enabled" detection code in `GlobalModel.ts`. + if (!ariaModel.get('enabled')) { + return; + } + var defaultOption = clone(DEFAULT_OPTION); + merge(defaultOption.label, ecModel.getLocaleModel().get('aria'), false); + merge(ariaModel.option, defaultOption, false); + setDecal(); + setLabel(); + function setDecal() { + var decalModel = ariaModel.getModel('decal'); + var useDecal = decalModel.get('show'); + if (useDecal) { + // Each type of series use one scope. + // Pie and funnel are using different scopes. + var paletteScopeGroupByType_1 = createHashMap(); + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.isColorBySeries()) { + return; + } + var decalScope = paletteScopeGroupByType_1.get(seriesModel.type); + if (!decalScope) { + decalScope = {}; + paletteScopeGroupByType_1.set(seriesModel.type, decalScope); + } + inner$l(seriesModel).scope = decalScope; + }); + ecModel.eachRawSeries(function (seriesModel) { + if (ecModel.isSeriesFiltered(seriesModel)) { + return; + } + if (isFunction(seriesModel.enableAriaDecal)) { + // Let series define how to use decal palette on data + seriesModel.enableAriaDecal(); + return; + } + var data = seriesModel.getData(); + if (!seriesModel.isColorBySeries()) { + var dataAll_1 = seriesModel.getRawData(); + var idxMap_1 = {}; + var decalScope_1 = inner$l(seriesModel).scope; + data.each(function (idx) { + var rawIdx = data.getRawIndex(idx); + idxMap_1[rawIdx] = idx; + }); + var dataCount_1 = dataAll_1.count(); + dataAll_1.each(function (rawIdx) { + var idx = idxMap_1[rawIdx]; + var name = dataAll_1.getName(rawIdx) || rawIdx + ''; + var paletteDecal = getDecalFromPalette(seriesModel.ecModel, name, decalScope_1, dataCount_1); + var specifiedDecal = data.getItemVisual(idx, 'decal'); + data.setItemVisual(idx, 'decal', mergeDecal(specifiedDecal, paletteDecal)); + }); + } else { + var paletteDecal = getDecalFromPalette(seriesModel.ecModel, seriesModel.name, decalPaletteScope, ecModel.getSeriesCount()); + var specifiedDecal = data.getVisual('decal'); + data.setVisual('decal', mergeDecal(specifiedDecal, paletteDecal)); + } + function mergeDecal(specifiedDecal, paletteDecal) { + // Merge decal from palette to decal from itemStyle. + // User do not need to specify all of the decal props. + var resultDecal = specifiedDecal ? extend(extend({}, paletteDecal), specifiedDecal) : paletteDecal; + resultDecal.dirty = true; + return resultDecal; + } + }); + } + } + function setLabel() { + var labelLocale = ecModel.getLocaleModel().get('aria'); + var labelModel = ariaModel.getModel('label'); + labelModel.option = defaults(labelModel.option, labelLocale); + if (!labelModel.get('enabled')) { + return; + } + var dom = api.getZr().dom; + if (labelModel.get('description')) { + dom.setAttribute('aria-label', labelModel.get('description')); + return; + } + var seriesCnt = ecModel.getSeriesCount(); + var maxDataCnt = labelModel.get(['data', 'maxCount']) || 10; + var maxSeriesCnt = labelModel.get(['series', 'maxCount']) || 10; + var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt); + var ariaLabel; + if (seriesCnt < 1) { + // No series, no aria label + return; + } else { + var title = getTitle(); + if (title) { + var withTitle = labelModel.get(['general', 'withTitle']); + ariaLabel = replace(withTitle, { + title: title + }); + } else { + ariaLabel = labelModel.get(['general', 'withoutTitle']); + } + var seriesLabels_1 = []; + var prefix = seriesCnt > 1 ? labelModel.get(['series', 'multiple', 'prefix']) : labelModel.get(['series', 'single', 'prefix']); + ariaLabel += replace(prefix, { + seriesCount: seriesCnt + }); + ecModel.eachSeries(function (seriesModel, idx) { + if (idx < displaySeriesCnt) { + var seriesLabel = void 0; + var seriesName = seriesModel.get('name'); + var withName = seriesName ? 'withName' : 'withoutName'; + seriesLabel = seriesCnt > 1 ? labelModel.get(['series', 'multiple', withName]) : labelModel.get(['series', 'single', withName]); + seriesLabel = replace(seriesLabel, { + seriesId: seriesModel.seriesIndex, + seriesName: seriesModel.get('name'), + seriesType: getSeriesTypeName(seriesModel.subType) + }); + var data = seriesModel.getData(); + if (data.count() > maxDataCnt) { + // Show part of data + var partialLabel = labelModel.get(['data', 'partialData']); + seriesLabel += replace(partialLabel, { + displayCnt: maxDataCnt + }); + } else { + seriesLabel += labelModel.get(['data', 'allData']); + } + var middleSeparator_1 = labelModel.get(['data', 'separator', 'middle']); + var endSeparator_1 = labelModel.get(['data', 'separator', 'end']); + var dataLabels = []; + for (var i = 0; i < data.count(); i++) { + if (i < maxDataCnt) { + var name_1 = data.getName(i); + var value = data.getValues(i); + var dataLabel = labelModel.get(['data', name_1 ? 'withName' : 'withoutName']); + dataLabels.push(replace(dataLabel, { + name: name_1, + value: value.join(middleSeparator_1) + })); + } + } + seriesLabel += dataLabels.join(middleSeparator_1) + endSeparator_1; + seriesLabels_1.push(seriesLabel); + } + }); + var separatorModel = labelModel.getModel(['series', 'multiple', 'separator']); + var middleSeparator = separatorModel.get('middle'); + var endSeparator = separatorModel.get('end'); + ariaLabel += seriesLabels_1.join(middleSeparator) + endSeparator; + dom.setAttribute('aria-label', ariaLabel); + } + } + function replace(str, keyValues) { + if (!isString(str)) { + return str; + } + var result = str; + each(keyValues, function (value, key) { + result = result.replace(new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'), value); + }); + return result; + } + function getTitle() { + var title = ecModel.get('title'); + if (title && title.length) { + title = title[0]; + } + return title && title.text; + } + function getSeriesTypeName(type) { + var typeNames = ecModel.getLocaleModel().get(['series', 'typeNames']); + return typeNames[type] || typeNames.chart; + } + } + + function ariaPreprocessor(option) { + if (!option || !option.aria) { + return; + } + var aria = option.aria; + // aria.show is deprecated and should use aria.enabled instead + if (aria.show != null) { + aria.enabled = aria.show; + } + aria.label = aria.label || {}; + // move description, general, series, data to be under aria.label + each(['description', 'general', 'series', 'data'], function (name) { + if (aria[name] != null) { + aria.label[name] = aria[name]; + } + }); + } + + function install$Q(registers) { + registers.registerPreprocessor(ariaPreprocessor); + registers.registerVisual(registers.PRIORITY.VISUAL.ARIA, ariaVisual); + } + + var RELATIONAL_EXPRESSION_OP_ALIAS_MAP = { + value: 'eq', + // PENDING: not good for literal semantic? + '<': 'lt', + '<=': 'lte', + '>': 'gt', + '>=': 'gte', + '=': 'eq', + '!=': 'ne', + '<>': 'ne' + // Might be misleading for sake of the difference between '==' and '===', + // so don't support them. + // '==': 'eq', + // '===': 'seq', + // '!==': 'sne' + // PENDING: Whether support some common alias "ge", "le", "neq"? + // ge: 'gte', + // le: 'lte', + // neq: 'ne', + }; + // type RelationalExpressionOpEvaluate = (tarVal: unknown, condVal: unknown) => boolean; + var RegExpEvaluator = /** @class */function () { + function RegExpEvaluator(rVal) { + // Support condVal: RegExp | string + var condValue = this._condVal = isString(rVal) ? new RegExp(rVal) : isRegExp(rVal) ? rVal : null; + if (condValue == null) { + var errMsg = ''; + if ("development" !== 'production') { + errMsg = makePrintable('Illegal regexp', rVal, 'in'); + } + throwError(errMsg); + } + } + RegExpEvaluator.prototype.evaluate = function (lVal) { + var type = typeof lVal; + return isString(type) ? this._condVal.test(lVal) : isNumber(type) ? this._condVal.test(lVal + '') : false; + }; + return RegExpEvaluator; + }(); + var ConstConditionInternal = /** @class */function () { + function ConstConditionInternal() {} + ConstConditionInternal.prototype.evaluate = function () { + return this.value; + }; + return ConstConditionInternal; + }(); + var AndConditionInternal = /** @class */function () { + function AndConditionInternal() {} + AndConditionInternal.prototype.evaluate = function () { + var children = this.children; + for (var i = 0; i < children.length; i++) { + if (!children[i].evaluate()) { + return false; + } + } + return true; + }; + return AndConditionInternal; + }(); + var OrConditionInternal = /** @class */function () { + function OrConditionInternal() {} + OrConditionInternal.prototype.evaluate = function () { + var children = this.children; + for (var i = 0; i < children.length; i++) { + if (children[i].evaluate()) { + return true; + } + } + return false; + }; + return OrConditionInternal; + }(); + var NotConditionInternal = /** @class */function () { + function NotConditionInternal() {} + NotConditionInternal.prototype.evaluate = function () { + return !this.child.evaluate(); + }; + return NotConditionInternal; + }(); + var RelationalConditionInternal = /** @class */function () { + function RelationalConditionInternal() {} + RelationalConditionInternal.prototype.evaluate = function () { + var needParse = !!this.valueParser; + // Call getValue with no `this`. + var getValue = this.getValue; + var tarValRaw = getValue(this.valueGetterParam); + var tarValParsed = needParse ? this.valueParser(tarValRaw) : null; + // Relational cond follow "and" logic internally. + for (var i = 0; i < this.subCondList.length; i++) { + if (!this.subCondList[i].evaluate(needParse ? tarValParsed : tarValRaw)) { + return false; + } + } + return true; + }; + return RelationalConditionInternal; + }(); + function parseOption(exprOption, getters) { + if (exprOption === true || exprOption === false) { + var cond = new ConstConditionInternal(); + cond.value = exprOption; + return cond; + } + var errMsg = ''; + if (!isObjectNotArray(exprOption)) { + if ("development" !== 'production') { + errMsg = makePrintable('Illegal config. Expect a plain object but actually', exprOption); + } + throwError(errMsg); + } + if (exprOption.and) { + return parseAndOrOption('and', exprOption, getters); + } else if (exprOption.or) { + return parseAndOrOption('or', exprOption, getters); + } else if (exprOption.not) { + return parseNotOption(exprOption, getters); + } + return parseRelationalOption(exprOption, getters); + } + function parseAndOrOption(op, exprOption, getters) { + var subOptionArr = exprOption[op]; + var errMsg = ''; + if ("development" !== 'production') { + errMsg = makePrintable('"and"/"or" condition should only be `' + op + ': [...]` and must not be empty array.', 'Illegal condition:', exprOption); + } + if (!isArray(subOptionArr)) { + throwError(errMsg); + } + if (!subOptionArr.length) { + throwError(errMsg); + } + var cond = op === 'and' ? new AndConditionInternal() : new OrConditionInternal(); + cond.children = map(subOptionArr, function (subOption) { + return parseOption(subOption, getters); + }); + if (!cond.children.length) { + throwError(errMsg); + } + return cond; + } + function parseNotOption(exprOption, getters) { + var subOption = exprOption.not; + var errMsg = ''; + if ("development" !== 'production') { + errMsg = makePrintable('"not" condition should only be `not: {}`.', 'Illegal condition:', exprOption); + } + if (!isObjectNotArray(subOption)) { + throwError(errMsg); + } + var cond = new NotConditionInternal(); + cond.child = parseOption(subOption, getters); + if (!cond.child) { + throwError(errMsg); + } + return cond; + } + function parseRelationalOption(exprOption, getters) { + var errMsg = ''; + var valueGetterParam = getters.prepareGetValue(exprOption); + var subCondList = []; + var exprKeys = keys(exprOption); + var parserName = exprOption.parser; + var valueParser = parserName ? getRawValueParser(parserName) : null; + for (var i = 0; i < exprKeys.length; i++) { + var keyRaw = exprKeys[i]; + if (keyRaw === 'parser' || getters.valueGetterAttrMap.get(keyRaw)) { + continue; + } + var op = hasOwn(RELATIONAL_EXPRESSION_OP_ALIAS_MAP, keyRaw) ? RELATIONAL_EXPRESSION_OP_ALIAS_MAP[keyRaw] : keyRaw; + var condValueRaw = exprOption[keyRaw]; + var condValueParsed = valueParser ? valueParser(condValueRaw) : condValueRaw; + var evaluator = createFilterComparator(op, condValueParsed) || op === 'reg' && new RegExpEvaluator(condValueParsed); + if (!evaluator) { + if ("development" !== 'production') { + errMsg = makePrintable('Illegal relational operation: "' + keyRaw + '" in condition:', exprOption); + } + throwError(errMsg); + } + subCondList.push(evaluator); + } + if (!subCondList.length) { + if ("development" !== 'production') { + errMsg = makePrintable('Relational condition must have at least one operator.', 'Illegal condition:', exprOption); + } + // No relational operator always disabled in case of dangers result. + throwError(errMsg); + } + var cond = new RelationalConditionInternal(); + cond.valueGetterParam = valueGetterParam; + cond.valueParser = valueParser; + cond.getValue = getters.getValue; + cond.subCondList = subCondList; + return cond; + } + function isObjectNotArray(val) { + return isObject(val) && !isArrayLike(val); + } + var ConditionalExpressionParsed = /** @class */function () { + function ConditionalExpressionParsed(exprOption, getters) { + this._cond = parseOption(exprOption, getters); + } + ConditionalExpressionParsed.prototype.evaluate = function () { + return this._cond.evaluate(); + }; + return ConditionalExpressionParsed; + }(); + function parseConditionalExpression(exprOption, getters) { + return new ConditionalExpressionParsed(exprOption, getters); + } + + var filterTransform = { + type: 'echarts:filter', + // PENDING: enhance to filter by index rather than create new data + transform: function (params) { + // [Caveat] Fail-Fast: + // Do not return the whole dataset unless user config indicates it explicitly. + // For example, if no condition is specified by mistake, returning an empty result + // is better than returning the entire raw source for the user to find the mistake. + var upstream = params.upstream; + var rawItem; + var condition = parseConditionalExpression(params.config, { + valueGetterAttrMap: createHashMap({ + dimension: true + }), + prepareGetValue: function (exprOption) { + var errMsg = ''; + var dimLoose = exprOption.dimension; + if (!hasOwn(exprOption, 'dimension')) { + if ("development" !== 'production') { + errMsg = makePrintable('Relation condition must has prop "dimension" specified.', 'Illegal condition:', exprOption); + } + throwError(errMsg); + } + var dimInfo = upstream.getDimensionInfo(dimLoose); + if (!dimInfo) { + if ("development" !== 'production') { + errMsg = makePrintable('Can not find dimension info via: ' + dimLoose + '.\n', 'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\n', 'Illegal condition:', exprOption, '.\n'); + } + throwError(errMsg); + } + return { + dimIdx: dimInfo.index + }; + }, + getValue: function (param) { + return upstream.retrieveValueFromItem(rawItem, param.dimIdx); + } + }); + var resultData = []; + for (var i = 0, len = upstream.count(); i < len; i++) { + rawItem = upstream.getRawDataItem(i); + if (condition.evaluate()) { + resultData.push(rawItem); + } + } + return { + data: resultData + }; + } + }; + + var sampleLog = ''; + if ("development" !== 'production') { + sampleLog = ['Valid config is like:', '{ dimension: "age", order: "asc" }', 'or [{ dimension: "age", order: "asc"], { dimension: "date", order: "desc" }]'].join(' '); + } + var sortTransform = { + type: 'echarts:sort', + transform: function (params) { + var upstream = params.upstream; + var config = params.config; + var errMsg = ''; + // Normalize + // const orderExprList: OrderExpression[] = isArray(config[0]) + // ? config as OrderExpression[] + // : [config as OrderExpression]; + var orderExprList = normalizeToArray(config); + if (!orderExprList.length) { + if ("development" !== 'production') { + errMsg = 'Empty `config` in sort transform.'; + } + throwError(errMsg); + } + var orderDefList = []; + each(orderExprList, function (orderExpr) { + var dimLoose = orderExpr.dimension; + var order = orderExpr.order; + var parserName = orderExpr.parser; + var incomparable = orderExpr.incomparable; + if (dimLoose == null) { + if ("development" !== 'production') { + errMsg = 'Sort transform config must has "dimension" specified.' + sampleLog; + } + throwError(errMsg); + } + if (order !== 'asc' && order !== 'desc') { + if ("development" !== 'production') { + errMsg = 'Sort transform config must has "order" specified.' + sampleLog; + } + throwError(errMsg); + } + if (incomparable && incomparable !== 'min' && incomparable !== 'max') { + var errMsg_1 = ''; + if ("development" !== 'production') { + errMsg_1 = 'incomparable must be "min" or "max" rather than "' + incomparable + '".'; + } + throwError(errMsg_1); + } + if (order !== 'asc' && order !== 'desc') { + var errMsg_2 = ''; + if ("development" !== 'production') { + errMsg_2 = 'order must be "asc" or "desc" rather than "' + order + '".'; + } + throwError(errMsg_2); + } + var dimInfo = upstream.getDimensionInfo(dimLoose); + if (!dimInfo) { + if ("development" !== 'production') { + errMsg = makePrintable('Can not find dimension info via: ' + dimLoose + '.\n', 'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\n', 'Illegal config:', orderExpr, '.\n'); + } + throwError(errMsg); + } + var parser = parserName ? getRawValueParser(parserName) : null; + if (parserName && !parser) { + if ("development" !== 'production') { + errMsg = makePrintable('Invalid parser name ' + parserName + '.\n', 'Illegal config:', orderExpr, '.\n'); + } + throwError(errMsg); + } + orderDefList.push({ + dimIdx: dimInfo.index, + parser: parser, + comparator: new SortOrderComparator(order, incomparable) + }); + }); + // TODO: support it? + var sourceFormat = upstream.sourceFormat; + if (sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS) { + if ("development" !== 'production') { + errMsg = 'sourceFormat "' + sourceFormat + '" is not supported yet'; + } + throwError(errMsg); + } + // Other upstream format are all array. + var resultData = []; + for (var i = 0, len = upstream.count(); i < len; i++) { + resultData.push(upstream.getRawDataItem(i)); + } + resultData.sort(function (item0, item1) { + for (var i = 0; i < orderDefList.length; i++) { + var orderDef = orderDefList[i]; + var val0 = upstream.retrieveValueFromItem(item0, orderDef.dimIdx); + var val1 = upstream.retrieveValueFromItem(item1, orderDef.dimIdx); + if (orderDef.parser) { + val0 = orderDef.parser(val0); + val1 = orderDef.parser(val1); + } + var result = orderDef.comparator.evaluate(val0, val1); + if (result !== 0) { + return result; + } + } + return 0; + }); + return { + data: resultData + }; + } + }; + + function install$R(registers) { + registers.registerTransform(filterTransform); + registers.registerTransform(sortTransform); + } + + var DatasetModel = /** @class */function (_super) { + __extends(DatasetModel, _super); + function DatasetModel() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'dataset'; + return _this; + } + DatasetModel.prototype.init = function (option, parentModel, ecModel) { + _super.prototype.init.call(this, option, parentModel, ecModel); + this._sourceManager = new SourceManager(this); + disableTransformOptionMerge(this); + }; + DatasetModel.prototype.mergeOption = function (newOption, ecModel) { + _super.prototype.mergeOption.call(this, newOption, ecModel); + disableTransformOptionMerge(this); + }; + DatasetModel.prototype.optionUpdated = function () { + this._sourceManager.dirty(); + }; + DatasetModel.prototype.getSourceManager = function () { + return this._sourceManager; + }; + DatasetModel.type = 'dataset'; + DatasetModel.defaultOption = { + seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN + }; + return DatasetModel; + }(ComponentModel); + var DatasetView = /** @class */function (_super) { + __extends(DatasetView, _super); + function DatasetView() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.type = 'dataset'; + return _this; + } + DatasetView.type = 'dataset'; + return DatasetView; + }(ComponentView); + function install$S(registers) { + registers.registerComponentModel(DatasetModel); + registers.registerComponentView(DatasetView); + } + + var CMD$4 = PathProxy.CMD; + function aroundEqual(a, b) { + return Math.abs(a - b) < 1e-5; + } + function pathToBezierCurves(path) { + var data = path.data; + var len = path.len(); + var bezierArrayGroups = []; + var currentSubpath; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + function createNewSubpath(x, y) { + if (currentSubpath && currentSubpath.length > 2) { + bezierArrayGroups.push(currentSubpath); + } + currentSubpath = [x, y]; + } + function addLine(x0, y0, x1, y1) { + if (!(aroundEqual(x0, x1) && aroundEqual(y0, y1))) { + currentSubpath.push(x0, y0, x1, y1, x1, y1); + } + } + function addArc(startAngle, endAngle, cx, cy, rx, ry) { + var delta = Math.abs(endAngle - startAngle); + var len = Math.tan(delta / 4) * 4 / 3; + var dir = endAngle < startAngle ? -1 : 1; + var c1 = Math.cos(startAngle); + var s1 = Math.sin(startAngle); + var c2 = Math.cos(endAngle); + var s2 = Math.sin(endAngle); + var x1 = c1 * rx + cx; + var y1 = s1 * ry + cy; + var x4 = c2 * rx + cx; + var y4 = s2 * ry + cy; + var hx = rx * len * dir; + var hy = ry * len * dir; + currentSubpath.push(x1 - hx * s1, y1 + hy * c1, x4 + hx * s2, y4 - hy * c2, x4, y4); + } + var x1; + var y1; + var x2; + var y2; + for (var i = 0; i < len;) { + var cmd = data[i++]; + var isFirst = i === 1; + if (isFirst) { + xi = data[i]; + yi = data[i + 1]; + x0 = xi; + y0 = yi; + if (cmd === CMD$4.L || cmd === CMD$4.C || cmd === CMD$4.Q) { + currentSubpath = [x0, y0]; + } + } + switch (cmd) { + case CMD$4.M: + xi = x0 = data[i++]; + yi = y0 = data[i++]; + createNewSubpath(x0, y0); + break; + case CMD$4.L: + x1 = data[i++]; + y1 = data[i++]; + addLine(xi, yi, x1, y1); + xi = x1; + yi = y1; + break; + case CMD$4.C: + currentSubpath.push(data[i++], data[i++], data[i++], data[i++], xi = data[i++], yi = data[i++]); + break; + case CMD$4.Q: + x1 = data[i++]; + y1 = data[i++]; + x2 = data[i++]; + y2 = data[i++]; + currentSubpath.push(xi + 2 / 3 * (x1 - xi), yi + 2 / 3 * (y1 - yi), x2 + 2 / 3 * (x1 - x2), y2 + 2 / 3 * (y1 - y2), x2, y2); + xi = x2; + yi = y2; + break; + case CMD$4.A: + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++]; + var endAngle = data[i++] + startAngle; + i += 1; + var anticlockwise = !data[i++]; + x1 = Math.cos(startAngle) * rx + cx; + y1 = Math.sin(startAngle) * ry + cy; + if (isFirst) { + x0 = x1; + y0 = y1; + createNewSubpath(x0, y0); + } + else { + addLine(xi, yi, x1, y1); + } + xi = Math.cos(endAngle) * rx + cx; + yi = Math.sin(endAngle) * ry + cy; + var step = (anticlockwise ? -1 : 1) * Math.PI / 2; + for (var angle = startAngle; anticlockwise ? angle > endAngle : angle < endAngle; angle += step) { + var nextAngle = anticlockwise ? Math.max(angle + step, endAngle) + : Math.min(angle + step, endAngle); + addArc(angle, nextAngle, cx, cy, rx, ry); + } + break; + case CMD$4.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + x1 = x0 + data[i++]; + y1 = y0 + data[i++]; + createNewSubpath(x1, y0); + addLine(x1, y0, x1, y1); + addLine(x1, y1, x0, y1); + addLine(x0, y1, x0, y0); + addLine(x0, y0, x1, y0); + break; + case CMD$4.Z: + currentSubpath && addLine(xi, yi, x0, y0); + xi = x0; + yi = y0; + break; + } + } + if (currentSubpath && currentSubpath.length > 2) { + bezierArrayGroups.push(currentSubpath); + } + return bezierArrayGroups; + } + function adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, out, scale) { + if (aroundEqual(x0, x1) && aroundEqual(y0, y1) && aroundEqual(x2, x3) && aroundEqual(y2, y3)) { + out.push(x3, y3); + return; + } + var PIXEL_DISTANCE = 2 / scale; + var PIXEL_DISTANCE_SQR = PIXEL_DISTANCE * PIXEL_DISTANCE; + var dx = x3 - x0; + var dy = y3 - y0; + var d = Math.sqrt(dx * dx + dy * dy); + dx /= d; + dy /= d; + var dx1 = x1 - x0; + var dy1 = y1 - y0; + var dx2 = x2 - x3; + var dy2 = y2 - y3; + var cp1LenSqr = dx1 * dx1 + dy1 * dy1; + var cp2LenSqr = dx2 * dx2 + dy2 * dy2; + if (cp1LenSqr < PIXEL_DISTANCE_SQR && cp2LenSqr < PIXEL_DISTANCE_SQR) { + out.push(x3, y3); + return; + } + var projLen1 = dx * dx1 + dy * dy1; + var projLen2 = -dx * dx2 - dy * dy2; + var d1Sqr = cp1LenSqr - projLen1 * projLen1; + var d2Sqr = cp2LenSqr - projLen2 * projLen2; + if (d1Sqr < PIXEL_DISTANCE_SQR && projLen1 >= 0 + && d2Sqr < PIXEL_DISTANCE_SQR && projLen2 >= 0) { + out.push(x3, y3); + return; + } + var tmpSegX = []; + var tmpSegY = []; + cubicSubdivide(x0, x1, x2, x3, 0.5, tmpSegX); + cubicSubdivide(y0, y1, y2, y3, 0.5, tmpSegY); + adpativeBezier(tmpSegX[0], tmpSegY[0], tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], tmpSegX[3], tmpSegY[3], out, scale); + adpativeBezier(tmpSegX[4], tmpSegY[4], tmpSegX[5], tmpSegY[5], tmpSegX[6], tmpSegY[6], tmpSegX[7], tmpSegY[7], out, scale); + } + function pathToPolygons(path, scale) { + var bezierArrayGroups = pathToBezierCurves(path); + var polygons = []; + scale = scale || 1; + for (var i = 0; i < bezierArrayGroups.length; i++) { + var beziers = bezierArrayGroups[i]; + var polygon = []; + var x0 = beziers[0]; + var y0 = beziers[1]; + polygon.push(x0, y0); + for (var k = 2; k < beziers.length;) { + var x1 = beziers[k++]; + var y1 = beziers[k++]; + var x2 = beziers[k++]; + var y2 = beziers[k++]; + var x3 = beziers[k++]; + var y3 = beziers[k++]; + adpativeBezier(x0, y0, x1, y1, x2, y2, x3, y3, polygon, scale); + x0 = x3; + y0 = y3; + } + polygons.push(polygon); + } + return polygons; + } + + function getDividingGrids(dimSize, rowDim, count) { + var rowSize = dimSize[rowDim]; + var columnSize = dimSize[1 - rowDim]; + var ratio = Math.abs(rowSize / columnSize); + var rowCount = Math.ceil(Math.sqrt(ratio * count)); + var columnCount = Math.floor(count / rowCount); + if (columnCount === 0) { + columnCount = 1; + rowCount = count; + } + var grids = []; + for (var i = 0; i < rowCount; i++) { + grids.push(columnCount); + } + var currentCount = rowCount * columnCount; + var remained = count - currentCount; + if (remained > 0) { + for (var i = 0; i < remained; i++) { + grids[i % rowCount] += 1; + } + } + return grids; + } + function divideSector(sectorShape, count, outShapes) { + var r0 = sectorShape.r0; + var r = sectorShape.r; + var startAngle = sectorShape.startAngle; + var endAngle = sectorShape.endAngle; + var angle = Math.abs(endAngle - startAngle); + var arcLen = angle * r; + var deltaR = r - r0; + var isAngleRow = arcLen > Math.abs(deltaR); + var grids = getDividingGrids([arcLen, deltaR], isAngleRow ? 0 : 1, count); + var rowSize = (isAngleRow ? angle : deltaR) / grids.length; + for (var row = 0; row < grids.length; row++) { + var columnSize = (isAngleRow ? deltaR : angle) / grids[row]; + for (var column = 0; column < grids[row]; column++) { + var newShape = {}; + if (isAngleRow) { + newShape.startAngle = startAngle + rowSize * row; + newShape.endAngle = startAngle + rowSize * (row + 1); + newShape.r0 = r0 + columnSize * column; + newShape.r = r0 + columnSize * (column + 1); + } + else { + newShape.startAngle = startAngle + columnSize * column; + newShape.endAngle = startAngle + columnSize * (column + 1); + newShape.r0 = r0 + rowSize * row; + newShape.r = r0 + rowSize * (row + 1); + } + newShape.clockwise = sectorShape.clockwise; + newShape.cx = sectorShape.cx; + newShape.cy = sectorShape.cy; + outShapes.push(newShape); + } + } + } + function divideRect(rectShape, count, outShapes) { + var width = rectShape.width; + var height = rectShape.height; + var isHorizontalRow = width > height; + var grids = getDividingGrids([width, height], isHorizontalRow ? 0 : 1, count); + var rowSizeDim = isHorizontalRow ? 'width' : 'height'; + var columnSizeDim = isHorizontalRow ? 'height' : 'width'; + var rowDim = isHorizontalRow ? 'x' : 'y'; + var columnDim = isHorizontalRow ? 'y' : 'x'; + var rowSize = rectShape[rowSizeDim] / grids.length; + for (var row = 0; row < grids.length; row++) { + var columnSize = rectShape[columnSizeDim] / grids[row]; + for (var column = 0; column < grids[row]; column++) { + var newShape = {}; + newShape[rowDim] = row * rowSize; + newShape[columnDim] = column * columnSize; + newShape[rowSizeDim] = rowSize; + newShape[columnSizeDim] = columnSize; + newShape.x += rectShape.x; + newShape.y += rectShape.y; + outShapes.push(newShape); + } + } + } + function crossProduct2d$1(x1, y1, x2, y2) { + return x1 * y2 - x2 * y1; + } + function lineLineIntersect$1(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) { + var mx = a2x - a1x; + var my = a2y - a1y; + var nx = b2x - b1x; + var ny = b2y - b1y; + var nmCrossProduct = crossProduct2d$1(nx, ny, mx, my); + if (Math.abs(nmCrossProduct) < 1e-6) { + return null; + } + var b1a1x = a1x - b1x; + var b1a1y = a1y - b1y; + var p = crossProduct2d$1(b1a1x, b1a1y, nx, ny) / nmCrossProduct; + if (p < 0 || p > 1) { + return null; + } + return new Point(p * mx + a1x, p * my + a1y); + } + function projPtOnLine(pt, lineA, lineB) { + var dir = new Point(); + Point.sub(dir, lineB, lineA); + dir.normalize(); + var dir2 = new Point(); + Point.sub(dir2, pt, lineA); + var len = dir2.dot(dir); + return len; + } + function addToPoly(poly, pt) { + var last = poly[poly.length - 1]; + if (last && last[0] === pt[0] && last[1] === pt[1]) { + return; + } + poly.push(pt); + } + function splitPolygonByLine(points, lineA, lineB) { + var len = points.length; + var intersections = []; + for (var i = 0; i < len; i++) { + var p0 = points[i]; + var p1 = points[(i + 1) % len]; + var intersectionPt = lineLineIntersect$1(p0[0], p0[1], p1[0], p1[1], lineA.x, lineA.y, lineB.x, lineB.y); + if (intersectionPt) { + intersections.push({ + projPt: projPtOnLine(intersectionPt, lineA, lineB), + pt: intersectionPt, + idx: i + }); + } + } + if (intersections.length < 2) { + return [{ points: points }, { points: points }]; + } + intersections.sort(function (a, b) { + return a.projPt - b.projPt; + }); + var splitPt0 = intersections[0]; + var splitPt1 = intersections[intersections.length - 1]; + if (splitPt1.idx < splitPt0.idx) { + var tmp = splitPt0; + splitPt0 = splitPt1; + splitPt1 = tmp; + } + var splitPt0Arr = [splitPt0.pt.x, splitPt0.pt.y]; + var splitPt1Arr = [splitPt1.pt.x, splitPt1.pt.y]; + var newPolyA = [splitPt0Arr]; + var newPolyB = [splitPt1Arr]; + for (var i = splitPt0.idx + 1; i <= splitPt1.idx; i++) { + addToPoly(newPolyA, points[i].slice()); + } + addToPoly(newPolyA, splitPt1Arr); + addToPoly(newPolyA, splitPt0Arr); + for (var i = splitPt1.idx + 1; i <= splitPt0.idx + len; i++) { + addToPoly(newPolyB, points[i % len].slice()); + } + addToPoly(newPolyB, splitPt0Arr); + addToPoly(newPolyB, splitPt1Arr); + return [{ + points: newPolyA + }, { + points: newPolyB + }]; + } + function binaryDividePolygon(polygonShape) { + var points = polygonShape.points; + var min = []; + var max = []; + fromPoints(points, min, max); + var boundingRect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]); + var width = boundingRect.width; + var height = boundingRect.height; + var x = boundingRect.x; + var y = boundingRect.y; + var pt0 = new Point(); + var pt1 = new Point(); + if (width > height) { + pt0.x = pt1.x = x + width / 2; + pt0.y = y; + pt1.y = y + height; + } + else { + pt0.y = pt1.y = y + height / 2; + pt0.x = x; + pt1.x = x + width; + } + return splitPolygonByLine(points, pt0, pt1); + } + function binaryDivideRecursive(divider, shape, count, out) { + if (count === 1) { + out.push(shape); + } + else { + var mid = Math.floor(count / 2); + var sub = divider(shape); + binaryDivideRecursive(divider, sub[0], mid, out); + binaryDivideRecursive(divider, sub[1], count - mid, out); + } + return out; + } + function clone$4(path, count) { + var paths = []; + for (var i = 0; i < count; i++) { + paths.push(clonePath(path)); + } + return paths; + } + function copyPathProps(source, target) { + target.setStyle(source.style); + target.z = source.z; + target.z2 = source.z2; + target.zlevel = source.zlevel; + } + function polygonConvert(points) { + var out = []; + for (var i = 0; i < points.length;) { + out.push([points[i++], points[i++]]); + } + return out; + } + function split(path, count) { + var outShapes = []; + var shape = path.shape; + var OutShapeCtor; + switch (path.type) { + case 'rect': + divideRect(shape, count, outShapes); + OutShapeCtor = Rect; + break; + case 'sector': + divideSector(shape, count, outShapes); + OutShapeCtor = Sector; + break; + case 'circle': + divideSector({ + r0: 0, r: shape.r, startAngle: 0, endAngle: Math.PI * 2, + cx: shape.cx, cy: shape.cy + }, count, outShapes); + OutShapeCtor = Sector; + break; + default: + var m = path.getComputedTransform(); + var scale = m ? Math.sqrt(Math.max(m[0] * m[0] + m[1] * m[1], m[2] * m[2] + m[3] * m[3])) : 1; + var polygons = map(pathToPolygons(path.getUpdatedPathProxy(), scale), function (poly) { return polygonConvert(poly); }); + var polygonCount = polygons.length; + if (polygonCount === 0) { + binaryDivideRecursive(binaryDividePolygon, { + points: polygons[0] + }, count, outShapes); + } + else if (polygonCount === count) { + for (var i = 0; i < polygonCount; i++) { + outShapes.push({ + points: polygons[i] + }); + } + } + else { + var totalArea_1 = 0; + var items = map(polygons, function (poly) { + var min = []; + var max = []; + fromPoints(poly, min, max); + var area = (max[1] - min[1]) * (max[0] - min[0]); + totalArea_1 += area; + return { poly: poly, area: area }; + }); + items.sort(function (a, b) { return b.area - a.area; }); + var left = count; + for (var i = 0; i < polygonCount; i++) { + var item = items[i]; + if (left <= 0) { + break; + } + var selfCount = i === polygonCount - 1 + ? left + : Math.ceil(item.area / totalArea_1 * count); + if (selfCount < 0) { + continue; + } + binaryDivideRecursive(binaryDividePolygon, { + points: item.poly + }, selfCount, outShapes); + left -= selfCount; + } + } + OutShapeCtor = Polygon; + break; + } + if (!OutShapeCtor) { + return clone$4(path, count); + } + var out = []; + for (var i = 0; i < outShapes.length; i++) { + var subPath = new OutShapeCtor(); + subPath.setShape(outShapes[i]); + copyPathProps(path, subPath); + out.push(subPath); + } + return out; + } + + function alignSubpath(subpath1, subpath2) { + var len1 = subpath1.length; + var len2 = subpath2.length; + if (len1 === len2) { + return [subpath1, subpath2]; + } + var tmpSegX = []; + var tmpSegY = []; + var shorterPath = len1 < len2 ? subpath1 : subpath2; + var shorterLen = Math.min(len1, len2); + var diff = Math.abs(len2 - len1) / 6; + var shorterBezierCount = (shorterLen - 2) / 6; + var eachCurveSubDivCount = Math.ceil(diff / shorterBezierCount) + 1; + var newSubpath = [shorterPath[0], shorterPath[1]]; + var remained = diff; + for (var i = 2; i < shorterLen;) { + var x0 = shorterPath[i - 2]; + var y0 = shorterPath[i - 1]; + var x1 = shorterPath[i++]; + var y1 = shorterPath[i++]; + var x2 = shorterPath[i++]; + var y2 = shorterPath[i++]; + var x3 = shorterPath[i++]; + var y3 = shorterPath[i++]; + if (remained <= 0) { + newSubpath.push(x1, y1, x2, y2, x3, y3); + continue; + } + var actualSubDivCount = Math.min(remained, eachCurveSubDivCount - 1) + 1; + for (var k = 1; k <= actualSubDivCount; k++) { + var p = k / actualSubDivCount; + cubicSubdivide(x0, x1, x2, x3, p, tmpSegX); + cubicSubdivide(y0, y1, y2, y3, p, tmpSegY); + x0 = tmpSegX[3]; + y0 = tmpSegY[3]; + newSubpath.push(tmpSegX[1], tmpSegY[1], tmpSegX[2], tmpSegY[2], x0, y0); + x1 = tmpSegX[5]; + y1 = tmpSegY[5]; + x2 = tmpSegX[6]; + y2 = tmpSegY[6]; + } + remained -= actualSubDivCount - 1; + } + return shorterPath === subpath1 ? [newSubpath, subpath2] : [subpath1, newSubpath]; + } + function createSubpath(lastSubpathSubpath, otherSubpath) { + var len = lastSubpathSubpath.length; + var lastX = lastSubpathSubpath[len - 2]; + var lastY = lastSubpathSubpath[len - 1]; + var newSubpath = []; + for (var i = 0; i < otherSubpath.length;) { + newSubpath[i++] = lastX; + newSubpath[i++] = lastY; + } + return newSubpath; + } + function alignBezierCurves(array1, array2) { + var _a; + var lastSubpath1; + var lastSubpath2; + var newArray1 = []; + var newArray2 = []; + for (var i = 0; i < Math.max(array1.length, array2.length); i++) { + var subpath1 = array1[i]; + var subpath2 = array2[i]; + var newSubpath1 = void 0; + var newSubpath2 = void 0; + if (!subpath1) { + newSubpath1 = createSubpath(lastSubpath1 || subpath2, subpath2); + newSubpath2 = subpath2; + } + else if (!subpath2) { + newSubpath2 = createSubpath(lastSubpath2 || subpath1, subpath1); + newSubpath1 = subpath1; + } + else { + _a = alignSubpath(subpath1, subpath2), newSubpath1 = _a[0], newSubpath2 = _a[1]; + lastSubpath1 = newSubpath1; + lastSubpath2 = newSubpath2; + } + newArray1.push(newSubpath1); + newArray2.push(newSubpath2); + } + return [newArray1, newArray2]; + } + function centroid$1(array) { + var signedArea = 0; + var cx = 0; + var cy = 0; + var len = array.length; + for (var i = 0, j = len - 2; i < len; j = i, i += 2) { + var x0 = array[j]; + var y0 = array[j + 1]; + var x1 = array[i]; + var y1 = array[i + 1]; + var a = x0 * y1 - x1 * y0; + signedArea += a; + cx += (x0 + x1) * a; + cy += (y0 + y1) * a; + } + if (signedArea === 0) { + return [array[0] || 0, array[1] || 0]; + } + return [cx / signedArea / 3, cy / signedArea / 3, signedArea]; + } + function findBestRingOffset(fromSubBeziers, toSubBeziers, fromCp, toCp) { + var bezierCount = (fromSubBeziers.length - 2) / 6; + var bestScore = Infinity; + var bestOffset = 0; + var len = fromSubBeziers.length; + var len2 = len - 2; + for (var offset = 0; offset < bezierCount; offset++) { + var cursorOffset = offset * 6; + var score = 0; + for (var k = 0; k < len; k += 2) { + var idx = k === 0 ? cursorOffset : ((cursorOffset + k - 2) % len2 + 2); + var x0 = fromSubBeziers[idx] - fromCp[0]; + var y0 = fromSubBeziers[idx + 1] - fromCp[1]; + var x1 = toSubBeziers[k] - toCp[0]; + var y1 = toSubBeziers[k + 1] - toCp[1]; + var dx = x1 - x0; + var dy = y1 - y0; + score += dx * dx + dy * dy; + } + if (score < bestScore) { + bestScore = score; + bestOffset = offset; + } + } + return bestOffset; + } + function reverse(array) { + var newArr = []; + var len = array.length; + for (var i = 0; i < len; i += 2) { + newArr[i] = array[len - i - 2]; + newArr[i + 1] = array[len - i - 1]; + } + return newArr; + } + function findBestMorphingRotation(fromArr, toArr, searchAngleIteration, searchAngleRange) { + var result = []; + var fromNeedsReverse; + for (var i = 0; i < fromArr.length; i++) { + var fromSubpathBezier = fromArr[i]; + var toSubpathBezier = toArr[i]; + var fromCp = centroid$1(fromSubpathBezier); + var toCp = centroid$1(toSubpathBezier); + if (fromNeedsReverse == null) { + fromNeedsReverse = fromCp[2] < 0 !== toCp[2] < 0; + } + var newFromSubpathBezier = []; + var newToSubpathBezier = []; + var bestAngle = 0; + var bestScore = Infinity; + var tmpArr = []; + var len = fromSubpathBezier.length; + if (fromNeedsReverse) { + fromSubpathBezier = reverse(fromSubpathBezier); + } + var offset = findBestRingOffset(fromSubpathBezier, toSubpathBezier, fromCp, toCp) * 6; + var len2 = len - 2; + for (var k = 0; k < len2; k += 2) { + var idx = (offset + k) % len2 + 2; + newFromSubpathBezier[k + 2] = fromSubpathBezier[idx] - fromCp[0]; + newFromSubpathBezier[k + 3] = fromSubpathBezier[idx + 1] - fromCp[1]; + } + newFromSubpathBezier[0] = fromSubpathBezier[offset] - fromCp[0]; + newFromSubpathBezier[1] = fromSubpathBezier[offset + 1] - fromCp[1]; + if (searchAngleIteration > 0) { + var step = searchAngleRange / searchAngleIteration; + for (var angle = -searchAngleRange / 2; angle <= searchAngleRange / 2; angle += step) { + var sa = Math.sin(angle); + var ca = Math.cos(angle); + var score = 0; + for (var k = 0; k < fromSubpathBezier.length; k += 2) { + var x0 = newFromSubpathBezier[k]; + var y0 = newFromSubpathBezier[k + 1]; + var x1 = toSubpathBezier[k] - toCp[0]; + var y1 = toSubpathBezier[k + 1] - toCp[1]; + var newX1 = x1 * ca - y1 * sa; + var newY1 = x1 * sa + y1 * ca; + tmpArr[k] = newX1; + tmpArr[k + 1] = newY1; + var dx = newX1 - x0; + var dy = newY1 - y0; + score += dx * dx + dy * dy; + } + if (score < bestScore) { + bestScore = score; + bestAngle = angle; + for (var m = 0; m < tmpArr.length; m++) { + newToSubpathBezier[m] = tmpArr[m]; + } + } + } + } + else { + for (var i_1 = 0; i_1 < len; i_1 += 2) { + newToSubpathBezier[i_1] = toSubpathBezier[i_1] - toCp[0]; + newToSubpathBezier[i_1 + 1] = toSubpathBezier[i_1 + 1] - toCp[1]; + } + } + result.push({ + from: newFromSubpathBezier, + to: newToSubpathBezier, + fromCp: fromCp, + toCp: toCp, + rotation: -bestAngle + }); + } + return result; + } + function isCombineMorphing(path) { + return path.__isCombineMorphing; + } + var SAVED_METHOD_PREFIX = '__mOriginal_'; + function saveAndModifyMethod(obj, methodName, modifiers) { + var savedMethodName = SAVED_METHOD_PREFIX + methodName; + var originalMethod = obj[savedMethodName] || obj[methodName]; + if (!obj[savedMethodName]) { + obj[savedMethodName] = obj[methodName]; + } + var replace = modifiers.replace; + var after = modifiers.after; + var before = modifiers.before; + obj[methodName] = function () { + var args = arguments; + var res; + before && before.apply(this, args); + if (replace) { + res = replace.apply(this, args); + } + else { + res = originalMethod.apply(this, args); + } + after && after.apply(this, args); + return res; + }; + } + function restoreMethod(obj, methodName) { + var savedMethodName = SAVED_METHOD_PREFIX + methodName; + if (obj[savedMethodName]) { + obj[methodName] = obj[savedMethodName]; + obj[savedMethodName] = null; + } + } + function applyTransformOnBeziers(bezierCurves, mm) { + for (var i = 0; i < bezierCurves.length; i++) { + var subBeziers = bezierCurves[i]; + for (var k = 0; k < subBeziers.length;) { + var x = subBeziers[k]; + var y = subBeziers[k + 1]; + subBeziers[k++] = mm[0] * x + mm[2] * y + mm[4]; + subBeziers[k++] = mm[1] * x + mm[3] * y + mm[5]; + } + } + } + function prepareMorphPath(fromPath, toPath) { + var fromPathProxy = fromPath.getUpdatedPathProxy(); + var toPathProxy = toPath.getUpdatedPathProxy(); + var _a = alignBezierCurves(pathToBezierCurves(fromPathProxy), pathToBezierCurves(toPathProxy)), fromBezierCurves = _a[0], toBezierCurves = _a[1]; + var fromPathTransform = fromPath.getComputedTransform(); + var toPathTransform = toPath.getComputedTransform(); + function updateIdentityTransform() { + this.transform = null; + } + fromPathTransform && applyTransformOnBeziers(fromBezierCurves, fromPathTransform); + toPathTransform && applyTransformOnBeziers(toBezierCurves, toPathTransform); + saveAndModifyMethod(toPath, 'updateTransform', { replace: updateIdentityTransform }); + toPath.transform = null; + var morphingData = findBestMorphingRotation(fromBezierCurves, toBezierCurves, 10, Math.PI); + var tmpArr = []; + saveAndModifyMethod(toPath, 'buildPath', { replace: function (path) { + var t = toPath.__morphT; + var onet = 1 - t; + var newCp = []; + for (var i = 0; i < morphingData.length; i++) { + var item = morphingData[i]; + var from = item.from; + var to = item.to; + var angle = item.rotation * t; + var fromCp = item.fromCp; + var toCp = item.toCp; + var sa = Math.sin(angle); + var ca = Math.cos(angle); + lerp(newCp, fromCp, toCp, t); + for (var m = 0; m < from.length; m += 2) { + var x0_1 = from[m]; + var y0_1 = from[m + 1]; + var x1 = to[m]; + var y1 = to[m + 1]; + var x = x0_1 * onet + x1 * t; + var y = y0_1 * onet + y1 * t; + tmpArr[m] = (x * ca - y * sa) + newCp[0]; + tmpArr[m + 1] = (x * sa + y * ca) + newCp[1]; + } + var x0 = tmpArr[0]; + var y0 = tmpArr[1]; + path.moveTo(x0, y0); + for (var m = 2; m < from.length;) { + var x1 = tmpArr[m++]; + var y1 = tmpArr[m++]; + var x2 = tmpArr[m++]; + var y2 = tmpArr[m++]; + var x3 = tmpArr[m++]; + var y3 = tmpArr[m++]; + if (x0 === x1 && y0 === y1 && x2 === x3 && y2 === y3) { + path.lineTo(x3, y3); + } + else { + path.bezierCurveTo(x1, y1, x2, y2, x3, y3); + } + x0 = x3; + y0 = y3; + } + } + } }); + } + function morphPath(fromPath, toPath, animationOpts) { + if (!fromPath || !toPath) { + return toPath; + } + var oldDone = animationOpts.done; + var oldDuring = animationOpts.during; + prepareMorphPath(fromPath, toPath); + toPath.__morphT = 0; + function restoreToPath() { + restoreMethod(toPath, 'buildPath'); + restoreMethod(toPath, 'updateTransform'); + toPath.__morphT = -1; + toPath.createPathProxy(); + toPath.dirtyShape(); + } + toPath.animateTo({ + __morphT: 1 + }, defaults({ + during: function (p) { + toPath.dirtyShape(); + oldDuring && oldDuring(p); + }, + done: function () { + restoreToPath(); + oldDone && oldDone(); + } + }, animationOpts)); + return toPath; + } + function hilbert(x, y, minX, minY, maxX, maxY) { + var bits = 16; + x = (maxX === minX) ? 0 : Math.round(32767 * (x - minX) / (maxX - minX)); + y = (maxY === minY) ? 0 : Math.round(32767 * (y - minY) / (maxY - minY)); + var d = 0; + var tmp; + for (var s = (1 << bits) / 2; s > 0; s /= 2) { + var rx = 0; + var ry = 0; + if ((x & s) > 0) { + rx = 1; + } + if ((y & s) > 0) { + ry = 1; + } + d += s * s * ((3 * rx) ^ ry); + if (ry === 0) { + if (rx === 1) { + x = s - 1 - x; + y = s - 1 - y; + } + tmp = x; + x = y; + y = tmp; + } + } + return d; + } + function sortPaths(pathList) { + var xMin = Infinity; + var yMin = Infinity; + var xMax = -Infinity; + var yMax = -Infinity; + var cps = map(pathList, function (path) { + var rect = path.getBoundingRect(); + var m = path.getComputedTransform(); + var x = rect.x + rect.width / 2 + (m ? m[4] : 0); + var y = rect.y + rect.height / 2 + (m ? m[5] : 0); + xMin = Math.min(x, xMin); + yMin = Math.min(y, yMin); + xMax = Math.max(x, xMax); + yMax = Math.max(y, yMax); + return [x, y]; + }); + var items = map(cps, function (cp, idx) { + return { + cp: cp, + z: hilbert(cp[0], cp[1], xMin, yMin, xMax, yMax), + path: pathList[idx] + }; + }); + return items.sort(function (a, b) { return a.z - b.z; }).map(function (item) { return item.path; }); + } + function defaultDividePath(param) { + return split(param.path, param.count); + } + function createEmptyReturn() { + return { + fromIndividuals: [], + toIndividuals: [], + count: 0 + }; + } + function combineMorph(fromList, toPath, animationOpts) { + var fromPathList = []; + function addFromPath(fromList) { + for (var i = 0; i < fromList.length; i++) { + var from = fromList[i]; + if (isCombineMorphing(from)) { + addFromPath(from.childrenRef()); + } + else if (from instanceof Path) { + fromPathList.push(from); + } + } + } + addFromPath(fromList); + var separateCount = fromPathList.length; + if (!separateCount) { + return createEmptyReturn(); + } + var dividePath = animationOpts.dividePath || defaultDividePath; + var toSubPathList = dividePath({ + path: toPath, count: separateCount + }); + if (toSubPathList.length !== separateCount) { + console.error('Invalid morphing: unmatched splitted path'); + return createEmptyReturn(); + } + fromPathList = sortPaths(fromPathList); + toSubPathList = sortPaths(toSubPathList); + var oldDone = animationOpts.done; + var oldDuring = animationOpts.during; + var individualDelay = animationOpts.individualDelay; + var identityTransform = new Transformable(); + for (var i = 0; i < separateCount; i++) { + var from = fromPathList[i]; + var to = toSubPathList[i]; + to.parent = toPath; + to.copyTransform(identityTransform); + if (!individualDelay) { + prepareMorphPath(from, to); + } + } + toPath.__isCombineMorphing = true; + toPath.childrenRef = function () { + return toSubPathList; + }; + function addToSubPathListToZr(zr) { + for (var i = 0; i < toSubPathList.length; i++) { + toSubPathList[i].addSelfToZr(zr); + } + } + saveAndModifyMethod(toPath, 'addSelfToZr', { + after: function (zr) { + addToSubPathListToZr(zr); + } + }); + saveAndModifyMethod(toPath, 'removeSelfFromZr', { + after: function (zr) { + for (var i = 0; i < toSubPathList.length; i++) { + toSubPathList[i].removeSelfFromZr(zr); + } + } + }); + function restoreToPath() { + toPath.__isCombineMorphing = false; + toPath.__morphT = -1; + toPath.childrenRef = null; + restoreMethod(toPath, 'addSelfToZr'); + restoreMethod(toPath, 'removeSelfFromZr'); + } + var toLen = toSubPathList.length; + if (individualDelay) { + var animating_1 = toLen; + var eachDone = function () { + animating_1--; + if (animating_1 === 0) { + restoreToPath(); + oldDone && oldDone(); + } + }; + for (var i = 0; i < toLen; i++) { + var indivdualAnimationOpts = individualDelay ? defaults({ + delay: (animationOpts.delay || 0) + individualDelay(i, toLen, fromPathList[i], toSubPathList[i]), + done: eachDone + }, animationOpts) : animationOpts; + morphPath(fromPathList[i], toSubPathList[i], indivdualAnimationOpts); + } + } + else { + toPath.__morphT = 0; + toPath.animateTo({ + __morphT: 1 + }, defaults({ + during: function (p) { + for (var i = 0; i < toLen; i++) { + var child = toSubPathList[i]; + child.__morphT = toPath.__morphT; + child.dirtyShape(); + } + oldDuring && oldDuring(p); + }, + done: function () { + restoreToPath(); + for (var i = 0; i < fromList.length; i++) { + restoreMethod(fromList[i], 'updateTransform'); + } + oldDone && oldDone(); + } + }, animationOpts)); + } + if (toPath.__zr) { + addToSubPathListToZr(toPath.__zr); + } + return { + fromIndividuals: fromPathList, + toIndividuals: toSubPathList, + count: toLen + }; + } + function separateMorph(fromPath, toPathList, animationOpts) { + var toLen = toPathList.length; + var fromPathList = []; + var dividePath = animationOpts.dividePath || defaultDividePath; + function addFromPath(fromList) { + for (var i = 0; i < fromList.length; i++) { + var from = fromList[i]; + if (isCombineMorphing(from)) { + addFromPath(from.childrenRef()); + } + else if (from instanceof Path) { + fromPathList.push(from); + } + } + } + if (isCombineMorphing(fromPath)) { + addFromPath(fromPath.childrenRef()); + var fromLen = fromPathList.length; + if (fromLen < toLen) { + var k = 0; + for (var i = fromLen; i < toLen; i++) { + fromPathList.push(clonePath(fromPathList[k++ % fromLen])); + } + } + fromPathList.length = toLen; + } + else { + fromPathList = dividePath({ path: fromPath, count: toLen }); + var fromPathTransform = fromPath.getComputedTransform(); + for (var i = 0; i < fromPathList.length; i++) { + fromPathList[i].setLocalTransform(fromPathTransform); + } + if (fromPathList.length !== toLen) { + console.error('Invalid morphing: unmatched splitted path'); + return createEmptyReturn(); + } + } + fromPathList = sortPaths(fromPathList); + toPathList = sortPaths(toPathList); + var individualDelay = animationOpts.individualDelay; + for (var i = 0; i < toLen; i++) { + var indivdualAnimationOpts = individualDelay ? defaults({ + delay: (animationOpts.delay || 0) + individualDelay(i, toLen, fromPathList[i], toPathList[i]) + }, animationOpts) : animationOpts; + morphPath(fromPathList[i], toPathList[i], indivdualAnimationOpts); + } + return { + fromIndividuals: fromPathList, + toIndividuals: toPathList, + count: toPathList.length + }; + } + + function isMultiple(elements) { + return isArray(elements[0]); + } + function prepareMorphBatches(one, many) { + var batches = []; + var batchCount = one.length; + for (var i = 0; i < batchCount; i++) { + batches.push({ + one: one[i], + many: [] + }); + } + for (var i = 0; i < many.length; i++) { + var len = many[i].length; + var k = void 0; + for (k = 0; k < len; k++) { + batches[k % batchCount].many.push(many[i][k]); + } + } + var off = 0; + // If one has more paths than each one of many. average them. + for (var i = batchCount - 1; i >= 0; i--) { + if (!batches[i].many.length) { + var moveFrom = batches[off].many; + if (moveFrom.length <= 1) { + // Not enough + // Start from the first one. + if (off) { + off = 0; + } else { + return batches; + } + } + var len = moveFrom.length; + var mid = Math.ceil(len / 2); + batches[i].many = moveFrom.slice(mid, len); + batches[off].many = moveFrom.slice(0, mid); + off++; + } + } + return batches; + } + var pathDividers = { + clone: function (params) { + var ret = []; + // Fitting the alpha + var approxOpacity = 1 - Math.pow(1 - params.path.style.opacity, 1 / params.count); + for (var i = 0; i < params.count; i++) { + var cloned = clonePath(params.path); + cloned.setStyle('opacity', approxOpacity); + ret.push(cloned); + } + return ret; + }, + // Use the default divider + split: null + }; + function applyMorphAnimation(from, to, divideShape, seriesModel, dataIndex, animateOtherProps) { + if (!from.length || !to.length) { + return; + } + var updateAnimationCfg = getAnimationConfig('update', seriesModel, dataIndex); + if (!(updateAnimationCfg && updateAnimationCfg.duration > 0)) { + return; + } + var animationDelay = seriesModel.getModel('universalTransition').get('delay'); + var animationCfg = Object.assign({ + // Need to setToFinal so the further calculation based on the style can be correct. + // Like emphasis color. + setToFinal: true + }, updateAnimationCfg); + var many; + var one; + if (isMultiple(from)) { + // manyToOne + many = from; + one = to; + } + if (isMultiple(to)) { + // oneToMany + many = to; + one = from; + } + function morphOneBatch(batch, fromIsMany, animateIndex, animateCount, forceManyOne) { + var batchMany = batch.many; + var batchOne = batch.one; + if (batchMany.length === 1 && !forceManyOne) { + // Is one to one + var batchFrom = fromIsMany ? batchMany[0] : batchOne; + var batchTo = fromIsMany ? batchOne : batchMany[0]; + if (isCombineMorphing(batchFrom)) { + // Keep doing combine animation. + morphOneBatch({ + many: [batchFrom], + one: batchTo + }, true, animateIndex, animateCount, true); + } else { + var individualAnimationCfg = animationDelay ? defaults({ + delay: animationDelay(animateIndex, animateCount) + }, animationCfg) : animationCfg; + morphPath(batchFrom, batchTo, individualAnimationCfg); + animateOtherProps(batchFrom, batchTo, batchFrom, batchTo, individualAnimationCfg); + } + } else { + var separateAnimationCfg = defaults({ + dividePath: pathDividers[divideShape], + individualDelay: animationDelay && function (idx, count, fromPath, toPath) { + return animationDelay(idx + animateIndex, animateCount); + } + }, animationCfg); + var _a = fromIsMany ? combineMorph(batchMany, batchOne, separateAnimationCfg) : separateMorph(batchOne, batchMany, separateAnimationCfg), + fromIndividuals = _a.fromIndividuals, + toIndividuals = _a.toIndividuals; + var count = fromIndividuals.length; + for (var k = 0; k < count; k++) { + var individualAnimationCfg = animationDelay ? defaults({ + delay: animationDelay(k, count) + }, animationCfg) : animationCfg; + animateOtherProps(fromIndividuals[k], toIndividuals[k], fromIsMany ? batchMany[k] : batch.one, fromIsMany ? batch.one : batchMany[k], individualAnimationCfg); + } + } + } + var fromIsMany = many ? many === from + // Is one to one. If the path number not match. also needs do merge and separate morphing. + : from.length > to.length; + var morphBatches = many ? prepareMorphBatches(one, many) : prepareMorphBatches(fromIsMany ? to : from, [fromIsMany ? from : to]); + var animateCount = 0; + for (var i = 0; i < morphBatches.length; i++) { + animateCount += morphBatches[i].many.length; + } + var animateIndex = 0; + for (var i = 0; i < morphBatches.length; i++) { + morphOneBatch(morphBatches[i], fromIsMany, animateIndex, animateCount); + animateIndex += morphBatches[i].many.length; + } + } + function getPathList(elements) { + if (!elements) { + return []; + } + if (isArray(elements)) { + var pathList_1 = []; + for (var i = 0; i < elements.length; i++) { + pathList_1.push(getPathList(elements[i])); + } + return pathList_1; + } + var pathList = []; + elements.traverse(function (el) { + if (el instanceof Path && !el.disableMorphing && !el.invisible && !el.ignore) { + pathList.push(el); + } + }); + return pathList; + } + + var DATA_COUNT_THRESHOLD = 1e4; + var TRANSITION_NONE = 0; + var TRANSITION_P2C = 1; + var TRANSITION_C2P = 2; + var getUniversalTransitionGlobalStore = makeInner(); + function getDimension(data, visualDimension) { + var dimensions = data.dimensions; + for (var i = 0; i < dimensions.length; i++) { + var dimInfo = data.getDimensionInfo(dimensions[i]); + if (dimInfo && dimInfo.otherDims[visualDimension] === 0) { + return dimensions[i]; + } + } + } + // get value by dimension. (only get value of itemGroupId or childGroupId, so convert it to string) + function getValueByDimension(data, dataIndex, dimension) { + var dimInfo = data.getDimensionInfo(dimension); + var dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta; + if (dimInfo) { + var value = data.get(dimInfo.name, dataIndex); + if (dimOrdinalMeta) { + return dimOrdinalMeta.categories[value] || value + ''; + } + return value + ''; + } + } + function getGroupId(data, dataIndex, dataGroupId, isChild) { + // try to get groupId from encode + var visualDimension = isChild ? 'itemChildGroupId' : 'itemGroupId'; + var groupIdDim = getDimension(data, visualDimension); + if (groupIdDim) { + var groupId = getValueByDimension(data, dataIndex, groupIdDim); + return groupId; + } + // try to get groupId from raw data item + var rawDataItem = data.getRawDataItem(dataIndex); + var property = isChild ? 'childGroupId' : 'groupId'; + if (rawDataItem && rawDataItem[property]) { + return rawDataItem[property] + ''; + } + // fallback + if (isChild) { + return; + } + // try to use series.dataGroupId as groupId, otherwise use dataItem's id as groupId + return dataGroupId || data.getId(dataIndex); + } + // flatten all data items from different serieses into one arrary + function flattenDataDiffItems(list) { + var items = []; + each(list, function (seriesInfo) { + var data = seriesInfo.data; + var dataGroupId = seriesInfo.dataGroupId; + if (data.count() > DATA_COUNT_THRESHOLD) { + if ("development" !== 'production') { + warn('Universal transition is disabled on large data > 10k.'); + } + return; + } + var indices = data.getIndices(); + for (var dataIndex = 0; dataIndex < indices.length; dataIndex++) { + items.push({ + data: data, + groupId: getGroupId(data, dataIndex, dataGroupId, false), + childGroupId: getGroupId(data, dataIndex, dataGroupId, true), + divide: seriesInfo.divide, + dataIndex: dataIndex + }); + } + }); + return items; + } + function fadeInElement(newEl, newSeries, newIndex) { + newEl.traverse(function (el) { + if (el instanceof Path) { + // TODO use fade in animation for target element. + initProps(el, { + style: { + opacity: 0 + } + }, newSeries, { + dataIndex: newIndex, + isFrom: true + }); + } + }); + } + function removeEl$1(el) { + if (el.parent) { + // Bake parent transform to element. + // So it can still have proper transform to transition after it's removed. + var computedTransform = el.getComputedTransform(); + el.setLocalTransform(computedTransform); + el.parent.remove(el); + } + } + function stopAnimation(el) { + el.stopAnimation(); + if (el.isGroup) { + el.traverse(function (child) { + child.stopAnimation(); + }); + } + } + function animateElementStyles(el, dataIndex, seriesModel) { + var animationConfig = getAnimationConfig('update', seriesModel, dataIndex); + animationConfig && el.traverse(function (child) { + if (child instanceof Displayable) { + var oldStyle = getOldStyle(child); + if (oldStyle) { + child.animateFrom({ + style: oldStyle + }, animationConfig); + } + } + }); + } + function isAllIdSame(oldDiffItems, newDiffItems) { + var len = oldDiffItems.length; + if (len !== newDiffItems.length) { + return false; + } + for (var i = 0; i < len; i++) { + var oldItem = oldDiffItems[i]; + var newItem = newDiffItems[i]; + if (oldItem.data.getId(oldItem.dataIndex) !== newItem.data.getId(newItem.dataIndex)) { + return false; + } + } + return true; + } + function transitionBetween(oldList, newList, api) { + var oldDiffItems = flattenDataDiffItems(oldList); + var newDiffItems = flattenDataDiffItems(newList); + function updateMorphingPathProps(from, to, rawFrom, rawTo, animationCfg) { + if (rawFrom || from) { + to.animateFrom({ + style: rawFrom && rawFrom !== from + // dividingMethod like clone may override the style(opacity) + // So extend it to raw style. + ? extend(extend({}, rawFrom.style), from.style) : from.style + }, animationCfg); + } + } + var hasMorphAnimation = false; + /** + * With groupId and childGroupId, we can build parent-child relationships between dataItems. + * However, we should mind the parent-child "direction" between old and new options. + * + * For example, suppose we have two dataItems from two series.data: + * + * dataA: [ dataB: [ + * { { + * value: 5, value: 3, + * groupId: 'creatures', groupId: 'animals', + * childGroupId: 'animals' childGroupId: 'dogs' + * }, }, + * ... ... + * ] ] + * + * where dataA is belong to optionA and dataB is belong to optionB. + * + * When we `setOption(optionB)` from optionA, we choose childGroupId of dataItemA and groupId of + * dataItemB as keys so the two keys are matched (both are 'animals'), then universalTransition + * will work. This derection is "parent -> child". + * + * If we `setOption(optionA)` from optionB, we also choose groupId of dataItemB and childGroupId + * of dataItemA as keys and universalTransition will work. This derection is "child -> parent". + * + * If there is no childGroupId specified, which means no multiLevelDrillDown/Up is needed and no + * parent-child relationship exists. This direction is "none". + * + * So we need to know whether to use groupId or childGroupId as the key when we call the keyGetter + * functions. Thus, we need to decide the direction first. + * + * The rule is: + * + * if (all childGroupIds in oldDiffItems and all groupIds in newDiffItems have common value) { + * direction = 'parent -> child'; + * } else if (all groupIds in oldDiffItems and all childGroupIds in newDiffItems have common value) { + * direction = 'child -> parent'; + * } else { + * direction = 'none'; + * } + */ + var direction = TRANSITION_NONE; + // find all groupIds and childGroupIds from oldDiffItems + var oldGroupIds = createHashMap(); + var oldChildGroupIds = createHashMap(); + oldDiffItems.forEach(function (item) { + item.groupId && oldGroupIds.set(item.groupId, true); + item.childGroupId && oldChildGroupIds.set(item.childGroupId, true); + }); + // traverse newDiffItems and decide the direction according to the rule + for (var i = 0; i < newDiffItems.length; i++) { + var newGroupId = newDiffItems[i].groupId; + if (oldChildGroupIds.get(newGroupId)) { + direction = TRANSITION_P2C; + break; + } + var newChildGroupId = newDiffItems[i].childGroupId; + if (newChildGroupId && oldGroupIds.get(newChildGroupId)) { + direction = TRANSITION_C2P; + break; + } + } + function createKeyGetter(isOld, onlyGetId) { + return function (diffItem) { + var data = diffItem.data; + var dataIndex = diffItem.dataIndex; + // TODO if specified dim + if (onlyGetId) { + return data.getId(dataIndex); + } + if (isOld) { + return direction === TRANSITION_P2C ? diffItem.childGroupId : diffItem.groupId; + } else { + return direction === TRANSITION_C2P ? diffItem.childGroupId : diffItem.groupId; + } + }; + } + // Use id if it's very likely to be an one to one animation + // It's more robust than groupId + // TODO Check if key dimension is specified. + var useId = isAllIdSame(oldDiffItems, newDiffItems); + var isElementStillInChart = {}; + if (!useId) { + // We may have different diff strategy with basicTransition if we use other dimension as key. + // If so, we can't simply check if oldEl is same with newEl. We need a map to check if oldEl is still being used in the new chart. + // We can't use the elements that already being morphed. Let it keep it's original basic transition. + for (var i = 0; i < newDiffItems.length; i++) { + var newItem = newDiffItems[i]; + var el = newItem.data.getItemGraphicEl(newItem.dataIndex); + if (el) { + isElementStillInChart[el.id] = true; + } + } + } + function updateOneToOne(newIndex, oldIndex) { + var oldItem = oldDiffItems[oldIndex]; + var newItem = newDiffItems[newIndex]; + var newSeries = newItem.data.hostModel; + // TODO Mark this elements is morphed and don't morph them anymore + var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex); + var newEl = newItem.data.getItemGraphicEl(newItem.dataIndex); + // Can't handle same elements. + if (oldEl === newEl) { + newEl && animateElementStyles(newEl, newItem.dataIndex, newSeries); + return; + } + if ( + // We can't use the elements that already being morphed + oldEl && isElementStillInChart[oldEl.id]) { + return; + } + if (newEl) { + // TODO: If keep animating the group in case + // some of the elements don't want to be morphed. + // TODO Label? + stopAnimation(newEl); + if (oldEl) { + stopAnimation(oldEl); + // If old element is doing leaving animation. stop it and remove it immediately. + removeEl$1(oldEl); + hasMorphAnimation = true; + applyMorphAnimation(getPathList(oldEl), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps); + } else { + fadeInElement(newEl, newSeries, newIndex); + } + } + // else keep oldEl leaving animation. + } + + new DataDiffer(oldDiffItems, newDiffItems, createKeyGetter(true, useId), createKeyGetter(false, useId), null, 'multiple').update(updateOneToOne).updateManyToOne(function (newIndex, oldIndices) { + var newItem = newDiffItems[newIndex]; + var newData = newItem.data; + var newSeries = newData.hostModel; + var newEl = newData.getItemGraphicEl(newItem.dataIndex); + var oldElsList = filter(map(oldIndices, function (idx) { + return oldDiffItems[idx].data.getItemGraphicEl(oldDiffItems[idx].dataIndex); + }), function (oldEl) { + return oldEl && oldEl !== newEl && !isElementStillInChart[oldEl.id]; + }); + if (newEl) { + stopAnimation(newEl); + if (oldElsList.length) { + // If old element is doing leaving animation. stop it and remove it immediately. + each(oldElsList, function (oldEl) { + stopAnimation(oldEl); + removeEl$1(oldEl); + }); + hasMorphAnimation = true; + applyMorphAnimation(getPathList(oldElsList), getPathList(newEl), newItem.divide, newSeries, newIndex, updateMorphingPathProps); + } else { + fadeInElement(newEl, newSeries, newItem.dataIndex); + } + } + // else keep oldEl leaving animation. + }).updateOneToMany(function (newIndices, oldIndex) { + var oldItem = oldDiffItems[oldIndex]; + var oldEl = oldItem.data.getItemGraphicEl(oldItem.dataIndex); + // We can't use the elements that already being morphed + if (oldEl && isElementStillInChart[oldEl.id]) { + return; + } + var newElsList = filter(map(newIndices, function (idx) { + return newDiffItems[idx].data.getItemGraphicEl(newDiffItems[idx].dataIndex); + }), function (el) { + return el && el !== oldEl; + }); + var newSeris = newDiffItems[newIndices[0]].data.hostModel; + if (newElsList.length) { + each(newElsList, function (newEl) { + return stopAnimation(newEl); + }); + if (oldEl) { + stopAnimation(oldEl); + // If old element is doing leaving animation. stop it and remove it immediately. + removeEl$1(oldEl); + hasMorphAnimation = true; + applyMorphAnimation(getPathList(oldEl), getPathList(newElsList), oldItem.divide, + // Use divide on old. + newSeris, newIndices[0], updateMorphingPathProps); + } else { + each(newElsList, function (newEl) { + return fadeInElement(newEl, newSeris, newIndices[0]); + }); + } + } + // else keep oldEl leaving animation. + }).updateManyToMany(function (newIndices, oldIndices) { + // If two data are same and both have groupId. + // Normally they should be diff by id. + new DataDiffer(oldIndices, newIndices, function (rawIdx) { + return oldDiffItems[rawIdx].data.getId(oldDiffItems[rawIdx].dataIndex); + }, function (rawIdx) { + return newDiffItems[rawIdx].data.getId(newDiffItems[rawIdx].dataIndex); + }).update(function (newIndex, oldIndex) { + // Use the original index + updateOneToOne(newIndices[newIndex], oldIndices[oldIndex]); + }).execute(); + }).execute(); + if (hasMorphAnimation) { + each(newList, function (_a) { + var data = _a.data; + var seriesModel = data.hostModel; + var view = seriesModel && api.getViewOfSeriesModel(seriesModel); + var animationCfg = getAnimationConfig('update', seriesModel, 0); // use 0 index. + if (view && seriesModel.isAnimationEnabled() && animationCfg && animationCfg.duration > 0) { + view.group.traverse(function (el) { + if (el instanceof Path && !el.animators.length) { + // We can't accept there still exists element that has no animation + // if universalTransition is enabled + el.animateFrom({ + style: { + opacity: 0 + } + }, animationCfg); + } + }); + } + }); + } + } + function getSeriesTransitionKey(series) { + var seriesKey = series.getModel('universalTransition').get('seriesKey'); + if (!seriesKey) { + // Use series id by default. + return series.id; + } + return seriesKey; + } + function convertArraySeriesKeyToString(seriesKey) { + if (isArray(seriesKey)) { + // Order independent. + return seriesKey.sort().join(','); + } + return seriesKey; + } + function getDivideShapeFromData(data) { + if (data.hostModel) { + return data.hostModel.getModel('universalTransition').get('divideShape'); + } + } + function findTransitionSeriesBatches(globalStore, params) { + var updateBatches = createHashMap(); + var oldDataMap = createHashMap(); + // Map that only store key in array seriesKey. + // Which is used to query the old data when transition from one to multiple series. + var oldDataMapForSplit = createHashMap(); + each(globalStore.oldSeries, function (series, idx) { + var oldDataGroupId = globalStore.oldDataGroupIds[idx]; + var oldData = globalStore.oldData[idx]; + var transitionKey = getSeriesTransitionKey(series); + var transitionKeyStr = convertArraySeriesKeyToString(transitionKey); + oldDataMap.set(transitionKeyStr, { + dataGroupId: oldDataGroupId, + data: oldData + }); + if (isArray(transitionKey)) { + // Same key can't in different array seriesKey. + each(transitionKey, function (key) { + oldDataMapForSplit.set(key, { + key: transitionKeyStr, + dataGroupId: oldDataGroupId, + data: oldData + }); + }); + } + }); + function checkTransitionSeriesKeyDuplicated(transitionKeyStr) { + if (updateBatches.get(transitionKeyStr)) { + warn("Duplicated seriesKey in universalTransition " + transitionKeyStr); + } + } + each(params.updatedSeries, function (series) { + if (series.isUniversalTransitionEnabled() && series.isAnimationEnabled()) { + var newDataGroupId = series.get('dataGroupId'); + var newData = series.getData(); + var transitionKey = getSeriesTransitionKey(series); + var transitionKeyStr = convertArraySeriesKeyToString(transitionKey); + // Only transition between series with same id. + var oldData = oldDataMap.get(transitionKeyStr); + // string transition key is the best match. + if (oldData) { + if ("development" !== 'production') { + checkTransitionSeriesKeyDuplicated(transitionKeyStr); + } + // TODO check if data is same? + updateBatches.set(transitionKeyStr, { + oldSeries: [{ + dataGroupId: oldData.dataGroupId, + divide: getDivideShapeFromData(oldData.data), + data: oldData.data + }], + newSeries: [{ + dataGroupId: newDataGroupId, + divide: getDivideShapeFromData(newData), + data: newData + }] + }); + } else { + // Transition from multiple series. + // e.g. 'female', 'male' -> ['female', 'male'] + if (isArray(transitionKey)) { + if ("development" !== 'production') { + checkTransitionSeriesKeyDuplicated(transitionKeyStr); + } + var oldSeries_1 = []; + each(transitionKey, function (key) { + var oldData = oldDataMap.get(key); + if (oldData.data) { + oldSeries_1.push({ + dataGroupId: oldData.dataGroupId, + divide: getDivideShapeFromData(oldData.data), + data: oldData.data + }); + } + }); + if (oldSeries_1.length) { + updateBatches.set(transitionKeyStr, { + oldSeries: oldSeries_1, + newSeries: [{ + dataGroupId: newDataGroupId, + data: newData, + divide: getDivideShapeFromData(newData) + }] + }); + } + } else { + // Try transition to multiple series. + // e.g. ['female', 'male'] -> 'female', 'male' + var oldData_1 = oldDataMapForSplit.get(transitionKey); + if (oldData_1) { + var batch = updateBatches.get(oldData_1.key); + if (!batch) { + batch = { + oldSeries: [{ + dataGroupId: oldData_1.dataGroupId, + data: oldData_1.data, + divide: getDivideShapeFromData(oldData_1.data) + }], + newSeries: [] + }; + updateBatches.set(oldData_1.key, batch); + } + batch.newSeries.push({ + dataGroupId: newDataGroupId, + data: newData, + divide: getDivideShapeFromData(newData) + }); + } + } + } + } + }); + return updateBatches; + } + function querySeries(series, finder) { + for (var i = 0; i < series.length; i++) { + var found = finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id; + if (found) { + return i; + } + } + } + function transitionSeriesFromOpt(transitionOpt, globalStore, params, api) { + var from = []; + var to = []; + each(normalizeToArray(transitionOpt.from), function (finder) { + var idx = querySeries(globalStore.oldSeries, finder); + if (idx >= 0) { + from.push({ + dataGroupId: globalStore.oldDataGroupIds[idx], + data: globalStore.oldData[idx], + // TODO can specify divideShape in transition. + divide: getDivideShapeFromData(globalStore.oldData[idx]), + groupIdDim: finder.dimension + }); + } + }); + each(normalizeToArray(transitionOpt.to), function (finder) { + var idx = querySeries(params.updatedSeries, finder); + if (idx >= 0) { + var data = params.updatedSeries[idx].getData(); + to.push({ + dataGroupId: globalStore.oldDataGroupIds[idx], + data: data, + divide: getDivideShapeFromData(data), + groupIdDim: finder.dimension + }); + } + }); + if (from.length > 0 && to.length > 0) { + transitionBetween(from, to, api); + } + } + function installUniversalTransition(registers) { + registers.registerUpdateLifecycle('series:beforeupdate', function (ecMOdel, api, params) { + each(normalizeToArray(params.seriesTransition), function (transOpt) { + each(normalizeToArray(transOpt.to), function (finder) { + var series = params.updatedSeries; + for (var i = 0; i < series.length; i++) { + if (finder.seriesIndex != null && finder.seriesIndex === series[i].seriesIndex || finder.seriesId != null && finder.seriesId === series[i].id) { + series[i][SERIES_UNIVERSAL_TRANSITION_PROP] = true; + } + } + }); + }); + }); + registers.registerUpdateLifecycle('series:transition', function (ecModel, api, params) { + // TODO api provide an namespace that can save stuff per instance + var globalStore = getUniversalTransitionGlobalStore(api); + // TODO multiple to multiple series. + if (globalStore.oldSeries && params.updatedSeries && params.optionChanged) { + // TODO transitionOpt was used in an old implementation and can be removed now + // Use give transition config if its' give; + var transitionOpt = params.seriesTransition; + if (transitionOpt) { + each(normalizeToArray(transitionOpt), function (opt) { + transitionSeriesFromOpt(opt, globalStore, params, api); + }); + } else { + // Else guess from series based on transition series key. + var updateBatches_1 = findTransitionSeriesBatches(globalStore, params); + each(updateBatches_1.keys(), function (key) { + var batch = updateBatches_1.get(key); + transitionBetween(batch.oldSeries, batch.newSeries, api); + }); + } + // Reset + each(params.updatedSeries, function (series) { + // Reset; + if (series[SERIES_UNIVERSAL_TRANSITION_PROP]) { + series[SERIES_UNIVERSAL_TRANSITION_PROP] = false; + } + }); + } + // Save all series of current update. Not only the updated one. + var allSeries = ecModel.getSeries(); + var savedSeries = globalStore.oldSeries = []; + var savedDataGroupIds = globalStore.oldDataGroupIds = []; + var savedData = globalStore.oldData = []; + for (var i = 0; i < allSeries.length; i++) { + var data = allSeries[i].getData(); + // Only save the data that can have transition. + // Avoid large data costing too much extra memory + if (data.count() < DATA_COUNT_THRESHOLD) { + savedSeries.push(allSeries[i]); + savedDataGroupIds.push(allSeries[i].get('dataGroupId')); + savedData.push(data); + } + } + }); + } + + // ----------------- + // Render engines + // ----------------- + // Render via Canvas. + // echarts.init(dom, null, { renderer: 'canvas' }) + use([install$1]); + // Render via SVG. + // echarts.init(dom, null, { renderer: 'svg' }) + use([install]); + // ---------------- + // Charts (series) + // ---------------- + // All of the series types, for example: + // chart.setOption({ + // series: [{ + // type: 'line' // or 'bar', 'pie', ... + // }] + // }); + use([install$2, install$3, install$4, install$6, install$8, install$a, install$b, install$c, install$d, install$e, install$f, install$h, install$i, install$j, install$k, install$l, install$m, install$n, install$o, install$p, install$q, install$r]); + // ------------------- + // Coordinate systems + // ------------------- + // All of the axis modules have been included in the + // coordinate system module below, do not need to + // make extra import. + // `cartesian` coordinate system. For some historical + // reasons, it is named as grid, for example: + // chart.setOption({ + // grid: {...}, + // xAxis: {...}, + // yAxis: {...}, + // series: [{...}] + // }); + use(install$t); + // `polar` coordinate system, for example: + // chart.setOption({ + // polar: {...}, + // radiusAxis: {...}, + // angleAxis: {...}, + // series: [{ + // coordinateSystem: 'polar' + // }] + // }); + use(install$u); + // `geo` coordinate system, for example: + // chart.setOption({ + // geo: {...}, + // series: [{ + // coordinateSystem: 'geo' + // }] + // }); + use(install$9); + // `singleAxis` coordinate system (notice, it is a coordinate system + // with only one axis, work for chart like theme river), for example: + // chart.setOption({ + // singleAxis: {...} + // series: [{type: 'themeRiver', ...}] + // }); + use(install$v); + // `parallel` coordinate system, only work for parallel series, for example: + // chart.setOption({ + // parallel: {...}, + // parallelAxis: [{...}, ...], + // series: [{ + // type: 'parallel' + // }] + // }); + use(install$g); + // `calendar` coordinate system. for example, + // chart.setOption({ + // calendar: {...}, + // series: [{ + // coordinateSystem: 'calendar' + // }] + // ); + use(install$w); + // ------------------ + // Other components + // ------------------ + // `graphic` component, for example: + // chart.setOption({ + // graphic: {...} + // }); + use(install$x); + // `toolbox` component, for example: + // chart.setOption({ + // toolbox: {...} + // }); + use(install$z); + // `tooltip` component, for example: + // chart.setOption({ + // tooltip: {...} + // }); + use(install$A); + // `axisPointer` component, for example: + // chart.setOption({ + // tooltip: {axisPointer: {...}, ...} + // }); + // Or + // chart.setOption({ + // axisPointer: {...} + // }); + use(install$s); + // `brush` component, for example: + // chart.setOption({ + // brush: {...} + // }); + // Or + // chart.setOption({ + // tooltip: {feature: {brush: {...}} + // }) + use(install$B); + // `title` component, for example: + // chart.setOption({ + // title: {...} + // }); + use(install$C); + // `timeline` component, for example: + // chart.setOption({ + // timeline: {...} + // }); + use(install$D); + // `markPoint` component, for example: + // chart.setOption({ + // series: [{markPoint: {...}}] + // }); + use(install$E); + // `markLine` component, for example: + // chart.setOption({ + // series: [{markLine: {...}}] + // }); + use(install$F); + // `markArea` component, for example: + // chart.setOption({ + // series: [{markArea: {...}}] + // }); + use(install$G); + // `legend` component not scrollable. for example: + // chart.setOption({ + // legend: {...} + // }); + use(install$J); + // `dataZoom` component including both `dataZoomInside` and `dataZoomSlider`. + use(install$M); + // `dataZoom` component providing drag, pinch, wheel behaviors + // inside coordinate system, for example: + // chart.setOption({ + // dataZoom: {type: 'inside'} + // }); + use(install$K); + // `dataZoom` component providing a slider bar, for example: + // chart.setOption({ + // dataZoom: {type: 'slider'} + // }); + use(install$L); + // `visualMap` component including both `visualMapContinuous` and `visualMapPiecewise`. + use(install$P); + // `visualMap` component providing continuous bar, for example: + // chart.setOption({ + // visualMap: {type: 'continuous'} + // }); + use(install$N); + // `visualMap` component providing pieces bar, for example: + // chart.setOption({ + // visualMap: {type: 'piecewise'} + // }); + use(install$O); + // `aria` component providing aria, for example: + // chart.setOption({ + // aria: {...} + // }); + use(install$Q); + // dataset transform + // chart.setOption({ + // dataset: { + // transform: [] + // } + // }); + use(install$R); + use(install$S); + // universal transition + // chart.setOption({ + // series: { + // universalTransition: { enabled: true } + // } + // }) + use(installUniversalTransition); + // label layout + // chart.setOption({ + // series: { + // labelLayout: { hideOverlap: true } + // } + // }) + use(installLabelLayout); + + exports.Axis = Axis; + exports.ChartView = ChartView; + exports.ComponentModel = ComponentModel; + exports.ComponentView = ComponentView; + exports.List = SeriesData; + exports.Model = Model; + exports.PRIORITY = PRIORITY; + exports.SeriesModel = SeriesModel; + exports.color = color; + exports.connect = connect; + exports.dataTool = dataTool; + exports.dependencies = dependencies; + exports.disConnect = disConnect; + exports.disconnect = disconnect; + exports.dispose = dispose$1; + exports.env = env; + exports.extendChartView = extendChartView; + exports.extendComponentModel = extendComponentModel; + exports.extendComponentView = extendComponentView; + exports.extendSeriesModel = extendSeriesModel; + exports.format = format$1; + exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions; + exports.getInstanceByDom = getInstanceByDom; + exports.getInstanceById = getInstanceById; + exports.getMap = getMap; + exports.graphic = graphic$1; + exports.helper = helper; + exports.init = init$1; + exports.innerDrawElementOnCanvas = brushSingle; + exports.matrix = matrix; + exports.number = number; + exports.parseGeoJSON = parseGeoJSON; + exports.parseGeoJson = parseGeoJSON; + exports.registerAction = registerAction; + exports.registerCoordinateSystem = registerCoordinateSystem; + exports.registerLayout = registerLayout; + exports.registerLoading = registerLoading; + exports.registerLocale = registerLocale; + exports.registerMap = registerMap; + exports.registerPostInit = registerPostInit; + exports.registerPostUpdate = registerPostUpdate; + exports.registerPreprocessor = registerPreprocessor; + exports.registerProcessor = registerProcessor; + exports.registerTheme = registerTheme; + exports.registerTransform = registerTransform; + exports.registerUpdateLifecycle = registerUpdateLifecycle; + exports.registerVisual = registerVisual; + exports.setCanvasCreator = setCanvasCreator; + exports.setPlatformAPI = setPlatformAPI; + exports.throttle = throttle; + exports.time = time; + exports.use = use; + exports.util = util$1; + exports.vector = vector; + exports.version = version$1; + exports.zrUtil = util; + exports.zrender = zrender; + + Object.defineProperty(exports, '__esModule', { value: true }); + +}))); +//# sourceMappingURL=echarts.js.map diff --git a/liteyuki/resources/templates/stats.html b/liteyuki/resources/templates/stats.html index 21c2907f..27fe44bb 100644 --- a/liteyuki/resources/templates/stats.html +++ b/liteyuki/resources/templates/stats.html @@ -1,12 +1,236 @@ + - + + Document + + + -
- + +
+ + BotIcon + + + + {{ BOT_NAME }} + + + {{ BOT_ID }} + +
+
- \ No newline at end of file + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + diff --git a/liteyuki/resources/templates/stats.json b/liteyuki/resources/templates/stats.json deleted file mode 100644 index 1b6a6f23..00000000 --- a/liteyuki/resources/templates/stats.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "type": "canvas", - "children": [ - { - "type": "rect", - "x": 0, - "y": 0, - "width": 100, - "height": 100, - "fill": "red" - }, - { - "type": "rect", - "x": 100, - "y": 100, - "width": 100, - "height": 100, - "fill": "green" - }, - { - "type": "rect", - "x": 200, - "y": 200, - "width": 100, - "height": 100, - "fill": "blue" - } - ] -} \ No newline at end of file diff --git a/liteyuki/resources/templates/test.html b/liteyuki/resources/templates/test.html new file mode 100644 index 00000000..d2edee31 --- /dev/null +++ b/liteyuki/resources/templates/test.html @@ -0,0 +1,93 @@ + + + + + + + Document + + + + + +
+ + + + diff --git a/liteyuki/utils/htmlrender/browser.py b/liteyuki/utils/htmlrender/browser.py index 3221face..06d18b2c 100644 --- a/liteyuki/utils/htmlrender/browser.py +++ b/liteyuki/utils/htmlrender/browser.py @@ -20,7 +20,7 @@ from playwright.async_api import Browser, Error, Page, Playwright, async_playwri from .config import Config import asyncio -config = get_plugin_config(Config) +config = Config() _browser: Optional[Browser] = None _playwright: Optional[Playwright] = None diff --git a/liteyuki/utils/language.py b/liteyuki/utils/language.py index a12be701..4aa874f7 100644 --- a/liteyuki/utils/language.py +++ b/liteyuki/utils/language.py @@ -130,6 +130,20 @@ class Language: nonebot.logger.error(f"Failed to get language text or format: {e}") return default or item + def get_many(self, *args) -> dict[str, str]: + """ + 获取多个文本 + Args: + *args: 文本键 + + Returns: + dict: 文本字典 + """ + d = {} + for item in args: + d[item] = self.get(item) + return d + def get_user_lang(user_id: str) -> Language: """ diff --git a/liteyuki/utils/liteyuki_api.py b/liteyuki/utils/liteyuki_api.py index 7b83b054..e9e7ad99 100644 --- a/liteyuki/utils/liteyuki_api.py +++ b/liteyuki/utils/liteyuki_api.py @@ -2,9 +2,11 @@ import json import os.path import platform +import aiohttp import nonebot import psutil import requests +from aiohttp import FormData from . import __VERSION_I__, __VERSION__, __NAME__ from .config import config, load_from_yaml @@ -66,5 +68,31 @@ class LiteyukiAPI: else: nonebot.logger.warning(f"Bug report is disabled: {content}") + async def upload_image(self, image: bytes) -> str | None: + """ + 上传图片到图床 + Args: + image: + + Returns: + 图片url + """ + assert self.liteyuki_id, "Liteyuki ID is not set" + assert isinstance(image, bytes), "Image must be bytes" + url = "https://api.liteyuki.icu/upload_image" + data = FormData() + data.add_field("liteyuki_id", self.liteyuki_id) + data.add_field('image', image, filename='image', content_type='application/octet-stream') + async with aiohttp.ClientSession() as session: + async with session.post( + url, + data=data + ) as resp: + if resp.status == 200: + return (await resp.json()).get("url") + else: + nonebot.logger.error(f"Upload image failed: {await resp.text()}") + return None + liteyuki_api = LiteyukiAPI() diff --git a/liteyuki/utils/message.py b/liteyuki/utils/message.py index a782767c..55781260 100644 --- a/liteyuki/utils/message.py +++ b/liteyuki/utils/message.py @@ -1,81 +1,135 @@ +import asyncio +import io from urllib.parse import quote +import aiofiles +from PIL import Image +import aiohttp import nonebot from nonebot.adapters.onebot import v11, v12 from typing import Any + +from .liteyuki_api import liteyuki_api from .ly_typing import T_Bot, T_MessageEvent -async def send_markdown(markdown: str, bot: T_Bot, *, message_type: str = None, session_id: str | int = None, event: T_MessageEvent = None, **kwargs) -> dict[ - str, Any]: - formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace('"', r'\\\"') - if event is not None and message_type is None: - message_type = event.message_type - session_id = event.user_id if event.message_type == "private" else event.group_id - try: - forward_id = await bot.call_api( - api="send_forward_msg", - messages=[ - v11.MessageSegment( - type="node", - data={ - "name" : "Liteyuki.OneBot", - "uin" : bot.self_id, - "content": [ - { - "type": "markdown", - "data": { - "content": '{"content":"%s"}' % formatted_md - } - }, - ] - }, - ) - ] - ) - data = await bot.send_msg( - user_id=session_id, - group_id=session_id, - message_type=message_type, - message=[ - v11.MessageSegment( - type="longmsg", - data={ - "id": forward_id - } - ), - ], - **kwargs - ) - except Exception as e: - nonebot.logger.warning("send_markdown error, send as plain text: %s" % e.__repr__()) - if isinstance(bot, v11.Bot): - data = await bot.send_msg( - message_type=message_type, - message=markdown, - user_id=int(session_id), - group_id=int(session_id), - **kwargs - ) - elif isinstance(bot, v12.Bot): - data = await bot.send_message( - detail_type=message_type, - message=v12.Message( - v12.MessageSegment.text( - text=markdown - ) - ), - user_id=str(session_id), - group_id=str(session_id), - **kwargs - ) - else: - nonebot.logger.error("send_markdown: bot type not supported") - data = {} - return data - - class Markdown: + @staticmethod + async def send_md( + markdown: str, + bot: T_Bot, *, + message_type: str = None, + session_id: str | int = None, + event: T_MessageEvent = None, + **kwargs + ) -> dict[str, Any]: + formatted_md = v11.unescape(markdown).replace("\n", r"\n").replace('"', r'\\\"') + if event is not None and message_type is None: + message_type = event.message_type + session_id = event.user_id if event.message_type == "private" else event.group_id + try: + forward_id = await bot.call_api( + api="send_forward_msg", + messages=[ + v11.MessageSegment( + type="node", + data={ + "name" : "Liteyuki.OneBot", + "uin" : bot.self_id, + "content": [ + { + "type": "markdown", + "data": { + "content": '{"content":"%s"}' % formatted_md + } + }, + ] + }, + ) + ] + ) + data = await bot.send_msg( + user_id=session_id, + group_id=session_id, + message_type=message_type, + message=[ + v11.MessageSegment( + type="longmsg", + data={ + "id": forward_id + } + ), + ], + **kwargs + ) + except Exception as e: + nonebot.logger.warning("send_markdown error, send as plain text: %s" % e.__repr__()) + if isinstance(bot, v11.Bot): + data = await bot.send_msg( + message_type=message_type, + message=markdown, + user_id=int(session_id), + group_id=int(session_id), + **kwargs + ) + elif isinstance(bot, v12.Bot): + data = await bot.send_message( + detail_type=message_type, + message=v12.Message( + v12.MessageSegment.text( + text=markdown + ) + ), + user_id=str(session_id), + group_id=str(session_id), + **kwargs + ) + else: + nonebot.logger.error("send_markdown: bot type not supported") + data = {} + return data + + @staticmethod + async def send_image( + image: bytes | str, + bot: T_Bot, *, + message_type: str = None, + session_id: str | int = None, + event: T_MessageEvent = None, + **kwargs + ) -> dict: + """ + 发送单张装逼大图 + Args: + image: 图片字节流或图片本地路径,链接请使用Markdown.image_async方法获取后通过send_md发送 + bot: bot instance + message_type: message type + session_id: session id + event: event + kwargs: other arguments + Returns: + dict: response data + + """ + if isinstance(image, str): + async with aiofiles.open(image, "rb") as f: + image = await f.read() + + image_url = await liteyuki_api.upload_image(image) + image_size = Image.open(io.BytesIO(image)).size + image_md = Markdown.image(image_url, image_size) + return await Markdown.send_md(image_md, bot, message_type=message_type, session_id=session_id, event=event, **kwargs) + + @staticmethod + async def get_image_url(image: bytes | str, bot: T_Bot) -> str: + """把图片上传到图床,返回链接 + Args: + bot: 发送的bot + image: 图片字节流或图片本地路径 + Returns: + """ + # 等林文轩修好Lagrange.OneBot再说 + @staticmethod def button(name: str, cmd: str, reply: bool = False, enter: bool = True) -> str: """生成点击回调按钮 @@ -104,6 +158,38 @@ class Markdown: """ return f"[🔗{name}]({url})" + @staticmethod + def image(url: str, size: tuple[int, int]) -> str: + """生成图片 + Args: + size: + url: 图片链接 + + Returns: + markdown格式的图片 + + """ + return f"![image #{size[0]}px #{size[1]}px]({url})" + + @staticmethod + async def image_async(url: str) -> str: + """获取图片,自动获取大小 + Args: + url: 图片链接 + + Returns: + 图片bytes + + """ + try: + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + image = Image.open(io.BytesIO(await resp.read())) + return Markdown.image(url, image.size) + except Exception as e: + nonebot.logger.error(f"get image error: {e}") + return "[Image Error]" + @staticmethod def escape(text: str) -> str: """转义特殊字符 diff --git a/liteyuki/utils/resource.py b/liteyuki/utils/resource.py index f91af6e6..16c33404 100644 --- a/liteyuki/utils/resource.py +++ b/liteyuki/utils/resource.py @@ -47,9 +47,21 @@ def get_path(path: str, abs_path: bool = False, default: Any = None) -> str | An """ 获取资源包中的文件 Args: - abs_path: + abs_path: 是否返回绝对路径 default: 默认 path: 文件相对路径 Returns: 文件绝对路径 """ return _resource_data.get(path, default) if not abs_path else os.path.abspath(_resource_data.get(path, default)) + + +def get_files(path: str, abs_path: bool = False) -> list[str]: + """ + 获取资源包中一个文件夹的所有文件 + Args: + abs_path: + path: 文件夹相对路径 + Returns: 文件绝对路径 + """ + return [os.path.abspath(file) for file in _resource_data if file.startswith(path)] if abs_path else [ + file for file in _resource_data if file.startswith(path)] diff --git a/requirements.txt b/requirements.txt index 14fd1275..adea711c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ aiofiles==23.2.1 arclet-alconna==1.8.5 arclet-alconna-tools==0.7.0 colored==2.2.4 +py-cpuinfo==9.0.0 dash==2.16.1 GitPython==3.1.42 nonebot2[fastapi]==2.2.1