Enhanced Ecommerce Google Analytics Plugin for WooCommerce - Version 4.2.0

Version Description

  • 20/09/2021 =

  • In this release, we have rolled out the most demanded feature by you all - Reporting dashboard. Reporting dashboard will showcase all the important e-commerce KPI reports in the plugin's UI.

  • Plugin UI enhancements

  • Performance enhancements and bug fixes

Download this release

Release Info

Developer Tatvic
Plugin Icon 128x128 Enhanced Ecommerce Google Analytics Plugin for WooCommerce
Version 4.2.0
Comparing to
See all releases

Code changes from version 4.1.3 to 4.2.0

Files changed (60) hide show
  1. admin/class-conversios-admin.php +240 -0
  2. admin/class-conversios-onboarding.php +7 -7
  3. admin/class-enhanced-ecommerce-google-analytics-admin.php +18 -188
  4. admin/class-tvc-admin-helper.php +69 -16
  5. admin/css/custom-style.css +0 -2
  6. admin/css/daterangepicker.css +71 -0
  7. admin/css/enhanced-ecommerce-google-analytics-admin.css +1 -1
  8. admin/css/responsive.css +279 -7
  9. admin/css/style.css +396 -22
  10. admin/helper/class-dashboard-helper.php +126 -0
  11. admin/helper/class-onboarding-helper.php +4 -4
  12. admin/images/active-conversios-google-ads-menu.png +0 -0
  13. admin/images/active-conversios-google-analytics-menu.png +0 -0
  14. admin/images/{sync-prdct-icon.png → active-conversios-google-shopping-feed-menu.png} +0 -0
  15. admin/images/active-conversios-menu.png +0 -0
  16. admin/images/blue-right-arrow.png +0 -0
  17. admin/images/caret-down.png +0 -0
  18. admin/images/claendar-icon.png +0 -0
  19. admin/images/clock-icon.png +0 -0
  20. admin/images/{smart-shopping-icon.png → conversios-google-ads-menu.png} +0 -0
  21. admin/images/{setting-icon.png → conversios-google-analytics-menu.png} +0 -0
  22. admin/images/conversios-google-shopping-feed-menu.png +0 -0
  23. admin/images/{dashboard-icon.png → conversios-menu.png} +0 -0
  24. admin/images/download-icon.png +0 -0
  25. admin/images/ecom-chart.jpg +0 -0
  26. admin/images/error-icon.png +0 -0
  27. admin/images/error-white-icon.png +0 -0
  28. admin/images/green-up.png +0 -0
  29. admin/images/lock-orange.png +0 -0
  30. admin/images/percentage-arrow-bg.png +0 -0
  31. admin/images/percentage-arrow.png +0 -0
  32. admin/images/prev.png +0 -0
  33. admin/images/red-down.png +0 -0
  34. admin/images/table-data.jpg +0 -0
  35. admin/js/chart.js +13225 -0
  36. admin/js/chartjs-plugin-datalabels.js +1358 -0
  37. admin/js/daterangepicker.js +1595 -0
  38. admin/js/jquery.basictable.min.js +4 -0
  39. admin/js/moment.min.js +1 -0
  40. admin/js/onboarding-custom.js +4 -4
  41. admin/js/tvc-ee-custom.js +522 -0
  42. admin/partials/class-conversios-footer.php +18 -0
  43. admin/partials/class-conversios-header.php +214 -0
  44. admin/partials/enhanced-ecommerce-google-analytics-admin-display.php +17 -17
  45. admin/partials/general-fields.php +3 -3
  46. admin/partials/pricings.php +4 -4
  47. enhanced-ecommerce-google-analytics.php +2 -2
  48. includes/class-enhanced-ecommerce-google-analytics.php +11 -27
  49. includes/class-tvc-register-scripts.php +8 -43
  50. includes/setup/CustomApi.php +71 -13
  51. includes/setup/ShoppingApi.php +0 -2
  52. includes/setup/account.php +1 -1
  53. includes/setup/add-campaign.php +1 -1
  54. includes/setup/class-conversios-dashboard.php +971 -0
  55. includes/setup/class-tvc-product-sync-helper.php +5 -7
  56. includes/setup/google-shopping-feed-gaa-config.php +4 -5
  57. includes/setup/google-shopping-feed-shopping-campaigns.php +5 -7
  58. includes/setup/google-shopping-feed-sync-product.php +5 -7
  59. includes/setup/google-shopping-feed.php +7 -10
  60. readme.txt +11 -3
admin/class-conversios-admin.php ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The admin-specific functionality of the plugin.
5
+ *
6
+ * @link tatvic.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package Enhanced_Ecommerce_Google_Analytics
10
+ * @subpackage Enhanced_Ecommerce_Google_Analytics/admin
11
+ */
12
+
13
+ /**
14
+ * The admin-specific functionality of the plugin.
15
+ *
16
+ * Defines the plugin name, version, and two examples hooks for how to
17
+ * enqueue the admin-specific stylesheet and JavaScript.
18
+ *
19
+ * @package Enhanced_Ecommerce_Google_Analytics
20
+ * @subpackage Enhanced_Ecommerce_Google_Analytics/admin
21
+ * @author Tatvic
22
+ */
23
+ if ( ! class_exists( 'Conversios_Admin' ) ) {
24
+ class Conversios_Admin extends TVC_Admin_Helper {
25
+ protected $google_detail;
26
+ protected $url;
27
+ protected $version;
28
+ public function __construct() {
29
+ $this->version = PLUGIN_TVC_VERSION;
30
+ $this->includes();
31
+ $this->url = $this->get_onboarding_page_url();
32
+ /* $this->pro_plan_site = $this->get_pro_plan_site();*/
33
+ $this->google_detail = $this->get_ee_options_data();
34
+ add_action( 'admin_menu', array( $this, 'add_admin_pages' ) );
35
+ add_action('admin_init',array($this, 'init'));
36
+ }
37
+ public function includes() {
38
+ if (!class_exists('Conversios_Header')) {
39
+ require_once(ENHANCAD_PLUGIN_DIR . 'admin/partials/class-conversios-header.php');
40
+ }
41
+ if (!class_exists('Conversios_Footer')) {
42
+ require_once(ENHANCAD_PLUGIN_DIR . 'admin/partials/class-conversios-footer.php');
43
+ }
44
+ }
45
+
46
+ public function init(){
47
+ add_action( 'admin_enqueue_scripts', array($this,'enqueue_styles'));
48
+ add_action( 'admin_enqueue_scripts', array($this,'enqueue_scripts'));
49
+ }
50
+
51
+ /**
52
+ * Register the stylesheets for the admin area.
53
+ *
54
+ * @since 4.1.4
55
+ */
56
+ public function enqueue_styles() {
57
+ $screen = get_current_screen();
58
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) ) {
59
+ //developres hook to custom css
60
+ do_action('add_conversios_css_'.$_GET['page']);
61
+ //conversios page css
62
+ if($_GET['page'] == "conversios"){
63
+ wp_register_style('conversios-slick-css', ENHANCAD_PLUGIN_URL.'/admin/css/slick.css');
64
+ wp_enqueue_style('conversios-slick-css');
65
+ wp_register_style('conversios-daterangepicker-css', ENHANCAD_PLUGIN_URL.'/admin/css/daterangepicker.css');
66
+ wp_enqueue_style('conversios-daterangepicker-css');
67
+ }
68
+ //all conversios page css
69
+ wp_enqueue_style('conversios-style-css',ENHANCAD_PLUGIN_URL . '/admin/css/style.css', array(), $this->version, 'all');
70
+ wp_enqueue_style('conversios-responsive-css',ENHANCAD_PLUGIN_URL . '/admin/css/responsive.css', array(), $this->version, 'all');
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Register the JavaScript for the admin area.
76
+ *
77
+ * @since 4.1.4
78
+ */
79
+ public function enqueue_scripts() {
80
+ $screen = get_current_screen();
81
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) ) {
82
+ if($_GET['page'] == "conversios"){
83
+ wp_enqueue_script( 'conversios-jquery-js', ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js' );
84
+
85
+ wp_enqueue_script( 'conversios-chart-js', ENHANCAD_PLUGIN_URL . '/admin/js/chart.js' );
86
+ wp_enqueue_script( 'conversios-chart-datalabels-js', ENHANCAD_PLUGIN_URL . '/admin/js/chartjs-plugin-datalabels.js');
87
+ wp_enqueue_script( 'conversios-basictable-js', ENHANCAD_PLUGIN_URL . '/admin/js/jquery.basictable.min.js');
88
+ wp_enqueue_script( 'conversios-moment-js', ENHANCAD_PLUGIN_URL . '/admin/js/moment.min.js');
89
+ wp_enqueue_script( 'conversios-daterangepicker-js', ENHANCAD_PLUGIN_URL . '/admin/js/daterangepicker.js');
90
+
91
+ wp_enqueue_script( 'tvc-ee-custom-js', ENHANCAD_PLUGIN_URL . '/admin/js/tvc-ee-custom.js', array( 'jquery' ), $this->version, false );
92
+ }
93
+ do_action('add_conversios_js_'.$_GET['page']);
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Display Admin Page.
99
+ *
100
+ * @since 4.1.4
101
+ */
102
+ public function add_admin_pages() {
103
+ $google_detail = $this->google_detail;
104
+ $plan_id = 1;
105
+ if(isset($google_detail['setting'])){
106
+ $googleDetail = $google_detail['setting'];
107
+ if(isset($googleDetail->plan_id) && !in_array($googleDetail->plan_id, array("1"))){
108
+ $plan_id = $googleDetail->plan_id;
109
+ }
110
+ }
111
+
112
+ add_menu_page(
113
+ esc_html__('Tatvic EE Plugin','conversios'), esc_html__('Tatvic EE Plugin','conversios'), 'manage_options', "conversios", array($this, 'showPage'), plugin_dir_url(__FILE__) . 'images/tatvic_logo.png', 26
114
+ );
115
+ add_submenu_page(
116
+ 'conversios',
117
+ esc_html__('Dashboard','conversios'),
118
+ esc_html__('Dashboard','conversios'),
119
+ 'manage_options',
120
+ 'conversios' );
121
+ add_submenu_page(
122
+ 'conversios',
123
+ esc_html__('Google Analytics', 'conversios'),
124
+ esc_html__('Google Analytics', 'conversios'),
125
+ 'manage_options',
126
+ 'conversios-google-analytics',
127
+ array($this, 'showPage')
128
+ );
129
+ add_submenu_page(
130
+ 'conversios',
131
+ esc_html__('Google Ads', 'conversios'),
132
+ esc_html__('Google Ads', 'conversios'),
133
+ 'manage_options',
134
+ 'conversios-google-ads',
135
+ array($this, 'showPage')
136
+ );
137
+ add_submenu_page(
138
+ 'conversios',
139
+ esc_html__('Google Shopping', 'conversios'),
140
+ esc_html__('Google Shopping', 'conversios'),
141
+ 'manage_options',
142
+ 'conversios-google-shopping-feed',
143
+ array($this, 'showPage')
144
+ );
145
+ add_submenu_page(
146
+ 'conversios',
147
+ esc_html__('Account Summary', 'conversios'),
148
+ esc_html__('Account Summary', 'conversios'),
149
+ 'manage_options',
150
+ 'conversios-account',
151
+ array($this, 'showPage')
152
+ );
153
+ if($plan_id == 1){
154
+ add_submenu_page(
155
+ 'conversios',
156
+ esc_html__('Free Vs Pro', 'conversios'),
157
+ esc_html__('Free Vs Pro', 'conversios'),
158
+ 'manage_options',
159
+ 'conversios-pricings',
160
+ array($this, 'showPage')
161
+ );
162
+ }
163
+
164
+ }
165
+
166
+ /**
167
+ * Display page.
168
+ *
169
+ * @since 4.1.4
170
+ */
171
+ public function showPage() {
172
+ do_action('add_conversios_header');
173
+ if (!empty($_GET['page'])) {
174
+ $get_action = str_replace("-", "_", $_GET['page']);
175
+ } else {
176
+ $get_action = "conversios";
177
+ }
178
+ if (method_exists($this, $get_action)) {
179
+ $this->$get_action();
180
+ }
181
+ echo $this->get_tvc_popup_message();
182
+ do_action('add_conversios_footer');
183
+ }
184
+
185
+ public function conversios(){
186
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/class-conversios-dashboard.php');
187
+ }
188
+
189
+ public function conversios_pricings(){
190
+ require_once(ENHANCAD_PLUGIN_DIR . 'admin/partials/pricings.php');
191
+ new TVC_Pricings();
192
+ }
193
+ public function conversios_account(){
194
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
195
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/account.php');
196
+ new TVC_Account();
197
+ }
198
+ public function conversios_google_analytics() {
199
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
200
+ require_once( 'partials/general-fields.php');
201
+ }
202
+ public function conversios_google_ads() {
203
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
204
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-ads.php');
205
+ new GoogleAds();
206
+ }
207
+ public function conversios_google_shopping_feed() {
208
+ include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
209
+ include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed.php');
210
+ $action_tab = (isset($_GET['tab']))?$_GET['tab']:"";
211
+ if($action_tab!=""){
212
+ $this->$action_tab();
213
+ }else{
214
+ new GoogleShoppingFeed();
215
+ }
216
+ }
217
+ public function gaa_config_page() {
218
+ //include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
219
+ include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed-gaa-config.php');
220
+ new GAAConfiguration();
221
+ }
222
+ public function sync_product_page() {
223
+ //include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
224
+ include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed-sync-product.php');
225
+ new SyncProductConfiguration();
226
+ }
227
+ public function shopping_campaigns_page() {
228
+ //include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
229
+ include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed-shopping-campaigns.php');
230
+ new CampaignsConfiguration();
231
+ }
232
+ public function add_campaign_page() {
233
+ //include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
234
+ include(ENHANCAD_PLUGIN_DIR . 'includes/setup/add-campaign.php');
235
+ new AddCampaign();
236
+ }
237
+
238
+ }
239
+ }
240
+ new Conversios_Admin();
admin/class-conversios-onboarding.php CHANGED
@@ -116,7 +116,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
116
  }
117
 
118
  if($this->subscriptionId != ""){
119
- $google_detail = $this->customApiObj->getGoogleAnalyticDetail($this->subscriptionId);
120
  if(property_exists($google_detail,"error") && $google_detail->error == false){
121
  if( property_exists($google_detail, "data") && $google_detail->data != "" ){
122
  $googleDetail = $google_detail->data;
@@ -181,7 +181,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
181
  </style>
182
  <div class="bodyrightpart onbordingbody-wapper">
183
  <div class="loader-section" id="loader-section"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/ajax-loader.gif';?>" alt="loader"></div>
184
- <div class="alert-message" id="tvc_popup_box"></div>
185
  <div class="onbordingbody">
186
  <div class="site-header">
187
  <div class="container">
@@ -814,7 +814,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
814
  if(response.return_url){
815
  location.replace( response.return_url);
816
  }else{
817
- location.replace( "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=general_settings");
818
  }
819
  }else{
820
  loaderSection(false);
@@ -885,7 +885,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
885
  </script>
886
  <script>
887
  $(document).ready(function(){
888
- $(".slect2bx").select2();
889
  });
890
  </script>
891
  <!-- popup script -->
@@ -958,15 +958,15 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
958
  if(isset($_GET['page']) && $_GET['page'] == "conversios_onboarding"){
959
  add_dashboard_page(
960
  esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
961
- esc_html__( 'Welcome to Conversios Onboarding', 'google-analytics-for-wordpress' ),
962
  apply_filters( 'conversios_welcome', 'manage_options' ),
963
  'conversios_onboarding',
964
  array( $this, 'welcome_screen' )
965
  );
966
  /*add_submenu_page(
967
  '__FILE__',
968
- esc_html__('Welcome to Conversios Onboarding', 'enhanced-ecommerce-google-analytics-admin-display'),
969
- esc_html__('Welcome to Conversios Onboarding', 'enhanced-ecommerce-google-analytics-admin-display'),
970
  'administrator',
971
  'conversios_onboarding',
972
  array($this, 'welcome_screen'),10
116
  }
117
 
118
  if($this->subscriptionId != ""){
119
+ $google_detail = $this->customApiObj->getGoogleAnalyticDetail($this->subscriptionId);
120
  if(property_exists($google_detail,"error") && $google_detail->error == false){
121
  if( property_exists($google_detail, "data") && $google_detail->data != "" ){
122
  $googleDetail = $google_detail->data;
181
  </style>
182
  <div class="bodyrightpart onbordingbody-wapper">
183
  <div class="loader-section" id="loader-section"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/ajax-loader.gif';?>" alt="loader"></div>
184
+ <div class="alert-message" id="tvc_onboarding_popup_box"></div>
185
  <div class="onbordingbody">
186
  <div class="site-header">
187
  <div class="container">
814
  if(response.return_url){
815
  location.replace( response.return_url);
816
  }else{
817
+ location.replace( "admin.php?page=conversios-google-analytics");
818
  }
819
  }else{
820
  loaderSection(false);
885
  </script>
886
  <script>
887
  $(document).ready(function(){
888
+ $(".slect2bx").select2();
889
  });
890
  </script>
891
  <!-- popup script -->
958
  if(isset($_GET['page']) && $_GET['page'] == "conversios_onboarding"){
959
  add_dashboard_page(
960
  esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
961
+ esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
962
  apply_filters( 'conversios_welcome', 'manage_options' ),
963
  'conversios_onboarding',
964
  array( $this, 'welcome_screen' )
965
  );
966
  /*add_submenu_page(
967
  '__FILE__',
968
+ esc_html__('Welcome to Conversios Onboarding', 'conversios'),
969
+ esc_html__('Welcome to Conversios Onboarding', 'conversios'),
970
  'administrator',
971
  'conversios_onboarding',
972
  array($this, 'welcome_screen'),10
admin/class-enhanced-ecommerce-google-analytics-admin.php CHANGED
@@ -58,9 +58,11 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
58
  $this->plugin_name = $plugin_name;
59
  $this->version = $version;
60
  $this->url = $this->get_onboarding_page_url();
61
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
62
  $this->pro_plan_site = $this->get_pro_plan_site();
63
  $this->google_detail = $this->get_ee_options_data();
 
 
64
  }
65
  public function tvc_admin_notice(){
66
  // add fixed message notification
@@ -70,7 +72,7 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
70
  }else{
71
  if(!$this->get_subscriptionId()){
72
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_a">
73
- <p>Tatvic EE plugin is now fully compatible with Google Analytics 4. Also, explore the new features of Google Shopping and Dynamic remarketing to reach million of shoppers across Google and scale your eCommerce business faster. <a href="admin.php?page=enhanced-ecommerce-google-analytics-admin-display"><b><u>CONFIGURE NOW</u></b></a></p>
74
  </div>';
75
  }
76
  }
@@ -81,7 +83,7 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
81
  $googleDetail = $google_detail['setting'];
82
  if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id =="" && $this->subscriptionId != "" ){
83
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_b">
84
- <p>Leverage the power of Google Shopping to reach out millions of shoppers across Google. Automate entire Google Shopping and get eligible for free listing when user searches on Google for products similar to your eCommerce business. <a href="admin.php?page=enhanced-ecommerce-google-analytics-admin-display"><b><u>Automate now</u></b></a></p>
85
  </div>';
86
 
87
  }
@@ -115,7 +117,10 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
115
  */
116
  public function enqueue_styles() {
117
  $screen = get_current_screen();
118
- if ($screen->id == 'toplevel_page_enhanced-ecommerce-google-analytics-admin-display' || (isset($_GET['page']) && $_GET['page'] == 'enhanced-ecommerce-google-analytics-admin-display')) {
 
 
 
119
  wp_register_style('font_awesome', '//use.fontawesome.com/releases/v5.0.13/css/all.css');
120
  wp_enqueue_style('font_awesome');
121
  wp_register_style('plugin-bootstrap',ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/css/bootstrap.min.css');
@@ -149,8 +154,10 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
149
  */
150
  public function enqueue_scripts() {
151
  $screen = get_current_screen();
152
- if ($screen->id == 'toplevel_page_enhanced-ecommerce-google-analytics-admin-display' || (isset($_GET['page']) && $_GET['page'] == 'enhanced-ecommerce-google-analytics-admin-display')) {
153
-
 
 
154
  wp_enqueue_script( 'custom-jquery', ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js', array( 'jquery' ), $this->version, false );
155
  wp_register_script('popper_bootstrap', '//cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js');
156
  wp_enqueue_script('popper_bootstrap');
@@ -186,60 +193,7 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
186
  }
187
  }
188
 
189
- /**
190
- * Display Admin Page.
191
- *
192
- * @since 1.0.0
193
- */
194
- public function display_admin_page() {
195
- $google_detail = $this->google_detail;
196
- $plan_id = 1;
197
- if(isset($google_detail['setting'])){
198
- $googleDetail = $google_detail['setting'];
199
- if(isset($googleDetail->plan_id) && !in_array($googleDetail->plan_id, array("1"))){
200
- $plan_id = $googleDetail->plan_id;
201
- }
202
- }
203
- add_menu_page(
204
- 'Tatvic EE Plugin', 'Tatvic EE Plugin', 'manage_options', "enhanced-ecommerce-google-analytics-admin-display", array($this, 'showPage'), plugin_dir_url(__FILE__) . 'images/tatvic_logo.png', 26
205
- );
206
- add_submenu_page('enhanced-ecommerce-google-analytics-admin-display', 'Google Analytics', 'Google Analytics', 'manage_options', 'enhanced-ecommerce-google-analytics-admin-display' );
207
- add_submenu_page(
208
- 'enhanced-ecommerce-google-analytics-admin-display',
209
- esc_html__('Google Ads', 'enhanced-ecommerce-google-analytics-admin-display'),
210
- esc_html__('Google Ads', 'enhanced-ecommerce-google-analytics-admin-display'),
211
- 'manage_options',
212
- 'enhanced-ecommerce-google-analytics-admin-display&tab=google_ads',
213
- array($this, 'showPage')
214
- );
215
- add_submenu_page(
216
- 'enhanced-ecommerce-google-analytics-admin-display',
217
- esc_html__('Google Shopping', 'enhanced-ecommerce-google-analytics-admin-display'),
218
- esc_html__('Google Shopping', 'enhanced-ecommerce-google-analytics-admin-display'),
219
- 'manage_options',
220
- 'enhanced-ecommerce-google-analytics-admin-display&tab=google_shopping_feed',
221
- array($this, 'showPage')
222
- );
223
- add_submenu_page(
224
- 'enhanced-ecommerce-google-analytics-admin-display',
225
- esc_html__('Account Summary', 'enhanced-ecommerce-google-analytics-admin-display'),
226
- esc_html__('Account Summary', 'enhanced-ecommerce-google-analytics-admin-display'),
227
- 'manage_options',
228
- 'enhanced-ecommerce-google-analytics-admin-display&tab=account',
229
- array($this, 'showPage')
230
- );
231
- if($plan_id == 1){
232
- add_submenu_page(
233
- 'enhanced-ecommerce-google-analytics-admin-display',
234
- esc_html__('Free Vs Pro', 'enhanced-ecommerce-google-analytics-admin-display'),
235
- esc_html__('Free Vs Pro', 'enhanced-ecommerce-google-analytics-admin-display'),
236
- 'manage_options',
237
- 'enhanced-ecommerce-google-analytics-admin-display&tab=pricings',
238
- array($this, 'showPage')
239
- );
240
- }
241
- }
242
- protected function create_head(){
243
  $google_detail = $this->google_detail;
244
  if(isset($google_detail['setting'])){
245
  $googleDetail = $google_detail['setting'];
@@ -338,11 +292,10 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
338
  $( '.tvc-notification' ).on( 'click', '.is-dismissible-notification', function( event, el ) {
339
  var this_id = $(this);
340
  var ee_dismiss_id = $(this).attr("data-id");
341
- jQuery.post(myAjaxNonces.ajaxurl,{
342
  action: "tvc_call_notification_dismiss",
343
  data:{ee_dismiss_id:ee_dismiss_id},
344
- dataType: "json",
345
- TVCNonce: myAjaxNonces.apiotificationDismissNonce
346
  },function( response ){
347
  var rsp = JSON.parse(response);
348
  if(rsp.status == "success"){
@@ -356,56 +309,14 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
356
  </div>
357
  <?php } ?>
358
  </div>
359
- <?php /*
360
- <div class="account-dropdown">
361
- <button type="button" class="btn dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
362
- <?php
363
- if (isset($_GET['tab']) && $_GET['tab'] == 'configuration_summary') {
364
- echo 'Configuration Summary';
365
- } else {
366
- echo 'Account Summary';
367
- }
368
- ?>
369
- </button>
370
- <div class="dropdown-menu dropdown-menu-right">
371
- <ul>
372
- <li><a href="<?php echo admin_url('admin.php?page=enhanced-ecommerce-google-analytics-admin-display&id=account-summary'); ?>" class="dropdown-item" type="button">Account Summary</a></li>
373
- <li><a href="<?php echo admin_url('admin.php?page=enhanced-ecommerce-google-analytics-admin-display&id=configuration-summary'); ?>" class="dropdown-item" type="button">Configuration Summary</a></li>
374
- </ul>
375
- </div>
376
- </div> */ ?>
377
  </div>
378
  </nav>
379
  </div>
380
  </div>
381
  <?php
382
  }
383
- /**
384
- * Display Tab page.
385
- *
386
- * @since 1.0.0
387
- */
388
- public function showPage() {
389
- echo '<div class="tvc_plugin_container">';
390
- require_once( 'partials/enhanced-ecommerce-google-analytics-admin-display.php');
391
- $this->create_head();
392
- echo '<div class="container">
393
- <div class="wiz-tab">';
394
- new TVC_Tabs();
395
- echo $this->call_tvc_site_verified_and_domain_claim();
396
- if (!empty($_GET['tab'])) {
397
- $get_action = $_GET['tab'];
398
- } else {
399
- $get_action = "general_settings";
400
- }
401
- if (method_exists($this, $get_action)) {
402
- $this->$get_action();
403
- }
404
- echo '</div>
405
- </div>
406
- </div>';
407
- echo $this->get_tvc_popup_message();
408
- }
409
  public function check_nall_and_message($val, $msg, $msg_false){
410
  if((isset($val) && $val != "" && $val != 0) ){
411
  return $msg;
@@ -413,85 +324,4 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
413
  return $msg_false;
414
  }
415
  }
416
- public function pricings(){
417
- require_once(ENHANCAD_PLUGIN_DIR . 'admin/partials/pricings.php');
418
- new TVC_Pricings();
419
- }
420
- public function account(){
421
- require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
422
- require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/account.php');
423
- new TVC_Account();
424
- }
425
- public function general_settings() {
426
- require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
427
- require_once( 'partials/general-fields.php');
428
- }
429
- public function google_ads() {
430
- require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
431
- require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-ads.php');
432
- new GoogleAds();
433
- }
434
- public function google_shopping_feed() {
435
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
436
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed.php');
437
- new GoogleShoppingFeed();
438
- }
439
- public function gaa_config_page() {
440
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
441
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed-gaa-config.php');
442
- new GAAConfiguration();
443
- }
444
- public function sync_product_page() {
445
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
446
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed-sync-product.php');
447
- new SyncProductConfiguration();
448
- }
449
- public function shopping_campaigns_page() {
450
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
451
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed-shopping-campaigns.php');
452
- new CampaignsConfiguration();
453
- }
454
- public function add_campaign_page() {
455
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
456
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/add-campaign.php');
457
- new AddCampaign();
458
- }
459
- /*
460
- public function conversion_tracking() {
461
- require_once( 'partials/conversion-tracking.php');
462
- }
463
- public function google_optimize() {
464
- require_once( 'partials/google-optimize.php');
465
- }
466
- public function about_plugin() {
467
- require_once( 'partials/about-plugin.php');
468
- }
469
- public function country_location() {
470
- // date function to hide 30% off sale after certain date
471
- return date_default_timezone_set('Australia/Sydney'); // Change this depending on what timezone your in
472
- }
473
- public function today() {
474
- $this->country_location();
475
- return strtotime(date('Y-m-d'));
476
- }
477
-
478
- public function current_time() {
479
- $this->country_location();
480
- return strtotime(date('h:i A'));
481
- }
482
-
483
- public function start_date() {
484
- $this->country_location();
485
- return strtotime(date('Y') . '-09-01');
486
- }
487
-
488
- public function end_date() {
489
- $this->country_location();
490
- return strtotime(date('Y') . '-09-08');
491
- }
492
-
493
- public function end_time() {
494
- $this->country_location();
495
- return strtotime('11:59 PM');
496
- }*/
497
  }
58
  $this->plugin_name = $plugin_name;
59
  $this->version = $version;
60
  $this->url = $this->get_onboarding_page_url();
61
+ $this->site_url = "admin.php?page=conversios";
62
  $this->pro_plan_site = $this->get_pro_plan_site();
63
  $this->google_detail = $this->get_ee_options_data();
64
+ //remove container_old_ui_head hook once new UI all implemented
65
+ add_action('container_old_ui_head',array($this,'create_head'));
66
  }
67
  public function tvc_admin_notice(){
68
  // add fixed message notification
72
  }else{
73
  if(!$this->get_subscriptionId()){
74
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_a">
75
+ <p>Tatvic EE plugin is now fully compatible with Google Analytics 4. Also, explore the new features of Google Shopping and Dynamic remarketing to reach million of shoppers across Google and scale your eCommerce business faster. <a href="admin.php?page=conversios"><b><u>CONFIGURE NOW</u></b></a></p>
76
  </div>';
77
  }
78
  }
83
  $googleDetail = $google_detail['setting'];
84
  if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id =="" && $this->subscriptionId != "" ){
85
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_b">
86
+ <p>Leverage the power of Google Shopping to reach out millions of shoppers across Google. Automate entire Google Shopping and get eligible for free listing when user searches on Google for products similar to your eCommerce business. <a href="admin.php?page=conversios"><b><u>Automate now</u></b></a></p>
87
  </div>';
88
 
89
  }
117
  */
118
  public function enqueue_styles() {
119
  $screen = get_current_screen();
120
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) ) {
121
+ if($_GET['page'] == "conversios_onboarding"){
122
+ return;
123
+ }
124
  wp_register_style('font_awesome', '//use.fontawesome.com/releases/v5.0.13/css/all.css');
125
  wp_enqueue_style('font_awesome');
126
  wp_register_style('plugin-bootstrap',ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/css/bootstrap.min.css');
154
  */
155
  public function enqueue_scripts() {
156
  $screen = get_current_screen();
157
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) ) {
158
+ if($_GET['page'] == "conversios_onboarding"){
159
+ return;
160
+ }
161
  wp_enqueue_script( 'custom-jquery', ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js', array( 'jquery' ), $this->version, false );
162
  wp_register_script('popper_bootstrap', '//cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js');
163
  wp_enqueue_script('popper_bootstrap');
193
  }
194
  }
195
 
196
+ public function create_head(){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  $google_detail = $this->google_detail;
198
  if(isset($google_detail['setting'])){
199
  $googleDetail = $google_detail['setting'];
292
  $( '.tvc-notification' ).on( 'click', '.is-dismissible-notification', function( event, el ) {
293
  var this_id = $(this);
294
  var ee_dismiss_id = $(this).attr("data-id");
295
+ jQuery.post(tvc_ajax_url,{
296
  action: "tvc_call_notification_dismiss",
297
  data:{ee_dismiss_id:ee_dismiss_id},
298
+ dataType: "json"
 
299
  },function( response ){
300
  var rsp = JSON.parse(response);
301
  if(rsp.status == "success"){
309
  </div>
310
  <?php } ?>
311
  </div>
312
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  </div>
314
  </nav>
315
  </div>
316
  </div>
317
  <?php
318
  }
319
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  public function check_nall_and_message($val, $msg, $msg_false){
321
  if((isset($val) && $val != "" && $val != 0) ){
322
  return $msg;
324
  return $msg_false;
325
  }
326
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  }
admin/class-tvc-admin-helper.php CHANGED
@@ -751,26 +751,39 @@ Class TVC_Admin_Helper{
751
 
752
  public function call_tvc_site_verified_and_domain_claim(){
753
  $google_detail = $this->get_ee_options_data();
754
- $class = 'notice notice-error tvc-notice-error';
755
  if(!isset($_GET['welcome_msg']) && isset($google_detail['setting']) && $google_detail['setting'] ){
756
  $googleDetail = $google_detail['setting'];
757
 
758
  if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id){
759
- $message = "";
 
760
  $call_js_function_args="";
761
  if (isset($googleDetail->is_site_verified) && isset($googleDetail->is_domain_claim) && $googleDetail->is_site_verified == '0' && $googleDetail->is_domain_claim == '0') {
762
- $message = esc_html__('Site verification and domain claim for merchant center account failed. Without a verified and claimed website, your products will get disapproved.');
 
763
  $call_js_function_args = "both";
764
  }else if(isset($googleDetail->is_site_verified) && $googleDetail->is_site_verified == '0'){
765
- $message = esc_html__('Site verification and domain claim for merchant center account failed. Without a verified and claimed website, your products will get disapproved.');
 
766
  $call_js_function_args = "site_verified";
767
- }/*else if(isset($googleDetail->is_domain_claim) && $googleDetail->is_domain_claim == '0'){
768
- $message = esc_html__('Domain claim for merchant center account failed. Without a verified and claimed website, your products will get disapproved.');
 
769
  $call_js_function_args = "domain_claim";
770
- }*/
771
- if($message!= ""){
772
- printf('<div class="%1$s"><p><b>%2$s Click <a href="javascript:void(0)" id="call_both_verification" onclick="call_tvc_site_verified_and_domain_claim(\'%3$s\');">here</a></b> to verify and claim the domain.</p></div>', esc_attr($class), esc_html($message),$call_js_function_args);
773
  ?>
 
 
 
 
 
 
 
 
 
 
 
774
  <script>
775
  function call_tvc_site_verified_and_domain_claim(call_args){
776
  var tvs_this = event.target;
@@ -779,9 +792,8 @@ Class TVC_Admin_Helper{
779
  if(call_args == "domain_claim"){
780
  call_domain_claim_both();
781
  }else{
782
- jQuery.post(myAjaxNonces.ajaxurl,{
783
- action: "tvc_call_site_verified",
784
- apiDomainClaimNonce: myAjaxNonces.SiteVerifiedNonce
785
  },function( response ){
786
  var rsp = JSON.parse(response);
787
  if(rsp.status == "success"){
@@ -800,9 +812,8 @@ Class TVC_Admin_Helper{
800
  }
801
  function call_domain_claim_both(first_message=null){
802
  //console.log("call_domain_claim");
803
- jQuery.post(myAjaxNonces.ajaxurl,{
804
- action: "tvc_call_domain_claim",
805
- apiDomainClaimNonce: myAjaxNonces.apiDomainClaimNonce
806
  },function( response ){
807
  var rsp = JSON.parse(response);
808
  if(rsp.status == "success"){
@@ -819,7 +830,7 @@ Class TVC_Admin_Helper{
819
  }, 4000);
820
  }
821
  }else{
822
- tvc_helper.tvc_alert("error","",rsp.message,true)
823
  }
824
  $("#both_verification-spinner").remove();
825
  });
@@ -1079,6 +1090,10 @@ Class TVC_Admin_Helper{
1079
  return "https://conversios.io/pricings/";
1080
  }
1081
 
 
 
 
 
1082
  public function is_ga_property(){
1083
  $data = $this->get_ee_options_settings();
1084
  $is_connected = false;
@@ -1106,5 +1121,43 @@ Class TVC_Admin_Helper{
1106
  return $this->plan_id = $plan_id;
1107
  }
1108
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1109
 
1110
  }
751
 
752
  public function call_tvc_site_verified_and_domain_claim(){
753
  $google_detail = $this->get_ee_options_data();
 
754
  if(!isset($_GET['welcome_msg']) && isset($google_detail['setting']) && $google_detail['setting'] ){
755
  $googleDetail = $google_detail['setting'];
756
 
757
  if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id){
758
+ $title = "";
759
+ $notice_text ="";
760
  $call_js_function_args="";
761
  if (isset($googleDetail->is_site_verified) && isset($googleDetail->is_domain_claim) && $googleDetail->is_site_verified == '0' && $googleDetail->is_domain_claim == '0') {
762
+ $title = "Site verification and Domain claim for merchant center account failed.";
763
+ $message = "Without a verified and claimed website, your product will get disapproved.";
764
  $call_js_function_args = "both";
765
  }else if(isset($googleDetail->is_site_verified) && $googleDetail->is_site_verified == '0'){
766
+ $title = "Site verification for merchant center account failed.";
767
+ $message = "Without a verified and claimed website, your product will get disapproved.";
768
  $call_js_function_args = "site_verified";
769
+ }else if(isset($googleDetail->is_domain_claim) && $googleDetail->is_domain_claim == '0'){
770
+ $title = "Site verification for merchant center account failed.";
771
+ $message = "Without a verified and claimed website, your product will get disapproved.";
772
  $call_js_function_args = "domain_claim";
773
+ }
774
+ if($message!= "" && $title != ""){
 
775
  ?>
776
+ <div class="errormsgtopbx claimalert">
777
+ <div class="errmscntbx">
778
+ <div class="errmsglft">
779
+ <span class="errmsgicon"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/error-white-icon.png'; ?>" alt="error" /></span>
780
+ </div>
781
+ <div class="erralertrigt">
782
+ <h6><?php echo $title; ?></h6>
783
+ <p><?php echo $message; ?> <a href="javascript:void(0)" id="call_both_verification" onclick="call_tvc_site_verified_and_domain_claim('<?php echo $call_js_function_args; ?>');">Click here</a> to verify and claim the domain.</p>
784
+ </div>
785
+ </div>
786
+ </div>
787
  <script>
788
  function call_tvc_site_verified_and_domain_claim(call_args){
789
  var tvs_this = event.target;
792
  if(call_args == "domain_claim"){
793
  call_domain_claim_both();
794
  }else{
795
+ jQuery.post(tvc_ajax_url,{
796
+ action: "tvc_call_site_verified"
 
797
  },function( response ){
798
  var rsp = JSON.parse(response);
799
  if(rsp.status == "success"){
812
  }
813
  function call_domain_claim_both(first_message=null){
814
  //console.log("call_domain_claim");
815
+ jQuery.post(tvc_ajax_url,{
816
+ action: "tvc_call_domain_claim"
 
817
  },function( response ){
818
  var rsp = JSON.parse(response);
819
  if(rsp.status == "success"){
830
  }, 4000);
831
  }
832
  }else{
833
+ tvc_helper.tvc_alert("error","",rsp.message,true,10000)
834
  }
835
  $("#both_verification-spinner").remove();
836
  });
1090
  return "https://conversios.io/pricings/";
1091
  }
1092
 
1093
+ public function get_conversios_site_url(){
1094
+ return "https://conversios.io/";
1095
+ }
1096
+
1097
  public function is_ga_property(){
1098
  $data = $this->get_ee_options_settings();
1099
  $is_connected = false;
1121
  return $this->plan_id = $plan_id;
1122
  }
1123
  }
1124
+
1125
+ /*
1126
+ * get user plan id
1127
+ */
1128
+ public function get_user_subscription_data(){
1129
+ $google_detail = $this->get_ee_options_data();
1130
+ if(isset($google_detail['setting'])){
1131
+ return $google_detail['setting'];
1132
+ }
1133
+ }
1134
+
1135
+ /*
1136
+ * conver curency code to currency symbols
1137
+ */
1138
+ public function get_currency_symbols($code){
1139
+ $currency_symbols = array(
1140
+ 'USD'=>'$', // US Dollar
1141
+ 'EUR'=>'€', // Euro
1142
+ 'CRC'=>'₡', // Costa Rican Colón
1143
+ 'GBP'=>'£', // British Pound Sterling
1144
+ 'ILS'=>'₪', // Israeli New Sheqel
1145
+ 'INR'=>'₹', // Indian Rupee
1146
+ 'JPY'=>'¥', // Japanese Yen
1147
+ 'KRW'=>'₩', // South Korean Won
1148
+ 'NGN'=>'₦', // Nigerian Naira
1149
+ 'PHP'=>'₱', // Philippine Peso
1150
+ 'PLN'=>'zł', // Polish Zloty
1151
+ 'PYG'=>'₲', // Paraguayan Guarani
1152
+ 'THB'=>'฿', // Thai Baht
1153
+ 'UAH'=>'₴', // Ukrainian Hryvnia
1154
+ 'VND'=>'₫' // Vietnamese Dong
1155
+ );
1156
+ if(isset($currency_symbols[$code]) && $currency_symbols[$code] != "") {
1157
+ return $currency_symbols[$code];
1158
+ }else{
1159
+ return $code;
1160
+ }
1161
+ }
1162
 
1163
  }
admin/css/custom-style.css CHANGED
@@ -2528,8 +2528,6 @@ textarea.form-control, .select2.select2-container--default textarea.select2-sele
2528
  width: 100%;
2529
  height: 100%;
2530
  }
2531
-
2532
- /*# sourceMappingURL=style.css.map */
2533
  #feed-spinner {
2534
  /* height: 50px; */
2535
  left: 0;
2528
  width: 100%;
2529
  height: 100%;
2530
  }
 
 
2531
  #feed-spinner {
2532
  /* height: 50px; */
2533
  left: 0;
admin/css/daterangepicker.css ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .daterangepicker{position:absolute;color:inherit;background-color:#fff;border-radius:6px;
2
+ box-shadow: 0px 0px 60px rgba(0, 0, 0, 0.12); width:278px;max-width:none;padding:0;margin-top:7px;top:100px;left:20px;z-index:3001;display:none;line-height:1em;}
3
+ .daterangepicker:before, .daterangepicker:after{position:absolute;display:inline-block;border-bottom-color:rgba(0, 0, 0, 0.2);content:none;}
4
+ .daterangepicker:before{top:-7px;border-right:7px solid transparent;border-left:7px solid transparent;border-bottom:7px solid #ccc;}
5
+ .daterangepicker:after{top:-6px;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;}
6
+ .daterangepicker.opensleft:before{right:40px;}
7
+ .daterangepicker.opensleft:after{right:41px;}
8
+ .daterangepicker.openscenter:before{left:0;right:0;width:0;margin-left:auto;margin-right:auto;}
9
+ .daterangepicker.openscenter:after{left:0;right:0;width:0;margin-left:auto;margin-right:auto;}
10
+ .daterangepicker.opensright:before{left:9px;}
11
+ .daterangepicker.opensright:after{left:10px;}
12
+ .daterangepicker.drop-up{margin-top:-7px;}
13
+ .daterangepicker.drop-up:before{top:initial;bottom:-7px;border-bottom:initial;border-top:7px solid #ccc;}
14
+ .daterangepicker.drop-up:after{top:initial;bottom:-6px;border-bottom:initial;border-top:6px solid #fff;}
15
+ .daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar{float:none;}
16
+ .daterangepicker.single .drp-selected{display:none;}
17
+ .daterangepicker.show-calendar .drp-calendar{display:block;}
18
+ .daterangepicker.show-calendar .drp-buttons{display:block;}
19
+ .daterangepicker.auto-apply .drp-buttons{display:none;}
20
+ .daterangepicker .drp-calendar{display:none;max-width:270px;}
21
+ .daterangepicker .drp-calendar.left{padding-top:8px; padding-bottom:8px;}
22
+ .daterangepicker .drp-calendar.right{padding-top:8px; padding-bottom: 8px}
23
+ .daterangepicker .drp-calendar.single .calendar-table{border:none;}
24
+ .daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span{color:#fff;display:inline-block;}
25
+
26
+ .daterangepicker .calendar-table th, .daterangepicker .calendar-table td{white-space:nowrap;text-align:center;vertical-align:middle;min-width:30px;width:30px;height:30px;line-height:22px;font-size:14px;border-radius:0px; padding: 0;white-space:nowrap;cursor:pointer;}
27
+ .daterangepicker .calendar-table th{color: #B6B6B6; font-weight: 500;}
28
+ .daterangepicker .calendar-table{border:1px solid #fff;border-radius:4px;background-color:#fff;}
29
+ .daterangepicker .calendar-table table{width:100%;margin:0;border-spacing:0;border-collapse:collapse;}
30
+ .daterangepicker td.available:hover, .daterangepicker th.available:hover{background-color:#F1F6FA;border-color:transparent;color:inherit;}
31
+ .daterangepicker td.week, .daterangepicker th.week{font-size:80%;color:#ccc;}
32
+ .daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date{background-color:#fff;border-color:transparent;color:#B6B6B6;}
33
+ .daterangepicker td.in-range{background-color:#ebf4f8;border-color:transparent;color:#002BFC;border-radius:0;}
34
+ .daterangepicker td.start-date{border-radius:6px 0 0 6px;}
35
+ .daterangepicker td.end-date{border-radius:0 6px 6px 0;}
36
+ .daterangepicker td.start-date.end-date{border-radius:6px;}
37
+ .daterangepicker td.active, .daterangepicker td.active:hover{background-color:#002BFC;border-color:transparent;color:#fff;}
38
+ .daterangepicker th.month{width:auto; color: #1e1e1e; font-weight: 500;}
39
+ .daterangepicker td.disabled, .daterangepicker option.disabled{color:#B6B6B6;cursor:not-allowed;text-decoration:line-through;}
40
+ .daterangepicker select.monthselect, .daterangepicker select.yearselect{font-size:12px;padding:1px;height:auto;margin:0;cursor:default;}
41
+ .daterangepicker select.monthselect{margin-right:2%;width:56%;}
42
+ .daterangepicker select.yearselect{width:40%;}
43
+ .daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect{width:50px;margin:0 auto;background:#eee;border:1px solid #eee;padding:2px;outline:0;font-size:12px;}
44
+ .daterangepicker .calendar-time{text-align:center;margin:4px auto 0 auto;line-height:30px;position:relative;}
45
+ .daterangepicker .calendar-time select.disabled{color:#ccc;cursor:not-allowed;}
46
+ .daterangepicker .drp-buttons{clear:both;padding:15px 0 0;border-top:1px solid #e9e9e9;display:none;line-height:12px;vertical-align:middle;}
47
+ .daterangepicker .drp-selected{display:inline-block;font-size:12px;padding-right:8px;}
48
+ .daterangepicker .drp-buttons .btn{margin-left:15px;font-size:12px;font-weight:400;padding:5px 10px; border:none; box-shadow: none;}
49
+ .daterangepicker.show-ranges.single.rtl .drp-calendar.left{border-right:1px solid #ddd;}
50
+ .daterangepicker.show-ranges.single.ltr .drp-calendar.left{border-left:1px solid #ddd;}
51
+ .daterangepicker .ranges{float:none;text-align:left;margin:0;}
52
+ .daterangepicker.show-calendar .ranges{padding-top:6px;}
53
+ .daterangepicker .ranges ul{list-style:none;margin:0 auto;padding:0;width:100%;}
54
+ .daterangepicker .ranges li{font-size:12px;padding:6px 15px;cursor:pointer; line-height: 20px; color: #1e1e1e;}
55
+ .daterangepicker .ranges li:hover{background-color:#F1F6FA;}
56
+
57
+ @media (min-width:576px){.daterangepicker{width:auto; max-width: 690px;}
58
+ .daterangepicker .ranges ul{width:100%;}
59
+ .daterangepicker.single .ranges ul{width:100%;}
60
+ .daterangepicker.single .drp-calendar.left{clear:none;}
61
+ .daterangepicker.single .ranges, .daterangepicker.single .drp-calendar{float:left;}
62
+ .daterangepicker{direction:ltr;text-align:left;}
63
+ .daterangepicker .drp-calendar.left{clear:left;margin-right:0;}
64
+ .daterangepicker .drp-calendar.left .calendar-table{border-right:none;border-top-right-radius:0;border-bottom-right-radius:0;}
65
+ .daterangepicker .drp-calendar.right{margin-left:0;}
66
+ .daterangepicker .drp-calendar.right .calendar-table{border-left:none;border-top-left-radius:0;border-bottom-left-radius:0;}
67
+
68
+ }
69
+ @media (min-width:730px){.daterangepicker .ranges{width:auto; max-width: 690px;}
70
+ .daterangepicker .drp-calendar.left{clear:none !important;}
71
+ }
admin/css/enhanced-ecommerce-google-analytics-admin.css CHANGED
@@ -356,7 +356,7 @@ button.tvc_animate_btn:hover::after, button.tvc_animate_btn:focus::after {
356
  padding: 0 1.4rem 0 1rem;
357
  margin: 10px 14px 0 2px;
358
  }
359
- .tvc_plugin_container .modal-dialog{ max-width: 700px;}
360
  .change_prodct_feed_cat {text-decoration: underline; transition: all ease 0.5s; color: #03a2b7; margin-left: 10px; cursor: pointer;}
361
  .config-head-active a{ color: #fff !important;}
362
  #staticBackdrop .modal-body, #tvc_google_connect .modal-body{padding: 2rem;}
356
  padding: 0 1.4rem 0 1rem;
357
  margin: 10px 14px 0 2px;
358
  }
359
+ .conversios-body-part .modal-dialog{ max-width: 700px;}
360
  .change_prodct_feed_cat {text-decoration: underline; transition: all ease 0.5s; color: #03a2b7; margin-left: 10px; cursor: pointer;}
361
  .config-head-active a{ color: #fff !important;}
362
  #staticBackdrop .modal-body, #tvc_google_connect .modal-body{padding: 2rem;}
admin/css/responsive.css CHANGED
@@ -1,9 +1,34 @@
 
 
 
 
 
 
 
 
 
 
1
  @media only screen and (min-width:1025px) and (max-width:1199px){
2
  /*------ sync-product -----*/
3
  .navinfotopnav ul li a{padding:0 10px 16px;}
4
  .navsmrybx{padding:15px;}
5
  .navsmrysmalltxt{font-size:13px;}
6
  .navsmrybigtxt{font-size:36px;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  }
8
  @media only screen and (max-width:1024px){
9
 
@@ -14,6 +39,29 @@
14
  .navsmrybx{padding:15px;min-height:110px;}
15
  .navsmrysmalltxt{font-size:13px;}
16
  .navsmrybigtxt{font-size:36px;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  }
18
  @media only screen and (max-width:991px){
19
  /*----- onboarding ----*/
@@ -39,12 +87,44 @@
39
  .syncpropagination ul li{margin:0 2px;}
40
  .pgprevbtn{margin-right:10px;}
41
  .pgnextbtn{margin-left:10px;}
42
- }
43
- @media only screen and (min-width:783px) and (max-width:960px){
44
- .site-header{left: 36px !important;}
45
- }
46
- @media only screen and (max-width:782px){
47
- .site-header{left: 0px !important;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
  @media only screen and (max-width:767px){
50
  /*----- onboarding ------*/
@@ -87,6 +167,77 @@
87
  .syncpropagination ul li{margin:0 1px;}
88
  .pgprevbtn{margin-right:8px}
89
  .pgnextbtn{margin-left:8px}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  }
91
  @media only screen and (min-width:639px) and (max-width:767px){
92
  /*------ sync-product -----*/
@@ -98,6 +249,17 @@
98
  .prdcinfopp-left{max-width:260px;flex:0 0 260px;}
99
  .prdtpplrg-imgwrp{width:280px;height:280px;}
100
  .prdcinfopp-right{max-width:280px;flex:0 0 280px;}
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
  @media only screen and (max-width:639px){
103
  /*------ sync-product -----*/
@@ -106,11 +268,30 @@
106
  .hdrtpright{max-width: 100%; flex: 1;}
107
  .hdrcntcbx{display: none;}
108
  .mblhdrcntcbx{display: inherit; max-width:100%;flex:0 0 100%;padding:10px 15px 0;margin-left:0;border-left:none;border-top:1px solid #e9e9e9;margin-top:15px;}
109
-
110
  .navsmryitem{max-width:50%;flex:0 0 50%;}
111
  .prdcinfopp-right{max-width:305px;flex:0 0 305px;padding-left:0px;margin-top:15px}
112
  .prdcinfobody{justify-content:center;}
113
  .prdctinfopp-cntn{max-width:400px;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
  @media only screen and (min-width:576px) and (max-width:639px){
116
  /*------ onboarding -----*/
@@ -118,6 +299,10 @@
118
  .chckbxbgbx{display: inherit;}
119
  .chckbxbgbx .cstmcheck-item{margin-right: 0px; margin-bottom: 9px;}
120
  .chckbxbgbx .cstmcheck-item:last-child{margin-bottom: 0px;}
 
 
 
 
121
  }
122
  @media only screen and (max-width:575px){
123
 
@@ -139,6 +324,43 @@
139
  .errmsgright{margin-left:0;padding-left:0;border-left:none;margin-left:28px;}
140
  .prdchdrsrch, .prdcfilter{padding:0 5px;}
141
  .syncprdcbtn{min-width:200px;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  }
143
  @media only screen and (max-width:479px){
144
 
@@ -162,6 +384,29 @@
162
  .pgprevbtn{margin-right: 1px;}
163
  .pgnextbtn{margin-left: 1px;}
164
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  }
166
  @media only screen and (max-width:399px){
167
  /*------- onboarding -------*/
@@ -178,6 +423,23 @@
178
 
179
  /*------ sync-product -----*/
180
  .hdrcntcbx{font-size:13px;line-height:20px;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  }
182
  @media only screen and (max-width:359px){
183
  /*------ sync-product -----*/
@@ -187,6 +449,16 @@
187
  .prdtpplrg-imgwrp{width:260px;height:260px;}
188
  .prdcinfopp-right{max-width:260px;flex:0 0 260px;}
189
  .hdrtpright{min-width: 100%; flex: 0 0 100%; margin-top: 6px;}
 
 
 
 
 
 
 
 
 
 
190
 
191
  }
192
  img{max-width:100%;height:auto;}
1
+ @media only screen and (min-width:1300px) and (max-width:1399px){
2
+ /*----- dashboard ------*/
3
+ .ecomchrtinfoflex{padding-left: 10px;}
4
+
5
+ }
6
+ @media only screen and (min-width:1200px) and (max-width:1299px){
7
+ /*----- dashboard ------*/
8
+ .ecomchrtinfoflex{padding-left: 0px;}
9
+
10
+ }
11
  @media only screen and (min-width:1025px) and (max-width:1199px){
12
  /*------ sync-product -----*/
13
  .navinfotopnav ul li a{padding:0 10px 16px;}
14
  .navsmrybx{padding:15px;}
15
  .navsmrysmalltxt{font-size:13px;}
16
  .navsmrybigtxt{font-size:36px;}
17
+
18
+ /*----- dashboard ------*/
19
+ .dshreporttble th, .dshreporttble td{padding: 13px 12px;}
20
+ .dshsmrylrgtxt{font-size: 36px; line-height: 42px;}
21
+ .ecomchrtinfoflex{padding-left: 0px; margin: 0 -10px;}
22
+ .ecomchartinfoitem{padding: 0 10px;}
23
+ .ecomchartinfolabel{max-width: 100%; }
24
+ .chartpercarrow{position: relative; right: 0; left: 0%;}
25
+
26
+ /*------ map product category ------*/
27
+ .sbmaincat{padding-right: 0px;}
28
+ .sbmaincat .mapcataccr-top{padding-right: 0px;}
29
+ .mapcataccr-title{min-width:280px;}
30
+ .mapcataccr-select{width: 240px;}
31
+
32
  }
33
  @media only screen and (max-width:1024px){
34
 
39
  .navsmrybx{padding:15px;min-height:110px;}
40
  .navsmrysmalltxt{font-size:13px;}
41
  .navsmrybigtxt{font-size:36px;}
42
+
43
+ /*------ account setting ------*/
44
+ .bilnginfo-scalability .sclabilityleft{max-width: 380px; flex: 1;}
45
+ .bilnginfo-scalability .sclabilityright{max-width: 300px; flex: 0 0 300px;}
46
+
47
+ /*----- dashboard ------*/
48
+ .dshreporttble th, .dshreporttble td{line-height: 18px; padding: 13px 12px;}
49
+ .dshsmrylrgtxt{font-size: 32px; line-height: 38px;}
50
+ .ecomchrtinfoflex{padding-left: 0px; margin: 0 -10px;}
51
+ .ecomchartinfoitem{padding: 0 10px;}
52
+ .ecomchartinfolabel{max-width: 100%; font-size: 11px; align-items: flex-start; min-height: 30px;}
53
+ .chartpercarrow{position: relative; right: 0; left: 0%;}
54
+
55
+ /*------ map product category ------*/
56
+ .sbmaincat{padding-right: 0px;}
57
+ .sbmaincat .mapcataccr-top{padding-right: 0px;}
58
+ .mapcataccr-title{min-width:270px;}
59
+ .mapcataccr-select{width: 222px;}
60
+
61
+ /*------ Map Product Attributes -----*/
62
+ .mapproattr-left .msppageinfotxt::after{margin-left: 20px; margin-right: 20px;}
63
+ .attrfrmleft label{font-size: 14px; line-height: 22px;}
64
+
65
  }
66
  @media only screen and (max-width:991px){
67
  /*----- onboarding ----*/
87
  .syncpropagination ul li{margin:0 2px;}
88
  .pgprevbtn{margin-right:10px;}
89
  .pgnextbtn{margin-left:10px;}
90
+
91
+ /*------ account setting ------*/
92
+ .accstngdtl-top h2{font-size: 24px;}
93
+ .cnvs-table th{font-size: 14px;}
94
+ .bilnginfo-scalability .sclabilityleft{max-width: 100%;}
95
+ .bilnginfo-scalability .sclabilityright{margin-top: 24px;}
96
+
97
+ /*------ dashboard -------*/
98
+ .claimalert .errmscntbx{display: flex; flex-wrap: nowrap;}
99
+ .claimalert .errmsglft{max-width:54px; flex: 0 0 54px;}
100
+ .dshreporttble th, .dshreporttble td{font-size: 13px; line-height: 18px; padding: 12px 6px;}
101
+ th.prdnm-cell, td.prdnm-cell{max-width: 120px;}
102
+ .tddshpertg{font-size: 13px;}
103
+ .dshsmrylrgtxt{font-size: 30px; line-height: 36px;}
104
+ .ecomchrtinfoflex{padding-left: 0px; margin: 0 -8px}
105
+ .chartpercarrow{position: relative; right: 0; left: 0%;}
106
+ .ecomchartinfoitem{padding: 0 8px;}
107
+ .ecomchartinfolabel{max-width: 100%; font-size: 11px; align-items: flex-start; min-height: 30px;}
108
+ .ggladschrtbx{padding-bottom: 9px}
109
+
110
+ /*------ map product category ------*/
111
+ .msppageinfowrap{padding-left: 0px;}
112
+ .msppageinfotxt::after{margin: 0 30px;}
113
+ .mapcataccr-title::before,.mapcataccr-title::after{content: none;}
114
+ .mapcataccr-top{display: inherit;}
115
+ .mapcataccr-title{min-width: inherit;}
116
+ .sbmaincat .mapcataccr-top{padding-right: 0px;}
117
+ .sbmaincat{padding: 18px 0px 0px 25px}
118
+ .topactive:before{left: 2px;}
119
+ .sbmaincat:before{left: 2px;}
120
+ .mapcataccr-select{padding-left: 10px;}
121
+
122
+ /*------ Map Product Attributes -----*/
123
+ .msppageinfotxt .attrinfoicon{margin-left: 4px;}
124
+ .mapproattr-left .msppageinfotxt::after{margin-left: 5px; margin-right: 5px;}
125
+ .attrfrmright{flex: 0 0 100%;}
126
+ .attrfrmleft label{font-size: 14px; line-height: 22px;}
127
+
128
  }
129
  @media only screen and (max-width:767px){
130
  /*----- onboarding ------*/
167
  .syncpropagination ul li{margin:0 1px;}
168
  .pgprevbtn{margin-right:8px}
169
  .pgnextbtn{margin-left:8px}
170
+
171
+ /*------ account setting ------*/
172
+ .accsrng-left{display: none; opacity: 0; visibility: hidden; right: -230px;}
173
+ .accsrng-right{border-left: none;}
174
+ .accstng-cntwrap{display: inherit;}
175
+ .accstngdtl-top{padding: 18px 15px 18px 15px}
176
+ .accstng-dtlarea{padding: 24px 15px; margin-bottom: 0px; padding-top: 24px !important;}
177
+ .accstngdtl-top h2{font-size: 18px; line-height: 1.2;}
178
+ .accstngdtl-item{margin-bottom: 24px;}
179
+ .mblacntstng-lefttrgr{display: flex; align-items: center; justify-content: center;}
180
+ .showaccsrngleft{display: inherit; opacity: 1; visibility: visible; right:0;}
181
+ .accprdcsyncstng-item{margin-bottom: 30px;}
182
+
183
+ /*------ dashboard -------*/
184
+ .dashsmrybx{max-width: 50%; flex: 0 0 50%; border-bottom:1px solid #e9e9e9;}
185
+ .mblsmry3bx{flex: 0 0 33.3333%; max-width: 33.3333%;}
186
+ .dashsmrybx:last-child{border-bottom: none;}
187
+ .col50{flex: 0 0 100%; max-width: 100%;}
188
+ .ecomfunnchart .col50:last-child{margin-top: 24px;}
189
+ .dshrprttp-left{max-width: 75%; flex: 0 0 75%;}
190
+ .dshrprttp-right{max-width: 25%; flex: 0 0 25%;}
191
+ .dashtp-right{margin-top: 9px; flex:0 0 100%; max-width: 100%; justify-content: flex-start}
192
+ .dashtablewrp{margin-top: 24px;}
193
+ .claimalert .errmscntbx{display: flex; flex-wrap: nowrap;}
194
+ .claimalert .errmsglft{max-width:54px; flex: 0 0 54px;}
195
+ .erralertrigt h6, .erralertrigt p{line-height: 18px}
196
+ .dshsmrylrgtxt{font-size: 36px; line-height: 42px;}
197
+ .ecomchrtinfoflex{padding-left: 0px;}
198
+ .chartpercarrow{position: relative; right: 0; left: 0%;}
199
+ .ecomchartinfolabel{max-width: 100%; align-items: flex-start; min-height: 30px;}
200
+ .ggladschrtbx{padding-bottom: 9px}
201
+
202
+ .daterangepicker {width: 96%;}
203
+
204
+
205
+ /*---- responsive table ----*/
206
+ .mbl-table{border:none; }
207
+ .mbl-table tr{margin-bottom: 15px; display: block; border-bottom: 1px solid #e3ecf1;}
208
+ .mbl-table th, .mbl-table td{text-align: left;}
209
+ table.bt thead,table.bt tbody th{display:none;}
210
+ table.bt tfoot th,table.bt tfoot td,table.bt tbody td{border:none;display:block;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;vertical-align:top;float:left\9;width:100%\9; padding: 0px; }
211
+ table.bt tbody td{border: 1px solid #e3ecf1; border-bottom: none; align-items: center;}
212
+ table.bt tfoot th::before,table.bt tfoot td::before,table.bt tbody td::before{content:attr(data-th) "";display:inline-block;-webkit-flex-shrink:0;-ms-flex-shrink:0;flex-shrink:0;font-weight:bold;width:34%;}
213
+ table.bt tfoot th.bt-hide,table.bt tfoot td.bt-hide,table.bt tbody td.bt-hide{display:none;}
214
+ table.bt tfoot th .bt-content,table.bt tfoot td .bt-content,table.bt tbody td .bt-content{vertical-align:top;}
215
+ .bt-wrapper.active{max-height:310px;overflow:auto;-webkit-overflow-scrolling:touch;}
216
+ table.bt.bt--no-header tfoot td::before,table.bt.bt--no-header tbody td::before{display:none;}
217
+ table.bt tfoot th:before,
218
+ table.bt tfoot td:before,
219
+ table.bt tbody td:before {background: #F1F6FA;margin-right:10px;padding: 10px 8px; font-weight: 400; color: #1e1e1e;}
220
+ th.prdnm-cell, td.prdnm-cell{max-width: 100%;}
221
+ td.prdnm-cell a{width: 96%;}
222
+
223
+
224
+ /*------ map product category ------*/
225
+ .backlnkbtn{margin-right: 10px}
226
+ .mapcataccr-detail{padding: 0px;}
227
+ .backlnkbtn img{max-width: 20px;}
228
+ .msppageinfowrap{padding-left: 0; }
229
+ .msppageinfotxt::after{margin: 0 15px 0 15px; height: 20px;}
230
+ .mapcataccr-top{flex-wrap: wrap; padding-right: 0px; display: inherit;}
231
+ .mapcataccr-title::before,.mapcataccr-title::after{content: none;}
232
+ .topactive::before{content: none;}
233
+ .mapcataccr-title{width: 100%; max-width: 298px; min-width: inherit;}
234
+ .sbmaincat{padding-right: 0px;}
235
+ .mapcataccr-item{padding:12px 15px}
236
+
237
+ /*------ Map Product Attributes -----*/
238
+ .mapproattr-right{max-width: 100%; flex: 0 0 100%; margin-top: 20px;}
239
+
240
+
241
  }
242
  @media only screen and (min-width:639px) and (max-width:767px){
243
  /*------ sync-product -----*/
249
  .prdcinfopp-left{max-width:260px;flex:0 0 260px;}
250
  .prdtpplrg-imgwrp{width:280px;height:280px;}
251
  .prdcinfopp-right{max-width:280px;flex:0 0 280px;}
252
+
253
+
254
+ /*------ account setting ------*/
255
+ .bilnginfo-scalability .sclabilityleft{max-width:380px; flex: 1}
256
+ .bilnginfo-scalability .sclabilityright{margin-top: 0px;max-width: 300px; flex: 0 0 300px;}
257
+
258
+ /*----- map product category page ------*/
259
+ .sbmaincat::before{left: 1px; top: 0}
260
+ .sbmaincat .mapcataccr-top::after{left: 1px;}
261
+ .sbmainlevel:last-child::before{height: 35px;}
262
+
263
  }
264
  @media only screen and (max-width:639px){
265
  /*------ sync-product -----*/
268
  .hdrtpright{max-width: 100%; flex: 1;}
269
  .hdrcntcbx{display: none;}
270
  .mblhdrcntcbx{display: inherit; max-width:100%;flex:0 0 100%;padding:10px 15px 0;margin-left:0;border-left:none;border-top:1px solid #e9e9e9;margin-top:15px;}
 
271
  .navsmryitem{max-width:50%;flex:0 0 50%;}
272
  .prdcinfopp-right{max-width:305px;flex:0 0 305px;padding-left:0px;margin-top:15px}
273
  .prdcinfobody{justify-content:center;}
274
  .prdctinfopp-cntn{max-width:400px;}
275
+
276
+ /*----- dashboard -------*/
277
+ .dshsmrylrgtxt{font-size: 32px; line-height: 38px;}
278
+ .daterangepickerflex{display: inherit;}
279
+ .daterangepicker-left{max-width: 100%;}
280
+ .daterangepicker-right{padding: 12px 15px;}
281
+
282
+ /*------ map product category ------*/
283
+
284
+ .mapcataccr-top{display: inherit;}
285
+ .sbmaincat{padding: 12px 0px 0px 20px}
286
+ .sbmaincat::before, .sbmaincat .mapcataccr-top::after{border-left-width: 1px;}
287
+ .sbmaincat::before{top: 0; left: 1px;}
288
+ .sbmaincat .mapcataccr-top::before{border-top-width: 1px; left: -15px;}
289
+ .sbmaincat::after{width: 5px; height: 7px; top: 14px; left: 15px;}
290
+ .sbmaincat .mapcataccr-top::after{left: 1px; height: calc(100% + 3px)}
291
+ .sbmainlevel:last-child::before{height: 28px;}
292
+
293
+
294
+
295
  }
296
  @media only screen and (min-width:576px) and (max-width:639px){
297
  /*------ onboarding -----*/
299
  .chckbxbgbx{display: inherit;}
300
  .chckbxbgbx .cstmcheck-item{margin-right: 0px; margin-bottom: 9px;}
301
  .chckbxbgbx .cstmcheck-item:last-child{margin-bottom: 0px;}
302
+
303
+
304
+
305
+
306
  }
307
  @media only screen and (max-width:575px){
308
 
324
  .errmsgright{margin-left:0;padding-left:0;border-left:none;margin-left:28px;}
325
  .prdchdrsrch, .prdcfilter{padding:0 5px;}
326
  .syncprdcbtn{min-width:200px;}
327
+
328
+ /*------ account setting ------*/
329
+ .acnttypebx-item{max-width: 100%; flex: 0 0 100%; margin-bottom:15px;}
330
+ .acnttypebx-item:last-child{margin-bottom: 0px;}
331
+ .accstngdtl-checkbx .cstmcheck-item{display: inline-flex; width: 100%;}
332
+ .accnthlpiocn{min-width: 18px; margin-top: 2px;}
333
+ .accprdcsync-left{flex: 0 0 100%; padding-right: 0px;}
334
+ .accprdcsync-right{flex: 0 0 100%; padding-left: 0px; margin-top: 9px; align-items:flex-start;}
335
+ .cnvs-table th{font-size: 14px; line-height: 22px; padding: 9px 5px;}
336
+
337
+ /*------ dashboard ------*/
338
+ .dsh-reprttop{display: block;}
339
+ .dshrprttp-left{max-width: 100%; flex: 0 0 100%;}
340
+ .dshrprttp-right{max-width: 100%; flex: 0 0 100%; margin-top: 9px;}
341
+ .dshsmrylrgtxt{font-size: 30px; line-height: 36px;}
342
+ .drp-calendar.left::before{content: none;}
343
+ .drp-calendar{max-width: 100%; flex: 0 0 100%;}
344
+ .drp-calendar.left,.drp-calendar.right{padding-left:0; padding-right: 0px; }
345
+ .daterangepicker {width: 276px; left: 50% !important;right: 50% !important; margin-left: -138px;}
346
+ .drpbtmbuttonflex{display: inherit;}
347
+ .drpbtmbuttonflex-left, .drpbtmbuttonflex-right{max-width: 100%; flex: 0 0 100%;}
348
+ .drpbtmbuttonflex-right{margin-top: 9px;}
349
+
350
+
351
+ /*---- map category page ------*/
352
+ .sbmaincat{padding: 12px 0px 0px 15px}
353
+ .sbmaincat::before, .sbmaincat .mapcataccr-top::after{border-left-width: 1px;}
354
+ .sbmaincat::before{top: 0; left: 1px;}
355
+ .sbmaincat .mapcataccr-top::before{border-top-width: 1px; left: -10px;}
356
+ .sbmaincat::after{width: 5px; height: 7px; top: 14px; left: 10px;}
357
+ .sbmaincat .mapcataccr-top::after{left: 1px; height: calc(100% + 3px)}
358
+ .sbmainlevel:last-child::before{height: 28px;}
359
+
360
+ /*------ Map Product Attributes -----*/
361
+ .attrfrmleft label{font-size: 14px; line-height: 22px;}
362
+
363
+
364
  }
365
  @media only screen and (max-width:479px){
366
 
384
  .pgprevbtn{margin-right: 1px;}
385
  .pgnextbtn{margin-left: 1px;}
386
 
387
+
388
+ /*------- dashboard ------*/
389
+ .dashtp-right{display: block; margin-top: 9px; justify-content: flex-start;}
390
+ .dshtpdaterange{margin-left: 0; margin-top: 6px;}
391
+ .dsh-reprttop{display: block;}
392
+ .dashsmrybx{max-width: 100%; flex: 0 0 100%;}
393
+ .ecomfunnchrtbx{padding: 15px;}
394
+ .ecomchrtinfoflex{margin: 0 -10px;}
395
+ .ecomchartinfoitem{padding: 0 10px;}
396
+ .ecomchartinfolabel{font-size: 11px; align-items: flex-start; min-height: 34px;}
397
+
398
+ /*------ account setting ------*/
399
+ .acntmrchnt-item{flex-wrap: wrap;}
400
+ .accmrchntitem-left{flex: 0 0 230px;}
401
+ .accmrchntitem-right{flex: 0 0 160px; min-width: 160px; margin-top: 6px;}
402
+
403
+ /*---- map category page -------*/
404
+ .mapcataccr-select{width: 100%;}
405
+ .mapcataccr-select select{width: 220px;}
406
+
407
+ /*------- map product attributes ------*/
408
+ .attrfrmleft{flex: 0 0 100%;}
409
+ .infoicontrgr::after{left: inherit; right: 0; margin-left: 0; margin-right: -15px;}
410
  }
411
  @media only screen and (max-width:399px){
412
  /*------- onboarding -------*/
423
 
424
  /*------ sync-product -----*/
425
  .hdrcntcbx{font-size:13px;line-height:20px;}
426
+ .dshrprttp-left h4{margin-right: 0px;}
427
+
428
+ /*------- dahboard -----*/
429
+ .ecomchartinfolabel{font-size: 10px; align-items: flex-start; min-height: 34px;}
430
+ .chartpercarrow{font-size: 10px; padding: 9px 0 9px 5px;}
431
+
432
+ /*---- map category page -------*/
433
+ .sbmaincat{padding: 12px 0px 0px 10px}
434
+ .sbmaincat::before, .sbmaincat .mapcataccr-top::after{border-left-width: 1px;}
435
+ .sbmaincat::before{top: 0;}
436
+ .sbmaincat .mapcataccr-top::before{border-top-width: 1px; left: -5px;}
437
+ .sbmaincat::after{width: 5px; height: 7px; top: 14px; left: 10px;}
438
+ .sbmaincat .mapcataccr-top::after{left: 1px; height: calc(100% + 3px)}
439
+ .mapcataccr-detail .mapcataccr-title{padding-left: 6px;}
440
+ .mapcataccr-select select{width: 200px;}
441
+ .mapcataccr-title{font-size: 14px; line-height:22px;}
442
+
443
  }
444
  @media only screen and (max-width:359px){
445
  /*------ sync-product -----*/
449
  .prdtpplrg-imgwrp{width:260px;height:260px;}
450
  .prdcinfopp-right{max-width:260px;flex:0 0 260px;}
451
  .hdrtpright{min-width: 100%; flex: 0 0 100%; margin-top: 6px;}
452
+ .dashtpleft-btn{margin-right: 3px;}
453
+
454
+ /*------- dahboard -----*/
455
+ .ecomchartinfolabel{font-size: 10px; align-items: flex-start; min-height: 34px;}
456
+ .chartpercarrow{font-size: 10px; padding: 9px 0 9px 5px;}
457
+
458
+ /*---- map category page -------*/
459
+
460
+ .mapcataccr-select select{width: 175px;}
461
+
462
 
463
  }
464
  img{max-width:100%;height:auto;}
admin/css/style.css CHANGED
@@ -2,6 +2,9 @@
2
  @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,500&display=swap');
3
  /*rc */
4
  .update-nag, .updated, .error, .is-dismissible, .notice { display: none !important; }
 
 
 
5
  #create_merchant_account{
6
  background-color: #002BFC;
7
  border-radius: 6px;
@@ -38,14 +41,14 @@
38
  75%{opacity: 1; margin-top: -20%;}
39
  100%{opacity: 0;margin-top: 40%;}
40
  }
41
- #tvc_popup_box.tvc_popup_box_close{
42
  display: none;
43
  animation: tvc_popup_box_close 0.5s;
44
  -webkit-animation: tvc_popup_box_close 0.5s;
45
  -webkit-animation-fill-mode: forwards;
46
  animation-fill-mode: forwards;
47
  }
48
- #tvc_popup_box.tvc_popup_box{
49
  overflow: hidden;
50
  /*transform: translate(-50%, -50%);*/
51
  z-index: 9999;
@@ -56,6 +59,24 @@
56
  left: 500px;
57
  top: 10px;
58
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  .alert-message {
60
  text-align: right;
61
  }
@@ -208,7 +229,53 @@ button:disabled,button[disabled], button:disabled:hover,button[disabled]:hover{b
208
  top: 0;}
209
  .cstmcheck-item-pro label{padding-left: 13px !important;}
210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  /*rc */
 
 
 
212
  /* comman style----------------------*/
213
  body{margin:0;padding:0;font-family:'Roboto', sans-serif;font-size:14px;color:#515151;scroll-behavior:smooth;overflow-x:hidden;font-weight:normal;}
214
  a{text-decoration:none;color:#002BFC;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;}
@@ -217,42 +284,33 @@ a:focus{outline:none;text-decoration:none;}
217
  :focus{outline:none;}
218
  h1,h2,h3,h4,h5,h6{margin:0 0 10px 0;color:#1E1E1E;padding:0;line-height:1.2;font-family:'Roboto', sans-serif;text-transform:none;font-weight:700;}
219
  h1{font-size:36px;}
220
- h2{font-size:30px;}
221
  h3{font-size:24px;}
222
  h4{font-size:18px;line-height:24px;}
223
- h5{font-size:16px;}
224
  h6{font-size:14px;line-height:22px;}
225
  h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:inherit;text-decoration:none;font-size:inherit;}
226
  h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{text-decoration:none;}
227
- onbording-right.small{display:block;margin-top:4px;}
228
  p{margin:0 0 20px 0;line-height:22px;}
229
  ul, ol{margin:0;}
230
- button{font-family:'Roboto', sans-serif;cursor:pointer;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;line-height:1;}
231
  strong, b{font-weight:600;}
232
  *,::after,::before{box-sizing:border-box;}
233
  select{-moz-appearance:none;-webkit-appearance:none;background-image:url(../images/selectarrow-new.png);background-repeat:no-repeat;background-position:right center;border:1px solid #C6C6C6;border-radius:6px;}
234
-
235
- img{
236
- image-rendering: -moz-crisp-edges;
237
- image-rendering: -o-crisp-edges;
238
- image-rendering: -webkit-optimize-contrast;
239
- image-rendering: crisp-edges;
240
- -ms-interpolation-mode: nearest-neighbor;
241
  }
242
-
243
  .container{max-width:1170px;margin:0 auto;padding:0 15px;}
244
  .row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}
245
  .primary-c{color:#002BFC;}
246
  .primary-bg{background-color:#002BFC;}
247
-
248
  /*------ onboarding page ------*/
249
  .onbordingbody{padding-top: 54px;}
250
  /*----- onboarding header ------*/
251
  .site-header{padding:12px 0;background-color:#ffffff;position:fixed;z-index:10;top:0;left:0;width:100%;height:54px;box-shadow:0px 1px 6px rgba(0, 0, 0, 0.12);}
252
  .brand{max-width:138px;}
253
  .brand img{max-width:138px;}
254
-
255
-
256
  /*---- onborading style ------*/
257
  .onbording-wrapper{padding:30px 0;}
258
  .smallcontainer{max-width:881px;margin:0 0 0 auto;}
@@ -299,7 +357,7 @@ input[type="radio"], input[type="checkbox"]{margin:0;}
299
  .chckbxbgbx .cstmcheck-item:last-child{margin-right:0px;}
300
  .stepnextbtn{display:inline-block;width:154px;height:42px;background-color:#2FBB0E;text-align:center;padding:10px;border-radius:6px;color:#ffffff;font-size:16px;font-weight:700;box-shadow:none;border:none;cursor:pointer;}
301
  .stepnextbtn:hover{background-color:#28d000;color:#ffffff;}
302
- .dslbbtn, .dslbbtn:hover{background-color:#CCCCCC;color:#ffffff;}
303
  .slctunivr-filed{padding-left:25px;margin-top:12px;}
304
  .slect2bx{border:1px solid #C6C6C6;width:258px;height:42px;}
305
  .botslectbxitem{margin-bottom:9px;}
@@ -370,7 +428,7 @@ input[type="radio"], input[type="checkbox"]{margin:0;}
370
  .crtemrchpplft-top p{line-height:22px;color:#515151;margin-bottom:0px;}
371
  .claimedbx{padding:10px 15px;border-radius:6px;background-color:#F3F0E4;color:#1E1E1E;font-weight:500;margin-top:24px;}
372
  .mrchntformwrp{margin-top:15px;}
373
- .fromfiled{border:1px solid #C6C6C6;border-radius:6px;width:258px;line-height:22px;font-size:14px;height:42px;padding:10px 15px;color:#1E1E1E;font-family:'Roboto', sans-serif;}
374
  .fromfiled::-webkit-input-placeholder{color:#B6B6B6;opacity:1;}
375
  .fromfiled::-moz-placeholder{color:#B6B6B6;opacity:1;}
376
  .fromfiled:-ms-input-placeholder{color:#B6B6B6;opacity:1;}
@@ -435,15 +493,18 @@ input[type="radio"], input[type="checkbox"]{margin:0;}
435
  /*------ sync product style start -----*/
436
 
437
  /*---- navinfo section ------*/
438
- .navinfowrap{margin-top:20px;background-color:#ffffff;border-radius:6px;}
439
- .navinfotopnav{padding:16px 10px 0;border-bottom:1px solid #E9E9E9;}
 
 
440
  .navinfotopnav ul{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap}
441
  .navinfotopnav ul li a{padding:0 15px 16px;display:flex;align-items:center;position:relative;font-size:16px;font-weight:500;line-height:1;color:#515151;}
442
  .navinfotopnav ul li a:before{content:"";opacity:0;position:absolute;left:0;bottom:-1px;width:100%;height:2px;background-color:#515151;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;}
443
  .navinfoicon{margin-right:8px;line-height:0;}
444
  .navinfotopnav ul li a:hover:before{opacity:1;}
 
445
  .navinfotopnav ul li.active a:before{opacity:1;background-color:#002BFC;}
446
- .navifosummary{padding:20px;}
447
  .navifosummary .row{margin-left:-10px;margin-right:-10px;}
448
  .navsmryitem{max-width:20%;flex:0 0 20%;padding:0 10px;}
449
  .navsmrybx{background-color:#ffffff;border-radius:6px;box-shadow:0px 1px 9px rgba(0, 0, 0, 0.15);padding:24px;min-height:124px}
@@ -579,7 +640,320 @@ select.properselect{min-width:64px;height:36px;margin-left:10px;padding-left:6px
579
  .prdtpp-thumb .slick-prev:hover, .prdtpp-thumb .slick-next:hover{background:#e0e8ef;}
580
  span.show-more-less-handler{color:#0083FC;padding-left:5px;}
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
  /* RC*/
584
  .onbordingbody{padding-top: 10px;}
585
  .dashboard_page_conversios_onboarding, .index_page_conversios_onboarding, .onbordingbody-wapper{background: #fff;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,500&display=swap');
3
  /*rc */
4
  .update-nag, .updated, .error, .is-dismissible, .notice { display: none !important; }
5
+ .tvc-notice-error {
6
+ display: block!important;
7
+ }
8
  #create_merchant_account{
9
  background-color: #002BFC;
10
  border-radius: 6px;
41
  75%{opacity: 1; margin-top: -20%;}
42
  100%{opacity: 0;margin-top: 40%;}
43
  }
44
+ #tvc_onboarding_popup_box.tvc_popup_box_close{
45
  display: none;
46
  animation: tvc_popup_box_close 0.5s;
47
  -webkit-animation: tvc_popup_box_close 0.5s;
48
  -webkit-animation-fill-mode: forwards;
49
  animation-fill-mode: forwards;
50
  }
51
+ #tvc_onboarding_popup_box.tvc_popup_box{
52
  overflow: hidden;
53
  /*transform: translate(-50%, -50%);*/
54
  z-index: 9999;
59
  left: 500px;
60
  top: 10px;
61
  }
62
+ #tvc_popup_box.tvc_popup_box {
63
+ width: 500px;
64
+ overflow: hidden;
65
+ background: #EEEEEE;
66
+ box-shadow: 0 0 10px black;
67
+ border-radius: 10px;
68
+ position: fixed;
69
+ top: 30%;
70
+ left: 50%;
71
+ margin-top: 100px;
72
+ transform: translate(-50%, -50%);
73
+ z-index: 9999;
74
+ padding: 10px;
75
+ text-align: center;
76
+ display: block;
77
+ animation: tvc_popup_box_open 0.5s;
78
+ -webkit-animation: tvc_popup_box_open 0.5s;
79
+ }
80
  .alert-message {
81
  text-align: right;
82
  }
229
  top: 0;}
230
  .cstmcheck-item-pro label{padding-left: 13px !important;}
231
 
232
+ /*dashboad*/
233
+ .dashbrdpage-wrap .loading-bg-effect{
234
+ font-size: 0;
235
+ background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
236
+ background-size: 400% 400%;
237
+ animation: gradient 15s ease infinite;
238
+ min-width: 30px;
239
+ }
240
+ .dashbrdpage-wrap .is_loading{
241
+ background: rgba(255,255,255,0.65) url('../images/ajax-loader.gif') no-repeat 50% 50%;
242
+ -webkit-transition: background-color 0;
243
+ transition: background-color 0;
244
+ background-size: 50px;
245
+ }
246
+ .dashbrdpage-wrap .loading-bg-effect img{display: none;}
247
+ @keyframes gradient {
248
+ 0% {
249
+ background-position: 0% 50%;
250
+ }
251
+ 50% {
252
+ background-position: 100% 50%;
253
+ }
254
+ 100% {
255
+ background-position: 0% 50%;
256
+ }
257
+ }
258
+ .product_performance_report table, .medium_performance_report table{min-height: 200px;}
259
+
260
+ .errmscntbx .tvc-nb-spinner {
261
+ display: inline-block;
262
+ position: absolute;
263
+ top: 32px;
264
+ right: 22px;
265
+ margin-left: 10px;
266
+ width: 30px;
267
+ height: 30px;
268
+ background: transparent;
269
+ border-top: 3px solid #0769D7;
270
+ border-right: 3px solid transparent;
271
+ border-radius: 50%;
272
+ -webkit-animation: 1s spin linear infinite;
273
+ animation: 1s tvc-spin linear infinite;
274
+ }
275
  /*rc */
276
+ @charset "utf-8";
277
+ @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;0,700;1,500&display=swap');
278
+
279
  /* comman style----------------------*/
280
  body{margin:0;padding:0;font-family:'Roboto', sans-serif;font-size:14px;color:#515151;scroll-behavior:smooth;overflow-x:hidden;font-weight:normal;}
281
  a{text-decoration:none;color:#002BFC;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;}
284
  :focus{outline:none;}
285
  h1,h2,h3,h4,h5,h6{margin:0 0 10px 0;color:#1E1E1E;padding:0;line-height:1.2;font-family:'Roboto', sans-serif;text-transform:none;font-weight:700;}
286
  h1{font-size:36px;}
287
+ h2{font-size:30px; line-height: 42px;}
288
  h3{font-size:24px;}
289
  h4{font-size:18px;line-height:24px;}
290
+ h5{font-size:16px; line-height: 24px;}
291
  h6{font-size:14px;line-height:22px;}
292
  h1 a,h2 a,h3 a,h4 a,h5 a,h6 a{color:inherit;text-decoration:none;font-size:inherit;}
293
  h1 a:hover,h2 a:hover,h3 a:hover,h4 a:hover,h5 a:hover,h6 a:hover{text-decoration:none;}
294
+ small{display:block;margin-top:4px;}
295
  p{margin:0 0 20px 0;line-height:22px;}
296
  ul, ol{margin:0;}
297
+ button{font-family:'Roboto', sans-serif;cursor:pointer;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;line-height:1; box-shadow:none;}
298
  strong, b{font-weight:600;}
299
  *,::after,::before{box-sizing:border-box;}
300
  select{-moz-appearance:none;-webkit-appearance:none;background-image:url(../images/selectarrow-new.png);background-repeat:no-repeat;background-position:right center;border:1px solid #C6C6C6;border-radius:6px;}
301
+ img{image-rendering: -moz-crisp-edges;image-rendering: -o-crisp-edges;image-rendering: -webkit-optimize-contrast;
302
+ image-rendering: crisp-edges;-ms-interpolation-mode: nearest-neighbor;
 
 
 
 
 
303
  }
 
304
  .container{max-width:1170px;margin:0 auto;padding:0 15px;}
305
  .row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}
306
  .primary-c{color:#002BFC;}
307
  .primary-bg{background-color:#002BFC;}
 
308
  /*------ onboarding page ------*/
309
  .onbordingbody{padding-top: 54px;}
310
  /*----- onboarding header ------*/
311
  .site-header{padding:12px 0;background-color:#ffffff;position:fixed;z-index:10;top:0;left:0;width:100%;height:54px;box-shadow:0px 1px 6px rgba(0, 0, 0, 0.12);}
312
  .brand{max-width:138px;}
313
  .brand img{max-width:138px;}
 
 
314
  /*---- onborading style ------*/
315
  .onbording-wrapper{padding:30px 0;}
316
  .smallcontainer{max-width:881px;margin:0 0 0 auto;}
357
  .chckbxbgbx .cstmcheck-item:last-child{margin-right:0px;}
358
  .stepnextbtn{display:inline-block;width:154px;height:42px;background-color:#2FBB0E;text-align:center;padding:10px;border-radius:6px;color:#ffffff;font-size:16px;font-weight:700;box-shadow:none;border:none;cursor:pointer;}
359
  .stepnextbtn:hover{background-color:#28d000;color:#ffffff;}
360
+
361
  .slctunivr-filed{padding-left:25px;margin-top:12px;}
362
  .slect2bx{border:1px solid #C6C6C6;width:258px;height:42px;}
363
  .botslectbxitem{margin-bottom:9px;}
428
  .crtemrchpplft-top p{line-height:22px;color:#515151;margin-bottom:0px;}
429
  .claimedbx{padding:10px 15px;border-radius:6px;background-color:#F3F0E4;color:#1E1E1E;font-weight:500;margin-top:24px;}
430
  .mrchntformwrp{margin-top:15px;}
431
+ .fromfiled{border:1px solid #C6C6C6;border-radius:6px;width:100%; max-width:258px;line-height:22px;font-size:14px;height:42px;padding:10px 15px;color:#1E1E1E;font-family:'Roboto', sans-serif;}
432
  .fromfiled::-webkit-input-placeholder{color:#B6B6B6;opacity:1;}
433
  .fromfiled::-moz-placeholder{color:#B6B6B6;opacity:1;}
434
  .fromfiled:-ms-input-placeholder{color:#B6B6B6;opacity:1;}
493
  /*------ sync product style start -----*/
494
 
495
  /*---- navinfo section ------*/
496
+ .navinfowrap{margin-top:20px;}
497
+ .navinfotopnav{padding:16px 0px 0;background-color: #ffffff; border-bottom:1px solid #E9E9E9; position: sticky; top: 0; z-index: 2;
498
+ overflow: hidden; border-radius:6px 6px 0 0;
499
+ }
500
  .navinfotopnav ul{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap}
501
  .navinfotopnav ul li a{padding:0 15px 16px;display:flex;align-items:center;position:relative;font-size:16px;font-weight:500;line-height:1;color:#515151;}
502
  .navinfotopnav ul li a:before{content:"";opacity:0;position:absolute;left:0;bottom:-1px;width:100%;height:2px;background-color:#515151;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;}
503
  .navinfoicon{margin-right:8px;line-height:0;}
504
  .navinfotopnav ul li a:hover:before{opacity:1;}
505
+ .navinfotopnav ul li.active a{color: #002BFC}
506
  .navinfotopnav ul li.active a:before{opacity:1;background-color:#002BFC;}
507
+ .navifosummary{padding:20px; background-color: #ffffff; border-radius: 0 0 6px 6px;}
508
  .navifosummary .row{margin-left:-10px;margin-right:-10px;}
509
  .navsmryitem{max-width:20%;flex:0 0 20%;padding:0 10px;}
510
  .navsmrybx{background-color:#ffffff;border-radius:6px;box-shadow:0px 1px 9px rgba(0, 0, 0, 0.15);padding:24px;min-height:124px}
640
  .prdtpp-thumb .slick-prev:hover, .prdtpp-thumb .slick-next:hover{background:#e0e8ef;}
641
  span.show-more-less-handler{color:#0083FC;padding-left:5px;}
642
 
643
+ /*---- account settings -------*/
644
+ .accstng-cntwrap{display: flex;}
645
+ .accsetting-wrap{background-color: #ffffff;}
646
+ .accsrng-left{padding: 10px 4px 10px 5px; width: 209px; min-width: 209px; transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;}
647
+ .accsrng-left ul{margin: 0; padding: 0; list-style: none; position: sticky; top: 62px;}
648
+ .accsrng-left ul li a{padding: 10px 15px; display: block; color: #B6B6B6; line-height: 22px;}
649
+ .accsrng-left ul li a:hover{color: #1e1e1e;}
650
+ .accsrng-left ul li.active a{color: #1e1e1e; font-weight: 500; background-color: #F1F6FA;}
651
+ .accsrng-right{border-left: 1px solid #E9E9E9; flex: 1;}
652
+ .accstngdtl-top{padding: 18px 21px 18px 30px; background-color: #ffffff; display: flex; align-items: center;
653
+ position: sticky; top: 57px; z-index: 1;
654
+ }
655
+ .accstngdtltp-shdw{box-shadow: 0px 4px 18px rgba(0, 0, 0, 0.06);}
656
+ .accstngdtl-top h2{flex: 1; margin-bottom: 0px; font-weight: 500;}
657
+
658
+ .savebtn{display:inline-block;width:91px;height:36px;background-color:#2FBB0E;text-align:center;padding:10px;border-radius:6px;color:#ffffff;font-size:14px;font-weight:500; line-height: 20px; box-shadow:none;border:none;cursor:pointer;}
659
+ .savebtn:hover{background-color:#28d000;color:#ffffff;}
660
+ .accstng-dtlarea{padding: 24px 30px; border-bottom: 1px solid #E9E9E9; margin-bottom: 24px;}
661
+ .accstng-item:last-child .accstng-dtlarea{margin-bottom: 0px;}
662
+ .acnttype-ttl strong{font-weight: 500; color: #1e1e1e}
663
+ .acnttype-bx{ background-color: #F1F6FA; padding: 20px 5px; margin: 12px 0 0; border-radius: 6px; display: flex; flex-wrap: wrap;}
664
+ .accstngdtl-item{margin-bottom: 40px;}
665
+ .accstngdtl-item:last-child{margin-bottom: 30px;}
666
+ .acnttypebx-item{max-width: 33.3333%; flex: 0 0 33.3333%; padding: 0 15px;}
667
+ .acnttypebx-item h6{font-weight: 500; margin-bottom: 6px;}
668
+ .acnttypebx-item p{margin-bottom: 3px;}
669
+ .changelink{line-height: 20px; font-size: 12px; color: #0083FC;}
670
+ .changelink:hover{color: #1E1E1E;}
671
+ .accstngdtl-item h5{margin-bottom: 12px; font-weight: 500;}
672
+ .accnthlpiocn{vertical-align: middle;}
673
+
674
+ .accstngdtl-item .cstmcheck-item .checkmark::before{top: 2px;}
675
+ .accstngdtl-checkbx .cstmcheck-item {margin-bottom: 9px;}
676
+ .accstngdtl-checkbx .cstmcheck-item:last-child {margin-bottom: 0px;}
677
+ .inputinfotxt strong{font-weight: 500;}
678
+ .impthsld-formrow .inputinfotxt{max-width: 258px;}
679
+ .acnpolyinfo{font-size: 12px; padding-left: 25px; line-height:18px; max-width: 283px; line-height: 18px;}
680
+ .acnpolyinfo a{color: #0083FC; }
681
+ .acnpolyinfo a:hover{color: #1e1e1e; }
682
+
683
+ .acntmrchnt-item{display: flex; align-items: center; margin-bottom: 24px;}
684
+ .accmrchntitem-left{flex: 1; max-width: 230px; font-size: 16px; font-weight: 500; line-height: 24px; color: #515151;}
685
+ .accmrchntitem-btn{min-width: 160px; max-width: 160px; border: 1px solid #c6c6c6; font-size: 14px; line-height: 22px; color: #515151; background-color: transparent; box-shadow: none; border-radius: 6px; padding: 6px 10px; text-align: center;}
686
+ .erraccbtn{border-color: #F51321; color: #F51321;}
687
+ .accbluebtn{border-color: #002BFC; color:#002BFC; max-width: 160px;}
688
+ .accmrchntitem-btn img{vertical-align: middle; margin-right: 8px; line-height: 0;}
689
+ button.accmrchntitem-btn:hover{border-color: #1e1e1e;}
690
+ .gglmrchnt-item .accstng-dtlarea{padding-bottom: 30px;}
691
+ .accprdcsyncsettng-list{margin-bottom: 54px;}
692
+ .accprdcsyncsettng-list:last-child{margin-bottom: 28px;}
693
+ .accprdcsyncstng-item h5{font-weight: 500; margin-bottom: 6px;}
694
+ .accprdcsyncstng-item{margin-bottom: 54px;}
695
+ .accprdcsyncstng-item:last-child{margin-bottom: 0px;}
696
+ .accprdcsyncitem-cnt{display: flex; flex-wrap: wrap;}
697
+ .accprdcsync-left{flex: 1; padding-right: 15px;}
698
+ .accprdcsync-right{max-width: 175px; padding-left: 15px; display: flex; flex-direction: column; align-items: flex-end;}
699
+ .justbrdrbtn{min-width: 160px; border: 1px solid #c6c6c6; font-size: 14px; line-height: 22px; color: #515151; background-color: transparent; box-shadow: none; border-radius: 6px; padding: 6px 10px; text-align: center;}
700
+ .justbrdrbtn:hover{color: #1e1e1e; border-color: #1e1e1e;}
701
+ .mappedcnt-info{font-size: 12px; line-height: 20px; margin-top: 6px; color:#1e1e1e;}
702
+ .accprdcsync-left p{margin-bottom: 0px;}
703
+ .accprdcsync-left p a{color: #0083FC; }
704
+ .accprdcsync-left p a:hover{color: #1e1e1e; }
705
+ .blnginfo-table{border-radius: 5px; overflow: hidden;}
706
+ .cnvs-table{width: 100%; border:1px solid #E9E9E9; border-radius: 5px;border-collapse: collapse;}
707
+ .cnvs-table th{background-color: #F1F6FA; width: 33%; font-size: 16px; line-height: 24px; color: #1e1e1e; font-weight: 500; text-align: center; padding: 9px 10px;}
708
+ .cnvs-table td{padding: 10px; text-align: center; font-size: 14px; line-height: 22px; color: #515151;}
709
+ .cnvs-table th, .cnvs-table td{border-right:1px solid #e9e9e9;}
710
+ .cnvs-table th:last-child, .cnvs-table td:last-child{border-right:0px solid #e9e9e9;}
711
+ .bilnginfo-scalability .scalabilitybx{padding: 38px 30px;}
712
+ .bilnginfo-scalability .sclabilityright{flex: 1; width: 100%; padding-left: 15px;}
713
+ .stickylitem .accstngdtl-top{box-shadow: 0px 4px 18px rgba(0, 0, 0, 0.06);}
714
+ .dslbbtn, .dslbbtn:hover{background-color:#CCCCCC;color:#ffffff;}
715
+ .mblacntstng-lefttrgr{display: none; position: fixed; right: 0; top: 15%; background-color: #ffffff; padding:6px 8px; border-radius: 6px 0 0 6px; cursor: pointer; z-index: 2;
716
+ transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;
717
+ box-shadow: 0px 4px 18px rgba(0, 0, 0, 0.06)}
718
+ .showmblacntstng{right:208px; }
719
+ .showaccsrngleft{position: fixed; right: -230px; top:15%; background-color: #ffffff; z-index: 2; box-shadow: 0px 4px 18px rgba(0, 0, 0, 0.06)}
720
+ .mblacngstngleft{display: none;}
721
+
722
+ /*------- dashboard ----------*/
723
+ .dflex{display: flex; flex-wrap: wrap; }
724
+ .mt24{margin-top: 24px;}
725
+ .mb24{margin-bottom: 24px;}
726
+ .dashtpleft-btn{background-color: #ffffff; box-shadow: none; border-radius: 6px; border:1px solid #0083FC; padding: 4px 9px 3px; font-size: 14px; line-height: 24px; color: #0083FC; margin-right: 12px;}
727
+ .dashtpleft-btn:last-child{margin-right: 0px;}
728
+ .dashtpleft-btn img{vertical-align: top; margin-right: 5px; display: inline-block; margin-top: 2px;}
729
+ .dashtpleft-btn:hover{background-color: #f4f4f4;}
730
+ .dashtp-right{flex: 1;}
731
+ .dashtp-right{flex: 1; display: flex; justify-content: flex-end; align-items: center;}
732
+ .dshtprightselect{min-width: 150px;}
733
+ .dshtprightselect select{width: 100%; padding: 4px 26px 4px 10px; line-height: 22px;}
734
+ .dshtpdaterange{margin-left: 24px; position: relative; padding: 5px 20px 5px 24px; line-height: 24px; font-size: 14px; font-weight: 500; color: #515151; cursor: pointer}
735
+ .dateclndicn{position: absolute; left: 0; top: 7px; line-height: 0;}
736
+ .careticn{position: absolute; right: 0; top: 7px; line-height: 0;}
737
+ .claimalert .errmscntbx{width: 100%; padding: 0; align-items: inherit; overflow: hidden;}
738
+ .erralertrigt {padding: 5px 15px 5px 0; overflow: hidden;}
739
+ .claimalert .errmsglft{background-color: #FA424D; min-width: 54px; padding: 15px 0 15px 15px; }
740
+ .erralertrigt{padding-left:15px; }
741
+ .erralertrigt h6{margin-bottom: 0px; color: #1e1e1e;}
742
+ .erralertrigt p{margin-bottom: 0px; color: #515151}
743
+ .erralertrigt p a{color:#0083FC;}
744
+ .erralertrigt p a:hover{color:#1e1e1e;}
745
+ .wht-rnd-shdwbx{background-color: #ffffff; box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.12); border-radius: 6px;}
746
+ .dashsmry-wrap{display: flex; flex-wrap: wrap;}
747
+ .dashsmry-item{display: flex; flex-wrap: wrap; width: 100%; border-bottom: 1px solid #e9e9e9; }
748
+ .dashsmry-item:last-child{border-bottom: none;}
749
+ .dashsmrybx{flex:1; border-right: 1px solid #E9E9E9; padding: 14px 9px; text-align: center;}
750
+ .dshsmrycattxt{line-height: 22px; font-size: 14px; color: #515151;}
751
+ .dshsmrylrgtxt{margin-top: 3px; font-size: 42px; line-height: 48px; color: #1e1e1e; font-weight: 300; }
752
+ .updownsmry{margin-top: 9px;display: inline-flex; align-items: center; font-size: 16px; line-height: 24px;}
753
+ .updownsmry img{margin-right: 4px;}
754
+ .dshsmryprdtxt{line-height: 20px; font-size: 12px; color: #B6B6B6; margin-top: 3px;}
755
+
756
+ /*---- ecoomerce chart ------*/
757
+ .chartbx{background-color: #ffffff; border-radius: 6px; position: relative;}
758
+ .ecomfunnchrtbx{padding: 24px; height: 100%;}
759
+ .col50{flex: 0 0 50%; max-width: 50%; padding: 0 15px;}
760
+ .prochrtftr{position: relative; filter: blur(12px); -webkit-filter:blur(12px);}
761
+ .prochrtovrbox{position: absolute; left: 0; top: 0; width: 100%; height: 100%; justify-content: center; align-items: center; display: flex;
762
+ background: linear-gradient(360deg, #F8F8F8 0%, rgba(248, 248, 248, 0.8) 41.15%, rgba(248, 248, 248, 0.6) 71.35%, rgba(248, 248, 248, 0) 100%);}
763
+ .prochrtcntn{max-width: 300px; text-align: center;}
764
+ .prochrttop{display: inline-flex; align-items: center; color: #F6B028; font-weight: 500; line-height: 24px; margin-bottom: 13px;}
765
+ .prochrttop img{margin-right: 3px;}
766
+ .prochrtcntn h5{font-weight: 500; margin-bottom: 3px; color: #515151;}
767
+ .prochrtcntn p{margin-bottom: 0;}
768
+ .blueupgrdbtn{margin-top: 12px; border:none; box-shadow: none; background-color: #002BFC; color: #ffffff; font-weight: 700; font-size: 16px; line-height: 24px; padding: 9px 28px; border-radius: 6px; min-width: 154px; text-align: center;}
769
+ .blueupgrdbtn:hover{background-color: #1e1e1e;}
770
+ .chartarea img{width: 100%;}
771
+ .whiteroundedbx{background-color: #ffffff; border-radius: 6px;}
772
+ .dshreport-sec{padding: 30px 20px 5px;}
773
+ .dsh-reprttop{display: flex; flex-wrap: wrap;}
774
+ .dshrprttp-left, .dshrprttp-right{flex: 1; padding: 0 15px;}
775
+ .dshrprttp-left{display: flex; align-items: center; flex-wrap: wrap;}
776
+ .dshrprttp-left h4{font-weight:400; margin-bottom: 0px; margin-right: 24px;}
777
+ .dshrprttp-right{display: flex; justify-content: flex-end;}
778
+ .dshrprttp-right .prdchdrsrchicon{margin-right: 24px;}
779
+ .viewallbtn{display: inline-flex; align-items: center; color: #0083FC;}
780
+ .dashtablewrp{margin-top: 30px;}
781
+ .dshreporttble{width: 100%; border-collapse: collapse;}
782
+ .dshreporttble thead{background-color: #F1F6FA;}
783
+ .dshreporttble th, .dshreporttble td{border-right: none; text-align: center; color: #1e1e1e; font-size: 14px;}
784
+ .dshreporttble th{padding: 13px 15px; font-size: 14px; font-weight: 400; line-height: 22px; }
785
+ .dshreporttble td{padding: 16px 15px; border-bottom: 1px solid #E9E9E9;}
786
+ .dshreporttble tr:last-child td{border-bottom: 0px solid #E9E9E9;}
787
+ th.prdnm-cell, td.prdnm-cell{text-align:left; max-width: 150px; }
788
+ th.hide{font-size: 0 !important;}
789
+ th.tddot-cell,td.tddot-cell{text-align: right;}
790
+ td.prdnm-cell a{color: #0083FC; width: 100%; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; display: inline-block;}
791
+ td.prdnm-cell a:hover{color: #1e1e1e}
792
+
793
+ .chartbx h5{ font-weight: 500; color: #515151; margin-bottom: 24px;}
794
+ .ggladsperfom-sec{padding: 30px 20px 10px;}
795
+ .ggladsperfom-sec h4{font-weight: 400; margin-bottom: 18px;}
796
+ .ggladschrtbx{filter: drop-shadow(0px 0px 6px rgba(0, 0, 0, 0.18)); padding: 24px 0 6px 6px;}
797
+ .ggladschrtbx h5{padding-left: 18px;}
798
+ .ggladsperfom-sec .col50{margin-bottom: 20px;}
799
+ .tddshpertg{font-size: 14px; font-weight: 700; color: #b6b6b6;}
800
+ .chartcntainer{width: 100%; height: 540px;}
801
+ .ecomchartinfo{margin-top: 24px}
802
+ .ecomchrtinfoflex{display: flex; padding:0 0 0 40px; margin: 0 -15px; }
803
+ .ecomchartinfoitem{padding: 0 15px 0 15px; flex: 0 0 20%; max-width: 20%; position: relative;}
804
+ .chartpercarrow{display: inline-block; background-image: url(../images/percentage-arrow-bg.png); background-position: 6px; background-repeat: repeat-x; position: relative; height: 38px; padding:9px 0px 9px 10px; font-size: 12px; font-weight: 500; line-height: 20px; position: absolute; right:14px; top: -5px;}
805
+ .chartpercarrow:after{content: ""; position: absolute; right: -25px; top: 0; width: 25px; height: 38px; background-image: url(../images/percentage-arrow.png); background-repeat: no-repeat; background-size: 100%;}
806
+ .chartpercarrow:before{content: ""; position: absolute; left: -1px; top: 6px; width: 1px; height: 26px; background-color: #1e1e1e; }
807
+ .ecomchartinfolabel{font-size: 11px;min-height: 28px; display: flex; align-items: center; font-weight: 400; color: #1e1e1e; max-width:55px; }
808
+
809
+ /*---- daterange -----*/
810
+ .daterangearea{min-height: 22px; min-width: 162px;}
811
+ .daterangepickerflex{display: flex;}
812
+ .daterangepicker-left{box-shadow: 1px 0px 9px rgba(0, 0, 0, 0.08); flex: 0 0 150px; max-width: 150px;}
813
+ .daterangepicker-right{display: flex; flex-wrap: wrap; padding: 15px 24px;}
814
+ .drp-calendar{padding: 0 11px; flex: 0 0 50%; max-width: 50%;}
815
+ .drp-buttons{flex: 0 0 100%; max-width: 100%; padding: 0 24px;}
816
+ .drp-calendar{position: relative;}
817
+ .drp-calendar.left{padding-left: 0px}
818
+ .drp-calendar.right{padding-right: 0px}
819
+ .drp-calendar.left:before{content: ""; position: absolute; top: 55px; right: -5px; height: 76%; width: 1px; background-color: #E9E9E9;}
820
+ .daterangepicker .calendar-table .next, .daterangepicker .calendar-table .prev{vertical-align: top; }
821
+ .daterangepicker .calendar-table .next:hover, .daterangepicker .calendar-table .prev:hover{background-color: inherit;}
822
+ .daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span{width: 24px; height: 24px; border-radius: 6px; border: 1px solid #e9e9e9; position: relative;}
823
+ .daterangepicker .calendar-table .next span:before, .daterangepicker .calendar-table .prev span:before{content: ""; position: absolute; width: 7px; height: 11px; background-size: 100%; background-repeat: no-repeat; left: 50%; top: 50%; margin-left: -3px; margin-top: -5px;}
824
+ .daterangepicker .calendar-table .next span:before{background-image: url(../images/next.png)}
825
+ .daterangepicker .calendar-table .prev span:before{background-image: url(../images/prev.png)}
826
+ .daterangepicker th.month{padding-bottom: 16px; min-height: 24px; line-height: 26px;}
827
+ .daterangepicker .calendar-table .next span:hover, .daterangepicker .calendar-table .prev span:hover{background-color: #e9e9e9;}
828
+ .drpbtmbuttonflex{display: flex; align-items: flex-end;}
829
+ .drpbtmbuttonflex-left, .drpbtmbuttonflex-right{max-width: 50%; flex: 1;}
830
+ .drpbtmbuttonflex-right{display: flex; justify-content: flex-end;}
831
+ .btmslectdatetitle{font-size: 12px; font-weight: 500; color: #1e1e1e; margin-bottom: 9px;}
832
+ .applyBtn{min-width: 90px; background-color: #002BFC; color: #ffffff; text-align: center; line-height: 20px; border-radius: 6px;}
833
+ .applyBtn:hover{background-color: #1e1e1e;}
834
+ .daterangepicker .drp-buttons .btn.cancelBtn{color:#002BFC; background-color: transparent; padding: 5px 0;}
835
+ .daterangepicker .drp-buttons .btn.cancelBtn:hover{color:#1e1e1e;}
836
+ .daterangepicker.show-calendar .ranges{height: 100%;}
837
+ .daterangepicker .ranges ul{position: relative; height: 100%;}
838
+ .daterangepicker .ranges li.cstmrang-li{position: absolute; bottom: 0; left: 0; width: 100%; border-right:4px solid #002BFC; background-color: #F1F6FA; color: #002BFC; font-weight: 500; font-size: 12px; line-height: 20px;}
839
+ .daterangepicker .ranges li.cstmrang-li.active{background-color:#002BFC; color: #ffffff; }
840
+
841
+ /*------- Dashboard upgrade popup -------*/
842
+ .upgradsbscrptnpp-hdr{background:linear-gradient(90deg, #0032FC 0%, #00D2F6 100%); padding: 12px 30px; text-align: center;}
843
+ .upgradsbscrptnpp-hdr h5{color: #ffffff; margin-bottom: 0px; color: #ffffff; text-transform: uppercase; font-weight: 700;}
844
+ .upgradsbscrptnpp-cntr{padding: 0; max-width: 420px; box-shadow: 0px 0px 60px rgba(0, 0, 0, 0.12);}
845
+ .upgradsbscrptnpp-cntr .ppmodal-body{padding: 18px 30px; text-align: center; color: #515151; }
846
+ .upgradsbscrptnpp-cntr .ppmodal-body p{font-size: 14px; line-height: 22px; margin-bottom: 18px;}
847
+ .ppupgrdbtn{display: inline-block; background-color: #FBC53B; border-radius: 6px; padding: 9px 29px; line-height: 24px; font-size: 16px; color: #1E1E1E; font-weight: 500;}
848
+ .ppupgrdbtn:hover{background-color: #1e1e1e; color: #ffffff;}
849
+ .upgradsbscrptnpp-cntr .absltpsclsbtn{top: 17px; right: 17px;}
850
+
851
+ /*------ map product category ------*/
852
+ .backlnkbtn{margin-right: 17px;}
853
+ .accstng-item:last-child .accstng-dtlarea{border-bottom: none;}
854
+ .cmnalert{padding: 10px 10px 8px 40px; position: relative; background-color: #F1F6FA; line-height: 22px; min-height: 42px; display: flex; align-items: center;}
855
+ .alerticon{position: absolute; left: 10px; top: 10px; }
856
+ .blualert{color: #002BFC;}
857
+ .msppageinfowrap{margin-top: 29px; display: flex; align-items: center; flex-wrap: wrap; padding-left: 24px;}
858
+ .msppageinfotxt{color:#515151;}
859
+ .msppageinfotxt:after{content:""; display: inline-block; vertical-align: middle; height: 24px; width: 1px; background-color: #515151; margin: 0 99px 0 98px;}
860
+ .msppageinfotxt:last-child:after{content: none;}
861
+ .mapprocat-accordion{margin-top: 24px;}
862
+ .mapcataccr-item{padding: 18px 24px; border-radius: 6px; background-color: #ffffff; box-shadow: 0px 0px 9px rgba(0, 0, 0, 0.18);margin-bottom: 18px;}
863
+ .mapcataccr-select{width: 235px; padding-left: 2px;}
864
+ .mapcataccr-select select{border:1px solid #c6c6c6; background-color: #ffffff; width: 100%; padding: 6px 30px 6px 12px; margin-bottom: 9px; line-height: 22px; }
865
+ .mapcataccr-select select:last-child{margin-bottom: 0px;}
866
+ .mapcataccr-top{ display: flex; position: relative; padding-right: 24px; flex-wrap: wrap;
867
+ transition: all 0.3s ease-in-out 0s; -moz-transition: all 0.3s ease-in-out 0s; -webkit-transition: all 0.3s ease-in-out 0s; -o-transition: all 0.3s ease-in-out 0s;}
868
+ .mapcataccr-title{font-size: 16px; padding: 6px 0; color: #1e1e1e; font-weight: 500; line-height: 24px; min-width: 298px; position: relative;}
869
+ .mapcataccr-title:before{content: ""; position: absolute; right: 0px; top: 16px; width: 100%; height: 2px; border-top: 2px dashed #c6c6c6}
870
+ .mapcataccr-title:after{content: ""; position: absolute; right: 0px; top:13px;width:6px; height: 9px; background-size: 100%; background-repeat: no-repeat; background-position: right center; background-image: url(../images/line-arrow.png); background-color: #ffffff;}
871
+ .mapcataccr-title span{background-color: #ffffff; padding-right: 4px; position: relative;}
872
+ .mapcataccr-plusminus{position: absolute; right: 0px; top: 12px; width: 24px; height: 24px; cursor: pointer}
873
+ .mapcataccr-plusminus:after{content:"";position:absolute;right:6px;top:0px;height:14px;width:2px;background-color:#0083FC;transition:all 0.3s ease-in-out 0s;-moz-transition:all 0.3s ease-in-out 0s;-webkit-transition:all 0.3s ease-in-out 0s;-o-transition:all 0.3s ease-in-out 0s;}
874
+ .mapcataccr-plusminus:before{content:"";position:absolute;right:0px;top:6px;height:2px;width:14px;background-color:#0083FC;}
875
+ .mapcataccr-plusminus.active:after{opacity:0;visibility:hidden;}
876
+ .mapcataccr-detail{display: none; padding: 0px 15px 0px 0; color: #364167; position: relative;}
877
+ .mapcataccr-detail .mapcataccr-title{font-weight: 400; font-size: 14px; line-height: 22px; color: #515151}
878
+ .sbmaincat{padding: 18px 15px 0px 25px; position: relative;}
879
+ .sbmaincat .mapcataccr-top{position: relative;}
880
+ .sbmaincat .mapcataccr-top:before{content: ""; position: absolute; left: -20px; top: 15px; width:13px; height: 2px; border-top: 2px dashed #c6c6c6;}
881
+ .sbmaincat .mapcataccr-top:after{content: ""; border-left: 2px dashed #c6c6c6; position: absolute; left: 4px; top: 25px; width: 2px; height:calc(100% + 10px);}
882
+ .sbmaincat:before{content: ""; position: absolute; left: 4px; top: -6px; width:2px; height: 100%; border-left: 2px dashed #c6c6c6;}
883
+ .sbmaincat:after{content: ""; position: absolute; left: 16px; top: 20px; margin-top: 10px; width:6px; height: 9px; background-size: 100%; background-repeat: no-repeat; background-position: right center; background-image: url(../images/line-arrow.png); background-color: #ffffff;}
884
+ .sbmaininnerlevel:before{content: none;}
885
+ .sbmainsblevel:before{height: 100%;}
886
+ .sbmaincat .sbmainsblevel:before{content: none;}
887
+ .sbmaincat .mapcataccr-top:last-child:after{content: none;}
888
+ .sbmaincat:last-child{padding-bottom: 0px;}
889
+ .sbmainlevel:last-child:before{height: 40px}
890
+ .topactive{position: relative;}
891
+ .topactive:before{content: ""; position: absolute; top: 24px; left: 4px; width: 2px; height: 80%; border-left: 2px dashed #c6c6c6}
892
+ .mapprocat-dtl{padding-top: 24px !important}
893
+ /*.mapdtlflex{display: flex !important; flex-wrap: wrap;}*/
894
+
895
+ /*------ Map Product Attributes page -----*/
896
+ .mapproattr-dtl{padding-top: 24px !important}
897
+ .mapproattr-dtlwrap{padding-top: 30px;}
898
+ .mapproattr-left{padding: 0 15px; flex: 1;}
899
+ .mapproattr-right{padding: 0 15px; min-width: 263px; max-width: 263px; flex: 0 0 263px;}
900
+ .mapproattr-left .msppageinfowrap{padding-left: 0px; margin-top: 0px;}
901
+ .mapproattr-left .msppageinfotxt::after{margin: -4px 24px 0;}
902
+ .attrinfoicon{vertical-align: middle;margin-left: 7px; line-height: 1; display: inline-block; max-width: 20px;}
903
+ .infoicontrgr{cursor: pointer}
904
+ .proattrformlist{margin-top: 36px}
905
+ .proattrfrm-item{display: flex; flex-wrap: wrap; margin: 0px -15px 18px;}
906
+ .attrfrmleft{flex: 1; padding: 0 15px; display: flex; align-items: center;}
907
+ .attrfrmleft label{font-size: 16px; line-height: 24px; font-weight: 500; color:#1e1e1e; flex: 1;}
908
+ .attrfrmleft .attrinfoicon{flex: 0 0 20px;}
909
+ .attrfrmright{flex:0 0 265px; max-width: 265px; padding: 0 15px;}
910
+ .attrfrmright select{width: 100%; padding:6px 30px 6px 12px; font-size: 14px; line-height: 22px;}
911
+ .addatrrrightbx{border-radius: 6px; background-color: #ffffff; box-shadow: 0px 0px 9px rgba(0, 0, 0, 0.12);}
912
+ .attrrighttop{padding: 18px 15px 16px 15px; border-bottom: 1px solid #E9E9E9; font-size: 16px; line-height: 24px; color: #1e1e1e; font-weight: 500;}
913
+ .attroptinlistbx{padding: 21px 15px; max-height: 330px; overflow: auto }
914
+ .attroptcheck-item{margin-bottom: 18px;}
915
+ .attroptcheck-item:last-child{margin-bottom:0px;}
916
+ .addattrbtn{width: 100%; border-radius:0 0 6px 6px; background-color: #2FBB0E; color: #ffffff; font-size: 16px; line-height: 24px; font-weight: 500; text-align: center; border:none; padding: 9px 15px;}
917
+ .addattlistbx{border-top:1px solid #e9e9e9; padding-top: 18px;}
918
+ .infoicontrgr {position: relative;}
919
+ .infoicontrgr:after{visibility: hidden; width: 160px;background-color: #ffffff; color: #1e1e1e;
920
+ text-align: center; border-radius: 6px; padding: 6px 9px; content: attr(title);
921
+ position: absolute; z-index: 1; bottom: 100%; margin-bottom: 10px; left: 50%;
922
+ margin-left: -80px; font-size: 12px; font-weight: 400; line-height: 20px; border:1px solid #E9E9E9; box-shadow: 0px 0px 9px rgba(0, 0, 0, 0.12);
923
+ }
924
+ .infoicontrgr:hover:after {visibility: visible;}
925
+ .infoicontrgr:hover:before {content: " ";position: absolute;top: -10px; left: 50%;margin-left: -5px;border-width: 5px; border-style: solid; border-color: #cccccc transparent transparent transparent;}
926
+
927
+ button:disabled,button[disabled], button:disabled:hover,button[disabled]:hover{background-color:#CCCCCC;color:#ffffff;}
928
 
929
  /* RC*/
930
  .onbordingbody{padding-top: 10px;}
931
  .dashboard_page_conversios_onboarding, .index_page_conversios_onboarding, .onbordingbody-wapper{background: #fff;}
932
+ .bodyrightpart{padding: 20px 10px 10px 0;}
933
+ .blueupgrdbtn{display: inline-block;}
934
+ .blueupgrdbtn:hover {color:#fff;}
935
+ .shop-category .form-label-control small{display: inline-block;}
936
+ .tvc-sync-progress-gmc{display: none;}
937
+ .ecomchartinfoitem::first-child {padding-left: 0;}
938
+ .chartpercarrow{ padding: 9px 0px 9px 2px; right: 12px;}
939
+ .navinfotopnav .navinfonavtext{line-height: 24px;}
940
+ .container-fluid{padding: 5px 15px;}
941
+ .prmoupgrdbtn .upgradebtn{padding-top: 7px; padding-bottom: 7px;}
942
+ .chartpercarrowt:after{content: ""; position: absolute; right: -25px; top: 0; width: 25px; height: 38px; background-image: url(../images/next.png); background-repeat: no-repeat; background-size: 100%;}
943
+ .prochrtcntn{max-width: 55%;}
944
+ .temp_note{
945
+ background-color: #ffffff;
946
+ box-shadow: 0px 0px 6px rgb(0 0 0 / 12%);
947
+ border-radius: 6px;
948
+ padding: 5px 6px 0px 4px;
949
+ margin: 10px 0 0 0;
950
+
951
+ }
952
+ .temp_note p{
953
+ margin: 0 0 20px 0;
954
+ line-height: 22px;
955
+ /* padding-bottom: 5px; */
956
+ padding: 5px 9px;
957
+ font-weight: 400;
958
+ text-align: center;
959
+ }
admin/helper/class-dashboard-helper.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The admin-specific functionality of the plugin.
4
+ *
5
+ * @link
6
+ * @since 1.0.0
7
+ *
8
+ * Woo Order Reports
9
+ */
10
+
11
+ if(!defined('ABSPATH')){
12
+ exit; // Exit if accessed directly
13
+ }
14
+ if(!class_exists('Conversios_Dashboard_Helper')){
15
+ class Conversios_Dashboard_Helper{
16
+ protected $ShoppingApi;
17
+ protected $TVC_Admin_Helper;
18
+ protected $CustomApi;
19
+ public function __construct(){
20
+ $this->req_int();
21
+ $this->TVC_Admin_Helper = new TVC_Admin_Helper();
22
+ $this->CustomApi = new CustomApi();
23
+ $this->ShoppingApi = new ShoppingApi();
24
+ add_action('wp_ajax_get_google_analytics_reports', array($this,'get_google_analytics_reports') );
25
+ add_action('wp_ajax_get_google_ads_reports', array($this,'get_google_ads_reports') );
26
+ }
27
+
28
+ public function req_int(){
29
+ if (!class_exists('CustomApi')) {
30
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/CustomApi.php');
31
+ }
32
+ if (!class_exists('ShoppingApi')) {
33
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/ShoppingApi.php');
34
+ }
35
+ }
36
+ protected function admin_safe_ajax_call( $nonce, $registered_nonce_name ) {
37
+ // only return results when the user is an admin with manage options
38
+ if ( is_admin() && wp_verify_nonce($nonce,$registered_nonce_name) ) {
39
+ return true;
40
+ } else {
41
+ return false;
42
+ }
43
+ }
44
+ public function get_google_ads_reports(){
45
+ $nonce = (isset($_POST['conversios_nonce']))?$_POST['conversios_nonce']:"";
46
+ if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
47
+ $post_data = (object)$_POST;
48
+ //$start_date = isset($post_data->start_date)?$post_data->start_date:date('Y-m-d',strtotime('-31 days'));
49
+ $start_date = str_replace(' ', '',(isset($_POST['start_date']))?$_POST['start_date']:"");
50
+ if($start_date != ""){
51
+ $date = DateTime::createFromFormat('d-m-Y', $start_date);
52
+ $start_date = $date->format('Y-m-d');
53
+ }
54
+ $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
55
+
56
+ //$end_date = isset($post_data->end_date)?$post_data->end_date:date('Y-m-d',strtotime('-1day'));
57
+ $end_date = str_replace(' ', '',(isset($_POST['end_date']))?$_POST['end_date']:"");
58
+ if($end_date != ""){
59
+ $date = DateTime::createFromFormat('d-m-Y', $end_date);
60
+ $end_date = $date->format('Y-m-d');
61
+ }
62
+ $end_date == (false !==strtotime( $end_date ))?date('Y-m-d', strtotime($end_date)):date( 'Y-m-d', strtotime( 'now' ));
63
+
64
+ $api_rs = $this->ShoppingApi->accountPerformance(2, 7, $start_date, $end_date);
65
+ if (isset($api_rs->error) && $api_rs->error == '') {
66
+ if(isset($api_rs->data) && $api_rs->data != ""){
67
+ $return = array('error'=>false, 'data'=>$api_rs->data,'message'=>'');
68
+ }
69
+ }else{
70
+ $return = array('error'=>true,'message'=>$api_rs->error);
71
+ }
72
+ //print_r($account_performance_res);
73
+
74
+ }
75
+ }
76
+ public function get_google_analytics_reports(){
77
+ $nonce = (isset($_POST['conversios_nonce']))?$_POST['conversios_nonce']:"";
78
+ if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
79
+ $post_data = (object)$_POST;
80
+ $ga_traking_type = isset($post_data->ga_traking_type)?$post_data->ga_traking_type:"";
81
+ $subscription_id = isset($post_data->subscription_id)?$post_data->subscription_id:"";
82
+ $view_id = isset($post_data->view_id)?$post_data->view_id:"";
83
+
84
+ $start_date = str_replace(' ', '',(isset($_POST['start_date']))?$_POST['start_date']:"");
85
+ if($start_date != ""){
86
+ $date = DateTime::createFromFormat('d-m-Y', $start_date);
87
+ $start_date = $date->format('Y-m-d');
88
+ }
89
+ $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
90
+
91
+ $end_date = str_replace(' ', '',(isset($_POST['end_date']))?$_POST['end_date']:"");
92
+ if($end_date != ""){
93
+ $date = DateTime::createFromFormat('d-m-Y', $end_date);
94
+ $end_date = $date->format('Y-m-d');
95
+ }
96
+ $end_date == (false !==strtotime( $end_date ))?date('Y-m-d', strtotime($end_date)):date( 'Y-m-d', strtotime( 'now' ));
97
+
98
+ $return = array();
99
+ if($subscription_id != "" && $view_id !="" &&( $ga_traking_type == "UA" || $ga_traking_type == "BOTH" )){
100
+ $data = array(
101
+ 'subscription_id'=>$subscription_id,
102
+ 'view_id'=>$view_id,
103
+ 'start_date'=>$start_date,
104
+ 'end_date'=>$end_date
105
+ );
106
+
107
+ $api_rs = $this->CustomApi->get_google_analytics_reports($data);
108
+ if(isset($api_rs->error) && $api_rs->error == '') {
109
+ if(isset($api_rs->data) && $api_rs->data != ""){
110
+ $return = array('error'=>false, 'data'=>$api_rs->data,'errors'=>'');
111
+ }
112
+ }else{
113
+ $return = array('error'=>true,'errors'=>$api_rs->error);
114
+ }
115
+ }else if($subscription_id != "" && ( $ga_traking_type == "GA4" || $ga_traking_type == "BOTH" )){
116
+ $return = array('error'=>true,'errors'=>'GA4 Coming soon...');
117
+ }
118
+ }else{
119
+ $return = array('error'=>true,'errors'=>'Admin security nonce is not verified.');
120
+ }
121
+ echo json_encode($return);
122
+ wp_die();
123
+ }
124
+ }
125
+ }
126
+ new Conversios_Dashboard_Helper();
admin/helper/class-onboarding-helper.php CHANGED
@@ -365,12 +365,12 @@ if(!class_exists('Conversios_Onboarding_Helper')):
365
  if(isset($_POST['ga_view_id']) && $_POST['ga_view_id']){
366
  update_option('ee_ga_view_id', $_POST['ga_view_id']);
367
  }
368
- $return_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=gaa_config_page";
369
  if(isset($googleDetail->google_merchant_center_id) || isset($googleDetail->google_ads_id) ){
370
  if( $googleDetail->google_merchant_center_id != "" && $googleDetail->google_ads_id != ""){
371
- $return_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=sync_product_page&welcome_msg=true";
372
  }else{
373
- $return_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=gaa_config_page&welcome_msg=true";
374
  }
375
  }
376
  return $return_url;
@@ -609,7 +609,7 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
609
  CURLOPT_TIMEOUT => 0,
610
  CURLOPT_HTTPHEADER => $header,
611
  CURLOPT_POSTFIELDS => ""
612
- ));
613
  $response = curl_exec($ch);
614
  return json_decode($response);
615
  }else{
365
  if(isset($_POST['ga_view_id']) && $_POST['ga_view_id']){
366
  update_option('ee_ga_view_id', $_POST['ga_view_id']);
367
  }
368
+ $return_url = "admin.php?page=conversios-google-shopping-feed&tab=gaa_config_page";
369
  if(isset($googleDetail->google_merchant_center_id) || isset($googleDetail->google_ads_id) ){
370
  if( $googleDetail->google_merchant_center_id != "" && $googleDetail->google_ads_id != ""){
371
+ $return_url = "admin.php?page=conversios-google-shopping-feed&tab=sync_product_page&welcome_msg=true";
372
  }else{
373
+ $return_url = "admin.php?page=conversios-google-shopping-feed&tab=gaa_config_page&welcome_msg=true";
374
  }
375
  }
376
  return $return_url;
609
  CURLOPT_TIMEOUT => 0,
610
  CURLOPT_HTTPHEADER => $header,
611
  CURLOPT_POSTFIELDS => ""
612
+ ));
613
  $response = curl_exec($ch);
614
  return json_decode($response);
615
  }else{
admin/images/active-conversios-google-ads-menu.png ADDED
Binary file
admin/images/active-conversios-google-analytics-menu.png ADDED
Binary file
admin/images/{sync-prdct-icon.png → active-conversios-google-shopping-feed-menu.png} RENAMED
File without changes
admin/images/active-conversios-menu.png ADDED
Binary file
admin/images/blue-right-arrow.png ADDED
Binary file
admin/images/caret-down.png ADDED
Binary file
admin/images/claendar-icon.png ADDED
Binary file
admin/images/clock-icon.png ADDED
Binary file
admin/images/{smart-shopping-icon.png → conversios-google-ads-menu.png} RENAMED
File without changes
admin/images/{setting-icon.png → conversios-google-analytics-menu.png} RENAMED
File without changes
admin/images/conversios-google-shopping-feed-menu.png ADDED
Binary file
admin/images/{dashboard-icon.png → conversios-menu.png} RENAMED
File without changes
admin/images/download-icon.png ADDED
Binary file
admin/images/ecom-chart.jpg ADDED
Binary file
admin/images/error-icon.png ADDED
Binary file
admin/images/error-white-icon.png ADDED
Binary file
admin/images/green-up.png ADDED
Binary file
admin/images/lock-orange.png ADDED
Binary file
admin/images/percentage-arrow-bg.png ADDED
Binary file
admin/images/percentage-arrow.png ADDED
Binary file
admin/images/prev.png ADDED
Binary file
admin/images/red-down.png ADDED
Binary file
admin/images/table-data.jpg ADDED
Binary file
admin/js/chart.js ADDED
@@ -0,0 +1,13225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * Chart.js v3.5.1
3
+ * https://www.chartjs.org
4
+ * (c) 2021 Chart.js Contributors
5
+ * Released under the MIT License
6
+ */
7
+ (function (global, factory) {
8
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
9
+ typeof define === 'function' && define.amd ? define(factory) :
10
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chart = factory());
11
+ }(this, (function () { 'use strict';
12
+
13
+ function fontString(pixelSize, fontStyle, fontFamily) {
14
+ return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
15
+ }
16
+ const requestAnimFrame = (function() {
17
+ if (typeof window === 'undefined') {
18
+ return function(callback) {
19
+ return callback();
20
+ };
21
+ }
22
+ return window.requestAnimationFrame;
23
+ }());
24
+ function throttled(fn, thisArg, updateFn) {
25
+ const updateArgs = updateFn || ((args) => Array.prototype.slice.call(args));
26
+ let ticking = false;
27
+ let args = [];
28
+ return function(...rest) {
29
+ args = updateArgs(rest);
30
+ if (!ticking) {
31
+ ticking = true;
32
+ requestAnimFrame.call(window, () => {
33
+ ticking = false;
34
+ fn.apply(thisArg, args);
35
+ });
36
+ }
37
+ };
38
+ }
39
+ function debounce(fn, delay) {
40
+ let timeout;
41
+ return function(...args) {
42
+ if (delay) {
43
+ clearTimeout(timeout);
44
+ timeout = setTimeout(fn, delay, args);
45
+ } else {
46
+ fn.apply(this, args);
47
+ }
48
+ return delay;
49
+ };
50
+ }
51
+ const _toLeftRightCenter = (align) => align === 'start' ? 'left' : align === 'end' ? 'right' : 'center';
52
+ const _alignStartEnd = (align, start, end) => align === 'start' ? start : align === 'end' ? end : (start + end) / 2;
53
+ const _textX = (align, left, right, rtl) => {
54
+ const check = rtl ? 'left' : 'right';
55
+ return align === check ? right : align === 'center' ? (left + right) / 2 : left;
56
+ };
57
+
58
+ class Animator {
59
+ constructor() {
60
+ this._request = null;
61
+ this._charts = new Map();
62
+ this._running = false;
63
+ this._lastDate = undefined;
64
+ }
65
+ _notify(chart, anims, date, type) {
66
+ const callbacks = anims.listeners[type];
67
+ const numSteps = anims.duration;
68
+ callbacks.forEach(fn => fn({
69
+ chart,
70
+ initial: anims.initial,
71
+ numSteps,
72
+ currentStep: Math.min(date - anims.start, numSteps)
73
+ }));
74
+ }
75
+ _refresh() {
76
+ const me = this;
77
+ if (me._request) {
78
+ return;
79
+ }
80
+ me._running = true;
81
+ me._request = requestAnimFrame.call(window, () => {
82
+ me._update();
83
+ me._request = null;
84
+ if (me._running) {
85
+ me._refresh();
86
+ }
87
+ });
88
+ }
89
+ _update(date = Date.now()) {
90
+ const me = this;
91
+ let remaining = 0;
92
+ me._charts.forEach((anims, chart) => {
93
+ if (!anims.running || !anims.items.length) {
94
+ return;
95
+ }
96
+ const items = anims.items;
97
+ let i = items.length - 1;
98
+ let draw = false;
99
+ let item;
100
+ for (; i >= 0; --i) {
101
+ item = items[i];
102
+ if (item._active) {
103
+ if (item._total > anims.duration) {
104
+ anims.duration = item._total;
105
+ }
106
+ item.tick(date);
107
+ draw = true;
108
+ } else {
109
+ items[i] = items[items.length - 1];
110
+ items.pop();
111
+ }
112
+ }
113
+ if (draw) {
114
+ chart.draw();
115
+ me._notify(chart, anims, date, 'progress');
116
+ }
117
+ if (!items.length) {
118
+ anims.running = false;
119
+ me._notify(chart, anims, date, 'complete');
120
+ anims.initial = false;
121
+ }
122
+ remaining += items.length;
123
+ });
124
+ me._lastDate = date;
125
+ if (remaining === 0) {
126
+ me._running = false;
127
+ }
128
+ }
129
+ _getAnims(chart) {
130
+ const charts = this._charts;
131
+ let anims = charts.get(chart);
132
+ if (!anims) {
133
+ anims = {
134
+ running: false,
135
+ initial: true,
136
+ items: [],
137
+ listeners: {
138
+ complete: [],
139
+ progress: []
140
+ }
141
+ };
142
+ charts.set(chart, anims);
143
+ }
144
+ return anims;
145
+ }
146
+ listen(chart, event, cb) {
147
+ this._getAnims(chart).listeners[event].push(cb);
148
+ }
149
+ add(chart, items) {
150
+ if (!items || !items.length) {
151
+ return;
152
+ }
153
+ this._getAnims(chart).items.push(...items);
154
+ }
155
+ has(chart) {
156
+ return this._getAnims(chart).items.length > 0;
157
+ }
158
+ start(chart) {
159
+ const anims = this._charts.get(chart);
160
+ if (!anims) {
161
+ return;
162
+ }
163
+ anims.running = true;
164
+ anims.start = Date.now();
165
+ anims.duration = anims.items.reduce((acc, cur) => Math.max(acc, cur._duration), 0);
166
+ this._refresh();
167
+ }
168
+ running(chart) {
169
+ if (!this._running) {
170
+ return false;
171
+ }
172
+ const anims = this._charts.get(chart);
173
+ if (!anims || !anims.running || !anims.items.length) {
174
+ return false;
175
+ }
176
+ return true;
177
+ }
178
+ stop(chart) {
179
+ const anims = this._charts.get(chart);
180
+ if (!anims || !anims.items.length) {
181
+ return;
182
+ }
183
+ const items = anims.items;
184
+ let i = items.length - 1;
185
+ for (; i >= 0; --i) {
186
+ items[i].cancel();
187
+ }
188
+ anims.items = [];
189
+ this._notify(chart, anims, Date.now(), 'complete');
190
+ }
191
+ remove(chart) {
192
+ return this._charts.delete(chart);
193
+ }
194
+ }
195
+ var animator = new Animator();
196
+
197
+ /*!
198
+ * @kurkle/color v0.1.9
199
+ * https://github.com/kurkle/color#readme
200
+ * (c) 2020 Jukka Kurkela
201
+ * Released under the MIT License
202
+ */
203
+ const map$1 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15};
204
+ const hex = '0123456789ABCDEF';
205
+ const h1 = (b) => hex[b & 0xF];
206
+ const h2 = (b) => hex[(b & 0xF0) >> 4] + hex[b & 0xF];
207
+ const eq = (b) => (((b & 0xF0) >> 4) === (b & 0xF));
208
+ function isShort(v) {
209
+ return eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a);
210
+ }
211
+ function hexParse(str) {
212
+ var len = str.length;
213
+ var ret;
214
+ if (str[0] === '#') {
215
+ if (len === 4 || len === 5) {
216
+ ret = {
217
+ r: 255 & map$1[str[1]] * 17,
218
+ g: 255 & map$1[str[2]] * 17,
219
+ b: 255 & map$1[str[3]] * 17,
220
+ a: len === 5 ? map$1[str[4]] * 17 : 255
221
+ };
222
+ } else if (len === 7 || len === 9) {
223
+ ret = {
224
+ r: map$1[str[1]] << 4 | map$1[str[2]],
225
+ g: map$1[str[3]] << 4 | map$1[str[4]],
226
+ b: map$1[str[5]] << 4 | map$1[str[6]],
227
+ a: len === 9 ? (map$1[str[7]] << 4 | map$1[str[8]]) : 255
228
+ };
229
+ }
230
+ }
231
+ return ret;
232
+ }
233
+ function hexString(v) {
234
+ var f = isShort(v) ? h1 : h2;
235
+ return v
236
+ ? '#' + f(v.r) + f(v.g) + f(v.b) + (v.a < 255 ? f(v.a) : '')
237
+ : v;
238
+ }
239
+ function round(v) {
240
+ return v + 0.5 | 0;
241
+ }
242
+ const lim = (v, l, h) => Math.max(Math.min(v, h), l);
243
+ function p2b(v) {
244
+ return lim(round(v * 2.55), 0, 255);
245
+ }
246
+ function n2b(v) {
247
+ return lim(round(v * 255), 0, 255);
248
+ }
249
+ function b2n(v) {
250
+ return lim(round(v / 2.55) / 100, 0, 1);
251
+ }
252
+ function n2p(v) {
253
+ return lim(round(v * 100), 0, 100);
254
+ }
255
+ const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;
256
+ function rgbParse(str) {
257
+ const m = RGB_RE.exec(str);
258
+ let a = 255;
259
+ let r, g, b;
260
+ if (!m) {
261
+ return;
262
+ }
263
+ if (m[7] !== r) {
264
+ const v = +m[7];
265
+ a = 255 & (m[8] ? p2b(v) : v * 255);
266
+ }
267
+ r = +m[1];
268
+ g = +m[3];
269
+ b = +m[5];
270
+ r = 255 & (m[2] ? p2b(r) : r);
271
+ g = 255 & (m[4] ? p2b(g) : g);
272
+ b = 255 & (m[6] ? p2b(b) : b);
273
+ return {
274
+ r: r,
275
+ g: g,
276
+ b: b,
277
+ a: a
278
+ };
279
+ }
280
+ function rgbString(v) {
281
+ return v && (
282
+ v.a < 255
283
+ ? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})`
284
+ : `rgb(${v.r}, ${v.g}, ${v.b})`
285
+ );
286
+ }
287
+ const HUE_RE = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;
288
+ function hsl2rgbn(h, s, l) {
289
+ const a = s * Math.min(l, 1 - l);
290
+ const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
291
+ return [f(0), f(8), f(4)];
292
+ }
293
+ function hsv2rgbn(h, s, v) {
294
+ const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
295
+ return [f(5), f(3), f(1)];
296
+ }
297
+ function hwb2rgbn(h, w, b) {
298
+ const rgb = hsl2rgbn(h, 1, 0.5);
299
+ let i;
300
+ if (w + b > 1) {
301
+ i = 1 / (w + b);
302
+ w *= i;
303
+ b *= i;
304
+ }
305
+ for (i = 0; i < 3; i++) {
306
+ rgb[i] *= 1 - w - b;
307
+ rgb[i] += w;
308
+ }
309
+ return rgb;
310
+ }
311
+ function rgb2hsl(v) {
312
+ const range = 255;
313
+ const r = v.r / range;
314
+ const g = v.g / range;
315
+ const b = v.b / range;
316
+ const max = Math.max(r, g, b);
317
+ const min = Math.min(r, g, b);
318
+ const l = (max + min) / 2;
319
+ let h, s, d;
320
+ if (max !== min) {
321
+ d = max - min;
322
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
323
+ h = max === r
324
+ ? ((g - b) / d) + (g < b ? 6 : 0)
325
+ : max === g
326
+ ? (b - r) / d + 2
327
+ : (r - g) / d + 4;
328
+ h = h * 60 + 0.5;
329
+ }
330
+ return [h | 0, s || 0, l];
331
+ }
332
+ function calln(f, a, b, c) {
333
+ return (
334
+ Array.isArray(a)
335
+ ? f(a[0], a[1], a[2])
336
+ : f(a, b, c)
337
+ ).map(n2b);
338
+ }
339
+ function hsl2rgb(h, s, l) {
340
+ return calln(hsl2rgbn, h, s, l);
341
+ }
342
+ function hwb2rgb(h, w, b) {
343
+ return calln(hwb2rgbn, h, w, b);
344
+ }
345
+ function hsv2rgb(h, s, v) {
346
+ return calln(hsv2rgbn, h, s, v);
347
+ }
348
+ function hue(h) {
349
+ return (h % 360 + 360) % 360;
350
+ }
351
+ function hueParse(str) {
352
+ const m = HUE_RE.exec(str);
353
+ let a = 255;
354
+ let v;
355
+ if (!m) {
356
+ return;
357
+ }
358
+ if (m[5] !== v) {
359
+ a = m[6] ? p2b(+m[5]) : n2b(+m[5]);
360
+ }
361
+ const h = hue(+m[2]);
362
+ const p1 = +m[3] / 100;
363
+ const p2 = +m[4] / 100;
364
+ if (m[1] === 'hwb') {
365
+ v = hwb2rgb(h, p1, p2);
366
+ } else if (m[1] === 'hsv') {
367
+ v = hsv2rgb(h, p1, p2);
368
+ } else {
369
+ v = hsl2rgb(h, p1, p2);
370
+ }
371
+ return {
372
+ r: v[0],
373
+ g: v[1],
374
+ b: v[2],
375
+ a: a
376
+ };
377
+ }
378
+ function rotate(v, deg) {
379
+ var h = rgb2hsl(v);
380
+ h[0] = hue(h[0] + deg);
381
+ h = hsl2rgb(h);
382
+ v.r = h[0];
383
+ v.g = h[1];
384
+ v.b = h[2];
385
+ }
386
+ function hslString(v) {
387
+ if (!v) {
388
+ return;
389
+ }
390
+ const a = rgb2hsl(v);
391
+ const h = a[0];
392
+ const s = n2p(a[1]);
393
+ const l = n2p(a[2]);
394
+ return v.a < 255
395
+ ? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})`
396
+ : `hsl(${h}, ${s}%, ${l}%)`;
397
+ }
398
+ const map$1$1 = {
399
+ x: 'dark',
400
+ Z: 'light',
401
+ Y: 're',
402
+ X: 'blu',
403
+ W: 'gr',
404
+ V: 'medium',
405
+ U: 'slate',
406
+ A: 'ee',
407
+ T: 'ol',
408
+ S: 'or',
409
+ B: 'ra',
410
+ C: 'lateg',
411
+ D: 'ights',
412
+ R: 'in',
413
+ Q: 'turquois',
414
+ E: 'hi',
415
+ P: 'ro',
416
+ O: 'al',
417
+ N: 'le',
418
+ M: 'de',
419
+ L: 'yello',
420
+ F: 'en',
421
+ K: 'ch',
422
+ G: 'arks',
423
+ H: 'ea',
424
+ I: 'ightg',
425
+ J: 'wh'
426
+ };
427
+ const names = {
428
+ OiceXe: 'f0f8ff',
429
+ antiquewEte: 'faebd7',
430
+ aqua: 'ffff',
431
+ aquamarRe: '7fffd4',
432
+ azuY: 'f0ffff',
433
+ beige: 'f5f5dc',
434
+ bisque: 'ffe4c4',
435
+ black: '0',
436
+ blanKedOmond: 'ffebcd',
437
+ Xe: 'ff',
438
+ XeviTet: '8a2be2',
439
+ bPwn: 'a52a2a',
440
+ burlywood: 'deb887',
441
+ caMtXe: '5f9ea0',
442
+ KartYuse: '7fff00',
443
+ KocTate: 'd2691e',
444
+ cSO: 'ff7f50',
445
+ cSnflowerXe: '6495ed',
446
+ cSnsilk: 'fff8dc',
447
+ crimson: 'dc143c',
448
+ cyan: 'ffff',
449
+ xXe: '8b',
450
+ xcyan: '8b8b',
451
+ xgTMnPd: 'b8860b',
452
+ xWay: 'a9a9a9',
453
+ xgYF: '6400',
454
+ xgYy: 'a9a9a9',
455
+ xkhaki: 'bdb76b',
456
+ xmagFta: '8b008b',
457
+ xTivegYF: '556b2f',
458
+ xSange: 'ff8c00',
459
+ xScEd: '9932cc',
460
+ xYd: '8b0000',
461
+ xsOmon: 'e9967a',
462
+ xsHgYF: '8fbc8f',
463
+ xUXe: '483d8b',
464
+ xUWay: '2f4f4f',
465
+ xUgYy: '2f4f4f',
466
+ xQe: 'ced1',
467
+ xviTet: '9400d3',
468
+ dAppRk: 'ff1493',
469
+ dApskyXe: 'bfff',
470
+ dimWay: '696969',
471
+ dimgYy: '696969',
472
+ dodgerXe: '1e90ff',
473
+ fiYbrick: 'b22222',
474
+ flSOwEte: 'fffaf0',
475
+ foYstWAn: '228b22',
476
+ fuKsia: 'ff00ff',
477
+ gaRsbSo: 'dcdcdc',
478
+ ghostwEte: 'f8f8ff',
479
+ gTd: 'ffd700',
480
+ gTMnPd: 'daa520',
481
+ Way: '808080',
482
+ gYF: '8000',
483
+ gYFLw: 'adff2f',
484
+ gYy: '808080',
485
+ honeyMw: 'f0fff0',
486
+ hotpRk: 'ff69b4',
487
+ RdianYd: 'cd5c5c',
488
+ Rdigo: '4b0082',
489
+ ivSy: 'fffff0',
490
+ khaki: 'f0e68c',
491
+ lavFMr: 'e6e6fa',
492
+ lavFMrXsh: 'fff0f5',
493
+ lawngYF: '7cfc00',
494
+ NmoncEffon: 'fffacd',
495
+ ZXe: 'add8e6',
496
+ ZcSO: 'f08080',
497
+ Zcyan: 'e0ffff',
498
+ ZgTMnPdLw: 'fafad2',
499
+ ZWay: 'd3d3d3',
500
+ ZgYF: '90ee90',
501
+ ZgYy: 'd3d3d3',
502
+ ZpRk: 'ffb6c1',
503
+ ZsOmon: 'ffa07a',
504
+ ZsHgYF: '20b2aa',
505
+ ZskyXe: '87cefa',
506
+ ZUWay: '778899',
507
+ ZUgYy: '778899',
508
+ ZstAlXe: 'b0c4de',
509
+ ZLw: 'ffffe0',
510
+ lime: 'ff00',
511
+ limegYF: '32cd32',
512
+ lRF: 'faf0e6',
513
+ magFta: 'ff00ff',
514
+ maPon: '800000',
515
+ VaquamarRe: '66cdaa',
516
+ VXe: 'cd',
517
+ VScEd: 'ba55d3',
518
+ VpurpN: '9370db',
519
+ VsHgYF: '3cb371',
520
+ VUXe: '7b68ee',
521
+ VsprRggYF: 'fa9a',
522
+ VQe: '48d1cc',
523
+ VviTetYd: 'c71585',
524
+ midnightXe: '191970',
525
+ mRtcYam: 'f5fffa',
526
+ mistyPse: 'ffe4e1',
527
+ moccasR: 'ffe4b5',
528
+ navajowEte: 'ffdead',
529
+ navy: '80',
530
+ Tdlace: 'fdf5e6',
531
+ Tive: '808000',
532
+ TivedBb: '6b8e23',
533
+ Sange: 'ffa500',
534
+ SangeYd: 'ff4500',
535
+ ScEd: 'da70d6',
536
+ pOegTMnPd: 'eee8aa',
537
+ pOegYF: '98fb98',
538
+ pOeQe: 'afeeee',
539
+ pOeviTetYd: 'db7093',
540
+ papayawEp: 'ffefd5',
541
+ pHKpuff: 'ffdab9',
542
+ peru: 'cd853f',
543
+ pRk: 'ffc0cb',
544
+ plum: 'dda0dd',
545
+ powMrXe: 'b0e0e6',
546
+ purpN: '800080',
547
+ YbeccapurpN: '663399',
548
+ Yd: 'ff0000',
549
+ Psybrown: 'bc8f8f',
550
+ PyOXe: '4169e1',
551
+ saddNbPwn: '8b4513',
552
+ sOmon: 'fa8072',
553
+ sandybPwn: 'f4a460',
554
+ sHgYF: '2e8b57',
555
+ sHshell: 'fff5ee',
556
+ siFna: 'a0522d',
557
+ silver: 'c0c0c0',
558
+ skyXe: '87ceeb',
559
+ UXe: '6a5acd',
560
+ UWay: '708090',
561
+ UgYy: '708090',
562
+ snow: 'fffafa',
563
+ sprRggYF: 'ff7f',
564
+ stAlXe: '4682b4',
565
+ tan: 'd2b48c',
566
+ teO: '8080',
567
+ tEstN: 'd8bfd8',
568
+ tomato: 'ff6347',
569
+ Qe: '40e0d0',
570
+ viTet: 'ee82ee',
571
+ JHt: 'f5deb3',
572
+ wEte: 'ffffff',
573
+ wEtesmoke: 'f5f5f5',
574
+ Lw: 'ffff00',
575
+ LwgYF: '9acd32'
576
+ };
577
+ function unpack() {
578
+ const unpacked = {};
579
+ const keys = Object.keys(names);
580
+ const tkeys = Object.keys(map$1$1);
581
+ let i, j, k, ok, nk;
582
+ for (i = 0; i < keys.length; i++) {
583
+ ok = nk = keys[i];
584
+ for (j = 0; j < tkeys.length; j++) {
585
+ k = tkeys[j];
586
+ nk = nk.replace(k, map$1$1[k]);
587
+ }
588
+ k = parseInt(names[ok], 16);
589
+ unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF];
590
+ }
591
+ return unpacked;
592
+ }
593
+ let names$1;
594
+ function nameParse(str) {
595
+ if (!names$1) {
596
+ names$1 = unpack();
597
+ names$1.transparent = [0, 0, 0, 0];
598
+ }
599
+ const a = names$1[str.toLowerCase()];
600
+ return a && {
601
+ r: a[0],
602
+ g: a[1],
603
+ b: a[2],
604
+ a: a.length === 4 ? a[3] : 255
605
+ };
606
+ }
607
+ function modHSL(v, i, ratio) {
608
+ if (v) {
609
+ let tmp = rgb2hsl(v);
610
+ tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1));
611
+ tmp = hsl2rgb(tmp);
612
+ v.r = tmp[0];
613
+ v.g = tmp[1];
614
+ v.b = tmp[2];
615
+ }
616
+ }
617
+ function clone$1(v, proto) {
618
+ return v ? Object.assign(proto || {}, v) : v;
619
+ }
620
+ function fromObject(input) {
621
+ var v = {r: 0, g: 0, b: 0, a: 255};
622
+ if (Array.isArray(input)) {
623
+ if (input.length >= 3) {
624
+ v = {r: input[0], g: input[1], b: input[2], a: 255};
625
+ if (input.length > 3) {
626
+ v.a = n2b(input[3]);
627
+ }
628
+ }
629
+ } else {
630
+ v = clone$1(input, {r: 0, g: 0, b: 0, a: 1});
631
+ v.a = n2b(v.a);
632
+ }
633
+ return v;
634
+ }
635
+ function functionParse(str) {
636
+ if (str.charAt(0) === 'r') {
637
+ return rgbParse(str);
638
+ }
639
+ return hueParse(str);
640
+ }
641
+ class Color {
642
+ constructor(input) {
643
+ if (input instanceof Color) {
644
+ return input;
645
+ }
646
+ const type = typeof input;
647
+ let v;
648
+ if (type === 'object') {
649
+ v = fromObject(input);
650
+ } else if (type === 'string') {
651
+ v = hexParse(input) || nameParse(input) || functionParse(input);
652
+ }
653
+ this._rgb = v;
654
+ this._valid = !!v;
655
+ }
656
+ get valid() {
657
+ return this._valid;
658
+ }
659
+ get rgb() {
660
+ var v = clone$1(this._rgb);
661
+ if (v) {
662
+ v.a = b2n(v.a);
663
+ }
664
+ return v;
665
+ }
666
+ set rgb(obj) {
667
+ this._rgb = fromObject(obj);
668
+ }
669
+ rgbString() {
670
+ return this._valid ? rgbString(this._rgb) : this._rgb;
671
+ }
672
+ hexString() {
673
+ return this._valid ? hexString(this._rgb) : this._rgb;
674
+ }
675
+ hslString() {
676
+ return this._valid ? hslString(this._rgb) : this._rgb;
677
+ }
678
+ mix(color, weight) {
679
+ const me = this;
680
+ if (color) {
681
+ const c1 = me.rgb;
682
+ const c2 = color.rgb;
683
+ let w2;
684
+ const p = weight === w2 ? 0.5 : weight;
685
+ const w = 2 * p - 1;
686
+ const a = c1.a - c2.a;
687
+ const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
688
+ w2 = 1 - w1;
689
+ c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5;
690
+ c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5;
691
+ c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5;
692
+ c1.a = p * c1.a + (1 - p) * c2.a;
693
+ me.rgb = c1;
694
+ }
695
+ return me;
696
+ }
697
+ clone() {
698
+ return new Color(this.rgb);
699
+ }
700
+ alpha(a) {
701
+ this._rgb.a = n2b(a);
702
+ return this;
703
+ }
704
+ clearer(ratio) {
705
+ const rgb = this._rgb;
706
+ rgb.a *= 1 - ratio;
707
+ return this;
708
+ }
709
+ greyscale() {
710
+ const rgb = this._rgb;
711
+ const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11);
712
+ rgb.r = rgb.g = rgb.b = val;
713
+ return this;
714
+ }
715
+ opaquer(ratio) {
716
+ const rgb = this._rgb;
717
+ rgb.a *= 1 + ratio;
718
+ return this;
719
+ }
720
+ negate() {
721
+ const v = this._rgb;
722
+ v.r = 255 - v.r;
723
+ v.g = 255 - v.g;
724
+ v.b = 255 - v.b;
725
+ return this;
726
+ }
727
+ lighten(ratio) {
728
+ modHSL(this._rgb, 2, ratio);
729
+ return this;
730
+ }
731
+ darken(ratio) {
732
+ modHSL(this._rgb, 2, -ratio);
733
+ return this;
734
+ }
735
+ saturate(ratio) {
736
+ modHSL(this._rgb, 1, ratio);
737
+ return this;
738
+ }
739
+ desaturate(ratio) {
740
+ modHSL(this._rgb, 1, -ratio);
741
+ return this;
742
+ }
743
+ rotate(deg) {
744
+ rotate(this._rgb, deg);
745
+ return this;
746
+ }
747
+ }
748
+ function index_esm(input) {
749
+ return new Color(input);
750
+ }
751
+
752
+ const isPatternOrGradient = (value) => value instanceof CanvasGradient || value instanceof CanvasPattern;
753
+ function color(value) {
754
+ return isPatternOrGradient(value) ? value : index_esm(value);
755
+ }
756
+ function getHoverColor(value) {
757
+ return isPatternOrGradient(value)
758
+ ? value
759
+ : index_esm(value).saturate(0.5).darken(0.1).hexString();
760
+ }
761
+
762
+ function noop() {}
763
+ const uid = (function() {
764
+ let id = 0;
765
+ return function() {
766
+ return id++;
767
+ };
768
+ }());
769
+ function isNullOrUndef(value) {
770
+ return value === null || typeof value === 'undefined';
771
+ }
772
+ function isArray(value) {
773
+ if (Array.isArray && Array.isArray(value)) {
774
+ return true;
775
+ }
776
+ const type = Object.prototype.toString.call(value);
777
+ if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
778
+ return true;
779
+ }
780
+ return false;
781
+ }
782
+ function isObject(value) {
783
+ return value !== null && Object.prototype.toString.call(value) === '[object Object]';
784
+ }
785
+ const isNumberFinite = (value) => (typeof value === 'number' || value instanceof Number) && isFinite(+value);
786
+ function finiteOrDefault(value, defaultValue) {
787
+ return isNumberFinite(value) ? value : defaultValue;
788
+ }
789
+ function valueOrDefault(value, defaultValue) {
790
+ return typeof value === 'undefined' ? defaultValue : value;
791
+ }
792
+ const toPercentage = (value, dimension) =>
793
+ typeof value === 'string' && value.endsWith('%') ?
794
+ parseFloat(value) / 100
795
+ : value / dimension;
796
+ const toDimension = (value, dimension) =>
797
+ typeof value === 'string' && value.endsWith('%') ?
798
+ parseFloat(value) / 100 * dimension
799
+ : +value;
800
+ function callback(fn, args, thisArg) {
801
+ if (fn && typeof fn.call === 'function') {
802
+ return fn.apply(thisArg, args);
803
+ }
804
+ }
805
+ function each(loopable, fn, thisArg, reverse) {
806
+ let i, len, keys;
807
+ if (isArray(loopable)) {
808
+ len = loopable.length;
809
+ if (reverse) {
810
+ for (i = len - 1; i >= 0; i--) {
811
+ fn.call(thisArg, loopable[i], i);
812
+ }
813
+ } else {
814
+ for (i = 0; i < len; i++) {
815
+ fn.call(thisArg, loopable[i], i);
816
+ }
817
+ }
818
+ } else if (isObject(loopable)) {
819
+ keys = Object.keys(loopable);
820
+ len = keys.length;
821
+ for (i = 0; i < len; i++) {
822
+ fn.call(thisArg, loopable[keys[i]], keys[i]);
823
+ }
824
+ }
825
+ }
826
+ function _elementsEqual(a0, a1) {
827
+ let i, ilen, v0, v1;
828
+ if (!a0 || !a1 || a0.length !== a1.length) {
829
+ return false;
830
+ }
831
+ for (i = 0, ilen = a0.length; i < ilen; ++i) {
832
+ v0 = a0[i];
833
+ v1 = a1[i];
834
+ if (v0.datasetIndex !== v1.datasetIndex || v0.index !== v1.index) {
835
+ return false;
836
+ }
837
+ }
838
+ return true;
839
+ }
840
+ function clone(source) {
841
+ if (isArray(source)) {
842
+ return source.map(clone);
843
+ }
844
+ if (isObject(source)) {
845
+ const target = Object.create(null);
846
+ const keys = Object.keys(source);
847
+ const klen = keys.length;
848
+ let k = 0;
849
+ for (; k < klen; ++k) {
850
+ target[keys[k]] = clone(source[keys[k]]);
851
+ }
852
+ return target;
853
+ }
854
+ return source;
855
+ }
856
+ function isValidKey(key) {
857
+ return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;
858
+ }
859
+ function _merger(key, target, source, options) {
860
+ if (!isValidKey(key)) {
861
+ return;
862
+ }
863
+ const tval = target[key];
864
+ const sval = source[key];
865
+ if (isObject(tval) && isObject(sval)) {
866
+ merge(tval, sval, options);
867
+ } else {
868
+ target[key] = clone(sval);
869
+ }
870
+ }
871
+ function merge(target, source, options) {
872
+ const sources = isArray(source) ? source : [source];
873
+ const ilen = sources.length;
874
+ if (!isObject(target)) {
875
+ return target;
876
+ }
877
+ options = options || {};
878
+ const merger = options.merger || _merger;
879
+ for (let i = 0; i < ilen; ++i) {
880
+ source = sources[i];
881
+ if (!isObject(source)) {
882
+ continue;
883
+ }
884
+ const keys = Object.keys(source);
885
+ for (let k = 0, klen = keys.length; k < klen; ++k) {
886
+ merger(keys[k], target, source, options);
887
+ }
888
+ }
889
+ return target;
890
+ }
891
+ function mergeIf(target, source) {
892
+ return merge(target, source, {merger: _mergerIf});
893
+ }
894
+ function _mergerIf(key, target, source) {
895
+ if (!isValidKey(key)) {
896
+ return;
897
+ }
898
+ const tval = target[key];
899
+ const sval = source[key];
900
+ if (isObject(tval) && isObject(sval)) {
901
+ mergeIf(tval, sval);
902
+ } else if (!Object.prototype.hasOwnProperty.call(target, key)) {
903
+ target[key] = clone(sval);
904
+ }
905
+ }
906
+ function _deprecated(scope, value, previous, current) {
907
+ if (value !== undefined) {
908
+ console.warn(scope + ': "' + previous +
909
+ '" is deprecated. Please use "' + current + '" instead');
910
+ }
911
+ }
912
+ const emptyString = '';
913
+ const dot = '.';
914
+ function indexOfDotOrLength(key, start) {
915
+ const idx = key.indexOf(dot, start);
916
+ return idx === -1 ? key.length : idx;
917
+ }
918
+ function resolveObjectKey(obj, key) {
919
+ if (key === emptyString) {
920
+ return obj;
921
+ }
922
+ let pos = 0;
923
+ let idx = indexOfDotOrLength(key, pos);
924
+ while (obj && idx > pos) {
925
+ obj = obj[key.substr(pos, idx - pos)];
926
+ pos = idx + 1;
927
+ idx = indexOfDotOrLength(key, pos);
928
+ }
929
+ return obj;
930
+ }
931
+ function _capitalize(str) {
932
+ return str.charAt(0).toUpperCase() + str.slice(1);
933
+ }
934
+ const defined = (value) => typeof value !== 'undefined';
935
+ const isFunction = (value) => typeof value === 'function';
936
+ const setsEqual = (a, b) => {
937
+ if (a.size !== b.size) {
938
+ return false;
939
+ }
940
+ for (const item of a) {
941
+ if (!b.has(item)) {
942
+ return false;
943
+ }
944
+ }
945
+ return true;
946
+ };
947
+
948
+ const overrides = Object.create(null);
949
+ const descriptors = Object.create(null);
950
+ function getScope$1(node, key) {
951
+ if (!key) {
952
+ return node;
953
+ }
954
+ const keys = key.split('.');
955
+ for (let i = 0, n = keys.length; i < n; ++i) {
956
+ const k = keys[i];
957
+ node = node[k] || (node[k] = Object.create(null));
958
+ }
959
+ return node;
960
+ }
961
+ function set(root, scope, values) {
962
+ if (typeof scope === 'string') {
963
+ return merge(getScope$1(root, scope), values);
964
+ }
965
+ return merge(getScope$1(root, ''), scope);
966
+ }
967
+ class Defaults {
968
+ constructor(_descriptors) {
969
+ this.animation = undefined;
970
+ this.backgroundColor = 'rgba(0,0,0,0.1)';
971
+ this.borderColor = 'rgba(0,0,0,0.1)';
972
+ this.color = '#666';
973
+ this.datasets = {};
974
+ this.devicePixelRatio = (context) => context.chart.platform.getDevicePixelRatio();
975
+ this.elements = {};
976
+ this.events = [
977
+ 'mousemove',
978
+ 'mouseout',
979
+ 'click',
980
+ 'touchstart',
981
+ 'touchmove'
982
+ ];
983
+ this.font = {
984
+ family: "'Roboto'",
985
+ size: 12,
986
+ style: 'normal',
987
+ lineHeight: 1.3,
988
+ weight: null
989
+ };
990
+ this.hover = {};
991
+ this.hoverBackgroundColor = (ctx, options) => getHoverColor(options.backgroundColor);
992
+ this.hoverBorderColor = (ctx, options) => getHoverColor(options.borderColor);
993
+ this.hoverColor = (ctx, options) => getHoverColor(options.color);
994
+ this.indexAxis = 'x';
995
+ this.interaction = {
996
+ mode: 'nearest',
997
+ intersect: true
998
+ };
999
+ this.maintainAspectRatio = true;
1000
+ this.onHover = null;
1001
+ this.onClick = null;
1002
+ this.parsing = true;
1003
+ this.plugins = {};
1004
+ this.responsive = true;
1005
+ this.scale = undefined;
1006
+ this.scales = {};
1007
+ this.showLine = true;
1008
+ this.describe(_descriptors);
1009
+ }
1010
+ set(scope, values) {
1011
+ return set(this, scope, values);
1012
+ }
1013
+ get(scope) {
1014
+ return getScope$1(this, scope);
1015
+ }
1016
+ describe(scope, values) {
1017
+ return set(descriptors, scope, values);
1018
+ }
1019
+ override(scope, values) {
1020
+ return set(overrides, scope, values);
1021
+ }
1022
+ route(scope, name, targetScope, targetName) {
1023
+ const scopeObject = getScope$1(this, scope);
1024
+ const targetScopeObject = getScope$1(this, targetScope);
1025
+ const privateName = '_' + name;
1026
+ Object.defineProperties(scopeObject, {
1027
+ [privateName]: {
1028
+ value: scopeObject[name],
1029
+ writable: true
1030
+ },
1031
+ [name]: {
1032
+ enumerable: true,
1033
+ get() {
1034
+ const local = this[privateName];
1035
+ const target = targetScopeObject[targetName];
1036
+ if (isObject(local)) {
1037
+ return Object.assign({}, target, local);
1038
+ }
1039
+ return valueOrDefault(local, target);
1040
+ },
1041
+ set(value) {
1042
+ this[privateName] = value;
1043
+ }
1044
+ }
1045
+ });
1046
+ }
1047
+ }
1048
+ var defaults = new Defaults({
1049
+ _scriptable: (name) => !name.startsWith('on'),
1050
+ _indexable: (name) => name !== 'events',
1051
+ hover: {
1052
+ _fallback: 'interaction'
1053
+ },
1054
+ interaction: {
1055
+ _scriptable: false,
1056
+ _indexable: false,
1057
+ }
1058
+ });
1059
+
1060
+ const PI = Math.PI;
1061
+ const TAU = 2 * PI;
1062
+ const PITAU = TAU + PI;
1063
+ const INFINITY = Number.POSITIVE_INFINITY;
1064
+ const RAD_PER_DEG = PI / 180;
1065
+ const HALF_PI = PI / 2;
1066
+ const QUARTER_PI = PI / 4;
1067
+ const TWO_THIRDS_PI = PI * 2 / 3;
1068
+ const log10 = Math.log10;
1069
+ const sign = Math.sign;
1070
+ function niceNum(range) {
1071
+ const roundedRange = Math.round(range);
1072
+ range = almostEquals(range, roundedRange, range / 1000) ? roundedRange : range;
1073
+ const niceRange = Math.pow(10, Math.floor(log10(range)));
1074
+ const fraction = range / niceRange;
1075
+ const niceFraction = fraction <= 1 ? 1 : fraction <= 2 ? 2 : fraction <= 5 ? 5 : 10;
1076
+ return niceFraction * niceRange;
1077
+ }
1078
+ function _factorize(value) {
1079
+ const result = [];
1080
+ const sqrt = Math.sqrt(value);
1081
+ let i;
1082
+ for (i = 1; i < sqrt; i++) {
1083
+ if (value % i === 0) {
1084
+ result.push(i);
1085
+ result.push(value / i);
1086
+ }
1087
+ }
1088
+ if (sqrt === (sqrt | 0)) {
1089
+ result.push(sqrt);
1090
+ }
1091
+ result.sort((a, b) => a - b).pop();
1092
+ return result;
1093
+ }
1094
+ function isNumber(n) {
1095
+ return !isNaN(parseFloat(n)) && isFinite(n);
1096
+ }
1097
+ function almostEquals(x, y, epsilon) {
1098
+ return Math.abs(x - y) < epsilon;
1099
+ }
1100
+ function almostWhole(x, epsilon) {
1101
+ const rounded = Math.round(x);
1102
+ return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);
1103
+ }
1104
+ function _setMinAndMaxByKey(array, target, property) {
1105
+ let i, ilen, value;
1106
+ for (i = 0, ilen = array.length; i < ilen; i++) {
1107
+ value = array[i][property];
1108
+ if (!isNaN(value)) {
1109
+ target.min = Math.min(target.min, value);
1110
+ target.max = Math.max(target.max, value);
1111
+ }
1112
+ }
1113
+ }
1114
+ function toRadians(degrees) {
1115
+ return degrees * (PI / 180);
1116
+ }
1117
+ function toDegrees(radians) {
1118
+ return radians * (180 / PI);
1119
+ }
1120
+ function _decimalPlaces(x) {
1121
+ if (!isNumberFinite(x)) {
1122
+ return;
1123
+ }
1124
+ let e = 1;
1125
+ let p = 0;
1126
+ while (Math.round(x * e) / e !== x) {
1127
+ e *= 10;
1128
+ p++;
1129
+ }
1130
+ return p;
1131
+ }
1132
+ function getAngleFromPoint(centrePoint, anglePoint) {
1133
+ const distanceFromXCenter = anglePoint.x - centrePoint.x;
1134
+ const distanceFromYCenter = anglePoint.y - centrePoint.y;
1135
+ const radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
1136
+ let angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
1137
+ if (angle < (-0.5 * PI)) {
1138
+ angle += TAU;
1139
+ }
1140
+ return {
1141
+ angle,
1142
+ distance: radialDistanceFromCenter
1143
+ };
1144
+ }
1145
+ function distanceBetweenPoints(pt1, pt2) {
1146
+ return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
1147
+ }
1148
+ function _angleDiff(a, b) {
1149
+ return (a - b + PITAU) % TAU - PI;
1150
+ }
1151
+ function _normalizeAngle(a) {
1152
+ return (a % TAU + TAU) % TAU;
1153
+ }
1154
+ function _angleBetween(angle, start, end, sameAngleIsFullCircle) {
1155
+ const a = _normalizeAngle(angle);
1156
+ const s = _normalizeAngle(start);
1157
+ const e = _normalizeAngle(end);
1158
+ const angleToStart = _normalizeAngle(s - a);
1159
+ const angleToEnd = _normalizeAngle(e - a);
1160
+ const startToAngle = _normalizeAngle(a - s);
1161
+ const endToAngle = _normalizeAngle(a - e);
1162
+ return a === s || a === e || (sameAngleIsFullCircle && s === e)
1163
+ || (angleToStart > angleToEnd && startToAngle < endToAngle);
1164
+ }
1165
+ function _limitValue(value, min, max) {
1166
+ return Math.max(min, Math.min(max, value));
1167
+ }
1168
+ function _int16Range(value) {
1169
+ return _limitValue(value, -32768, 32767);
1170
+ }
1171
+
1172
+ function toFontString(font) {
1173
+ if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
1174
+ return null;
1175
+ }
1176
+ return (font.style ? font.style + ' ' : '')
1177
+ + (font.weight ? font.weight + ' ' : '')
1178
+ + font.size + 'px '
1179
+ + font.family;
1180
+ }
1181
+ function _measureText(ctx, data, gc, longest, string) {
1182
+ let textWidth = data[string];
1183
+ if (!textWidth) {
1184
+ textWidth = data[string] = ctx.measureText(string).width;
1185
+ gc.push(string);
1186
+ }
1187
+ if (textWidth > longest) {
1188
+ longest = textWidth;
1189
+ }
1190
+ return longest;
1191
+ }
1192
+ function _longestText(ctx, font, arrayOfThings, cache) {
1193
+ cache = cache || {};
1194
+ let data = cache.data = cache.data || {};
1195
+ let gc = cache.garbageCollect = cache.garbageCollect || [];
1196
+ if (cache.font !== font) {
1197
+ data = cache.data = {};
1198
+ gc = cache.garbageCollect = [];
1199
+ cache.font = font;
1200
+ }
1201
+ ctx.save();
1202
+ ctx.font = font;
1203
+ let longest = 0;
1204
+ const ilen = arrayOfThings.length;
1205
+ let i, j, jlen, thing, nestedThing;
1206
+ for (i = 0; i < ilen; i++) {
1207
+ thing = arrayOfThings[i];
1208
+ if (thing !== undefined && thing !== null && isArray(thing) !== true) {
1209
+ longest = _measureText(ctx, data, gc, longest, thing);
1210
+ } else if (isArray(thing)) {
1211
+ for (j = 0, jlen = thing.length; j < jlen; j++) {
1212
+ nestedThing = thing[j];
1213
+ if (nestedThing !== undefined && nestedThing !== null && !isArray(nestedThing)) {
1214
+ longest = _measureText(ctx, data, gc, longest, nestedThing);
1215
+ }
1216
+ }
1217
+ }
1218
+ }
1219
+ ctx.restore();
1220
+ const gcLen = gc.length / 2;
1221
+ if (gcLen > arrayOfThings.length) {
1222
+ for (i = 0; i < gcLen; i++) {
1223
+ delete data[gc[i]];
1224
+ }
1225
+ gc.splice(0, gcLen);
1226
+ }
1227
+ return longest;
1228
+ }
1229
+ function _alignPixel(chart, pixel, width) {
1230
+ const devicePixelRatio = chart.currentDevicePixelRatio;
1231
+ const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
1232
+ return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
1233
+ }
1234
+ function clearCanvas(canvas, ctx) {
1235
+ ctx = ctx || canvas.getContext('2d');
1236
+ ctx.save();
1237
+ ctx.resetTransform();
1238
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1239
+ ctx.restore();
1240
+ }
1241
+ function drawPoint(ctx, options, x, y) {
1242
+ let type, xOffset, yOffset, size, cornerRadius;
1243
+ const style = options.pointStyle;
1244
+ const rotation = options.rotation;
1245
+ const radius = options.radius;
1246
+ let rad = (rotation || 0) * RAD_PER_DEG;
1247
+ if (style && typeof style === 'object') {
1248
+ type = style.toString();
1249
+ if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
1250
+ ctx.save();
1251
+ ctx.translate(x, y);
1252
+ ctx.rotate(rad);
1253
+ ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
1254
+ ctx.restore();
1255
+ return;
1256
+ }
1257
+ }
1258
+ if (isNaN(radius) || radius <= 0) {
1259
+ return;
1260
+ }
1261
+ ctx.beginPath();
1262
+ switch (style) {
1263
+ default:
1264
+ ctx.arc(x, y, radius, 0, TAU);
1265
+ ctx.closePath();
1266
+ break;
1267
+ case 'triangle':
1268
+ ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
1269
+ rad += TWO_THIRDS_PI;
1270
+ ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
1271
+ rad += TWO_THIRDS_PI;
1272
+ ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
1273
+ ctx.closePath();
1274
+ break;
1275
+ case 'rectRounded':
1276
+ cornerRadius = radius * 0.516;
1277
+ size = radius - cornerRadius;
1278
+ xOffset = Math.cos(rad + QUARTER_PI) * size;
1279
+ yOffset = Math.sin(rad + QUARTER_PI) * size;
1280
+ ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
1281
+ ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
1282
+ ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
1283
+ ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
1284
+ ctx.closePath();
1285
+ break;
1286
+ case 'rect':
1287
+ if (!rotation) {
1288
+ size = Math.SQRT1_2 * radius;
1289
+ ctx.rect(x - size, y - size, 2 * size, 2 * size);
1290
+ break;
1291
+ }
1292
+ rad += QUARTER_PI;
1293
+ case 'rectRot':
1294
+ xOffset = Math.cos(rad) * radius;
1295
+ yOffset = Math.sin(rad) * radius;
1296
+ ctx.moveTo(x - xOffset, y - yOffset);
1297
+ ctx.lineTo(x + yOffset, y - xOffset);
1298
+ ctx.lineTo(x + xOffset, y + yOffset);
1299
+ ctx.lineTo(x - yOffset, y + xOffset);
1300
+ ctx.closePath();
1301
+ break;
1302
+ case 'crossRot':
1303
+ rad += QUARTER_PI;
1304
+ case 'cross':
1305
+ xOffset = Math.cos(rad) * radius;
1306
+ yOffset = Math.sin(rad) * radius;
1307
+ ctx.moveTo(x - xOffset, y - yOffset);
1308
+ ctx.lineTo(x + xOffset, y + yOffset);
1309
+ ctx.moveTo(x + yOffset, y - xOffset);
1310
+ ctx.lineTo(x - yOffset, y + xOffset);
1311
+ break;
1312
+ case 'star':
1313
+ xOffset = Math.cos(rad) * radius;
1314
+ yOffset = Math.sin(rad) * radius;
1315
+ ctx.moveTo(x - xOffset, y - yOffset);
1316
+ ctx.lineTo(x + xOffset, y + yOffset);
1317
+ ctx.moveTo(x + yOffset, y - xOffset);
1318
+ ctx.lineTo(x - yOffset, y + xOffset);
1319
+ rad += QUARTER_PI;
1320
+ xOffset = Math.cos(rad) * radius;
1321
+ yOffset = Math.sin(rad) * radius;
1322
+ ctx.moveTo(x - xOffset, y - yOffset);
1323
+ ctx.lineTo(x + xOffset, y + yOffset);
1324
+ ctx.moveTo(x + yOffset, y - xOffset);
1325
+ ctx.lineTo(x - yOffset, y + xOffset);
1326
+ break;
1327
+ case 'line':
1328
+ xOffset = Math.cos(rad) * radius;
1329
+ yOffset = Math.sin(rad) * radius;
1330
+ ctx.moveTo(x - xOffset, y - yOffset);
1331
+ ctx.lineTo(x + xOffset, y + yOffset);
1332
+ break;
1333
+ case 'dash':
1334
+ ctx.moveTo(x, y);
1335
+ ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
1336
+ break;
1337
+ }
1338
+ ctx.fill();
1339
+ if (options.borderWidth > 0) {
1340
+ ctx.stroke();
1341
+ }
1342
+ }
1343
+ function _isPointInArea(point, area, margin) {
1344
+ margin = margin || 0.5;
1345
+ return !area || (point && point.x > area.left - margin && point.x < area.right + margin &&
1346
+ point.y > area.top - margin && point.y < area.bottom + margin);
1347
+ }
1348
+ function clipArea(ctx, area) {
1349
+ ctx.save();
1350
+ ctx.beginPath();
1351
+ ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
1352
+ ctx.clip();
1353
+ }
1354
+ function unclipArea(ctx) {
1355
+ ctx.restore();
1356
+ }
1357
+ function _steppedLineTo(ctx, previous, target, flip, mode) {
1358
+ if (!previous) {
1359
+ return ctx.lineTo(target.x, target.y);
1360
+ }
1361
+ if (mode === 'middle') {
1362
+ const midpoint = (previous.x + target.x) / 2.0;
1363
+ ctx.lineTo(midpoint, previous.y);
1364
+ ctx.lineTo(midpoint, target.y);
1365
+ } else if (mode === 'after' !== !!flip) {
1366
+ ctx.lineTo(previous.x, target.y);
1367
+ } else {
1368
+ ctx.lineTo(target.x, previous.y);
1369
+ }
1370
+ ctx.lineTo(target.x, target.y);
1371
+ }
1372
+ function _bezierCurveTo(ctx, previous, target, flip) {
1373
+ if (!previous) {
1374
+ return ctx.lineTo(target.x, target.y);
1375
+ }
1376
+ ctx.bezierCurveTo(
1377
+ flip ? previous.cp1x : previous.cp2x,
1378
+ flip ? previous.cp1y : previous.cp2y,
1379
+ flip ? target.cp2x : target.cp1x,
1380
+ flip ? target.cp2y : target.cp1y,
1381
+ target.x,
1382
+ target.y);
1383
+ }
1384
+ function renderText(ctx, text, x, y, font, opts = {}) {
1385
+ const lines = isArray(text) ? text : [text];
1386
+ const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
1387
+ let i, line;
1388
+ ctx.save();
1389
+ ctx.font = font.string;
1390
+ setRenderOpts(ctx, opts);
1391
+ for (i = 0; i < lines.length; ++i) {
1392
+ line = lines[i];
1393
+ if (stroke) {
1394
+ if (opts.strokeColor) {
1395
+ ctx.strokeStyle = opts.strokeColor;
1396
+ }
1397
+ if (!isNullOrUndef(opts.strokeWidth)) {
1398
+ ctx.lineWidth = opts.strokeWidth;
1399
+ }
1400
+ ctx.strokeText(line, x, y, opts.maxWidth);
1401
+ }
1402
+ ctx.fillText(line, x, y, opts.maxWidth);
1403
+ decorateText(ctx, x, y, line, opts);
1404
+ y += font.lineHeight;
1405
+ }
1406
+ ctx.restore();
1407
+ }
1408
+ function setRenderOpts(ctx, opts) {
1409
+ if (opts.translation) {
1410
+ ctx.translate(opts.translation[0], opts.translation[1]);
1411
+ }
1412
+ if (!isNullOrUndef(opts.rotation)) {
1413
+ ctx.rotate(opts.rotation);
1414
+ }
1415
+ if (opts.color) {
1416
+ ctx.fillStyle = opts.color;
1417
+ }
1418
+ if (opts.textAlign) {
1419
+ ctx.textAlign = opts.textAlign;
1420
+ }
1421
+ if (opts.textBaseline) {
1422
+ ctx.textBaseline = opts.textBaseline;
1423
+ }
1424
+ }
1425
+ function decorateText(ctx, x, y, line, opts) {
1426
+ if (opts.strikethrough || opts.underline) {
1427
+ const metrics = ctx.measureText(line);
1428
+ const left = x - metrics.actualBoundingBoxLeft;
1429
+ const right = x + metrics.actualBoundingBoxRight;
1430
+ const top = y - metrics.actualBoundingBoxAscent;
1431
+ const bottom = y + metrics.actualBoundingBoxDescent;
1432
+ const yDecoration = opts.strikethrough ? (top + bottom) / 2 : bottom;
1433
+ ctx.strokeStyle = ctx.fillStyle;
1434
+ ctx.beginPath();
1435
+ ctx.lineWidth = opts.decorationWidth || 2;
1436
+ ctx.moveTo(left, yDecoration);
1437
+ ctx.lineTo(right, yDecoration);
1438
+ ctx.stroke();
1439
+ }
1440
+ }
1441
+ function addRoundedRectPath(ctx, rect) {
1442
+ const {x, y, w, h, radius} = rect;
1443
+ ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_PI, PI, true);
1444
+ ctx.lineTo(x, y + h - radius.bottomLeft);
1445
+ ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true);
1446
+ ctx.lineTo(x + w - radius.bottomRight, y + h);
1447
+ ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true);
1448
+ ctx.lineTo(x + w, y + radius.topRight);
1449
+ ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true);
1450
+ ctx.lineTo(x + radius.topLeft, y);
1451
+ }
1452
+
1453
+ function _lookup(table, value, cmp) {
1454
+ cmp = cmp || ((index) => table[index] < value);
1455
+ let hi = table.length - 1;
1456
+ let lo = 0;
1457
+ let mid;
1458
+ while (hi - lo > 1) {
1459
+ mid = (lo + hi) >> 1;
1460
+ if (cmp(mid)) {
1461
+ lo = mid;
1462
+ } else {
1463
+ hi = mid;
1464
+ }
1465
+ }
1466
+ return {lo, hi};
1467
+ }
1468
+ const _lookupByKey = (table, key, value) =>
1469
+ _lookup(table, value, index => table[index][key] < value);
1470
+ const _rlookupByKey = (table, key, value) =>
1471
+ _lookup(table, value, index => table[index][key] >= value);
1472
+ function _filterBetween(values, min, max) {
1473
+ let start = 0;
1474
+ let end = values.length;
1475
+ while (start < end && values[start] < min) {
1476
+ start++;
1477
+ }
1478
+ while (end > start && values[end - 1] > max) {
1479
+ end--;
1480
+ }
1481
+ return start > 0 || end < values.length
1482
+ ? values.slice(start, end)
1483
+ : values;
1484
+ }
1485
+ const arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
1486
+ function listenArrayEvents(array, listener) {
1487
+ if (array._chartjs) {
1488
+ array._chartjs.listeners.push(listener);
1489
+ return;
1490
+ }
1491
+ Object.defineProperty(array, '_chartjs', {
1492
+ configurable: true,
1493
+ enumerable: false,
1494
+ value: {
1495
+ listeners: [listener]
1496
+ }
1497
+ });
1498
+ arrayEvents.forEach((key) => {
1499
+ const method = '_onData' + _capitalize(key);
1500
+ const base = array[key];
1501
+ Object.defineProperty(array, key, {
1502
+ configurable: true,
1503
+ enumerable: false,
1504
+ value(...args) {
1505
+ const res = base.apply(this, args);
1506
+ array._chartjs.listeners.forEach((object) => {
1507
+ if (typeof object[method] === 'function') {
1508
+ object[method](...args);
1509
+ }
1510
+ });
1511
+ return res;
1512
+ }
1513
+ });
1514
+ });
1515
+ }
1516
+ function unlistenArrayEvents(array, listener) {
1517
+ const stub = array._chartjs;
1518
+ if (!stub) {
1519
+ return;
1520
+ }
1521
+ const listeners = stub.listeners;
1522
+ const index = listeners.indexOf(listener);
1523
+ if (index !== -1) {
1524
+ listeners.splice(index, 1);
1525
+ }
1526
+ if (listeners.length > 0) {
1527
+ return;
1528
+ }
1529
+ arrayEvents.forEach((key) => {
1530
+ delete array[key];
1531
+ });
1532
+ delete array._chartjs;
1533
+ }
1534
+ function _arrayUnique(items) {
1535
+ const set = new Set();
1536
+ let i, ilen;
1537
+ for (i = 0, ilen = items.length; i < ilen; ++i) {
1538
+ set.add(items[i]);
1539
+ }
1540
+ if (set.size === ilen) {
1541
+ return items;
1542
+ }
1543
+ return Array.from(set);
1544
+ }
1545
+
1546
+ function _isDomSupported() {
1547
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
1548
+ }
1549
+ function _getParentNode(domNode) {
1550
+ let parent = domNode.parentNode;
1551
+ if (parent && parent.toString() === '[object ShadowRoot]') {
1552
+ parent = parent.host;
1553
+ }
1554
+ return parent;
1555
+ }
1556
+ function parseMaxStyle(styleValue, node, parentProperty) {
1557
+ let valueInPixels;
1558
+ if (typeof styleValue === 'string') {
1559
+ valueInPixels = parseInt(styleValue, 10);
1560
+ if (styleValue.indexOf('%') !== -1) {
1561
+ valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
1562
+ }
1563
+ } else {
1564
+ valueInPixels = styleValue;
1565
+ }
1566
+ return valueInPixels;
1567
+ }
1568
+ const getComputedStyle = (element) => window.getComputedStyle(element, null);
1569
+ function getStyle(el, property) {
1570
+ return getComputedStyle(el).getPropertyValue(property);
1571
+ }
1572
+ const positions = ['top', 'right', 'bottom', 'left'];
1573
+ function getPositionedStyle(styles, style, suffix) {
1574
+ const result = {};
1575
+ suffix = suffix ? '-' + suffix : '';
1576
+ for (let i = 0; i < 4; i++) {
1577
+ const pos = positions[i];
1578
+ result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;
1579
+ }
1580
+ result.width = result.left + result.right;
1581
+ result.height = result.top + result.bottom;
1582
+ return result;
1583
+ }
1584
+ const useOffsetPos = (x, y, target) => (x > 0 || y > 0) && (!target || !target.shadowRoot);
1585
+ function getCanvasPosition(evt, canvas) {
1586
+ const e = evt.native || evt;
1587
+ const touches = e.touches;
1588
+ const source = touches && touches.length ? touches[0] : e;
1589
+ const {offsetX, offsetY} = source;
1590
+ let box = false;
1591
+ let x, y;
1592
+ if (useOffsetPos(offsetX, offsetY, e.target)) {
1593
+ x = offsetX;
1594
+ y = offsetY;
1595
+ } else {
1596
+ const rect = canvas.getBoundingClientRect();
1597
+ x = source.clientX - rect.left;
1598
+ y = source.clientY - rect.top;
1599
+ box = true;
1600
+ }
1601
+ return {x, y, box};
1602
+ }
1603
+ function getRelativePosition$1(evt, chart) {
1604
+ const {canvas, currentDevicePixelRatio} = chart;
1605
+ const style = getComputedStyle(canvas);
1606
+ const borderBox = style.boxSizing === 'border-box';
1607
+ const paddings = getPositionedStyle(style, 'padding');
1608
+ const borders = getPositionedStyle(style, 'border', 'width');
1609
+ const {x, y, box} = getCanvasPosition(evt, canvas);
1610
+ const xOffset = paddings.left + (box && borders.left);
1611
+ const yOffset = paddings.top + (box && borders.top);
1612
+ let {width, height} = chart;
1613
+ if (borderBox) {
1614
+ width -= paddings.width + borders.width;
1615
+ height -= paddings.height + borders.height;
1616
+ }
1617
+ return {
1618
+ x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
1619
+ y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
1620
+ };
1621
+ }
1622
+ function getContainerSize(canvas, width, height) {
1623
+ let maxWidth, maxHeight;
1624
+ if (width === undefined || height === undefined) {
1625
+ const container = _getParentNode(canvas);
1626
+ if (!container) {
1627
+ width = canvas.clientWidth;
1628
+ height = canvas.clientHeight;
1629
+ } else {
1630
+ const rect = container.getBoundingClientRect();
1631
+ const containerStyle = getComputedStyle(container);
1632
+ const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');
1633
+ const containerPadding = getPositionedStyle(containerStyle, 'padding');
1634
+ width = rect.width - containerPadding.width - containerBorder.width;
1635
+ height = rect.height - containerPadding.height - containerBorder.height;
1636
+ maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');
1637
+ maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');
1638
+ }
1639
+ }
1640
+ return {
1641
+ width,
1642
+ height,
1643
+ maxWidth: maxWidth || INFINITY,
1644
+ maxHeight: maxHeight || INFINITY
1645
+ };
1646
+ }
1647
+ const round1 = v => Math.round(v * 10) / 10;
1648
+ function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
1649
+ const style = getComputedStyle(canvas);
1650
+ const margins = getPositionedStyle(style, 'margin');
1651
+ const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || INFINITY;
1652
+ const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || INFINITY;
1653
+ const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
1654
+ let {width, height} = containerSize;
1655
+ if (style.boxSizing === 'content-box') {
1656
+ const borders = getPositionedStyle(style, 'border', 'width');
1657
+ const paddings = getPositionedStyle(style, 'padding');
1658
+ width -= paddings.width + borders.width;
1659
+ height -= paddings.height + borders.height;
1660
+ }
1661
+ width = Math.max(0, width - margins.width);
1662
+ height = Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height - margins.height);
1663
+ width = round1(Math.min(width, maxWidth, containerSize.maxWidth));
1664
+ height = round1(Math.min(height, maxHeight, containerSize.maxHeight));
1665
+ if (width && !height) {
1666
+ height = round1(width / 2);
1667
+ }
1668
+ return {
1669
+ width,
1670
+ height
1671
+ };
1672
+ }
1673
+ function retinaScale(chart, forceRatio, forceStyle) {
1674
+ const pixelRatio = forceRatio || 1;
1675
+ const deviceHeight = Math.floor(chart.height * pixelRatio);
1676
+ const deviceWidth = Math.floor(chart.width * pixelRatio);
1677
+ chart.height = deviceHeight / pixelRatio;
1678
+ chart.width = deviceWidth / pixelRatio;
1679
+ const canvas = chart.canvas;
1680
+ if (canvas.style && (forceStyle || (!canvas.style.height && !canvas.style.width))) {
1681
+ canvas.style.height = `${chart.height}px`;
1682
+ canvas.style.width = `${chart.width}px`;
1683
+ }
1684
+ if (chart.currentDevicePixelRatio !== pixelRatio
1685
+ || canvas.height !== deviceHeight
1686
+ || canvas.width !== deviceWidth) {
1687
+ chart.currentDevicePixelRatio = pixelRatio;
1688
+ canvas.height = deviceHeight;
1689
+ canvas.width = deviceWidth;
1690
+ chart.ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
1691
+ return true;
1692
+ }
1693
+ return false;
1694
+ }
1695
+ const supportsEventListenerOptions = (function() {
1696
+ let passiveSupported = false;
1697
+ try {
1698
+ const options = {
1699
+ get passive() {
1700
+ passiveSupported = true;
1701
+ return false;
1702
+ }
1703
+ };
1704
+ window.addEventListener('test', null, options);
1705
+ window.removeEventListener('test', null, options);
1706
+ } catch (e) {
1707
+ }
1708
+ return passiveSupported;
1709
+ }());
1710
+ function readUsedSize(element, property) {
1711
+ const value = getStyle(element, property);
1712
+ const matches = value && value.match(/^(\d+)(\.\d+)?px$/);
1713
+ return matches ? +matches[1] : undefined;
1714
+ }
1715
+
1716
+ function getRelativePosition(e, chart) {
1717
+ if ('native' in e) {
1718
+ return {
1719
+ x: e.x,
1720
+ y: e.y
1721
+ };
1722
+ }
1723
+ return getRelativePosition$1(e, chart);
1724
+ }
1725
+ function evaluateAllVisibleItems(chart, handler) {
1726
+ const metasets = chart.getSortedVisibleDatasetMetas();
1727
+ let index, data, element;
1728
+ for (let i = 0, ilen = metasets.length; i < ilen; ++i) {
1729
+ ({index, data} = metasets[i]);
1730
+ for (let j = 0, jlen = data.length; j < jlen; ++j) {
1731
+ element = data[j];
1732
+ if (!element.skip) {
1733
+ handler(element, index, j);
1734
+ }
1735
+ }
1736
+ }
1737
+ }
1738
+ function binarySearch(metaset, axis, value, intersect) {
1739
+ const {controller, data, _sorted} = metaset;
1740
+ const iScale = controller._cachedMeta.iScale;
1741
+ if (iScale && axis === iScale.axis && _sorted && data.length) {
1742
+ const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;
1743
+ if (!intersect) {
1744
+ return lookupMethod(data, axis, value);
1745
+ } else if (controller._sharedOptions) {
1746
+ const el = data[0];
1747
+ const range = typeof el.getRange === 'function' && el.getRange(axis);
1748
+ if (range) {
1749
+ const start = lookupMethod(data, axis, value - range);
1750
+ const end = lookupMethod(data, axis, value + range);
1751
+ return {lo: start.lo, hi: end.hi};
1752
+ }
1753
+ }
1754
+ }
1755
+ return {lo: 0, hi: data.length - 1};
1756
+ }
1757
+ function optimizedEvaluateItems(chart, axis, position, handler, intersect) {
1758
+ const metasets = chart.getSortedVisibleDatasetMetas();
1759
+ const value = position[axis];
1760
+ for (let i = 0, ilen = metasets.length; i < ilen; ++i) {
1761
+ const {index, data} = metasets[i];
1762
+ const {lo, hi} = binarySearch(metasets[i], axis, value, intersect);
1763
+ for (let j = lo; j <= hi; ++j) {
1764
+ const element = data[j];
1765
+ if (!element.skip) {
1766
+ handler(element, index, j);
1767
+ }
1768
+ }
1769
+ }
1770
+ }
1771
+ function getDistanceMetricForAxis(axis) {
1772
+ const useX = axis.indexOf('x') !== -1;
1773
+ const useY = axis.indexOf('y') !== -1;
1774
+ return function(pt1, pt2) {
1775
+ const deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
1776
+ const deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
1777
+ return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
1778
+ };
1779
+ }
1780
+ function getIntersectItems(chart, position, axis, useFinalPosition) {
1781
+ const items = [];
1782
+ if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) {
1783
+ return items;
1784
+ }
1785
+ const evaluationFunc = function(element, datasetIndex, index) {
1786
+ if (element.inRange(position.x, position.y, useFinalPosition)) {
1787
+ items.push({element, datasetIndex, index});
1788
+ }
1789
+ };
1790
+ optimizedEvaluateItems(chart, axis, position, evaluationFunc, true);
1791
+ return items;
1792
+ }
1793
+ function getNearestItems(chart, position, axis, intersect, useFinalPosition) {
1794
+ const distanceMetric = getDistanceMetricForAxis(axis);
1795
+ let minDistance = Number.POSITIVE_INFINITY;
1796
+ let items = [];
1797
+ if (!_isPointInArea(position, chart.chartArea, chart._minPadding)) {
1798
+ return items;
1799
+ }
1800
+ const evaluationFunc = function(element, datasetIndex, index) {
1801
+ if (intersect && !element.inRange(position.x, position.y, useFinalPosition)) {
1802
+ return;
1803
+ }
1804
+ const center = element.getCenterPoint(useFinalPosition);
1805
+ if (!_isPointInArea(center, chart.chartArea, chart._minPadding) && !element.inRange(position.x, position.y, useFinalPosition)) {
1806
+ return;
1807
+ }
1808
+ const distance = distanceMetric(position, center);
1809
+ if (distance < minDistance) {
1810
+ items = [{element, datasetIndex, index}];
1811
+ minDistance = distance;
1812
+ } else if (distance === minDistance) {
1813
+ items.push({element, datasetIndex, index});
1814
+ }
1815
+ };
1816
+ optimizedEvaluateItems(chart, axis, position, evaluationFunc);
1817
+ return items;
1818
+ }
1819
+ function getAxisItems(chart, e, options, useFinalPosition) {
1820
+ const position = getRelativePosition(e, chart);
1821
+ const items = [];
1822
+ const axis = options.axis;
1823
+ const rangeMethod = axis === 'x' ? 'inXRange' : 'inYRange';
1824
+ let intersectsItem = false;
1825
+ evaluateAllVisibleItems(chart, (element, datasetIndex, index) => {
1826
+ if (element[rangeMethod](position[axis], useFinalPosition)) {
1827
+ items.push({element, datasetIndex, index});
1828
+ }
1829
+ if (element.inRange(position.x, position.y, useFinalPosition)) {
1830
+ intersectsItem = true;
1831
+ }
1832
+ });
1833
+ if (options.intersect && !intersectsItem) {
1834
+ return [];
1835
+ }
1836
+ return items;
1837
+ }
1838
+ var Interaction = {
1839
+ modes: {
1840
+ index(chart, e, options, useFinalPosition) {
1841
+ const position = getRelativePosition(e, chart);
1842
+ const axis = options.axis || 'x';
1843
+ const items = options.intersect
1844
+ ? getIntersectItems(chart, position, axis, useFinalPosition)
1845
+ : getNearestItems(chart, position, axis, false, useFinalPosition);
1846
+ const elements = [];
1847
+ if (!items.length) {
1848
+ return [];
1849
+ }
1850
+ chart.getSortedVisibleDatasetMetas().forEach((meta) => {
1851
+ const index = items[0].index;
1852
+ const element = meta.data[index];
1853
+ if (element && !element.skip) {
1854
+ elements.push({element, datasetIndex: meta.index, index});
1855
+ }
1856
+ });
1857
+ return elements;
1858
+ },
1859
+ dataset(chart, e, options, useFinalPosition) {
1860
+ const position = getRelativePosition(e, chart);
1861
+ const axis = options.axis || 'xy';
1862
+ let items = options.intersect
1863
+ ? getIntersectItems(chart, position, axis, useFinalPosition) :
1864
+ getNearestItems(chart, position, axis, false, useFinalPosition);
1865
+ if (items.length > 0) {
1866
+ const datasetIndex = items[0].datasetIndex;
1867
+ const data = chart.getDatasetMeta(datasetIndex).data;
1868
+ items = [];
1869
+ for (let i = 0; i < data.length; ++i) {
1870
+ items.push({element: data[i], datasetIndex, index: i});
1871
+ }
1872
+ }
1873
+ return items;
1874
+ },
1875
+ point(chart, e, options, useFinalPosition) {
1876
+ const position = getRelativePosition(e, chart);
1877
+ const axis = options.axis || 'xy';
1878
+ return getIntersectItems(chart, position, axis, useFinalPosition);
1879
+ },
1880
+ nearest(chart, e, options, useFinalPosition) {
1881
+ const position = getRelativePosition(e, chart);
1882
+ const axis = options.axis || 'xy';
1883
+ return getNearestItems(chart, position, axis, options.intersect, useFinalPosition);
1884
+ },
1885
+ x(chart, e, options, useFinalPosition) {
1886
+ options.axis = 'x';
1887
+ return getAxisItems(chart, e, options, useFinalPosition);
1888
+ },
1889
+ y(chart, e, options, useFinalPosition) {
1890
+ options.axis = 'y';
1891
+ return getAxisItems(chart, e, options, useFinalPosition);
1892
+ }
1893
+ }
1894
+ };
1895
+
1896
+ const LINE_HEIGHT = new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
1897
+ const FONT_STYLE = new RegExp(/^(normal|italic|initial|inherit|unset|(oblique( -?[0-9]?[0-9]deg)?))$/);
1898
+ function toLineHeight(value, size) {
1899
+ const matches = ('' + value).match(LINE_HEIGHT);
1900
+ if (!matches || matches[1] === 'normal') {
1901
+ return size * 1.2;
1902
+ }
1903
+ value = +matches[2];
1904
+ switch (matches[3]) {
1905
+ case 'px':
1906
+ return value;
1907
+ case '%':
1908
+ value /= 100;
1909
+ break;
1910
+ }
1911
+ return size * value;
1912
+ }
1913
+ const numberOrZero$1 = v => +v || 0;
1914
+ function _readValueToProps(value, props) {
1915
+ const ret = {};
1916
+ const objProps = isObject(props);
1917
+ const keys = objProps ? Object.keys(props) : props;
1918
+ const read = isObject(value)
1919
+ ? objProps
1920
+ ? prop => valueOrDefault(value[prop], value[props[prop]])
1921
+ : prop => value[prop]
1922
+ : () => value;
1923
+ for (const prop of keys) {
1924
+ ret[prop] = numberOrZero$1(read(prop));
1925
+ }
1926
+ return ret;
1927
+ }
1928
+ function toTRBL(value) {
1929
+ return _readValueToProps(value, {top: 'y', right: 'x', bottom: 'y', left: 'x'});
1930
+ }
1931
+ function toTRBLCorners(value) {
1932
+ return _readValueToProps(value, ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']);
1933
+ }
1934
+ function toPadding(value) {
1935
+ const obj = toTRBL(value);
1936
+ obj.width = obj.left + obj.right;
1937
+ obj.height = obj.top + obj.bottom;
1938
+ return obj;
1939
+ }
1940
+ function toFont(options, fallback) {
1941
+ options = options || {};
1942
+ fallback = fallback || defaults.font;
1943
+ let size = valueOrDefault(options.size, fallback.size);
1944
+ if (typeof size === 'string') {
1945
+ size = parseInt(size, 10);
1946
+ }
1947
+ let style = valueOrDefault(options.style, fallback.style);
1948
+ if (style && !('' + style).match(FONT_STYLE)) {
1949
+ console.warn('Invalid font style specified: "' + style + '"');
1950
+ style = '';
1951
+ }
1952
+ const font = {
1953
+ family: valueOrDefault(options.family, fallback.family),
1954
+ lineHeight: toLineHeight(valueOrDefault(options.lineHeight, fallback.lineHeight), size),
1955
+ size,
1956
+ style,
1957
+ weight: valueOrDefault(options.weight, fallback.weight),
1958
+ string: ''
1959
+ };
1960
+ font.string = toFontString(font);
1961
+ return font;
1962
+ }
1963
+ function resolve(inputs, context, index, info) {
1964
+ let cacheable = true;
1965
+ let i, ilen, value;
1966
+ for (i = 0, ilen = inputs.length; i < ilen; ++i) {
1967
+ value = inputs[i];
1968
+ if (value === undefined) {
1969
+ continue;
1970
+ }
1971
+ if (context !== undefined && typeof value === 'function') {
1972
+ value = value(context);
1973
+ cacheable = false;
1974
+ }
1975
+ if (index !== undefined && isArray(value)) {
1976
+ value = value[index % value.length];
1977
+ cacheable = false;
1978
+ }
1979
+ if (value !== undefined) {
1980
+ if (info && !cacheable) {
1981
+ info.cacheable = false;
1982
+ }
1983
+ return value;
1984
+ }
1985
+ }
1986
+ }
1987
+ function _addGrace(minmax, grace) {
1988
+ const {min, max} = minmax;
1989
+ return {
1990
+ min: min - Math.abs(toDimension(grace, min)),
1991
+ max: max + toDimension(grace, max)
1992
+ };
1993
+ }
1994
+
1995
+ const STATIC_POSITIONS = ['left', 'top', 'right', 'bottom'];
1996
+ function filterByPosition(array, position) {
1997
+ return array.filter(v => v.pos === position);
1998
+ }
1999
+ function filterDynamicPositionByAxis(array, axis) {
2000
+ return array.filter(v => STATIC_POSITIONS.indexOf(v.pos) === -1 && v.box.axis === axis);
2001
+ }
2002
+ function sortByWeight(array, reverse) {
2003
+ return array.sort((a, b) => {
2004
+ const v0 = reverse ? b : a;
2005
+ const v1 = reverse ? a : b;
2006
+ return v0.weight === v1.weight ?
2007
+ v0.index - v1.index :
2008
+ v0.weight - v1.weight;
2009
+ });
2010
+ }
2011
+ function wrapBoxes(boxes) {
2012
+ const layoutBoxes = [];
2013
+ let i, ilen, box, pos, stack, stackWeight;
2014
+ for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {
2015
+ box = boxes[i];
2016
+ ({position: pos, options: {stack, stackWeight = 1}} = box);
2017
+ layoutBoxes.push({
2018
+ index: i,
2019
+ box,
2020
+ pos,
2021
+ horizontal: box.isHorizontal(),
2022
+ weight: box.weight,
2023
+ stack: stack && (pos + stack),
2024
+ stackWeight
2025
+ });
2026
+ }
2027
+ return layoutBoxes;
2028
+ }
2029
+ function buildStacks(layouts) {
2030
+ const stacks = {};
2031
+ for (const wrap of layouts) {
2032
+ const {stack, pos, stackWeight} = wrap;
2033
+ if (!stack || !STATIC_POSITIONS.includes(pos)) {
2034
+ continue;
2035
+ }
2036
+ const _stack = stacks[stack] || (stacks[stack] = {count: 0, placed: 0, weight: 0, size: 0});
2037
+ _stack.count++;
2038
+ _stack.weight += stackWeight;
2039
+ }
2040
+ return stacks;
2041
+ }
2042
+ function setLayoutDims(layouts, params) {
2043
+ const stacks = buildStacks(layouts);
2044
+ const {vBoxMaxWidth, hBoxMaxHeight} = params;
2045
+ let i, ilen, layout;
2046
+ for (i = 0, ilen = layouts.length; i < ilen; ++i) {
2047
+ layout = layouts[i];
2048
+ const {fullSize} = layout.box;
2049
+ const stack = stacks[layout.stack];
2050
+ const factor = stack && layout.stackWeight / stack.weight;
2051
+ if (layout.horizontal) {
2052
+ layout.width = factor ? factor * vBoxMaxWidth : fullSize && params.availableWidth;
2053
+ layout.height = hBoxMaxHeight;
2054
+ } else {
2055
+ layout.width = vBoxMaxWidth;
2056
+ layout.height = factor ? factor * hBoxMaxHeight : fullSize && params.availableHeight;
2057
+ }
2058
+ }
2059
+ return stacks;
2060
+ }
2061
+ function buildLayoutBoxes(boxes) {
2062
+ const layoutBoxes = wrapBoxes(boxes);
2063
+ const fullSize = sortByWeight(layoutBoxes.filter(wrap => wrap.box.fullSize), true);
2064
+ const left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
2065
+ const right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
2066
+ const top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
2067
+ const bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
2068
+ const centerHorizontal = filterDynamicPositionByAxis(layoutBoxes, 'x');
2069
+ const centerVertical = filterDynamicPositionByAxis(layoutBoxes, 'y');
2070
+ return {
2071
+ fullSize,
2072
+ leftAndTop: left.concat(top),
2073
+ rightAndBottom: right.concat(centerVertical).concat(bottom).concat(centerHorizontal),
2074
+ chartArea: filterByPosition(layoutBoxes, 'chartArea'),
2075
+ vertical: left.concat(right).concat(centerVertical),
2076
+ horizontal: top.concat(bottom).concat(centerHorizontal)
2077
+ };
2078
+ }
2079
+ function getCombinedMax(maxPadding, chartArea, a, b) {
2080
+ return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
2081
+ }
2082
+ function updateMaxPadding(maxPadding, boxPadding) {
2083
+ maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
2084
+ maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
2085
+ maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
2086
+ maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
2087
+ }
2088
+ function updateDims(chartArea, params, layout, stacks) {
2089
+ const {pos, box} = layout;
2090
+ const maxPadding = chartArea.maxPadding;
2091
+ if (!isObject(pos)) {
2092
+ if (layout.size) {
2093
+ chartArea[pos] -= layout.size;
2094
+ }
2095
+ const stack = stacks[layout.stack] || {size: 0, count: 1};
2096
+ stack.size = Math.max(stack.size, layout.horizontal ? box.height : box.width);
2097
+ layout.size = stack.size / stack.count;
2098
+ chartArea[pos] += layout.size;
2099
+ }
2100
+ if (box.getPadding) {
2101
+ updateMaxPadding(maxPadding, box.getPadding());
2102
+ }
2103
+ const newWidth = Math.max(0, params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right'));
2104
+ const newHeight = Math.max(0, params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom'));
2105
+ const widthChanged = newWidth !== chartArea.w;
2106
+ const heightChanged = newHeight !== chartArea.h;
2107
+ chartArea.w = newWidth;
2108
+ chartArea.h = newHeight;
2109
+ return layout.horizontal
2110
+ ? {same: widthChanged, other: heightChanged}
2111
+ : {same: heightChanged, other: widthChanged};
2112
+ }
2113
+ function handleMaxPadding(chartArea) {
2114
+ const maxPadding = chartArea.maxPadding;
2115
+ function updatePos(pos) {
2116
+ const change = Math.max(maxPadding[pos] - chartArea[pos], 0);
2117
+ chartArea[pos] += change;
2118
+ return change;
2119
+ }
2120
+ chartArea.y += updatePos('top');
2121
+ chartArea.x += updatePos('left');
2122
+ updatePos('right');
2123
+ updatePos('bottom');
2124
+ }
2125
+ function getMargins(horizontal, chartArea) {
2126
+ const maxPadding = chartArea.maxPadding;
2127
+ function marginForPositions(positions) {
2128
+ const margin = {left: 0, top: 0, right: 0, bottom: 0};
2129
+ positions.forEach((pos) => {
2130
+ margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
2131
+ });
2132
+ return margin;
2133
+ }
2134
+ return horizontal
2135
+ ? marginForPositions(['left', 'right'])
2136
+ : marginForPositions(['top', 'bottom']);
2137
+ }
2138
+ function fitBoxes(boxes, chartArea, params, stacks) {
2139
+ const refitBoxes = [];
2140
+ let i, ilen, layout, box, refit, changed;
2141
+ for (i = 0, ilen = boxes.length, refit = 0; i < ilen; ++i) {
2142
+ layout = boxes[i];
2143
+ box = layout.box;
2144
+ box.update(
2145
+ layout.width || chartArea.w,
2146
+ layout.height || chartArea.h,
2147
+ getMargins(layout.horizontal, chartArea)
2148
+ );
2149
+ const {same, other} = updateDims(chartArea, params, layout, stacks);
2150
+ refit |= same && refitBoxes.length;
2151
+ changed = changed || other;
2152
+ if (!box.fullSize) {
2153
+ refitBoxes.push(layout);
2154
+ }
2155
+ }
2156
+ return refit && fitBoxes(refitBoxes, chartArea, params, stacks) || changed;
2157
+ }
2158
+ function setBoxDims(box, left, top, width, height) {
2159
+ box.top = top;
2160
+ box.left = left;
2161
+ box.right = left + width;
2162
+ box.bottom = top + height;
2163
+ box.width = width;
2164
+ box.height = height;
2165
+ }
2166
+ function placeBoxes(boxes, chartArea, params, stacks) {
2167
+ const userPadding = params.padding;
2168
+ let {x, y} = chartArea;
2169
+ for (const layout of boxes) {
2170
+ const box = layout.box;
2171
+ const stack = stacks[layout.stack] || {count: 1, placed: 0, weight: 1};
2172
+ const weight = (layout.stackWeight / stack.weight) || 1;
2173
+ if (layout.horizontal) {
2174
+ const width = chartArea.w * weight;
2175
+ const height = stack.size || box.height;
2176
+ if (defined(stack.start)) {
2177
+ y = stack.start;
2178
+ }
2179
+ if (box.fullSize) {
2180
+ setBoxDims(box, userPadding.left, y, params.outerWidth - userPadding.right - userPadding.left, height);
2181
+ } else {
2182
+ setBoxDims(box, chartArea.left + stack.placed, y, width, height);
2183
+ }
2184
+ stack.start = y;
2185
+ stack.placed += width;
2186
+ y = box.bottom;
2187
+ } else {
2188
+ const height = chartArea.h * weight;
2189
+ const width = stack.size || box.width;
2190
+ if (defined(stack.start)) {
2191
+ x = stack.start;
2192
+ }
2193
+ if (box.fullSize) {
2194
+ setBoxDims(box, x, userPadding.top, width, params.outerHeight - userPadding.bottom - userPadding.top);
2195
+ } else {
2196
+ setBoxDims(box, x, chartArea.top + stack.placed, width, height);
2197
+ }
2198
+ stack.start = x;
2199
+ stack.placed += height;
2200
+ x = box.right;
2201
+ }
2202
+ }
2203
+ chartArea.x = x;
2204
+ chartArea.y = y;
2205
+ }
2206
+ defaults.set('layout', {
2207
+ padding: {
2208
+ top: 0,
2209
+ right: 0,
2210
+ bottom: 0,
2211
+ left: 0
2212
+ }
2213
+ });
2214
+ var layouts = {
2215
+ addBox(chart, item) {
2216
+ if (!chart.boxes) {
2217
+ chart.boxes = [];
2218
+ }
2219
+ item.fullSize = item.fullSize || false;
2220
+ item.position = item.position || 'top';
2221
+ item.weight = item.weight || 0;
2222
+ item._layers = item._layers || function() {
2223
+ return [{
2224
+ z: 0,
2225
+ draw(chartArea) {
2226
+ item.draw(chartArea);
2227
+ }
2228
+ }];
2229
+ };
2230
+ chart.boxes.push(item);
2231
+ },
2232
+ removeBox(chart, layoutItem) {
2233
+ const index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
2234
+ if (index !== -1) {
2235
+ chart.boxes.splice(index, 1);
2236
+ }
2237
+ },
2238
+ configure(chart, item, options) {
2239
+ item.fullSize = options.fullSize;
2240
+ item.position = options.position;
2241
+ item.weight = options.weight;
2242
+ },
2243
+ update(chart, width, height, minPadding) {
2244
+ if (!chart) {
2245
+ return;
2246
+ }
2247
+ const padding = toPadding(chart.options.layout.padding);
2248
+ const availableWidth = Math.max(width - padding.width, 0);
2249
+ const availableHeight = Math.max(height - padding.height, 0);
2250
+ const boxes = buildLayoutBoxes(chart.boxes);
2251
+ const verticalBoxes = boxes.vertical;
2252
+ const horizontalBoxes = boxes.horizontal;
2253
+ each(chart.boxes, box => {
2254
+ if (typeof box.beforeLayout === 'function') {
2255
+ box.beforeLayout();
2256
+ }
2257
+ });
2258
+ const visibleVerticalBoxCount = verticalBoxes.reduce((total, wrap) =>
2259
+ wrap.box.options && wrap.box.options.display === false ? total : total + 1, 0) || 1;
2260
+ const params = Object.freeze({
2261
+ outerWidth: width,
2262
+ outerHeight: height,
2263
+ padding,
2264
+ availableWidth,
2265
+ availableHeight,
2266
+ vBoxMaxWidth: availableWidth / 2 / visibleVerticalBoxCount,
2267
+ hBoxMaxHeight: availableHeight / 2
2268
+ });
2269
+ const maxPadding = Object.assign({}, padding);
2270
+ updateMaxPadding(maxPadding, toPadding(minPadding));
2271
+ const chartArea = Object.assign({
2272
+ maxPadding,
2273
+ w: availableWidth,
2274
+ h: availableHeight,
2275
+ x: padding.left,
2276
+ y: padding.top
2277
+ }, padding);
2278
+ const stacks = setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
2279
+ fitBoxes(boxes.fullSize, chartArea, params, stacks);
2280
+ fitBoxes(verticalBoxes, chartArea, params, stacks);
2281
+ if (fitBoxes(horizontalBoxes, chartArea, params, stacks)) {
2282
+ fitBoxes(verticalBoxes, chartArea, params, stacks);
2283
+ }
2284
+ handleMaxPadding(chartArea);
2285
+ placeBoxes(boxes.leftAndTop, chartArea, params, stacks);
2286
+ chartArea.x += chartArea.w;
2287
+ chartArea.y += chartArea.h;
2288
+ placeBoxes(boxes.rightAndBottom, chartArea, params, stacks);
2289
+ chart.chartArea = {
2290
+ left: chartArea.left,
2291
+ top: chartArea.top,
2292
+ right: chartArea.left + chartArea.w,
2293
+ bottom: chartArea.top + chartArea.h,
2294
+ height: chartArea.h,
2295
+ width: chartArea.w,
2296
+ };
2297
+ each(boxes.chartArea, (layout) => {
2298
+ const box = layout.box;
2299
+ Object.assign(box, chart.chartArea);
2300
+ box.update(chartArea.w, chartArea.h);
2301
+ });
2302
+ }
2303
+ };
2304
+
2305
+ function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback, getTarget = () => scopes[0]) {
2306
+ if (!defined(fallback)) {
2307
+ fallback = _resolve('_fallback', scopes);
2308
+ }
2309
+ const cache = {
2310
+ [Symbol.toStringTag]: 'Object',
2311
+ _cacheable: true,
2312
+ _scopes: scopes,
2313
+ _rootScopes: rootScopes,
2314
+ _fallback: fallback,
2315
+ _getTarget: getTarget,
2316
+ override: (scope) => _createResolver([scope, ...scopes], prefixes, rootScopes, fallback),
2317
+ };
2318
+ return new Proxy(cache, {
2319
+ deleteProperty(target, prop) {
2320
+ delete target[prop];
2321
+ delete target._keys;
2322
+ delete scopes[0][prop];
2323
+ return true;
2324
+ },
2325
+ get(target, prop) {
2326
+ return _cached(target, prop,
2327
+ () => _resolveWithPrefixes(prop, prefixes, scopes, target));
2328
+ },
2329
+ getOwnPropertyDescriptor(target, prop) {
2330
+ return Reflect.getOwnPropertyDescriptor(target._scopes[0], prop);
2331
+ },
2332
+ getPrototypeOf() {
2333
+ return Reflect.getPrototypeOf(scopes[0]);
2334
+ },
2335
+ has(target, prop) {
2336
+ return getKeysFromAllScopes(target).includes(prop);
2337
+ },
2338
+ ownKeys(target) {
2339
+ return getKeysFromAllScopes(target);
2340
+ },
2341
+ set(target, prop, value) {
2342
+ const storage = target._storage || (target._storage = getTarget());
2343
+ storage[prop] = value;
2344
+ delete target[prop];
2345
+ delete target._keys;
2346
+ return true;
2347
+ }
2348
+ });
2349
+ }
2350
+ function _attachContext(proxy, context, subProxy, descriptorDefaults) {
2351
+ const cache = {
2352
+ _cacheable: false,
2353
+ _proxy: proxy,
2354
+ _context: context,
2355
+ _subProxy: subProxy,
2356
+ _stack: new Set(),
2357
+ _descriptors: _descriptors(proxy, descriptorDefaults),
2358
+ setContext: (ctx) => _attachContext(proxy, ctx, subProxy, descriptorDefaults),
2359
+ override: (scope) => _attachContext(proxy.override(scope), context, subProxy, descriptorDefaults)
2360
+ };
2361
+ return new Proxy(cache, {
2362
+ deleteProperty(target, prop) {
2363
+ delete target[prop];
2364
+ delete proxy[prop];
2365
+ return true;
2366
+ },
2367
+ get(target, prop, receiver) {
2368
+ return _cached(target, prop,
2369
+ () => _resolveWithContext(target, prop, receiver));
2370
+ },
2371
+ getOwnPropertyDescriptor(target, prop) {
2372
+ return target._descriptors.allKeys
2373
+ ? Reflect.has(proxy, prop) ? {enumerable: true, configurable: true} : undefined
2374
+ : Reflect.getOwnPropertyDescriptor(proxy, prop);
2375
+ },
2376
+ getPrototypeOf() {
2377
+ return Reflect.getPrototypeOf(proxy);
2378
+ },
2379
+ has(target, prop) {
2380
+ return Reflect.has(proxy, prop);
2381
+ },
2382
+ ownKeys() {
2383
+ return Reflect.ownKeys(proxy);
2384
+ },
2385
+ set(target, prop, value) {
2386
+ proxy[prop] = value;
2387
+ delete target[prop];
2388
+ return true;
2389
+ }
2390
+ });
2391
+ }
2392
+ function _descriptors(proxy, defaults = {scriptable: true, indexable: true}) {
2393
+ const {_scriptable = defaults.scriptable, _indexable = defaults.indexable, _allKeys = defaults.allKeys} = proxy;
2394
+ return {
2395
+ allKeys: _allKeys,
2396
+ scriptable: _scriptable,
2397
+ indexable: _indexable,
2398
+ isScriptable: isFunction(_scriptable) ? _scriptable : () => _scriptable,
2399
+ isIndexable: isFunction(_indexable) ? _indexable : () => _indexable
2400
+ };
2401
+ }
2402
+ const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name;
2403
+ const needsSubResolver = (prop, value) => isObject(value) && prop !== 'adapters';
2404
+ function _cached(target, prop, resolve) {
2405
+ let value = target[prop];
2406
+ if (defined(value)) {
2407
+ return value;
2408
+ }
2409
+ value = resolve();
2410
+ if (defined(value)) {
2411
+ target[prop] = value;
2412
+ }
2413
+ return value;
2414
+ }
2415
+ function _resolveWithContext(target, prop, receiver) {
2416
+ const {_proxy, _context, _subProxy, _descriptors: descriptors} = target;
2417
+ let value = _proxy[prop];
2418
+ if (isFunction(value) && descriptors.isScriptable(prop)) {
2419
+ value = _resolveScriptable(prop, value, target, receiver);
2420
+ }
2421
+ if (isArray(value) && value.length) {
2422
+ value = _resolveArray(prop, value, target, descriptors.isIndexable);
2423
+ }
2424
+ if (needsSubResolver(prop, value)) {
2425
+ value = _attachContext(value, _context, _subProxy && _subProxy[prop], descriptors);
2426
+ }
2427
+ return value;
2428
+ }
2429
+ function _resolveScriptable(prop, value, target, receiver) {
2430
+ const {_proxy, _context, _subProxy, _stack} = target;
2431
+ if (_stack.has(prop)) {
2432
+ throw new Error('Recursion detected: ' + Array.from(_stack).join('->') + '->' + prop);
2433
+ }
2434
+ _stack.add(prop);
2435
+ value = value(_context, _subProxy || receiver);
2436
+ _stack.delete(prop);
2437
+ if (isObject(value)) {
2438
+ value = createSubResolver(_proxy._scopes, _proxy, prop, value);
2439
+ }
2440
+ return value;
2441
+ }
2442
+ function _resolveArray(prop, value, target, isIndexable) {
2443
+ const {_proxy, _context, _subProxy, _descriptors: descriptors} = target;
2444
+ if (defined(_context.index) && isIndexable(prop)) {
2445
+ value = value[_context.index % value.length];
2446
+ } else if (isObject(value[0])) {
2447
+ const arr = value;
2448
+ const scopes = _proxy._scopes.filter(s => s !== arr);
2449
+ value = [];
2450
+ for (const item of arr) {
2451
+ const resolver = createSubResolver(scopes, _proxy, prop, item);
2452
+ value.push(_attachContext(resolver, _context, _subProxy && _subProxy[prop], descriptors));
2453
+ }
2454
+ }
2455
+ return value;
2456
+ }
2457
+ function resolveFallback(fallback, prop, value) {
2458
+ return isFunction(fallback) ? fallback(prop, value) : fallback;
2459
+ }
2460
+ const getScope = (key, parent) => key === true ? parent
2461
+ : typeof key === 'string' ? resolveObjectKey(parent, key) : undefined;
2462
+ function addScopes(set, parentScopes, key, parentFallback) {
2463
+ for (const parent of parentScopes) {
2464
+ const scope = getScope(key, parent);
2465
+ if (scope) {
2466
+ set.add(scope);
2467
+ const fallback = resolveFallback(scope._fallback, key, scope);
2468
+ if (defined(fallback) && fallback !== key && fallback !== parentFallback) {
2469
+ return fallback;
2470
+ }
2471
+ } else if (scope === false && defined(parentFallback) && key !== parentFallback) {
2472
+ return null;
2473
+ }
2474
+ }
2475
+ return false;
2476
+ }
2477
+ function createSubResolver(parentScopes, resolver, prop, value) {
2478
+ const rootScopes = resolver._rootScopes;
2479
+ const fallback = resolveFallback(resolver._fallback, prop, value);
2480
+ const allScopes = [...parentScopes, ...rootScopes];
2481
+ const set = new Set();
2482
+ set.add(value);
2483
+ let key = addScopesFromKey(set, allScopes, prop, fallback || prop);
2484
+ if (key === null) {
2485
+ return false;
2486
+ }
2487
+ if (defined(fallback) && fallback !== prop) {
2488
+ key = addScopesFromKey(set, allScopes, fallback, key);
2489
+ if (key === null) {
2490
+ return false;
2491
+ }
2492
+ }
2493
+ return _createResolver(Array.from(set), [''], rootScopes, fallback,
2494
+ () => subGetTarget(resolver, prop, value));
2495
+ }
2496
+ function addScopesFromKey(set, allScopes, key, fallback) {
2497
+ while (key) {
2498
+ key = addScopes(set, allScopes, key, fallback);
2499
+ }
2500
+ return key;
2501
+ }
2502
+ function subGetTarget(resolver, prop, value) {
2503
+ const parent = resolver._getTarget();
2504
+ if (!(prop in parent)) {
2505
+ parent[prop] = {};
2506
+ }
2507
+ const target = parent[prop];
2508
+ if (isArray(target) && isObject(value)) {
2509
+ return value;
2510
+ }
2511
+ return target;
2512
+ }
2513
+ function _resolveWithPrefixes(prop, prefixes, scopes, proxy) {
2514
+ let value;
2515
+ for (const prefix of prefixes) {
2516
+ value = _resolve(readKey(prefix, prop), scopes);
2517
+ if (defined(value)) {
2518
+ return needsSubResolver(prop, value)
2519
+ ? createSubResolver(scopes, proxy, prop, value)
2520
+ : value;
2521
+ }
2522
+ }
2523
+ }
2524
+ function _resolve(key, scopes) {
2525
+ for (const scope of scopes) {
2526
+ if (!scope) {
2527
+ continue;
2528
+ }
2529
+ const value = scope[key];
2530
+ if (defined(value)) {
2531
+ return value;
2532
+ }
2533
+ }
2534
+ }
2535
+ function getKeysFromAllScopes(target) {
2536
+ let keys = target._keys;
2537
+ if (!keys) {
2538
+ keys = target._keys = resolveKeysFromAllScopes(target._scopes);
2539
+ }
2540
+ return keys;
2541
+ }
2542
+ function resolveKeysFromAllScopes(scopes) {
2543
+ const set = new Set();
2544
+ for (const scope of scopes) {
2545
+ for (const key of Object.keys(scope).filter(k => !k.startsWith('_'))) {
2546
+ set.add(key);
2547
+ }
2548
+ }
2549
+ return Array.from(set);
2550
+ }
2551
+
2552
+ const EPSILON = Number.EPSILON || 1e-14;
2553
+ const getPoint = (points, i) => i < points.length && !points[i].skip && points[i];
2554
+ const getValueAxis = (indexAxis) => indexAxis === 'x' ? 'y' : 'x';
2555
+ function splineCurve(firstPoint, middlePoint, afterPoint, t) {
2556
+ const previous = firstPoint.skip ? middlePoint : firstPoint;
2557
+ const current = middlePoint;
2558
+ const next = afterPoint.skip ? middlePoint : afterPoint;
2559
+ const d01 = distanceBetweenPoints(current, previous);
2560
+ const d12 = distanceBetweenPoints(next, current);
2561
+ let s01 = d01 / (d01 + d12);
2562
+ let s12 = d12 / (d01 + d12);
2563
+ s01 = isNaN(s01) ? 0 : s01;
2564
+ s12 = isNaN(s12) ? 0 : s12;
2565
+ const fa = t * s01;
2566
+ const fb = t * s12;
2567
+ return {
2568
+ previous: {
2569
+ x: current.x - fa * (next.x - previous.x),
2570
+ y: current.y - fa * (next.y - previous.y)
2571
+ },
2572
+ next: {
2573
+ x: current.x + fb * (next.x - previous.x),
2574
+ y: current.y + fb * (next.y - previous.y)
2575
+ }
2576
+ };
2577
+ }
2578
+ function monotoneAdjust(points, deltaK, mK) {
2579
+ const pointsLen = points.length;
2580
+ let alphaK, betaK, tauK, squaredMagnitude, pointCurrent;
2581
+ let pointAfter = getPoint(points, 0);
2582
+ for (let i = 0; i < pointsLen - 1; ++i) {
2583
+ pointCurrent = pointAfter;
2584
+ pointAfter = getPoint(points, i + 1);
2585
+ if (!pointCurrent || !pointAfter) {
2586
+ continue;
2587
+ }
2588
+ if (almostEquals(deltaK[i], 0, EPSILON)) {
2589
+ mK[i] = mK[i + 1] = 0;
2590
+ continue;
2591
+ }
2592
+ alphaK = mK[i] / deltaK[i];
2593
+ betaK = mK[i + 1] / deltaK[i];
2594
+ squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
2595
+ if (squaredMagnitude <= 9) {
2596
+ continue;
2597
+ }
2598
+ tauK = 3 / Math.sqrt(squaredMagnitude);
2599
+ mK[i] = alphaK * tauK * deltaK[i];
2600
+ mK[i + 1] = betaK * tauK * deltaK[i];
2601
+ }
2602
+ }
2603
+ function monotoneCompute(points, mK, indexAxis = 'x') {
2604
+ const valueAxis = getValueAxis(indexAxis);
2605
+ const pointsLen = points.length;
2606
+ let delta, pointBefore, pointCurrent;
2607
+ let pointAfter = getPoint(points, 0);
2608
+ for (let i = 0; i < pointsLen; ++i) {
2609
+ pointBefore = pointCurrent;
2610
+ pointCurrent = pointAfter;
2611
+ pointAfter = getPoint(points, i + 1);
2612
+ if (!pointCurrent) {
2613
+ continue;
2614
+ }
2615
+ const iPixel = pointCurrent[indexAxis];
2616
+ const vPixel = pointCurrent[valueAxis];
2617
+ if (pointBefore) {
2618
+ delta = (iPixel - pointBefore[indexAxis]) / 3;
2619
+ pointCurrent[`cp1${indexAxis}`] = iPixel - delta;
2620
+ pointCurrent[`cp1${valueAxis}`] = vPixel - delta * mK[i];
2621
+ }
2622
+ if (pointAfter) {
2623
+ delta = (pointAfter[indexAxis] - iPixel) / 3;
2624
+ pointCurrent[`cp2${indexAxis}`] = iPixel + delta;
2625
+ pointCurrent[`cp2${valueAxis}`] = vPixel + delta * mK[i];
2626
+ }
2627
+ }
2628
+ }
2629
+ function splineCurveMonotone(points, indexAxis = 'x') {
2630
+ const valueAxis = getValueAxis(indexAxis);
2631
+ const pointsLen = points.length;
2632
+ const deltaK = Array(pointsLen).fill(0);
2633
+ const mK = Array(pointsLen);
2634
+ let i, pointBefore, pointCurrent;
2635
+ let pointAfter = getPoint(points, 0);
2636
+ for (i = 0; i < pointsLen; ++i) {
2637
+ pointBefore = pointCurrent;
2638
+ pointCurrent = pointAfter;
2639
+ pointAfter = getPoint(points, i + 1);
2640
+ if (!pointCurrent) {
2641
+ continue;
2642
+ }
2643
+ if (pointAfter) {
2644
+ const slopeDelta = pointAfter[indexAxis] - pointCurrent[indexAxis];
2645
+ deltaK[i] = slopeDelta !== 0 ? (pointAfter[valueAxis] - pointCurrent[valueAxis]) / slopeDelta : 0;
2646
+ }
2647
+ mK[i] = !pointBefore ? deltaK[i]
2648
+ : !pointAfter ? deltaK[i - 1]
2649
+ : (sign(deltaK[i - 1]) !== sign(deltaK[i])) ? 0
2650
+ : (deltaK[i - 1] + deltaK[i]) / 2;
2651
+ }
2652
+ monotoneAdjust(points, deltaK, mK);
2653
+ monotoneCompute(points, mK, indexAxis);
2654
+ }
2655
+ function capControlPoint(pt, min, max) {
2656
+ return Math.max(Math.min(pt, max), min);
2657
+ }
2658
+ function capBezierPoints(points, area) {
2659
+ let i, ilen, point, inArea, inAreaPrev;
2660
+ let inAreaNext = _isPointInArea(points[0], area);
2661
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
2662
+ inAreaPrev = inArea;
2663
+ inArea = inAreaNext;
2664
+ inAreaNext = i < ilen - 1 && _isPointInArea(points[i + 1], area);
2665
+ if (!inArea) {
2666
+ continue;
2667
+ }
2668
+ point = points[i];
2669
+ if (inAreaPrev) {
2670
+ point.cp1x = capControlPoint(point.cp1x, area.left, area.right);
2671
+ point.cp1y = capControlPoint(point.cp1y, area.top, area.bottom);
2672
+ }
2673
+ if (inAreaNext) {
2674
+ point.cp2x = capControlPoint(point.cp2x, area.left, area.right);
2675
+ point.cp2y = capControlPoint(point.cp2y, area.top, area.bottom);
2676
+ }
2677
+ }
2678
+ }
2679
+ function _updateBezierControlPoints(points, options, area, loop, indexAxis) {
2680
+ let i, ilen, point, controlPoints;
2681
+ if (options.spanGaps) {
2682
+ points = points.filter((pt) => !pt.skip);
2683
+ }
2684
+ if (options.cubicInterpolationMode === 'monotone') {
2685
+ splineCurveMonotone(points, indexAxis);
2686
+ } else {
2687
+ let prev = loop ? points[points.length - 1] : points[0];
2688
+ for (i = 0, ilen = points.length; i < ilen; ++i) {
2689
+ point = points[i];
2690
+ controlPoints = splineCurve(
2691
+ prev,
2692
+ point,
2693
+ points[Math.min(i + 1, ilen - (loop ? 0 : 1)) % ilen],
2694
+ options.tension
2695
+ );
2696
+ point.cp1x = controlPoints.previous.x;
2697
+ point.cp1y = controlPoints.previous.y;
2698
+ point.cp2x = controlPoints.next.x;
2699
+ point.cp2y = controlPoints.next.y;
2700
+ prev = point;
2701
+ }
2702
+ }
2703
+ if (options.capBezierPoints) {
2704
+ capBezierPoints(points, area);
2705
+ }
2706
+ }
2707
+
2708
+ const atEdge = (t) => t === 0 || t === 1;
2709
+ const elasticIn = (t, s, p) => -(Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * TAU / p));
2710
+ const elasticOut = (t, s, p) => Math.pow(2, -10 * t) * Math.sin((t - s) * TAU / p) + 1;
2711
+ const effects = {
2712
+ linear: t => t,
2713
+ easeInQuad: t => t * t,
2714
+ easeOutQuad: t => -t * (t - 2),
2715
+ easeInOutQuad: t => ((t /= 0.5) < 1)
2716
+ ? 0.5 * t * t
2717
+ : -0.5 * ((--t) * (t - 2) - 1),
2718
+ easeInCubic: t => t * t * t,
2719
+ easeOutCubic: t => (t -= 1) * t * t + 1,
2720
+ easeInOutCubic: t => ((t /= 0.5) < 1)
2721
+ ? 0.5 * t * t * t
2722
+ : 0.5 * ((t -= 2) * t * t + 2),
2723
+ easeInQuart: t => t * t * t * t,
2724
+ easeOutQuart: t => -((t -= 1) * t * t * t - 1),
2725
+ easeInOutQuart: t => ((t /= 0.5) < 1)
2726
+ ? 0.5 * t * t * t * t
2727
+ : -0.5 * ((t -= 2) * t * t * t - 2),
2728
+ easeInQuint: t => t * t * t * t * t,
2729
+ easeOutQuint: t => (t -= 1) * t * t * t * t + 1,
2730
+ easeInOutQuint: t => ((t /= 0.5) < 1)
2731
+ ? 0.5 * t * t * t * t * t
2732
+ : 0.5 * ((t -= 2) * t * t * t * t + 2),
2733
+ easeInSine: t => -Math.cos(t * HALF_PI) + 1,
2734
+ easeOutSine: t => Math.sin(t * HALF_PI),
2735
+ easeInOutSine: t => -0.5 * (Math.cos(PI * t) - 1),
2736
+ easeInExpo: t => (t === 0) ? 0 : Math.pow(2, 10 * (t - 1)),
2737
+ easeOutExpo: t => (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1,
2738
+ easeInOutExpo: t => atEdge(t) ? t : t < 0.5
2739
+ ? 0.5 * Math.pow(2, 10 * (t * 2 - 1))
2740
+ : 0.5 * (-Math.pow(2, -10 * (t * 2 - 1)) + 2),
2741
+ easeInCirc: t => (t >= 1) ? t : -(Math.sqrt(1 - t * t) - 1),
2742
+ easeOutCirc: t => Math.sqrt(1 - (t -= 1) * t),
2743
+ easeInOutCirc: t => ((t /= 0.5) < 1)
2744
+ ? -0.5 * (Math.sqrt(1 - t * t) - 1)
2745
+ : 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1),
2746
+ easeInElastic: t => atEdge(t) ? t : elasticIn(t, 0.075, 0.3),
2747
+ easeOutElastic: t => atEdge(t) ? t : elasticOut(t, 0.075, 0.3),
2748
+ easeInOutElastic(t) {
2749
+ const s = 0.1125;
2750
+ const p = 0.45;
2751
+ return atEdge(t) ? t :
2752
+ t < 0.5
2753
+ ? 0.5 * elasticIn(t * 2, s, p)
2754
+ : 0.5 + 0.5 * elasticOut(t * 2 - 1, s, p);
2755
+ },
2756
+ easeInBack(t) {
2757
+ const s = 1.70158;
2758
+ return t * t * ((s + 1) * t - s);
2759
+ },
2760
+ easeOutBack(t) {
2761
+ const s = 1.70158;
2762
+ return (t -= 1) * t * ((s + 1) * t + s) + 1;
2763
+ },
2764
+ easeInOutBack(t) {
2765
+ let s = 1.70158;
2766
+ if ((t /= 0.5) < 1) {
2767
+ return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
2768
+ }
2769
+ return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
2770
+ },
2771
+ easeInBounce: t => 1 - effects.easeOutBounce(1 - t),
2772
+ easeOutBounce(t) {
2773
+ const m = 7.5625;
2774
+ const d = 2.75;
2775
+ if (t < (1 / d)) {
2776
+ return m * t * t;
2777
+ }
2778
+ if (t < (2 / d)) {
2779
+ return m * (t -= (1.5 / d)) * t + 0.75;
2780
+ }
2781
+ if (t < (2.5 / d)) {
2782
+ return m * (t -= (2.25 / d)) * t + 0.9375;
2783
+ }
2784
+ return m * (t -= (2.625 / d)) * t + 0.984375;
2785
+ },
2786
+ easeInOutBounce: t => (t < 0.5)
2787
+ ? effects.easeInBounce(t * 2) * 0.5
2788
+ : effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5,
2789
+ };
2790
+
2791
+ function _pointInLine(p1, p2, t, mode) {
2792
+ return {
2793
+ x: p1.x + t * (p2.x - p1.x),
2794
+ y: p1.y + t * (p2.y - p1.y)
2795
+ };
2796
+ }
2797
+ function _steppedInterpolation(p1, p2, t, mode) {
2798
+ return {
2799
+ x: p1.x + t * (p2.x - p1.x),
2800
+ y: mode === 'middle' ? t < 0.5 ? p1.y : p2.y
2801
+ : mode === 'after' ? t < 1 ? p1.y : p2.y
2802
+ : t > 0 ? p2.y : p1.y
2803
+ };
2804
+ }
2805
+ function _bezierInterpolation(p1, p2, t, mode) {
2806
+ const cp1 = {x: p1.cp2x, y: p1.cp2y};
2807
+ const cp2 = {x: p2.cp1x, y: p2.cp1y};
2808
+ const a = _pointInLine(p1, cp1, t);
2809
+ const b = _pointInLine(cp1, cp2, t);
2810
+ const c = _pointInLine(cp2, p2, t);
2811
+ const d = _pointInLine(a, b, t);
2812
+ const e = _pointInLine(b, c, t);
2813
+ return _pointInLine(d, e, t);
2814
+ }
2815
+
2816
+ const intlCache = new Map();
2817
+ function getNumberFormat(locale, options) {
2818
+ options = options || {};
2819
+ const cacheKey = locale + JSON.stringify(options);
2820
+ let formatter = intlCache.get(cacheKey);
2821
+ if (!formatter) {
2822
+ formatter = new Intl.NumberFormat(locale, options);
2823
+ intlCache.set(cacheKey, formatter);
2824
+ }
2825
+ return formatter;
2826
+ }
2827
+ function formatNumber(num, locale, options) {
2828
+ return getNumberFormat(locale, options).format(num);
2829
+ }
2830
+
2831
+ const getRightToLeftAdapter = function(rectX, width) {
2832
+ return {
2833
+ x(x) {
2834
+ return rectX + rectX + width - x;
2835
+ },
2836
+ setWidth(w) {
2837
+ width = w;
2838
+ },
2839
+ textAlign(align) {
2840
+ if (align === 'center') {
2841
+ return align;
2842
+ }
2843
+ return align === 'right' ? 'left' : 'right';
2844
+ },
2845
+ xPlus(x, value) {
2846
+ return x - value;
2847
+ },
2848
+ leftForLtr(x, itemWidth) {
2849
+ return x - itemWidth;
2850
+ },
2851
+ };
2852
+ };
2853
+ const getLeftToRightAdapter = function() {
2854
+ return {
2855
+ x(x) {
2856
+ return x;
2857
+ },
2858
+ setWidth(w) {
2859
+ },
2860
+ textAlign(align) {
2861
+ return align;
2862
+ },
2863
+ xPlus(x, value) {
2864
+ return x + value;
2865
+ },
2866
+ leftForLtr(x, _itemWidth) {
2867
+ return x;
2868
+ },
2869
+ };
2870
+ };
2871
+ function getRtlAdapter(rtl, rectX, width) {
2872
+ return rtl ? getRightToLeftAdapter(rectX, width) : getLeftToRightAdapter();
2873
+ }
2874
+ function overrideTextDirection(ctx, direction) {
2875
+ let style, original;
2876
+ if (direction === 'ltr' || direction === 'rtl') {
2877
+ style = ctx.canvas.style;
2878
+ original = [
2879
+ style.getPropertyValue('direction'),
2880
+ style.getPropertyPriority('direction'),
2881
+ ];
2882
+ style.setProperty('direction', direction, 'important');
2883
+ ctx.prevTextDirection = original;
2884
+ }
2885
+ }
2886
+ function restoreTextDirection(ctx, original) {
2887
+ if (original !== undefined) {
2888
+ delete ctx.prevTextDirection;
2889
+ ctx.canvas.style.setProperty('direction', original[0], original[1]);
2890
+ }
2891
+ }
2892
+
2893
+ function propertyFn(property) {
2894
+ if (property === 'angle') {
2895
+ return {
2896
+ between: _angleBetween,
2897
+ compare: _angleDiff,
2898
+ normalize: _normalizeAngle,
2899
+ };
2900
+ }
2901
+ return {
2902
+ between: (n, s, e) => n >= Math.min(s, e) && n <= Math.max(e, s),
2903
+ compare: (a, b) => a - b,
2904
+ normalize: x => x
2905
+ };
2906
+ }
2907
+ function normalizeSegment({start, end, count, loop, style}) {
2908
+ return {
2909
+ start: start % count,
2910
+ end: end % count,
2911
+ loop: loop && (end - start + 1) % count === 0,
2912
+ style
2913
+ };
2914
+ }
2915
+ function getSegment(segment, points, bounds) {
2916
+ const {property, start: startBound, end: endBound} = bounds;
2917
+ const {between, normalize} = propertyFn(property);
2918
+ const count = points.length;
2919
+ let {start, end, loop} = segment;
2920
+ let i, ilen;
2921
+ if (loop) {
2922
+ start += count;
2923
+ end += count;
2924
+ for (i = 0, ilen = count; i < ilen; ++i) {
2925
+ if (!between(normalize(points[start % count][property]), startBound, endBound)) {
2926
+ break;
2927
+ }
2928
+ start--;
2929
+ end--;
2930
+ }
2931
+ start %= count;
2932
+ end %= count;
2933
+ }
2934
+ if (end < start) {
2935
+ end += count;
2936
+ }
2937
+ return {start, end, loop, style: segment.style};
2938
+ }
2939
+ function _boundSegment(segment, points, bounds) {
2940
+ if (!bounds) {
2941
+ return [segment];
2942
+ }
2943
+ const {property, start: startBound, end: endBound} = bounds;
2944
+ const count = points.length;
2945
+ const {compare, between, normalize} = propertyFn(property);
2946
+ const {start, end, loop, style} = getSegment(segment, points, bounds);
2947
+ const result = [];
2948
+ let inside = false;
2949
+ let subStart = null;
2950
+ let value, point, prevValue;
2951
+ const startIsBefore = () => between(startBound, prevValue, value) && compare(startBound, prevValue) !== 0;
2952
+ const endIsBefore = () => compare(endBound, value) === 0 || between(endBound, prevValue, value);
2953
+ const shouldStart = () => inside || startIsBefore();
2954
+ const shouldStop = () => !inside || endIsBefore();
2955
+ for (let i = start, prev = start; i <= end; ++i) {
2956
+ point = points[i % count];
2957
+ if (point.skip) {
2958
+ continue;
2959
+ }
2960
+ value = normalize(point[property]);
2961
+ if (value === prevValue) {
2962
+ continue;
2963
+ }
2964
+ inside = between(value, startBound, endBound);
2965
+ if (subStart === null && shouldStart()) {
2966
+ subStart = compare(value, startBound) === 0 ? i : prev;
2967
+ }
2968
+ if (subStart !== null && shouldStop()) {
2969
+ result.push(normalizeSegment({start: subStart, end: i, loop, count, style}));
2970
+ subStart = null;
2971
+ }
2972
+ prev = i;
2973
+ prevValue = value;
2974
+ }
2975
+ if (subStart !== null) {
2976
+ result.push(normalizeSegment({start: subStart, end, loop, count, style}));
2977
+ }
2978
+ return result;
2979
+ }
2980
+ function _boundSegments(line, bounds) {
2981
+ const result = [];
2982
+ const segments = line.segments;
2983
+ for (let i = 0; i < segments.length; i++) {
2984
+ const sub = _boundSegment(segments[i], line.points, bounds);
2985
+ if (sub.length) {
2986
+ result.push(...sub);
2987
+ }
2988
+ }
2989
+ return result;
2990
+ }
2991
+ function findStartAndEnd(points, count, loop, spanGaps) {
2992
+ let start = 0;
2993
+ let end = count - 1;
2994
+ if (loop && !spanGaps) {
2995
+ while (start < count && !points[start].skip) {
2996
+ start++;
2997
+ }
2998
+ }
2999
+ while (start < count && points[start].skip) {
3000
+ start++;
3001
+ }
3002
+ start %= count;
3003
+ if (loop) {
3004
+ end += start;
3005
+ }
3006
+ while (end > start && points[end % count].skip) {
3007
+ end--;
3008
+ }
3009
+ end %= count;
3010
+ return {start, end};
3011
+ }
3012
+ function solidSegments(points, start, max, loop) {
3013
+ const count = points.length;
3014
+ const result = [];
3015
+ let last = start;
3016
+ let prev = points[start];
3017
+ let end;
3018
+ for (end = start + 1; end <= max; ++end) {
3019
+ const cur = points[end % count];
3020
+ if (cur.skip || cur.stop) {
3021
+ if (!prev.skip) {
3022
+ loop = false;
3023
+ result.push({start: start % count, end: (end - 1) % count, loop});
3024
+ start = last = cur.stop ? end : null;
3025
+ }
3026
+ } else {
3027
+ last = end;
3028
+ if (prev.skip) {
3029
+ start = end;
3030
+ }
3031
+ }
3032
+ prev = cur;
3033
+ }
3034
+ if (last !== null) {
3035
+ result.push({start: start % count, end: last % count, loop});
3036
+ }
3037
+ return result;
3038
+ }
3039
+ function _computeSegments(line, segmentOptions) {
3040
+ const points = line.points;
3041
+ const spanGaps = line.options.spanGaps;
3042
+ const count = points.length;
3043
+ if (!count) {
3044
+ return [];
3045
+ }
3046
+ const loop = !!line._loop;
3047
+ const {start, end} = findStartAndEnd(points, count, loop, spanGaps);
3048
+ if (spanGaps === true) {
3049
+ return splitByStyles(line, [{start, end, loop}], points, segmentOptions);
3050
+ }
3051
+ const max = end < start ? end + count : end;
3052
+ const completeLoop = !!line._fullLoop && start === 0 && end === count - 1;
3053
+ return splitByStyles(line, solidSegments(points, start, max, completeLoop), points, segmentOptions);
3054
+ }
3055
+ function splitByStyles(line, segments, points, segmentOptions) {
3056
+ if (!segmentOptions || !segmentOptions.setContext || !points) {
3057
+ return segments;
3058
+ }
3059
+ return doSplitByStyles(line, segments, points, segmentOptions);
3060
+ }
3061
+ function doSplitByStyles(line, segments, points, segmentOptions) {
3062
+ const baseStyle = readStyle(line.options);
3063
+ const count = points.length;
3064
+ const result = [];
3065
+ let start = segments[0].start;
3066
+ let i = start;
3067
+ for (const segment of segments) {
3068
+ let prevStyle = baseStyle;
3069
+ let prev = points[start % count];
3070
+ let style;
3071
+ for (i = start + 1; i <= segment.end; i++) {
3072
+ const pt = points[i % count];
3073
+ style = readStyle(segmentOptions.setContext({
3074
+ type: 'segment',
3075
+ p0: prev,
3076
+ p1: pt,
3077
+ p0DataIndex: (i - 1) % count,
3078
+ p1DataIndex: i % count,
3079
+ datasetIndex: line._datasetIndex
3080
+ }));
3081
+ if (styleChanged(style, prevStyle)) {
3082
+ result.push({start: start, end: i - 1, loop: segment.loop, style: prevStyle});
3083
+ prevStyle = style;
3084
+ start = i - 1;
3085
+ }
3086
+ prev = pt;
3087
+ prevStyle = style;
3088
+ }
3089
+ if (start < i - 1) {
3090
+ result.push({start, end: i - 1, loop: segment.loop, style});
3091
+ start = i - 1;
3092
+ }
3093
+ }
3094
+ return result;
3095
+ }
3096
+ function readStyle(options) {
3097
+ return {
3098
+ backgroundColor: options.backgroundColor,
3099
+ borderCapStyle: options.borderCapStyle,
3100
+ borderDash: options.borderDash,
3101
+ borderDashOffset: options.borderDashOffset,
3102
+ borderJoinStyle: options.borderJoinStyle,
3103
+ borderWidth: options.borderWidth,
3104
+ borderColor: options.borderColor
3105
+ };
3106
+ }
3107
+ function styleChanged(style, prevStyle) {
3108
+ return prevStyle && JSON.stringify(style) !== JSON.stringify(prevStyle);
3109
+ }
3110
+
3111
+ var helpers = /*#__PURE__*/Object.freeze({
3112
+ __proto__: null,
3113
+ easingEffects: effects,
3114
+ color: color,
3115
+ getHoverColor: getHoverColor,
3116
+ noop: noop,
3117
+ uid: uid,
3118
+ isNullOrUndef: isNullOrUndef,
3119
+ isArray: isArray,
3120
+ isObject: isObject,
3121
+ isFinite: isNumberFinite,
3122
+ finiteOrDefault: finiteOrDefault,
3123
+ valueOrDefault: valueOrDefault,
3124
+ toPercentage: toPercentage,
3125
+ toDimension: toDimension,
3126
+ callback: callback,
3127
+ each: each,
3128
+ _elementsEqual: _elementsEqual,
3129
+ clone: clone,
3130
+ _merger: _merger,
3131
+ merge: merge,
3132
+ mergeIf: mergeIf,
3133
+ _mergerIf: _mergerIf,
3134
+ _deprecated: _deprecated,
3135
+ resolveObjectKey: resolveObjectKey,
3136
+ _capitalize: _capitalize,
3137
+ defined: defined,
3138
+ isFunction: isFunction,
3139
+ setsEqual: setsEqual,
3140
+ toFontString: toFontString,
3141
+ _measureText: _measureText,
3142
+ _longestText: _longestText,
3143
+ _alignPixel: _alignPixel,
3144
+ clearCanvas: clearCanvas,
3145
+ drawPoint: drawPoint,
3146
+ _isPointInArea: _isPointInArea,
3147
+ clipArea: clipArea,
3148
+ unclipArea: unclipArea,
3149
+ _steppedLineTo: _steppedLineTo,
3150
+ _bezierCurveTo: _bezierCurveTo,
3151
+ renderText: renderText,
3152
+ addRoundedRectPath: addRoundedRectPath,
3153
+ _lookup: _lookup,
3154
+ _lookupByKey: _lookupByKey,
3155
+ _rlookupByKey: _rlookupByKey,
3156
+ _filterBetween: _filterBetween,
3157
+ listenArrayEvents: listenArrayEvents,
3158
+ unlistenArrayEvents: unlistenArrayEvents,
3159
+ _arrayUnique: _arrayUnique,
3160
+ _createResolver: _createResolver,
3161
+ _attachContext: _attachContext,
3162
+ _descriptors: _descriptors,
3163
+ splineCurve: splineCurve,
3164
+ splineCurveMonotone: splineCurveMonotone,
3165
+ _updateBezierControlPoints: _updateBezierControlPoints,
3166
+ _isDomSupported: _isDomSupported,
3167
+ _getParentNode: _getParentNode,
3168
+ getStyle: getStyle,
3169
+ getRelativePosition: getRelativePosition$1,
3170
+ getMaximumSize: getMaximumSize,
3171
+ retinaScale: retinaScale,
3172
+ supportsEventListenerOptions: supportsEventListenerOptions,
3173
+ readUsedSize: readUsedSize,
3174
+ fontString: fontString,
3175
+ requestAnimFrame: requestAnimFrame,
3176
+ throttled: throttled,
3177
+ debounce: debounce,
3178
+ _toLeftRightCenter: _toLeftRightCenter,
3179
+ _alignStartEnd: _alignStartEnd,
3180
+ _textX: _textX,
3181
+ _pointInLine: _pointInLine,
3182
+ _steppedInterpolation: _steppedInterpolation,
3183
+ _bezierInterpolation: _bezierInterpolation,
3184
+ formatNumber: formatNumber,
3185
+ toLineHeight: toLineHeight,
3186
+ _readValueToProps: _readValueToProps,
3187
+ toTRBL: toTRBL,
3188
+ toTRBLCorners: toTRBLCorners,
3189
+ toPadding: toPadding,
3190
+ toFont: toFont,
3191
+ resolve: resolve,
3192
+ _addGrace: _addGrace,
3193
+ PI: PI,
3194
+ TAU: TAU,
3195
+ PITAU: PITAU,
3196
+ INFINITY: INFINITY,
3197
+ RAD_PER_DEG: RAD_PER_DEG,
3198
+ HALF_PI: HALF_PI,
3199
+ QUARTER_PI: QUARTER_PI,
3200
+ TWO_THIRDS_PI: TWO_THIRDS_PI,
3201
+ log10: log10,
3202
+ sign: sign,
3203
+ niceNum: niceNum,
3204
+ _factorize: _factorize,
3205
+ isNumber: isNumber,
3206
+ almostEquals: almostEquals,
3207
+ almostWhole: almostWhole,
3208
+ _setMinAndMaxByKey: _setMinAndMaxByKey,
3209
+ toRadians: toRadians,
3210
+ toDegrees: toDegrees,
3211
+ _decimalPlaces: _decimalPlaces,
3212
+ getAngleFromPoint: getAngleFromPoint,
3213
+ distanceBetweenPoints: distanceBetweenPoints,
3214
+ _angleDiff: _angleDiff,
3215
+ _normalizeAngle: _normalizeAngle,
3216
+ _angleBetween: _angleBetween,
3217
+ _limitValue: _limitValue,
3218
+ _int16Range: _int16Range,
3219
+ getRtlAdapter: getRtlAdapter,
3220
+ overrideTextDirection: overrideTextDirection,
3221
+ restoreTextDirection: restoreTextDirection,
3222
+ _boundSegment: _boundSegment,
3223
+ _boundSegments: _boundSegments,
3224
+ _computeSegments: _computeSegments
3225
+ });
3226
+
3227
+ class BasePlatform {
3228
+ acquireContext(canvas, aspectRatio) {}
3229
+ releaseContext(context) {
3230
+ return false;
3231
+ }
3232
+ addEventListener(chart, type, listener) {}
3233
+ removeEventListener(chart, type, listener) {}
3234
+ getDevicePixelRatio() {
3235
+ return 1;
3236
+ }
3237
+ getMaximumSize(element, width, height, aspectRatio) {
3238
+ width = Math.max(0, width || element.width);
3239
+ height = height || element.height;
3240
+ return {
3241
+ width,
3242
+ height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)
3243
+ };
3244
+ }
3245
+ isAttached(canvas) {
3246
+ return true;
3247
+ }
3248
+ }
3249
+
3250
+ class BasicPlatform extends BasePlatform {
3251
+ acquireContext(item) {
3252
+ return item && item.getContext && item.getContext('2d') || null;
3253
+ }
3254
+ }
3255
+
3256
+ const EXPANDO_KEY = '$chartjs';
3257
+ const EVENT_TYPES = {
3258
+ touchstart: 'mousedown',
3259
+ touchmove: 'mousemove',
3260
+ touchend: 'mouseup',
3261
+ pointerenter: 'mouseenter',
3262
+ pointerdown: 'mousedown',
3263
+ pointermove: 'mousemove',
3264
+ pointerup: 'mouseup',
3265
+ pointerleave: 'mouseout',
3266
+ pointerout: 'mouseout'
3267
+ };
3268
+ const isNullOrEmpty = value => value === null || value === '';
3269
+ function initCanvas(canvas, aspectRatio) {
3270
+ const style = canvas.style;
3271
+ const renderHeight = canvas.getAttribute('height');
3272
+ const renderWidth = canvas.getAttribute('width');
3273
+ canvas[EXPANDO_KEY] = {
3274
+ initial: {
3275
+ height: renderHeight,
3276
+ width: renderWidth,
3277
+ style: {
3278
+ display: style.display,
3279
+ height: style.height,
3280
+ width: style.width
3281
+ }
3282
+ }
3283
+ };
3284
+ style.display = style.display || 'block';
3285
+ style.boxSizing = style.boxSizing || 'border-box';
3286
+ if (isNullOrEmpty(renderWidth)) {
3287
+ const displayWidth = readUsedSize(canvas, 'width');
3288
+ if (displayWidth !== undefined) {
3289
+ canvas.width = displayWidth;
3290
+ }
3291
+ }
3292
+ if (isNullOrEmpty(renderHeight)) {
3293
+ if (canvas.style.height === '') {
3294
+ canvas.height = canvas.width / (aspectRatio || 2);
3295
+ } else {
3296
+ const displayHeight = readUsedSize(canvas, 'height');
3297
+ if (displayHeight !== undefined) {
3298
+ canvas.height = displayHeight;
3299
+ }
3300
+ }
3301
+ }
3302
+ return canvas;
3303
+ }
3304
+ const eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
3305
+ function addListener(node, type, listener) {
3306
+ node.addEventListener(type, listener, eventListenerOptions);
3307
+ }
3308
+ function removeListener(chart, type, listener) {
3309
+ chart.canvas.removeEventListener(type, listener, eventListenerOptions);
3310
+ }
3311
+ function fromNativeEvent(event, chart) {
3312
+ const type = EVENT_TYPES[event.type] || event.type;
3313
+ const {x, y} = getRelativePosition$1(event, chart);
3314
+ return {
3315
+ type,
3316
+ chart,
3317
+ native: event,
3318
+ x: x !== undefined ? x : null,
3319
+ y: y !== undefined ? y : null,
3320
+ };
3321
+ }
3322
+ function createAttachObserver(chart, type, listener) {
3323
+ const canvas = chart.canvas;
3324
+ const observer = new MutationObserver(entries => {
3325
+ for (const entry of entries) {
3326
+ for (const node of entry.addedNodes) {
3327
+ if (node === canvas || node.contains(canvas)) {
3328
+ return listener();
3329
+ }
3330
+ }
3331
+ }
3332
+ });
3333
+ observer.observe(document, {childList: true, subtree: true});
3334
+ return observer;
3335
+ }
3336
+ function createDetachObserver(chart, type, listener) {
3337
+ const canvas = chart.canvas;
3338
+ const observer = new MutationObserver(entries => {
3339
+ for (const entry of entries) {
3340
+ for (const node of entry.removedNodes) {
3341
+ if (node === canvas || node.contains(canvas)) {
3342
+ return listener();
3343
+ }
3344
+ }
3345
+ }
3346
+ });
3347
+ observer.observe(document, {childList: true, subtree: true});
3348
+ return observer;
3349
+ }
3350
+ const drpListeningCharts = new Map();
3351
+ let oldDevicePixelRatio = 0;
3352
+ function onWindowResize() {
3353
+ const dpr = window.devicePixelRatio;
3354
+ if (dpr === oldDevicePixelRatio) {
3355
+ return;
3356
+ }
3357
+ oldDevicePixelRatio = dpr;
3358
+ drpListeningCharts.forEach((resize, chart) => {
3359
+ if (chart.currentDevicePixelRatio !== dpr) {
3360
+ resize();
3361
+ }
3362
+ });
3363
+ }
3364
+ function listenDevicePixelRatioChanges(chart, resize) {
3365
+ if (!drpListeningCharts.size) {
3366
+ window.addEventListener('resize', onWindowResize);
3367
+ }
3368
+ drpListeningCharts.set(chart, resize);
3369
+ }
3370
+ function unlistenDevicePixelRatioChanges(chart) {
3371
+ drpListeningCharts.delete(chart);
3372
+ if (!drpListeningCharts.size) {
3373
+ window.removeEventListener('resize', onWindowResize);
3374
+ }
3375
+ }
3376
+ function createResizeObserver(chart, type, listener) {
3377
+ const canvas = chart.canvas;
3378
+ const container = canvas && _getParentNode(canvas);
3379
+ if (!container) {
3380
+ return;
3381
+ }
3382
+ const resize = throttled((width, height) => {
3383
+ const w = container.clientWidth;
3384
+ listener(width, height);
3385
+ if (w < container.clientWidth) {
3386
+ listener();
3387
+ }
3388
+ }, window);
3389
+ const observer = new ResizeObserver(entries => {
3390
+ const entry = entries[0];
3391
+ const width = entry.contentRect.width;
3392
+ const height = entry.contentRect.height;
3393
+ if (width === 0 && height === 0) {
3394
+ return;
3395
+ }
3396
+ resize(width, height);
3397
+ });
3398
+ observer.observe(container);
3399
+ listenDevicePixelRatioChanges(chart, resize);
3400
+ return observer;
3401
+ }
3402
+ function releaseObserver(chart, type, observer) {
3403
+ if (observer) {
3404
+ observer.disconnect();
3405
+ }
3406
+ if (type === 'resize') {
3407
+ unlistenDevicePixelRatioChanges(chart);
3408
+ }
3409
+ }
3410
+ function createProxyAndListen(chart, type, listener) {
3411
+ const canvas = chart.canvas;
3412
+ const proxy = throttled((event) => {
3413
+ if (chart.ctx !== null) {
3414
+ listener(fromNativeEvent(event, chart));
3415
+ }
3416
+ }, chart, (args) => {
3417
+ const event = args[0];
3418
+ return [event, event.offsetX, event.offsetY];
3419
+ });
3420
+ addListener(canvas, type, proxy);
3421
+ return proxy;
3422
+ }
3423
+ class DomPlatform extends BasePlatform {
3424
+ acquireContext(canvas, aspectRatio) {
3425
+ const context = canvas && canvas.getContext && canvas.getContext('2d');
3426
+ if (context && context.canvas === canvas) {
3427
+ initCanvas(canvas, aspectRatio);
3428
+ return context;
3429
+ }
3430
+ return null;
3431
+ }
3432
+ releaseContext(context) {
3433
+ const canvas = context.canvas;
3434
+ if (!canvas[EXPANDO_KEY]) {
3435
+ return false;
3436
+ }
3437
+ const initial = canvas[EXPANDO_KEY].initial;
3438
+ ['height', 'width'].forEach((prop) => {
3439
+ const value = initial[prop];
3440
+ if (isNullOrUndef(value)) {
3441
+ canvas.removeAttribute(prop);
3442
+ } else {
3443
+ canvas.setAttribute(prop, value);
3444
+ }
3445
+ });
3446
+ const style = initial.style || {};
3447
+ Object.keys(style).forEach((key) => {
3448
+ canvas.style[key] = style[key];
3449
+ });
3450
+ canvas.width = canvas.width;
3451
+ delete canvas[EXPANDO_KEY];
3452
+ return true;
3453
+ }
3454
+ addEventListener(chart, type, listener) {
3455
+ this.removeEventListener(chart, type);
3456
+ const proxies = chart.$proxies || (chart.$proxies = {});
3457
+ const handlers = {
3458
+ attach: createAttachObserver,
3459
+ detach: createDetachObserver,
3460
+ resize: createResizeObserver
3461
+ };
3462
+ const handler = handlers[type] || createProxyAndListen;
3463
+ proxies[type] = handler(chart, type, listener);
3464
+ }
3465
+ removeEventListener(chart, type) {
3466
+ const proxies = chart.$proxies || (chart.$proxies = {});
3467
+ const proxy = proxies[type];
3468
+ if (!proxy) {
3469
+ return;
3470
+ }
3471
+ const handlers = {
3472
+ attach: releaseObserver,
3473
+ detach: releaseObserver,
3474
+ resize: releaseObserver
3475
+ };
3476
+ const handler = handlers[type] || removeListener;
3477
+ handler(chart, type, proxy);
3478
+ proxies[type] = undefined;
3479
+ }
3480
+ getDevicePixelRatio() {
3481
+ return window.devicePixelRatio;
3482
+ }
3483
+ getMaximumSize(canvas, width, height, aspectRatio) {
3484
+ return getMaximumSize(canvas, width, height, aspectRatio);
3485
+ }
3486
+ isAttached(canvas) {
3487
+ const container = _getParentNode(canvas);
3488
+ return !!(container && container.isConnected);
3489
+ }
3490
+ }
3491
+
3492
+ function _detectPlatform(canvas) {
3493
+ if (!_isDomSupported() || (typeof OffscreenCanvas !== 'undefined' && canvas instanceof OffscreenCanvas)) {
3494
+ return BasicPlatform;
3495
+ }
3496
+ return DomPlatform;
3497
+ }
3498
+
3499
+ var platforms = /*#__PURE__*/Object.freeze({
3500
+ __proto__: null,
3501
+ _detectPlatform: _detectPlatform,
3502
+ BasePlatform: BasePlatform,
3503
+ BasicPlatform: BasicPlatform,
3504
+ DomPlatform: DomPlatform
3505
+ });
3506
+
3507
+ const transparent = 'transparent';
3508
+ const interpolators = {
3509
+ boolean(from, to, factor) {
3510
+ return factor > 0.5 ? to : from;
3511
+ },
3512
+ color(from, to, factor) {
3513
+ const c0 = color(from || transparent);
3514
+ const c1 = c0.valid && color(to || transparent);
3515
+ return c1 && c1.valid
3516
+ ? c1.mix(c0, factor).hexString()
3517
+ : to;
3518
+ },
3519
+ number(from, to, factor) {
3520
+ return from + (to - from) * factor;
3521
+ }
3522
+ };
3523
+ class Animation {
3524
+ constructor(cfg, target, prop, to) {
3525
+ const currentValue = target[prop];
3526
+ to = resolve([cfg.to, to, currentValue, cfg.from]);
3527
+ const from = resolve([cfg.from, currentValue, to]);
3528
+ this._active = true;
3529
+ this._fn = cfg.fn || interpolators[cfg.type || typeof from];
3530
+ this._easing = effects[cfg.easing] || effects.linear;
3531
+ this._start = Math.floor(Date.now() + (cfg.delay || 0));
3532
+ this._duration = this._total = Math.floor(cfg.duration);
3533
+ this._loop = !!cfg.loop;
3534
+ this._target = target;
3535
+ this._prop = prop;
3536
+ this._from = from;
3537
+ this._to = to;
3538
+ this._promises = undefined;
3539
+ }
3540
+ active() {
3541
+ return this._active;
3542
+ }
3543
+ update(cfg, to, date) {
3544
+ const me = this;
3545
+ if (me._active) {
3546
+ me._notify(false);
3547
+ const currentValue = me._target[me._prop];
3548
+ const elapsed = date - me._start;
3549
+ const remain = me._duration - elapsed;
3550
+ me._start = date;
3551
+ me._duration = Math.floor(Math.max(remain, cfg.duration));
3552
+ me._total += elapsed;
3553
+ me._loop = !!cfg.loop;
3554
+ me._to = resolve([cfg.to, to, currentValue, cfg.from]);
3555
+ me._from = resolve([cfg.from, currentValue, to]);
3556
+ }
3557
+ }
3558
+ cancel() {
3559
+ const me = this;
3560
+ if (me._active) {
3561
+ me.tick(Date.now());
3562
+ me._active = false;
3563
+ me._notify(false);
3564
+ }
3565
+ }
3566
+ tick(date) {
3567
+ const me = this;
3568
+ const elapsed = date - me._start;
3569
+ const duration = me._duration;
3570
+ const prop = me._prop;
3571
+ const from = me._from;
3572
+ const loop = me._loop;
3573
+ const to = me._to;
3574
+ let factor;
3575
+ me._active = from !== to && (loop || (elapsed < duration));
3576
+ if (!me._active) {
3577
+ me._target[prop] = to;
3578
+ me._notify(true);
3579
+ return;
3580
+ }
3581
+ if (elapsed < 0) {
3582
+ me._target[prop] = from;
3583
+ return;
3584
+ }
3585
+ factor = (elapsed / duration) % 2;
3586
+ factor = loop && factor > 1 ? 2 - factor : factor;
3587
+ factor = me._easing(Math.min(1, Math.max(0, factor)));
3588
+ me._target[prop] = me._fn(from, to, factor);
3589
+ }
3590
+ wait() {
3591
+ const promises = this._promises || (this._promises = []);
3592
+ return new Promise((res, rej) => {
3593
+ promises.push({res, rej});
3594
+ });
3595
+ }
3596
+ _notify(resolved) {
3597
+ const method = resolved ? 'res' : 'rej';
3598
+ const promises = this._promises || [];
3599
+ for (let i = 0; i < promises.length; i++) {
3600
+ promises[i][method]();
3601
+ }
3602
+ }
3603
+ }
3604
+
3605
+ const numbers = ['x', 'y', 'borderWidth', 'radius', 'tension'];
3606
+ const colors = ['color', 'borderColor', 'backgroundColor'];
3607
+ defaults.set('animation', {
3608
+ delay: undefined,
3609
+ duration: 1000,
3610
+ easing: 'easeOutQuart',
3611
+ fn: undefined,
3612
+ from: undefined,
3613
+ loop: undefined,
3614
+ to: undefined,
3615
+ type: undefined,
3616
+ });
3617
+ const animationOptions = Object.keys(defaults.animation);
3618
+ defaults.describe('animation', {
3619
+ _fallback: false,
3620
+ _indexable: false,
3621
+ _scriptable: (name) => name !== 'onProgress' && name !== 'onComplete' && name !== 'fn',
3622
+ });
3623
+ defaults.set('animations', {
3624
+ colors: {
3625
+ type: 'color',
3626
+ properties: colors
3627
+ },
3628
+ numbers: {
3629
+ type: 'number',
3630
+ properties: numbers
3631
+ },
3632
+ });
3633
+ defaults.describe('animations', {
3634
+ _fallback: 'animation',
3635
+ });
3636
+ defaults.set('transitions', {
3637
+ active: {
3638
+ animation: {
3639
+ duration: 400
3640
+ }
3641
+ },
3642
+ resize: {
3643
+ animation: {
3644
+ duration: 0
3645
+ }
3646
+ },
3647
+ show: {
3648
+ animations: {
3649
+ colors: {
3650
+ from: 'transparent'
3651
+ },
3652
+ visible: {
3653
+ type: 'boolean',
3654
+ duration: 0
3655
+ },
3656
+ }
3657
+ },
3658
+ hide: {
3659
+ animations: {
3660
+ colors: {
3661
+ to: 'transparent'
3662
+ },
3663
+ visible: {
3664
+ type: 'boolean',
3665
+ easing: 'linear',
3666
+ fn: v => v | 0
3667
+ },
3668
+ }
3669
+ }
3670
+ });
3671
+ class Animations {
3672
+ constructor(chart, config) {
3673
+ this._chart = chart;
3674
+ this._properties = new Map();
3675
+ this.configure(config);
3676
+ }
3677
+ configure(config) {
3678
+ if (!isObject(config)) {
3679
+ return;
3680
+ }
3681
+ const animatedProps = this._properties;
3682
+ Object.getOwnPropertyNames(config).forEach(key => {
3683
+ const cfg = config[key];
3684
+ if (!isObject(cfg)) {
3685
+ return;
3686
+ }
3687
+ const resolved = {};
3688
+ for (const option of animationOptions) {
3689
+ resolved[option] = cfg[option];
3690
+ }
3691
+ (isArray(cfg.properties) && cfg.properties || [key]).forEach((prop) => {
3692
+ if (prop === key || !animatedProps.has(prop)) {
3693
+ animatedProps.set(prop, resolved);
3694
+ }
3695
+ });
3696
+ });
3697
+ }
3698
+ _animateOptions(target, values) {
3699
+ const newOptions = values.options;
3700
+ const options = resolveTargetOptions(target, newOptions);
3701
+ if (!options) {
3702
+ return [];
3703
+ }
3704
+ const animations = this._createAnimations(options, newOptions);
3705
+ if (newOptions.$shared) {
3706
+ awaitAll(target.options.$animations, newOptions).then(() => {
3707
+ target.options = newOptions;
3708
+ }, () => {
3709
+ });
3710
+ }
3711
+ return animations;
3712
+ }
3713
+ _createAnimations(target, values) {
3714
+ const animatedProps = this._properties;
3715
+ const animations = [];
3716
+ const running = target.$animations || (target.$animations = {});
3717
+ const props = Object.keys(values);
3718
+ const date = Date.now();
3719
+ let i;
3720
+ for (i = props.length - 1; i >= 0; --i) {
3721
+ const prop = props[i];
3722
+ if (prop.charAt(0) === '$') {
3723
+ continue;
3724
+ }
3725
+ if (prop === 'options') {
3726
+ animations.push(...this._animateOptions(target, values));
3727
+ continue;
3728
+ }
3729
+ const value = values[prop];
3730
+ let animation = running[prop];
3731
+ const cfg = animatedProps.get(prop);
3732
+ if (animation) {
3733
+ if (cfg && animation.active()) {
3734
+ animation.update(cfg, value, date);
3735
+ continue;
3736
+ } else {
3737
+ animation.cancel();
3738
+ }
3739
+ }
3740
+ if (!cfg || !cfg.duration) {
3741
+ target[prop] = value;
3742
+ continue;
3743
+ }
3744
+ running[prop] = animation = new Animation(cfg, target, prop, value);
3745
+ animations.push(animation);
3746
+ }
3747
+ return animations;
3748
+ }
3749
+ update(target, values) {
3750
+ if (this._properties.size === 0) {
3751
+ Object.assign(target, values);
3752
+ return;
3753
+ }
3754
+ const animations = this._createAnimations(target, values);
3755
+ if (animations.length) {
3756
+ animator.add(this._chart, animations);
3757
+ return true;
3758
+ }
3759
+ }
3760
+ }
3761
+ function awaitAll(animations, properties) {
3762
+ const running = [];
3763
+ const keys = Object.keys(properties);
3764
+ for (let i = 0; i < keys.length; i++) {
3765
+ const anim = animations[keys[i]];
3766
+ if (anim && anim.active()) {
3767
+ running.push(anim.wait());
3768
+ }
3769
+ }
3770
+ return Promise.all(running);
3771
+ }
3772
+ function resolveTargetOptions(target, newOptions) {
3773
+ if (!newOptions) {
3774
+ return;
3775
+ }
3776
+ let options = target.options;
3777
+ if (!options) {
3778
+ target.options = newOptions;
3779
+ return;
3780
+ }
3781
+ if (options.$shared) {
3782
+ target.options = options = Object.assign({}, options, {$shared: false, $animations: {}});
3783
+ }
3784
+ return options;
3785
+ }
3786
+
3787
+ function scaleClip(scale, allowedOverflow) {
3788
+ const opts = scale && scale.options || {};
3789
+ const reverse = opts.reverse;
3790
+ const min = opts.min === undefined ? allowedOverflow : 0;
3791
+ const max = opts.max === undefined ? allowedOverflow : 0;
3792
+ return {
3793
+ start: reverse ? max : min,
3794
+ end: reverse ? min : max
3795
+ };
3796
+ }
3797
+ function defaultClip(xScale, yScale, allowedOverflow) {
3798
+ if (allowedOverflow === false) {
3799
+ return false;
3800
+ }
3801
+ const x = scaleClip(xScale, allowedOverflow);
3802
+ const y = scaleClip(yScale, allowedOverflow);
3803
+ return {
3804
+ top: y.end,
3805
+ right: x.end,
3806
+ bottom: y.start,
3807
+ left: x.start
3808
+ };
3809
+ }
3810
+ function toClip(value) {
3811
+ let t, r, b, l;
3812
+ if (isObject(value)) {
3813
+ t = value.top;
3814
+ r = value.right;
3815
+ b = value.bottom;
3816
+ l = value.left;
3817
+ } else {
3818
+ t = r = b = l = value;
3819
+ }
3820
+ return {
3821
+ top: t,
3822
+ right: r,
3823
+ bottom: b,
3824
+ left: l,
3825
+ disabled: value === false
3826
+ };
3827
+ }
3828
+ function getSortedDatasetIndices(chart, filterVisible) {
3829
+ const keys = [];
3830
+ const metasets = chart._getSortedDatasetMetas(filterVisible);
3831
+ let i, ilen;
3832
+ for (i = 0, ilen = metasets.length; i < ilen; ++i) {
3833
+ keys.push(metasets[i].index);
3834
+ }
3835
+ return keys;
3836
+ }
3837
+ function applyStack(stack, value, dsIndex, options) {
3838
+ const keys = stack.keys;
3839
+ const singleMode = options.mode === 'single';
3840
+ let i, ilen, datasetIndex, otherValue;
3841
+ if (value === null) {
3842
+ return;
3843
+ }
3844
+ for (i = 0, ilen = keys.length; i < ilen; ++i) {
3845
+ datasetIndex = +keys[i];
3846
+ if (datasetIndex === dsIndex) {
3847
+ if (options.all) {
3848
+ continue;
3849
+ }
3850
+ break;
3851
+ }
3852
+ otherValue = stack.values[datasetIndex];
3853
+ if (isNumberFinite(otherValue) && (singleMode || (value === 0 || sign(value) === sign(otherValue)))) {
3854
+ value += otherValue;
3855
+ }
3856
+ }
3857
+ return value;
3858
+ }
3859
+ function convertObjectDataToArray(data) {
3860
+ const keys = Object.keys(data);
3861
+ const adata = new Array(keys.length);
3862
+ let i, ilen, key;
3863
+ for (i = 0, ilen = keys.length; i < ilen; ++i) {
3864
+ key = keys[i];
3865
+ adata[i] = {
3866
+ x: key,
3867
+ y: data[key]
3868
+ };
3869
+ }
3870
+ return adata;
3871
+ }
3872
+ function isStacked(scale, meta) {
3873
+ const stacked = scale && scale.options.stacked;
3874
+ return stacked || (stacked === undefined && meta.stack !== undefined);
3875
+ }
3876
+ function getStackKey(indexScale, valueScale, meta) {
3877
+ return `${indexScale.id}.${valueScale.id}.${meta.stack || meta.type}`;
3878
+ }
3879
+ function getUserBounds(scale) {
3880
+ const {min, max, minDefined, maxDefined} = scale.getUserBounds();
3881
+ return {
3882
+ min: minDefined ? min : Number.NEGATIVE_INFINITY,
3883
+ max: maxDefined ? max : Number.POSITIVE_INFINITY
3884
+ };
3885
+ }
3886
+ function getOrCreateStack(stacks, stackKey, indexValue) {
3887
+ const subStack = stacks[stackKey] || (stacks[stackKey] = {});
3888
+ return subStack[indexValue] || (subStack[indexValue] = {});
3889
+ }
3890
+ function getLastIndexInStack(stack, vScale, positive) {
3891
+ for (const meta of vScale.getMatchingVisibleMetas('bar').reverse()) {
3892
+ const value = stack[meta.index];
3893
+ if ((positive && value > 0) || (!positive && value < 0)) {
3894
+ return meta.index;
3895
+ }
3896
+ }
3897
+ return null;
3898
+ }
3899
+ function updateStacks(controller, parsed) {
3900
+ const {chart, _cachedMeta: meta} = controller;
3901
+ const stacks = chart._stacks || (chart._stacks = {});
3902
+ const {iScale, vScale, index: datasetIndex} = meta;
3903
+ const iAxis = iScale.axis;
3904
+ const vAxis = vScale.axis;
3905
+ const key = getStackKey(iScale, vScale, meta);
3906
+ const ilen = parsed.length;
3907
+ let stack;
3908
+ for (let i = 0; i < ilen; ++i) {
3909
+ const item = parsed[i];
3910
+ const {[iAxis]: index, [vAxis]: value} = item;
3911
+ const itemStacks = item._stacks || (item._stacks = {});
3912
+ stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
3913
+ stack[datasetIndex] = value;
3914
+ stack._top = getLastIndexInStack(stack, vScale, true);
3915
+ stack._bottom = getLastIndexInStack(stack, vScale, false);
3916
+ }
3917
+ }
3918
+ function getFirstScaleId(chart, axis) {
3919
+ const scales = chart.scales;
3920
+ return Object.keys(scales).filter(key => scales[key].axis === axis).shift();
3921
+ }
3922
+ function createDatasetContext(parent, index) {
3923
+ return Object.assign(Object.create(parent),
3924
+ {
3925
+ active: false,
3926
+ dataset: undefined,
3927
+ datasetIndex: index,
3928
+ index,
3929
+ mode: 'default',
3930
+ type: 'dataset'
3931
+ }
3932
+ );
3933
+ }
3934
+ function createDataContext(parent, index, element) {
3935
+ return Object.assign(Object.create(parent), {
3936
+ active: false,
3937
+ dataIndex: index,
3938
+ parsed: undefined,
3939
+ raw: undefined,
3940
+ element,
3941
+ index,
3942
+ mode: 'default',
3943
+ type: 'data'
3944
+ });
3945
+ }
3946
+ function clearStacks(meta, items) {
3947
+ const datasetIndex = meta.controller.index;
3948
+ const axis = meta.vScale && meta.vScale.axis;
3949
+ if (!axis) {
3950
+ return;
3951
+ }
3952
+ items = items || meta._parsed;
3953
+ for (const parsed of items) {
3954
+ const stacks = parsed._stacks;
3955
+ if (!stacks || stacks[axis] === undefined || stacks[axis][datasetIndex] === undefined) {
3956
+ return;
3957
+ }
3958
+ delete stacks[axis][datasetIndex];
3959
+ }
3960
+ }
3961
+ const isDirectUpdateMode = (mode) => mode === 'reset' || mode === 'none';
3962
+ const cloneIfNotShared = (cached, shared) => shared ? cached : Object.assign({}, cached);
3963
+ class DatasetController {
3964
+ constructor(chart, datasetIndex) {
3965
+ this.chart = chart;
3966
+ this._ctx = chart.ctx;
3967
+ this.index = datasetIndex;
3968
+ this._cachedDataOpts = {};
3969
+ this._cachedMeta = this.getMeta();
3970
+ this._type = this._cachedMeta.type;
3971
+ this.options = undefined;
3972
+ this._parsing = false;
3973
+ this._data = undefined;
3974
+ this._objectData = undefined;
3975
+ this._sharedOptions = undefined;
3976
+ this._drawStart = undefined;
3977
+ this._drawCount = undefined;
3978
+ this.enableOptionSharing = false;
3979
+ this.$context = undefined;
3980
+ this._syncList = [];
3981
+ this.initialize();
3982
+ }
3983
+ initialize() {
3984
+ const me = this;
3985
+ const meta = me._cachedMeta;
3986
+ me.configure();
3987
+ me.linkScales();
3988
+ meta._stacked = isStacked(meta.vScale, meta);
3989
+ me.addElements();
3990
+ }
3991
+ updateIndex(datasetIndex) {
3992
+ if (this.index !== datasetIndex) {
3993
+ clearStacks(this._cachedMeta);
3994
+ }
3995
+ this.index = datasetIndex;
3996
+ }
3997
+ linkScales() {
3998
+ const me = this;
3999
+ const chart = me.chart;
4000
+ const meta = me._cachedMeta;
4001
+ const dataset = me.getDataset();
4002
+ const chooseId = (axis, x, y, r) => axis === 'x' ? x : axis === 'r' ? r : y;
4003
+ const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));
4004
+ const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));
4005
+ const rid = meta.rAxisID = valueOrDefault(dataset.rAxisID, getFirstScaleId(chart, 'r'));
4006
+ const indexAxis = meta.indexAxis;
4007
+ const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);
4008
+ const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);
4009
+ meta.xScale = me.getScaleForId(xid);
4010
+ meta.yScale = me.getScaleForId(yid);
4011
+ meta.rScale = me.getScaleForId(rid);
4012
+ meta.iScale = me.getScaleForId(iid);
4013
+ meta.vScale = me.getScaleForId(vid);
4014
+ }
4015
+ getDataset() {
4016
+ return this.chart.data.datasets[this.index];
4017
+ }
4018
+ getMeta() {
4019
+ return this.chart.getDatasetMeta(this.index);
4020
+ }
4021
+ getScaleForId(scaleID) {
4022
+ return this.chart.scales[scaleID];
4023
+ }
4024
+ _getOtherScale(scale) {
4025
+ const meta = this._cachedMeta;
4026
+ return scale === meta.iScale
4027
+ ? meta.vScale
4028
+ : meta.iScale;
4029
+ }
4030
+ reset() {
4031
+ this._update('reset');
4032
+ }
4033
+ _destroy() {
4034
+ const meta = this._cachedMeta;
4035
+ if (this._data) {
4036
+ unlistenArrayEvents(this._data, this);
4037
+ }
4038
+ if (meta._stacked) {
4039
+ clearStacks(meta);
4040
+ }
4041
+ }
4042
+ _dataCheck() {
4043
+ const me = this;
4044
+ const dataset = me.getDataset();
4045
+ const data = dataset.data || (dataset.data = []);
4046
+ const _data = me._data;
4047
+ if (isObject(data)) {
4048
+ me._data = convertObjectDataToArray(data);
4049
+ } else if (_data !== data) {
4050
+ if (_data) {
4051
+ unlistenArrayEvents(_data, me);
4052
+ const meta = me._cachedMeta;
4053
+ clearStacks(meta);
4054
+ meta._parsed = [];
4055
+ }
4056
+ if (data && Object.isExtensible(data)) {
4057
+ listenArrayEvents(data, me);
4058
+ }
4059
+ me._syncList = [];
4060
+ me._data = data;
4061
+ }
4062
+ }
4063
+ addElements() {
4064
+ const me = this;
4065
+ const meta = me._cachedMeta;
4066
+ me._dataCheck();
4067
+ if (me.datasetElementType) {
4068
+ meta.dataset = new me.datasetElementType();
4069
+ }
4070
+ }
4071
+ buildOrUpdateElements(resetNewElements) {
4072
+ const me = this;
4073
+ const meta = me._cachedMeta;
4074
+ const dataset = me.getDataset();
4075
+ let stackChanged = false;
4076
+ me._dataCheck();
4077
+ const oldStacked = meta._stacked;
4078
+ meta._stacked = isStacked(meta.vScale, meta);
4079
+ if (meta.stack !== dataset.stack) {
4080
+ stackChanged = true;
4081
+ clearStacks(meta);
4082
+ meta.stack = dataset.stack;
4083
+ }
4084
+ me._resyncElements(resetNewElements);
4085
+ if (stackChanged || oldStacked !== meta._stacked) {
4086
+ updateStacks(me, meta._parsed);
4087
+ }
4088
+ }
4089
+ configure() {
4090
+ const me = this;
4091
+ const config = me.chart.config;
4092
+ const scopeKeys = config.datasetScopeKeys(me._type);
4093
+ const scopes = config.getOptionScopes(me.getDataset(), scopeKeys, true);
4094
+ me.options = config.createResolver(scopes, me.getContext());
4095
+ me._parsing = me.options.parsing;
4096
+ }
4097
+ parse(start, count) {
4098
+ const me = this;
4099
+ const {_cachedMeta: meta, _data: data} = me;
4100
+ const {iScale, _stacked} = meta;
4101
+ const iAxis = iScale.axis;
4102
+ let sorted = start === 0 && count === data.length ? true : meta._sorted;
4103
+ let prev = start > 0 && meta._parsed[start - 1];
4104
+ let i, cur, parsed;
4105
+ if (me._parsing === false) {
4106
+ meta._parsed = data;
4107
+ meta._sorted = true;
4108
+ parsed = data;
4109
+ } else {
4110
+ if (isArray(data[start])) {
4111
+ parsed = me.parseArrayData(meta, data, start, count);
4112
+ } else if (isObject(data[start])) {
4113
+ parsed = me.parseObjectData(meta, data, start, count);
4114
+ } else {
4115
+ parsed = me.parsePrimitiveData(meta, data, start, count);
4116
+ }
4117
+ const isNotInOrderComparedToPrev = () => cur[iAxis] === null || (prev && cur[iAxis] < prev[iAxis]);
4118
+ for (i = 0; i < count; ++i) {
4119
+ meta._parsed[i + start] = cur = parsed[i];
4120
+ if (sorted) {
4121
+ if (isNotInOrderComparedToPrev()) {
4122
+ sorted = false;
4123
+ }
4124
+ prev = cur;
4125
+ }
4126
+ }
4127
+ meta._sorted = sorted;
4128
+ }
4129
+ if (_stacked) {
4130
+ updateStacks(me, parsed);
4131
+ }
4132
+ }
4133
+ parsePrimitiveData(meta, data, start, count) {
4134
+ const {iScale, vScale} = meta;
4135
+ const iAxis = iScale.axis;
4136
+ const vAxis = vScale.axis;
4137
+ const labels = iScale.getLabels();
4138
+ const singleScale = iScale === vScale;
4139
+ const parsed = new Array(count);
4140
+ let i, ilen, index;
4141
+ for (i = 0, ilen = count; i < ilen; ++i) {
4142
+ index = i + start;
4143
+ parsed[i] = {
4144
+ [iAxis]: singleScale || iScale.parse(labels[index], index),
4145
+ [vAxis]: vScale.parse(data[index], index)
4146
+ };
4147
+ }
4148
+ return parsed;
4149
+ }
4150
+ parseArrayData(meta, data, start, count) {
4151
+ const {xScale, yScale} = meta;
4152
+ const parsed = new Array(count);
4153
+ let i, ilen, index, item;
4154
+ for (i = 0, ilen = count; i < ilen; ++i) {
4155
+ index = i + start;
4156
+ item = data[index];
4157
+ parsed[i] = {
4158
+ x: xScale.parse(item[0], index),
4159
+ y: yScale.parse(item[1], index)
4160
+ };
4161
+ }
4162
+ return parsed;
4163
+ }
4164
+ parseObjectData(meta, data, start, count) {
4165
+ const {xScale, yScale} = meta;
4166
+ const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing;
4167
+ const parsed = new Array(count);
4168
+ let i, ilen, index, item;
4169
+ for (i = 0, ilen = count; i < ilen; ++i) {
4170
+ index = i + start;
4171
+ item = data[index];
4172
+ parsed[i] = {
4173
+ x: xScale.parse(resolveObjectKey(item, xAxisKey), index),
4174
+ y: yScale.parse(resolveObjectKey(item, yAxisKey), index)
4175
+ };
4176
+ }
4177
+ return parsed;
4178
+ }
4179
+ getParsed(index) {
4180
+ return this._cachedMeta._parsed[index];
4181
+ }
4182
+ getDataElement(index) {
4183
+ return this._cachedMeta.data[index];
4184
+ }
4185
+ applyStack(scale, parsed, mode) {
4186
+ const chart = this.chart;
4187
+ const meta = this._cachedMeta;
4188
+ const value = parsed[scale.axis];
4189
+ const stack = {
4190
+ keys: getSortedDatasetIndices(chart, true),
4191
+ values: parsed._stacks[scale.axis]
4192
+ };
4193
+ return applyStack(stack, value, meta.index, {mode});
4194
+ }
4195
+ updateRangeFromParsed(range, scale, parsed, stack) {
4196
+ const parsedValue = parsed[scale.axis];
4197
+ let value = parsedValue === null ? NaN : parsedValue;
4198
+ const values = stack && parsed._stacks[scale.axis];
4199
+ if (stack && values) {
4200
+ stack.values = values;
4201
+ range.min = Math.min(range.min, value);
4202
+ range.max = Math.max(range.max, value);
4203
+ value = applyStack(stack, parsedValue, this._cachedMeta.index, {all: true});
4204
+ }
4205
+ range.min = Math.min(range.min, value);
4206
+ range.max = Math.max(range.max, value);
4207
+ }
4208
+ getMinMax(scale, canStack) {
4209
+ const me = this;
4210
+ const meta = me._cachedMeta;
4211
+ const _parsed = meta._parsed;
4212
+ const sorted = meta._sorted && scale === meta.iScale;
4213
+ const ilen = _parsed.length;
4214
+ const otherScale = me._getOtherScale(scale);
4215
+ const stack = canStack && meta._stacked && {keys: getSortedDatasetIndices(me.chart, true), values: null};
4216
+ const range = {min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY};
4217
+ const {min: otherMin, max: otherMax} = getUserBounds(otherScale);
4218
+ let i, value, parsed, otherValue;
4219
+ function _skip() {
4220
+ parsed = _parsed[i];
4221
+ value = parsed[scale.axis];
4222
+ otherValue = parsed[otherScale.axis];
4223
+ return !isNumberFinite(value) || otherMin > otherValue || otherMax < otherValue;
4224
+ }
4225
+ for (i = 0; i < ilen; ++i) {
4226
+ if (_skip()) {
4227
+ continue;
4228
+ }
4229
+ me.updateRangeFromParsed(range, scale, parsed, stack);
4230
+ if (sorted) {
4231
+ break;
4232
+ }
4233
+ }
4234
+ if (sorted) {
4235
+ for (i = ilen - 1; i >= 0; --i) {
4236
+ if (_skip()) {
4237
+ continue;
4238
+ }
4239
+ me.updateRangeFromParsed(range, scale, parsed, stack);
4240
+ break;
4241
+ }
4242
+ }
4243
+ return range;
4244
+ }
4245
+ getAllParsedValues(scale) {
4246
+ const parsed = this._cachedMeta._parsed;
4247
+ const values = [];
4248
+ let i, ilen, value;
4249
+ for (i = 0, ilen = parsed.length; i < ilen; ++i) {
4250
+ value = parsed[i][scale.axis];
4251
+ if (isNumberFinite(value)) {
4252
+ values.push(value);
4253
+ }
4254
+ }
4255
+ return values;
4256
+ }
4257
+ getMaxOverflow() {
4258
+ return false;
4259
+ }
4260
+ getLabelAndValue(index) {
4261
+ const me = this;
4262
+ const meta = me._cachedMeta;
4263
+ const iScale = meta.iScale;
4264
+ const vScale = meta.vScale;
4265
+ const parsed = me.getParsed(index);
4266
+ return {
4267
+ label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',
4268
+ value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''
4269
+ };
4270
+ }
4271
+ _update(mode) {
4272
+ const me = this;
4273
+ const meta = me._cachedMeta;
4274
+ me.configure();
4275
+ me._cachedDataOpts = {};
4276
+ me.update(mode || 'default');
4277
+ meta._clip = toClip(valueOrDefault(me.options.clip, defaultClip(meta.xScale, meta.yScale, me.getMaxOverflow())));
4278
+ }
4279
+ update(mode) {}
4280
+ draw() {
4281
+ const me = this;
4282
+ const ctx = me._ctx;
4283
+ const chart = me.chart;
4284
+ const meta = me._cachedMeta;
4285
+ const elements = meta.data || [];
4286
+ const area = chart.chartArea;
4287
+ const active = [];
4288
+ const start = me._drawStart || 0;
4289
+ const count = me._drawCount || (elements.length - start);
4290
+ let i;
4291
+ if (meta.dataset) {
4292
+ meta.dataset.draw(ctx, area, start, count);
4293
+ }
4294
+ for (i = start; i < start + count; ++i) {
4295
+ const element = elements[i];
4296
+ if (element.hidden) {
4297
+ continue;
4298
+ }
4299
+ if (element.active) {
4300
+ active.push(element);
4301
+ } else {
4302
+ element.draw(ctx, area);
4303
+ }
4304
+ }
4305
+ for (i = 0; i < active.length; ++i) {
4306
+ active[i].draw(ctx, area);
4307
+ }
4308
+ }
4309
+ getStyle(index, active) {
4310
+ const mode = active ? 'active' : 'default';
4311
+ return index === undefined && this._cachedMeta.dataset
4312
+ ? this.resolveDatasetElementOptions(mode)
4313
+ : this.resolveDataElementOptions(index || 0, mode);
4314
+ }
4315
+ getContext(index, active, mode) {
4316
+ const me = this;
4317
+ const dataset = me.getDataset();
4318
+ let context;
4319
+ if (index >= 0 && index < me._cachedMeta.data.length) {
4320
+ const element = me._cachedMeta.data[index];
4321
+ context = element.$context ||
4322
+ (element.$context = createDataContext(me.getContext(), index, element));
4323
+ context.parsed = me.getParsed(index);
4324
+ context.raw = dataset.data[index];
4325
+ context.index = context.dataIndex = index;
4326
+ } else {
4327
+ context = me.$context ||
4328
+ (me.$context = createDatasetContext(me.chart.getContext(), me.index));
4329
+ context.dataset = dataset;
4330
+ context.index = context.datasetIndex = me.index;
4331
+ }
4332
+ context.active = !!active;
4333
+ context.mode = mode;
4334
+ return context;
4335
+ }
4336
+ resolveDatasetElementOptions(mode) {
4337
+ return this._resolveElementOptions(this.datasetElementType.id, mode);
4338
+ }
4339
+ resolveDataElementOptions(index, mode) {
4340
+ return this._resolveElementOptions(this.dataElementType.id, mode, index);
4341
+ }
4342
+ _resolveElementOptions(elementType, mode = 'default', index) {
4343
+ const me = this;
4344
+ const active = mode === 'active';
4345
+ const cache = me._cachedDataOpts;
4346
+ const cacheKey = elementType + '-' + mode;
4347
+ const cached = cache[cacheKey];
4348
+ const sharing = me.enableOptionSharing && defined(index);
4349
+ if (cached) {
4350
+ return cloneIfNotShared(cached, sharing);
4351
+ }
4352
+ const config = me.chart.config;
4353
+ const scopeKeys = config.datasetElementScopeKeys(me._type, elementType);
4354
+ const prefixes = active ? [`${elementType}Hover`, 'hover', elementType, ''] : [elementType, ''];
4355
+ const scopes = config.getOptionScopes(me.getDataset(), scopeKeys);
4356
+ const names = Object.keys(defaults.elements[elementType]);
4357
+ const context = () => me.getContext(index, active);
4358
+ const values = config.resolveNamedOptions(scopes, names, context, prefixes);
4359
+ if (values.$shared) {
4360
+ values.$shared = sharing;
4361
+ cache[cacheKey] = Object.freeze(cloneIfNotShared(values, sharing));
4362
+ }
4363
+ return values;
4364
+ }
4365
+ _resolveAnimations(index, transition, active) {
4366
+ const me = this;
4367
+ const chart = me.chart;
4368
+ const cache = me._cachedDataOpts;
4369
+ const cacheKey = `animation-${transition}`;
4370
+ const cached = cache[cacheKey];
4371
+ if (cached) {
4372
+ return cached;
4373
+ }
4374
+ let options;
4375
+ if (chart.options.animation !== false) {
4376
+ const config = me.chart.config;
4377
+ const scopeKeys = config.datasetAnimationScopeKeys(me._type, transition);
4378
+ const scopes = config.getOptionScopes(me.getDataset(), scopeKeys);
4379
+ options = config.createResolver(scopes, me.getContext(index, active, transition));
4380
+ }
4381
+ const animations = new Animations(chart, options && options.animations);
4382
+ if (options && options._cacheable) {
4383
+ cache[cacheKey] = Object.freeze(animations);
4384
+ }
4385
+ return animations;
4386
+ }
4387
+ getSharedOptions(options) {
4388
+ if (!options.$shared) {
4389
+ return;
4390
+ }
4391
+ return this._sharedOptions || (this._sharedOptions = Object.assign({}, options));
4392
+ }
4393
+ includeOptions(mode, sharedOptions) {
4394
+ return !sharedOptions || isDirectUpdateMode(mode) || this.chart._animationsDisabled;
4395
+ }
4396
+ updateElement(element, index, properties, mode) {
4397
+ if (isDirectUpdateMode(mode)) {
4398
+ Object.assign(element, properties);
4399
+ } else {
4400
+ this._resolveAnimations(index, mode).update(element, properties);
4401
+ }
4402
+ }
4403
+ updateSharedOptions(sharedOptions, mode, newOptions) {
4404
+ if (sharedOptions && !isDirectUpdateMode(mode)) {
4405
+ this._resolveAnimations(undefined, mode).update(sharedOptions, newOptions);
4406
+ }
4407
+ }
4408
+ _setStyle(element, index, mode, active) {
4409
+ element.active = active;
4410
+ const options = this.getStyle(index, active);
4411
+ this._resolveAnimations(index, mode, active).update(element, {
4412
+ options: (!active && this.getSharedOptions(options)) || options
4413
+ });
4414
+ }
4415
+ removeHoverStyle(element, datasetIndex, index) {
4416
+ this._setStyle(element, index, 'active', false);
4417
+ }
4418
+ setHoverStyle(element, datasetIndex, index) {
4419
+ this._setStyle(element, index, 'active', true);
4420
+ }
4421
+ _removeDatasetHoverStyle() {
4422
+ const element = this._cachedMeta.dataset;
4423
+ if (element) {
4424
+ this._setStyle(element, undefined, 'active', false);
4425
+ }
4426
+ }
4427
+ _setDatasetHoverStyle() {
4428
+ const element = this._cachedMeta.dataset;
4429
+ if (element) {
4430
+ this._setStyle(element, undefined, 'active', true);
4431
+ }
4432
+ }
4433
+ _resyncElements(resetNewElements) {
4434
+ const me = this;
4435
+ const data = me._data;
4436
+ const elements = me._cachedMeta.data;
4437
+ for (const [method, arg1, arg2] of me._syncList) {
4438
+ me[method](arg1, arg2);
4439
+ }
4440
+ me._syncList = [];
4441
+ const numMeta = elements.length;
4442
+ const numData = data.length;
4443
+ const count = Math.min(numData, numMeta);
4444
+ if (count) {
4445
+ me.parse(0, count);
4446
+ }
4447
+ if (numData > numMeta) {
4448
+ me._insertElements(numMeta, numData - numMeta, resetNewElements);
4449
+ } else if (numData < numMeta) {
4450
+ me._removeElements(numData, numMeta - numData);
4451
+ }
4452
+ }
4453
+ _insertElements(start, count, resetNewElements = true) {
4454
+ const me = this;
4455
+ const meta = me._cachedMeta;
4456
+ const data = meta.data;
4457
+ const end = start + count;
4458
+ let i;
4459
+ const move = (arr) => {
4460
+ arr.length += count;
4461
+ for (i = arr.length - 1; i >= end; i--) {
4462
+ arr[i] = arr[i - count];
4463
+ }
4464
+ };
4465
+ move(data);
4466
+ for (i = start; i < end; ++i) {
4467
+ data[i] = new me.dataElementType();
4468
+ }
4469
+ if (me._parsing) {
4470
+ move(meta._parsed);
4471
+ }
4472
+ me.parse(start, count);
4473
+ if (resetNewElements) {
4474
+ me.updateElements(data, start, count, 'reset');
4475
+ }
4476
+ }
4477
+ updateElements(element, start, count, mode) {}
4478
+ _removeElements(start, count) {
4479
+ const me = this;
4480
+ const meta = me._cachedMeta;
4481
+ if (me._parsing) {
4482
+ const removed = meta._parsed.splice(start, count);
4483
+ if (meta._stacked) {
4484
+ clearStacks(meta, removed);
4485
+ }
4486
+ }
4487
+ meta.data.splice(start, count);
4488
+ }
4489
+ _sync(args) {
4490
+ if (this._parsing) {
4491
+ this._syncList.push(args);
4492
+ } else {
4493
+ const [method, arg1, arg2] = args;
4494
+ this[method](arg1, arg2);
4495
+ }
4496
+ }
4497
+ _onDataPush() {
4498
+ const count = arguments.length;
4499
+ this._sync(['_insertElements', this.getDataset().data.length - count, count]);
4500
+ }
4501
+ _onDataPop() {
4502
+ this._sync(['_removeElements', this._cachedMeta.data.length - 1, 1]);
4503
+ }
4504
+ _onDataShift() {
4505
+ this._sync(['_removeElements', 0, 1]);
4506
+ }
4507
+ _onDataSplice(start, count) {
4508
+ this._sync(['_removeElements', start, count]);
4509
+ this._sync(['_insertElements', start, arguments.length - 2]);
4510
+ }
4511
+ _onDataUnshift() {
4512
+ this._sync(['_insertElements', 0, arguments.length]);
4513
+ }
4514
+ }
4515
+ DatasetController.defaults = {};
4516
+ DatasetController.prototype.datasetElementType = null;
4517
+ DatasetController.prototype.dataElementType = null;
4518
+
4519
+ class Element {
4520
+ constructor() {
4521
+ this.x = undefined;
4522
+ this.y = undefined;
4523
+ this.active = false;
4524
+ this.options = undefined;
4525
+ this.$animations = undefined;
4526
+ }
4527
+ tooltipPosition(useFinalPosition) {
4528
+ const {x, y} = this.getProps(['x', 'y'], useFinalPosition);
4529
+ return {x, y};
4530
+ }
4531
+ hasValue() {
4532
+ return isNumber(this.x) && isNumber(this.y);
4533
+ }
4534
+ getProps(props, final) {
4535
+ const me = this;
4536
+ const anims = this.$animations;
4537
+ if (!final || !anims) {
4538
+ return me;
4539
+ }
4540
+ const ret = {};
4541
+ props.forEach(prop => {
4542
+ ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : me[prop];
4543
+ });
4544
+ return ret;
4545
+ }
4546
+ }
4547
+ Element.defaults = {};
4548
+ Element.defaultRoutes = undefined;
4549
+
4550
+ const formatters = {
4551
+ values(value) {
4552
+ return isArray(value) ? value : '' + value;
4553
+ },
4554
+ numeric(tickValue, index, ticks) {
4555
+ if (tickValue === 0) {
4556
+ return '0';
4557
+ }
4558
+ const locale = this.chart.options.locale;
4559
+ let notation;
4560
+ let delta = tickValue;
4561
+ if (ticks.length > 1) {
4562
+ const maxTick = Math.max(Math.abs(ticks[0].value), Math.abs(ticks[ticks.length - 1].value));
4563
+ if (maxTick < 1e-4 || maxTick > 1e+15) {
4564
+ notation = 'scientific';
4565
+ }
4566
+ delta = calculateDelta(tickValue, ticks);
4567
+ }
4568
+ const logDelta = log10(Math.abs(delta));
4569
+ const numDecimal = Math.max(Math.min(-1 * Math.floor(logDelta), 20), 0);
4570
+ const options = {notation, minimumFractionDigits: numDecimal, maximumFractionDigits: numDecimal};
4571
+ Object.assign(options, this.options.ticks.format);
4572
+ return formatNumber(tickValue, locale, options);
4573
+ },
4574
+ logarithmic(tickValue, index, ticks) {
4575
+ if (tickValue === 0) {
4576
+ return '0';
4577
+ }
4578
+ const remain = tickValue / (Math.pow(10, Math.floor(log10(tickValue))));
4579
+ if (remain === 1 || remain === 2 || remain === 5) {
4580
+ return formatters.numeric.call(this, tickValue, index, ticks);
4581
+ }
4582
+ return '';
4583
+ }
4584
+ };
4585
+ function calculateDelta(tickValue, ticks) {
4586
+ let delta = ticks.length > 3 ? ticks[2].value - ticks[1].value : ticks[1].value - ticks[0].value;
4587
+ if (Math.abs(delta) >= 1 && tickValue !== Math.floor(tickValue)) {
4588
+ delta = tickValue - Math.floor(tickValue);
4589
+ }
4590
+ return delta;
4591
+ }
4592
+ var Ticks = {formatters};
4593
+
4594
+ defaults.set('scale', {
4595
+ display: true,
4596
+ offset: false,
4597
+ reverse: false,
4598
+ beginAtZero: false,
4599
+ bounds: 'ticks',
4600
+ grace: 0,
4601
+ grid: {
4602
+ display: true,
4603
+ lineWidth: 1,
4604
+ drawBorder: true,
4605
+ drawOnChartArea: true,
4606
+ drawTicks: true,
4607
+ tickLength: 8,
4608
+ tickWidth: (_ctx, options) => options.lineWidth,
4609
+ tickColor: (_ctx, options) => options.color,
4610
+ offset: false,
4611
+ borderDash: [],
4612
+ borderDashOffset: 0.0,
4613
+ borderWidth: 1
4614
+ },
4615
+ title: {
4616
+ display: false,
4617
+ text: '',
4618
+ padding: {
4619
+ top: 4,
4620
+ bottom: 4
4621
+ }
4622
+ },
4623
+ ticks: {
4624
+ minRotation: 0,
4625
+ maxRotation: 50,
4626
+ mirror: false,
4627
+ textStrokeWidth: 0,
4628
+ textStrokeColor: '',
4629
+ padding: 3,
4630
+ display: true,
4631
+ autoSkip: true,
4632
+ autoSkipPadding: 3,
4633
+ labelOffset: 0,
4634
+ callback: Ticks.formatters.values,
4635
+ minor: {},
4636
+ major: {},
4637
+ align: 'center',
4638
+ crossAlign: 'near',
4639
+ showLabelBackdrop: false,
4640
+ backdropColor: 'rgba(255, 255, 255, 0.75)',
4641
+ backdropPadding: 2,
4642
+ }
4643
+ });
4644
+ defaults.route('scale.ticks', 'color', '', 'color');
4645
+ defaults.route('scale.grid', 'color', '', 'borderColor');
4646
+ defaults.route('scale.grid', 'borderColor', '', 'borderColor');
4647
+ defaults.route('scale.title', 'color', '', 'color');
4648
+ defaults.describe('scale', {
4649
+ _fallback: false,
4650
+ _scriptable: (name) => !name.startsWith('before') && !name.startsWith('after') && name !== 'callback' && name !== 'parser',
4651
+ _indexable: (name) => name !== 'borderDash' && name !== 'tickBorderDash',
4652
+ });
4653
+ defaults.describe('scales', {
4654
+ _fallback: 'scale',
4655
+ });
4656
+ defaults.describe('scale.ticks', {
4657
+ _scriptable: (name) => name !== 'backdropPadding' && name !== 'callback',
4658
+ _indexable: (name) => name !== 'backdropPadding',
4659
+ });
4660
+
4661
+ function autoSkip(scale, ticks) {
4662
+ const tickOpts = scale.options.ticks;
4663
+ const ticksLimit = tickOpts.maxTicksLimit || determineMaxTicks(scale);
4664
+ const majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
4665
+ const numMajorIndices = majorIndices.length;
4666
+ const first = majorIndices[0];
4667
+ const last = majorIndices[numMajorIndices - 1];
4668
+ const newTicks = [];
4669
+ if (numMajorIndices > ticksLimit) {
4670
+ skipMajors(ticks, newTicks, majorIndices, numMajorIndices / ticksLimit);
4671
+ return newTicks;
4672
+ }
4673
+ const spacing = calculateSpacing(majorIndices, ticks, ticksLimit);
4674
+ if (numMajorIndices > 0) {
4675
+ let i, ilen;
4676
+ const avgMajorSpacing = numMajorIndices > 1 ? Math.round((last - first) / (numMajorIndices - 1)) : null;
4677
+ skip(ticks, newTicks, spacing, isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
4678
+ for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
4679
+ skip(ticks, newTicks, spacing, majorIndices[i], majorIndices[i + 1]);
4680
+ }
4681
+ skip(ticks, newTicks, spacing, last, isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
4682
+ return newTicks;
4683
+ }
4684
+ skip(ticks, newTicks, spacing);
4685
+ return newTicks;
4686
+ }
4687
+ function determineMaxTicks(scale) {
4688
+ const offset = scale.options.offset;
4689
+ const tickLength = scale._tickSize();
4690
+ const maxScale = scale._length / tickLength + (offset ? 0 : 1);
4691
+ const maxChart = scale._maxLength / tickLength;
4692
+ return Math.floor(Math.min(maxScale, maxChart));
4693
+ }
4694
+ function calculateSpacing(majorIndices, ticks, ticksLimit) {
4695
+ const evenMajorSpacing = getEvenSpacing(majorIndices);
4696
+ const spacing = ticks.length / ticksLimit;
4697
+ if (!evenMajorSpacing) {
4698
+ return Math.max(spacing, 1);
4699
+ }
4700
+ const factors = _factorize(evenMajorSpacing);
4701
+ for (let i = 0, ilen = factors.length - 1; i < ilen; i++) {
4702
+ const factor = factors[i];
4703
+ if (factor > spacing) {
4704
+ return factor;
4705
+ }
4706
+ }
4707
+ return Math.max(spacing, 1);
4708
+ }
4709
+ function getMajorIndices(ticks) {
4710
+ const result = [];
4711
+ let i, ilen;
4712
+ for (i = 0, ilen = ticks.length; i < ilen; i++) {
4713
+ if (ticks[i].major) {
4714
+ result.push(i);
4715
+ }
4716
+ }
4717
+ return result;
4718
+ }
4719
+ function skipMajors(ticks, newTicks, majorIndices, spacing) {
4720
+ let count = 0;
4721
+ let next = majorIndices[0];
4722
+ let i;
4723
+ spacing = Math.ceil(spacing);
4724
+ for (i = 0; i < ticks.length; i++) {
4725
+ if (i === next) {
4726
+ newTicks.push(ticks[i]);
4727
+ count++;
4728
+ next = majorIndices[count * spacing];
4729
+ }
4730
+ }
4731
+ }
4732
+ function skip(ticks, newTicks, spacing, majorStart, majorEnd) {
4733
+ const start = valueOrDefault(majorStart, 0);
4734
+ const end = Math.min(valueOrDefault(majorEnd, ticks.length), ticks.length);
4735
+ let count = 0;
4736
+ let length, i, next;
4737
+ spacing = Math.ceil(spacing);
4738
+ if (majorEnd) {
4739
+ length = majorEnd - majorStart;
4740
+ spacing = length / Math.floor(length / spacing);
4741
+ }
4742
+ next = start;
4743
+ while (next < 0) {
4744
+ count++;
4745
+ next = Math.round(start + count * spacing);
4746
+ }
4747
+ for (i = Math.max(start, 0); i < end; i++) {
4748
+ if (i === next) {
4749
+ newTicks.push(ticks[i]);
4750
+ count++;
4751
+ next = Math.round(start + count * spacing);
4752
+ }
4753
+ }
4754
+ }
4755
+ function getEvenSpacing(arr) {
4756
+ const len = arr.length;
4757
+ let i, diff;
4758
+ if (len < 2) {
4759
+ return false;
4760
+ }
4761
+ for (diff = arr[0], i = 1; i < len; ++i) {
4762
+ if (arr[i] - arr[i - 1] !== diff) {
4763
+ return false;
4764
+ }
4765
+ }
4766
+ return diff;
4767
+ }
4768
+
4769
+ const reverseAlign = (align) => align === 'left' ? 'right' : align === 'right' ? 'left' : align;
4770
+ const offsetFromEdge = (scale, edge, offset) => edge === 'top' || edge === 'left' ? scale[edge] + offset : scale[edge] - offset;
4771
+ function sample(arr, numItems) {
4772
+ const result = [];
4773
+ const increment = arr.length / numItems;
4774
+ const len = arr.length;
4775
+ let i = 0;
4776
+ for (; i < len; i += increment) {
4777
+ result.push(arr[Math.floor(i)]);
4778
+ }
4779
+ return result;
4780
+ }
4781
+ function getPixelForGridLine(scale, index, offsetGridLines) {
4782
+ const length = scale.ticks.length;
4783
+ const validIndex = Math.min(index, length - 1);
4784
+ const start = scale._startPixel;
4785
+ const end = scale._endPixel;
4786
+ const epsilon = 1e-6;
4787
+ let lineValue = scale.getPixelForTick(validIndex);
4788
+ let offset;
4789
+ if (offsetGridLines) {
4790
+ if (length === 1) {
4791
+ offset = Math.max(lineValue - start, end - lineValue);
4792
+ } else if (index === 0) {
4793
+ offset = (scale.getPixelForTick(1) - lineValue) / 2;
4794
+ } else {
4795
+ offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;
4796
+ }
4797
+ lineValue += validIndex < index ? offset : -offset;
4798
+ if (lineValue < start - epsilon || lineValue > end + epsilon) {
4799
+ return;
4800
+ }
4801
+ }
4802
+ return lineValue;
4803
+ }
4804
+ function garbageCollect(caches, length) {
4805
+ each(caches, (cache) => {
4806
+ const gc = cache.gc;
4807
+ const gcLen = gc.length / 2;
4808
+ let i;
4809
+ if (gcLen > length) {
4810
+ for (i = 0; i < gcLen; ++i) {
4811
+ delete cache.data[gc[i]];
4812
+ }
4813
+ gc.splice(0, gcLen);
4814
+ }
4815
+ });
4816
+ }
4817
+ function getTickMarkLength(options) {
4818
+ return options.drawTicks ? options.tickLength : 0;
4819
+ }
4820
+ function getTitleHeight(options, fallback) {
4821
+ if (!options.display) {
4822
+ return 0;
4823
+ }
4824
+ const font = toFont(options.font, fallback);
4825
+ const padding = toPadding(options.padding);
4826
+ const lines = isArray(options.text) ? options.text.length : 1;
4827
+ return (lines * font.lineHeight) + padding.height;
4828
+ }
4829
+ function createScaleContext(parent, scale) {
4830
+ return Object.assign(Object.create(parent), {
4831
+ scale,
4832
+ type: 'scale'
4833
+ });
4834
+ }
4835
+ function createTickContext(parent, index, tick) {
4836
+ return Object.assign(Object.create(parent), {
4837
+ tick,
4838
+ index,
4839
+ type: 'tick'
4840
+ });
4841
+ }
4842
+ function titleAlign(align, position, reverse) {
4843
+ let ret = _toLeftRightCenter(align);
4844
+ if ((reverse && position !== 'right') || (!reverse && position === 'right')) {
4845
+ ret = reverseAlign(ret);
4846
+ }
4847
+ return ret;
4848
+ }
4849
+ function titleArgs(scale, offset, position, align) {
4850
+ const {top, left, bottom, right, chart} = scale;
4851
+ const {chartArea, scales} = chart;
4852
+ let rotation = 0;
4853
+ let maxWidth, titleX, titleY;
4854
+ const height = bottom - top;
4855
+ const width = right - left;
4856
+ if (scale.isHorizontal()) {
4857
+ titleX = _alignStartEnd(align, left, right);
4858
+ if (isObject(position)) {
4859
+ const positionAxisID = Object.keys(position)[0];
4860
+ const value = position[positionAxisID];
4861
+ titleY = scales[positionAxisID].getPixelForValue(value) + height - offset;
4862
+ } else if (position === 'center') {
4863
+ titleY = (chartArea.bottom + chartArea.top) / 2 + height - offset;
4864
+ } else {
4865
+ titleY = offsetFromEdge(scale, position, offset);
4866
+ }
4867
+ maxWidth = right - left;
4868
+ } else {
4869
+ if (isObject(position)) {
4870
+ const positionAxisID = Object.keys(position)[0];
4871
+ const value = position[positionAxisID];
4872
+ titleX = scales[positionAxisID].getPixelForValue(value) - width + offset;
4873
+ } else if (position === 'center') {
4874
+ titleX = (chartArea.left + chartArea.right) / 2 - width + offset;
4875
+ } else {
4876
+ titleX = offsetFromEdge(scale, position, offset);
4877
+ }
4878
+ titleY = _alignStartEnd(align, bottom, top);
4879
+ rotation = position === 'left' ? -HALF_PI : HALF_PI;
4880
+ }
4881
+ return {titleX, titleY, maxWidth, rotation};
4882
+ }
4883
+ class Scale extends Element {
4884
+ constructor(cfg) {
4885
+ super();
4886
+ this.id = cfg.id;
4887
+ this.type = cfg.type;
4888
+ this.options = undefined;
4889
+ this.ctx = cfg.ctx;
4890
+ this.chart = cfg.chart;
4891
+ this.top = undefined;
4892
+ this.bottom = undefined;
4893
+ this.left = undefined;
4894
+ this.right = undefined;
4895
+ this.width = undefined;
4896
+ this.height = undefined;
4897
+ this._margins = {
4898
+ left: 0,
4899
+ right: 0,
4900
+ top: 0,
4901
+ bottom: 0
4902
+ };
4903
+ this.maxWidth = undefined;
4904
+ this.maxHeight = undefined;
4905
+ this.paddingTop = undefined;
4906
+ this.paddingBottom = undefined;
4907
+ this.paddingLeft = undefined;
4908
+ this.paddingRight = undefined;
4909
+ this.axis = undefined;
4910
+ this.labelRotation = undefined;
4911
+ this.min = undefined;
4912
+ this.max = undefined;
4913
+ this._range = undefined;
4914
+ this.ticks = [];
4915
+ this._gridLineItems = null;
4916
+ this._labelItems = null;
4917
+ this._labelSizes = null;
4918
+ this._length = 0;
4919
+ this._maxLength = 0;
4920
+ this._longestTextCache = {};
4921
+ this._startPixel = undefined;
4922
+ this._endPixel = undefined;
4923
+ this._reversePixels = false;
4924
+ this._userMax = undefined;
4925
+ this._userMin = undefined;
4926
+ this._suggestedMax = undefined;
4927
+ this._suggestedMin = undefined;
4928
+ this._ticksLength = 0;
4929
+ this._borderValue = 0;
4930
+ this._cache = {};
4931
+ this._dataLimitsCached = false;
4932
+ this.$context = undefined;
4933
+ }
4934
+ init(options) {
4935
+ const me = this;
4936
+ me.options = options.setContext(me.getContext());
4937
+ me.axis = options.axis;
4938
+ me._userMin = me.parse(options.min);
4939
+ me._userMax = me.parse(options.max);
4940
+ me._suggestedMin = me.parse(options.suggestedMin);
4941
+ me._suggestedMax = me.parse(options.suggestedMax);
4942
+ }
4943
+ parse(raw, index) {
4944
+ return raw;
4945
+ }
4946
+ getUserBounds() {
4947
+ let {_userMin, _userMax, _suggestedMin, _suggestedMax} = this;
4948
+ _userMin = finiteOrDefault(_userMin, Number.POSITIVE_INFINITY);
4949
+ _userMax = finiteOrDefault(_userMax, Number.NEGATIVE_INFINITY);
4950
+ _suggestedMin = finiteOrDefault(_suggestedMin, Number.POSITIVE_INFINITY);
4951
+ _suggestedMax = finiteOrDefault(_suggestedMax, Number.NEGATIVE_INFINITY);
4952
+ return {
4953
+ min: finiteOrDefault(_userMin, _suggestedMin),
4954
+ max: finiteOrDefault(_userMax, _suggestedMax),
4955
+ minDefined: isNumberFinite(_userMin),
4956
+ maxDefined: isNumberFinite(_userMax)
4957
+ };
4958
+ }
4959
+ getMinMax(canStack) {
4960
+ const me = this;
4961
+ let {min, max, minDefined, maxDefined} = me.getUserBounds();
4962
+ let range;
4963
+ if (minDefined && maxDefined) {
4964
+ return {min, max};
4965
+ }
4966
+ const metas = me.getMatchingVisibleMetas();
4967
+ for (let i = 0, ilen = metas.length; i < ilen; ++i) {
4968
+ range = metas[i].controller.getMinMax(me, canStack);
4969
+ if (!minDefined) {
4970
+ min = Math.min(min, range.min);
4971
+ }
4972
+ if (!maxDefined) {
4973
+ max = Math.max(max, range.max);
4974
+ }
4975
+ }
4976
+ return {
4977
+ min: finiteOrDefault(min, finiteOrDefault(max, min)),
4978
+ max: finiteOrDefault(max, finiteOrDefault(min, max))
4979
+ };
4980
+ }
4981
+ getPadding() {
4982
+ const me = this;
4983
+ return {
4984
+ left: me.paddingLeft || 0,
4985
+ top: me.paddingTop || 0,
4986
+ right: me.paddingRight || 0,
4987
+ bottom: me.paddingBottom || 0
4988
+ };
4989
+ }
4990
+ getTicks() {
4991
+ return this.ticks;
4992
+ }
4993
+ getLabels() {
4994
+ const data = this.chart.data;
4995
+ return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];
4996
+ }
4997
+ beforeLayout() {
4998
+ this._cache = {};
4999
+ this._dataLimitsCached = false;
5000
+ }
5001
+ beforeUpdate() {
5002
+ callback(this.options.beforeUpdate, [this]);
5003
+ }
5004
+ update(maxWidth, maxHeight, margins) {
5005
+ const me = this;
5006
+ const tickOpts = me.options.ticks;
5007
+ const sampleSize = tickOpts.sampleSize;
5008
+ me.beforeUpdate();
5009
+ me.maxWidth = maxWidth;
5010
+ me.maxHeight = maxHeight;
5011
+ me._margins = margins = Object.assign({
5012
+ left: 0,
5013
+ right: 0,
5014
+ top: 0,
5015
+ bottom: 0
5016
+ }, margins);
5017
+ me.ticks = null;
5018
+ me._labelSizes = null;
5019
+ me._gridLineItems = null;
5020
+ me._labelItems = null;
5021
+ me.beforeSetDimensions();
5022
+ me.setDimensions();
5023
+ me.afterSetDimensions();
5024
+ me._maxLength = me.isHorizontal()
5025
+ ? me.width + margins.left + margins.right
5026
+ : me.height + margins.top + margins.bottom;
5027
+ if (!me._dataLimitsCached) {
5028
+ me.beforeDataLimits();
5029
+ me.determineDataLimits();
5030
+ me.afterDataLimits();
5031
+ me._range = _addGrace(me, me.options.grace);
5032
+ me._dataLimitsCached = true;
5033
+ }
5034
+ me.beforeBuildTicks();
5035
+ me.ticks = me.buildTicks() || [];
5036
+ me.afterBuildTicks();
5037
+ const samplingEnabled = sampleSize < me.ticks.length;
5038
+ me._convertTicksToLabels(samplingEnabled ? sample(me.ticks, sampleSize) : me.ticks);
5039
+ me.configure();
5040
+ me.beforeCalculateLabelRotation();
5041
+ me.calculateLabelRotation();
5042
+ me.afterCalculateLabelRotation();
5043
+ if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {
5044
+ me.ticks = autoSkip(me, me.ticks);
5045
+ me._labelSizes = null;
5046
+ }
5047
+ if (samplingEnabled) {
5048
+ me._convertTicksToLabels(me.ticks);
5049
+ }
5050
+ me.beforeFit();
5051
+ me.fit();
5052
+ me.afterFit();
5053
+ me.afterUpdate();
5054
+ }
5055
+ configure() {
5056
+ const me = this;
5057
+ let reversePixels = me.options.reverse;
5058
+ let startPixel, endPixel;
5059
+ if (me.isHorizontal()) {
5060
+ startPixel = me.left;
5061
+ endPixel = me.right;
5062
+ } else {
5063
+ startPixel = me.top;
5064
+ endPixel = me.bottom;
5065
+ reversePixels = !reversePixels;
5066
+ }
5067
+ me._startPixel = startPixel;
5068
+ me._endPixel = endPixel;
5069
+ me._reversePixels = reversePixels;
5070
+ me._length = endPixel - startPixel;
5071
+ me._alignToPixels = me.options.alignToPixels;
5072
+ }
5073
+ afterUpdate() {
5074
+ callback(this.options.afterUpdate, [this]);
5075
+ }
5076
+ beforeSetDimensions() {
5077
+ callback(this.options.beforeSetDimensions, [this]);
5078
+ }
5079
+ setDimensions() {
5080
+ const me = this;
5081
+ if (me.isHorizontal()) {
5082
+ me.width = me.maxWidth;
5083
+ me.left = 0;
5084
+ me.right = me.width;
5085
+ } else {
5086
+ me.height = me.maxHeight;
5087
+ me.top = 0;
5088
+ me.bottom = me.height;
5089
+ }
5090
+ me.paddingLeft = 0;
5091
+ me.paddingTop = 0;
5092
+ me.paddingRight = 0;
5093
+ me.paddingBottom = 0;
5094
+ }
5095
+ afterSetDimensions() {
5096
+ callback(this.options.afterSetDimensions, [this]);
5097
+ }
5098
+ _callHooks(name) {
5099
+ const me = this;
5100
+ me.chart.notifyPlugins(name, me.getContext());
5101
+ callback(me.options[name], [me]);
5102
+ }
5103
+ beforeDataLimits() {
5104
+ this._callHooks('beforeDataLimits');
5105
+ }
5106
+ determineDataLimits() {}
5107
+ afterDataLimits() {
5108
+ this._callHooks('afterDataLimits');
5109
+ }
5110
+ beforeBuildTicks() {
5111
+ this._callHooks('beforeBuildTicks');
5112
+ }
5113
+ buildTicks() {
5114
+ return [];
5115
+ }
5116
+ afterBuildTicks() {
5117
+ this._callHooks('afterBuildTicks');
5118
+ }
5119
+ beforeTickToLabelConversion() {
5120
+ callback(this.options.beforeTickToLabelConversion, [this]);
5121
+ }
5122
+ generateTickLabels(ticks) {
5123
+ const me = this;
5124
+ const tickOpts = me.options.ticks;
5125
+ let i, ilen, tick;
5126
+ for (i = 0, ilen = ticks.length; i < ilen; i++) {
5127
+ tick = ticks[i];
5128
+ tick.label = callback(tickOpts.callback, [tick.value, i, ticks], me);
5129
+ }
5130
+ }
5131
+ afterTickToLabelConversion() {
5132
+ callback(this.options.afterTickToLabelConversion, [this]);
5133
+ }
5134
+ beforeCalculateLabelRotation() {
5135
+ callback(this.options.beforeCalculateLabelRotation, [this]);
5136
+ }
5137
+ calculateLabelRotation() {
5138
+ const me = this;
5139
+ const options = me.options;
5140
+ const tickOpts = options.ticks;
5141
+ const numTicks = me.ticks.length;
5142
+ const minRotation = tickOpts.minRotation || 0;
5143
+ const maxRotation = tickOpts.maxRotation;
5144
+ let labelRotation = minRotation;
5145
+ let tickWidth, maxHeight, maxLabelDiagonal;
5146
+ if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) {
5147
+ me.labelRotation = minRotation;
5148
+ return;
5149
+ }
5150
+ const labelSizes = me._getLabelSizes();
5151
+ const maxLabelWidth = labelSizes.widest.width;
5152
+ const maxLabelHeight = labelSizes.highest.height;
5153
+ const maxWidth = _limitValue(me.chart.width - maxLabelWidth, 0, me.maxWidth);
5154
+ tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1);
5155
+ if (maxLabelWidth + 6 > tickWidth) {
5156
+ tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
5157
+ maxHeight = me.maxHeight - getTickMarkLength(options.grid)
5158
+ - tickOpts.padding - getTitleHeight(options.title, me.chart.options.font);
5159
+ maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
5160
+ labelRotation = toDegrees(Math.min(
5161
+ Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)),
5162
+ Math.asin(_limitValue(maxHeight / maxLabelDiagonal, -1, 1)) - Math.asin(_limitValue(maxLabelHeight / maxLabelDiagonal, -1, 1))
5163
+ ));
5164
+ labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
5165
+ }
5166
+ me.labelRotation = labelRotation;
5167
+ }
5168
+ afterCalculateLabelRotation() {
5169
+ callback(this.options.afterCalculateLabelRotation, [this]);
5170
+ }
5171
+ beforeFit() {
5172
+ callback(this.options.beforeFit, [this]);
5173
+ }
5174
+ fit() {
5175
+ const me = this;
5176
+ const minSize = {
5177
+ width: 0,
5178
+ height: 0
5179
+ };
5180
+ const {chart, options: {ticks: tickOpts, title: titleOpts, grid: gridOpts}} = me;
5181
+ const display = me._isVisible();
5182
+ const isHorizontal = me.isHorizontal();
5183
+ if (display) {
5184
+ const titleHeight = getTitleHeight(titleOpts, chart.options.font);
5185
+ if (isHorizontal) {
5186
+ minSize.width = me.maxWidth;
5187
+ minSize.height = getTickMarkLength(gridOpts) + titleHeight;
5188
+ } else {
5189
+ minSize.height = me.maxHeight;
5190
+ minSize.width = getTickMarkLength(gridOpts) + titleHeight;
5191
+ }
5192
+ if (tickOpts.display && me.ticks.length) {
5193
+ const {first, last, widest, highest} = me._getLabelSizes();
5194
+ const tickPadding = tickOpts.padding * 2;
5195
+ const angleRadians = toRadians(me.labelRotation);
5196
+ const cos = Math.cos(angleRadians);
5197
+ const sin = Math.sin(angleRadians);
5198
+ if (isHorizontal) {
5199
+ const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;
5200
+ minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
5201
+ } else {
5202
+ const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;
5203
+ minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth + tickPadding);
5204
+ }
5205
+ me._calculatePadding(first, last, sin, cos);
5206
+ }
5207
+ }
5208
+ me._handleMargins();
5209
+ if (isHorizontal) {
5210
+ me.width = me._length = chart.width - me._margins.left - me._margins.right;
5211
+ me.height = minSize.height;
5212
+ } else {
5213
+ me.width = minSize.width;
5214
+ me.height = me._length = chart.height - me._margins.top - me._margins.bottom;
5215
+ }
5216
+ }
5217
+ _calculatePadding(first, last, sin, cos) {
5218
+ const me = this;
5219
+ const {ticks: {align, padding}, position} = me.options;
5220
+ const isRotated = me.labelRotation !== 0;
5221
+ const labelsBelowTicks = position !== 'top' && me.axis === 'x';
5222
+ if (me.isHorizontal()) {
5223
+ const offsetLeft = me.getPixelForTick(0) - me.left;
5224
+ const offsetRight = me.right - me.getPixelForTick(me.ticks.length - 1);
5225
+ let paddingLeft = 0;
5226
+ let paddingRight = 0;
5227
+ if (isRotated) {
5228
+ if (labelsBelowTicks) {
5229
+ paddingLeft = cos * first.width;
5230
+ paddingRight = sin * last.height;
5231
+ } else {
5232
+ paddingLeft = sin * first.height;
5233
+ paddingRight = cos * last.width;
5234
+ }
5235
+ } else if (align === 'start') {
5236
+ paddingRight = last.width;
5237
+ } else if (align === 'end') {
5238
+ paddingLeft = first.width;
5239
+ } else {
5240
+ paddingLeft = first.width / 2;
5241
+ paddingRight = last.width / 2;
5242
+ }
5243
+ me.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * me.width / (me.width - offsetLeft), 0);
5244
+ me.paddingRight = Math.max((paddingRight - offsetRight + padding) * me.width / (me.width - offsetRight), 0);
5245
+ } else {
5246
+ let paddingTop = last.height / 2;
5247
+ let paddingBottom = first.height / 2;
5248
+ if (align === 'start') {
5249
+ paddingTop = 0;
5250
+ paddingBottom = first.height;
5251
+ } else if (align === 'end') {
5252
+ paddingTop = last.height;
5253
+ paddingBottom = 0;
5254
+ }
5255
+ me.paddingTop = paddingTop + padding;
5256
+ me.paddingBottom = paddingBottom + padding;
5257
+ }
5258
+ }
5259
+ _handleMargins() {
5260
+ const me = this;
5261
+ if (me._margins) {
5262
+ me._margins.left = Math.max(me.paddingLeft, me._margins.left);
5263
+ me._margins.top = Math.max(me.paddingTop, me._margins.top);
5264
+ me._margins.right = Math.max(me.paddingRight, me._margins.right);
5265
+ me._margins.bottom = Math.max(me.paddingBottom, me._margins.bottom);
5266
+ }
5267
+ }
5268
+ afterFit() {
5269
+ callback(this.options.afterFit, [this]);
5270
+ }
5271
+ isHorizontal() {
5272
+ const {axis, position} = this.options;
5273
+ return position === 'top' || position === 'bottom' || axis === 'x';
5274
+ }
5275
+ isFullSize() {
5276
+ return this.options.fullSize;
5277
+ }
5278
+ _convertTicksToLabels(ticks) {
5279
+ const me = this;
5280
+ me.beforeTickToLabelConversion();
5281
+ me.generateTickLabels(ticks);
5282
+ let i, ilen;
5283
+ for (i = 0, ilen = ticks.length; i < ilen; i++) {
5284
+ if (isNullOrUndef(ticks[i].label)) {
5285
+ ticks.splice(i, 1);
5286
+ ilen--;
5287
+ i--;
5288
+ }
5289
+ }
5290
+ me.afterTickToLabelConversion();
5291
+ }
5292
+ _getLabelSizes() {
5293
+ const me = this;
5294
+ let labelSizes = me._labelSizes;
5295
+ if (!labelSizes) {
5296
+ const sampleSize = me.options.ticks.sampleSize;
5297
+ let ticks = me.ticks;
5298
+ if (sampleSize < ticks.length) {
5299
+ ticks = sample(ticks, sampleSize);
5300
+ }
5301
+ me._labelSizes = labelSizes = me._computeLabelSizes(ticks, ticks.length);
5302
+ }
5303
+ return labelSizes;
5304
+ }
5305
+ _computeLabelSizes(ticks, length) {
5306
+ const {ctx, _longestTextCache: caches} = this;
5307
+ const widths = [];
5308
+ const heights = [];
5309
+ let widestLabelSize = 0;
5310
+ let highestLabelSize = 0;
5311
+ let i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel;
5312
+ for (i = 0; i < length; ++i) {
5313
+ label = ticks[i].label;
5314
+ tickFont = this._resolveTickFontOptions(i);
5315
+ ctx.font = fontString = tickFont.string;
5316
+ cache = caches[fontString] = caches[fontString] || {data: {}, gc: []};
5317
+ lineHeight = tickFont.lineHeight;
5318
+ width = height = 0;
5319
+ if (!isNullOrUndef(label) && !isArray(label)) {
5320
+ width = _measureText(ctx, cache.data, cache.gc, width, label);
5321
+ height = lineHeight;
5322
+ } else if (isArray(label)) {
5323
+ for (j = 0, jlen = label.length; j < jlen; ++j) {
5324
+ nestedLabel = label[j];
5325
+ if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
5326
+ width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);
5327
+ height += lineHeight;
5328
+ }
5329
+ }
5330
+ }
5331
+ widths.push(width);
5332
+ heights.push(height);
5333
+ widestLabelSize = Math.max(width, widestLabelSize);
5334
+ highestLabelSize = Math.max(height, highestLabelSize);
5335
+ }
5336
+ garbageCollect(caches, length);
5337
+ const widest = widths.indexOf(widestLabelSize);
5338
+ const highest = heights.indexOf(highestLabelSize);
5339
+ const valueAt = (idx) => ({width: widths[idx] || 0, height: heights[idx] || 0});
5340
+ return {
5341
+ first: valueAt(0),
5342
+ last: valueAt(length - 1),
5343
+ widest: valueAt(widest),
5344
+ highest: valueAt(highest),
5345
+ widths,
5346
+ heights,
5347
+ };
5348
+ }
5349
+ getLabelForValue(value) {
5350
+ return value;
5351
+ }
5352
+ getPixelForValue(value, index) {
5353
+ return NaN;
5354
+ }
5355
+ getValueForPixel(pixel) {}
5356
+ getPixelForTick(index) {
5357
+ const ticks = this.ticks;
5358
+ if (index < 0 || index > ticks.length - 1) {
5359
+ return null;
5360
+ }
5361
+ return this.getPixelForValue(ticks[index].value);
5362
+ }
5363
+ getPixelForDecimal(decimal) {
5364
+ const me = this;
5365
+ if (me._reversePixels) {
5366
+ decimal = 1 - decimal;
5367
+ }
5368
+ const pixel = me._startPixel + decimal * me._length;
5369
+ return _int16Range(me._alignToPixels ? _alignPixel(me.chart, pixel, 0) : pixel);
5370
+ }
5371
+ getDecimalForPixel(pixel) {
5372
+ const decimal = (pixel - this._startPixel) / this._length;
5373
+ return this._reversePixels ? 1 - decimal : decimal;
5374
+ }
5375
+ getBasePixel() {
5376
+ return this.getPixelForValue(this.getBaseValue());
5377
+ }
5378
+ getBaseValue() {
5379
+ const {min, max} = this;
5380
+ return min < 0 && max < 0 ? max :
5381
+ min > 0 && max > 0 ? min :
5382
+ 0;
5383
+ }
5384
+ getContext(index) {
5385
+ const me = this;
5386
+ const ticks = me.ticks || [];
5387
+ if (index >= 0 && index < ticks.length) {
5388
+ const tick = ticks[index];
5389
+ return tick.$context ||
5390
+ (tick.$context = createTickContext(me.getContext(), index, tick));
5391
+ }
5392
+ return me.$context ||
5393
+ (me.$context = createScaleContext(me.chart.getContext(), me));
5394
+ }
5395
+ _tickSize() {
5396
+ const me = this;
5397
+ const optionTicks = me.options.ticks;
5398
+ const rot = toRadians(me.labelRotation);
5399
+ const cos = Math.abs(Math.cos(rot));
5400
+ const sin = Math.abs(Math.sin(rot));
5401
+ const labelSizes = me._getLabelSizes();
5402
+ const padding = optionTicks.autoSkipPadding || 0;
5403
+ const w = labelSizes ? labelSizes.widest.width + padding : 0;
5404
+ const h = labelSizes ? labelSizes.highest.height + padding : 0;
5405
+ return me.isHorizontal()
5406
+ ? h * cos > w * sin ? w / cos : h / sin
5407
+ : h * sin < w * cos ? h / cos : w / sin;
5408
+ }
5409
+ _isVisible() {
5410
+ const display = this.options.display;
5411
+ if (display !== 'auto') {
5412
+ return !!display;
5413
+ }
5414
+ return this.getMatchingVisibleMetas().length > 0;
5415
+ }
5416
+ _computeGridLineItems(chartArea) {
5417
+ const me = this;
5418
+ const axis = me.axis;
5419
+ const chart = me.chart;
5420
+ const options = me.options;
5421
+ const {grid, position} = options;
5422
+ const offset = grid.offset;
5423
+ const isHorizontal = me.isHorizontal();
5424
+ const ticks = me.ticks;
5425
+ const ticksLength = ticks.length + (offset ? 1 : 0);
5426
+ const tl = getTickMarkLength(grid);
5427
+ const items = [];
5428
+ const borderOpts = grid.setContext(me.getContext());
5429
+ const axisWidth = borderOpts.drawBorder ? borderOpts.borderWidth : 0;
5430
+ const axisHalfWidth = axisWidth / 2;
5431
+ const alignBorderValue = function(pixel) {
5432
+ return _alignPixel(chart, pixel, axisWidth);
5433
+ };
5434
+ let borderValue, i, lineValue, alignedLineValue;
5435
+ let tx1, ty1, tx2, ty2, x1, y1, x2, y2;
5436
+ if (position === 'top') {
5437
+ borderValue = alignBorderValue(me.bottom);
5438
+ ty1 = me.bottom - tl;
5439
+ ty2 = borderValue - axisHalfWidth;
5440
+ y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
5441
+ y2 = chartArea.bottom;
5442
+ } else if (position === 'bottom') {
5443
+ borderValue = alignBorderValue(me.top);
5444
+ y1 = chartArea.top;
5445
+ y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
5446
+ ty1 = borderValue + axisHalfWidth;
5447
+ ty2 = me.top + tl;
5448
+ } else if (position === 'left') {
5449
+ borderValue = alignBorderValue(me.right);
5450
+ tx1 = me.right - tl;
5451
+ tx2 = borderValue - axisHalfWidth;
5452
+ x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
5453
+ x2 = chartArea.right;
5454
+ } else if (position === 'right') {
5455
+ borderValue = alignBorderValue(me.left);
5456
+ x1 = chartArea.left;
5457
+ x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
5458
+ tx1 = borderValue + axisHalfWidth;
5459
+ tx2 = me.left + tl;
5460
+ } else if (axis === 'x') {
5461
+ if (position === 'center') {
5462
+ borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);
5463
+ } else if (isObject(position)) {
5464
+ const positionAxisID = Object.keys(position)[0];
5465
+ const value = position[positionAxisID];
5466
+ borderValue = alignBorderValue(me.chart.scales[positionAxisID].getPixelForValue(value));
5467
+ }
5468
+ y1 = chartArea.top;
5469
+ y2 = chartArea.bottom;
5470
+ ty1 = borderValue + axisHalfWidth;
5471
+ ty2 = ty1 + tl;
5472
+ } else if (axis === 'y') {
5473
+ if (position === 'center') {
5474
+ borderValue = alignBorderValue((chartArea.left + chartArea.right) / 2);
5475
+ } else if (isObject(position)) {
5476
+ const positionAxisID = Object.keys(position)[0];
5477
+ const value = position[positionAxisID];
5478
+ borderValue = alignBorderValue(me.chart.scales[positionAxisID].getPixelForValue(value));
5479
+ }
5480
+ tx1 = borderValue - axisHalfWidth;
5481
+ tx2 = tx1 - tl;
5482
+ x1 = chartArea.left;
5483
+ x2 = chartArea.right;
5484
+ }
5485
+ const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);
5486
+ const step = Math.max(1, Math.ceil(ticksLength / limit));
5487
+ for (i = 0; i < ticksLength; i += step) {
5488
+ const optsAtIndex = grid.setContext(me.getContext(i));
5489
+ const lineWidth = optsAtIndex.lineWidth;
5490
+ const lineColor = optsAtIndex.color;
5491
+ const borderDash = grid.borderDash || [];
5492
+ const borderDashOffset = optsAtIndex.borderDashOffset;
5493
+ const tickWidth = optsAtIndex.tickWidth;
5494
+ const tickColor = optsAtIndex.tickColor;
5495
+ const tickBorderDash = optsAtIndex.tickBorderDash || [];
5496
+ const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;
5497
+ lineValue = getPixelForGridLine(me, i, offset);
5498
+ if (lineValue === undefined) {
5499
+ continue;
5500
+ }
5501
+ alignedLineValue = _alignPixel(chart, lineValue, lineWidth);
5502
+ if (isHorizontal) {
5503
+ tx1 = tx2 = x1 = x2 = alignedLineValue;
5504
+ } else {
5505
+ ty1 = ty2 = y1 = y2 = alignedLineValue;
5506
+ }
5507
+ items.push({
5508
+ tx1,
5509
+ ty1,
5510
+ tx2,
5511
+ ty2,
5512
+ x1,
5513
+ y1,
5514
+ x2,
5515
+ y2,
5516
+ width: lineWidth,
5517
+ color: lineColor,
5518
+ borderDash,
5519
+ borderDashOffset,
5520
+ tickWidth,
5521
+ tickColor,
5522
+ tickBorderDash,
5523
+ tickBorderDashOffset,
5524
+ });
5525
+ }
5526
+ me._ticksLength = ticksLength;
5527
+ me._borderValue = borderValue;
5528
+ return items;
5529
+ }
5530
+ _computeLabelItems(chartArea) {
5531
+ const me = this;
5532
+ const axis = me.axis;
5533
+ const options = me.options;
5534
+ const {position, ticks: optionTicks} = options;
5535
+ const isHorizontal = me.isHorizontal();
5536
+ const ticks = me.ticks;
5537
+ const {align, crossAlign, padding, mirror} = optionTicks;
5538
+ const tl = getTickMarkLength(options.grid);
5539
+ const tickAndPadding = tl + padding;
5540
+ const hTickAndPadding = mirror ? -padding : tickAndPadding;
5541
+ const rotation = -toRadians(me.labelRotation);
5542
+ const items = [];
5543
+ let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
5544
+ let textBaseline = 'middle';
5545
+ if (position === 'top') {
5546
+ y = me.bottom - hTickAndPadding;
5547
+ textAlign = me._getXAxisLabelAlignment();
5548
+ } else if (position === 'bottom') {
5549
+ y = me.top + hTickAndPadding;
5550
+ textAlign = me._getXAxisLabelAlignment();
5551
+ } else if (position === 'left') {
5552
+ const ret = me._getYAxisLabelAlignment(tl);
5553
+ textAlign = ret.textAlign;
5554
+ x = ret.x;
5555
+ } else if (position === 'right') {
5556
+ const ret = me._getYAxisLabelAlignment(tl);
5557
+ textAlign = ret.textAlign;
5558
+ x = ret.x;
5559
+ } else if (axis === 'x') {
5560
+ if (position === 'center') {
5561
+ y = ((chartArea.top + chartArea.bottom) / 2) + tickAndPadding;
5562
+ } else if (isObject(position)) {
5563
+ const positionAxisID = Object.keys(position)[0];
5564
+ const value = position[positionAxisID];
5565
+ y = me.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;
5566
+ }
5567
+ textAlign = me._getXAxisLabelAlignment();
5568
+ } else if (axis === 'y') {
5569
+ if (position === 'center') {
5570
+ x = ((chartArea.left + chartArea.right) / 2) - tickAndPadding;
5571
+ } else if (isObject(position)) {
5572
+ const positionAxisID = Object.keys(position)[0];
5573
+ const value = position[positionAxisID];
5574
+ x = me.chart.scales[positionAxisID].getPixelForValue(value);
5575
+ }
5576
+ textAlign = me._getYAxisLabelAlignment(tl).textAlign;
5577
+ }
5578
+ if (axis === 'y') {
5579
+ if (align === 'start') {
5580
+ textBaseline = 'top';
5581
+ } else if (align === 'end') {
5582
+ textBaseline = 'bottom';
5583
+ }
5584
+ }
5585
+ const labelSizes = me._getLabelSizes();
5586
+ for (i = 0, ilen = ticks.length; i < ilen; ++i) {
5587
+ tick = ticks[i];
5588
+ label = tick.label;
5589
+ const optsAtIndex = optionTicks.setContext(me.getContext(i));
5590
+ pixel = me.getPixelForTick(i) + optionTicks.labelOffset;
5591
+ font = me._resolveTickFontOptions(i);
5592
+ lineHeight = font.lineHeight;
5593
+ lineCount = isArray(label) ? label.length : 1;
5594
+ const halfCount = lineCount / 2;
5595
+ const color = optsAtIndex.color;
5596
+ const strokeColor = optsAtIndex.textStrokeColor;
5597
+ const strokeWidth = optsAtIndex.textStrokeWidth;
5598
+ if (isHorizontal) {
5599
+ x = pixel;
5600
+ if (position === 'top') {
5601
+ if (crossAlign === 'near' || rotation !== 0) {
5602
+ textOffset = -lineCount * lineHeight + lineHeight / 2;
5603
+ } else if (crossAlign === 'center') {
5604
+ textOffset = -labelSizes.highest.height / 2 - halfCount * lineHeight + lineHeight;
5605
+ } else {
5606
+ textOffset = -labelSizes.highest.height + lineHeight / 2;
5607
+ }
5608
+ } else {
5609
+ if (crossAlign === 'near' || rotation !== 0) {
5610
+ textOffset = lineHeight / 2;
5611
+ } else if (crossAlign === 'center') {
5612
+ textOffset = labelSizes.highest.height / 2 - halfCount * lineHeight;
5613
+ } else {
5614
+ textOffset = labelSizes.highest.height - lineCount * lineHeight;
5615
+ }
5616
+ }
5617
+ if (mirror) {
5618
+ textOffset *= -1;
5619
+ }
5620
+ } else {
5621
+ y = pixel;
5622
+ textOffset = (1 - lineCount) * lineHeight / 2;
5623
+ }
5624
+ let backdrop;
5625
+ if (optsAtIndex.showLabelBackdrop) {
5626
+ const labelPadding = toPadding(optsAtIndex.backdropPadding);
5627
+ const height = labelSizes.heights[i];
5628
+ const width = labelSizes.widths[i];
5629
+ let top = y + textOffset - labelPadding.top;
5630
+ let left = x - labelPadding.left;
5631
+ switch (textBaseline) {
5632
+ case 'middle':
5633
+ top -= height / 2;
5634
+ break;
5635
+ case 'bottom':
5636
+ top -= height;
5637
+ break;
5638
+ }
5639
+ switch (textAlign) {
5640
+ case 'center':
5641
+ left -= width / 2;
5642
+ break;
5643
+ case 'right':
5644
+ left -= width;
5645
+ break;
5646
+ }
5647
+ backdrop = {
5648
+ left,
5649
+ top,
5650
+ width: width + labelPadding.width,
5651
+ height: height + labelPadding.height,
5652
+ color: optsAtIndex.backdropColor,
5653
+ };
5654
+ }
5655
+ items.push({
5656
+ rotation,
5657
+ label,
5658
+ font,
5659
+ color,
5660
+ strokeColor,
5661
+ strokeWidth,
5662
+ textOffset,
5663
+ textAlign,
5664
+ textBaseline,
5665
+ translation: [x, y],
5666
+ backdrop,
5667
+ });
5668
+ }
5669
+ return items;
5670
+ }
5671
+ _getXAxisLabelAlignment() {
5672
+ const me = this;
5673
+ const {position, ticks} = me.options;
5674
+ const rotation = -toRadians(me.labelRotation);
5675
+ if (rotation) {
5676
+ return position === 'top' ? 'left' : 'right';
5677
+ }
5678
+ let align = 'center';
5679
+ if (ticks.align === 'start') {
5680
+ align = 'left';
5681
+ } else if (ticks.align === 'end') {
5682
+ align = 'right';
5683
+ }
5684
+ return align;
5685
+ }
5686
+ _getYAxisLabelAlignment(tl) {
5687
+ const me = this;
5688
+ const {position, ticks: {crossAlign, mirror, padding}} = me.options;
5689
+ const labelSizes = me._getLabelSizes();
5690
+ const tickAndPadding = tl + padding;
5691
+ const widest = labelSizes.widest.width;
5692
+ let textAlign;
5693
+ let x;
5694
+ if (position === 'left') {
5695
+ if (mirror) {
5696
+ x = me.right + padding;
5697
+ if (crossAlign === 'near') {
5698
+ textAlign = 'left';
5699
+ } else if (crossAlign === 'center') {
5700
+ textAlign = 'center';
5701
+ x += (widest / 2);
5702
+ } else {
5703
+ textAlign = 'right';
5704
+ x += widest;
5705
+ }
5706
+ } else {
5707
+ x = me.right - tickAndPadding;
5708
+ if (crossAlign === 'near') {
5709
+ textAlign = 'right';
5710
+ } else if (crossAlign === 'center') {
5711
+ textAlign = 'center';
5712
+ x -= (widest / 2);
5713
+ } else {
5714
+ textAlign = 'left';
5715
+ x = me.left;
5716
+ }
5717
+ }
5718
+ } else if (position === 'right') {
5719
+ if (mirror) {
5720
+ x = me.left + padding;
5721
+ if (crossAlign === 'near') {
5722
+ textAlign = 'right';
5723
+ } else if (crossAlign === 'center') {
5724
+ textAlign = 'center';
5725
+ x -= (widest / 2);
5726
+ } else {
5727
+ textAlign = 'left';
5728
+ x -= widest;
5729
+ }
5730
+ } else {
5731
+ x = me.left + tickAndPadding;
5732
+ if (crossAlign === 'near') {
5733
+ textAlign = 'left';
5734
+ } else if (crossAlign === 'center') {
5735
+ textAlign = 'center';
5736
+ x += widest / 2;
5737
+ } else {
5738
+ textAlign = 'right';
5739
+ x = me.right;
5740
+ }
5741
+ }
5742
+ } else {
5743
+ textAlign = 'right';
5744
+ }
5745
+ return {textAlign, x};
5746
+ }
5747
+ _computeLabelArea() {
5748
+ const me = this;
5749
+ if (me.options.ticks.mirror) {
5750
+ return;
5751
+ }
5752
+ const chart = me.chart;
5753
+ const position = me.options.position;
5754
+ if (position === 'left' || position === 'right') {
5755
+ return {top: 0, left: me.left, bottom: chart.height, right: me.right};
5756
+ } if (position === 'top' || position === 'bottom') {
5757
+ return {top: me.top, left: 0, bottom: me.bottom, right: chart.width};
5758
+ }
5759
+ }
5760
+ drawBackground() {
5761
+ const {ctx, options: {backgroundColor}, left, top, width, height} = this;
5762
+ if (backgroundColor) {
5763
+ ctx.save();
5764
+ ctx.fillStyle = backgroundColor;
5765
+ ctx.fillRect(left, top, width, height);
5766
+ ctx.restore();
5767
+ }
5768
+ }
5769
+ getLineWidthForValue(value) {
5770
+ const me = this;
5771
+ const grid = me.options.grid;
5772
+ if (!me._isVisible() || !grid.display) {
5773
+ return 0;
5774
+ }
5775
+ const ticks = me.ticks;
5776
+ const index = ticks.findIndex(t => t.value === value);
5777
+ if (index >= 0) {
5778
+ const opts = grid.setContext(me.getContext(index));
5779
+ return opts.lineWidth;
5780
+ }
5781
+ return 0;
5782
+ }
5783
+ drawGrid(chartArea) {
5784
+ const me = this;
5785
+ const grid = me.options.grid;
5786
+ const ctx = me.ctx;
5787
+ const items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea));
5788
+ let i, ilen;
5789
+ const drawLine = (p1, p2, style) => {
5790
+ if (!style.width || !style.color) {
5791
+ return;
5792
+ }
5793
+ ctx.save();
5794
+ ctx.lineWidth = style.width;
5795
+ ctx.strokeStyle = style.color;
5796
+ ctx.setLineDash(style.borderDash || []);
5797
+ ctx.lineDashOffset = style.borderDashOffset;
5798
+ ctx.beginPath();
5799
+ ctx.moveTo(p1.x, p1.y);
5800
+ ctx.lineTo(p2.x, p2.y);
5801
+ ctx.stroke();
5802
+ ctx.restore();
5803
+ };
5804
+ if (grid.display) {
5805
+ for (i = 0, ilen = items.length; i < ilen; ++i) {
5806
+ const item = items[i];
5807
+ if (grid.drawOnChartArea) {
5808
+ drawLine(
5809
+ {x: item.x1, y: item.y1},
5810
+ {x: item.x2, y: item.y2},
5811
+ item
5812
+ );
5813
+ }
5814
+ if (grid.drawTicks) {
5815
+ drawLine(
5816
+ {x: item.tx1, y: item.ty1},
5817
+ {x: item.tx2, y: item.ty2},
5818
+ {
5819
+ color: item.tickColor,
5820
+ width: item.tickWidth,
5821
+ borderDash: item.tickBorderDash,
5822
+ borderDashOffset: item.tickBorderDashOffset
5823
+ }
5824
+ );
5825
+ }
5826
+ }
5827
+ }
5828
+ }
5829
+ drawBorder() {
5830
+ const me = this;
5831
+ const {chart, ctx, options: {grid}} = me;
5832
+ const borderOpts = grid.setContext(me.getContext());
5833
+ const axisWidth = grid.drawBorder ? borderOpts.borderWidth : 0;
5834
+ if (!axisWidth) {
5835
+ return;
5836
+ }
5837
+ const lastLineWidth = grid.setContext(me.getContext(0)).lineWidth;
5838
+ const borderValue = me._borderValue;
5839
+ let x1, x2, y1, y2;
5840
+ if (me.isHorizontal()) {
5841
+ x1 = _alignPixel(chart, me.left, axisWidth) - axisWidth / 2;
5842
+ x2 = _alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2;
5843
+ y1 = y2 = borderValue;
5844
+ } else {
5845
+ y1 = _alignPixel(chart, me.top, axisWidth) - axisWidth / 2;
5846
+ y2 = _alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2;
5847
+ x1 = x2 = borderValue;
5848
+ }
5849
+ ctx.save();
5850
+ ctx.lineWidth = borderOpts.borderWidth;
5851
+ ctx.strokeStyle = borderOpts.borderColor;
5852
+ ctx.beginPath();
5853
+ ctx.moveTo(x1, y1);
5854
+ ctx.lineTo(x2, y2);
5855
+ ctx.stroke();
5856
+ ctx.restore();
5857
+ }
5858
+ drawLabels(chartArea) {
5859
+ const me = this;
5860
+ const optionTicks = me.options.ticks;
5861
+ if (!optionTicks.display) {
5862
+ return;
5863
+ }
5864
+ const ctx = me.ctx;
5865
+ const area = me._computeLabelArea();
5866
+ if (area) {
5867
+ clipArea(ctx, area);
5868
+ }
5869
+ const items = me._labelItems || (me._labelItems = me._computeLabelItems(chartArea));
5870
+ let i, ilen;
5871
+ for (i = 0, ilen = items.length; i < ilen; ++i) {
5872
+ const item = items[i];
5873
+ const tickFont = item.font;
5874
+ const label = item.label;
5875
+ if (item.backdrop) {
5876
+ ctx.fillStyle = item.backdrop.color;
5877
+ ctx.fillRect(item.backdrop.left, item.backdrop.top, item.backdrop.width, item.backdrop.height);
5878
+ }
5879
+ let y = item.textOffset;
5880
+ renderText(ctx, label, 0, y, tickFont, item);
5881
+ }
5882
+ if (area) {
5883
+ unclipArea(ctx);
5884
+ }
5885
+ }
5886
+ drawTitle() {
5887
+ const {ctx, options: {position, title, reverse}} = this;
5888
+ if (!title.display) {
5889
+ return;
5890
+ }
5891
+ const font = toFont(title.font);
5892
+ const padding = toPadding(title.padding);
5893
+ const align = title.align;
5894
+ let offset = font.lineHeight / 2;
5895
+ if (position === 'bottom' || position === 'center' || isObject(position)) {
5896
+ offset += padding.bottom;
5897
+ if (isArray(title.text)) {
5898
+ offset += font.lineHeight * (title.text.length - 1);
5899
+ }
5900
+ } else {
5901
+ offset += padding.top;
5902
+ }
5903
+ const {titleX, titleY, maxWidth, rotation} = titleArgs(this, offset, position, align);
5904
+ renderText(ctx, title.text, 0, 0, font, {
5905
+ color: title.color,
5906
+ maxWidth,
5907
+ rotation,
5908
+ textAlign: titleAlign(align, position, reverse),
5909
+ textBaseline: 'middle',
5910
+ translation: [titleX, titleY],
5911
+ });
5912
+ }
5913
+ draw(chartArea) {
5914
+ const me = this;
5915
+ if (!me._isVisible()) {
5916
+ return;
5917
+ }
5918
+ me.drawBackground();
5919
+ me.drawGrid(chartArea);
5920
+ me.drawBorder();
5921
+ me.drawTitle();
5922
+ me.drawLabels(chartArea);
5923
+ }
5924
+ _layers() {
5925
+ const me = this;
5926
+ const opts = me.options;
5927
+ const tz = opts.ticks && opts.ticks.z || 0;
5928
+ const gz = valueOrDefault(opts.grid && opts.grid.z, -1);
5929
+ if (!me._isVisible() || me.draw !== Scale.prototype.draw) {
5930
+ return [{
5931
+ z: tz,
5932
+ draw(chartArea) {
5933
+ me.draw(chartArea);
5934
+ }
5935
+ }];
5936
+ }
5937
+ return [{
5938
+ z: gz,
5939
+ draw(chartArea) {
5940
+ me.drawBackground();
5941
+ me.drawGrid(chartArea);
5942
+ me.drawTitle();
5943
+ }
5944
+ }, {
5945
+ z: gz + 1,
5946
+ draw() {
5947
+ me.drawBorder();
5948
+ }
5949
+ }, {
5950
+ z: tz,
5951
+ draw(chartArea) {
5952
+ me.drawLabels(chartArea);
5953
+ }
5954
+ }];
5955
+ }
5956
+ getMatchingVisibleMetas(type) {
5957
+ const me = this;
5958
+ const metas = me.chart.getSortedVisibleDatasetMetas();
5959
+ const axisID = me.axis + 'AxisID';
5960
+ const result = [];
5961
+ let i, ilen;
5962
+ for (i = 0, ilen = metas.length; i < ilen; ++i) {
5963
+ const meta = metas[i];
5964
+ if (meta[axisID] === me.id && (!type || meta.type === type)) {
5965
+ result.push(meta);
5966
+ }
5967
+ }
5968
+ return result;
5969
+ }
5970
+ _resolveTickFontOptions(index) {
5971
+ const opts = this.options.ticks.setContext(this.getContext(index));
5972
+ return toFont(opts.font);
5973
+ }
5974
+ _maxDigits() {
5975
+ const me = this;
5976
+ const fontSize = me._resolveTickFontOptions(0).lineHeight;
5977
+ return (me.isHorizontal() ? me.width : me.height) / fontSize;
5978
+ }
5979
+ }
5980
+
5981
+ class TypedRegistry {
5982
+ constructor(type, scope, override) {
5983
+ this.type = type;
5984
+ this.scope = scope;
5985
+ this.override = override;
5986
+ this.items = Object.create(null);
5987
+ }
5988
+ isForType(type) {
5989
+ return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);
5990
+ }
5991
+ register(item) {
5992
+ const me = this;
5993
+ const proto = Object.getPrototypeOf(item);
5994
+ let parentScope;
5995
+ if (isIChartComponent(proto)) {
5996
+ parentScope = me.register(proto);
5997
+ }
5998
+ const items = me.items;
5999
+ const id = item.id;
6000
+ const scope = me.scope + '.' + id;
6001
+ if (!id) {
6002
+ throw new Error('class does not have id: ' + item);
6003
+ }
6004
+ if (id in items) {
6005
+ return scope;
6006
+ }
6007
+ items[id] = item;
6008
+ registerDefaults(item, scope, parentScope);
6009
+ if (me.override) {
6010
+ defaults.override(item.id, item.overrides);
6011
+ }
6012
+ return scope;
6013
+ }
6014
+ get(id) {
6015
+ return this.items[id];
6016
+ }
6017
+ unregister(item) {
6018
+ const items = this.items;
6019
+ const id = item.id;
6020
+ const scope = this.scope;
6021
+ if (id in items) {
6022
+ delete items[id];
6023
+ }
6024
+ if (scope && id in defaults[scope]) {
6025
+ delete defaults[scope][id];
6026
+ if (this.override) {
6027
+ delete overrides[id];
6028
+ }
6029
+ }
6030
+ }
6031
+ }
6032
+ function registerDefaults(item, scope, parentScope) {
6033
+ const itemDefaults = merge(Object.create(null), [
6034
+ parentScope ? defaults.get(parentScope) : {},
6035
+ defaults.get(scope),
6036
+ item.defaults
6037
+ ]);
6038
+ defaults.set(scope, itemDefaults);
6039
+ if (item.defaultRoutes) {
6040
+ routeDefaults(scope, item.defaultRoutes);
6041
+ }
6042
+ if (item.descriptors) {
6043
+ defaults.describe(scope, item.descriptors);
6044
+ }
6045
+ }
6046
+ function routeDefaults(scope, routes) {
6047
+ Object.keys(routes).forEach(property => {
6048
+ const propertyParts = property.split('.');
6049
+ const sourceName = propertyParts.pop();
6050
+ const sourceScope = [scope].concat(propertyParts).join('.');
6051
+ const parts = routes[property].split('.');
6052
+ const targetName = parts.pop();
6053
+ const targetScope = parts.join('.');
6054
+ defaults.route(sourceScope, sourceName, targetScope, targetName);
6055
+ });
6056
+ }
6057
+ function isIChartComponent(proto) {
6058
+ return 'id' in proto && 'defaults' in proto;
6059
+ }
6060
+
6061
+ class Registry {
6062
+ constructor() {
6063
+ this.controllers = new TypedRegistry(DatasetController, 'datasets', true);
6064
+ this.elements = new TypedRegistry(Element, 'elements');
6065
+ this.plugins = new TypedRegistry(Object, 'plugins');
6066
+ this.scales = new TypedRegistry(Scale, 'scales');
6067
+ this._typedRegistries = [this.controllers, this.scales, this.elements];
6068
+ }
6069
+ add(...args) {
6070
+ this._each('register', args);
6071
+ }
6072
+ remove(...args) {
6073
+ this._each('unregister', args);
6074
+ }
6075
+ addControllers(...args) {
6076
+ this._each('register', args, this.controllers);
6077
+ }
6078
+ addElements(...args) {
6079
+ this._each('register', args, this.elements);
6080
+ }
6081
+ addPlugins(...args) {
6082
+ this._each('register', args, this.plugins);
6083
+ }
6084
+ addScales(...args) {
6085
+ this._each('register', args, this.scales);
6086
+ }
6087
+ getController(id) {
6088
+ return this._get(id, this.controllers, 'controller');
6089
+ }
6090
+ getElement(id) {
6091
+ return this._get(id, this.elements, 'element');
6092
+ }
6093
+ getPlugin(id) {
6094
+ return this._get(id, this.plugins, 'plugin');
6095
+ }
6096
+ getScale(id) {
6097
+ return this._get(id, this.scales, 'scale');
6098
+ }
6099
+ removeControllers(...args) {
6100
+ this._each('unregister', args, this.controllers);
6101
+ }
6102
+ removeElements(...args) {
6103
+ this._each('unregister', args, this.elements);
6104
+ }
6105
+ removePlugins(...args) {
6106
+ this._each('unregister', args, this.plugins);
6107
+ }
6108
+ removeScales(...args) {
6109
+ this._each('unregister', args, this.scales);
6110
+ }
6111
+ _each(method, args, typedRegistry) {
6112
+ const me = this;
6113
+ [...args].forEach(arg => {
6114
+ const reg = typedRegistry || me._getRegistryForType(arg);
6115
+ if (typedRegistry || reg.isForType(arg) || (reg === me.plugins && arg.id)) {
6116
+ me._exec(method, reg, arg);
6117
+ } else {
6118
+ each(arg, item => {
6119
+ const itemReg = typedRegistry || me._getRegistryForType(item);
6120
+ me._exec(method, itemReg, item);
6121
+ });
6122
+ }
6123
+ });
6124
+ }
6125
+ _exec(method, registry, component) {
6126
+ const camelMethod = _capitalize(method);
6127
+ callback(component['before' + camelMethod], [], component);
6128
+ registry[method](component);
6129
+ callback(component['after' + camelMethod], [], component);
6130
+ }
6131
+ _getRegistryForType(type) {
6132
+ for (let i = 0; i < this._typedRegistries.length; i++) {
6133
+ const reg = this._typedRegistries[i];
6134
+ if (reg.isForType(type)) {
6135
+ return reg;
6136
+ }
6137
+ }
6138
+ return this.plugins;
6139
+ }
6140
+ _get(id, typedRegistry, type) {
6141
+ const item = typedRegistry.get(id);
6142
+ if (item === undefined) {
6143
+ throw new Error('"' + id + '" is not a registered ' + type + '.');
6144
+ }
6145
+ return item;
6146
+ }
6147
+ }
6148
+ var registry = new Registry();
6149
+
6150
+ class PluginService {
6151
+ constructor() {
6152
+ this._init = [];
6153
+ }
6154
+ notify(chart, hook, args, filter) {
6155
+ const me = this;
6156
+ if (hook === 'beforeInit') {
6157
+ me._init = me._createDescriptors(chart, true);
6158
+ me._notify(me._init, chart, 'install');
6159
+ }
6160
+ const descriptors = filter ? me._descriptors(chart).filter(filter) : me._descriptors(chart);
6161
+ const result = me._notify(descriptors, chart, hook, args);
6162
+ if (hook === 'destroy') {
6163
+ me._notify(descriptors, chart, 'stop');
6164
+ me._notify(me._init, chart, 'uninstall');
6165
+ }
6166
+ return result;
6167
+ }
6168
+ _notify(descriptors, chart, hook, args) {
6169
+ args = args || {};
6170
+ for (const descriptor of descriptors) {
6171
+ const plugin = descriptor.plugin;
6172
+ const method = plugin[hook];
6173
+ const params = [chart, args, descriptor.options];
6174
+ if (callback(method, params, plugin) === false && args.cancelable) {
6175
+ return false;
6176
+ }
6177
+ }
6178
+ return true;
6179
+ }
6180
+ invalidate() {
6181
+ if (!isNullOrUndef(this._cache)) {
6182
+ this._oldCache = this._cache;
6183
+ this._cache = undefined;
6184
+ }
6185
+ }
6186
+ _descriptors(chart) {
6187
+ if (this._cache) {
6188
+ return this._cache;
6189
+ }
6190
+ const descriptors = this._cache = this._createDescriptors(chart);
6191
+ this._notifyStateChanges(chart);
6192
+ return descriptors;
6193
+ }
6194
+ _createDescriptors(chart, all) {
6195
+ const config = chart && chart.config;
6196
+ const options = valueOrDefault(config.options && config.options.plugins, {});
6197
+ const plugins = allPlugins(config);
6198
+ return options === false && !all ? [] : createDescriptors(chart, plugins, options, all);
6199
+ }
6200
+ _notifyStateChanges(chart) {
6201
+ const previousDescriptors = this._oldCache || [];
6202
+ const descriptors = this._cache;
6203
+ const diff = (a, b) => a.filter(x => !b.some(y => x.plugin.id === y.plugin.id));
6204
+ this._notify(diff(previousDescriptors, descriptors), chart, 'stop');
6205
+ this._notify(diff(descriptors, previousDescriptors), chart, 'start');
6206
+ }
6207
+ }
6208
+ function allPlugins(config) {
6209
+ const plugins = [];
6210
+ const keys = Object.keys(registry.plugins.items);
6211
+ for (let i = 0; i < keys.length; i++) {
6212
+ plugins.push(registry.getPlugin(keys[i]));
6213
+ }
6214
+ const local = config.plugins || [];
6215
+ for (let i = 0; i < local.length; i++) {
6216
+ const plugin = local[i];
6217
+ if (plugins.indexOf(plugin) === -1) {
6218
+ plugins.push(plugin);
6219
+ }
6220
+ }
6221
+ return plugins;
6222
+ }
6223
+ function getOpts(options, all) {
6224
+ if (!all && options === false) {
6225
+ return null;
6226
+ }
6227
+ if (options === true) {
6228
+ return {};
6229
+ }
6230
+ return options;
6231
+ }
6232
+ function createDescriptors(chart, plugins, options, all) {
6233
+ const result = [];
6234
+ const context = chart.getContext();
6235
+ for (let i = 0; i < plugins.length; i++) {
6236
+ const plugin = plugins[i];
6237
+ const id = plugin.id;
6238
+ const opts = getOpts(options[id], all);
6239
+ if (opts === null) {
6240
+ continue;
6241
+ }
6242
+ result.push({
6243
+ plugin,
6244
+ options: pluginOpts(chart.config, plugin, opts, context)
6245
+ });
6246
+ }
6247
+ return result;
6248
+ }
6249
+ function pluginOpts(config, plugin, opts, context) {
6250
+ const keys = config.pluginScopeKeys(plugin);
6251
+ const scopes = config.getOptionScopes(opts, keys);
6252
+ return config.createResolver(scopes, context, [''], {scriptable: false, indexable: false, allKeys: true});
6253
+ }
6254
+
6255
+ function getIndexAxis(type, options) {
6256
+ const datasetDefaults = defaults.datasets[type] || {};
6257
+ const datasetOptions = (options.datasets || {})[type] || {};
6258
+ return datasetOptions.indexAxis || options.indexAxis || datasetDefaults.indexAxis || 'x';
6259
+ }
6260
+ function getAxisFromDefaultScaleID(id, indexAxis) {
6261
+ let axis = id;
6262
+ if (id === '_index_') {
6263
+ axis = indexAxis;
6264
+ } else if (id === '_value_') {
6265
+ axis = indexAxis === 'x' ? 'y' : 'x';
6266
+ }
6267
+ return axis;
6268
+ }
6269
+ function getDefaultScaleIDFromAxis(axis, indexAxis) {
6270
+ return axis === indexAxis ? '_index_' : '_value_';
6271
+ }
6272
+ function axisFromPosition(position) {
6273
+ if (position === 'top' || position === 'bottom') {
6274
+ return 'x';
6275
+ }
6276
+ if (position === 'left' || position === 'right') {
6277
+ return 'y';
6278
+ }
6279
+ }
6280
+ function determineAxis(id, scaleOptions) {
6281
+ if (id === 'x' || id === 'y') {
6282
+ return id;
6283
+ }
6284
+ return scaleOptions.axis || axisFromPosition(scaleOptions.position) || id.charAt(0).toLowerCase();
6285
+ }
6286
+ function mergeScaleConfig(config, options) {
6287
+ const chartDefaults = overrides[config.type] || {scales: {}};
6288
+ const configScales = options.scales || {};
6289
+ const chartIndexAxis = getIndexAxis(config.type, options);
6290
+ const firstIDs = Object.create(null);
6291
+ const scales = Object.create(null);
6292
+ Object.keys(configScales).forEach(id => {
6293
+ const scaleConf = configScales[id];
6294
+ const axis = determineAxis(id, scaleConf);
6295
+ const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
6296
+ const defaultScaleOptions = chartDefaults.scales || {};
6297
+ firstIDs[axis] = firstIDs[axis] || id;
6298
+ scales[id] = mergeIf(Object.create(null), [{axis}, scaleConf, defaultScaleOptions[axis], defaultScaleOptions[defaultId]]);
6299
+ });
6300
+ config.data.datasets.forEach(dataset => {
6301
+ const type = dataset.type || config.type;
6302
+ const indexAxis = dataset.indexAxis || getIndexAxis(type, options);
6303
+ const datasetDefaults = overrides[type] || {};
6304
+ const defaultScaleOptions = datasetDefaults.scales || {};
6305
+ Object.keys(defaultScaleOptions).forEach(defaultID => {
6306
+ const axis = getAxisFromDefaultScaleID(defaultID, indexAxis);
6307
+ const id = dataset[axis + 'AxisID'] || firstIDs[axis] || axis;
6308
+ scales[id] = scales[id] || Object.create(null);
6309
+ mergeIf(scales[id], [{axis}, configScales[id], defaultScaleOptions[defaultID]]);
6310
+ });
6311
+ });
6312
+ Object.keys(scales).forEach(key => {
6313
+ const scale = scales[key];
6314
+ mergeIf(scale, [defaults.scales[scale.type], defaults.scale]);
6315
+ });
6316
+ return scales;
6317
+ }
6318
+ function initOptions(config) {
6319
+ const options = config.options || (config.options = {});
6320
+ options.plugins = valueOrDefault(options.plugins, {});
6321
+ options.scales = mergeScaleConfig(config, options);
6322
+ }
6323
+ function initData(data) {
6324
+ data = data || {};
6325
+ data.datasets = data.datasets || [];
6326
+ data.labels = data.labels || [];
6327
+ return data;
6328
+ }
6329
+ function initConfig(config) {
6330
+ config = config || {};
6331
+ config.data = initData(config.data);
6332
+ initOptions(config);
6333
+ return config;
6334
+ }
6335
+ const keyCache = new Map();
6336
+ const keysCached = new Set();
6337
+ function cachedKeys(cacheKey, generate) {
6338
+ let keys = keyCache.get(cacheKey);
6339
+ if (!keys) {
6340
+ keys = generate();
6341
+ keyCache.set(cacheKey, keys);
6342
+ keysCached.add(keys);
6343
+ }
6344
+ return keys;
6345
+ }
6346
+ const addIfFound = (set, obj, key) => {
6347
+ const opts = resolveObjectKey(obj, key);
6348
+ if (opts !== undefined) {
6349
+ set.add(opts);
6350
+ }
6351
+ };
6352
+ class Config {
6353
+ constructor(config) {
6354
+ this._config = initConfig(config);
6355
+ this._scopeCache = new Map();
6356
+ this._resolverCache = new Map();
6357
+ }
6358
+ get platform() {
6359
+ return this._config.platform;
6360
+ }
6361
+ get type() {
6362
+ return this._config.type;
6363
+ }
6364
+ set type(type) {
6365
+ this._config.type = type;
6366
+ }
6367
+ get data() {
6368
+ return this._config.data;
6369
+ }
6370
+ set data(data) {
6371
+ this._config.data = initData(data);
6372
+ }
6373
+ get options() {
6374
+ return this._config.options;
6375
+ }
6376
+ set options(options) {
6377
+ this._config.options = options;
6378
+ }
6379
+ get plugins() {
6380
+ return this._config.plugins;
6381
+ }
6382
+ update() {
6383
+ const config = this._config;
6384
+ this.clearCache();
6385
+ initOptions(config);
6386
+ }
6387
+ clearCache() {
6388
+ this._scopeCache.clear();
6389
+ this._resolverCache.clear();
6390
+ }
6391
+ datasetScopeKeys(datasetType) {
6392
+ return cachedKeys(datasetType,
6393
+ () => [[
6394
+ `datasets.${datasetType}`,
6395
+ ''
6396
+ ]]);
6397
+ }
6398
+ datasetAnimationScopeKeys(datasetType, transition) {
6399
+ return cachedKeys(`${datasetType}.transition.${transition}`,
6400
+ () => [
6401
+ [
6402
+ `datasets.${datasetType}.transitions.${transition}`,
6403
+ `transitions.${transition}`,
6404
+ ],
6405
+ [
6406
+ `datasets.${datasetType}`,
6407
+ ''
6408
+ ]
6409
+ ]);
6410
+ }
6411
+ datasetElementScopeKeys(datasetType, elementType) {
6412
+ return cachedKeys(`${datasetType}-${elementType}`,
6413
+ () => [[
6414
+ `datasets.${datasetType}.elements.${elementType}`,
6415
+ `datasets.${datasetType}`,
6416
+ `elements.${elementType}`,
6417
+ ''
6418
+ ]]);
6419
+ }
6420
+ pluginScopeKeys(plugin) {
6421
+ const id = plugin.id;
6422
+ const type = this.type;
6423
+ return cachedKeys(`${type}-plugin-${id}`,
6424
+ () => [[
6425
+ `plugins.${id}`,
6426
+ ...plugin.additionalOptionScopes || [],
6427
+ ]]);
6428
+ }
6429
+ _cachedScopes(mainScope, resetCache) {
6430
+ const _scopeCache = this._scopeCache;
6431
+ let cache = _scopeCache.get(mainScope);
6432
+ if (!cache || resetCache) {
6433
+ cache = new Map();
6434
+ _scopeCache.set(mainScope, cache);
6435
+ }
6436
+ return cache;
6437
+ }
6438
+ getOptionScopes(mainScope, keyLists, resetCache) {
6439
+ const {options, type} = this;
6440
+ const cache = this._cachedScopes(mainScope, resetCache);
6441
+ const cached = cache.get(keyLists);
6442
+ if (cached) {
6443
+ return cached;
6444
+ }
6445
+ const scopes = new Set();
6446
+ keyLists.forEach(keys => {
6447
+ if (mainScope) {
6448
+ scopes.add(mainScope);
6449
+ keys.forEach(key => addIfFound(scopes, mainScope, key));
6450
+ }
6451
+ keys.forEach(key => addIfFound(scopes, options, key));
6452
+ keys.forEach(key => addIfFound(scopes, overrides[type] || {}, key));
6453
+ keys.forEach(key => addIfFound(scopes, defaults, key));
6454
+ keys.forEach(key => addIfFound(scopes, descriptors, key));
6455
+ });
6456
+ const array = Array.from(scopes);
6457
+ if (array.length === 0) {
6458
+ array.push(Object.create(null));
6459
+ }
6460
+ if (keysCached.has(keyLists)) {
6461
+ cache.set(keyLists, array);
6462
+ }
6463
+ return array;
6464
+ }
6465
+ chartOptionScopes() {
6466
+ const {options, type} = this;
6467
+ return [
6468
+ options,
6469
+ overrides[type] || {},
6470
+ defaults.datasets[type] || {},
6471
+ {type},
6472
+ defaults,
6473
+ descriptors
6474
+ ];
6475
+ }
6476
+ resolveNamedOptions(scopes, names, context, prefixes = ['']) {
6477
+ const result = {$shared: true};
6478
+ const {resolver, subPrefixes} = getResolver(this._resolverCache, scopes, prefixes);
6479
+ let options = resolver;
6480
+ if (needContext(resolver, names)) {
6481
+ result.$shared = false;
6482
+ context = isFunction(context) ? context() : context;
6483
+ const subResolver = this.createResolver(scopes, context, subPrefixes);
6484
+ options = _attachContext(resolver, context, subResolver);
6485
+ }
6486
+ for (const prop of names) {
6487
+ result[prop] = options[prop];
6488
+ }
6489
+ return result;
6490
+ }
6491
+ createResolver(scopes, context, prefixes = [''], descriptorDefaults) {
6492
+ const {resolver} = getResolver(this._resolverCache, scopes, prefixes);
6493
+ return isObject(context)
6494
+ ? _attachContext(resolver, context, undefined, descriptorDefaults)
6495
+ : resolver;
6496
+ }
6497
+ }
6498
+ function getResolver(resolverCache, scopes, prefixes) {
6499
+ let cache = resolverCache.get(scopes);
6500
+ if (!cache) {
6501
+ cache = new Map();
6502
+ resolverCache.set(scopes, cache);
6503
+ }
6504
+ const cacheKey = prefixes.join();
6505
+ let cached = cache.get(cacheKey);
6506
+ if (!cached) {
6507
+ const resolver = _createResolver(scopes, prefixes);
6508
+ cached = {
6509
+ resolver,
6510
+ subPrefixes: prefixes.filter(p => !p.toLowerCase().includes('hover'))
6511
+ };
6512
+ cache.set(cacheKey, cached);
6513
+ }
6514
+ return cached;
6515
+ }
6516
+ function needContext(proxy, names) {
6517
+ const {isScriptable, isIndexable} = _descriptors(proxy);
6518
+ for (const prop of names) {
6519
+ if ((isScriptable(prop) && isFunction(proxy[prop]))
6520
+ || (isIndexable(prop) && isArray(proxy[prop]))) {
6521
+ return true;
6522
+ }
6523
+ }
6524
+ return false;
6525
+ }
6526
+
6527
+ var version = "3.5.1";
6528
+
6529
+ const KNOWN_POSITIONS = ['top', 'bottom', 'left', 'right', 'chartArea'];
6530
+ function positionIsHorizontal(position, axis) {
6531
+ return position === 'top' || position === 'bottom' || (KNOWN_POSITIONS.indexOf(position) === -1 && axis === 'x');
6532
+ }
6533
+ function compare2Level(l1, l2) {
6534
+ return function(a, b) {
6535
+ return a[l1] === b[l1]
6536
+ ? a[l2] - b[l2]
6537
+ : a[l1] - b[l1];
6538
+ };
6539
+ }
6540
+ function onAnimationsComplete(context) {
6541
+ const chart = context.chart;
6542
+ const animationOptions = chart.options.animation;
6543
+ chart.notifyPlugins('afterRender');
6544
+ callback(animationOptions && animationOptions.onComplete, [context], chart);
6545
+ }
6546
+ function onAnimationProgress(context) {
6547
+ const chart = context.chart;
6548
+ const animationOptions = chart.options.animation;
6549
+ callback(animationOptions && animationOptions.onProgress, [context], chart);
6550
+ }
6551
+ function getCanvas(item) {
6552
+ if (_isDomSupported() && typeof item === 'string') {
6553
+ item = document.getElementById(item);
6554
+ } else if (item && item.length) {
6555
+ item = item[0];
6556
+ }
6557
+ if (item && item.canvas) {
6558
+ item = item.canvas;
6559
+ }
6560
+ return item;
6561
+ }
6562
+ const instances = {};
6563
+ const getChart = (key) => {
6564
+ const canvas = getCanvas(key);
6565
+ return Object.values(instances).filter((c) => c.canvas === canvas).pop();
6566
+ };
6567
+ class Chart {
6568
+ constructor(item, userConfig) {
6569
+ const me = this;
6570
+ const config = this.config = new Config(userConfig);
6571
+ const initialCanvas = getCanvas(item);
6572
+ const existingChart = getChart(initialCanvas);
6573
+ if (existingChart) {
6574
+ throw new Error(
6575
+ 'Canvas is already in use. Chart with ID \'' + existingChart.id + '\'' +
6576
+ ' must be destroyed before the canvas can be reused.'
6577
+ );
6578
+ }
6579
+ const options = config.createResolver(config.chartOptionScopes(), me.getContext());
6580
+ this.platform = new (config.platform || _detectPlatform(initialCanvas))();
6581
+ const context = me.platform.acquireContext(initialCanvas, options.aspectRatio);
6582
+ const canvas = context && context.canvas;
6583
+ const height = canvas && canvas.height;
6584
+ const width = canvas && canvas.width;
6585
+ this.id = uid();
6586
+ this.ctx = context;
6587
+ this.canvas = canvas;
6588
+ this.width = width;
6589
+ this.height = height;
6590
+ this._options = options;
6591
+ this._aspectRatio = this.aspectRatio;
6592
+ this._layers = [];
6593
+ this._metasets = [];
6594
+ this._stacks = undefined;
6595
+ this.boxes = [];
6596
+ this.currentDevicePixelRatio = undefined;
6597
+ this.chartArea = undefined;
6598
+ this._active = [];
6599
+ this._lastEvent = undefined;
6600
+ this._listeners = {};
6601
+ this._responsiveListeners = undefined;
6602
+ this._sortedMetasets = [];
6603
+ this.scales = {};
6604
+ this._plugins = new PluginService();
6605
+ this.$proxies = {};
6606
+ this._hiddenIndices = {};
6607
+ this.attached = false;
6608
+ this._animationsDisabled = undefined;
6609
+ this.$context = undefined;
6610
+ this._doResize = debounce(mode => this.update(mode), options.resizeDelay || 0);
6611
+ instances[me.id] = me;
6612
+ if (!context || !canvas) {
6613
+ console.error("Failed to create chart: can't acquire context from the given item");
6614
+ return;
6615
+ }
6616
+ animator.listen(me, 'complete', onAnimationsComplete);
6617
+ animator.listen(me, 'progress', onAnimationProgress);
6618
+ me._initialize();
6619
+ if (me.attached) {
6620
+ me.update();
6621
+ }
6622
+ }
6623
+ get aspectRatio() {
6624
+ const {options: {aspectRatio, maintainAspectRatio}, width, height, _aspectRatio} = this;
6625
+ if (!isNullOrUndef(aspectRatio)) {
6626
+ return aspectRatio;
6627
+ }
6628
+ if (maintainAspectRatio && _aspectRatio) {
6629
+ return _aspectRatio;
6630
+ }
6631
+ return height ? width / height : null;
6632
+ }
6633
+ get data() {
6634
+ return this.config.data;
6635
+ }
6636
+ set data(data) {
6637
+ this.config.data = data;
6638
+ }
6639
+ get options() {
6640
+ return this._options;
6641
+ }
6642
+ set options(options) {
6643
+ this.config.options = options;
6644
+ }
6645
+ _initialize() {
6646
+ const me = this;
6647
+ me.notifyPlugins('beforeInit');
6648
+ if (me.options.responsive) {
6649
+ me.resize();
6650
+ } else {
6651
+ retinaScale(me, me.options.devicePixelRatio);
6652
+ }
6653
+ me.bindEvents();
6654
+ me.notifyPlugins('afterInit');
6655
+ return me;
6656
+ }
6657
+ clear() {
6658
+ clearCanvas(this.canvas, this.ctx);
6659
+ return this;
6660
+ }
6661
+ stop() {
6662
+ animator.stop(this);
6663
+ return this;
6664
+ }
6665
+ resize(width, height) {
6666
+ if (!animator.running(this)) {
6667
+ this._resize(width, height);
6668
+ } else {
6669
+ this._resizeBeforeDraw = {width, height};
6670
+ }
6671
+ }
6672
+ _resize(width, height) {
6673
+ const me = this;
6674
+ const options = me.options;
6675
+ const canvas = me.canvas;
6676
+ const aspectRatio = options.maintainAspectRatio && me.aspectRatio;
6677
+ const newSize = me.platform.getMaximumSize(canvas, width, height, aspectRatio);
6678
+ const newRatio = options.devicePixelRatio || me.platform.getDevicePixelRatio();
6679
+ const mode = me.width ? 'resize' : 'attach';
6680
+ me.width = newSize.width;
6681
+ me.height = newSize.height;
6682
+ me._aspectRatio = me.aspectRatio;
6683
+ if (!retinaScale(me, newRatio, true)) {
6684
+ return;
6685
+ }
6686
+ me.notifyPlugins('resize', {size: newSize});
6687
+ callback(options.onResize, [me, newSize], me);
6688
+ if (me.attached) {
6689
+ if (me._doResize(mode)) {
6690
+ me.render();
6691
+ }
6692
+ }
6693
+ }
6694
+ ensureScalesHaveIDs() {
6695
+ const options = this.options;
6696
+ const scalesOptions = options.scales || {};
6697
+ each(scalesOptions, (axisOptions, axisID) => {
6698
+ axisOptions.id = axisID;
6699
+ });
6700
+ }
6701
+ buildOrUpdateScales() {
6702
+ const me = this;
6703
+ const options = me.options;
6704
+ const scaleOpts = options.scales;
6705
+ const scales = me.scales;
6706
+ const updated = Object.keys(scales).reduce((obj, id) => {
6707
+ obj[id] = false;
6708
+ return obj;
6709
+ }, {});
6710
+ let items = [];
6711
+ if (scaleOpts) {
6712
+ items = items.concat(
6713
+ Object.keys(scaleOpts).map((id) => {
6714
+ const scaleOptions = scaleOpts[id];
6715
+ const axis = determineAxis(id, scaleOptions);
6716
+ const isRadial = axis === 'r';
6717
+ const isHorizontal = axis === 'x';
6718
+ return {
6719
+ options: scaleOptions,
6720
+ dposition: isRadial ? 'chartArea' : isHorizontal ? 'bottom' : 'left',
6721
+ dtype: isRadial ? 'radialLinear' : isHorizontal ? 'category' : 'linear'
6722
+ };
6723
+ })
6724
+ );
6725
+ }
6726
+ each(items, (item) => {
6727
+ const scaleOptions = item.options;
6728
+ const id = scaleOptions.id;
6729
+ const axis = determineAxis(id, scaleOptions);
6730
+ const scaleType = valueOrDefault(scaleOptions.type, item.dtype);
6731
+ if (scaleOptions.position === undefined || positionIsHorizontal(scaleOptions.position, axis) !== positionIsHorizontal(item.dposition)) {
6732
+ scaleOptions.position = item.dposition;
6733
+ }
6734
+ updated[id] = true;
6735
+ let scale = null;
6736
+ if (id in scales && scales[id].type === scaleType) {
6737
+ scale = scales[id];
6738
+ } else {
6739
+ const scaleClass = registry.getScale(scaleType);
6740
+ scale = new scaleClass({
6741
+ id,
6742
+ type: scaleType,
6743
+ ctx: me.ctx,
6744
+ chart: me
6745
+ });
6746
+ scales[scale.id] = scale;
6747
+ }
6748
+ scale.init(scaleOptions, options);
6749
+ });
6750
+ each(updated, (hasUpdated, id) => {
6751
+ if (!hasUpdated) {
6752
+ delete scales[id];
6753
+ }
6754
+ });
6755
+ each(scales, (scale) => {
6756
+ layouts.configure(me, scale, scale.options);
6757
+ layouts.addBox(me, scale);
6758
+ });
6759
+ }
6760
+ _updateMetasets() {
6761
+ const me = this;
6762
+ const metasets = me._metasets;
6763
+ const numData = me.data.datasets.length;
6764
+ const numMeta = metasets.length;
6765
+ metasets.sort((a, b) => a.index - b.index);
6766
+ if (numMeta > numData) {
6767
+ for (let i = numData; i < numMeta; ++i) {
6768
+ me._destroyDatasetMeta(i);
6769
+ }
6770
+ metasets.splice(numData, numMeta - numData);
6771
+ }
6772
+ me._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
6773
+ }
6774
+ _removeUnreferencedMetasets() {
6775
+ const me = this;
6776
+ const {_metasets: metasets, data: {datasets}} = me;
6777
+ if (metasets.length > datasets.length) {
6778
+ delete me._stacks;
6779
+ }
6780
+ metasets.forEach((meta, index) => {
6781
+ if (datasets.filter(x => x === meta._dataset).length === 0) {
6782
+ me._destroyDatasetMeta(index);
6783
+ }
6784
+ });
6785
+ }
6786
+ buildOrUpdateControllers() {
6787
+ const me = this;
6788
+ const newControllers = [];
6789
+ const datasets = me.data.datasets;
6790
+ let i, ilen;
6791
+ me._removeUnreferencedMetasets();
6792
+ for (i = 0, ilen = datasets.length; i < ilen; i++) {
6793
+ const dataset = datasets[i];
6794
+ let meta = me.getDatasetMeta(i);
6795
+ const type = dataset.type || me.config.type;
6796
+ if (meta.type && meta.type !== type) {
6797
+ me._destroyDatasetMeta(i);
6798
+ meta = me.getDatasetMeta(i);
6799
+ }
6800
+ meta.type = type;
6801
+ meta.indexAxis = dataset.indexAxis || getIndexAxis(type, me.options);
6802
+ meta.order = dataset.order || 0;
6803
+ meta.index = i;
6804
+ meta.label = '' + dataset.label;
6805
+ meta.visible = me.isDatasetVisible(i);
6806
+ if (meta.controller) {
6807
+ meta.controller.updateIndex(i);
6808
+ meta.controller.linkScales();
6809
+ } else {
6810
+ const ControllerClass = registry.getController(type);
6811
+ const {datasetElementType, dataElementType} = defaults.datasets[type];
6812
+ Object.assign(ControllerClass.prototype, {
6813
+ dataElementType: registry.getElement(dataElementType),
6814
+ datasetElementType: datasetElementType && registry.getElement(datasetElementType)
6815
+ });
6816
+ meta.controller = new ControllerClass(me, i);
6817
+ newControllers.push(meta.controller);
6818
+ }
6819
+ }
6820
+ me._updateMetasets();
6821
+ return newControllers;
6822
+ }
6823
+ _resetElements() {
6824
+ const me = this;
6825
+ each(me.data.datasets, (dataset, datasetIndex) => {
6826
+ me.getDatasetMeta(datasetIndex).controller.reset();
6827
+ }, me);
6828
+ }
6829
+ reset() {
6830
+ this._resetElements();
6831
+ this.notifyPlugins('reset');
6832
+ }
6833
+ update(mode) {
6834
+ const me = this;
6835
+ const config = me.config;
6836
+ config.update();
6837
+ me._options = config.createResolver(config.chartOptionScopes(), me.getContext());
6838
+ each(me.scales, (scale) => {
6839
+ layouts.removeBox(me, scale);
6840
+ });
6841
+ const animsDisabled = me._animationsDisabled = !me.options.animation;
6842
+ me.ensureScalesHaveIDs();
6843
+ me.buildOrUpdateScales();
6844
+ const existingEvents = new Set(Object.keys(me._listeners));
6845
+ const newEvents = new Set(me.options.events);
6846
+ if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== me.options.responsive) {
6847
+ me.unbindEvents();
6848
+ me.bindEvents();
6849
+ }
6850
+ me._plugins.invalidate();
6851
+ if (me.notifyPlugins('beforeUpdate', {mode, cancelable: true}) === false) {
6852
+ return;
6853
+ }
6854
+ const newControllers = me.buildOrUpdateControllers();
6855
+ me.notifyPlugins('beforeElementsUpdate');
6856
+ let minPadding = 0;
6857
+ for (let i = 0, ilen = me.data.datasets.length; i < ilen; i++) {
6858
+ const {controller} = me.getDatasetMeta(i);
6859
+ const reset = !animsDisabled && newControllers.indexOf(controller) === -1;
6860
+ controller.buildOrUpdateElements(reset);
6861
+ minPadding = Math.max(+controller.getMaxOverflow(), minPadding);
6862
+ }
6863
+ me._minPadding = minPadding;
6864
+ me._updateLayout(minPadding);
6865
+ if (!animsDisabled) {
6866
+ each(newControllers, (controller) => {
6867
+ controller.reset();
6868
+ });
6869
+ }
6870
+ me._updateDatasets(mode);
6871
+ me.notifyPlugins('afterUpdate', {mode});
6872
+ me._layers.sort(compare2Level('z', '_idx'));
6873
+ if (me._lastEvent) {
6874
+ me._eventHandler(me._lastEvent, true);
6875
+ }
6876
+ me.render();
6877
+ }
6878
+ _updateLayout(minPadding) {
6879
+ const me = this;
6880
+ if (me.notifyPlugins('beforeLayout', {cancelable: true}) === false) {
6881
+ return;
6882
+ }
6883
+ layouts.update(me, me.width, me.height, minPadding);
6884
+ const area = me.chartArea;
6885
+ const noArea = area.width <= 0 || area.height <= 0;
6886
+ me._layers = [];
6887
+ each(me.boxes, (box) => {
6888
+ if (noArea && box.position === 'chartArea') {
6889
+ return;
6890
+ }
6891
+ if (box.configure) {
6892
+ box.configure();
6893
+ }
6894
+ me._layers.push(...box._layers());
6895
+ }, me);
6896
+ me._layers.forEach((item, index) => {
6897
+ item._idx = index;
6898
+ });
6899
+ me.notifyPlugins('afterLayout');
6900
+ }
6901
+ _updateDatasets(mode) {
6902
+ const me = this;
6903
+ const isFunction = typeof mode === 'function';
6904
+ if (me.notifyPlugins('beforeDatasetsUpdate', {mode, cancelable: true}) === false) {
6905
+ return;
6906
+ }
6907
+ for (let i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
6908
+ me._updateDataset(i, isFunction ? mode({datasetIndex: i}) : mode);
6909
+ }
6910
+ me.notifyPlugins('afterDatasetsUpdate', {mode});
6911
+ }
6912
+ _updateDataset(index, mode) {
6913
+ const me = this;
6914
+ const meta = me.getDatasetMeta(index);
6915
+ const args = {meta, index, mode, cancelable: true};
6916
+ if (me.notifyPlugins('beforeDatasetUpdate', args) === false) {
6917
+ return;
6918
+ }
6919
+ meta.controller._update(mode);
6920
+ args.cancelable = false;
6921
+ me.notifyPlugins('afterDatasetUpdate', args);
6922
+ }
6923
+ render() {
6924
+ const me = this;
6925
+ if (me.notifyPlugins('beforeRender', {cancelable: true}) === false) {
6926
+ return;
6927
+ }
6928
+ if (animator.has(me)) {
6929
+ if (me.attached && !animator.running(me)) {
6930
+ animator.start(me);
6931
+ }
6932
+ } else {
6933
+ me.draw();
6934
+ onAnimationsComplete({chart: me});
6935
+ }
6936
+ }
6937
+ draw() {
6938
+ const me = this;
6939
+ let i;
6940
+ if (me._resizeBeforeDraw) {
6941
+ const {width, height} = me._resizeBeforeDraw;
6942
+ me._resize(width, height);
6943
+ me._resizeBeforeDraw = null;
6944
+ }
6945
+ me.clear();
6946
+ if (me.width <= 0 || me.height <= 0) {
6947
+ return;
6948
+ }
6949
+ if (me.notifyPlugins('beforeDraw', {cancelable: true}) === false) {
6950
+ return;
6951
+ }
6952
+ const layers = me._layers;
6953
+ for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
6954
+ layers[i].draw(me.chartArea);
6955
+ }
6956
+ me._drawDatasets();
6957
+ for (; i < layers.length; ++i) {
6958
+ layers[i].draw(me.chartArea);
6959
+ }
6960
+ me.notifyPlugins('afterDraw');
6961
+ }
6962
+ _getSortedDatasetMetas(filterVisible) {
6963
+ const me = this;
6964
+ const metasets = me._sortedMetasets;
6965
+ const result = [];
6966
+ let i, ilen;
6967
+ for (i = 0, ilen = metasets.length; i < ilen; ++i) {
6968
+ const meta = metasets[i];
6969
+ if (!filterVisible || meta.visible) {
6970
+ result.push(meta);
6971
+ }
6972
+ }
6973
+ return result;
6974
+ }
6975
+ getSortedVisibleDatasetMetas() {
6976
+ return this._getSortedDatasetMetas(true);
6977
+ }
6978
+ _drawDatasets() {
6979
+ const me = this;
6980
+ if (me.notifyPlugins('beforeDatasetsDraw', {cancelable: true}) === false) {
6981
+ return;
6982
+ }
6983
+ const metasets = me.getSortedVisibleDatasetMetas();
6984
+ for (let i = metasets.length - 1; i >= 0; --i) {
6985
+ me._drawDataset(metasets[i]);
6986
+ }
6987
+ me.notifyPlugins('afterDatasetsDraw');
6988
+ }
6989
+ _drawDataset(meta) {
6990
+ const me = this;
6991
+ const ctx = me.ctx;
6992
+ const clip = meta._clip;
6993
+ const useClip = !clip.disabled;
6994
+ const area = me.chartArea;
6995
+ const args = {
6996
+ meta,
6997
+ index: meta.index,
6998
+ cancelable: true
6999
+ };
7000
+ if (me.notifyPlugins('beforeDatasetDraw', args) === false) {
7001
+ return;
7002
+ }
7003
+ if (useClip) {
7004
+ clipArea(ctx, {
7005
+ left: clip.left === false ? 0 : area.left - clip.left,
7006
+ right: clip.right === false ? me.width : area.right + clip.right,
7007
+ top: clip.top === false ? 0 : area.top - clip.top,
7008
+ bottom: clip.bottom === false ? me.height : area.bottom + clip.bottom
7009
+ });
7010
+ }
7011
+ meta.controller.draw();
7012
+ if (useClip) {
7013
+ unclipArea(ctx);
7014
+ }
7015
+ args.cancelable = false;
7016
+ me.notifyPlugins('afterDatasetDraw', args);
7017
+ }
7018
+ getElementsAtEventForMode(e, mode, options, useFinalPosition) {
7019
+ const method = Interaction.modes[mode];
7020
+ if (typeof method === 'function') {
7021
+ return method(this, e, options, useFinalPosition);
7022
+ }
7023
+ return [];
7024
+ }
7025
+ getDatasetMeta(datasetIndex) {
7026
+ const me = this;
7027
+ const dataset = me.data.datasets[datasetIndex];
7028
+ const metasets = me._metasets;
7029
+ let meta = metasets.filter(x => x && x._dataset === dataset).pop();
7030
+ if (!meta) {
7031
+ meta = {
7032
+ type: null,
7033
+ data: [],
7034
+ dataset: null,
7035
+ controller: null,
7036
+ hidden: null,
7037
+ xAxisID: null,
7038
+ yAxisID: null,
7039
+ order: dataset && dataset.order || 0,
7040
+ index: datasetIndex,
7041
+ _dataset: dataset,
7042
+ _parsed: [],
7043
+ _sorted: false
7044
+ };
7045
+ metasets.push(meta);
7046
+ }
7047
+ return meta;
7048
+ }
7049
+ getContext() {
7050
+ return this.$context || (this.$context = {chart: this, type: 'chart'});
7051
+ }
7052
+ getVisibleDatasetCount() {
7053
+ return this.getSortedVisibleDatasetMetas().length;
7054
+ }
7055
+ isDatasetVisible(datasetIndex) {
7056
+ const dataset = this.data.datasets[datasetIndex];
7057
+ if (!dataset) {
7058
+ return false;
7059
+ }
7060
+ const meta = this.getDatasetMeta(datasetIndex);
7061
+ return typeof meta.hidden === 'boolean' ? !meta.hidden : !dataset.hidden;
7062
+ }
7063
+ setDatasetVisibility(datasetIndex, visible) {
7064
+ const meta = this.getDatasetMeta(datasetIndex);
7065
+ meta.hidden = !visible;
7066
+ }
7067
+ toggleDataVisibility(index) {
7068
+ this._hiddenIndices[index] = !this._hiddenIndices[index];
7069
+ }
7070
+ getDataVisibility(index) {
7071
+ return !this._hiddenIndices[index];
7072
+ }
7073
+ _updateVisibility(datasetIndex, dataIndex, visible) {
7074
+ const me = this;
7075
+ const mode = visible ? 'show' : 'hide';
7076
+ const meta = me.getDatasetMeta(datasetIndex);
7077
+ const anims = meta.controller._resolveAnimations(undefined, mode);
7078
+ if (defined(dataIndex)) {
7079
+ meta.data[dataIndex].hidden = !visible;
7080
+ me.update();
7081
+ } else {
7082
+ me.setDatasetVisibility(datasetIndex, visible);
7083
+ anims.update(meta, {visible});
7084
+ me.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : undefined);
7085
+ }
7086
+ }
7087
+ hide(datasetIndex, dataIndex) {
7088
+ this._updateVisibility(datasetIndex, dataIndex, false);
7089
+ }
7090
+ show(datasetIndex, dataIndex) {
7091
+ this._updateVisibility(datasetIndex, dataIndex, true);
7092
+ }
7093
+ _destroyDatasetMeta(datasetIndex) {
7094
+ const me = this;
7095
+ const meta = me._metasets && me._metasets[datasetIndex];
7096
+ if (meta && meta.controller) {
7097
+ meta.controller._destroy();
7098
+ delete me._metasets[datasetIndex];
7099
+ }
7100
+ }
7101
+ _stop() {
7102
+ const me = this;
7103
+ let i, ilen;
7104
+ me.stop();
7105
+ animator.remove(me);
7106
+ for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
7107
+ me._destroyDatasetMeta(i);
7108
+ }
7109
+ }
7110
+ destroy() {
7111
+ const me = this;
7112
+ const {canvas, ctx} = me;
7113
+ me._stop();
7114
+ me.config.clearCache();
7115
+ if (canvas) {
7116
+ me.unbindEvents();
7117
+ clearCanvas(canvas, ctx);
7118
+ me.platform.releaseContext(ctx);
7119
+ me.canvas = null;
7120
+ me.ctx = null;
7121
+ }
7122
+ me.notifyPlugins('destroy');
7123
+ delete instances[me.id];
7124
+ }
7125
+ toBase64Image(...args) {
7126
+ return this.canvas.toDataURL(...args);
7127
+ }
7128
+ bindEvents() {
7129
+ this.bindUserEvents();
7130
+ if (this.options.responsive) {
7131
+ this.bindResponsiveEvents();
7132
+ } else {
7133
+ this.attached = true;
7134
+ }
7135
+ }
7136
+ bindUserEvents() {
7137
+ const me = this;
7138
+ const listeners = me._listeners;
7139
+ const platform = me.platform;
7140
+ const _add = (type, listener) => {
7141
+ platform.addEventListener(me, type, listener);
7142
+ listeners[type] = listener;
7143
+ };
7144
+ const listener = function(e, x, y) {
7145
+ e.offsetX = x;
7146
+ e.offsetY = y;
7147
+ me._eventHandler(e);
7148
+ };
7149
+ each(me.options.events, (type) => _add(type, listener));
7150
+ }
7151
+ bindResponsiveEvents() {
7152
+ const me = this;
7153
+ if (!me._responsiveListeners) {
7154
+ me._responsiveListeners = {};
7155
+ }
7156
+ const listeners = me._responsiveListeners;
7157
+ const platform = me.platform;
7158
+ const _add = (type, listener) => {
7159
+ platform.addEventListener(me, type, listener);
7160
+ listeners[type] = listener;
7161
+ };
7162
+ const _remove = (type, listener) => {
7163
+ if (listeners[type]) {
7164
+ platform.removeEventListener(me, type, listener);
7165
+ delete listeners[type];
7166
+ }
7167
+ };
7168
+ const listener = (width, height) => {
7169
+ if (me.canvas) {
7170
+ me.resize(width, height);
7171
+ }
7172
+ };
7173
+ let detached;
7174
+ const attached = () => {
7175
+ _remove('attach', attached);
7176
+ me.attached = true;
7177
+ me.resize();
7178
+ _add('resize', listener);
7179
+ _add('detach', detached);
7180
+ };
7181
+ detached = () => {
7182
+ me.attached = false;
7183
+ _remove('resize', listener);
7184
+ me._stop();
7185
+ me._resize(0, 0);
7186
+ _add('attach', attached);
7187
+ };
7188
+ if (platform.isAttached(me.canvas)) {
7189
+ attached();
7190
+ } else {
7191
+ detached();
7192
+ }
7193
+ }
7194
+ unbindEvents() {
7195
+ const me = this;
7196
+ each(me._listeners, (listener, type) => {
7197
+ me.platform.removeEventListener(me, type, listener);
7198
+ });
7199
+ me._listeners = {};
7200
+ each(me._responsiveListeners, (listener, type) => {
7201
+ me.platform.removeEventListener(me, type, listener);
7202
+ });
7203
+ me._responsiveListeners = undefined;
7204
+ }
7205
+ updateHoverStyle(items, mode, enabled) {
7206
+ const prefix = enabled ? 'set' : 'remove';
7207
+ let meta, item, i, ilen;
7208
+ if (mode === 'dataset') {
7209
+ meta = this.getDatasetMeta(items[0].datasetIndex);
7210
+ meta.controller['_' + prefix + 'DatasetHoverStyle']();
7211
+ }
7212
+ for (i = 0, ilen = items.length; i < ilen; ++i) {
7213
+ item = items[i];
7214
+ const controller = item && this.getDatasetMeta(item.datasetIndex).controller;
7215
+ if (controller) {
7216
+ controller[prefix + 'HoverStyle'](item.element, item.datasetIndex, item.index);
7217
+ }
7218
+ }
7219
+ }
7220
+ getActiveElements() {
7221
+ return this._active || [];
7222
+ }
7223
+ setActiveElements(activeElements) {
7224
+ const me = this;
7225
+ const lastActive = me._active || [];
7226
+ const active = activeElements.map(({datasetIndex, index}) => {
7227
+ const meta = me.getDatasetMeta(datasetIndex);
7228
+ if (!meta) {
7229
+ throw new Error('No dataset found at index ' + datasetIndex);
7230
+ }
7231
+ return {
7232
+ datasetIndex,
7233
+ element: meta.data[index],
7234
+ index,
7235
+ };
7236
+ });
7237
+ const changed = !_elementsEqual(active, lastActive);
7238
+ if (changed) {
7239
+ me._active = active;
7240
+ me._updateHoverStyles(active, lastActive);
7241
+ }
7242
+ }
7243
+ notifyPlugins(hook, args, filter) {
7244
+ return this._plugins.notify(this, hook, args, filter);
7245
+ }
7246
+ _updateHoverStyles(active, lastActive, replay) {
7247
+ const me = this;
7248
+ const hoverOptions = me.options.hover;
7249
+ const diff = (a, b) => a.filter(x => !b.some(y => x.datasetIndex === y.datasetIndex && x.index === y.index));
7250
+ const deactivated = diff(lastActive, active);
7251
+ const activated = replay ? active : diff(active, lastActive);
7252
+ if (deactivated.length) {
7253
+ me.updateHoverStyle(deactivated, hoverOptions.mode, false);
7254
+ }
7255
+ if (activated.length && hoverOptions.mode) {
7256
+ me.updateHoverStyle(activated, hoverOptions.mode, true);
7257
+ }
7258
+ }
7259
+ _eventHandler(e, replay) {
7260
+ const me = this;
7261
+ const args = {event: e, replay, cancelable: true};
7262
+ const eventFilter = (plugin) => (plugin.options.events || this.options.events).includes(e.type);
7263
+ if (me.notifyPlugins('beforeEvent', args, eventFilter) === false) {
7264
+ return;
7265
+ }
7266
+ const changed = me._handleEvent(e, replay);
7267
+ args.cancelable = false;
7268
+ me.notifyPlugins('afterEvent', args, eventFilter);
7269
+ if (changed || args.changed) {
7270
+ me.render();
7271
+ }
7272
+ return me;
7273
+ }
7274
+ _handleEvent(e, replay) {
7275
+ const me = this;
7276
+ const {_active: lastActive = [], options} = me;
7277
+ const hoverOptions = options.hover;
7278
+ const useFinalPosition = replay;
7279
+ let active = [];
7280
+ let changed = false;
7281
+ let lastEvent = null;
7282
+ if (e.type !== 'mouseout') {
7283
+ active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
7284
+ lastEvent = e.type === 'click' ? me._lastEvent : e;
7285
+ }
7286
+ me._lastEvent = null;
7287
+ if (_isPointInArea(e, me.chartArea, me._minPadding)) {
7288
+ callback(options.onHover, [e, active, me], me);
7289
+ if (e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu') {
7290
+ callback(options.onClick, [e, active, me], me);
7291
+ }
7292
+ }
7293
+ changed = !_elementsEqual(active, lastActive);
7294
+ if (changed || replay) {
7295
+ me._active = active;
7296
+ me._updateHoverStyles(active, lastActive, replay);
7297
+ }
7298
+ me._lastEvent = lastEvent;
7299
+ return changed;
7300
+ }
7301
+ }
7302
+ const invalidatePlugins = () => each(Chart.instances, (chart) => chart._plugins.invalidate());
7303
+ const enumerable = true;
7304
+ Object.defineProperties(Chart, {
7305
+ defaults: {
7306
+ enumerable,
7307
+ value: defaults
7308
+ },
7309
+ instances: {
7310
+ enumerable,
7311
+ value: instances
7312
+ },
7313
+ overrides: {
7314
+ enumerable,
7315
+ value: overrides
7316
+ },
7317
+ registry: {
7318
+ enumerable,
7319
+ value: registry
7320
+ },
7321
+ version: {
7322
+ enumerable,
7323
+ value: version
7324
+ },
7325
+ getChart: {
7326
+ enumerable,
7327
+ value: getChart
7328
+ },
7329
+ register: {
7330
+ enumerable,
7331
+ value: (...items) => {
7332
+ registry.add(...items);
7333
+ invalidatePlugins();
7334
+ }
7335
+ },
7336
+ unregister: {
7337
+ enumerable,
7338
+ value: (...items) => {
7339
+ registry.remove(...items);
7340
+ invalidatePlugins();
7341
+ }
7342
+ }
7343
+ });
7344
+
7345
+ function abstract() {
7346
+ throw new Error('This method is not implemented: Check that a complete date adapter is provided.');
7347
+ }
7348
+ class DateAdapter {
7349
+ constructor(options) {
7350
+ this.options = options || {};
7351
+ }
7352
+ formats() {
7353
+ return abstract();
7354
+ }
7355
+ parse(value, format) {
7356
+ return abstract();
7357
+ }
7358
+ format(timestamp, format) {
7359
+ return abstract();
7360
+ }
7361
+ add(timestamp, amount, unit) {
7362
+ return abstract();
7363
+ }
7364
+ diff(a, b, unit) {
7365
+ return abstract();
7366
+ }
7367
+ startOf(timestamp, unit, weekday) {
7368
+ return abstract();
7369
+ }
7370
+ endOf(timestamp, unit) {
7371
+ return abstract();
7372
+ }
7373
+ }
7374
+ DateAdapter.override = function(members) {
7375
+ Object.assign(DateAdapter.prototype, members);
7376
+ };
7377
+ var _adapters = {
7378
+ _date: DateAdapter
7379
+ };
7380
+
7381
+ function getAllScaleValues(scale) {
7382
+ if (!scale._cache.$bar) {
7383
+ const metas = scale.getMatchingVisibleMetas('bar');
7384
+ let values = [];
7385
+ for (let i = 0, ilen = metas.length; i < ilen; i++) {
7386
+ values = values.concat(metas[i].controller.getAllParsedValues(scale));
7387
+ }
7388
+ scale._cache.$bar = _arrayUnique(values.sort((a, b) => a - b));
7389
+ }
7390
+ return scale._cache.$bar;
7391
+ }
7392
+ function computeMinSampleSize(scale) {
7393
+ const values = getAllScaleValues(scale);
7394
+ let min = scale._length;
7395
+ let i, ilen, curr, prev;
7396
+ const updateMinAndPrev = () => {
7397
+ if (curr === 32767 || curr === -32768) {
7398
+ return;
7399
+ }
7400
+ if (defined(prev)) {
7401
+ min = Math.min(min, Math.abs(curr - prev) || min);
7402
+ }
7403
+ prev = curr;
7404
+ };
7405
+ for (i = 0, ilen = values.length; i < ilen; ++i) {
7406
+ curr = scale.getPixelForValue(values[i]);
7407
+ updateMinAndPrev();
7408
+ }
7409
+ prev = undefined;
7410
+ for (i = 0, ilen = scale.ticks.length; i < ilen; ++i) {
7411
+ curr = scale.getPixelForTick(i);
7412
+ updateMinAndPrev();
7413
+ }
7414
+ return min;
7415
+ }
7416
+ function computeFitCategoryTraits(index, ruler, options, stackCount) {
7417
+ const thickness = options.barThickness;
7418
+ let size, ratio;
7419
+ if (isNullOrUndef(thickness)) {
7420
+ size = ruler.min * options.categoryPercentage;
7421
+ ratio = options.barPercentage;
7422
+ } else {
7423
+ size = thickness * stackCount;
7424
+ ratio = 1;
7425
+ }
7426
+ return {
7427
+ chunk: size / stackCount,
7428
+ ratio,
7429
+ start: ruler.pixels[index] - (size / 2)
7430
+ };
7431
+ }
7432
+ function computeFlexCategoryTraits(index, ruler, options, stackCount) {
7433
+ const pixels = ruler.pixels;
7434
+ const curr = pixels[index];
7435
+ let prev = index > 0 ? pixels[index - 1] : null;
7436
+ let next = index < pixels.length - 1 ? pixels[index + 1] : null;
7437
+ const percent = options.categoryPercentage;
7438
+ if (prev === null) {
7439
+ prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
7440
+ }
7441
+ if (next === null) {
7442
+ next = curr + curr - prev;
7443
+ }
7444
+ const start = curr - (curr - Math.min(prev, next)) / 2 * percent;
7445
+ const size = Math.abs(next - prev) / 2 * percent;
7446
+ return {
7447
+ chunk: size / stackCount,
7448
+ ratio: options.barPercentage,
7449
+ start
7450
+ };
7451
+ }
7452
+ function parseFloatBar(entry, item, vScale, i) {
7453
+ const startValue = vScale.parse(entry[0], i);
7454
+ const endValue = vScale.parse(entry[1], i);
7455
+ const min = Math.min(startValue, endValue);
7456
+ const max = Math.max(startValue, endValue);
7457
+ let barStart = min;
7458
+ let barEnd = max;
7459
+ if (Math.abs(min) > Math.abs(max)) {
7460
+ barStart = max;
7461
+ barEnd = min;
7462
+ }
7463
+ item[vScale.axis] = barEnd;
7464
+ item._custom = {
7465
+ barStart,
7466
+ barEnd,
7467
+ start: startValue,
7468
+ end: endValue,
7469
+ min,
7470
+ max
7471
+ };
7472
+ }
7473
+ function parseValue(entry, item, vScale, i) {
7474
+ if (isArray(entry)) {
7475
+ parseFloatBar(entry, item, vScale, i);
7476
+ } else {
7477
+ item[vScale.axis] = vScale.parse(entry, i);
7478
+ }
7479
+ return item;
7480
+ }
7481
+ function parseArrayOrPrimitive(meta, data, start, count) {
7482
+ const iScale = meta.iScale;
7483
+ const vScale = meta.vScale;
7484
+ const labels = iScale.getLabels();
7485
+ const singleScale = iScale === vScale;
7486
+ const parsed = [];
7487
+ let i, ilen, item, entry;
7488
+ for (i = start, ilen = start + count; i < ilen; ++i) {
7489
+ entry = data[i];
7490
+ item = {};
7491
+ item[iScale.axis] = singleScale || iScale.parse(labels[i], i);
7492
+ parsed.push(parseValue(entry, item, vScale, i));
7493
+ }
7494
+ return parsed;
7495
+ }
7496
+ function isFloatBar(custom) {
7497
+ return custom && custom.barStart !== undefined && custom.barEnd !== undefined;
7498
+ }
7499
+ function barSign(size, vScale, actualBase) {
7500
+ if (size !== 0) {
7501
+ return sign(size);
7502
+ }
7503
+ return (vScale.isHorizontal() ? 1 : -1) * (vScale.min >= actualBase ? 1 : -1);
7504
+ }
7505
+ function borderProps(properties) {
7506
+ let reverse, start, end, top, bottom;
7507
+ if (properties.horizontal) {
7508
+ reverse = properties.base > properties.x;
7509
+ start = 'left';
7510
+ end = 'right';
7511
+ } else {
7512
+ reverse = properties.base < properties.y;
7513
+ start = 'bottom';
7514
+ end = 'top';
7515
+ }
7516
+ if (reverse) {
7517
+ top = 'end';
7518
+ bottom = 'start';
7519
+ } else {
7520
+ top = 'start';
7521
+ bottom = 'end';
7522
+ }
7523
+ return {start, end, reverse, top, bottom};
7524
+ }
7525
+ function setBorderSkipped(properties, options, stack, index) {
7526
+ let edge = options.borderSkipped;
7527
+ const res = {};
7528
+ if (!edge) {
7529
+ properties.borderSkipped = res;
7530
+ return;
7531
+ }
7532
+ const {start, end, reverse, top, bottom} = borderProps(properties);
7533
+ if (edge === 'middle' && stack) {
7534
+ properties.enableBorderRadius = true;
7535
+ if ((stack._top || 0) === index) {
7536
+ edge = top;
7537
+ } else if ((stack._bottom || 0) === index) {
7538
+ edge = bottom;
7539
+ } else {
7540
+ res[parseEdge(bottom, start, end, reverse)] = true;
7541
+ edge = top;
7542
+ }
7543
+ }
7544
+ res[parseEdge(edge, start, end, reverse)] = true;
7545
+ properties.borderSkipped = res;
7546
+ }
7547
+ function parseEdge(edge, a, b, reverse) {
7548
+ if (reverse) {
7549
+ edge = swap(edge, a, b);
7550
+ edge = startEnd(edge, b, a);
7551
+ } else {
7552
+ edge = startEnd(edge, a, b);
7553
+ }
7554
+ return edge;
7555
+ }
7556
+ function swap(orig, v1, v2) {
7557
+ return orig === v1 ? v2 : orig === v2 ? v1 : orig;
7558
+ }
7559
+ function startEnd(v, start, end) {
7560
+ return v === 'start' ? start : v === 'end' ? end : v;
7561
+ }
7562
+ class BarController extends DatasetController {
7563
+ parsePrimitiveData(meta, data, start, count) {
7564
+ return parseArrayOrPrimitive(meta, data, start, count);
7565
+ }
7566
+ parseArrayData(meta, data, start, count) {
7567
+ return parseArrayOrPrimitive(meta, data, start, count);
7568
+ }
7569
+ parseObjectData(meta, data, start, count) {
7570
+ const {iScale, vScale} = meta;
7571
+ const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing;
7572
+ const iAxisKey = iScale.axis === 'x' ? xAxisKey : yAxisKey;
7573
+ const vAxisKey = vScale.axis === 'x' ? xAxisKey : yAxisKey;
7574
+ const parsed = [];
7575
+ let i, ilen, item, obj;
7576
+ for (i = start, ilen = start + count; i < ilen; ++i) {
7577
+ obj = data[i];
7578
+ item = {};
7579
+ item[iScale.axis] = iScale.parse(resolveObjectKey(obj, iAxisKey), i);
7580
+ parsed.push(parseValue(resolveObjectKey(obj, vAxisKey), item, vScale, i));
7581
+ }
7582
+ return parsed;
7583
+ }
7584
+ updateRangeFromParsed(range, scale, parsed, stack) {
7585
+ super.updateRangeFromParsed(range, scale, parsed, stack);
7586
+ const custom = parsed._custom;
7587
+ if (custom && scale === this._cachedMeta.vScale) {
7588
+ range.min = Math.min(range.min, custom.min);
7589
+ range.max = Math.max(range.max, custom.max);
7590
+ }
7591
+ }
7592
+ getMaxOverflow() {
7593
+ return 0;
7594
+ }
7595
+ getLabelAndValue(index) {
7596
+ const me = this;
7597
+ const meta = me._cachedMeta;
7598
+ const {iScale, vScale} = meta;
7599
+ const parsed = me.getParsed(index);
7600
+ const custom = parsed._custom;
7601
+ const value = isFloatBar(custom)
7602
+ ? '[' + custom.start + ', ' + custom.end + ']'
7603
+ : '' + vScale.getLabelForValue(parsed[vScale.axis]);
7604
+ return {
7605
+ label: '' + iScale.getLabelForValue(parsed[iScale.axis]),
7606
+ value
7607
+ };
7608
+ }
7609
+ initialize() {
7610
+ const me = this;
7611
+ me.enableOptionSharing = true;
7612
+ super.initialize();
7613
+ const meta = me._cachedMeta;
7614
+ meta.stack = me.getDataset().stack;
7615
+ }
7616
+ update(mode) {
7617
+ const me = this;
7618
+ const meta = me._cachedMeta;
7619
+ me.updateElements(meta.data, 0, meta.data.length, mode);
7620
+ }
7621
+ updateElements(bars, start, count, mode) {
7622
+ const me = this;
7623
+ const reset = mode === 'reset';
7624
+ const {index, _cachedMeta: {vScale}} = me;
7625
+ const base = vScale.getBasePixel();
7626
+ const horizontal = vScale.isHorizontal();
7627
+ const ruler = me._getRuler();
7628
+ const firstOpts = me.resolveDataElementOptions(start, mode);
7629
+ const sharedOptions = me.getSharedOptions(firstOpts);
7630
+ const includeOptions = me.includeOptions(mode, sharedOptions);
7631
+ me.updateSharedOptions(sharedOptions, mode, firstOpts);
7632
+ for (let i = start; i < start + count; i++) {
7633
+ const parsed = me.getParsed(i);
7634
+ const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : me._calculateBarValuePixels(i);
7635
+ const ipixels = me._calculateBarIndexPixels(i, ruler);
7636
+ const stack = (parsed._stacks || {})[vScale.axis];
7637
+ const properties = {
7638
+ horizontal,
7639
+ base: vpixels.base,
7640
+ enableBorderRadius: !stack || isFloatBar(parsed._custom) || (index === stack._top || index === stack._bottom),
7641
+ x: horizontal ? vpixels.head : ipixels.center,
7642
+ y: horizontal ? ipixels.center : vpixels.head,
7643
+ height: horizontal ? ipixels.size : Math.abs(vpixels.size),
7644
+ width: horizontal ? Math.abs(vpixels.size) : ipixels.size
7645
+ };
7646
+ if (includeOptions) {
7647
+ properties.options = sharedOptions || me.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
7648
+ }
7649
+ setBorderSkipped(properties, properties.options || bars[i].options, stack, index);
7650
+ me.updateElement(bars[i], i, properties, mode);
7651
+ }
7652
+ }
7653
+ _getStacks(last, dataIndex) {
7654
+ const me = this;
7655
+ const meta = me._cachedMeta;
7656
+ const iScale = meta.iScale;
7657
+ const metasets = iScale.getMatchingVisibleMetas(me._type);
7658
+ const stacked = iScale.options.stacked;
7659
+ const ilen = metasets.length;
7660
+ const stacks = [];
7661
+ let i, item;
7662
+ for (i = 0; i < ilen; ++i) {
7663
+ item = metasets[i];
7664
+ if (!item.controller.options.grouped) {
7665
+ continue;
7666
+ }
7667
+ if (typeof dataIndex !== 'undefined') {
7668
+ const val = item.controller.getParsed(dataIndex)[
7669
+ item.controller._cachedMeta.vScale.axis
7670
+ ];
7671
+ if (isNullOrUndef(val) || isNaN(val)) {
7672
+ continue;
7673
+ }
7674
+ }
7675
+ if (stacked === false || stacks.indexOf(item.stack) === -1 ||
7676
+ (stacked === undefined && item.stack === undefined)) {
7677
+ stacks.push(item.stack);
7678
+ }
7679
+ if (item.index === last) {
7680
+ break;
7681
+ }
7682
+ }
7683
+ if (!stacks.length) {
7684
+ stacks.push(undefined);
7685
+ }
7686
+ return stacks;
7687
+ }
7688
+ _getStackCount(index) {
7689
+ return this._getStacks(undefined, index).length;
7690
+ }
7691
+ _getStackIndex(datasetIndex, name, dataIndex) {
7692
+ const stacks = this._getStacks(datasetIndex, dataIndex);
7693
+ const index = (name !== undefined)
7694
+ ? stacks.indexOf(name)
7695
+ : -1;
7696
+ return (index === -1)
7697
+ ? stacks.length - 1
7698
+ : index;
7699
+ }
7700
+ _getRuler() {
7701
+ const me = this;
7702
+ const opts = me.options;
7703
+ const meta = me._cachedMeta;
7704
+ const iScale = meta.iScale;
7705
+ const pixels = [];
7706
+ let i, ilen;
7707
+ for (i = 0, ilen = meta.data.length; i < ilen; ++i) {
7708
+ pixels.push(iScale.getPixelForValue(me.getParsed(i)[iScale.axis], i));
7709
+ }
7710
+ const barThickness = opts.barThickness;
7711
+ const min = barThickness || computeMinSampleSize(iScale);
7712
+ return {
7713
+ min,
7714
+ pixels,
7715
+ start: iScale._startPixel,
7716
+ end: iScale._endPixel,
7717
+ stackCount: me._getStackCount(),
7718
+ scale: iScale,
7719
+ grouped: opts.grouped,
7720
+ ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage
7721
+ };
7722
+ }
7723
+ _calculateBarValuePixels(index) {
7724
+ const me = this;
7725
+ const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = me;
7726
+ const actualBase = baseValue || 0;
7727
+ const parsed = me.getParsed(index);
7728
+ const custom = parsed._custom;
7729
+ const floating = isFloatBar(custom);
7730
+ let value = parsed[vScale.axis];
7731
+ let start = 0;
7732
+ let length = _stacked ? me.applyStack(vScale, parsed, _stacked) : value;
7733
+ let head, size;
7734
+ if (length !== value) {
7735
+ start = length - value;
7736
+ length = value;
7737
+ }
7738
+ if (floating) {
7739
+ value = custom.barStart;
7740
+ length = custom.barEnd - custom.barStart;
7741
+ if (value !== 0 && sign(value) !== sign(custom.barEnd)) {
7742
+ start = 0;
7743
+ }
7744
+ start += value;
7745
+ }
7746
+ const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;
7747
+ let base = vScale.getPixelForValue(startValue);
7748
+ if (me.chart.getDataVisibility(index)) {
7749
+ head = vScale.getPixelForValue(start + length);
7750
+ } else {
7751
+ head = base;
7752
+ }
7753
+ size = head - base;
7754
+ if (Math.abs(size) < minBarLength) {
7755
+ size = barSign(size, vScale, actualBase) * minBarLength;
7756
+ if (value === actualBase) {
7757
+ base -= size / 2;
7758
+ }
7759
+ head = base + size;
7760
+ }
7761
+ if (base === vScale.getPixelForValue(actualBase)) {
7762
+ const halfGrid = sign(size) * vScale.getLineWidthForValue(actualBase) / 2;
7763
+ base += halfGrid;
7764
+ size -= halfGrid;
7765
+ }
7766
+ return {
7767
+ size,
7768
+ base,
7769
+ head,
7770
+ center: head + size / 2
7771
+ };
7772
+ }
7773
+ _calculateBarIndexPixels(index, ruler) {
7774
+ const me = this;
7775
+ const scale = ruler.scale;
7776
+ const options = me.options;
7777
+ const skipNull = options.skipNull;
7778
+ const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);
7779
+ let center, size;
7780
+ if (ruler.grouped) {
7781
+ const stackCount = skipNull ? me._getStackCount(index) : ruler.stackCount;
7782
+ const range = options.barThickness === 'flex'
7783
+ ? computeFlexCategoryTraits(index, ruler, options, stackCount)
7784
+ : computeFitCategoryTraits(index, ruler, options, stackCount);
7785
+ const stackIndex = me._getStackIndex(me.index, me._cachedMeta.stack, skipNull ? index : undefined);
7786
+ center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
7787
+ size = Math.min(maxBarThickness, range.chunk * range.ratio);
7788
+ } else {
7789
+ center = scale.getPixelForValue(me.getParsed(index)[scale.axis], index);
7790
+ size = Math.min(maxBarThickness, ruler.min * ruler.ratio);
7791
+ }
7792
+ return {
7793
+ base: center - size / 2,
7794
+ head: center + size / 2,
7795
+ center,
7796
+ size
7797
+ };
7798
+ }
7799
+ draw() {
7800
+ const me = this;
7801
+ const meta = me._cachedMeta;
7802
+ const vScale = meta.vScale;
7803
+ const rects = meta.data;
7804
+ const ilen = rects.length;
7805
+ let i = 0;
7806
+ for (; i < ilen; ++i) {
7807
+ if (me.getParsed(i)[vScale.axis] !== null) {
7808
+ rects[i].draw(me._ctx);
7809
+ }
7810
+ }
7811
+ }
7812
+ }
7813
+ BarController.id = 'bar';
7814
+ BarController.defaults = {
7815
+ datasetElementType: false,
7816
+ dataElementType: 'bar',
7817
+ categoryPercentage: 0.8,
7818
+ barPercentage: 0.9,
7819
+ grouped: true,
7820
+ animations: {
7821
+ numbers: {
7822
+ type: 'number',
7823
+ properties: ['x', 'y', 'base', 'width', 'height']
7824
+ }
7825
+ }
7826
+ };
7827
+ BarController.overrides = {
7828
+ scales: {
7829
+ _index_: {
7830
+ type: 'category',
7831
+ offset: true,
7832
+ grid: {
7833
+ offset: true
7834
+ }
7835
+ },
7836
+ _value_: {
7837
+ type: 'linear',
7838
+ beginAtZero: true,
7839
+ }
7840
+ }
7841
+ };
7842
+
7843
+ class BubbleController extends DatasetController {
7844
+ initialize() {
7845
+ this.enableOptionSharing = true;
7846
+ super.initialize();
7847
+ }
7848
+ parseObjectData(meta, data, start, count) {
7849
+ const {xScale, yScale} = meta;
7850
+ const {xAxisKey = 'x', yAxisKey = 'y'} = this._parsing;
7851
+ const parsed = [];
7852
+ let i, ilen, item;
7853
+ for (i = start, ilen = start + count; i < ilen; ++i) {
7854
+ item = data[i];
7855
+ parsed.push({
7856
+ x: xScale.parse(resolveObjectKey(item, xAxisKey), i),
7857
+ y: yScale.parse(resolveObjectKey(item, yAxisKey), i),
7858
+ _custom: item && item.r && +item.r
7859
+ });
7860
+ }
7861
+ return parsed;
7862
+ }
7863
+ getMaxOverflow() {
7864
+ const {data, _parsed} = this._cachedMeta;
7865
+ let max = 0;
7866
+ for (let i = data.length - 1; i >= 0; --i) {
7867
+ max = Math.max(max, data[i].size() / 2, _parsed[i]._custom);
7868
+ }
7869
+ return max > 0 && max;
7870
+ }
7871
+ getLabelAndValue(index) {
7872
+ const me = this;
7873
+ const meta = me._cachedMeta;
7874
+ const {xScale, yScale} = meta;
7875
+ const parsed = me.getParsed(index);
7876
+ const x = xScale.getLabelForValue(parsed.x);
7877
+ const y = yScale.getLabelForValue(parsed.y);
7878
+ const r = parsed._custom;
7879
+ return {
7880
+ label: meta.label,
7881
+ value: '(' + x + ', ' + y + (r ? ', ' + r : '') + ')'
7882
+ };
7883
+ }
7884
+ update(mode) {
7885
+ const me = this;
7886
+ const points = me._cachedMeta.data;
7887
+ me.updateElements(points, 0, points.length, mode);
7888
+ }
7889
+ updateElements(points, start, count, mode) {
7890
+ const me = this;
7891
+ const reset = mode === 'reset';
7892
+ const {iScale, vScale} = me._cachedMeta;
7893
+ const firstOpts = me.resolveDataElementOptions(start, mode);
7894
+ const sharedOptions = me.getSharedOptions(firstOpts);
7895
+ const includeOptions = me.includeOptions(mode, sharedOptions);
7896
+ const iAxis = iScale.axis;
7897
+ const vAxis = vScale.axis;
7898
+ for (let i = start; i < start + count; i++) {
7899
+ const point = points[i];
7900
+ const parsed = !reset && me.getParsed(i);
7901
+ const properties = {};
7902
+ const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);
7903
+ const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);
7904
+ properties.skip = isNaN(iPixel) || isNaN(vPixel);
7905
+ if (includeOptions) {
7906
+ properties.options = me.resolveDataElementOptions(i, point.active ? 'active' : mode);
7907
+ if (reset) {
7908
+ properties.options.radius = 0;
7909
+ }
7910
+ }
7911
+ me.updateElement(point, i, properties, mode);
7912
+ }
7913
+ me.updateSharedOptions(sharedOptions, mode, firstOpts);
7914
+ }
7915
+ resolveDataElementOptions(index, mode) {
7916
+ const parsed = this.getParsed(index);
7917
+ let values = super.resolveDataElementOptions(index, mode);
7918
+ if (values.$shared) {
7919
+ values = Object.assign({}, values, {$shared: false});
7920
+ }
7921
+ const radius = values.radius;
7922
+ if (mode !== 'active') {
7923
+ values.radius = 0;
7924
+ }
7925
+ values.radius += valueOrDefault(parsed && parsed._custom, radius);
7926
+ return values;
7927
+ }
7928
+ }
7929
+ BubbleController.id = 'bubble';
7930
+ BubbleController.defaults = {
7931
+ datasetElementType: false,
7932
+ dataElementType: 'point',
7933
+ animations: {
7934
+ numbers: {
7935
+ type: 'number',
7936
+ properties: ['x', 'y', 'borderWidth', 'radius']
7937
+ }
7938
+ }
7939
+ };
7940
+ BubbleController.overrides = {
7941
+ scales: {
7942
+ x: {
7943
+ type: 'linear'
7944
+ },
7945
+ y: {
7946
+ type: 'linear'
7947
+ }
7948
+ },
7949
+ plugins: {
7950
+ tooltip: {
7951
+ callbacks: {
7952
+ title() {
7953
+ return '';
7954
+ }
7955
+ }
7956
+ }
7957
+ }
7958
+ };
7959
+
7960
+ function getRatioAndOffset(rotation, circumference, cutout) {
7961
+ let ratioX = 1;
7962
+ let ratioY = 1;
7963
+ let offsetX = 0;
7964
+ let offsetY = 0;
7965
+ if (circumference < TAU) {
7966
+ const startAngle = rotation;
7967
+ const endAngle = startAngle + circumference;
7968
+ const startX = Math.cos(startAngle);
7969
+ const startY = Math.sin(startAngle);
7970
+ const endX = Math.cos(endAngle);
7971
+ const endY = Math.sin(endAngle);
7972
+ const calcMax = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? 1 : Math.max(a, a * cutout, b, b * cutout);
7973
+ const calcMin = (angle, a, b) => _angleBetween(angle, startAngle, endAngle, true) ? -1 : Math.min(a, a * cutout, b, b * cutout);
7974
+ const maxX = calcMax(0, startX, endX);
7975
+ const maxY = calcMax(HALF_PI, startY, endY);
7976
+ const minX = calcMin(PI, startX, endX);
7977
+ const minY = calcMin(PI + HALF_PI, startY, endY);
7978
+ ratioX = (maxX - minX) / 2;
7979
+ ratioY = (maxY - minY) / 2;
7980
+ offsetX = -(maxX + minX) / 2;
7981
+ offsetY = -(maxY + minY) / 2;
7982
+ }
7983
+ return {ratioX, ratioY, offsetX, offsetY};
7984
+ }
7985
+ class DoughnutController extends DatasetController {
7986
+ constructor(chart, datasetIndex) {
7987
+ super(chart, datasetIndex);
7988
+ this.enableOptionSharing = true;
7989
+ this.innerRadius = undefined;
7990
+ this.outerRadius = undefined;
7991
+ this.offsetX = undefined;
7992
+ this.offsetY = undefined;
7993
+ }
7994
+ linkScales() {}
7995
+ parse(start, count) {
7996
+ const data = this.getDataset().data;
7997
+ const meta = this._cachedMeta;
7998
+ let i, ilen;
7999
+ for (i = start, ilen = start + count; i < ilen; ++i) {
8000
+ meta._parsed[i] = +data[i];
8001
+ }
8002
+ }
8003
+ _getRotation() {
8004
+ return toRadians(this.options.rotation - 90);
8005
+ }
8006
+ _getCircumference() {
8007
+ return toRadians(this.options.circumference);
8008
+ }
8009
+ _getRotationExtents() {
8010
+ let min = TAU;
8011
+ let max = -TAU;
8012
+ const me = this;
8013
+ for (let i = 0; i < me.chart.data.datasets.length; ++i) {
8014
+ if (me.chart.isDatasetVisible(i)) {
8015
+ const controller = me.chart.getDatasetMeta(i).controller;
8016
+ const rotation = controller._getRotation();
8017
+ const circumference = controller._getCircumference();
8018
+ min = Math.min(min, rotation);
8019
+ max = Math.max(max, rotation + circumference);
8020
+ }
8021
+ }
8022
+ return {
8023
+ rotation: min,
8024
+ circumference: max - min,
8025
+ };
8026
+ }
8027
+ update(mode) {
8028
+ const me = this;
8029
+ const chart = me.chart;
8030
+ const {chartArea} = chart;
8031
+ const meta = me._cachedMeta;
8032
+ const arcs = meta.data;
8033
+ const spacing = me.getMaxBorderWidth() + me.getMaxOffset(arcs) + me.options.spacing;
8034
+ const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);
8035
+ const cutout = Math.min(toPercentage(me.options.cutout, maxSize), 1);
8036
+ const chartWeight = me._getRingWeight(me.index);
8037
+ const {circumference, rotation} = me._getRotationExtents();
8038
+ const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout);
8039
+ const maxWidth = (chartArea.width - spacing) / ratioX;
8040
+ const maxHeight = (chartArea.height - spacing) / ratioY;
8041
+ const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
8042
+ const outerRadius = toDimension(me.options.radius, maxRadius);
8043
+ const innerRadius = Math.max(outerRadius * cutout, 0);
8044
+ const radiusLength = (outerRadius - innerRadius) / me._getVisibleDatasetWeightTotal();
8045
+ me.offsetX = offsetX * outerRadius;
8046
+ me.offsetY = offsetY * outerRadius;
8047
+ meta.total = me.calculateTotal();
8048
+ me.outerRadius = outerRadius - radiusLength * me._getRingWeightOffset(me.index);
8049
+ me.innerRadius = Math.max(me.outerRadius - radiusLength * chartWeight, 0);
8050
+ me.updateElements(arcs, 0, arcs.length, mode);
8051
+ }
8052
+ _circumference(i, reset) {
8053
+ const me = this;
8054
+ const opts = me.options;
8055
+ const meta = me._cachedMeta;
8056
+ const circumference = me._getCircumference();
8057
+ if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {
8058
+ return 0;
8059
+ }
8060
+ return me.calculateCircumference(meta._parsed[i] * circumference / TAU);
8061
+ }
8062
+ updateElements(arcs, start, count, mode) {
8063
+ const me = this;
8064
+ const reset = mode === 'reset';
8065
+ const chart = me.chart;
8066
+ const chartArea = chart.chartArea;
8067
+ const opts = chart.options;
8068
+ const animationOpts = opts.animation;
8069
+ const centerX = (chartArea.left + chartArea.right) / 2;
8070
+ const centerY = (chartArea.top + chartArea.bottom) / 2;
8071
+ const animateScale = reset && animationOpts.animateScale;
8072
+ const innerRadius = animateScale ? 0 : me.innerRadius;
8073
+ const outerRadius = animateScale ? 0 : me.outerRadius;
8074
+ const firstOpts = me.resolveDataElementOptions(start, mode);
8075
+ const sharedOptions = me.getSharedOptions(firstOpts);
8076
+ const includeOptions = me.includeOptions(mode, sharedOptions);
8077
+ let startAngle = me._getRotation();
8078
+ let i;
8079
+ for (i = 0; i < start; ++i) {
8080
+ startAngle += me._circumference(i, reset);
8081
+ }
8082
+ for (i = start; i < start + count; ++i) {
8083
+ const circumference = me._circumference(i, reset);
8084
+ const arc = arcs[i];
8085
+ const properties = {
8086
+ x: centerX + me.offsetX,
8087
+ y: centerY + me.offsetY,
8088
+ startAngle,
8089
+ endAngle: startAngle + circumference,
8090
+ circumference,
8091
+ outerRadius,
8092
+ innerRadius
8093
+ };
8094
+ if (includeOptions) {
8095
+ properties.options = sharedOptions || me.resolveDataElementOptions(i, arc.active ? 'active' : mode);
8096
+ }
8097
+ startAngle += circumference;
8098
+ me.updateElement(arc, i, properties, mode);
8099
+ }
8100
+ me.updateSharedOptions(sharedOptions, mode, firstOpts);
8101
+ }
8102
+ calculateTotal() {
8103
+ const meta = this._cachedMeta;
8104
+ const metaData = meta.data;
8105
+ let total = 0;
8106
+ let i;
8107
+ for (i = 0; i < metaData.length; i++) {
8108
+ const value = meta._parsed[i];
8109
+ if (value !== null && !isNaN(value) && this.chart.getDataVisibility(i) && !metaData[i].hidden) {
8110
+ total += Math.abs(value);
8111
+ }
8112
+ }
8113
+ return total;
8114
+ }
8115
+ calculateCircumference(value) {
8116
+ const total = this._cachedMeta.total;
8117
+ if (total > 0 && !isNaN(value)) {
8118
+ return TAU * (Math.abs(value) / total);
8119
+ }
8120
+ return 0;
8121
+ }
8122
+ getLabelAndValue(index) {
8123
+ const me = this;
8124
+ const meta = me._cachedMeta;
8125
+ const chart = me.chart;
8126
+ const labels = chart.data.labels || [];
8127
+ const value = formatNumber(meta._parsed[index], chart.options.locale);
8128
+ return {
8129
+ label: labels[index] || '',
8130
+ value,
8131
+ };
8132
+ }
8133
+ getMaxBorderWidth(arcs) {
8134
+ const me = this;
8135
+ let max = 0;
8136
+ const chart = me.chart;
8137
+ let i, ilen, meta, controller, options;
8138
+ if (!arcs) {
8139
+ for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
8140
+ if (chart.isDatasetVisible(i)) {
8141
+ meta = chart.getDatasetMeta(i);
8142
+ arcs = meta.data;
8143
+ controller = meta.controller;
8144
+ if (controller !== me) {
8145
+ controller.configure();
8146
+ }
8147
+ break;
8148
+ }
8149
+ }
8150
+ }
8151
+ if (!arcs) {
8152
+ return 0;
8153
+ }
8154
+ for (i = 0, ilen = arcs.length; i < ilen; ++i) {
8155
+ options = controller.resolveDataElementOptions(i);
8156
+ if (options.borderAlign !== 'inner') {
8157
+ max = Math.max(max, options.borderWidth || 0, options.hoverBorderWidth || 0);
8158
+ }
8159
+ }
8160
+ return max;
8161
+ }
8162
+ getMaxOffset(arcs) {
8163
+ let max = 0;
8164
+ for (let i = 0, ilen = arcs.length; i < ilen; ++i) {
8165
+ const options = this.resolveDataElementOptions(i);
8166
+ max = Math.max(max, options.offset || 0, options.hoverOffset || 0);
8167
+ }
8168
+ return max;
8169
+ }
8170
+ _getRingWeightOffset(datasetIndex) {
8171
+ let ringWeightOffset = 0;
8172
+ for (let i = 0; i < datasetIndex; ++i) {
8173
+ if (this.chart.isDatasetVisible(i)) {
8174
+ ringWeightOffset += this._getRingWeight(i);
8175
+ }
8176
+ }
8177
+ return ringWeightOffset;
8178
+ }
8179
+ _getRingWeight(datasetIndex) {
8180
+ return Math.max(valueOrDefault(this.chart.data.datasets[datasetIndex].weight, 1), 0);
8181
+ }
8182
+ _getVisibleDatasetWeightTotal() {
8183
+ return this._getRingWeightOffset(this.chart.data.datasets.length) || 1;
8184
+ }
8185
+ }
8186
+ DoughnutController.id = 'doughnut';
8187
+ DoughnutController.defaults = {
8188
+ datasetElementType: false,
8189
+ dataElementType: 'arc',
8190
+ animation: {
8191
+ animateRotate: true,
8192
+ animateScale: false
8193
+ },
8194
+ animations: {
8195
+ numbers: {
8196
+ type: 'number',
8197
+ properties: ['circumference', 'endAngle', 'innerRadius', 'outerRadius', 'startAngle', 'x', 'y', 'offset', 'borderWidth', 'spacing']
8198
+ },
8199
+ },
8200
+ cutout: '50%',
8201
+ rotation: 0,
8202
+ circumference: 360,
8203
+ radius: '100%',
8204
+ spacing: 0,
8205
+ indexAxis: 'r',
8206
+ };
8207
+ DoughnutController.descriptors = {
8208
+ _scriptable: (name) => name !== 'spacing',
8209
+ _indexable: (name) => name !== 'spacing',
8210
+ };
8211
+ DoughnutController.overrides = {
8212
+ aspectRatio: 1,
8213
+ plugins: {
8214
+ legend: {
8215
+ labels: {
8216
+ generateLabels(chart) {
8217
+ const data = chart.data;
8218
+ if (data.labels.length && data.datasets.length) {
8219
+ const {labels: {pointStyle}} = chart.legend.options;
8220
+ return data.labels.map((label, i) => {
8221
+ const meta = chart.getDatasetMeta(0);
8222
+ const style = meta.controller.getStyle(i);
8223
+ return {
8224
+ text: label,
8225
+ fillStyle: style.backgroundColor,
8226
+ strokeStyle: style.borderColor,
8227
+ lineWidth: style.borderWidth,
8228
+ pointStyle: pointStyle,
8229
+ hidden: !chart.getDataVisibility(i),
8230
+ index: i
8231
+ };
8232
+ });
8233
+ }
8234
+ return [];
8235
+ }
8236
+ },
8237
+ onClick(e, legendItem, legend) {
8238
+ legend.chart.toggleDataVisibility(legendItem.index);
8239
+ legend.chart.update();
8240
+ }
8241
+ },
8242
+ tooltip: {
8243
+ callbacks: {
8244
+ title() {
8245
+ return '';
8246
+ },
8247
+ label(tooltipItem) {
8248
+ let dataLabel = tooltipItem.label;
8249
+ const value = ': ' + tooltipItem.formattedValue;
8250
+ if (isArray(dataLabel)) {
8251
+ dataLabel = dataLabel.slice();
8252
+ dataLabel[0] += value;
8253
+ } else {
8254
+ dataLabel += value;
8255
+ }
8256
+ return dataLabel;
8257
+ }
8258
+ }
8259
+ }
8260
+ }
8261
+ };
8262
+
8263
+ class LineController extends DatasetController {
8264
+ initialize() {
8265
+ this.enableOptionSharing = true;
8266
+ super.initialize();
8267
+ }
8268
+ update(mode) {
8269
+ const me = this;
8270
+ const meta = me._cachedMeta;
8271
+ const {dataset: line, data: points = [], _dataset} = meta;
8272
+ const animationsDisabled = me.chart._animationsDisabled;
8273
+ let {start, count} = getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
8274
+ me._drawStart = start;
8275
+ me._drawCount = count;
8276
+ if (scaleRangesChanged(meta)) {
8277
+ start = 0;
8278
+ count = points.length;
8279
+ }
8280
+ line._datasetIndex = me.index;
8281
+ line._decimated = !!_dataset._decimated;
8282
+ line.points = points;
8283
+ const options = me.resolveDatasetElementOptions(mode);
8284
+ if (!me.options.showLine) {
8285
+ options.borderWidth = 0;
8286
+ }
8287
+ options.segment = me.options.segment;
8288
+ me.updateElement(line, undefined, {
8289
+ animated: !animationsDisabled,
8290
+ options
8291
+ }, mode);
8292
+ me.updateElements(points, start, count, mode);
8293
+ }
8294
+ updateElements(points, start, count, mode) {
8295
+ const me = this;
8296
+ const reset = mode === 'reset';
8297
+ const {iScale, vScale, _stacked} = me._cachedMeta;
8298
+ const firstOpts = me.resolveDataElementOptions(start, mode);
8299
+ const sharedOptions = me.getSharedOptions(firstOpts);
8300
+ const includeOptions = me.includeOptions(mode, sharedOptions);
8301
+ const iAxis = iScale.axis;
8302
+ const vAxis = vScale.axis;
8303
+ const spanGaps = me.options.spanGaps;
8304
+ const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
8305
+ const directUpdate = me.chart._animationsDisabled || reset || mode === 'none';
8306
+ let prevParsed = start > 0 && me.getParsed(start - 1);
8307
+ for (let i = start; i < start + count; ++i) {
8308
+ const point = points[i];
8309
+ const parsed = me.getParsed(i);
8310
+ const properties = directUpdate ? point : {};
8311
+ const nullData = isNullOrUndef(parsed[vAxis]);
8312
+ const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
8313
+ const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? me.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
8314
+ properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
8315
+ properties.stop = i > 0 && (parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
8316
+ properties.parsed = parsed;
8317
+ if (includeOptions) {
8318
+ properties.options = sharedOptions || me.resolveDataElementOptions(i, point.active ? 'active' : mode);
8319
+ }
8320
+ if (!directUpdate) {
8321
+ me.updateElement(point, i, properties, mode);
8322
+ }
8323
+ prevParsed = parsed;
8324
+ }
8325
+ me.updateSharedOptions(sharedOptions, mode, firstOpts);
8326
+ }
8327
+ getMaxOverflow() {
8328
+ const me = this;
8329
+ const meta = me._cachedMeta;
8330
+ const dataset = meta.dataset;
8331
+ const border = dataset.options && dataset.options.borderWidth || 0;
8332
+ const data = meta.data || [];
8333
+ if (!data.length) {
8334
+ return border;
8335
+ }
8336
+ const firstPoint = data[0].size(me.resolveDataElementOptions(0));
8337
+ const lastPoint = data[data.length - 1].size(me.resolveDataElementOptions(data.length - 1));
8338
+ return Math.max(border, firstPoint, lastPoint) / 2;
8339
+ }
8340
+ draw() {
8341
+ const meta = this._cachedMeta;
8342
+ meta.dataset.updateControlPoints(this.chart.chartArea, meta.iScale.axis);
8343
+ super.draw();
8344
+ }
8345
+ }
8346
+ LineController.id = 'line';
8347
+ LineController.defaults = {
8348
+ datasetElementType: 'line',
8349
+ dataElementType: 'point',
8350
+ showLine: true,
8351
+ spanGaps: false,
8352
+ };
8353
+ LineController.overrides = {
8354
+ scales: {
8355
+ _index_: {
8356
+ type: 'category',
8357
+ },
8358
+ _value_: {
8359
+ type: 'linear',
8360
+ },
8361
+ }
8362
+ };
8363
+ function getStartAndCountOfVisiblePoints(meta, points, animationsDisabled) {
8364
+ const pointCount = points.length;
8365
+ let start = 0;
8366
+ let count = pointCount;
8367
+ if (meta._sorted) {
8368
+ const {iScale, _parsed} = meta;
8369
+ const axis = iScale.axis;
8370
+ const {min, max, minDefined, maxDefined} = iScale.getUserBounds();
8371
+ if (minDefined) {
8372
+ start = _limitValue(Math.min(
8373
+ _lookupByKey(_parsed, iScale.axis, min).lo,
8374
+ animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo),
8375
+ 0, pointCount - 1);
8376
+ }
8377
+ if (maxDefined) {
8378
+ count = _limitValue(Math.max(
8379
+ _lookupByKey(_parsed, iScale.axis, max).hi + 1,
8380
+ animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max)).hi + 1),
8381
+ start, pointCount) - start;
8382
+ } else {
8383
+ count = pointCount - start;
8384
+ }
8385
+ }
8386
+ return {start, count};
8387
+ }
8388
+ function scaleRangesChanged(meta) {
8389
+ const {xScale, yScale, _scaleRanges} = meta;
8390
+ const newRanges = {
8391
+ xmin: xScale.min,
8392
+ xmax: xScale.max,
8393
+ ymin: yScale.min,
8394
+ ymax: yScale.max
8395
+ };
8396
+ if (!_scaleRanges) {
8397
+ meta._scaleRanges = newRanges;
8398
+ return true;
8399
+ }
8400
+ const changed = _scaleRanges.xmin !== xScale.min
8401
+ || _scaleRanges.xmax !== xScale.max
8402
+ || _scaleRanges.ymin !== yScale.min
8403
+ || _scaleRanges.ymax !== yScale.max;
8404
+ Object.assign(_scaleRanges, newRanges);
8405
+ return changed;
8406
+ }
8407
+
8408
+ class PolarAreaController extends DatasetController {
8409
+ constructor(chart, datasetIndex) {
8410
+ super(chart, datasetIndex);
8411
+ this.innerRadius = undefined;
8412
+ this.outerRadius = undefined;
8413
+ }
8414
+ getLabelAndValue(index) {
8415
+ const me = this;
8416
+ const meta = me._cachedMeta;
8417
+ const chart = me.chart;
8418
+ const labels = chart.data.labels || [];
8419
+ const value = formatNumber(meta._parsed[index].r, chart.options.locale);
8420
+ return {
8421
+ label: labels[index] || '',
8422
+ value,
8423
+ };
8424
+ }
8425
+ update(mode) {
8426
+ const arcs = this._cachedMeta.data;
8427
+ this._updateRadius();
8428
+ this.updateElements(arcs, 0, arcs.length, mode);
8429
+ }
8430
+ _updateRadius() {
8431
+ const me = this;
8432
+ const chart = me.chart;
8433
+ const chartArea = chart.chartArea;
8434
+ const opts = chart.options;
8435
+ const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
8436
+ const outerRadius = Math.max(minSize / 2, 0);
8437
+ const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
8438
+ const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
8439
+ me.outerRadius = outerRadius - (radiusLength * me.index);
8440
+ me.innerRadius = me.outerRadius - radiusLength;
8441
+ }
8442
+ updateElements(arcs, start, count, mode) {
8443
+ const me = this;
8444
+ const reset = mode === 'reset';
8445
+ const chart = me.chart;
8446
+ const dataset = me.getDataset();
8447
+ const opts = chart.options;
8448
+ const animationOpts = opts.animation;
8449
+ const scale = me._cachedMeta.rScale;
8450
+ const centerX = scale.xCenter;
8451
+ const centerY = scale.yCenter;
8452
+ const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;
8453
+ let angle = datasetStartAngle;
8454
+ let i;
8455
+ const defaultAngle = 360 / me.countVisibleElements();
8456
+ for (i = 0; i < start; ++i) {
8457
+ angle += me._computeAngle(i, mode, defaultAngle);
8458
+ }
8459
+ for (i = start; i < start + count; i++) {
8460
+ const arc = arcs[i];
8461
+ let startAngle = angle;
8462
+ let endAngle = angle + me._computeAngle(i, mode, defaultAngle);
8463
+ let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0;
8464
+ angle = endAngle;
8465
+ if (reset) {
8466
+ if (animationOpts.animateScale) {
8467
+ outerRadius = 0;
8468
+ }
8469
+ if (animationOpts.animateRotate) {
8470
+ startAngle = endAngle = datasetStartAngle;
8471
+ }
8472
+ }
8473
+ const properties = {
8474
+ x: centerX,
8475
+ y: centerY,
8476
+ innerRadius: 0,
8477
+ outerRadius,
8478
+ startAngle,
8479
+ endAngle,
8480
+ options: me.resolveDataElementOptions(i, arc.active ? 'active' : mode)
8481
+ };
8482
+ me.updateElement(arc, i, properties, mode);
8483
+ }
8484
+ }
8485
+ countVisibleElements() {
8486
+ const dataset = this.getDataset();
8487
+ const meta = this._cachedMeta;
8488
+ let count = 0;
8489
+ meta.data.forEach((element, index) => {
8490
+ if (!isNaN(dataset.data[index]) && this.chart.getDataVisibility(index)) {
8491
+ count++;
8492
+ }
8493
+ });
8494
+ return count;
8495
+ }
8496
+ _computeAngle(index, mode, defaultAngle) {
8497
+ return this.chart.getDataVisibility(index)
8498
+ ? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle)
8499
+ : 0;
8500
+ }
8501
+ }
8502
+ PolarAreaController.id = 'polarArea';
8503
+ PolarAreaController.defaults = {
8504
+ dataElementType: 'arc',
8505
+ animation: {
8506
+ animateRotate: true,
8507
+ animateScale: true
8508
+ },
8509
+ animations: {
8510
+ numbers: {
8511
+ type: 'number',
8512
+ properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius']
8513
+ },
8514
+ },
8515
+ indexAxis: 'r',
8516
+ startAngle: 0,
8517
+ };
8518
+ PolarAreaController.overrides = {
8519
+ aspectRatio: 1,
8520
+ plugins: {
8521
+ legend: {
8522
+ labels: {
8523
+ generateLabels(chart) {
8524
+ const data = chart.data;
8525
+ if (data.labels.length && data.datasets.length) {
8526
+ const {labels: {pointStyle}} = chart.legend.options;
8527
+ return data.labels.map((label, i) => {
8528
+ const meta = chart.getDatasetMeta(0);
8529
+ const style = meta.controller.getStyle(i);
8530
+ return {
8531
+ text: label,
8532
+ fillStyle: style.backgroundColor,
8533
+ strokeStyle: style.borderColor,
8534
+ lineWidth: style.borderWidth,
8535
+ pointStyle: pointStyle,
8536
+ hidden: !chart.getDataVisibility(i),
8537
+ index: i
8538
+ };
8539
+ });
8540
+ }
8541
+ return [];
8542
+ }
8543
+ },
8544
+ onClick(e, legendItem, legend) {
8545
+ legend.chart.toggleDataVisibility(legendItem.index);
8546
+ legend.chart.update();
8547
+ }
8548
+ },
8549
+ tooltip: {
8550
+ callbacks: {
8551
+ title() {
8552
+ return '';
8553
+ },
8554
+ label(context) {
8555
+ return context.chart.data.labels[context.dataIndex] + ': ' + context.formattedValue;
8556
+ }
8557
+ }
8558
+ }
8559
+ },
8560
+ scales: {
8561
+ r: {
8562
+ type: 'radialLinear',
8563
+ angleLines: {
8564
+ display: false
8565
+ },
8566
+ beginAtZero: true,
8567
+ grid: {
8568
+ circular: true
8569
+ },
8570
+ pointLabels: {
8571
+ display: false
8572
+ },
8573
+ startAngle: 0
8574
+ }
8575
+ }
8576
+ };
8577
+
8578
+ class PieController extends DoughnutController {
8579
+ }
8580
+ PieController.id = 'pie';
8581
+ PieController.defaults = {
8582
+ cutout: 0,
8583
+ rotation: 0,
8584
+ circumference: 360,
8585
+ radius: '100%'
8586
+ };
8587
+
8588
+ class RadarController extends DatasetController {
8589
+ getLabelAndValue(index) {
8590
+ const me = this;
8591
+ const vScale = me._cachedMeta.vScale;
8592
+ const parsed = me.getParsed(index);
8593
+ return {
8594
+ label: vScale.getLabels()[index],
8595
+ value: '' + vScale.getLabelForValue(parsed[vScale.axis])
8596
+ };
8597
+ }
8598
+ update(mode) {
8599
+ const me = this;
8600
+ const meta = me._cachedMeta;
8601
+ const line = meta.dataset;
8602
+ const points = meta.data || [];
8603
+ const labels = meta.iScale.getLabels();
8604
+ line.points = points;
8605
+ if (mode !== 'resize') {
8606
+ const options = me.resolveDatasetElementOptions(mode);
8607
+ if (!me.options.showLine) {
8608
+ options.borderWidth = 0;
8609
+ }
8610
+ const properties = {
8611
+ _loop: true,
8612
+ _fullLoop: labels.length === points.length,
8613
+ options
8614
+ };
8615
+ me.updateElement(line, undefined, properties, mode);
8616
+ }
8617
+ me.updateElements(points, 0, points.length, mode);
8618
+ }
8619
+ updateElements(points, start, count, mode) {
8620
+ const me = this;
8621
+ const dataset = me.getDataset();
8622
+ const scale = me._cachedMeta.rScale;
8623
+ const reset = mode === 'reset';
8624
+ for (let i = start; i < start + count; i++) {
8625
+ const point = points[i];
8626
+ const options = me.resolveDataElementOptions(i, point.active ? 'active' : mode);
8627
+ const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]);
8628
+ const x = reset ? scale.xCenter : pointPosition.x;
8629
+ const y = reset ? scale.yCenter : pointPosition.y;
8630
+ const properties = {
8631
+ x,
8632
+ y,
8633
+ angle: pointPosition.angle,
8634
+ skip: isNaN(x) || isNaN(y),
8635
+ options
8636
+ };
8637
+ me.updateElement(point, i, properties, mode);
8638
+ }
8639
+ }
8640
+ }
8641
+ RadarController.id = 'radar';
8642
+ RadarController.defaults = {
8643
+ datasetElementType: 'line',
8644
+ dataElementType: 'point',
8645
+ indexAxis: 'r',
8646
+ showLine: true,
8647
+ elements: {
8648
+ line: {
8649
+ fill: 'start'
8650
+ }
8651
+ },
8652
+ };
8653
+ RadarController.overrides = {
8654
+ aspectRatio: 1,
8655
+ scales: {
8656
+ r: {
8657
+ type: 'radialLinear',
8658
+ }
8659
+ }
8660
+ };
8661
+
8662
+ class ScatterController extends LineController {
8663
+ }
8664
+ ScatterController.id = 'scatter';
8665
+ ScatterController.defaults = {
8666
+ showLine: false,
8667
+ fill: false
8668
+ };
8669
+ ScatterController.overrides = {
8670
+ interaction: {
8671
+ mode: 'point'
8672
+ },
8673
+ plugins: {
8674
+ tooltip: {
8675
+ callbacks: {
8676
+ title() {
8677
+ return '';
8678
+ },
8679
+ label(item) {
8680
+ return '(' + item.label + ', ' + item.formattedValue + ')';
8681
+ }
8682
+ }
8683
+ }
8684
+ },
8685
+ scales: {
8686
+ x: {
8687
+ type: 'linear'
8688
+ },
8689
+ y: {
8690
+ type: 'linear'
8691
+ }
8692
+ }
8693
+ };
8694
+
8695
+ var controllers = /*#__PURE__*/Object.freeze({
8696
+ __proto__: null,
8697
+ BarController: BarController,
8698
+ BubbleController: BubbleController,
8699
+ DoughnutController: DoughnutController,
8700
+ LineController: LineController,
8701
+ PolarAreaController: PolarAreaController,
8702
+ PieController: PieController,
8703
+ RadarController: RadarController,
8704
+ ScatterController: ScatterController
8705
+ });
8706
+
8707
+ function clipArc(ctx, element, endAngle) {
8708
+ const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element;
8709
+ let angleMargin = pixelMargin / outerRadius;
8710
+ ctx.beginPath();
8711
+ ctx.arc(x, y, outerRadius, startAngle - angleMargin, endAngle + angleMargin);
8712
+ if (innerRadius > pixelMargin) {
8713
+ angleMargin = pixelMargin / innerRadius;
8714
+ ctx.arc(x, y, innerRadius, endAngle + angleMargin, startAngle - angleMargin, true);
8715
+ } else {
8716
+ ctx.arc(x, y, pixelMargin, endAngle + HALF_PI, startAngle - HALF_PI);
8717
+ }
8718
+ ctx.closePath();
8719
+ ctx.clip();
8720
+ }
8721
+ function toRadiusCorners(value) {
8722
+ return _readValueToProps(value, ['outerStart', 'outerEnd', 'innerStart', 'innerEnd']);
8723
+ }
8724
+ function parseBorderRadius$1(arc, innerRadius, outerRadius, angleDelta) {
8725
+ const o = toRadiusCorners(arc.options.borderRadius);
8726
+ const halfThickness = (outerRadius - innerRadius) / 2;
8727
+ const innerLimit = Math.min(halfThickness, angleDelta * innerRadius / 2);
8728
+ const computeOuterLimit = (val) => {
8729
+ const outerArcLimit = (outerRadius - Math.min(halfThickness, val)) * angleDelta / 2;
8730
+ return _limitValue(val, 0, Math.min(halfThickness, outerArcLimit));
8731
+ };
8732
+ return {
8733
+ outerStart: computeOuterLimit(o.outerStart),
8734
+ outerEnd: computeOuterLimit(o.outerEnd),
8735
+ innerStart: _limitValue(o.innerStart, 0, innerLimit),
8736
+ innerEnd: _limitValue(o.innerEnd, 0, innerLimit),
8737
+ };
8738
+ }
8739
+ function rThetaToXY(r, theta, x, y) {
8740
+ return {
8741
+ x: x + r * Math.cos(theta),
8742
+ y: y + r * Math.sin(theta),
8743
+ };
8744
+ }
8745
+ function pathArc(ctx, element, offset, spacing, end) {
8746
+ const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element;
8747
+ const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);
8748
+ const innerRadius = innerR > 0 ? innerR + spacing + offset + pixelMargin : 0;
8749
+ let spacingOffset = 0;
8750
+ const alpha = end - start;
8751
+ if (spacing) {
8752
+ const noSpacingInnerRadius = innerR > 0 ? innerR - spacing : 0;
8753
+ const noSpacingOuterRadius = outerRadius > 0 ? outerRadius - spacing : 0;
8754
+ const avNogSpacingRadius = (noSpacingInnerRadius + noSpacingOuterRadius) / 2;
8755
+ const adjustedAngle = avNogSpacingRadius !== 0 ? (alpha * avNogSpacingRadius) / (avNogSpacingRadius + spacing) : alpha;
8756
+ spacingOffset = (alpha - adjustedAngle) / 2;
8757
+ }
8758
+ const beta = Math.max(0.001, alpha * outerRadius - offset / PI) / outerRadius;
8759
+ const angleOffset = (alpha - beta) / 2;
8760
+ const startAngle = start + angleOffset + spacingOffset;
8761
+ const endAngle = end - angleOffset - spacingOffset;
8762
+ const {outerStart, outerEnd, innerStart, innerEnd} = parseBorderRadius$1(element, innerRadius, outerRadius, endAngle - startAngle);
8763
+ const outerStartAdjustedRadius = outerRadius - outerStart;
8764
+ const outerEndAdjustedRadius = outerRadius - outerEnd;
8765
+ const outerStartAdjustedAngle = startAngle + outerStart / outerStartAdjustedRadius;
8766
+ const outerEndAdjustedAngle = endAngle - outerEnd / outerEndAdjustedRadius;
8767
+ const innerStartAdjustedRadius = innerRadius + innerStart;
8768
+ const innerEndAdjustedRadius = innerRadius + innerEnd;
8769
+ const innerStartAdjustedAngle = startAngle + innerStart / innerStartAdjustedRadius;
8770
+ const innerEndAdjustedAngle = endAngle - innerEnd / innerEndAdjustedRadius;
8771
+ ctx.beginPath();
8772
+ ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
8773
+ if (outerEnd > 0) {
8774
+ const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
8775
+ ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
8776
+ }
8777
+ const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
8778
+ ctx.lineTo(p4.x, p4.y);
8779
+ if (innerEnd > 0) {
8780
+ const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
8781
+ ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
8782
+ }
8783
+ ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true);
8784
+ if (innerStart > 0) {
8785
+ const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
8786
+ ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
8787
+ }
8788
+ const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
8789
+ ctx.lineTo(p8.x, p8.y);
8790
+ if (outerStart > 0) {
8791
+ const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
8792
+ ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
8793
+ }
8794
+ ctx.closePath();
8795
+ }
8796
+ function drawArc(ctx, element, offset, spacing) {
8797
+ const {fullCircles, startAngle, circumference} = element;
8798
+ let endAngle = element.endAngle;
8799
+ if (fullCircles) {
8800
+ pathArc(ctx, element, offset, spacing, startAngle + TAU);
8801
+ for (let i = 0; i < fullCircles; ++i) {
8802
+ ctx.fill();
8803
+ }
8804
+ if (!isNaN(circumference)) {
8805
+ endAngle = startAngle + circumference % TAU;
8806
+ if (circumference % TAU === 0) {
8807
+ endAngle += TAU;
8808
+ }
8809
+ }
8810
+ }
8811
+ pathArc(ctx, element, offset, spacing, endAngle);
8812
+ ctx.fill();
8813
+ return endAngle;
8814
+ }
8815
+ function drawFullCircleBorders(ctx, element, inner) {
8816
+ const {x, y, startAngle, pixelMargin, fullCircles} = element;
8817
+ const outerRadius = Math.max(element.outerRadius - pixelMargin, 0);
8818
+ const innerRadius = element.innerRadius + pixelMargin;
8819
+ let i;
8820
+ if (inner) {
8821
+ clipArc(ctx, element, startAngle + TAU);
8822
+ }
8823
+ ctx.beginPath();
8824
+ ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true);
8825
+ for (i = 0; i < fullCircles; ++i) {
8826
+ ctx.stroke();
8827
+ }
8828
+ ctx.beginPath();
8829
+ ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU);
8830
+ for (i = 0; i < fullCircles; ++i) {
8831
+ ctx.stroke();
8832
+ }
8833
+ }
8834
+ function drawBorder(ctx, element, offset, spacing, endAngle) {
8835
+ const {options} = element;
8836
+ const inner = options.borderAlign === 'inner';
8837
+ if (!options.borderWidth) {
8838
+ return;
8839
+ }
8840
+ if (inner) {
8841
+ ctx.lineWidth = options.borderWidth * 2;
8842
+ ctx.lineJoin = 'round';
8843
+ } else {
8844
+ ctx.lineWidth = options.borderWidth;
8845
+ ctx.lineJoin = 'bevel';
8846
+ }
8847
+ if (element.fullCircles) {
8848
+ drawFullCircleBorders(ctx, element, inner);
8849
+ }
8850
+ if (inner) {
8851
+ clipArc(ctx, element, endAngle);
8852
+ }
8853
+ pathArc(ctx, element, offset, spacing, endAngle);
8854
+ ctx.stroke();
8855
+ }
8856
+ class ArcElement extends Element {
8857
+ constructor(cfg) {
8858
+ super();
8859
+ this.options = undefined;
8860
+ this.circumference = undefined;
8861
+ this.startAngle = undefined;
8862
+ this.endAngle = undefined;
8863
+ this.innerRadius = undefined;
8864
+ this.outerRadius = undefined;
8865
+ this.pixelMargin = 0;
8866
+ this.fullCircles = 0;
8867
+ if (cfg) {
8868
+ Object.assign(this, cfg);
8869
+ }
8870
+ }
8871
+ inRange(chartX, chartY, useFinalPosition) {
8872
+ const point = this.getProps(['x', 'y'], useFinalPosition);
8873
+ const {angle, distance} = getAngleFromPoint(point, {x: chartX, y: chartY});
8874
+ const {startAngle, endAngle, innerRadius, outerRadius, circumference} = this.getProps([
8875
+ 'startAngle',
8876
+ 'endAngle',
8877
+ 'innerRadius',
8878
+ 'outerRadius',
8879
+ 'circumference'
8880
+ ], useFinalPosition);
8881
+ const rAdjust = this.options.spacing / 2;
8882
+ const betweenAngles = circumference >= TAU || _angleBetween(angle, startAngle, endAngle);
8883
+ const withinRadius = (distance >= innerRadius + rAdjust && distance <= outerRadius + rAdjust);
8884
+ return (betweenAngles && withinRadius);
8885
+ }
8886
+ getCenterPoint(useFinalPosition) {
8887
+ const {x, y, startAngle, endAngle, innerRadius, outerRadius} = this.getProps([
8888
+ 'x',
8889
+ 'y',
8890
+ 'startAngle',
8891
+ 'endAngle',
8892
+ 'innerRadius',
8893
+ 'outerRadius',
8894
+ 'circumference',
8895
+ ], useFinalPosition);
8896
+ const {offset, spacing} = this.options;
8897
+ const halfAngle = (startAngle + endAngle) / 2;
8898
+ const halfRadius = (innerRadius + outerRadius + spacing + offset) / 2;
8899
+ return {
8900
+ x: x + Math.cos(halfAngle) * halfRadius,
8901
+ y: y + Math.sin(halfAngle) * halfRadius
8902
+ };
8903
+ }
8904
+ tooltipPosition(useFinalPosition) {
8905
+ return this.getCenterPoint(useFinalPosition);
8906
+ }
8907
+ draw(ctx) {
8908
+ const me = this;
8909
+ const {options, circumference} = me;
8910
+ const offset = (options.offset || 0) / 2;
8911
+ const spacing = (options.spacing || 0) / 2;
8912
+ me.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
8913
+ me.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
8914
+ if (circumference === 0 || me.innerRadius < 0 || me.outerRadius < 0) {
8915
+ return;
8916
+ }
8917
+ ctx.save();
8918
+ let radiusOffset = 0;
8919
+ if (offset) {
8920
+ radiusOffset = offset / 2;
8921
+ const halfAngle = (me.startAngle + me.endAngle) / 2;
8922
+ ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset);
8923
+ if (me.circumference >= PI) {
8924
+ radiusOffset = offset;
8925
+ }
8926
+ }
8927
+ ctx.fillStyle = options.backgroundColor;
8928
+ ctx.strokeStyle = options.borderColor;
8929
+ const endAngle = drawArc(ctx, me, radiusOffset, spacing);
8930
+ drawBorder(ctx, me, radiusOffset, spacing, endAngle);
8931
+ ctx.restore();
8932
+ }
8933
+ }
8934
+ ArcElement.id = 'arc';
8935
+ ArcElement.defaults = {
8936
+ borderAlign: 'center',
8937
+ borderColor: '#fff',
8938
+ borderRadius: 0,
8939
+ borderWidth: 2,
8940
+ offset: 0,
8941
+ spacing: 0,
8942
+ angle: undefined,
8943
+ };
8944
+ ArcElement.defaultRoutes = {
8945
+ backgroundColor: 'backgroundColor'
8946
+ };
8947
+
8948
+ function setStyle(ctx, options, style = options) {
8949
+ ctx.lineCap = valueOrDefault(style.borderCapStyle, options.borderCapStyle);
8950
+ ctx.setLineDash(valueOrDefault(style.borderDash, options.borderDash));
8951
+ ctx.lineDashOffset = valueOrDefault(style.borderDashOffset, options.borderDashOffset);
8952
+ ctx.lineJoin = valueOrDefault(style.borderJoinStyle, options.borderJoinStyle);
8953
+ ctx.lineWidth = valueOrDefault(style.borderWidth, options.borderWidth);
8954
+ ctx.strokeStyle = valueOrDefault(style.borderColor, options.borderColor);
8955
+ }
8956
+ function lineTo(ctx, previous, target) {
8957
+ ctx.lineTo(target.x, target.y);
8958
+ }
8959
+ function getLineMethod(options) {
8960
+ if (options.stepped) {
8961
+ return _steppedLineTo;
8962
+ }
8963
+ if (options.tension || options.cubicInterpolationMode === 'monotone') {
8964
+ return _bezierCurveTo;
8965
+ }
8966
+ return lineTo;
8967
+ }
8968
+ function pathVars(points, segment, params = {}) {
8969
+ const count = points.length;
8970
+ const {start: paramsStart = 0, end: paramsEnd = count - 1} = params;
8971
+ const {start: segmentStart, end: segmentEnd} = segment;
8972
+ const start = Math.max(paramsStart, segmentStart);
8973
+ const end = Math.min(paramsEnd, segmentEnd);
8974
+ const outside = paramsStart < segmentStart && paramsEnd < segmentStart || paramsStart > segmentEnd && paramsEnd > segmentEnd;
8975
+ return {
8976
+ count,
8977
+ start,
8978
+ loop: segment.loop,
8979
+ ilen: end < start && !outside ? count + end - start : end - start
8980
+ };
8981
+ }
8982
+ function pathSegment(ctx, line, segment, params) {
8983
+ const {points, options} = line;
8984
+ const {count, start, loop, ilen} = pathVars(points, segment, params);
8985
+ const lineMethod = getLineMethod(options);
8986
+ let {move = true, reverse} = params || {};
8987
+ let i, point, prev;
8988
+ for (i = 0; i <= ilen; ++i) {
8989
+ point = points[(start + (reverse ? ilen - i : i)) % count];
8990
+ if (point.skip) {
8991
+ continue;
8992
+ } else if (move) {
8993
+ ctx.moveTo(point.x, point.y);
8994
+ move = false;
8995
+ } else {
8996
+ lineMethod(ctx, prev, point, reverse, options.stepped);
8997
+ }
8998
+ prev = point;
8999
+ }
9000
+ if (loop) {
9001
+ point = points[(start + (reverse ? ilen : 0)) % count];
9002
+ lineMethod(ctx, prev, point, reverse, options.stepped);
9003
+ }
9004
+ return !!loop;
9005
+ }
9006
+ function fastPathSegment(ctx, line, segment, params) {
9007
+ const points = line.points;
9008
+ const {count, start, ilen} = pathVars(points, segment, params);
9009
+ const {move = true, reverse} = params || {};
9010
+ let avgX = 0;
9011
+ let countX = 0;
9012
+ let i, point, prevX, minY, maxY, lastY;
9013
+ const pointIndex = (index) => (start + (reverse ? ilen - index : index)) % count;
9014
+ const drawX = () => {
9015
+ if (minY !== maxY) {
9016
+ ctx.lineTo(avgX, maxY);
9017
+ ctx.lineTo(avgX, minY);
9018
+ ctx.lineTo(avgX, lastY);
9019
+ }
9020
+ };
9021
+ if (move) {
9022
+ point = points[pointIndex(0)];
9023
+ ctx.moveTo(point.x, point.y);
9024
+ }
9025
+ for (i = 0; i <= ilen; ++i) {
9026
+ point = points[pointIndex(i)];
9027
+ if (point.skip) {
9028
+ continue;
9029
+ }
9030
+ const x = point.x;
9031
+ const y = point.y;
9032
+ const truncX = x | 0;
9033
+ if (truncX === prevX) {
9034
+ if (y < minY) {
9035
+ minY = y;
9036
+ } else if (y > maxY) {
9037
+ maxY = y;
9038
+ }
9039
+ avgX = (countX * avgX + x) / ++countX;
9040
+ } else {
9041
+ drawX();
9042
+ ctx.lineTo(x, y);
9043
+ prevX = truncX;
9044
+ countX = 0;
9045
+ minY = maxY = y;
9046
+ }
9047
+ lastY = y;
9048
+ }
9049
+ drawX();
9050
+ }
9051
+ function _getSegmentMethod(line) {
9052
+ const opts = line.options;
9053
+ const borderDash = opts.borderDash && opts.borderDash.length;
9054
+ const useFastPath = !line._decimated && !line._loop && !opts.tension && opts.cubicInterpolationMode !== 'monotone' && !opts.stepped && !borderDash;
9055
+ return useFastPath ? fastPathSegment : pathSegment;
9056
+ }
9057
+ function _getInterpolationMethod(options) {
9058
+ if (options.stepped) {
9059
+ return _steppedInterpolation;
9060
+ }
9061
+ if (options.tension || options.cubicInterpolationMode === 'monotone') {
9062
+ return _bezierInterpolation;
9063
+ }
9064
+ return _pointInLine;
9065
+ }
9066
+ function strokePathWithCache(ctx, line, start, count) {
9067
+ let path = line._path;
9068
+ if (!path) {
9069
+ path = line._path = new Path2D();
9070
+ if (line.path(path, start, count)) {
9071
+ path.closePath();
9072
+ }
9073
+ }
9074
+ setStyle(ctx, line.options);
9075
+ ctx.stroke(path);
9076
+ }
9077
+ function strokePathDirect(ctx, line, start, count) {
9078
+ const {segments, options} = line;
9079
+ const segmentMethod = _getSegmentMethod(line);
9080
+ for (const segment of segments) {
9081
+ setStyle(ctx, options, segment.style);
9082
+ ctx.beginPath();
9083
+ if (segmentMethod(ctx, line, segment, {start, end: start + count - 1})) {
9084
+ ctx.closePath();
9085
+ }
9086
+ ctx.stroke();
9087
+ }
9088
+ }
9089
+ const usePath2D = typeof Path2D === 'function';
9090
+ function draw(ctx, line, start, count) {
9091
+ if (usePath2D && line.segments.length === 1) {
9092
+ strokePathWithCache(ctx, line, start, count);
9093
+ } else {
9094
+ strokePathDirect(ctx, line, start, count);
9095
+ }
9096
+ }
9097
+ class LineElement extends Element {
9098
+ constructor(cfg) {
9099
+ super();
9100
+ this.animated = true;
9101
+ this.options = undefined;
9102
+ this._loop = undefined;
9103
+ this._fullLoop = undefined;
9104
+ this._path = undefined;
9105
+ this._points = undefined;
9106
+ this._segments = undefined;
9107
+ this._decimated = false;
9108
+ this._pointsUpdated = false;
9109
+ this._datasetIndex = undefined;
9110
+ if (cfg) {
9111
+ Object.assign(this, cfg);
9112
+ }
9113
+ }
9114
+ updateControlPoints(chartArea, indexAxis) {
9115
+ const me = this;
9116
+ const options = me.options;
9117
+ if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !me._pointsUpdated) {
9118
+ const loop = options.spanGaps ? me._loop : me._fullLoop;
9119
+ _updateBezierControlPoints(me._points, options, chartArea, loop, indexAxis);
9120
+ me._pointsUpdated = true;
9121
+ }
9122
+ }
9123
+ set points(points) {
9124
+ const me = this;
9125
+ me._points = points;
9126
+ delete me._segments;
9127
+ delete me._path;
9128
+ me._pointsUpdated = false;
9129
+ }
9130
+ get points() {
9131
+ return this._points;
9132
+ }
9133
+ get segments() {
9134
+ return this._segments || (this._segments = _computeSegments(this, this.options.segment));
9135
+ }
9136
+ first() {
9137
+ const segments = this.segments;
9138
+ const points = this.points;
9139
+ return segments.length && points[segments[0].start];
9140
+ }
9141
+ last() {
9142
+ const segments = this.segments;
9143
+ const points = this.points;
9144
+ const count = segments.length;
9145
+ return count && points[segments[count - 1].end];
9146
+ }
9147
+ interpolate(point, property) {
9148
+ const me = this;
9149
+ const options = me.options;
9150
+ const value = point[property];
9151
+ const points = me.points;
9152
+ const segments = _boundSegments(me, {property, start: value, end: value});
9153
+ if (!segments.length) {
9154
+ return;
9155
+ }
9156
+ const result = [];
9157
+ const _interpolate = _getInterpolationMethod(options);
9158
+ let i, ilen;
9159
+ for (i = 0, ilen = segments.length; i < ilen; ++i) {
9160
+ const {start, end} = segments[i];
9161
+ const p1 = points[start];
9162
+ const p2 = points[end];
9163
+ if (p1 === p2) {
9164
+ result.push(p1);
9165
+ continue;
9166
+ }
9167
+ const t = Math.abs((value - p1[property]) / (p2[property] - p1[property]));
9168
+ const interpolated = _interpolate(p1, p2, t, options.stepped);
9169
+ interpolated[property] = point[property];
9170
+ result.push(interpolated);
9171
+ }
9172
+ return result.length === 1 ? result[0] : result;
9173
+ }
9174
+ pathSegment(ctx, segment, params) {
9175
+ const segmentMethod = _getSegmentMethod(this);
9176
+ return segmentMethod(ctx, this, segment, params);
9177
+ }
9178
+ path(ctx, start, count) {
9179
+ const me = this;
9180
+ const segments = me.segments;
9181
+ const segmentMethod = _getSegmentMethod(me);
9182
+ let loop = me._loop;
9183
+ start = start || 0;
9184
+ count = count || (me.points.length - start);
9185
+ for (const segment of segments) {
9186
+ loop &= segmentMethod(ctx, me, segment, {start, end: start + count - 1});
9187
+ }
9188
+ return !!loop;
9189
+ }
9190
+ draw(ctx, chartArea, start, count) {
9191
+ const me = this;
9192
+ const options = me.options || {};
9193
+ const points = me.points || [];
9194
+ if (!points.length || !options.borderWidth) {
9195
+ return;
9196
+ }
9197
+ ctx.save();
9198
+ draw(ctx, me, start, count);
9199
+ ctx.restore();
9200
+ if (me.animated) {
9201
+ me._pointsUpdated = false;
9202
+ me._path = undefined;
9203
+ }
9204
+ }
9205
+ }
9206
+ LineElement.id = 'line';
9207
+ LineElement.defaults = {
9208
+ borderCapStyle: 'butt',
9209
+ borderDash: [],
9210
+ borderDashOffset: 0,
9211
+ borderJoinStyle: 'miter',
9212
+ borderWidth: 3,
9213
+ capBezierPoints: true,
9214
+ cubicInterpolationMode: 'default',
9215
+ fill: false,
9216
+ spanGaps: false,
9217
+ stepped: false,
9218
+ tension: 0,
9219
+ };
9220
+ LineElement.defaultRoutes = {
9221
+ backgroundColor: 'backgroundColor',
9222
+ borderColor: 'borderColor'
9223
+ };
9224
+ LineElement.descriptors = {
9225
+ _scriptable: true,
9226
+ _indexable: (name) => name !== 'borderDash' && name !== 'fill',
9227
+ };
9228
+
9229
+ function inRange$1(el, pos, axis, useFinalPosition) {
9230
+ const options = el.options;
9231
+ const {[axis]: value} = el.getProps([axis], useFinalPosition);
9232
+ return (Math.abs(pos - value) < options.radius + options.hitRadius);
9233
+ }
9234
+ class PointElement extends Element {
9235
+ constructor(cfg) {
9236
+ super();
9237
+ this.options = undefined;
9238
+ this.parsed = undefined;
9239
+ this.skip = undefined;
9240
+ this.stop = undefined;
9241
+ if (cfg) {
9242
+ Object.assign(this, cfg);
9243
+ }
9244
+ }
9245
+ inRange(mouseX, mouseY, useFinalPosition) {
9246
+ const options = this.options;
9247
+ const {x, y} = this.getProps(['x', 'y'], useFinalPosition);
9248
+ return ((Math.pow(mouseX - x, 2) + Math.pow(mouseY - y, 2)) < Math.pow(options.hitRadius + options.radius, 2));
9249
+ }
9250
+ inXRange(mouseX, useFinalPosition) {
9251
+ return inRange$1(this, mouseX, 'x', useFinalPosition);
9252
+ }
9253
+ inYRange(mouseY, useFinalPosition) {
9254
+ return inRange$1(this, mouseY, 'y', useFinalPosition);
9255
+ }
9256
+ getCenterPoint(useFinalPosition) {
9257
+ const {x, y} = this.getProps(['x', 'y'], useFinalPosition);
9258
+ return {x, y};
9259
+ }
9260
+ size(options) {
9261
+ options = options || this.options || {};
9262
+ let radius = options.radius || 0;
9263
+ radius = Math.max(radius, radius && options.hoverRadius || 0);
9264
+ const borderWidth = radius && options.borderWidth || 0;
9265
+ return (radius + borderWidth) * 2;
9266
+ }
9267
+ draw(ctx, area) {
9268
+ const me = this;
9269
+ const options = me.options;
9270
+ if (me.skip || options.radius < 0.1 || !_isPointInArea(me, area, me.size(options) / 2)) {
9271
+ return;
9272
+ }
9273
+ ctx.strokeStyle = options.borderColor;
9274
+ ctx.lineWidth = options.borderWidth;
9275
+ ctx.fillStyle = options.backgroundColor;
9276
+ drawPoint(ctx, options, me.x, me.y);
9277
+ }
9278
+ getRange() {
9279
+ const options = this.options || {};
9280
+ return options.radius + options.hitRadius;
9281
+ }
9282
+ }
9283
+ PointElement.id = 'point';
9284
+ PointElement.defaults = {
9285
+ borderWidth: 1,
9286
+ hitRadius: 1,
9287
+ hoverBorderWidth: 1,
9288
+ hoverRadius: 4,
9289
+ pointStyle: 'circle',
9290
+ radius: 3,
9291
+ rotation: 0
9292
+ };
9293
+ PointElement.defaultRoutes = {
9294
+ backgroundColor: 'backgroundColor',
9295
+ borderColor: 'borderColor'
9296
+ };
9297
+
9298
+ function getBarBounds(bar, useFinalPosition) {
9299
+ const {x, y, base, width, height} = bar.getProps(['x', 'y', 'base', 'width', 'height'], useFinalPosition);
9300
+ let left, right, top, bottom, half;
9301
+ if (bar.horizontal) {
9302
+ half = height / 2;
9303
+ left = Math.min(x, base);
9304
+ right = Math.max(x, base);
9305
+ top = y - half;
9306
+ bottom = y + half;
9307
+ } else {
9308
+ half = width / 2;
9309
+ left = x - half;
9310
+ right = x + half;
9311
+ top = Math.min(y, base);
9312
+ bottom = Math.max(y, base);
9313
+ }
9314
+ return {left, top, right, bottom};
9315
+ }
9316
+ function skipOrLimit(skip, value, min, max) {
9317
+ return skip ? 0 : _limitValue(value, min, max);
9318
+ }
9319
+ function parseBorderWidth(bar, maxW, maxH) {
9320
+ const value = bar.options.borderWidth;
9321
+ const skip = bar.borderSkipped;
9322
+ const o = toTRBL(value);
9323
+ return {
9324
+ t: skipOrLimit(skip.top, o.top, 0, maxH),
9325
+ r: skipOrLimit(skip.right, o.right, 0, maxW),
9326
+ b: skipOrLimit(skip.bottom, o.bottom, 0, maxH),
9327
+ l: skipOrLimit(skip.left, o.left, 0, maxW)
9328
+ };
9329
+ }
9330
+ function parseBorderRadius(bar, maxW, maxH) {
9331
+ const {enableBorderRadius} = bar.getProps(['enableBorderRadius']);
9332
+ const value = bar.options.borderRadius;
9333
+ const o = toTRBLCorners(value);
9334
+ const maxR = Math.min(maxW, maxH);
9335
+ const skip = bar.borderSkipped;
9336
+ const enableBorder = enableBorderRadius || isObject(value);
9337
+ return {
9338
+ topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),
9339
+ topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),
9340
+ bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),
9341
+ bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)
9342
+ };
9343
+ }
9344
+ function boundingRects(bar) {
9345
+ const bounds = getBarBounds(bar);
9346
+ const width = bounds.right - bounds.left;
9347
+ const height = bounds.bottom - bounds.top;
9348
+ const border = parseBorderWidth(bar, width / 2, height / 2);
9349
+ const radius = parseBorderRadius(bar, width / 2, height / 2);
9350
+ return {
9351
+ outer: {
9352
+ x: bounds.left,
9353
+ y: bounds.top,
9354
+ w: width,
9355
+ h: height,
9356
+ radius
9357
+ },
9358
+ inner: {
9359
+ x: bounds.left + border.l,
9360
+ y: bounds.top + border.t,
9361
+ w: width - border.l - border.r,
9362
+ h: height - border.t - border.b,
9363
+ radius: {
9364
+ topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)),
9365
+ topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)),
9366
+ bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)),
9367
+ bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)),
9368
+ }
9369
+ }
9370
+ };
9371
+ }
9372
+ function inRange(bar, x, y, useFinalPosition) {
9373
+ const skipX = x === null;
9374
+ const skipY = y === null;
9375
+ const skipBoth = skipX && skipY;
9376
+ const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);
9377
+ return bounds
9378
+ && (skipX || x >= bounds.left && x <= bounds.right)
9379
+ && (skipY || y >= bounds.top && y <= bounds.bottom);
9380
+ }
9381
+ function hasRadius(radius) {
9382
+ return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
9383
+ }
9384
+ function addNormalRectPath(ctx, rect) {
9385
+ ctx.rect(rect.x, rect.y, rect.w, rect.h);
9386
+ }
9387
+ function inflateRect(rect, amount, refRect = {}) {
9388
+ const x = rect.x !== refRect.x ? -amount : 0;
9389
+ const y = rect.y !== refRect.y ? -amount : 0;
9390
+ const w = (rect.x + rect.w !== refRect.x + refRect.w ? amount : 0) - x;
9391
+ const h = (rect.y + rect.h !== refRect.y + refRect.h ? amount : 0) - y;
9392
+ return {
9393
+ x: rect.x + x,
9394
+ y: rect.y + y,
9395
+ w: rect.w + w,
9396
+ h: rect.h + h,
9397
+ radius: rect.radius
9398
+ };
9399
+ }
9400
+ class BarElement extends Element {
9401
+ constructor(cfg) {
9402
+ super();
9403
+ this.options = undefined;
9404
+ this.horizontal = undefined;
9405
+ this.base = undefined;
9406
+ this.width = undefined;
9407
+ this.height = undefined;
9408
+ if (cfg) {
9409
+ Object.assign(this, cfg);
9410
+ }
9411
+ }
9412
+ draw(ctx) {
9413
+ const options = this.options;
9414
+ const {inner, outer} = boundingRects(this);
9415
+ const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;
9416
+ const inflateAmount = 0.33;
9417
+ ctx.save();
9418
+ if (outer.w !== inner.w || outer.h !== inner.h) {
9419
+ ctx.beginPath();
9420
+ addRectPath(ctx, inflateRect(outer, inflateAmount, inner));
9421
+ ctx.clip();
9422
+ addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));
9423
+ ctx.fillStyle = options.borderColor;
9424
+ ctx.fill('evenodd');
9425
+ }
9426
+ ctx.beginPath();
9427
+ addRectPath(ctx, inflateRect(inner, inflateAmount, outer));
9428
+ ctx.fillStyle = options.backgroundColor;
9429
+ ctx.fill();
9430
+ ctx.restore();
9431
+ }
9432
+ inRange(mouseX, mouseY, useFinalPosition) {
9433
+ return inRange(this, mouseX, mouseY, useFinalPosition);
9434
+ }
9435
+ inXRange(mouseX, useFinalPosition) {
9436
+ return inRange(this, mouseX, null, useFinalPosition);
9437
+ }
9438
+ inYRange(mouseY, useFinalPosition) {
9439
+ return inRange(this, null, mouseY, useFinalPosition);
9440
+ }
9441
+ getCenterPoint(useFinalPosition) {
9442
+ const {x, y, base, horizontal} = this.getProps(['x', 'y', 'base', 'horizontal'], useFinalPosition);
9443
+ return {
9444
+ x: horizontal ? (x + base) / 2 : x,
9445
+ y: horizontal ? y : (y + base) / 2
9446
+ };
9447
+ }
9448
+ getRange(axis) {
9449
+ return axis === 'x' ? this.width / 2 : this.height / 2;
9450
+ }
9451
+ }
9452
+ BarElement.id = 'bar';
9453
+ BarElement.defaults = {
9454
+ borderSkipped: 'start',
9455
+ borderWidth: 0,
9456
+ borderRadius: 0,
9457
+ enableBorderRadius: true,
9458
+ pointStyle: undefined
9459
+ };
9460
+ BarElement.defaultRoutes = {
9461
+ backgroundColor: 'backgroundColor',
9462
+ borderColor: 'borderColor'
9463
+ };
9464
+
9465
+ var elements = /*#__PURE__*/Object.freeze({
9466
+ __proto__: null,
9467
+ ArcElement: ArcElement,
9468
+ LineElement: LineElement,
9469
+ PointElement: PointElement,
9470
+ BarElement: BarElement
9471
+ });
9472
+
9473
+ function lttbDecimation(data, start, count, availableWidth, options) {
9474
+ const samples = options.samples || availableWidth;
9475
+ if (samples >= count) {
9476
+ return data.slice(start, start + count);
9477
+ }
9478
+ const decimated = [];
9479
+ const bucketWidth = (count - 2) / (samples - 2);
9480
+ let sampledIndex = 0;
9481
+ const endIndex = start + count - 1;
9482
+ let a = start;
9483
+ let i, maxAreaPoint, maxArea, area, nextA;
9484
+ decimated[sampledIndex++] = data[a];
9485
+ for (i = 0; i < samples - 2; i++) {
9486
+ let avgX = 0;
9487
+ let avgY = 0;
9488
+ let j;
9489
+ const avgRangeStart = Math.floor((i + 1) * bucketWidth) + 1 + start;
9490
+ const avgRangeEnd = Math.min(Math.floor((i + 2) * bucketWidth) + 1, count) + start;
9491
+ const avgRangeLength = avgRangeEnd - avgRangeStart;
9492
+ for (j = avgRangeStart; j < avgRangeEnd; j++) {
9493
+ avgX += data[j].x;
9494
+ avgY += data[j].y;
9495
+ }
9496
+ avgX /= avgRangeLength;
9497
+ avgY /= avgRangeLength;
9498
+ const rangeOffs = Math.floor(i * bucketWidth) + 1 + start;
9499
+ const rangeTo = Math.min(Math.floor((i + 1) * bucketWidth) + 1, count) + start;
9500
+ const {x: pointAx, y: pointAy} = data[a];
9501
+ maxArea = area = -1;
9502
+ for (j = rangeOffs; j < rangeTo; j++) {
9503
+ area = 0.5 * Math.abs(
9504
+ (pointAx - avgX) * (data[j].y - pointAy) -
9505
+ (pointAx - data[j].x) * (avgY - pointAy)
9506
+ );
9507
+ if (area > maxArea) {
9508
+ maxArea = area;
9509
+ maxAreaPoint = data[j];
9510
+ nextA = j;
9511
+ }
9512
+ }
9513
+ decimated[sampledIndex++] = maxAreaPoint;
9514
+ a = nextA;
9515
+ }
9516
+ decimated[sampledIndex++] = data[endIndex];
9517
+ return decimated;
9518
+ }
9519
+ function minMaxDecimation(data, start, count, availableWidth) {
9520
+ let avgX = 0;
9521
+ let countX = 0;
9522
+ let i, point, x, y, prevX, minIndex, maxIndex, startIndex, minY, maxY;
9523
+ const decimated = [];
9524
+ const endIndex = start + count - 1;
9525
+ const xMin = data[start].x;
9526
+ const xMax = data[endIndex].x;
9527
+ const dx = xMax - xMin;
9528
+ for (i = start; i < start + count; ++i) {
9529
+ point = data[i];
9530
+ x = (point.x - xMin) / dx * availableWidth;
9531
+ y = point.y;
9532
+ const truncX = x | 0;
9533
+ if (truncX === prevX) {
9534
+ if (y < minY) {
9535
+ minY = y;
9536
+ minIndex = i;
9537
+ } else if (y > maxY) {
9538
+ maxY = y;
9539
+ maxIndex = i;
9540
+ }
9541
+ avgX = (countX * avgX + point.x) / ++countX;
9542
+ } else {
9543
+ const lastIndex = i - 1;
9544
+ if (!isNullOrUndef(minIndex) && !isNullOrUndef(maxIndex)) {
9545
+ const intermediateIndex1 = Math.min(minIndex, maxIndex);
9546
+ const intermediateIndex2 = Math.max(minIndex, maxIndex);
9547
+ if (intermediateIndex1 !== startIndex && intermediateIndex1 !== lastIndex) {
9548
+ decimated.push({
9549
+ ...data[intermediateIndex1],
9550
+ x: avgX,
9551
+ });
9552
+ }
9553
+ if (intermediateIndex2 !== startIndex && intermediateIndex2 !== lastIndex) {
9554
+ decimated.push({
9555
+ ...data[intermediateIndex2],
9556
+ x: avgX
9557
+ });
9558
+ }
9559
+ }
9560
+ if (i > 0 && lastIndex !== startIndex) {
9561
+ decimated.push(data[lastIndex]);
9562
+ }
9563
+ decimated.push(point);
9564
+ prevX = truncX;
9565
+ countX = 0;
9566
+ minY = maxY = y;
9567
+ minIndex = maxIndex = startIndex = i;
9568
+ }
9569
+ }
9570
+ return decimated;
9571
+ }
9572
+ function cleanDecimatedDataset(dataset) {
9573
+ if (dataset._decimated) {
9574
+ const data = dataset._data;
9575
+ delete dataset._decimated;
9576
+ delete dataset._data;
9577
+ Object.defineProperty(dataset, 'data', {value: data});
9578
+ }
9579
+ }
9580
+ function cleanDecimatedData(chart) {
9581
+ chart.data.datasets.forEach((dataset) => {
9582
+ cleanDecimatedDataset(dataset);
9583
+ });
9584
+ }
9585
+ function getStartAndCountOfVisiblePointsSimplified(meta, points) {
9586
+ const pointCount = points.length;
9587
+ let start = 0;
9588
+ let count;
9589
+ const {iScale} = meta;
9590
+ const {min, max, minDefined, maxDefined} = iScale.getUserBounds();
9591
+ if (minDefined) {
9592
+ start = _limitValue(_lookupByKey(points, iScale.axis, min).lo, 0, pointCount - 1);
9593
+ }
9594
+ if (maxDefined) {
9595
+ count = _limitValue(_lookupByKey(points, iScale.axis, max).hi + 1, start, pointCount) - start;
9596
+ } else {
9597
+ count = pointCount - start;
9598
+ }
9599
+ return {start, count};
9600
+ }
9601
+ var plugin_decimation = {
9602
+ id: 'decimation',
9603
+ defaults: {
9604
+ algorithm: 'min-max',
9605
+ enabled: false,
9606
+ },
9607
+ beforeElementsUpdate: (chart, args, options) => {
9608
+ if (!options.enabled) {
9609
+ cleanDecimatedData(chart);
9610
+ return;
9611
+ }
9612
+ const availableWidth = chart.width;
9613
+ chart.data.datasets.forEach((dataset, datasetIndex) => {
9614
+ const {_data, indexAxis} = dataset;
9615
+ const meta = chart.getDatasetMeta(datasetIndex);
9616
+ const data = _data || dataset.data;
9617
+ if (resolve([indexAxis, chart.options.indexAxis]) === 'y') {
9618
+ return;
9619
+ }
9620
+ if (meta.type !== 'line') {
9621
+ return;
9622
+ }
9623
+ const xAxis = chart.scales[meta.xAxisID];
9624
+ if (xAxis.type !== 'linear' && xAxis.type !== 'time') {
9625
+ return;
9626
+ }
9627
+ if (chart.options.parsing) {
9628
+ return;
9629
+ }
9630
+ let {start, count} = getStartAndCountOfVisiblePointsSimplified(meta, data);
9631
+ const threshold = options.threshold || 4 * availableWidth;
9632
+ if (count <= threshold) {
9633
+ cleanDecimatedDataset(dataset);
9634
+ return;
9635
+ }
9636
+ if (isNullOrUndef(_data)) {
9637
+ dataset._data = data;
9638
+ delete dataset.data;
9639
+ Object.defineProperty(dataset, 'data', {
9640
+ configurable: true,
9641
+ enumerable: true,
9642
+ get: function() {
9643
+ return this._decimated;
9644
+ },
9645
+ set: function(d) {
9646
+ this._data = d;
9647
+ }
9648
+ });
9649
+ }
9650
+ let decimated;
9651
+ switch (options.algorithm) {
9652
+ case 'lttb':
9653
+ decimated = lttbDecimation(data, start, count, availableWidth, options);
9654
+ break;
9655
+ case 'min-max':
9656
+ decimated = minMaxDecimation(data, start, count, availableWidth);
9657
+ break;
9658
+ default:
9659
+ throw new Error(`Unsupported decimation algorithm '${options.algorithm}'`);
9660
+ }
9661
+ dataset._decimated = decimated;
9662
+ });
9663
+ },
9664
+ destroy(chart) {
9665
+ cleanDecimatedData(chart);
9666
+ }
9667
+ };
9668
+
9669
+ function getLineByIndex(chart, index) {
9670
+ const meta = chart.getDatasetMeta(index);
9671
+ const visible = meta && chart.isDatasetVisible(index);
9672
+ return visible ? meta.dataset : null;
9673
+ }
9674
+ function parseFillOption(line) {
9675
+ const options = line.options;
9676
+ const fillOption = options.fill;
9677
+ let fill = valueOrDefault(fillOption && fillOption.target, fillOption);
9678
+ if (fill === undefined) {
9679
+ fill = !!options.backgroundColor;
9680
+ }
9681
+ if (fill === false || fill === null) {
9682
+ return false;
9683
+ }
9684
+ if (fill === true) {
9685
+ return 'origin';
9686
+ }
9687
+ return fill;
9688
+ }
9689
+ function decodeFill(line, index, count) {
9690
+ const fill = parseFillOption(line);
9691
+ if (isObject(fill)) {
9692
+ return isNaN(fill.value) ? false : fill;
9693
+ }
9694
+ let target = parseFloat(fill);
9695
+ if (isNumberFinite(target) && Math.floor(target) === target) {
9696
+ if (fill[0] === '-' || fill[0] === '+') {
9697
+ target = index + target;
9698
+ }
9699
+ if (target === index || target < 0 || target >= count) {
9700
+ return false;
9701
+ }
9702
+ return target;
9703
+ }
9704
+ return ['origin', 'start', 'end', 'stack', 'shape'].indexOf(fill) >= 0 && fill;
9705
+ }
9706
+ function computeLinearBoundary(source) {
9707
+ const {scale = {}, fill} = source;
9708
+ let target = null;
9709
+ let horizontal;
9710
+ if (fill === 'start') {
9711
+ target = scale.bottom;
9712
+ } else if (fill === 'end') {
9713
+ target = scale.top;
9714
+ } else if (isObject(fill)) {
9715
+ target = scale.getPixelForValue(fill.value);
9716
+ } else if (scale.getBasePixel) {
9717
+ target = scale.getBasePixel();
9718
+ }
9719
+ if (isNumberFinite(target)) {
9720
+ horizontal = scale.isHorizontal();
9721
+ return {
9722
+ x: horizontal ? target : null,
9723
+ y: horizontal ? null : target
9724
+ };
9725
+ }
9726
+ return null;
9727
+ }
9728
+ class simpleArc {
9729
+ constructor(opts) {
9730
+ this.x = opts.x;
9731
+ this.y = opts.y;
9732
+ this.radius = opts.radius;
9733
+ }
9734
+ pathSegment(ctx, bounds, opts) {
9735
+ const {x, y, radius} = this;
9736
+ bounds = bounds || {start: 0, end: TAU};
9737
+ ctx.arc(x, y, radius, bounds.end, bounds.start, true);
9738
+ return !opts.bounds;
9739
+ }
9740
+ interpolate(point) {
9741
+ const {x, y, radius} = this;
9742
+ const angle = point.angle;
9743
+ return {
9744
+ x: x + Math.cos(angle) * radius,
9745
+ y: y + Math.sin(angle) * radius,
9746
+ angle
9747
+ };
9748
+ }
9749
+ }
9750
+ function computeCircularBoundary(source) {
9751
+ const {scale, fill} = source;
9752
+ const options = scale.options;
9753
+ const length = scale.getLabels().length;
9754
+ const target = [];
9755
+ const start = options.reverse ? scale.max : scale.min;
9756
+ const end = options.reverse ? scale.min : scale.max;
9757
+ let i, center, value;
9758
+ if (fill === 'start') {
9759
+ value = start;
9760
+ } else if (fill === 'end') {
9761
+ value = end;
9762
+ } else if (isObject(fill)) {
9763
+ value = fill.value;
9764
+ } else {
9765
+ value = scale.getBaseValue();
9766
+ }
9767
+ if (options.grid.circular) {
9768
+ center = scale.getPointPositionForValue(0, start);
9769
+ return new simpleArc({
9770
+ x: center.x,
9771
+ y: center.y,
9772
+ radius: scale.getDistanceFromCenterForValue(value)
9773
+ });
9774
+ }
9775
+ for (i = 0; i < length; ++i) {
9776
+ target.push(scale.getPointPositionForValue(i, value));
9777
+ }
9778
+ return target;
9779
+ }
9780
+ function computeBoundary(source) {
9781
+ const scale = source.scale || {};
9782
+ if (scale.getPointPositionForValue) {
9783
+ return computeCircularBoundary(source);
9784
+ }
9785
+ return computeLinearBoundary(source);
9786
+ }
9787
+ function findSegmentEnd(start, end, points) {
9788
+ for (;end > start; end--) {
9789
+ const point = points[end];
9790
+ if (!isNaN(point.x) && !isNaN(point.y)) {
9791
+ break;
9792
+ }
9793
+ }
9794
+ return end;
9795
+ }
9796
+ function pointsFromSegments(boundary, line) {
9797
+ const {x = null, y = null} = boundary || {};
9798
+ const linePoints = line.points;
9799
+ const points = [];
9800
+ line.segments.forEach(({start, end}) => {
9801
+ end = findSegmentEnd(start, end, linePoints);
9802
+ const first = linePoints[start];
9803
+ const last = linePoints[end];
9804
+ if (y !== null) {
9805
+ points.push({x: first.x, y});
9806
+ points.push({x: last.x, y});
9807
+ } else if (x !== null) {
9808
+ points.push({x, y: first.y});
9809
+ points.push({x, y: last.y});
9810
+ }
9811
+ });
9812
+ return points;
9813
+ }
9814
+ function buildStackLine(source) {
9815
+ const {chart, scale, index, line} = source;
9816
+ const points = [];
9817
+ const segments = line.segments;
9818
+ const sourcePoints = line.points;
9819
+ const linesBelow = getLinesBelow(chart, index);
9820
+ linesBelow.push(createBoundaryLine({x: null, y: scale.bottom}, line));
9821
+ for (let i = 0; i < segments.length; i++) {
9822
+ const segment = segments[i];
9823
+ for (let j = segment.start; j <= segment.end; j++) {
9824
+ addPointsBelow(points, sourcePoints[j], linesBelow);
9825
+ }
9826
+ }
9827
+ return new LineElement({points, options: {}});
9828
+ }
9829
+ const isLineAndNotInHideAnimation = (meta) => meta.type === 'line' && !meta.hidden;
9830
+ function getLinesBelow(chart, index) {
9831
+ const below = [];
9832
+ const metas = chart.getSortedVisibleDatasetMetas();
9833
+ for (let i = 0; i < metas.length; i++) {
9834
+ const meta = metas[i];
9835
+ if (meta.index === index) {
9836
+ break;
9837
+ }
9838
+ if (isLineAndNotInHideAnimation(meta)) {
9839
+ below.unshift(meta.dataset);
9840
+ }
9841
+ }
9842
+ return below;
9843
+ }
9844
+ function addPointsBelow(points, sourcePoint, linesBelow) {
9845
+ const postponed = [];
9846
+ for (let j = 0; j < linesBelow.length; j++) {
9847
+ const line = linesBelow[j];
9848
+ const {first, last, point} = findPoint(line, sourcePoint, 'x');
9849
+ if (!point || (first && last)) {
9850
+ continue;
9851
+ }
9852
+ if (first) {
9853
+ postponed.unshift(point);
9854
+ } else {
9855
+ points.push(point);
9856
+ if (!last) {
9857
+ break;
9858
+ }
9859
+ }
9860
+ }
9861
+ points.push(...postponed);
9862
+ }
9863
+ function findPoint(line, sourcePoint, property) {
9864
+ const point = line.interpolate(sourcePoint, property);
9865
+ if (!point) {
9866
+ return {};
9867
+ }
9868
+ const pointValue = point[property];
9869
+ const segments = line.segments;
9870
+ const linePoints = line.points;
9871
+ let first = false;
9872
+ let last = false;
9873
+ for (let i = 0; i < segments.length; i++) {
9874
+ const segment = segments[i];
9875
+ const firstValue = linePoints[segment.start][property];
9876
+ const lastValue = linePoints[segment.end][property];
9877
+ if (pointValue >= firstValue && pointValue <= lastValue) {
9878
+ first = pointValue === firstValue;
9879
+ last = pointValue === lastValue;
9880
+ break;
9881
+ }
9882
+ }
9883
+ return {first, last, point};
9884
+ }
9885
+ function getTarget(source) {
9886
+ const {chart, fill, line} = source;
9887
+ if (isNumberFinite(fill)) {
9888
+ return getLineByIndex(chart, fill);
9889
+ }
9890
+ if (fill === 'stack') {
9891
+ return buildStackLine(source);
9892
+ }
9893
+ if (fill === 'shape') {
9894
+ return true;
9895
+ }
9896
+ const boundary = computeBoundary(source);
9897
+ if (boundary instanceof simpleArc) {
9898
+ return boundary;
9899
+ }
9900
+ return createBoundaryLine(boundary, line);
9901
+ }
9902
+ function createBoundaryLine(boundary, line) {
9903
+ let points = [];
9904
+ let _loop = false;
9905
+ if (isArray(boundary)) {
9906
+ _loop = true;
9907
+ points = boundary;
9908
+ } else {
9909
+ points = pointsFromSegments(boundary, line);
9910
+ }
9911
+ return points.length ? new LineElement({
9912
+ points,
9913
+ options: {tension: 0},
9914
+ _loop,
9915
+ _fullLoop: _loop
9916
+ }) : null;
9917
+ }
9918
+ function resolveTarget(sources, index, propagate) {
9919
+ const source = sources[index];
9920
+ let fill = source.fill;
9921
+ const visited = [index];
9922
+ let target;
9923
+ if (!propagate) {
9924
+ return fill;
9925
+ }
9926
+ while (fill !== false && visited.indexOf(fill) === -1) {
9927
+ if (!isNumberFinite(fill)) {
9928
+ return fill;
9929
+ }
9930
+ target = sources[fill];
9931
+ if (!target) {
9932
+ return false;
9933
+ }
9934
+ if (target.visible) {
9935
+ return fill;
9936
+ }
9937
+ visited.push(fill);
9938
+ fill = target.fill;
9939
+ }
9940
+ return false;
9941
+ }
9942
+ function _clip(ctx, target, clipY) {
9943
+ ctx.beginPath();
9944
+ target.path(ctx);
9945
+ ctx.lineTo(target.last().x, clipY);
9946
+ ctx.lineTo(target.first().x, clipY);
9947
+ ctx.closePath();
9948
+ ctx.clip();
9949
+ }
9950
+ function getBounds(property, first, last, loop) {
9951
+ if (loop) {
9952
+ return;
9953
+ }
9954
+ let start = first[property];
9955
+ let end = last[property];
9956
+ if (property === 'angle') {
9957
+ start = _normalizeAngle(start);
9958
+ end = _normalizeAngle(end);
9959
+ }
9960
+ return {property, start, end};
9961
+ }
9962
+ function _getEdge(a, b, prop, fn) {
9963
+ if (a && b) {
9964
+ return fn(a[prop], b[prop]);
9965
+ }
9966
+ return a ? a[prop] : b ? b[prop] : 0;
9967
+ }
9968
+ function _segments(line, target, property) {
9969
+ const segments = line.segments;
9970
+ const points = line.points;
9971
+ const tpoints = target.points;
9972
+ const parts = [];
9973
+ for (const segment of segments) {
9974
+ let {start, end} = segment;
9975
+ end = findSegmentEnd(start, end, points);
9976
+ const bounds = getBounds(property, points[start], points[end], segment.loop);
9977
+ if (!target.segments) {
9978
+ parts.push({
9979
+ source: segment,
9980
+ target: bounds,
9981
+ start: points[start],
9982
+ end: points[end]
9983
+ });
9984
+ continue;
9985
+ }
9986
+ const targetSegments = _boundSegments(target, bounds);
9987
+ for (const tgt of targetSegments) {
9988
+ const subBounds = getBounds(property, tpoints[tgt.start], tpoints[tgt.end], tgt.loop);
9989
+ const fillSources = _boundSegment(segment, points, subBounds);
9990
+ for (const fillSource of fillSources) {
9991
+ parts.push({
9992
+ source: fillSource,
9993
+ target: tgt,
9994
+ start: {
9995
+ [property]: _getEdge(bounds, subBounds, 'start', Math.max)
9996
+ },
9997
+ end: {
9998
+ [property]: _getEdge(bounds, subBounds, 'end', Math.min)
9999
+ }
10000
+ });
10001
+ }
10002
+ }
10003
+ }
10004
+ return parts;
10005
+ }
10006
+ function clipBounds(ctx, scale, bounds) {
10007
+ const {top, bottom} = scale.chart.chartArea;
10008
+ const {property, start, end} = bounds || {};
10009
+ if (property === 'x') {
10010
+ ctx.beginPath();
10011
+ ctx.rect(start, top, end - start, bottom - top);
10012
+ ctx.clip();
10013
+ }
10014
+ }
10015
+ function interpolatedLineTo(ctx, target, point, property) {
10016
+ const interpolatedPoint = target.interpolate(point, property);
10017
+ if (interpolatedPoint) {
10018
+ ctx.lineTo(interpolatedPoint.x, interpolatedPoint.y);
10019
+ }
10020
+ }
10021
+ function _fill(ctx, cfg) {
10022
+ const {line, target, property, color, scale} = cfg;
10023
+ const segments = _segments(line, target, property);
10024
+ for (const {source: src, target: tgt, start, end} of segments) {
10025
+ const {style: {backgroundColor = color} = {}} = src;
10026
+ const notShape = target !== true;
10027
+ ctx.save();
10028
+ ctx.fillStyle = backgroundColor;
10029
+ clipBounds(ctx, scale, notShape && getBounds(property, start, end));
10030
+ ctx.beginPath();
10031
+ const lineLoop = !!line.pathSegment(ctx, src);
10032
+ let loop;
10033
+ if (notShape) {
10034
+ if (lineLoop) {
10035
+ ctx.closePath();
10036
+ } else {
10037
+ interpolatedLineTo(ctx, target, end, property);
10038
+ }
10039
+ const targetLoop = !!target.pathSegment(ctx, tgt, {move: lineLoop, reverse: true});
10040
+ loop = lineLoop && targetLoop;
10041
+ if (!loop) {
10042
+ interpolatedLineTo(ctx, target, start, property);
10043
+ }
10044
+ }
10045
+ ctx.closePath();
10046
+ ctx.fill(loop ? 'evenodd' : 'nonzero');
10047
+ ctx.restore();
10048
+ }
10049
+ }
10050
+ function doFill(ctx, cfg) {
10051
+ const {line, target, above, below, area, scale} = cfg;
10052
+ const property = line._loop ? 'angle' : cfg.axis;
10053
+ ctx.save();
10054
+ if (property === 'x' && below !== above) {
10055
+ _clip(ctx, target, area.top);
10056
+ _fill(ctx, {line, target, color: above, scale, property});
10057
+ ctx.restore();
10058
+ ctx.save();
10059
+ _clip(ctx, target, area.bottom);
10060
+ }
10061
+ _fill(ctx, {line, target, color: below, scale, property});
10062
+ ctx.restore();
10063
+ }
10064
+ function drawfill(ctx, source, area) {
10065
+ const target = getTarget(source);
10066
+ const {line, scale, axis} = source;
10067
+ const lineOpts = line.options;
10068
+ const fillOption = lineOpts.fill;
10069
+ const color = lineOpts.backgroundColor;
10070
+ const {above = color, below = color} = fillOption || {};
10071
+ if (target && line.points.length) {
10072
+ clipArea(ctx, area);
10073
+ doFill(ctx, {line, target, above, below, area, scale, axis});
10074
+ unclipArea(ctx);
10075
+ }
10076
+ }
10077
+ var plugin_filler = {
10078
+ id: 'filler',
10079
+ afterDatasetsUpdate(chart, _args, options) {
10080
+ const count = (chart.data.datasets || []).length;
10081
+ const sources = [];
10082
+ let meta, i, line, source;
10083
+ for (i = 0; i < count; ++i) {
10084
+ meta = chart.getDatasetMeta(i);
10085
+ line = meta.dataset;
10086
+ source = null;
10087
+ if (line && line.options && line instanceof LineElement) {
10088
+ source = {
10089
+ visible: chart.isDatasetVisible(i),
10090
+ index: i,
10091
+ fill: decodeFill(line, i, count),
10092
+ chart,
10093
+ axis: meta.controller.options.indexAxis,
10094
+ scale: meta.vScale,
10095
+ line,
10096
+ };
10097
+ }
10098
+ meta.$filler = source;
10099
+ sources.push(source);
10100
+ }
10101
+ for (i = 0; i < count; ++i) {
10102
+ source = sources[i];
10103
+ if (!source || source.fill === false) {
10104
+ continue;
10105
+ }
10106
+ source.fill = resolveTarget(sources, i, options.propagate);
10107
+ }
10108
+ },
10109
+ beforeDraw(chart, _args, options) {
10110
+ const draw = options.drawTime === 'beforeDraw';
10111
+ const metasets = chart.getSortedVisibleDatasetMetas();
10112
+ const area = chart.chartArea;
10113
+ for (let i = metasets.length - 1; i >= 0; --i) {
10114
+ const source = metasets[i].$filler;
10115
+ if (!source) {
10116
+ continue;
10117
+ }
10118
+ source.line.updateControlPoints(area, source.axis);
10119
+ if (draw) {
10120
+ drawfill(chart.ctx, source, area);
10121
+ }
10122
+ }
10123
+ },
10124
+ beforeDatasetsDraw(chart, _args, options) {
10125
+ if (options.drawTime !== 'beforeDatasetsDraw') {
10126
+ return;
10127
+ }
10128
+ const metasets = chart.getSortedVisibleDatasetMetas();
10129
+ for (let i = metasets.length - 1; i >= 0; --i) {
10130
+ const source = metasets[i].$filler;
10131
+ if (source) {
10132
+ drawfill(chart.ctx, source, chart.chartArea);
10133
+ }
10134
+ }
10135
+ },
10136
+ beforeDatasetDraw(chart, args, options) {
10137
+ const source = args.meta.$filler;
10138
+ if (!source || source.fill === false || options.drawTime !== 'beforeDatasetDraw') {
10139
+ return;
10140
+ }
10141
+ drawfill(chart.ctx, source, chart.chartArea);
10142
+ },
10143
+ defaults: {
10144
+ propagate: true,
10145
+ drawTime: 'beforeDatasetDraw'
10146
+ }
10147
+ };
10148
+
10149
+ const getBoxSize = (labelOpts, fontSize) => {
10150
+ let {boxHeight = fontSize, boxWidth = fontSize} = labelOpts;
10151
+ if (labelOpts.usePointStyle) {
10152
+ boxHeight = Math.min(boxHeight, fontSize);
10153
+ boxWidth = Math.min(boxWidth, fontSize);
10154
+ }
10155
+ return {
10156
+ boxWidth,
10157
+ boxHeight,
10158
+ itemHeight: Math.max(fontSize, boxHeight)
10159
+ };
10160
+ };
10161
+ const itemsEqual = (a, b) => a !== null && b !== null && a.datasetIndex === b.datasetIndex && a.index === b.index;
10162
+ class Legend extends Element {
10163
+ constructor(config) {
10164
+ super();
10165
+ this._added = false;
10166
+ this.legendHitBoxes = [];
10167
+ this._hoveredItem = null;
10168
+ this.doughnutMode = false;
10169
+ this.chart = config.chart;
10170
+ this.options = config.options;
10171
+ this.ctx = config.ctx;
10172
+ this.legendItems = undefined;
10173
+ this.columnSizes = undefined;
10174
+ this.lineWidths = undefined;
10175
+ this.maxHeight = undefined;
10176
+ this.maxWidth = undefined;
10177
+ this.top = undefined;
10178
+ this.bottom = undefined;
10179
+ this.left = undefined;
10180
+ this.right = undefined;
10181
+ this.height = undefined;
10182
+ this.width = undefined;
10183
+ this._margins = undefined;
10184
+ this.position = undefined;
10185
+ this.weight = undefined;
10186
+ this.fullSize = undefined;
10187
+ }
10188
+ update(maxWidth, maxHeight, margins) {
10189
+ const me = this;
10190
+ me.maxWidth = maxWidth;
10191
+ me.maxHeight = maxHeight;
10192
+ me._margins = margins;
10193
+ me.setDimensions();
10194
+ me.buildLabels();
10195
+ me.fit();
10196
+ }
10197
+ setDimensions() {
10198
+ const me = this;
10199
+ if (me.isHorizontal()) {
10200
+ me.width = me.maxWidth;
10201
+ me.left = me._margins.left;
10202
+ me.right = me.width;
10203
+ } else {
10204
+ me.height = me.maxHeight;
10205
+ me.top = me._margins.top;
10206
+ me.bottom = me.height;
10207
+ }
10208
+ }
10209
+ buildLabels() {
10210
+ const me = this;
10211
+ const labelOpts = me.options.labels || {};
10212
+ let legendItems = callback(labelOpts.generateLabels, [me.chart], me) || [];
10213
+ if (labelOpts.filter) {
10214
+ legendItems = legendItems.filter((item) => labelOpts.filter(item, me.chart.data));
10215
+ }
10216
+ if (labelOpts.sort) {
10217
+ legendItems = legendItems.sort((a, b) => labelOpts.sort(a, b, me.chart.data));
10218
+ }
10219
+ if (me.options.reverse) {
10220
+ legendItems.reverse();
10221
+ }
10222
+ me.legendItems = legendItems;
10223
+ }
10224
+ fit() {
10225
+ const me = this;
10226
+ const {options, ctx} = me;
10227
+ if (!options.display) {
10228
+ me.width = me.height = 0;
10229
+ return;
10230
+ }
10231
+ const labelOpts = options.labels;
10232
+ const labelFont = toFont(labelOpts.font);
10233
+ const fontSize = labelFont.size;
10234
+ const titleHeight = me._computeTitleHeight();
10235
+ const {boxWidth, itemHeight} = getBoxSize(labelOpts, fontSize);
10236
+ let width, height;
10237
+ ctx.font = labelFont.string;
10238
+ if (me.isHorizontal()) {
10239
+ width = me.maxWidth;
10240
+ height = me._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;
10241
+ } else {
10242
+ height = me.maxHeight;
10243
+ width = me._fitCols(titleHeight, fontSize, boxWidth, itemHeight) + 10;
10244
+ }
10245
+ me.width = Math.min(width, options.maxWidth || me.maxWidth);
10246
+ me.height = Math.min(height, options.maxHeight || me.maxHeight);
10247
+ }
10248
+ _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {
10249
+ const me = this;
10250
+ const {ctx, maxWidth, options: {labels: {padding}}} = me;
10251
+ const hitboxes = me.legendHitBoxes = [];
10252
+ const lineWidths = me.lineWidths = [0];
10253
+ const lineHeight = itemHeight + padding;
10254
+ let totalHeight = titleHeight;
10255
+ ctx.textAlign = 'left';
10256
+ ctx.textBaseline = 'middle';
10257
+ let row = -1;
10258
+ let top = -lineHeight;
10259
+ me.legendItems.forEach((legendItem, i) => {
10260
+ const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
10261
+ if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {
10262
+ totalHeight += lineHeight;
10263
+ lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
10264
+ top += lineHeight;
10265
+ row++;
10266
+ }
10267
+ hitboxes[i] = {left: 0, top, row, width: itemWidth, height: itemHeight};
10268
+ lineWidths[lineWidths.length - 1] += itemWidth + padding;
10269
+ });
10270
+ return totalHeight;
10271
+ }
10272
+ _fitCols(titleHeight, fontSize, boxWidth, itemHeight) {
10273
+ const me = this;
10274
+ const {ctx, maxHeight, options: {labels: {padding}}} = me;
10275
+ const hitboxes = me.legendHitBoxes = [];
10276
+ const columnSizes = me.columnSizes = [];
10277
+ const heightLimit = maxHeight - titleHeight;
10278
+ let totalWidth = padding;
10279
+ let currentColWidth = 0;
10280
+ let currentColHeight = 0;
10281
+ let left = 0;
10282
+ let col = 0;
10283
+ me.legendItems.forEach((legendItem, i) => {
10284
+ const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
10285
+ if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {
10286
+ totalWidth += currentColWidth + padding;
10287
+ columnSizes.push({width: currentColWidth, height: currentColHeight});
10288
+ left += currentColWidth + padding;
10289
+ col++;
10290
+ currentColWidth = currentColHeight = 0;
10291
+ }
10292
+ hitboxes[i] = {left, top: currentColHeight, col, width: itemWidth, height: itemHeight};
10293
+ currentColWidth = Math.max(currentColWidth, itemWidth);
10294
+ currentColHeight += itemHeight + padding;
10295
+ });
10296
+ totalWidth += currentColWidth;
10297
+ columnSizes.push({width: currentColWidth, height: currentColHeight});
10298
+ return totalWidth;
10299
+ }
10300
+ adjustHitBoxes() {
10301
+ const me = this;
10302
+ if (!me.options.display) {
10303
+ return;
10304
+ }
10305
+ const titleHeight = me._computeTitleHeight();
10306
+ const {legendHitBoxes: hitboxes, options: {align, labels: {padding}, rtl}} = me;
10307
+ const rtlHelper = getRtlAdapter(rtl, me.left, me.width);
10308
+ if (this.isHorizontal()) {
10309
+ let row = 0;
10310
+ let left = _alignStartEnd(align, me.left + padding, me.right - me.lineWidths[row]);
10311
+ for (const hitbox of hitboxes) {
10312
+ if (row !== hitbox.row) {
10313
+ row = hitbox.row;
10314
+ left = _alignStartEnd(align, me.left + padding, me.right - me.lineWidths[row]);
10315
+ }
10316
+ hitbox.top += me.top + titleHeight + padding;
10317
+ hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);
10318
+ left += hitbox.width + padding;
10319
+ }
10320
+ } else {
10321
+ let col = 0;
10322
+ let top = _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - me.columnSizes[col].height);
10323
+ for (const hitbox of hitboxes) {
10324
+ if (hitbox.col !== col) {
10325
+ col = hitbox.col;
10326
+ top = _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - me.columnSizes[col].height);
10327
+ }
10328
+ hitbox.top = top;
10329
+ hitbox.left += me.left + padding;
10330
+ hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width);
10331
+ top += hitbox.height + padding;
10332
+ }
10333
+ }
10334
+ }
10335
+ isHorizontal() {
10336
+ return this.options.position === 'top' || this.options.position === 'bottom';
10337
+ }
10338
+ draw() {
10339
+ const me = this;
10340
+ if (me.options.display) {
10341
+ const ctx = me.ctx;
10342
+ clipArea(ctx, me);
10343
+ me._draw();
10344
+ unclipArea(ctx);
10345
+ }
10346
+ }
10347
+ _draw() {
10348
+ const me = this;
10349
+ const {options: opts, columnSizes, lineWidths, ctx} = me;
10350
+ const {align, labels: labelOpts} = opts;
10351
+ const defaultColor = defaults.color;
10352
+ const rtlHelper = getRtlAdapter(opts.rtl, me.left, me.width);
10353
+ const labelFont = toFont(labelOpts.font);
10354
+ const {color: fontColor, padding} = labelOpts;
10355
+ const fontSize = labelFont.size;
10356
+ const halfFontSize = fontSize / 2;
10357
+ let cursor;
10358
+ me.drawTitle();
10359
+ ctx.textAlign = rtlHelper.textAlign('left');
10360
+ ctx.textBaseline = 'middle';
10361
+ ctx.lineWidth = 0.5;
10362
+ ctx.font = labelFont.string;
10363
+ const {boxWidth, boxHeight, itemHeight} = getBoxSize(labelOpts, fontSize);
10364
+ const drawLegendBox = function(x, y, legendItem) {
10365
+ if (isNaN(boxWidth) || boxWidth <= 0 || isNaN(boxHeight) || boxHeight < 0) {
10366
+ return;
10367
+ }
10368
+ ctx.save();
10369
+ const lineWidth = valueOrDefault(legendItem.lineWidth, 1);
10370
+ ctx.fillStyle = valueOrDefault(legendItem.fillStyle, defaultColor);
10371
+ ctx.lineCap = valueOrDefault(legendItem.lineCap, 'butt');
10372
+ ctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, 0);
10373
+ ctx.lineJoin = valueOrDefault(legendItem.lineJoin, 'miter');
10374
+ ctx.lineWidth = lineWidth;
10375
+ ctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, defaultColor);
10376
+ ctx.setLineDash(valueOrDefault(legendItem.lineDash, []));
10377
+ if (labelOpts.usePointStyle) {
10378
+ const drawOptions = {
10379
+ radius: boxWidth * Math.SQRT2 / 2,
10380
+ pointStyle: legendItem.pointStyle,
10381
+ rotation: legendItem.rotation,
10382
+ borderWidth: lineWidth
10383
+ };
10384
+ const centerX = rtlHelper.xPlus(x, boxWidth / 2);
10385
+ const centerY = y + halfFontSize;
10386
+ drawPoint(ctx, drawOptions, centerX, centerY);
10387
+ } else {
10388
+ const yBoxTop = y + Math.max((fontSize - boxHeight) / 2, 0);
10389
+ const xBoxLeft = rtlHelper.leftForLtr(x, boxWidth);
10390
+ const borderRadius = toTRBLCorners(legendItem.borderRadius);
10391
+ ctx.beginPath();
10392
+ if (Object.values(borderRadius).some(v => v !== 0)) {
10393
+ addRoundedRectPath(ctx, {
10394
+ x: xBoxLeft,
10395
+ y: yBoxTop,
10396
+ w: boxWidth,
10397
+ h: boxHeight,
10398
+ radius: borderRadius,
10399
+ });
10400
+ } else {
10401
+ ctx.rect(xBoxLeft, yBoxTop, boxWidth, boxHeight);
10402
+ }
10403
+ ctx.fill();
10404
+ if (lineWidth !== 0) {
10405
+ ctx.stroke();
10406
+ }
10407
+ }
10408
+ ctx.restore();
10409
+ };
10410
+ const fillText = function(x, y, legendItem) {
10411
+ renderText(ctx, legendItem.text, x, y + (itemHeight / 2), labelFont, {
10412
+ strikethrough: legendItem.hidden,
10413
+ textAlign: rtlHelper.textAlign(legendItem.textAlign)
10414
+ });
10415
+ };
10416
+ const isHorizontal = me.isHorizontal();
10417
+ const titleHeight = this._computeTitleHeight();
10418
+ if (isHorizontal) {
10419
+ cursor = {
10420
+ x: _alignStartEnd(align, me.left + padding, me.right - lineWidths[0]),
10421
+ y: me.top + padding + titleHeight,
10422
+ line: 0
10423
+ };
10424
+ } else {
10425
+ cursor = {
10426
+ x: me.left + padding,
10427
+ y: _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - columnSizes[0].height),
10428
+ line: 0
10429
+ };
10430
+ }
10431
+ overrideTextDirection(me.ctx, opts.textDirection);
10432
+ const lineHeight = itemHeight + padding;
10433
+ me.legendItems.forEach((legendItem, i) => {
10434
+ ctx.strokeStyle = legendItem.fontColor || fontColor;
10435
+ ctx.fillStyle = legendItem.fontColor || fontColor;
10436
+ const textWidth = ctx.measureText(legendItem.text).width;
10437
+ const textAlign = rtlHelper.textAlign(legendItem.textAlign || (legendItem.textAlign = labelOpts.textAlign));
10438
+ const width = boxWidth + halfFontSize + textWidth;
10439
+ let x = cursor.x;
10440
+ let y = cursor.y;
10441
+ rtlHelper.setWidth(me.width);
10442
+ if (isHorizontal) {
10443
+ if (i > 0 && x + width + padding > me.right) {
10444
+ y = cursor.y += lineHeight;
10445
+ cursor.line++;
10446
+ x = cursor.x = _alignStartEnd(align, me.left + padding, me.right - lineWidths[cursor.line]);
10447
+ }
10448
+ } else if (i > 0 && y + lineHeight > me.bottom) {
10449
+ x = cursor.x = x + columnSizes[cursor.line].width + padding;
10450
+ cursor.line++;
10451
+ y = cursor.y = _alignStartEnd(align, me.top + titleHeight + padding, me.bottom - columnSizes[cursor.line].height);
10452
+ }
10453
+ const realX = rtlHelper.x(x);
10454
+ drawLegendBox(realX, y, legendItem);
10455
+ x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : me.right, opts.rtl);
10456
+ fillText(rtlHelper.x(x), y, legendItem);
10457
+ if (isHorizontal) {
10458
+ cursor.x += width + padding;
10459
+ } else {
10460
+ cursor.y += lineHeight;
10461
+ }
10462
+ });
10463
+ restoreTextDirection(me.ctx, opts.textDirection);
10464
+ }
10465
+ drawTitle() {
10466
+ const me = this;
10467
+ const opts = me.options;
10468
+ const titleOpts = opts.title;
10469
+ const titleFont = toFont(titleOpts.font);
10470
+ const titlePadding = toPadding(titleOpts.padding);
10471
+ if (!titleOpts.display) {
10472
+ return;
10473
+ }
10474
+ const rtlHelper = getRtlAdapter(opts.rtl, me.left, me.width);
10475
+ const ctx = me.ctx;
10476
+ const position = titleOpts.position;
10477
+ const halfFontSize = titleFont.size / 2;
10478
+ const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;
10479
+ let y;
10480
+ let left = me.left;
10481
+ let maxWidth = me.width;
10482
+ if (this.isHorizontal()) {
10483
+ maxWidth = Math.max(...me.lineWidths);
10484
+ y = me.top + topPaddingPlusHalfFontSize;
10485
+ left = _alignStartEnd(opts.align, left, me.right - maxWidth);
10486
+ } else {
10487
+ const maxHeight = me.columnSizes.reduce((acc, size) => Math.max(acc, size.height), 0);
10488
+ y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, me.top, me.bottom - maxHeight - opts.labels.padding - me._computeTitleHeight());
10489
+ }
10490
+ const x = _alignStartEnd(position, left, left + maxWidth);
10491
+ ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position));
10492
+ ctx.textBaseline = 'middle';
10493
+ ctx.strokeStyle = titleOpts.color;
10494
+ ctx.fillStyle = titleOpts.color;
10495
+ ctx.font = titleFont.string;
10496
+ renderText(ctx, titleOpts.text, x, y, titleFont);
10497
+ }
10498
+ _computeTitleHeight() {
10499
+ const titleOpts = this.options.title;
10500
+ const titleFont = toFont(titleOpts.font);
10501
+ const titlePadding = toPadding(titleOpts.padding);
10502
+ return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;
10503
+ }
10504
+ _getLegendItemAt(x, y) {
10505
+ const me = this;
10506
+ let i, hitBox, lh;
10507
+ if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
10508
+ lh = me.legendHitBoxes;
10509
+ for (i = 0; i < lh.length; ++i) {
10510
+ hitBox = lh[i];
10511
+ if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
10512
+ return me.legendItems[i];
10513
+ }
10514
+ }
10515
+ }
10516
+ return null;
10517
+ }
10518
+ handleEvent(e) {
10519
+ const me = this;
10520
+ const opts = me.options;
10521
+ if (!isListened(e.type, opts)) {
10522
+ return;
10523
+ }
10524
+ const hoveredItem = me._getLegendItemAt(e.x, e.y);
10525
+ if (e.type === 'mousemove') {
10526
+ const previous = me._hoveredItem;
10527
+ const sameItem = itemsEqual(previous, hoveredItem);
10528
+ if (previous && !sameItem) {
10529
+ callback(opts.onLeave, [e, previous, me], me);
10530
+ }
10531
+ me._hoveredItem = hoveredItem;
10532
+ if (hoveredItem && !sameItem) {
10533
+ callback(opts.onHover, [e, hoveredItem, me], me);
10534
+ }
10535
+ } else if (hoveredItem) {
10536
+ callback(opts.onClick, [e, hoveredItem, me], me);
10537
+ }
10538
+ }
10539
+ }
10540
+ function isListened(type, opts) {
10541
+ if (type === 'mousemove' && (opts.onHover || opts.onLeave)) {
10542
+ return true;
10543
+ }
10544
+ if (opts.onClick && (type === 'click' || type === 'mouseup')) {
10545
+ return true;
10546
+ }
10547
+ return false;
10548
+ }
10549
+ var plugin_legend = {
10550
+ id: 'legend',
10551
+ _element: Legend,
10552
+ start(chart, _args, options) {
10553
+ const legend = chart.legend = new Legend({ctx: chart.ctx, options, chart});
10554
+ layouts.configure(chart, legend, options);
10555
+ layouts.addBox(chart, legend);
10556
+ },
10557
+ stop(chart) {
10558
+ layouts.removeBox(chart, chart.legend);
10559
+ delete chart.legend;
10560
+ },
10561
+ beforeUpdate(chart, _args, options) {
10562
+ const legend = chart.legend;
10563
+ layouts.configure(chart, legend, options);
10564
+ legend.options = options;
10565
+ },
10566
+ afterUpdate(chart) {
10567
+ const legend = chart.legend;
10568
+ legend.buildLabels();
10569
+ legend.adjustHitBoxes();
10570
+ },
10571
+ afterEvent(chart, args) {
10572
+ if (!args.replay) {
10573
+ chart.legend.handleEvent(args.event);
10574
+ }
10575
+ },
10576
+ defaults: {
10577
+ display: true,
10578
+ position: 'top',
10579
+ align: 'center',
10580
+ fullSize: true,
10581
+ reverse: false,
10582
+ weight: 1000,
10583
+ onClick(e, legendItem, legend) {
10584
+ const index = legendItem.datasetIndex;
10585
+ const ci = legend.chart;
10586
+ if (ci.isDatasetVisible(index)) {
10587
+ ci.hide(index);
10588
+ legendItem.hidden = true;
10589
+ } else {
10590
+ ci.show(index);
10591
+ legendItem.hidden = false;
10592
+ }
10593
+ },
10594
+ onHover: null,
10595
+ onLeave: null,
10596
+ labels: {
10597
+ color: (ctx) => ctx.chart.options.color,
10598
+ boxWidth: 40,
10599
+ padding: 10,
10600
+ generateLabels(chart) {
10601
+ const datasets = chart.data.datasets;
10602
+ const {labels: {usePointStyle, pointStyle, textAlign, color}} = chart.legend.options;
10603
+ return chart._getSortedDatasetMetas().map((meta) => {
10604
+ const style = meta.controller.getStyle(usePointStyle ? 0 : undefined);
10605
+ const borderWidth = toPadding(style.borderWidth);
10606
+ return {
10607
+ text: datasets[meta.index].label,
10608
+ fillStyle: style.backgroundColor,
10609
+ fontColor: color,
10610
+ hidden: !meta.visible,
10611
+ lineCap: style.borderCapStyle,
10612
+ lineDash: style.borderDash,
10613
+ lineDashOffset: style.borderDashOffset,
10614
+ lineJoin: style.borderJoinStyle,
10615
+ lineWidth: (borderWidth.width + borderWidth.height) / 4,
10616
+ strokeStyle: style.borderColor,
10617
+ pointStyle: pointStyle || style.pointStyle,
10618
+ rotation: style.rotation,
10619
+ textAlign: textAlign || style.textAlign,
10620
+ borderRadius: 0,
10621
+ datasetIndex: meta.index
10622
+ };
10623
+ }, this);
10624
+ }
10625
+ },
10626
+ title: {
10627
+ color: (ctx) => ctx.chart.options.color,
10628
+ display: false,
10629
+ position: 'center',
10630
+ text: '',
10631
+ }
10632
+ },
10633
+ descriptors: {
10634
+ _scriptable: (name) => !name.startsWith('on'),
10635
+ labels: {
10636
+ _scriptable: (name) => !['generateLabels', 'filter', 'sort'].includes(name),
10637
+ }
10638
+ },
10639
+ };
10640
+
10641
+ class Title extends Element {
10642
+ constructor(config) {
10643
+ super();
10644
+ this.chart = config.chart;
10645
+ this.options = config.options;
10646
+ this.ctx = config.ctx;
10647
+ this._padding = undefined;
10648
+ this.top = undefined;
10649
+ this.bottom = undefined;
10650
+ this.left = undefined;
10651
+ this.right = undefined;
10652
+ this.width = undefined;
10653
+ this.height = undefined;
10654
+ this.position = undefined;
10655
+ this.weight = undefined;
10656
+ this.fullSize = undefined;
10657
+ }
10658
+ update(maxWidth, maxHeight) {
10659
+ const me = this;
10660
+ const opts = me.options;
10661
+ me.left = 0;
10662
+ me.top = 0;
10663
+ if (!opts.display) {
10664
+ me.width = me.height = me.right = me.bottom = 0;
10665
+ return;
10666
+ }
10667
+ me.width = me.right = maxWidth;
10668
+ me.height = me.bottom = maxHeight;
10669
+ const lineCount = isArray(opts.text) ? opts.text.length : 1;
10670
+ me._padding = toPadding(opts.padding);
10671
+ const textSize = lineCount * toFont(opts.font).lineHeight + me._padding.height;
10672
+ if (me.isHorizontal()) {
10673
+ me.height = textSize;
10674
+ } else {
10675
+ me.width = textSize;
10676
+ }
10677
+ }
10678
+ isHorizontal() {
10679
+ const pos = this.options.position;
10680
+ return pos === 'top' || pos === 'bottom';
10681
+ }
10682
+ _drawArgs(offset) {
10683
+ const {top, left, bottom, right, options} = this;
10684
+ const align = options.align;
10685
+ let rotation = 0;
10686
+ let maxWidth, titleX, titleY;
10687
+ if (this.isHorizontal()) {
10688
+ titleX = _alignStartEnd(align, left, right);
10689
+ titleY = top + offset;
10690
+ maxWidth = right - left;
10691
+ } else {
10692
+ if (options.position === 'left') {
10693
+ titleX = left + offset;
10694
+ titleY = _alignStartEnd(align, bottom, top);
10695
+ rotation = PI * -0.5;
10696
+ } else {
10697
+ titleX = right - offset;
10698
+ titleY = _alignStartEnd(align, top, bottom);
10699
+ rotation = PI * 0.5;
10700
+ }
10701
+ maxWidth = bottom - top;
10702
+ }
10703
+ return {titleX, titleY, maxWidth, rotation};
10704
+ }
10705
+ draw() {
10706
+ const me = this;
10707
+ const ctx = me.ctx;
10708
+ const opts = me.options;
10709
+ if (!opts.display) {
10710
+ return;
10711
+ }
10712
+ const fontOpts = toFont(opts.font);
10713
+ const lineHeight = fontOpts.lineHeight;
10714
+ const offset = lineHeight / 2 + me._padding.top;
10715
+ const {titleX, titleY, maxWidth, rotation} = me._drawArgs(offset);
10716
+ renderText(ctx, opts.text, 0, 0, fontOpts, {
10717
+ color: opts.color,
10718
+ maxWidth,
10719
+ rotation,
10720
+ textAlign: _toLeftRightCenter(opts.align),
10721
+ textBaseline: 'middle',
10722
+ translation: [titleX, titleY],
10723
+ });
10724
+ }
10725
+ }
10726
+ function createTitle(chart, titleOpts) {
10727
+ const title = new Title({
10728
+ ctx: chart.ctx,
10729
+ options: titleOpts,
10730
+ chart
10731
+ });
10732
+ layouts.configure(chart, title, titleOpts);
10733
+ layouts.addBox(chart, title);
10734
+ chart.titleBlock = title;
10735
+ }
10736
+ var plugin_title = {
10737
+ id: 'title',
10738
+ _element: Title,
10739
+ start(chart, _args, options) {
10740
+ createTitle(chart, options);
10741
+ },
10742
+ stop(chart) {
10743
+ const titleBlock = chart.titleBlock;
10744
+ layouts.removeBox(chart, titleBlock);
10745
+ delete chart.titleBlock;
10746
+ },
10747
+ beforeUpdate(chart, _args, options) {
10748
+ const title = chart.titleBlock;
10749
+ layouts.configure(chart, title, options);
10750
+ title.options = options;
10751
+ },
10752
+ defaults: {
10753
+ align: 'center',
10754
+ display: false,
10755
+ font: {
10756
+ weight: 'bold',
10757
+ },
10758
+ fullSize: true,
10759
+ padding: 10,
10760
+ position: 'top',
10761
+ text: '',
10762
+ weight: 2000
10763
+ },
10764
+ defaultRoutes: {
10765
+ color: 'color'
10766
+ },
10767
+ descriptors: {
10768
+ _scriptable: true,
10769
+ _indexable: false,
10770
+ },
10771
+ };
10772
+
10773
+ const map = new WeakMap();
10774
+ var plugin_subtitle = {
10775
+ id: 'subtitle',
10776
+ start(chart, _args, options) {
10777
+ const title = new Title({
10778
+ ctx: chart.ctx,
10779
+ options,
10780
+ chart
10781
+ });
10782
+ layouts.configure(chart, title, options);
10783
+ layouts.addBox(chart, title);
10784
+ map.set(chart, title);
10785
+ },
10786
+ stop(chart) {
10787
+ layouts.removeBox(chart, map.get(chart));
10788
+ map.delete(chart);
10789
+ },
10790
+ beforeUpdate(chart, _args, options) {
10791
+ const title = map.get(chart);
10792
+ layouts.configure(chart, title, options);
10793
+ title.options = options;
10794
+ },
10795
+ defaults: {
10796
+ align: 'center',
10797
+ display: false,
10798
+ font: {
10799
+ weight: 'normal',
10800
+ },
10801
+ fullSize: true,
10802
+ padding: 0,
10803
+ position: 'top',
10804
+ text: '',
10805
+ weight: 1500
10806
+ },
10807
+ defaultRoutes: {
10808
+ color: 'color'
10809
+ },
10810
+ descriptors: {
10811
+ _scriptable: true,
10812
+ _indexable: false,
10813
+ },
10814
+ };
10815
+
10816
+ const positioners = {
10817
+ average(items) {
10818
+ if (!items.length) {
10819
+ return false;
10820
+ }
10821
+ let i, len;
10822
+ let x = 0;
10823
+ let y = 0;
10824
+ let count = 0;
10825
+ for (i = 0, len = items.length; i < len; ++i) {
10826
+ const el = items[i].element;
10827
+ if (el && el.hasValue()) {
10828
+ const pos = el.tooltipPosition();
10829
+ x += pos.x;
10830
+ y += pos.y;
10831
+ ++count;
10832
+ }
10833
+ }
10834
+ return {
10835
+ x: x / count,
10836
+ y: y / count
10837
+ };
10838
+ },
10839
+ nearest(items, eventPosition) {
10840
+ if (!items.length) {
10841
+ return false;
10842
+ }
10843
+ let x = eventPosition.x;
10844
+ let y = eventPosition.y;
10845
+ let minDistance = Number.POSITIVE_INFINITY;
10846
+ let i, len, nearestElement;
10847
+ for (i = 0, len = items.length; i < len; ++i) {
10848
+ const el = items[i].element;
10849
+ if (el && el.hasValue()) {
10850
+ const center = el.getCenterPoint();
10851
+ const d = distanceBetweenPoints(eventPosition, center);
10852
+ if (d < minDistance) {
10853
+ minDistance = d;
10854
+ nearestElement = el;
10855
+ }
10856
+ }
10857
+ }
10858
+ if (nearestElement) {
10859
+ const tp = nearestElement.tooltipPosition();
10860
+ x = tp.x;
10861
+ y = tp.y;
10862
+ }
10863
+ return {
10864
+ x,
10865
+ y
10866
+ };
10867
+ }
10868
+ };
10869
+ function pushOrConcat(base, toPush) {
10870
+ if (toPush) {
10871
+ if (isArray(toPush)) {
10872
+ Array.prototype.push.apply(base, toPush);
10873
+ } else {
10874
+ base.push(toPush);
10875
+ }
10876
+ }
10877
+ return base;
10878
+ }
10879
+ function splitNewlines(str) {
10880
+ if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
10881
+ return str.split('\n');
10882
+ }
10883
+ return str;
10884
+ }
10885
+ function createTooltipItem(chart, item) {
10886
+ const {element, datasetIndex, index} = item;
10887
+ const controller = chart.getDatasetMeta(datasetIndex).controller;
10888
+ const {label, value} = controller.getLabelAndValue(index);
10889
+ return {
10890
+ chart,
10891
+ label,
10892
+ parsed: controller.getParsed(index),
10893
+ raw: chart.data.datasets[datasetIndex].data[index],
10894
+ formattedValue: value,
10895
+ dataset: controller.getDataset(),
10896
+ dataIndex: index,
10897
+ datasetIndex,
10898
+ element
10899
+ };
10900
+ }
10901
+ function getTooltipSize(tooltip, options) {
10902
+ const ctx = tooltip._chart.ctx;
10903
+ const {body, footer, title} = tooltip;
10904
+ const {boxWidth, boxHeight} = options;
10905
+ const bodyFont = toFont(options.bodyFont);
10906
+ const titleFont = toFont(options.titleFont);
10907
+ const footerFont = toFont(options.footerFont);
10908
+ const titleLineCount = title.length;
10909
+ const footerLineCount = footer.length;
10910
+ const bodyLineItemCount = body.length;
10911
+ const padding = toPadding(options.padding);
10912
+ let height = padding.height;
10913
+ let width = 0;
10914
+ let combinedBodyLength = body.reduce((count, bodyItem) => count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length, 0);
10915
+ combinedBodyLength += tooltip.beforeBody.length + tooltip.afterBody.length;
10916
+ if (titleLineCount) {
10917
+ height += titleLineCount * titleFont.lineHeight
10918
+ + (titleLineCount - 1) * options.titleSpacing
10919
+ + options.titleMarginBottom;
10920
+ }
10921
+ if (combinedBodyLength) {
10922
+ const bodyLineHeight = options.displayColors ? Math.max(boxHeight, bodyFont.lineHeight) : bodyFont.lineHeight;
10923
+ height += bodyLineItemCount * bodyLineHeight
10924
+ + (combinedBodyLength - bodyLineItemCount) * bodyFont.lineHeight
10925
+ + (combinedBodyLength - 1) * options.bodySpacing;
10926
+ }
10927
+ if (footerLineCount) {
10928
+ height += options.footerMarginTop
10929
+ + footerLineCount * footerFont.lineHeight
10930
+ + (footerLineCount - 1) * options.footerSpacing;
10931
+ }
10932
+ let widthPadding = 0;
10933
+ const maxLineWidth = function(line) {
10934
+ width = Math.max(width, ctx.measureText(line).width + widthPadding);
10935
+ };
10936
+ ctx.save();
10937
+ ctx.font = titleFont.string;
10938
+ each(tooltip.title, maxLineWidth);
10939
+ ctx.font = bodyFont.string;
10940
+ each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);
10941
+ widthPadding = options.displayColors ? (boxWidth + 2) : 0;
10942
+ each(body, (bodyItem) => {
10943
+ each(bodyItem.before, maxLineWidth);
10944
+ each(bodyItem.lines, maxLineWidth);
10945
+ each(bodyItem.after, maxLineWidth);
10946
+ });
10947
+ widthPadding = 0;
10948
+ ctx.font = footerFont.string;
10949
+ each(tooltip.footer, maxLineWidth);
10950
+ ctx.restore();
10951
+ width += padding.width;
10952
+ return {width, height};
10953
+ }
10954
+ function determineYAlign(chart, size) {
10955
+ const {y, height} = size;
10956
+ if (y < height / 2) {
10957
+ return 'top';
10958
+ } else if (y > (chart.height - height / 2)) {
10959
+ return 'bottom';
10960
+ }
10961
+ return 'center';
10962
+ }
10963
+ function doesNotFitWithAlign(xAlign, chart, options, size) {
10964
+ const {x, width} = size;
10965
+ const caret = options.caretSize + options.caretPadding;
10966
+ if (xAlign === 'left' && x + width + caret > chart.width) {
10967
+ return true;
10968
+ }
10969
+ if (xAlign === 'right' && x - width - caret < 0) {
10970
+ return true;
10971
+ }
10972
+ }
10973
+ function determineXAlign(chart, options, size, yAlign) {
10974
+ const {x, width} = size;
10975
+ const {width: chartWidth, chartArea: {left, right}} = chart;
10976
+ let xAlign = 'center';
10977
+ if (yAlign === 'center') {
10978
+ xAlign = x <= (left + right) / 2 ? 'left' : 'right';
10979
+ } else if (x <= width / 2) {
10980
+ xAlign = 'left';
10981
+ } else if (x >= chartWidth - width / 2) {
10982
+ xAlign = 'right';
10983
+ }
10984
+ if (doesNotFitWithAlign(xAlign, chart, options, size)) {
10985
+ xAlign = 'center';
10986
+ }
10987
+ return xAlign;
10988
+ }
10989
+ function determineAlignment(chart, options, size) {
10990
+ const yAlign = options.yAlign || determineYAlign(chart, size);
10991
+ return {
10992
+ xAlign: options.xAlign || determineXAlign(chart, options, size, yAlign),
10993
+ yAlign
10994
+ };
10995
+ }
10996
+ function alignX(size, xAlign) {
10997
+ let {x, width} = size;
10998
+ if (xAlign === 'right') {
10999
+ x -= width;
11000
+ } else if (xAlign === 'center') {
11001
+ x -= (width / 2);
11002
+ }
11003
+ return x;
11004
+ }
11005
+ function alignY(size, yAlign, paddingAndSize) {
11006
+ let {y, height} = size;
11007
+ if (yAlign === 'top') {
11008
+ y += paddingAndSize;
11009
+ } else if (yAlign === 'bottom') {
11010
+ y -= height + paddingAndSize;
11011
+ } else {
11012
+ y -= (height / 2);
11013
+ }
11014
+ return y;
11015
+ }
11016
+ function getBackgroundPoint(options, size, alignment, chart) {
11017
+ const {caretSize, caretPadding, cornerRadius} = options;
11018
+ const {xAlign, yAlign} = alignment;
11019
+ const paddingAndSize = caretSize + caretPadding;
11020
+ const radiusAndPadding = cornerRadius + caretPadding;
11021
+ let x = alignX(size, xAlign);
11022
+ const y = alignY(size, yAlign, paddingAndSize);
11023
+ if (yAlign === 'center') {
11024
+ if (xAlign === 'left') {
11025
+ x += paddingAndSize;
11026
+ } else if (xAlign === 'right') {
11027
+ x -= paddingAndSize;
11028
+ }
11029
+ } else if (xAlign === 'left') {
11030
+ x -= radiusAndPadding;
11031
+ } else if (xAlign === 'right') {
11032
+ x += radiusAndPadding;
11033
+ }
11034
+ return {
11035
+ x: _limitValue(x, 0, chart.width - size.width),
11036
+ y: _limitValue(y, 0, chart.height - size.height)
11037
+ };
11038
+ }
11039
+ function getAlignedX(tooltip, align, options) {
11040
+ const padding = toPadding(options.padding);
11041
+ return align === 'center'
11042
+ ? tooltip.x + tooltip.width / 2
11043
+ : align === 'right'
11044
+ ? tooltip.x + tooltip.width - padding.right
11045
+ : tooltip.x + padding.left;
11046
+ }
11047
+ function getBeforeAfterBodyLines(callback) {
11048
+ return pushOrConcat([], splitNewlines(callback));
11049
+ }
11050
+ function createTooltipContext(parent, tooltip, tooltipItems) {
11051
+ return Object.assign(Object.create(parent), {
11052
+ tooltip,
11053
+ tooltipItems,
11054
+ type: 'tooltip'
11055
+ });
11056
+ }
11057
+ function overrideCallbacks(callbacks, context) {
11058
+ const override = context && context.dataset && context.dataset.tooltip && context.dataset.tooltip.callbacks;
11059
+ return override ? callbacks.override(override) : callbacks;
11060
+ }
11061
+ class Tooltip extends Element {
11062
+ constructor(config) {
11063
+ super();
11064
+ this.opacity = 0;
11065
+ this._active = [];
11066
+ this._chart = config._chart;
11067
+ this._eventPosition = undefined;
11068
+ this._size = undefined;
11069
+ this._cachedAnimations = undefined;
11070
+ this._tooltipItems = [];
11071
+ this.$animations = undefined;
11072
+ this.$context = undefined;
11073
+ this.options = config.options;
11074
+ this.dataPoints = undefined;
11075
+ this.title = undefined;
11076
+ this.beforeBody = undefined;
11077
+ this.body = undefined;
11078
+ this.afterBody = undefined;
11079
+ this.footer = undefined;
11080
+ this.xAlign = undefined;
11081
+ this.yAlign = undefined;
11082
+ this.x = undefined;
11083
+ this.y = undefined;
11084
+ this.height = undefined;
11085
+ this.width = undefined;
11086
+ this.caretX = undefined;
11087
+ this.caretY = undefined;
11088
+ this.labelColors = undefined;
11089
+ this.labelPointStyles = undefined;
11090
+ this.labelTextColors = undefined;
11091
+ }
11092
+ initialize(options) {
11093
+ this.options = options;
11094
+ this._cachedAnimations = undefined;
11095
+ this.$context = undefined;
11096
+ }
11097
+ _resolveAnimations() {
11098
+ const me = this;
11099
+ const cached = me._cachedAnimations;
11100
+ if (cached) {
11101
+ return cached;
11102
+ }
11103
+ const chart = me._chart;
11104
+ const options = me.options.setContext(me.getContext());
11105
+ const opts = options.enabled && chart.options.animation && options.animations;
11106
+ const animations = new Animations(me._chart, opts);
11107
+ if (opts._cacheable) {
11108
+ me._cachedAnimations = Object.freeze(animations);
11109
+ }
11110
+ return animations;
11111
+ }
11112
+ getContext() {
11113
+ const me = this;
11114
+ return me.$context ||
11115
+ (me.$context = createTooltipContext(me._chart.getContext(), me, me._tooltipItems));
11116
+ }
11117
+ getTitle(context, options) {
11118
+ const me = this;
11119
+ const {callbacks} = options;
11120
+ const beforeTitle = callbacks.beforeTitle.apply(me, [context]);
11121
+ const title = callbacks.title.apply(me, [context]);
11122
+ const afterTitle = callbacks.afterTitle.apply(me, [context]);
11123
+ let lines = [];
11124
+ lines = pushOrConcat(lines, splitNewlines(beforeTitle));
11125
+ lines = pushOrConcat(lines, splitNewlines(title));
11126
+ lines = pushOrConcat(lines, splitNewlines(afterTitle));
11127
+ return lines;
11128
+ }
11129
+ getBeforeBody(tooltipItems, options) {
11130
+ return getBeforeAfterBodyLines(options.callbacks.beforeBody.apply(this, [tooltipItems]));
11131
+ }
11132
+ getBody(tooltipItems, options) {
11133
+ const me = this;
11134
+ const {callbacks} = options;
11135
+ const bodyItems = [];
11136
+ each(tooltipItems, (context) => {
11137
+ const bodyItem = {
11138
+ before: [],
11139
+ lines: [],
11140
+ after: []
11141
+ };
11142
+ const scoped = overrideCallbacks(callbacks, context);
11143
+ pushOrConcat(bodyItem.before, splitNewlines(scoped.beforeLabel.call(me, context)));
11144
+ pushOrConcat(bodyItem.lines, scoped.label.call(me, context));
11145
+ pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(me, context)));
11146
+ bodyItems.push(bodyItem);
11147
+ });
11148
+ return bodyItems;
11149
+ }
11150
+ getAfterBody(tooltipItems, options) {
11151
+ return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this, [tooltipItems]));
11152
+ }
11153
+ getFooter(tooltipItems, options) {
11154
+ const me = this;
11155
+ const {callbacks} = options;
11156
+ const beforeFooter = callbacks.beforeFooter.apply(me, [tooltipItems]);
11157
+ const footer = callbacks.footer.apply(me, [tooltipItems]);
11158
+ const afterFooter = callbacks.afterFooter.apply(me, [tooltipItems]);
11159
+ let lines = [];
11160
+ lines = pushOrConcat(lines, splitNewlines(beforeFooter));
11161
+ lines = pushOrConcat(lines, splitNewlines(footer));
11162
+ lines = pushOrConcat(lines, splitNewlines(afterFooter));
11163
+ return lines;
11164
+ }
11165
+ _createItems(options) {
11166
+ const me = this;
11167
+ const active = me._active;
11168
+ const data = me._chart.data;
11169
+ const labelColors = [];
11170
+ const labelPointStyles = [];
11171
+ const labelTextColors = [];
11172
+ let tooltipItems = [];
11173
+ let i, len;
11174
+ for (i = 0, len = active.length; i < len; ++i) {
11175
+ tooltipItems.push(createTooltipItem(me._chart, active[i]));
11176
+ }
11177
+ if (options.filter) {
11178
+ tooltipItems = tooltipItems.filter((element, index, array) => options.filter(element, index, array, data));
11179
+ }
11180
+ if (options.itemSort) {
11181
+ tooltipItems = tooltipItems.sort((a, b) => options.itemSort(a, b, data));
11182
+ }
11183
+ each(tooltipItems, (context) => {
11184
+ const scoped = overrideCallbacks(options.callbacks, context);
11185
+ labelColors.push(scoped.labelColor.call(me, context));
11186
+ labelPointStyles.push(scoped.labelPointStyle.call(me, context));
11187
+ labelTextColors.push(scoped.labelTextColor.call(me, context));
11188
+ });
11189
+ me.labelColors = labelColors;
11190
+ me.labelPointStyles = labelPointStyles;
11191
+ me.labelTextColors = labelTextColors;
11192
+ me.dataPoints = tooltipItems;
11193
+ return tooltipItems;
11194
+ }
11195
+ update(changed, replay) {
11196
+ const me = this;
11197
+ const options = me.options.setContext(me.getContext());
11198
+ const active = me._active;
11199
+ let properties;
11200
+ let tooltipItems = [];
11201
+ if (!active.length) {
11202
+ if (me.opacity !== 0) {
11203
+ properties = {
11204
+ opacity: 0
11205
+ };
11206
+ }
11207
+ } else {
11208
+ const position = positioners[options.position].call(me, active, me._eventPosition);
11209
+ tooltipItems = me._createItems(options);
11210
+ me.title = me.getTitle(tooltipItems, options);
11211
+ me.beforeBody = me.getBeforeBody(tooltipItems, options);
11212
+ me.body = me.getBody(tooltipItems, options);
11213
+ me.afterBody = me.getAfterBody(tooltipItems, options);
11214
+ me.footer = me.getFooter(tooltipItems, options);
11215
+ const size = me._size = getTooltipSize(me, options);
11216
+ const positionAndSize = Object.assign({}, position, size);
11217
+ const alignment = determineAlignment(me._chart, options, positionAndSize);
11218
+ const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, me._chart);
11219
+ me.xAlign = alignment.xAlign;
11220
+ me.yAlign = alignment.yAlign;
11221
+ properties = {
11222
+ opacity: 1,
11223
+ x: backgroundPoint.x,
11224
+ y: backgroundPoint.y,
11225
+ width: size.width,
11226
+ height: size.height,
11227
+ caretX: position.x,
11228
+ caretY: position.y
11229
+ };
11230
+ }
11231
+ me._tooltipItems = tooltipItems;
11232
+ me.$context = undefined;
11233
+ if (properties) {
11234
+ me._resolveAnimations().update(me, properties);
11235
+ }
11236
+ if (changed && options.external) {
11237
+ options.external.call(me, {chart: me._chart, tooltip: me, replay});
11238
+ }
11239
+ }
11240
+ drawCaret(tooltipPoint, ctx, size, options) {
11241
+ const caretPosition = this.getCaretPosition(tooltipPoint, size, options);
11242
+ ctx.lineTo(caretPosition.x1, caretPosition.y1);
11243
+ ctx.lineTo(caretPosition.x2, caretPosition.y2);
11244
+ ctx.lineTo(caretPosition.x3, caretPosition.y3);
11245
+ }
11246
+ getCaretPosition(tooltipPoint, size, options) {
11247
+ const {xAlign, yAlign} = this;
11248
+ const {cornerRadius, caretSize} = options;
11249
+ const {x: ptX, y: ptY} = tooltipPoint;
11250
+ const {width, height} = size;
11251
+ let x1, x2, x3, y1, y2, y3;
11252
+ if (yAlign === 'center') {
11253
+ y2 = ptY + (height / 2);
11254
+ if (xAlign === 'left') {
11255
+ x1 = ptX;
11256
+ x2 = x1 - caretSize;
11257
+ y1 = y2 + caretSize;
11258
+ y3 = y2 - caretSize;
11259
+ } else {
11260
+ x1 = ptX + width;
11261
+ x2 = x1 + caretSize;
11262
+ y1 = y2 - caretSize;
11263
+ y3 = y2 + caretSize;
11264
+ }
11265
+ x3 = x1;
11266
+ } else {
11267
+ if (xAlign === 'left') {
11268
+ x2 = ptX + cornerRadius + (caretSize);
11269
+ } else if (xAlign === 'right') {
11270
+ x2 = ptX + width - cornerRadius - caretSize;
11271
+ } else {
11272
+ x2 = this.caretX;
11273
+ }
11274
+ if (yAlign === 'top') {
11275
+ y1 = ptY;
11276
+ y2 = y1 - caretSize;
11277
+ x1 = x2 - caretSize;
11278
+ x3 = x2 + caretSize;
11279
+ } else {
11280
+ y1 = ptY + height;
11281
+ y2 = y1 + caretSize;
11282
+ x1 = x2 + caretSize;
11283
+ x3 = x2 - caretSize;
11284
+ }
11285
+ y3 = y1;
11286
+ }
11287
+ return {x1, x2, x3, y1, y2, y3};
11288
+ }
11289
+ drawTitle(pt, ctx, options) {
11290
+ const me = this;
11291
+ const title = me.title;
11292
+ const length = title.length;
11293
+ let titleFont, titleSpacing, i;
11294
+ if (length) {
11295
+ const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width);
11296
+ pt.x = getAlignedX(me, options.titleAlign, options);
11297
+ ctx.textAlign = rtlHelper.textAlign(options.titleAlign);
11298
+ ctx.textBaseline = 'middle';
11299
+ titleFont = toFont(options.titleFont);
11300
+ titleSpacing = options.titleSpacing;
11301
+ ctx.fillStyle = options.titleColor;
11302
+ ctx.font = titleFont.string;
11303
+ for (i = 0; i < length; ++i) {
11304
+ ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFont.lineHeight / 2);
11305
+ pt.y += titleFont.lineHeight + titleSpacing;
11306
+ if (i + 1 === length) {
11307
+ pt.y += options.titleMarginBottom - titleSpacing;
11308
+ }
11309
+ }
11310
+ }
11311
+ }
11312
+ _drawColorBox(ctx, pt, i, rtlHelper, options) {
11313
+ const me = this;
11314
+ const labelColors = me.labelColors[i];
11315
+ const labelPointStyle = me.labelPointStyles[i];
11316
+ const {boxHeight, boxWidth} = options;
11317
+ const bodyFont = toFont(options.bodyFont);
11318
+ const colorX = getAlignedX(me, 'left', options);
11319
+ const rtlColorX = rtlHelper.x(colorX);
11320
+ const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;
11321
+ const colorY = pt.y + yOffSet;
11322
+ if (options.usePointStyle) {
11323
+ const drawOptions = {
11324
+ radius: Math.min(boxWidth, boxHeight) / 2,
11325
+ pointStyle: labelPointStyle.pointStyle,
11326
+ rotation: labelPointStyle.rotation,
11327
+ borderWidth: 1
11328
+ };
11329
+ const centerX = rtlHelper.leftForLtr(rtlColorX, boxWidth) + boxWidth / 2;
11330
+ const centerY = colorY + boxHeight / 2;
11331
+ ctx.strokeStyle = options.multiKeyBackground;
11332
+ ctx.fillStyle = options.multiKeyBackground;
11333
+ drawPoint(ctx, drawOptions, centerX, centerY);
11334
+ ctx.strokeStyle = labelColors.borderColor;
11335
+ ctx.fillStyle = labelColors.backgroundColor;
11336
+ drawPoint(ctx, drawOptions, centerX, centerY);
11337
+ } else {
11338
+ ctx.lineWidth = labelColors.borderWidth || 1;
11339
+ ctx.strokeStyle = labelColors.borderColor;
11340
+ ctx.setLineDash(labelColors.borderDash || []);
11341
+ ctx.lineDashOffset = labelColors.borderDashOffset || 0;
11342
+ const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth);
11343
+ const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - 2);
11344
+ const borderRadius = toTRBLCorners(labelColors.borderRadius);
11345
+ if (Object.values(borderRadius).some(v => v !== 0)) {
11346
+ ctx.beginPath();
11347
+ ctx.fillStyle = options.multiKeyBackground;
11348
+ addRoundedRectPath(ctx, {
11349
+ x: outerX,
11350
+ y: colorY,
11351
+ w: boxWidth,
11352
+ h: boxHeight,
11353
+ radius: borderRadius,
11354
+ });
11355
+ ctx.fill();
11356
+ ctx.stroke();
11357
+ ctx.fillStyle = labelColors.backgroundColor;
11358
+ ctx.beginPath();
11359
+ addRoundedRectPath(ctx, {
11360
+ x: innerX,
11361
+ y: colorY + 1,
11362
+ w: boxWidth - 2,
11363
+ h: boxHeight - 2,
11364
+ radius: borderRadius,
11365
+ });
11366
+ ctx.fill();
11367
+ } else {
11368
+ ctx.fillStyle = options.multiKeyBackground;
11369
+ ctx.fillRect(outerX, colorY, boxWidth, boxHeight);
11370
+ ctx.strokeRect(outerX, colorY, boxWidth, boxHeight);
11371
+ ctx.fillStyle = labelColors.backgroundColor;
11372
+ ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);
11373
+ }
11374
+ }
11375
+ ctx.fillStyle = me.labelTextColors[i];
11376
+ }
11377
+ drawBody(pt, ctx, options) {
11378
+ const me = this;
11379
+ const {body} = me;
11380
+ const {bodySpacing, bodyAlign, displayColors, boxHeight, boxWidth} = options;
11381
+ const bodyFont = toFont(options.bodyFont);
11382
+ let bodyLineHeight = bodyFont.lineHeight;
11383
+ let xLinePadding = 0;
11384
+ const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width);
11385
+ const fillLineOfText = function(line) {
11386
+ ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);
11387
+ pt.y += bodyLineHeight + bodySpacing;
11388
+ };
11389
+ const bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);
11390
+ let bodyItem, textColor, lines, i, j, ilen, jlen;
11391
+ ctx.textAlign = bodyAlign;
11392
+ ctx.textBaseline = 'middle';
11393
+ ctx.font = bodyFont.string;
11394
+ pt.x = getAlignedX(me, bodyAlignForCalculation, options);
11395
+ ctx.fillStyle = options.bodyColor;
11396
+ each(me.beforeBody, fillLineOfText);
11397
+ xLinePadding = displayColors && bodyAlignForCalculation !== 'right'
11398
+ ? bodyAlign === 'center' ? (boxWidth / 2 + 1) : (boxWidth + 2)
11399
+ : 0;
11400
+ for (i = 0, ilen = body.length; i < ilen; ++i) {
11401
+ bodyItem = body[i];
11402
+ textColor = me.labelTextColors[i];
11403
+ ctx.fillStyle = textColor;
11404
+ each(bodyItem.before, fillLineOfText);
11405
+ lines = bodyItem.lines;
11406
+ if (displayColors && lines.length) {
11407
+ me._drawColorBox(ctx, pt, i, rtlHelper, options);
11408
+ bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);
11409
+ }
11410
+ for (j = 0, jlen = lines.length; j < jlen; ++j) {
11411
+ fillLineOfText(lines[j]);
11412
+ bodyLineHeight = bodyFont.lineHeight;
11413
+ }
11414
+ each(bodyItem.after, fillLineOfText);
11415
+ }
11416
+ xLinePadding = 0;
11417
+ bodyLineHeight = bodyFont.lineHeight;
11418
+ each(me.afterBody, fillLineOfText);
11419
+ pt.y -= bodySpacing;
11420
+ }
11421
+ drawFooter(pt, ctx, options) {
11422
+ const me = this;
11423
+ const footer = me.footer;
11424
+ const length = footer.length;
11425
+ let footerFont, i;
11426
+ if (length) {
11427
+ const rtlHelper = getRtlAdapter(options.rtl, me.x, me.width);
11428
+ pt.x = getAlignedX(me, options.footerAlign, options);
11429
+ pt.y += options.footerMarginTop;
11430
+ ctx.textAlign = rtlHelper.textAlign(options.footerAlign);
11431
+ ctx.textBaseline = 'middle';
11432
+ footerFont = toFont(options.footerFont);
11433
+ ctx.fillStyle = options.footerColor;
11434
+ ctx.font = footerFont.string;
11435
+ for (i = 0; i < length; ++i) {
11436
+ ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFont.lineHeight / 2);
11437
+ pt.y += footerFont.lineHeight + options.footerSpacing;
11438
+ }
11439
+ }
11440
+ }
11441
+ drawBackground(pt, ctx, tooltipSize, options) {
11442
+ const {xAlign, yAlign} = this;
11443
+ const {x, y} = pt;
11444
+ const {width, height} = tooltipSize;
11445
+ const radius = options.cornerRadius;
11446
+ ctx.fillStyle = options.backgroundColor;
11447
+ ctx.strokeStyle = options.borderColor;
11448
+ ctx.lineWidth = options.borderWidth;
11449
+ ctx.beginPath();
11450
+ ctx.moveTo(x + radius, y);
11451
+ if (yAlign === 'top') {
11452
+ this.drawCaret(pt, ctx, tooltipSize, options);
11453
+ }
11454
+ ctx.lineTo(x + width - radius, y);
11455
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
11456
+ if (yAlign === 'center' && xAlign === 'right') {
11457
+ this.drawCaret(pt, ctx, tooltipSize, options);
11458
+ }
11459
+ ctx.lineTo(x + width, y + height - radius);
11460
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
11461
+ if (yAlign === 'bottom') {
11462
+ this.drawCaret(pt, ctx, tooltipSize, options);
11463
+ }
11464
+ ctx.lineTo(x + radius, y + height);
11465
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
11466
+ if (yAlign === 'center' && xAlign === 'left') {
11467
+ this.drawCaret(pt, ctx, tooltipSize, options);
11468
+ }
11469
+ ctx.lineTo(x, y + radius);
11470
+ ctx.quadraticCurveTo(x, y, x + radius, y);
11471
+ ctx.closePath();
11472
+ ctx.fill();
11473
+ if (options.borderWidth > 0) {
11474
+ ctx.stroke();
11475
+ }
11476
+ }
11477
+ _updateAnimationTarget(options) {
11478
+ const me = this;
11479
+ const chart = me._chart;
11480
+ const anims = me.$animations;
11481
+ const animX = anims && anims.x;
11482
+ const animY = anims && anims.y;
11483
+ if (animX || animY) {
11484
+ const position = positioners[options.position].call(me, me._active, me._eventPosition);
11485
+ if (!position) {
11486
+ return;
11487
+ }
11488
+ const size = me._size = getTooltipSize(me, options);
11489
+ const positionAndSize = Object.assign({}, position, me._size);
11490
+ const alignment = determineAlignment(chart, options, positionAndSize);
11491
+ const point = getBackgroundPoint(options, positionAndSize, alignment, chart);
11492
+ if (animX._to !== point.x || animY._to !== point.y) {
11493
+ me.xAlign = alignment.xAlign;
11494
+ me.yAlign = alignment.yAlign;
11495
+ me.width = size.width;
11496
+ me.height = size.height;
11497
+ me.caretX = position.x;
11498
+ me.caretY = position.y;
11499
+ me._resolveAnimations().update(me, point);
11500
+ }
11501
+ }
11502
+ }
11503
+ draw(ctx) {
11504
+ const me = this;
11505
+ const options = me.options.setContext(me.getContext());
11506
+ let opacity = me.opacity;
11507
+ if (!opacity) {
11508
+ return;
11509
+ }
11510
+ me._updateAnimationTarget(options);
11511
+ const tooltipSize = {
11512
+ width: me.width,
11513
+ height: me.height
11514
+ };
11515
+ const pt = {
11516
+ x: me.x,
11517
+ y: me.y
11518
+ };
11519
+ opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;
11520
+ const padding = toPadding(options.padding);
11521
+ const hasTooltipContent = me.title.length || me.beforeBody.length || me.body.length || me.afterBody.length || me.footer.length;
11522
+ if (options.enabled && hasTooltipContent) {
11523
+ ctx.save();
11524
+ ctx.globalAlpha = opacity;
11525
+ me.drawBackground(pt, ctx, tooltipSize, options);
11526
+ overrideTextDirection(ctx, options.textDirection);
11527
+ pt.y += padding.top;
11528
+ me.drawTitle(pt, ctx, options);
11529
+ me.drawBody(pt, ctx, options);
11530
+ me.drawFooter(pt, ctx, options);
11531
+ restoreTextDirection(ctx, options.textDirection);
11532
+ ctx.restore();
11533
+ }
11534
+ }
11535
+ getActiveElements() {
11536
+ return this._active || [];
11537
+ }
11538
+ setActiveElements(activeElements, eventPosition) {
11539
+ const me = this;
11540
+ const lastActive = me._active;
11541
+ const active = activeElements.map(({datasetIndex, index}) => {
11542
+ const meta = me._chart.getDatasetMeta(datasetIndex);
11543
+ if (!meta) {
11544
+ throw new Error('Cannot find a dataset at index ' + datasetIndex);
11545
+ }
11546
+ return {
11547
+ datasetIndex,
11548
+ element: meta.data[index],
11549
+ index,
11550
+ };
11551
+ });
11552
+ const changed = !_elementsEqual(lastActive, active);
11553
+ const positionChanged = me._positionChanged(active, eventPosition);
11554
+ if (changed || positionChanged) {
11555
+ me._active = active;
11556
+ me._eventPosition = eventPosition;
11557
+ me.update(true);
11558
+ }
11559
+ }
11560
+ handleEvent(e, replay) {
11561
+ const me = this;
11562
+ const options = me.options;
11563
+ const lastActive = me._active || [];
11564
+ let changed = false;
11565
+ let active = [];
11566
+ if (e.type !== 'mouseout') {
11567
+ active = me._chart.getElementsAtEventForMode(e, options.mode, options, replay);
11568
+ if (options.reverse) {
11569
+ active.reverse();
11570
+ }
11571
+ }
11572
+ const positionChanged = me._positionChanged(active, e);
11573
+ changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
11574
+ if (changed) {
11575
+ me._active = active;
11576
+ if (options.enabled || options.external) {
11577
+ me._eventPosition = {
11578
+ x: e.x,
11579
+ y: e.y
11580
+ };
11581
+ me.update(true, replay);
11582
+ }
11583
+ }
11584
+ return changed;
11585
+ }
11586
+ _positionChanged(active, e) {
11587
+ const {caretX, caretY, options} = this;
11588
+ const position = positioners[options.position].call(this, active, e);
11589
+ return position !== false && (caretX !== position.x || caretY !== position.y);
11590
+ }
11591
+ }
11592
+ Tooltip.positioners = positioners;
11593
+ var plugin_tooltip = {
11594
+ id: 'tooltip',
11595
+ _element: Tooltip,
11596
+ positioners,
11597
+ afterInit(chart, _args, options) {
11598
+ if (options) {
11599
+ chart.tooltip = new Tooltip({_chart: chart, options});
11600
+ }
11601
+ },
11602
+ beforeUpdate(chart, _args, options) {
11603
+ if (chart.tooltip) {
11604
+ chart.tooltip.initialize(options);
11605
+ }
11606
+ },
11607
+ reset(chart, _args, options) {
11608
+ if (chart.tooltip) {
11609
+ chart.tooltip.initialize(options);
11610
+ }
11611
+ },
11612
+ afterDraw(chart) {
11613
+ const tooltip = chart.tooltip;
11614
+ const args = {
11615
+ tooltip
11616
+ };
11617
+ if (chart.notifyPlugins('beforeTooltipDraw', args) === false) {
11618
+ return;
11619
+ }
11620
+ if (tooltip) {
11621
+ tooltip.draw(chart.ctx);
11622
+ }
11623
+ chart.notifyPlugins('afterTooltipDraw', args);
11624
+ },
11625
+ afterEvent(chart, args) {
11626
+ if (chart.tooltip) {
11627
+ const useFinalPosition = args.replay;
11628
+ if (chart.tooltip.handleEvent(args.event, useFinalPosition)) {
11629
+ args.changed = true;
11630
+ }
11631
+ }
11632
+ },
11633
+ defaults: {
11634
+ enabled: true,
11635
+ external: null,
11636
+ position: 'average',
11637
+ backgroundColor: '#ffffff',
11638
+
11639
+ titleColor: '#515151',
11640
+ titleFont: {
11641
+ weight: 'normal',
11642
+ size:'12',
11643
+ },
11644
+ titleSpacing: 2,
11645
+ titleMarginBottom: 6,
11646
+ titleAlign: 'left',
11647
+ bodyColor: '#515151',
11648
+ bodySpacing: 2,
11649
+ bodyFont: {
11650
+ weight: 'bold',
11651
+ size:'12',
11652
+ },
11653
+ bodyAlign: 'left',
11654
+ footerColor: '#515151',
11655
+ footerSpacing: 2,
11656
+ footerMarginTop: 6,
11657
+ footerFont: {
11658
+ weight: 'bold',
11659
+ size:'12',
11660
+ },
11661
+ footerAlign: 'left',
11662
+ padding: 10,
11663
+ caretPadding: 10,
11664
+ caretSize: 0,
11665
+ cornerRadius: 6,
11666
+ boxHeight: (ctx, opts) => opts.bodyFont.size,
11667
+ boxWidth: (ctx, opts) => opts.bodyFont.size,
11668
+ multiKeyBackground: '#fff',
11669
+ displayColors: false,
11670
+ borderColor: '#f9f9f9',
11671
+ borderWidth: 1,
11672
+ animation: {
11673
+ duration: 400,
11674
+ easing: 'easeOutQuart',
11675
+ },
11676
+ animations: {
11677
+ numbers: {
11678
+ type: 'number',
11679
+ properties: ['x', 'y', 'width', 'height', 'caretX', 'caretY'],
11680
+ },
11681
+ opacity: {
11682
+ easing: 'linear',
11683
+ duration: 200
11684
+ }
11685
+ },
11686
+ callbacks: {
11687
+ beforeTitle: noop,
11688
+ title(tooltipItems) {
11689
+ if (tooltipItems.length > 0) {
11690
+ const item = tooltipItems[0];
11691
+ const labels = item.chart.data.labels;
11692
+ const labelCount = labels ? labels.length : 0;
11693
+ if (this && this.options && this.options.mode === 'dataset') {
11694
+ return item.dataset.label || '';
11695
+ } else if (item.label) {
11696
+ return item.label;
11697
+ } else if (labelCount > 0 && item.dataIndex < labelCount) {
11698
+ return labels[item.dataIndex];
11699
+ }
11700
+ }
11701
+ return '';
11702
+ },
11703
+ afterTitle: noop,
11704
+ beforeBody: noop,
11705
+ beforeLabel: noop,
11706
+ label(tooltipItem) {
11707
+ if (this && this.options && this.options.mode === 'dataset') {
11708
+ return tooltipItem.label + ': ' + tooltipItem.formattedValue || tooltipItem.formattedValue;
11709
+ }
11710
+ let label = tooltipItem.dataset.label || '';
11711
+ if (label) {
11712
+ label += ': ';
11713
+ }
11714
+ const value = tooltipItem.formattedValue;
11715
+ if (!isNullOrUndef(value)) {
11716
+ label += value;
11717
+ }
11718
+ return label;
11719
+ },
11720
+ labelColor(tooltipItem) {
11721
+ const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
11722
+ const options = meta.controller.getStyle(tooltipItem.dataIndex);
11723
+ return {
11724
+ borderColor: options.borderColor,
11725
+ backgroundColor: options.backgroundColor,
11726
+ borderWidth: options.borderWidth,
11727
+ borderDash: options.borderDash,
11728
+ borderDashOffset: options.borderDashOffset,
11729
+ borderRadius: 0,
11730
+ };
11731
+ },
11732
+ labelTextColor() {
11733
+ return this.options.bodyColor;
11734
+ },
11735
+ labelPointStyle(tooltipItem) {
11736
+ const meta = tooltipItem.chart.getDatasetMeta(tooltipItem.datasetIndex);
11737
+ const options = meta.controller.getStyle(tooltipItem.dataIndex);
11738
+ return {
11739
+ pointStyle: options.pointStyle,
11740
+ rotation: options.rotation,
11741
+ };
11742
+ },
11743
+ afterLabel: noop,
11744
+ afterBody: noop,
11745
+ beforeFooter: noop,
11746
+ footer: noop,
11747
+ afterFooter: noop
11748
+ }
11749
+ },
11750
+ defaultRoutes: {
11751
+ bodyFont: 'font',
11752
+ footerFont: 'font',
11753
+ titleFont: 'font'
11754
+ },
11755
+ descriptors: {
11756
+ _scriptable: (name) => name !== 'filter' && name !== 'itemSort' && name !== 'external',
11757
+ _indexable: false,
11758
+ callbacks: {
11759
+ _scriptable: false,
11760
+ _indexable: false,
11761
+ },
11762
+ animation: {
11763
+ _fallback: false
11764
+ },
11765
+ animations: {
11766
+ _fallback: 'animation'
11767
+ }
11768
+ },
11769
+ additionalOptionScopes: ['interaction']
11770
+ };
11771
+
11772
+ var plugins = /*#__PURE__*/Object.freeze({
11773
+ __proto__: null,
11774
+ Decimation: plugin_decimation,
11775
+ Filler: plugin_filler,
11776
+ Legend: plugin_legend,
11777
+ SubTitle: plugin_subtitle,
11778
+ Title: plugin_title,
11779
+ Tooltip: plugin_tooltip
11780
+ });
11781
+
11782
+ const addIfString = (labels, raw, index) => typeof raw === 'string'
11783
+ ? labels.push(raw) - 1
11784
+ : isNaN(raw) ? null : index;
11785
+ function findOrAddLabel(labels, raw, index) {
11786
+ const first = labels.indexOf(raw);
11787
+ if (first === -1) {
11788
+ return addIfString(labels, raw, index);
11789
+ }
11790
+ const last = labels.lastIndexOf(raw);
11791
+ return first !== last ? index : first;
11792
+ }
11793
+ const validIndex = (index, max) => index === null ? null : _limitValue(Math.round(index), 0, max);
11794
+ class CategoryScale extends Scale {
11795
+ constructor(cfg) {
11796
+ super(cfg);
11797
+ this._startValue = undefined;
11798
+ this._valueRange = 0;
11799
+ }
11800
+ parse(raw, index) {
11801
+ if (isNullOrUndef(raw)) {
11802
+ return null;
11803
+ }
11804
+ const labels = this.getLabels();
11805
+ index = isFinite(index) && labels[index] === raw ? index
11806
+ : findOrAddLabel(labels, raw, valueOrDefault(index, raw));
11807
+ return validIndex(index, labels.length - 1);
11808
+ }
11809
+ determineDataLimits() {
11810
+ const me = this;
11811
+ const {minDefined, maxDefined} = me.getUserBounds();
11812
+ let {min, max} = me.getMinMax(true);
11813
+ if (me.options.bounds === 'ticks') {
11814
+ if (!minDefined) {
11815
+ min = 0;
11816
+ }
11817
+ if (!maxDefined) {
11818
+ max = me.getLabels().length - 1;
11819
+ }
11820
+ }
11821
+ me.min = min;
11822
+ me.max = max;
11823
+ }
11824
+ buildTicks() {
11825
+ const me = this;
11826
+ const min = me.min;
11827
+ const max = me.max;
11828
+ const offset = me.options.offset;
11829
+ const ticks = [];
11830
+ let labels = me.getLabels();
11831
+ labels = (min === 0 && max === labels.length - 1) ? labels : labels.slice(min, max + 1);
11832
+ me._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
11833
+ me._startValue = me.min - (offset ? 0.5 : 0);
11834
+ for (let value = min; value <= max; value++) {
11835
+ ticks.push({value});
11836
+ }
11837
+ return ticks;
11838
+ }
11839
+ getLabelForValue(value) {
11840
+ const me = this;
11841
+ const labels = me.getLabels();
11842
+ if (value >= 0 && value < labels.length) {
11843
+ return labels[value];
11844
+ }
11845
+ return value;
11846
+ }
11847
+ configure() {
11848
+ const me = this;
11849
+ super.configure();
11850
+ if (!me.isHorizontal()) {
11851
+ me._reversePixels = !me._reversePixels;
11852
+ }
11853
+ }
11854
+ getPixelForValue(value) {
11855
+ const me = this;
11856
+ if (typeof value !== 'number') {
11857
+ value = me.parse(value);
11858
+ }
11859
+ return value === null ? NaN : me.getPixelForDecimal((value - me._startValue) / me._valueRange);
11860
+ }
11861
+ getPixelForTick(index) {
11862
+ const me = this;
11863
+ const ticks = me.ticks;
11864
+ if (index < 0 || index > ticks.length - 1) {
11865
+ return null;
11866
+ }
11867
+ return me.getPixelForValue(ticks[index].value);
11868
+ }
11869
+ getValueForPixel(pixel) {
11870
+ const me = this;
11871
+ return Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
11872
+ }
11873
+ getBasePixel() {
11874
+ return this.bottom;
11875
+ }
11876
+ }
11877
+ CategoryScale.id = 'category';
11878
+ CategoryScale.defaults = {
11879
+ ticks: {
11880
+ callback: CategoryScale.prototype.getLabelForValue
11881
+ }
11882
+ };
11883
+
11884
+ function generateTicks$1(generationOptions, dataRange) {
11885
+ const ticks = [];
11886
+ const MIN_SPACING = 1e-14;
11887
+ const {bounds, step, min, max, precision, count, maxTicks, maxDigits, includeBounds} = generationOptions;
11888
+ const unit = step || 1;
11889
+ const maxSpaces = maxTicks - 1;
11890
+ const {min: rmin, max: rmax} = dataRange;
11891
+ const minDefined = !isNullOrUndef(min);
11892
+ const maxDefined = !isNullOrUndef(max);
11893
+ const countDefined = !isNullOrUndef(count);
11894
+ const minSpacing = (rmax - rmin) / (maxDigits + 1);
11895
+ let spacing = niceNum((rmax - rmin) / maxSpaces / unit) * unit;
11896
+ let factor, niceMin, niceMax, numSpaces;
11897
+ if (spacing < MIN_SPACING && !minDefined && !maxDefined) {
11898
+ return [{value: rmin}, {value: rmax}];
11899
+ }
11900
+ numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
11901
+ if (numSpaces > maxSpaces) {
11902
+ spacing = niceNum(numSpaces * spacing / maxSpaces / unit) * unit;
11903
+ }
11904
+ if (!isNullOrUndef(precision)) {
11905
+ factor = Math.pow(10, precision);
11906
+ spacing = Math.ceil(spacing * factor) / factor;
11907
+ }
11908
+ if (bounds === 'ticks') {
11909
+ niceMin = Math.floor(rmin / spacing) * spacing;
11910
+ niceMax = Math.ceil(rmax / spacing) * spacing;
11911
+ } else {
11912
+ niceMin = rmin;
11913
+ niceMax = rmax;
11914
+ }
11915
+ if (minDefined && maxDefined && step && almostWhole((max - min) / step, spacing / 1000)) {
11916
+ numSpaces = Math.round(Math.min((max - min) / spacing, maxTicks));
11917
+ spacing = (max - min) / numSpaces;
11918
+ niceMin = min;
11919
+ niceMax = max;
11920
+ } else if (countDefined) {
11921
+ niceMin = minDefined ? min : niceMin;
11922
+ niceMax = maxDefined ? max : niceMax;
11923
+ numSpaces = count - 1;
11924
+ spacing = (niceMax - niceMin) / numSpaces;
11925
+ } else {
11926
+ numSpaces = (niceMax - niceMin) / spacing;
11927
+ if (almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
11928
+ numSpaces = Math.round(numSpaces);
11929
+ } else {
11930
+ numSpaces = Math.ceil(numSpaces);
11931
+ }
11932
+ }
11933
+ const decimalPlaces = Math.max(
11934
+ _decimalPlaces(spacing),
11935
+ _decimalPlaces(niceMin)
11936
+ );
11937
+ factor = Math.pow(10, isNullOrUndef(precision) ? decimalPlaces : precision);
11938
+ niceMin = Math.round(niceMin * factor) / factor;
11939
+ niceMax = Math.round(niceMax * factor) / factor;
11940
+ let j = 0;
11941
+ if (minDefined) {
11942
+ if (includeBounds && niceMin !== min) {
11943
+ ticks.push({value: min});
11944
+ if (niceMin < min) {
11945
+ j++;
11946
+ }
11947
+ if (almostEquals(Math.round((niceMin + j * spacing) * factor) / factor, min, relativeLabelSize(min, minSpacing, generationOptions))) {
11948
+ j++;
11949
+ }
11950
+ } else if (niceMin < min) {
11951
+ j++;
11952
+ }
11953
+ }
11954
+ for (; j < numSpaces; ++j) {
11955
+ ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor});
11956
+ }
11957
+ if (maxDefined && includeBounds && niceMax !== max) {
11958
+ if (almostEquals(ticks[ticks.length - 1].value, max, relativeLabelSize(max, minSpacing, generationOptions))) {
11959
+ ticks[ticks.length - 1].value = max;
11960
+ } else {
11961
+ ticks.push({value: max});
11962
+ }
11963
+ } else if (!maxDefined || niceMax === max) {
11964
+ ticks.push({value: niceMax});
11965
+ }
11966
+ return ticks;
11967
+ }
11968
+ function relativeLabelSize(value, minSpacing, {horizontal, minRotation}) {
11969
+ const rad = toRadians(minRotation);
11970
+ const ratio = (horizontal ? Math.sin(rad) : Math.cos(rad)) || 0.001;
11971
+ const length = 0.75 * minSpacing * ('' + value).length;
11972
+ return Math.min(minSpacing / ratio, length);
11973
+ }
11974
+ class LinearScaleBase extends Scale {
11975
+ constructor(cfg) {
11976
+ super(cfg);
11977
+ this.start = undefined;
11978
+ this.end = undefined;
11979
+ this._startValue = undefined;
11980
+ this._endValue = undefined;
11981
+ this._valueRange = 0;
11982
+ }
11983
+ parse(raw, index) {
11984
+ if (isNullOrUndef(raw)) {
11985
+ return null;
11986
+ }
11987
+ if ((typeof raw === 'number' || raw instanceof Number) && !isFinite(+raw)) {
11988
+ return null;
11989
+ }
11990
+ return +raw;
11991
+ }
11992
+ handleTickRangeOptions() {
11993
+ const me = this;
11994
+ const {beginAtZero} = me.options;
11995
+ const {minDefined, maxDefined} = me.getUserBounds();
11996
+ let {min, max} = me;
11997
+ const setMin = v => (min = minDefined ? min : v);
11998
+ const setMax = v => (max = maxDefined ? max : v);
11999
+ if (beginAtZero) {
12000
+ const minSign = sign(min);
12001
+ const maxSign = sign(max);
12002
+ if (minSign < 0 && maxSign < 0) {
12003
+ setMax(0);
12004
+ } else if (minSign > 0 && maxSign > 0) {
12005
+ setMin(0);
12006
+ }
12007
+ }
12008
+ if (min === max) {
12009
+ let offset = 1;
12010
+ if (max >= Number.MAX_SAFE_INTEGER || min <= Number.MIN_SAFE_INTEGER) {
12011
+ offset = Math.abs(max * 0.05);
12012
+ }
12013
+ setMax(max + offset);
12014
+ if (!beginAtZero) {
12015
+ setMin(min - offset);
12016
+ }
12017
+ }
12018
+ me.min = min;
12019
+ me.max = max;
12020
+ }
12021
+ getTickLimit() {
12022
+ const me = this;
12023
+ const tickOpts = me.options.ticks;
12024
+ let {maxTicksLimit, stepSize} = tickOpts;
12025
+ let maxTicks;
12026
+ if (stepSize) {
12027
+ maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
12028
+ } else {
12029
+ maxTicks = me.computeTickLimit();
12030
+ maxTicksLimit = maxTicksLimit || 11;
12031
+ }
12032
+ if (maxTicksLimit) {
12033
+ maxTicks = Math.min(maxTicksLimit, maxTicks);
12034
+ }
12035
+ return maxTicks;
12036
+ }
12037
+ computeTickLimit() {
12038
+ return Number.POSITIVE_INFINITY;
12039
+ }
12040
+ buildTicks() {
12041
+ const me = this;
12042
+ const opts = me.options;
12043
+ const tickOpts = opts.ticks;
12044
+ let maxTicks = me.getTickLimit();
12045
+ maxTicks = Math.max(2, maxTicks);
12046
+ const numericGeneratorOptions = {
12047
+ maxTicks,
12048
+ bounds: opts.bounds,
12049
+ min: opts.min,
12050
+ max: opts.max,
12051
+ precision: tickOpts.precision,
12052
+ step: tickOpts.stepSize,
12053
+ count: tickOpts.count,
12054
+ maxDigits: me._maxDigits(),
12055
+ horizontal: me.isHorizontal(),
12056
+ minRotation: tickOpts.minRotation || 0,
12057
+ includeBounds: tickOpts.includeBounds !== false
12058
+ };
12059
+ const dataRange = me._range || me;
12060
+ const ticks = generateTicks$1(numericGeneratorOptions, dataRange);
12061
+ if (opts.bounds === 'ticks') {
12062
+ _setMinAndMaxByKey(ticks, me, 'value');
12063
+ }
12064
+ if (opts.reverse) {
12065
+ ticks.reverse();
12066
+ me.start = me.max;
12067
+ me.end = me.min;
12068
+ } else {
12069
+ me.start = me.min;
12070
+ me.end = me.max;
12071
+ }
12072
+ return ticks;
12073
+ }
12074
+ configure() {
12075
+ const me = this;
12076
+ const ticks = me.ticks;
12077
+ let start = me.min;
12078
+ let end = me.max;
12079
+ super.configure();
12080
+ if (me.options.offset && ticks.length) {
12081
+ const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
12082
+ start -= offset;
12083
+ end += offset;
12084
+ }
12085
+ me._startValue = start;
12086
+ me._endValue = end;
12087
+ me._valueRange = end - start;
12088
+ }
12089
+ getLabelForValue(value) {
12090
+ return formatNumber(value, this.chart.options.locale);
12091
+ }
12092
+ }
12093
+
12094
+ class LinearScale extends LinearScaleBase {
12095
+ determineDataLimits() {
12096
+ const me = this;
12097
+ const {min, max} = me.getMinMax(true);
12098
+ me.min = isNumberFinite(min) ? min : 0;
12099
+ me.max = isNumberFinite(max) ? max : 1;
12100
+ me.handleTickRangeOptions();
12101
+ }
12102
+ computeTickLimit() {
12103
+ const me = this;
12104
+ const horizontal = me.isHorizontal();
12105
+ const length = horizontal ? me.width : me.height;
12106
+ const minRotation = toRadians(me.options.ticks.minRotation);
12107
+ const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;
12108
+ const tickFont = me._resolveTickFontOptions(0);
12109
+ return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));
12110
+ }
12111
+ getPixelForValue(value) {
12112
+ return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
12113
+ }
12114
+ getValueForPixel(pixel) {
12115
+ return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
12116
+ }
12117
+ }
12118
+ LinearScale.id = 'linear';
12119
+ LinearScale.defaults = {
12120
+ ticks: {
12121
+ callback: Ticks.formatters.numeric
12122
+ }
12123
+ };
12124
+
12125
+ function isMajor(tickVal) {
12126
+ const remain = tickVal / (Math.pow(10, Math.floor(log10(tickVal))));
12127
+ return remain === 1;
12128
+ }
12129
+ function generateTicks(generationOptions, dataRange) {
12130
+ const endExp = Math.floor(log10(dataRange.max));
12131
+ const endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
12132
+ const ticks = [];
12133
+ let tickVal = finiteOrDefault(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min))));
12134
+ let exp = Math.floor(log10(tickVal));
12135
+ let significand = Math.floor(tickVal / Math.pow(10, exp));
12136
+ let precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
12137
+ do {
12138
+ ticks.push({value: tickVal, major: isMajor(tickVal)});
12139
+ ++significand;
12140
+ if (significand === 10) {
12141
+ significand = 1;
12142
+ ++exp;
12143
+ precision = exp >= 0 ? 1 : precision;
12144
+ }
12145
+ tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
12146
+ } while (exp < endExp || (exp === endExp && significand < endSignificand));
12147
+ const lastTick = finiteOrDefault(generationOptions.max, tickVal);
12148
+ ticks.push({value: lastTick, major: isMajor(tickVal)});
12149
+ return ticks;
12150
+ }
12151
+ class LogarithmicScale extends Scale {
12152
+ constructor(cfg) {
12153
+ super(cfg);
12154
+ this.start = undefined;
12155
+ this.end = undefined;
12156
+ this._startValue = undefined;
12157
+ this._valueRange = 0;
12158
+ }
12159
+ parse(raw, index) {
12160
+ const value = LinearScaleBase.prototype.parse.apply(this, [raw, index]);
12161
+ if (value === 0) {
12162
+ this._zero = true;
12163
+ return undefined;
12164
+ }
12165
+ return isNumberFinite(value) && value > 0 ? value : null;
12166
+ }
12167
+ determineDataLimits() {
12168
+ const me = this;
12169
+ const {min, max} = me.getMinMax(true);
12170
+ me.min = isNumberFinite(min) ? Math.max(0, min) : null;
12171
+ me.max = isNumberFinite(max) ? Math.max(0, max) : null;
12172
+ if (me.options.beginAtZero) {
12173
+ me._zero = true;
12174
+ }
12175
+ me.handleTickRangeOptions();
12176
+ }
12177
+ handleTickRangeOptions() {
12178
+ const me = this;
12179
+ const {minDefined, maxDefined} = me.getUserBounds();
12180
+ let min = me.min;
12181
+ let max = me.max;
12182
+ const setMin = v => (min = minDefined ? min : v);
12183
+ const setMax = v => (max = maxDefined ? max : v);
12184
+ const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m);
12185
+ if (min === max) {
12186
+ if (min <= 0) {
12187
+ setMin(1);
12188
+ setMax(10);
12189
+ } else {
12190
+ setMin(exp(min, -1));
12191
+ setMax(exp(max, +1));
12192
+ }
12193
+ }
12194
+ if (min <= 0) {
12195
+ setMin(exp(max, -1));
12196
+ }
12197
+ if (max <= 0) {
12198
+ setMax(exp(min, +1));
12199
+ }
12200
+ if (me._zero && me.min !== me._suggestedMin && min === exp(me.min, 0)) {
12201
+ setMin(exp(min, -1));
12202
+ }
12203
+ me.min = min;
12204
+ me.max = max;
12205
+ }
12206
+ buildTicks() {
12207
+ const me = this;
12208
+ const opts = me.options;
12209
+ const generationOptions = {
12210
+ min: me._userMin,
12211
+ max: me._userMax
12212
+ };
12213
+ const ticks = generateTicks(generationOptions, me);
12214
+ if (opts.bounds === 'ticks') {
12215
+ _setMinAndMaxByKey(ticks, me, 'value');
12216
+ }
12217
+ if (opts.reverse) {
12218
+ ticks.reverse();
12219
+ me.start = me.max;
12220
+ me.end = me.min;
12221
+ } else {
12222
+ me.start = me.min;
12223
+ me.end = me.max;
12224
+ }
12225
+ return ticks;
12226
+ }
12227
+ getLabelForValue(value) {
12228
+ return value === undefined ? '0' : formatNumber(value, this.chart.options.locale);
12229
+ }
12230
+ configure() {
12231
+ const me = this;
12232
+ const start = me.min;
12233
+ super.configure();
12234
+ me._startValue = log10(start);
12235
+ me._valueRange = log10(me.max) - log10(start);
12236
+ }
12237
+ getPixelForValue(value) {
12238
+ const me = this;
12239
+ if (value === undefined || value === 0) {
12240
+ value = me.min;
12241
+ }
12242
+ if (value === null || isNaN(value)) {
12243
+ return NaN;
12244
+ }
12245
+ return me.getPixelForDecimal(value === me.min
12246
+ ? 0
12247
+ : (log10(value) - me._startValue) / me._valueRange);
12248
+ }
12249
+ getValueForPixel(pixel) {
12250
+ const me = this;
12251
+ const decimal = me.getDecimalForPixel(pixel);
12252
+ return Math.pow(10, me._startValue + decimal * me._valueRange);
12253
+ }
12254
+ }
12255
+ LogarithmicScale.id = 'logarithmic';
12256
+ LogarithmicScale.defaults = {
12257
+ ticks: {
12258
+ callback: Ticks.formatters.logarithmic,
12259
+ major: {
12260
+ enabled: true
12261
+ }
12262
+ }
12263
+ };
12264
+
12265
+ function getTickBackdropHeight(opts) {
12266
+ const tickOpts = opts.ticks;
12267
+ if (tickOpts.display && opts.display) {
12268
+ const padding = toPadding(tickOpts.backdropPadding);
12269
+ return valueOrDefault(tickOpts.font && tickOpts.font.size, defaults.font.size) + padding.height;
12270
+ }
12271
+ return 0;
12272
+ }
12273
+ function measureLabelSize(ctx, font, label) {
12274
+ label = isArray(label) ? label : [label];
12275
+ return {
12276
+ w: _longestText(ctx, font.string, label),
12277
+ h: label.length * font.lineHeight
12278
+ };
12279
+ }
12280
+ function determineLimits(angle, pos, size, min, max) {
12281
+ if (angle === min || angle === max) {
12282
+ return {
12283
+ start: pos - (size / 2),
12284
+ end: pos + (size / 2)
12285
+ };
12286
+ } else if (angle < min || angle > max) {
12287
+ return {
12288
+ start: pos - size,
12289
+ end: pos
12290
+ };
12291
+ }
12292
+ return {
12293
+ start: pos,
12294
+ end: pos + size
12295
+ };
12296
+ }
12297
+ function fitWithPointLabels(scale) {
12298
+ const furthestLimits = {
12299
+ l: 0,
12300
+ r: scale.width,
12301
+ t: 0,
12302
+ b: scale.height - scale.paddingTop
12303
+ };
12304
+ const furthestAngles = {};
12305
+ const labelSizes = [];
12306
+ const padding = [];
12307
+ const valueCount = scale.getLabels().length;
12308
+ for (let i = 0; i < valueCount; i++) {
12309
+ const opts = scale.options.pointLabels.setContext(scale.getPointLabelContext(i));
12310
+ padding[i] = opts.padding;
12311
+ const pointPosition = scale.getPointPosition(i, scale.drawingArea + padding[i]);
12312
+ const plFont = toFont(opts.font);
12313
+ const textSize = measureLabelSize(scale.ctx, plFont, scale._pointLabels[i]);
12314
+ labelSizes[i] = textSize;
12315
+ const angleRadians = scale.getIndexAngle(i);
12316
+ const angle = toDegrees(angleRadians);
12317
+ const hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
12318
+ const vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
12319
+ if (hLimits.start < furthestLimits.l) {
12320
+ furthestLimits.l = hLimits.start;
12321
+ furthestAngles.l = angleRadians;
12322
+ }
12323
+ if (hLimits.end > furthestLimits.r) {
12324
+ furthestLimits.r = hLimits.end;
12325
+ furthestAngles.r = angleRadians;
12326
+ }
12327
+ if (vLimits.start < furthestLimits.t) {
12328
+ furthestLimits.t = vLimits.start;
12329
+ furthestAngles.t = angleRadians;
12330
+ }
12331
+ if (vLimits.end > furthestLimits.b) {
12332
+ furthestLimits.b = vLimits.end;
12333
+ furthestAngles.b = angleRadians;
12334
+ }
12335
+ }
12336
+ scale._setReductions(scale.drawingArea, furthestLimits, furthestAngles);
12337
+ scale._pointLabelItems = buildPointLabelItems(scale, labelSizes, padding);
12338
+ }
12339
+ function buildPointLabelItems(scale, labelSizes, padding) {
12340
+ const items = [];
12341
+ const valueCount = scale.getLabels().length;
12342
+ const opts = scale.options;
12343
+ const tickBackdropHeight = getTickBackdropHeight(opts);
12344
+ const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
12345
+ for (let i = 0; i < valueCount; i++) {
12346
+ const extra = (i === 0 ? tickBackdropHeight / 2 : 0);
12347
+ const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + padding[i]);
12348
+ const angle = toDegrees(scale.getIndexAngle(i));
12349
+ const size = labelSizes[i];
12350
+ const y = yForAngle(pointLabelPosition.y, size.h, angle);
12351
+ const textAlign = getTextAlignForAngle(angle);
12352
+ const left = leftForTextAlign(pointLabelPosition.x, size.w, textAlign);
12353
+ items.push({
12354
+ x: pointLabelPosition.x,
12355
+ y,
12356
+ textAlign,
12357
+ left,
12358
+ top: y,
12359
+ right: left + size.w,
12360
+ bottom: y + size.h
12361
+ });
12362
+ }
12363
+ return items;
12364
+ }
12365
+ function getTextAlignForAngle(angle) {
12366
+ if (angle === 0 || angle === 180) {
12367
+ return 'center';
12368
+ } else if (angle < 180) {
12369
+ return 'left';
12370
+ }
12371
+ return 'right';
12372
+ }
12373
+ function leftForTextAlign(x, w, align) {
12374
+ if (align === 'right') {
12375
+ x -= w;
12376
+ } else if (align === 'center') {
12377
+ x -= (w / 2);
12378
+ }
12379
+ return x;
12380
+ }
12381
+ function yForAngle(y, h, angle) {
12382
+ if (angle === 90 || angle === 270) {
12383
+ y -= (h / 2);
12384
+ } else if (angle > 270 || angle < 90) {
12385
+ y -= h;
12386
+ }
12387
+ return y;
12388
+ }
12389
+ function drawPointLabels(scale, labelCount) {
12390
+ const {ctx, options: {pointLabels}} = scale;
12391
+ for (let i = labelCount - 1; i >= 0; i--) {
12392
+ const optsAtIndex = pointLabels.setContext(scale.getPointLabelContext(i));
12393
+ const plFont = toFont(optsAtIndex.font);
12394
+ const {x, y, textAlign, left, top, right, bottom} = scale._pointLabelItems[i];
12395
+ const {backdropColor} = optsAtIndex;
12396
+ if (!isNullOrUndef(backdropColor)) {
12397
+ const padding = toPadding(optsAtIndex.backdropPadding);
12398
+ ctx.fillStyle = backdropColor;
12399
+ ctx.fillRect(left - padding.left, top - padding.top, right - left + padding.width, bottom - top + padding.height);
12400
+ }
12401
+ renderText(
12402
+ ctx,
12403
+ scale._pointLabels[i],
12404
+ x,
12405
+ y + (plFont.lineHeight / 2),
12406
+ plFont,
12407
+ {
12408
+ color: optsAtIndex.color,
12409
+ textAlign: textAlign,
12410
+ textBaseline: 'middle'
12411
+ }
12412
+ );
12413
+ }
12414
+ }
12415
+ function pathRadiusLine(scale, radius, circular, labelCount) {
12416
+ const {ctx} = scale;
12417
+ if (circular) {
12418
+ ctx.arc(scale.xCenter, scale.yCenter, radius, 0, TAU);
12419
+ } else {
12420
+ let pointPosition = scale.getPointPosition(0, radius);
12421
+ ctx.moveTo(pointPosition.x, pointPosition.y);
12422
+ for (let i = 1; i < labelCount; i++) {
12423
+ pointPosition = scale.getPointPosition(i, radius);
12424
+ ctx.lineTo(pointPosition.x, pointPosition.y);
12425
+ }
12426
+ }
12427
+ }
12428
+ function drawRadiusLine(scale, gridLineOpts, radius, labelCount) {
12429
+ const ctx = scale.ctx;
12430
+ const circular = gridLineOpts.circular;
12431
+ const {color, lineWidth} = gridLineOpts;
12432
+ if ((!circular && !labelCount) || !color || !lineWidth || radius < 0) {
12433
+ return;
12434
+ }
12435
+ ctx.save();
12436
+ ctx.strokeStyle = color;
12437
+ ctx.lineWidth = lineWidth;
12438
+ ctx.setLineDash(gridLineOpts.borderDash);
12439
+ ctx.lineDashOffset = gridLineOpts.borderDashOffset;
12440
+ ctx.beginPath();
12441
+ pathRadiusLine(scale, radius, circular, labelCount);
12442
+ ctx.closePath();
12443
+ ctx.stroke();
12444
+ ctx.restore();
12445
+ }
12446
+ function numberOrZero(param) {
12447
+ return isNumber(param) ? param : 0;
12448
+ }
12449
+ function createPointLabelContext(parent, index, label) {
12450
+ return Object.assign(Object.create(parent), {
12451
+ label,
12452
+ index,
12453
+ type: 'pointLabel'
12454
+ });
12455
+ }
12456
+ class RadialLinearScale extends LinearScaleBase {
12457
+ constructor(cfg) {
12458
+ super(cfg);
12459
+ this.xCenter = undefined;
12460
+ this.yCenter = undefined;
12461
+ this.drawingArea = undefined;
12462
+ this._pointLabels = [];
12463
+ this._pointLabelItems = [];
12464
+ }
12465
+ setDimensions() {
12466
+ const me = this;
12467
+ me.width = me.maxWidth;
12468
+ me.height = me.maxHeight;
12469
+ me.paddingTop = getTickBackdropHeight(me.options) / 2;
12470
+ me.xCenter = Math.floor(me.width / 2);
12471
+ me.yCenter = Math.floor((me.height - me.paddingTop) / 2);
12472
+ me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2;
12473
+ }
12474
+ determineDataLimits() {
12475
+ const me = this;
12476
+ const {min, max} = me.getMinMax(false);
12477
+ me.min = isNumberFinite(min) && !isNaN(min) ? min : 0;
12478
+ me.max = isNumberFinite(max) && !isNaN(max) ? max : 0;
12479
+ me.handleTickRangeOptions();
12480
+ }
12481
+ computeTickLimit() {
12482
+ return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
12483
+ }
12484
+ generateTickLabels(ticks) {
12485
+ const me = this;
12486
+ LinearScaleBase.prototype.generateTickLabels.call(me, ticks);
12487
+ me._pointLabels = me.getLabels().map((value, index) => {
12488
+ const label = callback(me.options.pointLabels.callback, [value, index], me);
12489
+ return label || label === 0 ? label : '';
12490
+ });
12491
+ }
12492
+ fit() {
12493
+ const me = this;
12494
+ const opts = me.options;
12495
+ if (opts.display && opts.pointLabels.display) {
12496
+ fitWithPointLabels(me);
12497
+ } else {
12498
+ me.setCenterPoint(0, 0, 0, 0);
12499
+ }
12500
+ }
12501
+ _setReductions(largestPossibleRadius, furthestLimits, furthestAngles) {
12502
+ const me = this;
12503
+ let radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
12504
+ let radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
12505
+ let radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
12506
+ let radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);
12507
+ radiusReductionLeft = numberOrZero(radiusReductionLeft);
12508
+ radiusReductionRight = numberOrZero(radiusReductionRight);
12509
+ radiusReductionTop = numberOrZero(radiusReductionTop);
12510
+ radiusReductionBottom = numberOrZero(radiusReductionBottom);
12511
+ me.drawingArea = Math.max(largestPossibleRadius / 2, Math.min(
12512
+ Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
12513
+ Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)));
12514
+ me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
12515
+ }
12516
+ setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {
12517
+ const me = this;
12518
+ const maxRight = me.width - rightMovement - me.drawingArea;
12519
+ const maxLeft = leftMovement + me.drawingArea;
12520
+ const maxTop = topMovement + me.drawingArea;
12521
+ const maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;
12522
+ me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
12523
+ me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
12524
+ }
12525
+ getIndexAngle(index) {
12526
+ const angleMultiplier = TAU / this.getLabels().length;
12527
+ const startAngle = this.options.startAngle || 0;
12528
+ return _normalizeAngle(index * angleMultiplier + toRadians(startAngle));
12529
+ }
12530
+ getDistanceFromCenterForValue(value) {
12531
+ const me = this;
12532
+ if (isNullOrUndef(value)) {
12533
+ return NaN;
12534
+ }
12535
+ const scalingFactor = me.drawingArea / (me.max - me.min);
12536
+ if (me.options.reverse) {
12537
+ return (me.max - value) * scalingFactor;
12538
+ }
12539
+ return (value - me.min) * scalingFactor;
12540
+ }
12541
+ getValueForDistanceFromCenter(distance) {
12542
+ if (isNullOrUndef(distance)) {
12543
+ return NaN;
12544
+ }
12545
+ const me = this;
12546
+ const scaledDistance = distance / (me.drawingArea / (me.max - me.min));
12547
+ return me.options.reverse ? me.max - scaledDistance : me.min + scaledDistance;
12548
+ }
12549
+ getPointLabelContext(index) {
12550
+ const me = this;
12551
+ const pointLabels = me._pointLabels || [];
12552
+ if (index >= 0 && index < pointLabels.length) {
12553
+ const pointLabel = pointLabels[index];
12554
+ return createPointLabelContext(me.getContext(), index, pointLabel);
12555
+ }
12556
+ }
12557
+ getPointPosition(index, distanceFromCenter) {
12558
+ const me = this;
12559
+ const angle = me.getIndexAngle(index) - HALF_PI;
12560
+ return {
12561
+ x: Math.cos(angle) * distanceFromCenter + me.xCenter,
12562
+ y: Math.sin(angle) * distanceFromCenter + me.yCenter,
12563
+ angle
12564
+ };
12565
+ }
12566
+ getPointPositionForValue(index, value) {
12567
+ return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
12568
+ }
12569
+ getBasePosition(index) {
12570
+ return this.getPointPositionForValue(index || 0, this.getBaseValue());
12571
+ }
12572
+ getPointLabelPosition(index) {
12573
+ const {left, top, right, bottom} = this._pointLabelItems[index];
12574
+ return {
12575
+ left,
12576
+ top,
12577
+ right,
12578
+ bottom,
12579
+ };
12580
+ }
12581
+ drawBackground() {
12582
+ const me = this;
12583
+ const {backgroundColor, grid: {circular}} = me.options;
12584
+ if (backgroundColor) {
12585
+ const ctx = me.ctx;
12586
+ ctx.save();
12587
+ ctx.beginPath();
12588
+ pathRadiusLine(me, me.getDistanceFromCenterForValue(me._endValue), circular, me.getLabels().length);
12589
+ ctx.closePath();
12590
+ ctx.fillStyle = backgroundColor;
12591
+ ctx.fill();
12592
+ ctx.restore();
12593
+ }
12594
+ }
12595
+ drawGrid() {
12596
+ const me = this;
12597
+ const ctx = me.ctx;
12598
+ const opts = me.options;
12599
+ const {angleLines, grid} = opts;
12600
+ const labelCount = me.getLabels().length;
12601
+ let i, offset, position;
12602
+ if (opts.pointLabels.display) {
12603
+ drawPointLabels(me, labelCount);
12604
+ }
12605
+ if (grid.display) {
12606
+ me.ticks.forEach((tick, index) => {
12607
+ if (index !== 0) {
12608
+ offset = me.getDistanceFromCenterForValue(tick.value);
12609
+ const optsAtIndex = grid.setContext(me.getContext(index - 1));
12610
+ drawRadiusLine(me, optsAtIndex, offset, labelCount);
12611
+ }
12612
+ });
12613
+ }
12614
+ if (angleLines.display) {
12615
+ ctx.save();
12616
+ for (i = me.getLabels().length - 1; i >= 0; i--) {
12617
+ const optsAtIndex = angleLines.setContext(me.getPointLabelContext(i));
12618
+ const {color, lineWidth} = optsAtIndex;
12619
+ if (!lineWidth || !color) {
12620
+ continue;
12621
+ }
12622
+ ctx.lineWidth = lineWidth;
12623
+ ctx.strokeStyle = color;
12624
+ ctx.setLineDash(optsAtIndex.borderDash);
12625
+ ctx.lineDashOffset = optsAtIndex.borderDashOffset;
12626
+ offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max);
12627
+ position = me.getPointPosition(i, offset);
12628
+ ctx.beginPath();
12629
+ ctx.moveTo(me.xCenter, me.yCenter);
12630
+ ctx.lineTo(position.x, position.y);
12631
+ ctx.stroke();
12632
+ }
12633
+ ctx.restore();
12634
+ }
12635
+ }
12636
+ drawBorder() {}
12637
+ drawLabels() {
12638
+ const me = this;
12639
+ const ctx = me.ctx;
12640
+ const opts = me.options;
12641
+ const tickOpts = opts.ticks;
12642
+ if (!tickOpts.display) {
12643
+ return;
12644
+ }
12645
+ const startAngle = me.getIndexAngle(0);
12646
+ let offset, width;
12647
+ ctx.save();
12648
+ ctx.translate(me.xCenter, me.yCenter);
12649
+ ctx.rotate(startAngle);
12650
+ ctx.textAlign = 'center';
12651
+ ctx.textBaseline = 'middle';
12652
+ me.ticks.forEach((tick, index) => {
12653
+ if (index === 0 && !opts.reverse) {
12654
+ return;
12655
+ }
12656
+ const optsAtIndex = tickOpts.setContext(me.getContext(index));
12657
+ const tickFont = toFont(optsAtIndex.font);
12658
+ offset = me.getDistanceFromCenterForValue(me.ticks[index].value);
12659
+ if (optsAtIndex.showLabelBackdrop) {
12660
+ ctx.font = tickFont.string;
12661
+ width = ctx.measureText(tick.label).width;
12662
+ ctx.fillStyle = optsAtIndex.backdropColor;
12663
+ const padding = toPadding(optsAtIndex.backdropPadding);
12664
+ ctx.fillRect(
12665
+ -width / 2 - padding.left,
12666
+ -offset - tickFont.size / 2 - padding.top,
12667
+ width + padding.width,
12668
+ tickFont.size + padding.height
12669
+ );
12670
+ }
12671
+ renderText(ctx, tick.label, 0, -offset, tickFont, {
12672
+ color: optsAtIndex.color,
12673
+ });
12674
+ });
12675
+ ctx.restore();
12676
+ }
12677
+ drawTitle() {}
12678
+ }
12679
+ RadialLinearScale.id = 'radialLinear';
12680
+ RadialLinearScale.defaults = {
12681
+ display: true,
12682
+ animate: true,
12683
+ position: 'chartArea',
12684
+ angleLines: {
12685
+ display: true,
12686
+ lineWidth: 1,
12687
+ borderDash: [],
12688
+ borderDashOffset: 0.0
12689
+ },
12690
+ grid: {
12691
+ circular: false
12692
+ },
12693
+ startAngle: 0,
12694
+ ticks: {
12695
+ showLabelBackdrop: true,
12696
+ callback: Ticks.formatters.numeric
12697
+ },
12698
+ pointLabels: {
12699
+ backdropColor: undefined,
12700
+ backdropPadding: 2,
12701
+ display: true,
12702
+ font: {
12703
+ size: 10
12704
+ },
12705
+ callback(label) {
12706
+ return label;
12707
+ },
12708
+ padding: 5
12709
+ }
12710
+ };
12711
+ RadialLinearScale.defaultRoutes = {
12712
+ 'angleLines.color': 'borderColor',
12713
+ 'pointLabels.color': 'color',
12714
+ 'ticks.color': 'color'
12715
+ };
12716
+ RadialLinearScale.descriptors = {
12717
+ angleLines: {
12718
+ _fallback: 'grid'
12719
+ }
12720
+ };
12721
+
12722
+ const INTERVALS = {
12723
+ millisecond: {common: true, size: 1, steps: 1000},
12724
+ second: {common: true, size: 1000, steps: 60},
12725
+ minute: {common: true, size: 60000, steps: 60},
12726
+ hour: {common: true, size: 3600000, steps: 24},
12727
+ day: {common: true, size: 86400000, steps: 30},
12728
+ week: {common: false, size: 604800000, steps: 4},
12729
+ month: {common: true, size: 2.628e9, steps: 12},
12730
+ quarter: {common: false, size: 7.884e9, steps: 4},
12731
+ year: {common: true, size: 3.154e10}
12732
+ };
12733
+ const UNITS = (Object.keys(INTERVALS));
12734
+ function sorter(a, b) {
12735
+ return a - b;
12736
+ }
12737
+ function parse(scale, input) {
12738
+ if (isNullOrUndef(input)) {
12739
+ return null;
12740
+ }
12741
+ const adapter = scale._adapter;
12742
+ const {parser, round, isoWeekday} = scale._parseOpts;
12743
+ let value = input;
12744
+ if (typeof parser === 'function') {
12745
+ value = parser(value);
12746
+ }
12747
+ if (!isNumberFinite(value)) {
12748
+ value = typeof parser === 'string'
12749
+ ? adapter.parse(value, parser)
12750
+ : adapter.parse(value);
12751
+ }
12752
+ if (value === null) {
12753
+ return null;
12754
+ }
12755
+ if (round) {
12756
+ value = round === 'week' && (isNumber(isoWeekday) || isoWeekday === true)
12757
+ ? adapter.startOf(value, 'isoWeek', isoWeekday)
12758
+ : adapter.startOf(value, round);
12759
+ }
12760
+ return +value;
12761
+ }
12762
+ function determineUnitForAutoTicks(minUnit, min, max, capacity) {
12763
+ const ilen = UNITS.length;
12764
+ for (let i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
12765
+ const interval = INTERVALS[UNITS[i]];
12766
+ const factor = interval.steps ? interval.steps : Number.MAX_SAFE_INTEGER;
12767
+ if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
12768
+ return UNITS[i];
12769
+ }
12770
+ }
12771
+ return UNITS[ilen - 1];
12772
+ }
12773
+ function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {
12774
+ for (let i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) {
12775
+ const unit = UNITS[i];
12776
+ if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {
12777
+ return unit;
12778
+ }
12779
+ }
12780
+ return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
12781
+ }
12782
+ function determineMajorUnit(unit) {
12783
+ for (let i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
12784
+ if (INTERVALS[UNITS[i]].common) {
12785
+ return UNITS[i];
12786
+ }
12787
+ }
12788
+ }
12789
+ function addTick(ticks, time, timestamps) {
12790
+ if (!timestamps) {
12791
+ ticks[time] = true;
12792
+ } else if (timestamps.length) {
12793
+ const {lo, hi} = _lookup(timestamps, time);
12794
+ const timestamp = timestamps[lo] >= time ? timestamps[lo] : timestamps[hi];
12795
+ ticks[timestamp] = true;
12796
+ }
12797
+ }
12798
+ function setMajorTicks(scale, ticks, map, majorUnit) {
12799
+ const adapter = scale._adapter;
12800
+ const first = +adapter.startOf(ticks[0].value, majorUnit);
12801
+ const last = ticks[ticks.length - 1].value;
12802
+ let major, index;
12803
+ for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) {
12804
+ index = map[major];
12805
+ if (index >= 0) {
12806
+ ticks[index].major = true;
12807
+ }
12808
+ }
12809
+ return ticks;
12810
+ }
12811
+ function ticksFromTimestamps(scale, values, majorUnit) {
12812
+ const ticks = [];
12813
+ const map = {};
12814
+ const ilen = values.length;
12815
+ let i, value;
12816
+ for (i = 0; i < ilen; ++i) {
12817
+ value = values[i];
12818
+ map[value] = i;
12819
+ ticks.push({
12820
+ value,
12821
+ major: false
12822
+ });
12823
+ }
12824
+ return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit);
12825
+ }
12826
+ class TimeScale extends Scale {
12827
+ constructor(props) {
12828
+ super(props);
12829
+ this._cache = {
12830
+ data: [],
12831
+ labels: [],
12832
+ all: []
12833
+ };
12834
+ this._unit = 'day';
12835
+ this._majorUnit = undefined;
12836
+ this._offsets = {};
12837
+ this._normalized = false;
12838
+ this._parseOpts = undefined;
12839
+ }
12840
+ init(scaleOpts, opts) {
12841
+ const time = scaleOpts.time || (scaleOpts.time = {});
12842
+ const adapter = this._adapter = new _adapters._date(scaleOpts.adapters.date);
12843
+ mergeIf(time.displayFormats, adapter.formats());
12844
+ this._parseOpts = {
12845
+ parser: time.parser,
12846
+ round: time.round,
12847
+ isoWeekday: time.isoWeekday
12848
+ };
12849
+ super.init(scaleOpts);
12850
+ this._normalized = opts.normalized;
12851
+ }
12852
+ parse(raw, index) {
12853
+ if (raw === undefined) {
12854
+ return null;
12855
+ }
12856
+ return parse(this, raw);
12857
+ }
12858
+ beforeLayout() {
12859
+ super.beforeLayout();
12860
+ this._cache = {
12861
+ data: [],
12862
+ labels: [],
12863
+ all: []
12864
+ };
12865
+ }
12866
+ determineDataLimits() {
12867
+ const me = this;
12868
+ const options = me.options;
12869
+ const adapter = me._adapter;
12870
+ const unit = options.time.unit || 'day';
12871
+ let {min, max, minDefined, maxDefined} = me.getUserBounds();
12872
+ function _applyBounds(bounds) {
12873
+ if (!minDefined && !isNaN(bounds.min)) {
12874
+ min = Math.min(min, bounds.min);
12875
+ }
12876
+ if (!maxDefined && !isNaN(bounds.max)) {
12877
+ max = Math.max(max, bounds.max);
12878
+ }
12879
+ }
12880
+ if (!minDefined || !maxDefined) {
12881
+ _applyBounds(me._getLabelBounds());
12882
+ if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {
12883
+ _applyBounds(me.getMinMax(false));
12884
+ }
12885
+ }
12886
+ min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);
12887
+ max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;
12888
+ me.min = Math.min(min, max - 1);
12889
+ me.max = Math.max(min + 1, max);
12890
+ }
12891
+ _getLabelBounds() {
12892
+ const arr = this.getLabelTimestamps();
12893
+ let min = Number.POSITIVE_INFINITY;
12894
+ let max = Number.NEGATIVE_INFINITY;
12895
+ if (arr.length) {
12896
+ min = arr[0];
12897
+ max = arr[arr.length - 1];
12898
+ }
12899
+ return {min, max};
12900
+ }
12901
+ buildTicks() {
12902
+ const me = this;
12903
+ const options = me.options;
12904
+ const timeOpts = options.time;
12905
+ const tickOpts = options.ticks;
12906
+ const timestamps = tickOpts.source === 'labels' ? me.getLabelTimestamps() : me._generate();
12907
+ if (options.bounds === 'ticks' && timestamps.length) {
12908
+ me.min = me._userMin || timestamps[0];
12909
+ me.max = me._userMax || timestamps[timestamps.length - 1];
12910
+ }
12911
+ const min = me.min;
12912
+ const max = me.max;
12913
+ const ticks = _filterBetween(timestamps, min, max);
12914
+ me._unit = timeOpts.unit || (tickOpts.autoSkip
12915
+ ? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, me._getLabelCapacity(min))
12916
+ : determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max));
12917
+ me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined
12918
+ : determineMajorUnit(me._unit);
12919
+ me.initOffsets(timestamps);
12920
+ if (options.reverse) {
12921
+ ticks.reverse();
12922
+ }
12923
+ return ticksFromTimestamps(me, ticks, me._majorUnit);
12924
+ }
12925
+ initOffsets(timestamps) {
12926
+ const me = this;
12927
+ let start = 0;
12928
+ let end = 0;
12929
+ let first, last;
12930
+ if (me.options.offset && timestamps.length) {
12931
+ first = me.getDecimalForValue(timestamps[0]);
12932
+ if (timestamps.length === 1) {
12933
+ start = 1 - first;
12934
+ } else {
12935
+ start = (me.getDecimalForValue(timestamps[1]) - first) / 2;
12936
+ }
12937
+ last = me.getDecimalForValue(timestamps[timestamps.length - 1]);
12938
+ if (timestamps.length === 1) {
12939
+ end = last;
12940
+ } else {
12941
+ end = (last - me.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
12942
+ }
12943
+ }
12944
+ const limit = timestamps.length < 3 ? 0.5 : 0.25;
12945
+ start = _limitValue(start, 0, limit);
12946
+ end = _limitValue(end, 0, limit);
12947
+ me._offsets = {start, end, factor: 1 / (start + 1 + end)};
12948
+ }
12949
+ _generate() {
12950
+ const me = this;
12951
+ const adapter = me._adapter;
12952
+ const min = me.min;
12953
+ const max = me.max;
12954
+ const options = me.options;
12955
+ const timeOpts = options.time;
12956
+ const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, me._getLabelCapacity(min));
12957
+ const stepSize = valueOrDefault(timeOpts.stepSize, 1);
12958
+ const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
12959
+ const hasWeekday = isNumber(weekday) || weekday === true;
12960
+ const ticks = {};
12961
+ let first = min;
12962
+ let time, count;
12963
+ if (hasWeekday) {
12964
+ first = +adapter.startOf(first, 'isoWeek', weekday);
12965
+ }
12966
+ first = +adapter.startOf(first, hasWeekday ? 'day' : minor);
12967
+ if (adapter.diff(max, min, minor) > 100000 * stepSize) {
12968
+ throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
12969
+ }
12970
+ const timestamps = options.ticks.source === 'data' && me.getDataTimestamps();
12971
+ for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) {
12972
+ addTick(ticks, time, timestamps);
12973
+ }
12974
+ if (time === max || options.bounds === 'ticks' || count === 1) {
12975
+ addTick(ticks, time, timestamps);
12976
+ }
12977
+ return Object.keys(ticks).sort((a, b) => a - b).map(x => +x);
12978
+ }
12979
+ getLabelForValue(value) {
12980
+ const me = this;
12981
+ const adapter = me._adapter;
12982
+ const timeOpts = me.options.time;
12983
+ if (timeOpts.tooltipFormat) {
12984
+ return adapter.format(value, timeOpts.tooltipFormat);
12985
+ }
12986
+ return adapter.format(value, timeOpts.displayFormats.datetime);
12987
+ }
12988
+ _tickFormatFunction(time, index, ticks, format) {
12989
+ const me = this;
12990
+ const options = me.options;
12991
+ const formats = options.time.displayFormats;
12992
+ const unit = me._unit;
12993
+ const majorUnit = me._majorUnit;
12994
+ const minorFormat = unit && formats[unit];
12995
+ const majorFormat = majorUnit && formats[majorUnit];
12996
+ const tick = ticks[index];
12997
+ const major = majorUnit && majorFormat && tick && tick.major;
12998
+ const label = me._adapter.format(time, format || (major ? majorFormat : minorFormat));
12999
+ const formatter = options.ticks.callback;
13000
+ return formatter ? callback(formatter, [label, index, ticks], me) : label;
13001
+ }
13002
+ generateTickLabels(ticks) {
13003
+ let i, ilen, tick;
13004
+ for (i = 0, ilen = ticks.length; i < ilen; ++i) {
13005
+ tick = ticks[i];
13006
+ tick.label = this._tickFormatFunction(tick.value, i, ticks);
13007
+ }
13008
+ }
13009
+ getDecimalForValue(value) {
13010
+ const me = this;
13011
+ return value === null ? NaN : (value - me.min) / (me.max - me.min);
13012
+ }
13013
+ getPixelForValue(value) {
13014
+ const me = this;
13015
+ const offsets = me._offsets;
13016
+ const pos = me.getDecimalForValue(value);
13017
+ return me.getPixelForDecimal((offsets.start + pos) * offsets.factor);
13018
+ }
13019
+ getValueForPixel(pixel) {
13020
+ const me = this;
13021
+ const offsets = me._offsets;
13022
+ const pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
13023
+ return me.min + pos * (me.max - me.min);
13024
+ }
13025
+ _getLabelSize(label) {
13026
+ const me = this;
13027
+ const ticksOpts = me.options.ticks;
13028
+ const tickLabelWidth = me.ctx.measureText(label).width;
13029
+ const angle = toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
13030
+ const cosRotation = Math.cos(angle);
13031
+ const sinRotation = Math.sin(angle);
13032
+ const tickFontSize = me._resolveTickFontOptions(0).size;
13033
+ return {
13034
+ w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation),
13035
+ h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation)
13036
+ };
13037
+ }
13038
+ _getLabelCapacity(exampleTime) {
13039
+ const me = this;
13040
+ const timeOpts = me.options.time;
13041
+ const displayFormats = timeOpts.displayFormats;
13042
+ const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
13043
+ const exampleLabel = me._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format);
13044
+ const size = me._getLabelSize(exampleLabel);
13045
+ const capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h) - 1;
13046
+ return capacity > 0 ? capacity : 1;
13047
+ }
13048
+ getDataTimestamps() {
13049
+ const me = this;
13050
+ let timestamps = me._cache.data || [];
13051
+ let i, ilen;
13052
+ if (timestamps.length) {
13053
+ return timestamps;
13054
+ }
13055
+ const metas = me.getMatchingVisibleMetas();
13056
+ if (me._normalized && metas.length) {
13057
+ return (me._cache.data = metas[0].controller.getAllParsedValues(me));
13058
+ }
13059
+ for (i = 0, ilen = metas.length; i < ilen; ++i) {
13060
+ timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(me));
13061
+ }
13062
+ return (me._cache.data = me.normalize(timestamps));
13063
+ }
13064
+ getLabelTimestamps() {
13065
+ const me = this;
13066
+ const timestamps = me._cache.labels || [];
13067
+ let i, ilen;
13068
+ if (timestamps.length) {
13069
+ return timestamps;
13070
+ }
13071
+ const labels = me.getLabels();
13072
+ for (i = 0, ilen = labels.length; i < ilen; ++i) {
13073
+ timestamps.push(parse(me, labels[i]));
13074
+ }
13075
+ return (me._cache.labels = me._normalized ? timestamps : me.normalize(timestamps));
13076
+ }
13077
+ normalize(values) {
13078
+ return _arrayUnique(values.sort(sorter));
13079
+ }
13080
+ }
13081
+ TimeScale.id = 'time';
13082
+ TimeScale.defaults = {
13083
+ bounds: 'data',
13084
+ adapters: {},
13085
+ time: {
13086
+ parser: false,
13087
+ unit: false,
13088
+ round: false,
13089
+ isoWeekday: false,
13090
+ minUnit: 'millisecond',
13091
+ displayFormats: {}
13092
+ },
13093
+ ticks: {
13094
+ source: 'auto',
13095
+ major: {
13096
+ enabled: false
13097
+ }
13098
+ }
13099
+ };
13100
+
13101
+ function interpolate(table, val, reverse) {
13102
+ let lo = 0;
13103
+ let hi = table.length - 1;
13104
+ let prevSource, nextSource, prevTarget, nextTarget;
13105
+ if (reverse) {
13106
+ if (val >= table[lo].pos && val <= table[hi].pos) {
13107
+ ({lo, hi} = _lookupByKey(table, 'pos', val));
13108
+ }
13109
+ ({pos: prevSource, time: prevTarget} = table[lo]);
13110
+ ({pos: nextSource, time: nextTarget} = table[hi]);
13111
+ } else {
13112
+ if (val >= table[lo].time && val <= table[hi].time) {
13113
+ ({lo, hi} = _lookupByKey(table, 'time', val));
13114
+ }
13115
+ ({time: prevSource, pos: prevTarget} = table[lo]);
13116
+ ({time: nextSource, pos: nextTarget} = table[hi]);
13117
+ }
13118
+ const span = nextSource - prevSource;
13119
+ return span ? prevTarget + (nextTarget - prevTarget) * (val - prevSource) / span : prevTarget;
13120
+ }
13121
+ class TimeSeriesScale extends TimeScale {
13122
+ constructor(props) {
13123
+ super(props);
13124
+ this._table = [];
13125
+ this._minPos = undefined;
13126
+ this._tableRange = undefined;
13127
+ }
13128
+ initOffsets() {
13129
+ const me = this;
13130
+ const timestamps = me._getTimestampsForTable();
13131
+ const table = me._table = me.buildLookupTable(timestamps);
13132
+ me._minPos = interpolate(table, me.min);
13133
+ me._tableRange = interpolate(table, me.max) - me._minPos;
13134
+ super.initOffsets(timestamps);
13135
+ }
13136
+ buildLookupTable(timestamps) {
13137
+ const {min, max} = this;
13138
+ const items = [];
13139
+ const table = [];
13140
+ let i, ilen, prev, curr, next;
13141
+ for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
13142
+ curr = timestamps[i];
13143
+ if (curr >= min && curr <= max) {
13144
+ items.push(curr);
13145
+ }
13146
+ }
13147
+ if (items.length < 2) {
13148
+ return [
13149
+ {time: min, pos: 0},
13150
+ {time: max, pos: 1}
13151
+ ];
13152
+ }
13153
+ for (i = 0, ilen = items.length; i < ilen; ++i) {
13154
+ next = items[i + 1];
13155
+ prev = items[i - 1];
13156
+ curr = items[i];
13157
+ if (Math.round((next + prev) / 2) !== curr) {
13158
+ table.push({time: curr, pos: i / (ilen - 1)});
13159
+ }
13160
+ }
13161
+ return table;
13162
+ }
13163
+ _getTimestampsForTable() {
13164
+ const me = this;
13165
+ let timestamps = me._cache.all || [];
13166
+ if (timestamps.length) {
13167
+ return timestamps;
13168
+ }
13169
+ const data = me.getDataTimestamps();
13170
+ const label = me.getLabelTimestamps();
13171
+ if (data.length && label.length) {
13172
+ timestamps = me.normalize(data.concat(label));
13173
+ } else {
13174
+ timestamps = data.length ? data : label;
13175
+ }
13176
+ timestamps = me._cache.all = timestamps;
13177
+ return timestamps;
13178
+ }
13179
+ getDecimalForValue(value) {
13180
+ return (interpolate(this._table, value) - this._minPos) / this._tableRange;
13181
+ }
13182
+ getValueForPixel(pixel) {
13183
+ const me = this;
13184
+ const offsets = me._offsets;
13185
+ const decimal = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
13186
+ return interpolate(me._table, decimal * me._tableRange + me._minPos, true);
13187
+ }
13188
+ }
13189
+ TimeSeriesScale.id = 'timeseries';
13190
+ TimeSeriesScale.defaults = TimeScale.defaults;
13191
+
13192
+ var scales = /*#__PURE__*/Object.freeze({
13193
+ __proto__: null,
13194
+ CategoryScale: CategoryScale,
13195
+ LinearScale: LinearScale,
13196
+ LogarithmicScale: LogarithmicScale,
13197
+ RadialLinearScale: RadialLinearScale,
13198
+ TimeScale: TimeScale,
13199
+ TimeSeriesScale: TimeSeriesScale
13200
+ });
13201
+
13202
+ Chart.register(controllers, scales, elements, plugins);
13203
+ Chart.helpers = {...helpers};
13204
+ Chart._adapters = _adapters;
13205
+ Chart.Animation = Animation;
13206
+ Chart.Animations = Animations;
13207
+ Chart.animator = animator;
13208
+ Chart.controllers = registry.controllers.items;
13209
+ Chart.DatasetController = DatasetController;
13210
+ Chart.Element = Element;
13211
+ Chart.elements = elements;
13212
+ Chart.Interaction = Interaction;
13213
+ Chart.layouts = layouts;
13214
+ Chart.platforms = platforms;
13215
+ Chart.Scale = Scale;
13216
+ Chart.Ticks = Ticks;
13217
+ Object.assign(Chart, controllers, scales, elements, plugins, platforms);
13218
+ Chart.Chart = Chart;
13219
+ if (typeof window !== 'undefined') {
13220
+ window.Chart = Chart;
13221
+ }
13222
+
13223
+ return Chart;
13224
+
13225
+ })));
admin/js/chartjs-plugin-datalabels.js ADDED
@@ -0,0 +1,1358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * chartjs-plugin-datalabels v2.0.0
3
+ * https://chartjs-plugin-datalabels.netlify.app
4
+ * (c) 2017-2021 chartjs-plugin-datalabels contributors
5
+ * Released under the MIT license
6
+ */
7
+ (function (global, factory) {
8
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js/helpers'), require('chart.js')) :
9
+ typeof define === 'function' && define.amd ? define(['chart.js/helpers', 'chart.js'], factory) :
10
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ChartDataLabels = factory(global.Chart.helpers, global.Chart));
11
+ }(this, (function (helpers, chart_js) { 'use strict';
12
+
13
+ var devicePixelRatio = (function() {
14
+ if (typeof window !== 'undefined') {
15
+ if (window.devicePixelRatio) {
16
+ return window.devicePixelRatio;
17
+ }
18
+
19
+ // devicePixelRatio is undefined on IE10
20
+ // https://stackoverflow.com/a/20204180/8837887
21
+ // https://github.com/chartjs/chartjs-plugin-datalabels/issues/85
22
+ var screen = window.screen;
23
+ if (screen) {
24
+ return (screen.deviceXDPI || 1) / (screen.logicalXDPI || 1);
25
+ }
26
+ }
27
+
28
+ return 1;
29
+ }());
30
+
31
+ var utils = {
32
+ // @todo move this in Chart.helpers.toTextLines
33
+ toTextLines: function(inputs) {
34
+ var lines = [];
35
+ var input;
36
+
37
+ inputs = [].concat(inputs);
38
+ while (inputs.length) {
39
+ input = inputs.pop();
40
+ if (typeof input === 'string') {
41
+ lines.unshift.apply(lines, input.split('\n'));
42
+ } else if (Array.isArray(input)) {
43
+ inputs.push.apply(inputs, input);
44
+ } else if (!helpers.isNullOrUndef(inputs)) {
45
+ lines.unshift('' + input);
46
+ }
47
+ }
48
+
49
+ return lines;
50
+ },
51
+
52
+ // @todo move this in Chart.helpers.canvas.textSize
53
+ // @todo cache calls of measureText if font doesn't change?!
54
+ textSize: function(ctx, lines, font) {
55
+ var items = [].concat(lines);
56
+ var ilen = items.length;
57
+ var prev = ctx.font;
58
+ var width = 0;
59
+ var i;
60
+
61
+ ctx.font = font.string;
62
+
63
+ for (i = 0; i < ilen; ++i) {
64
+ width = Math.max(ctx.measureText(items[i]).width, width);
65
+ }
66
+
67
+ ctx.font = prev;
68
+
69
+ return {
70
+ height: ilen * font.lineHeight,
71
+ width: width
72
+ };
73
+ },
74
+
75
+ /**
76
+ * Returns value bounded by min and max. This is equivalent to max(min, min(value, max)).
77
+ * @todo move this method in Chart.helpers.bound
78
+ * https://doc.qt.io/qt-5/qtglobal.html#qBound
79
+ */
80
+ bound: function(min, value, max) {
81
+ return Math.max(min, Math.min(value, max));
82
+ },
83
+
84
+ /**
85
+ * Returns an array of pair [value, state] where state is:
86
+ * * -1: value is only in a0 (removed)
87
+ * * 1: value is only in a1 (added)
88
+ */
89
+ arrayDiff: function(a0, a1) {
90
+ var prev = a0.slice();
91
+ var updates = [];
92
+ var i, j, ilen, v;
93
+
94
+ for (i = 0, ilen = a1.length; i < ilen; ++i) {
95
+ v = a1[i];
96
+ j = prev.indexOf(v);
97
+
98
+ if (j === -1) {
99
+ updates.push([v, 1]);
100
+ } else {
101
+ prev.splice(j, 1);
102
+ }
103
+ }
104
+
105
+ for (i = 0, ilen = prev.length; i < ilen; ++i) {
106
+ updates.push([prev[i], -1]);
107
+ }
108
+
109
+ return updates;
110
+ },
111
+
112
+ /**
113
+ * https://github.com/chartjs/chartjs-plugin-datalabels/issues/70
114
+ */
115
+ rasterize: function(v) {
116
+ return Math.round(v * devicePixelRatio) / devicePixelRatio;
117
+ }
118
+ };
119
+
120
+ function orient(point, origin) {
121
+ var x0 = origin.x;
122
+ var y0 = origin.y;
123
+
124
+ if (x0 === null) {
125
+ return {x: 0, y: -1};
126
+ }
127
+ if (y0 === null) {
128
+ return {x: 1, y: 0};
129
+ }
130
+
131
+ var dx = point.x - x0;
132
+ var dy = point.y - y0;
133
+ var ln = Math.sqrt(dx * dx + dy * dy);
134
+
135
+ return {
136
+ x: ln ? dx / ln : 0,
137
+ y: ln ? dy / ln : -1
138
+ };
139
+ }
140
+
141
+ function aligned(x, y, vx, vy, align) {
142
+ switch (align) {
143
+ case 'center':
144
+ vx = vy = 0;
145
+ break;
146
+ case 'bottom':
147
+ vx = 0;
148
+ vy = 1;
149
+ break;
150
+ case 'right':
151
+ vx = 1;
152
+ vy = 0;
153
+ break;
154
+ case 'left':
155
+ vx = -1;
156
+ vy = 0;
157
+ break;
158
+ case 'top':
159
+ vx = 0;
160
+ vy = -1;
161
+ break;
162
+ case 'start':
163
+ vx = -vx;
164
+ vy = -vy;
165
+ break;
166
+ case 'end':
167
+ // keep natural orientation
168
+ break;
169
+ default:
170
+ // clockwise rotation (in degree)
171
+ align *= (Math.PI / 180);
172
+ vx = Math.cos(align);
173
+ vy = Math.sin(align);
174
+ break;
175
+ }
176
+
177
+ return {
178
+ x: x,
179
+ y: y,
180
+ vx: vx,
181
+ vy: vy
182
+ };
183
+ }
184
+
185
+ // Line clipping (Cohen–Sutherland algorithm)
186
+ // https://en.wikipedia.org/wiki/Cohen–Sutherland_algorithm
187
+
188
+ var R_INSIDE = 0;
189
+ var R_LEFT = 1;
190
+ var R_RIGHT = 2;
191
+ var R_BOTTOM = 4;
192
+ var R_TOP = 8;
193
+
194
+ function region(x, y, rect) {
195
+ var res = R_INSIDE;
196
+
197
+ if (x < rect.left) {
198
+ res |= R_LEFT;
199
+ } else if (x > rect.right) {
200
+ res |= R_RIGHT;
201
+ }
202
+ if (y < rect.top) {
203
+ res |= R_TOP;
204
+ } else if (y > rect.bottom) {
205
+ res |= R_BOTTOM;
206
+ }
207
+
208
+ return res;
209
+ }
210
+
211
+ function clipped(segment, area) {
212
+ var x0 = segment.x0;
213
+ var y0 = segment.y0;
214
+ var x1 = segment.x1;
215
+ var y1 = segment.y1;
216
+ var r0 = region(x0, y0, area);
217
+ var r1 = region(x1, y1, area);
218
+ var r, x, y;
219
+
220
+ // eslint-disable-next-line no-constant-condition
221
+ while (true) {
222
+ if (!(r0 | r1) || (r0 & r1)) {
223
+ // both points inside or on the same side: no clipping
224
+ break;
225
+ }
226
+
227
+ // at least one point is outside
228
+ r = r0 || r1;
229
+
230
+ if (r & R_TOP) {
231
+ x = x0 + (x1 - x0) * (area.top - y0) / (y1 - y0);
232
+ y = area.top;
233
+ } else if (r & R_BOTTOM) {
234
+ x = x0 + (x1 - x0) * (area.bottom - y0) / (y1 - y0);
235
+ y = area.bottom;
236
+ } else if (r & R_RIGHT) {
237
+ y = y0 + (y1 - y0) * (area.right - x0) / (x1 - x0);
238
+ x = area.right;
239
+ } else if (r & R_LEFT) {
240
+ y = y0 + (y1 - y0) * (area.left - x0) / (x1 - x0);
241
+ x = area.left;
242
+ }
243
+
244
+ if (r === r0) {
245
+ x0 = x;
246
+ y0 = y;
247
+ r0 = region(x0, y0, area);
248
+ } else {
249
+ x1 = x;
250
+ y1 = y;
251
+ r1 = region(x1, y1, area);
252
+ }
253
+ }
254
+
255
+ return {
256
+ x0: x0,
257
+ x1: x1,
258
+ y0: y0,
259
+ y1: y1
260
+ };
261
+ }
262
+
263
+ function compute$1(range, config) {
264
+ var anchor = config.anchor;
265
+ var segment = range;
266
+ var x, y;
267
+
268
+ if (config.clamp) {
269
+ segment = clipped(segment, config.area);
270
+ }
271
+
272
+ if (anchor === 'start') {
273
+ x = segment.x0;
274
+ y = segment.y0;
275
+ } else if (anchor === 'end') {
276
+ x = segment.x1;
277
+ y = segment.y1;
278
+ } else {
279
+ x = (segment.x0 + segment.x1) / 2;
280
+ y = (segment.y0 + segment.y1) / 2;
281
+ }
282
+
283
+ return aligned(x, y, range.vx, range.vy, config.align);
284
+ }
285
+
286
+ var positioners = {
287
+ arc: function(el, config) {
288
+ var angle = (el.startAngle + el.endAngle) / 2;
289
+ var vx = Math.cos(angle);
290
+ var vy = Math.sin(angle);
291
+ var r0 = el.innerRadius;
292
+ var r1 = el.outerRadius;
293
+
294
+ return compute$1({
295
+ x0: el.x + vx * r0,
296
+ y0: el.y + vy * r0,
297
+ x1: el.x + vx * r1,
298
+ y1: el.y + vy * r1,
299
+ vx: vx,
300
+ vy: vy
301
+ }, config);
302
+ },
303
+
304
+ point: function(el, config) {
305
+ var v = orient(el, config.origin);
306
+ var rx = v.x * el.options.radius;
307
+ var ry = v.y * el.options.radius;
308
+
309
+ return compute$1({
310
+ x0: el.x - rx,
311
+ y0: el.y - ry,
312
+ x1: el.x + rx,
313
+ y1: el.y + ry,
314
+ vx: v.x,
315
+ vy: v.y
316
+ }, config);
317
+ },
318
+
319
+ bar: function(el, config) {
320
+ var v = orient(el, config.origin);
321
+ var x = el.x;
322
+ var y = el.y;
323
+ var sx = 0;
324
+ var sy = 0;
325
+
326
+ if (el.horizontal) {
327
+ x = Math.min(el.x, el.base);
328
+ sx = Math.abs(el.base - el.x);
329
+ } else {
330
+ y = Math.min(el.y, el.base);
331
+ sy = Math.abs(el.base - el.y);
332
+ }
333
+
334
+ return compute$1({
335
+ x0: x,
336
+ y0: y + sy,
337
+ x1: x + sx,
338
+ y1: y,
339
+ vx: v.x,
340
+ vy: v.y
341
+ }, config);
342
+ },
343
+
344
+ fallback: function(el, config) {
345
+ var v = orient(el, config.origin);
346
+
347
+ return compute$1({
348
+ x0: el.x,
349
+ y0: el.y,
350
+ x1: el.x,
351
+ y1: el.y,
352
+ vx: v.x,
353
+ vy: v.y
354
+ }, config);
355
+ }
356
+ };
357
+
358
+ var rasterize = utils.rasterize;
359
+
360
+ function boundingRects(model) {
361
+ var borderWidth = model.borderWidth || 0;
362
+ var padding = model.padding;
363
+ var th = model.size.height;
364
+ var tw = model.size.width;
365
+ var tx = -tw / 2;
366
+ var ty = -th / 2;
367
+
368
+ return {
369
+ frame: {
370
+ x: tx - padding.left - borderWidth,
371
+ y: ty - padding.top - borderWidth,
372
+ w: tw + padding.width + borderWidth * 2,
373
+ h: th + padding.height + borderWidth * 2
374
+ },
375
+ text: {
376
+ x: tx,
377
+ y: ty,
378
+ w: tw,
379
+ h: th
380
+ }
381
+ };
382
+ }
383
+
384
+ function getScaleOrigin(el, context) {
385
+ var scale = context.chart.getDatasetMeta(context.datasetIndex).vScale;
386
+
387
+ if (!scale) {
388
+ return null;
389
+ }
390
+
391
+ if (scale.xCenter !== undefined && scale.yCenter !== undefined) {
392
+ return {x: scale.xCenter, y: scale.yCenter};
393
+ }
394
+
395
+ var pixel = scale.getBasePixel();
396
+ return el.horizontal ?
397
+ {x: pixel, y: null} :
398
+ {x: null, y: pixel};
399
+ }
400
+
401
+ function getPositioner(el) {
402
+ if (el instanceof chart_js.ArcElement) {
403
+ return positioners.arc;
404
+ }
405
+ if (el instanceof chart_js.PointElement) {
406
+ return positioners.point;
407
+ }
408
+ if (el instanceof chart_js.BarElement) {
409
+ return positioners.bar;
410
+ }
411
+ return positioners.fallback;
412
+ }
413
+
414
+ function drawRoundedRect(ctx, x, y, w, h, radius) {
415
+ var HALF_PI = Math.PI / 2;
416
+
417
+ if (radius) {
418
+ var r = Math.min(radius, h / 2, w / 2);
419
+ var left = x + r;
420
+ var top = y + r;
421
+ var right = x + w - r;
422
+ var bottom = y + h - r;
423
+
424
+ ctx.moveTo(x, top);
425
+ if (left < right && top < bottom) {
426
+ ctx.arc(left, top, r, -Math.PI, -HALF_PI);
427
+ ctx.arc(right, top, r, -HALF_PI, 0);
428
+ ctx.arc(right, bottom, r, 0, HALF_PI);
429
+ ctx.arc(left, bottom, r, HALF_PI, Math.PI);
430
+ } else if (left < right) {
431
+ ctx.moveTo(left, y);
432
+ ctx.arc(right, top, r, -HALF_PI, HALF_PI);
433
+ ctx.arc(left, top, r, HALF_PI, Math.PI + HALF_PI);
434
+ } else if (top < bottom) {
435
+ ctx.arc(left, top, r, -Math.PI, 0);
436
+ ctx.arc(left, bottom, r, 0, Math.PI);
437
+ } else {
438
+ ctx.arc(left, top, r, -Math.PI, Math.PI);
439
+ }
440
+ ctx.closePath();
441
+ ctx.moveTo(x, y);
442
+ } else {
443
+ ctx.rect(x, y, w, h);
444
+ }
445
+ }
446
+
447
+ function drawFrame(ctx, rect, model) {
448
+ var bgColor = model.backgroundColor;
449
+ var borderColor = model.borderColor;
450
+ var borderWidth = model.borderWidth;
451
+
452
+ if (!bgColor && (!borderColor || !borderWidth)) {
453
+ return;
454
+ }
455
+
456
+ ctx.beginPath();
457
+
458
+ drawRoundedRect(
459
+ ctx,
460
+ rasterize(rect.x) + borderWidth / 2,
461
+ rasterize(rect.y) + borderWidth / 2,
462
+ rasterize(rect.w) - borderWidth,
463
+ rasterize(rect.h) - borderWidth,
464
+ model.borderRadius);
465
+
466
+ ctx.closePath();
467
+
468
+ if (bgColor) {
469
+ ctx.fillStyle = bgColor;
470
+ ctx.fill();
471
+ }
472
+
473
+ if (borderColor && borderWidth) {
474
+ ctx.strokeStyle = borderColor;
475
+ ctx.lineWidth = borderWidth;
476
+ ctx.lineJoin = 'miter';
477
+ ctx.stroke();
478
+ }
479
+ }
480
+
481
+ function textGeometry(rect, align, font) {
482
+ var h = font.lineHeight;
483
+ var w = rect.w;
484
+ var x = rect.x;
485
+ var y = rect.y + h / 2;
486
+
487
+ if (align === 'center') {
488
+ x += w / 2;
489
+ } else if (align === 'end' || align === 'right') {
490
+ x += w;
491
+ }
492
+
493
+ return {
494
+ h: h,
495
+ w: w,
496
+ x: x,
497
+ y: y
498
+ };
499
+ }
500
+
501
+ function drawTextLine(ctx, text, cfg) {
502
+ var shadow = ctx.shadowBlur;
503
+ var stroked = cfg.stroked;
504
+ var x = rasterize(cfg.x);
505
+ var y = rasterize(cfg.y);
506
+ var w = rasterize(cfg.w);
507
+
508
+ if (stroked) {
509
+ ctx.strokeText(text, x, y, w);
510
+ }
511
+
512
+ if (cfg.filled) {
513
+ if (shadow && stroked) {
514
+ // Prevent drawing shadow on both the text stroke and fill, so
515
+ // if the text is stroked, remove the shadow for the text fill.
516
+ ctx.shadowBlur = 0;
517
+ }
518
+
519
+ ctx.fillText(text, x, y, w);
520
+
521
+ if (shadow && stroked) {
522
+ ctx.shadowBlur = shadow;
523
+ }
524
+ }
525
+ }
526
+
527
+ function drawText(ctx, lines, rect, model) {
528
+ var align = model.textAlign;
529
+ var color = model.color;
530
+ var filled = !!color;
531
+ var font = model.font;
532
+ var ilen = lines.length;
533
+ var strokeColor = model.textStrokeColor;
534
+ var strokeWidth = model.textStrokeWidth;
535
+ var stroked = strokeColor && strokeWidth;
536
+ var i;
537
+
538
+ if (!ilen || (!filled && !stroked)) {
539
+ return;
540
+ }
541
+
542
+ // Adjust coordinates based on text alignment and line height
543
+ rect = textGeometry(rect, align, font);
544
+
545
+ ctx.font = font.string;
546
+ ctx.textAlign = align;
547
+ ctx.textBaseline = 'middle';
548
+ ctx.shadowBlur = model.textShadowBlur;
549
+ ctx.shadowColor = model.textShadowColor;
550
+
551
+ if (filled) {
552
+ ctx.fillStyle = color;
553
+ }
554
+ if (stroked) {
555
+ ctx.lineJoin = 'round';
556
+ ctx.lineWidth = strokeWidth;
557
+ ctx.strokeStyle = strokeColor;
558
+ }
559
+
560
+ for (i = 0, ilen = lines.length; i < ilen; ++i) {
561
+ drawTextLine(ctx, lines[i], {
562
+ stroked: stroked,
563
+ filled: filled,
564
+ w: rect.w,
565
+ x: rect.x,
566
+ y: rect.y + rect.h * i
567
+ });
568
+ }
569
+ }
570
+
571
+ var Label = function(config, ctx, el, index) {
572
+ var me = this;
573
+
574
+ me._config = config;
575
+ me._index = index;
576
+ me._model = null;
577
+ me._rects = null;
578
+ me._ctx = ctx;
579
+ me._el = el;
580
+ };
581
+
582
+ helpers.merge(Label.prototype, {
583
+ /**
584
+ * @private
585
+ */
586
+ _modelize: function(display, lines, config, context) {
587
+ var me = this;
588
+ var index = me._index;
589
+ var font = helpers.toFont(helpers.resolve([config.font, {}], context, index));
590
+ var color = helpers.resolve([config.color, chart_js.defaults.color], context, index);
591
+
592
+ return {
593
+ align: helpers.resolve([config.align, 'center'], context, index),
594
+ anchor: helpers.resolve([config.anchor, 'center'], context, index),
595
+ area: context.chart.chartArea,
596
+ backgroundColor: helpers.resolve([config.backgroundColor, null], context, index),
597
+ borderColor: helpers.resolve([config.borderColor, null], context, index),
598
+ borderRadius: helpers.resolve([config.borderRadius, 0], context, index),
599
+ borderWidth: helpers.resolve([config.borderWidth, 0], context, index),
600
+ clamp: helpers.resolve([config.clamp, false], context, index),
601
+ clip: helpers.resolve([config.clip, false], context, index),
602
+ color: color,
603
+ display: display,
604
+ font: font,
605
+ lines: lines,
606
+ offset: helpers.resolve([config.offset, 0], context, index),
607
+ opacity: helpers.resolve([config.opacity, 1], context, index),
608
+ origin: getScaleOrigin(me._el, context),
609
+ padding: helpers.toPadding(helpers.resolve([config.padding, 0], context, index)),
610
+ positioner: getPositioner(me._el),
611
+ rotation: helpers.resolve([config.rotation, 0], context, index) * (Math.PI / 180),
612
+ size: utils.textSize(me._ctx, lines, font),
613
+ textAlign: helpers.resolve([config.textAlign, 'start'], context, index),
614
+ textShadowBlur: helpers.resolve([config.textShadowBlur, 0], context, index),
615
+ textShadowColor: helpers.resolve([config.textShadowColor, color], context, index),
616
+ textStrokeColor: helpers.resolve([config.textStrokeColor, color], context, index),
617
+ textStrokeWidth: helpers.resolve([config.textStrokeWidth, 0], context, index)
618
+ };
619
+ },
620
+
621
+ update: function(context) {
622
+ var me = this;
623
+ var model = null;
624
+ var rects = null;
625
+ var index = me._index;
626
+ var config = me._config;
627
+ var value, label, lines;
628
+
629
+ // We first resolve the display option (separately) to avoid computing
630
+ // other options in case the label is hidden (i.e. display: false).
631
+ var display = helpers.resolve([config.display, true], context, index);
632
+
633
+ if (display) {
634
+ value = context.dataset.data[index];
635
+ label = helpers.valueOrDefault(helpers.callback(config.formatter, [value, context]), value);
636
+ lines = helpers.isNullOrUndef(label) ? [] : utils.toTextLines(label);
637
+
638
+ if (lines.length) {
639
+ model = me._modelize(display, lines, config, context);
640
+ rects = boundingRects(model);
641
+ }
642
+ }
643
+
644
+ me._model = model;
645
+ me._rects = rects;
646
+ },
647
+
648
+ geometry: function() {
649
+ return this._rects ? this._rects.frame : {};
650
+ },
651
+
652
+ rotation: function() {
653
+ return this._model ? this._model.rotation : 0;
654
+ },
655
+
656
+ visible: function() {
657
+ return this._model && this._model.opacity;
658
+ },
659
+
660
+ model: function() {
661
+ return this._model;
662
+ },
663
+
664
+ draw: function(chart, center) {
665
+ var me = this;
666
+ var ctx = chart.ctx;
667
+ var model = me._model;
668
+ var rects = me._rects;
669
+ var area;
670
+
671
+ if (!this.visible()) {
672
+ return;
673
+ }
674
+
675
+ ctx.save();
676
+
677
+ if (model.clip) {
678
+ area = model.area;
679
+ ctx.beginPath();
680
+ ctx.rect(
681
+ area.left,
682
+ area.top,
683
+ area.right - area.left,
684
+ area.bottom - area.top);
685
+ ctx.clip();
686
+ }
687
+
688
+ ctx.globalAlpha = utils.bound(0, model.opacity, 1);
689
+ ctx.translate(rasterize(center.x), rasterize(center.y));
690
+ ctx.rotate(model.rotation);
691
+
692
+ drawFrame(ctx, rects.frame, model);
693
+ drawText(ctx, model.lines, rects.text, model);
694
+
695
+ ctx.restore();
696
+ }
697
+ });
698
+
699
+ var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991; // eslint-disable-line es/no-number-minsafeinteger
700
+ var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; // eslint-disable-line es/no-number-maxsafeinteger
701
+
702
+ function rotated(point, center, angle) {
703
+ var cos = Math.cos(angle);
704
+ var sin = Math.sin(angle);
705
+ var cx = center.x;
706
+ var cy = center.y;
707
+
708
+ return {
709
+ x: cx + cos * (point.x - cx) - sin * (point.y - cy),
710
+ y: cy + sin * (point.x - cx) + cos * (point.y - cy)
711
+ };
712
+ }
713
+
714
+ function projected(points, axis) {
715
+ var min = MAX_INTEGER;
716
+ var max = MIN_INTEGER;
717
+ var origin = axis.origin;
718
+ var i, pt, vx, vy, dp;
719
+
720
+ for (i = 0; i < points.length; ++i) {
721
+ pt = points[i];
722
+ vx = pt.x - origin.x;
723
+ vy = pt.y - origin.y;
724
+ dp = axis.vx * vx + axis.vy * vy;
725
+ min = Math.min(min, dp);
726
+ max = Math.max(max, dp);
727
+ }
728
+
729
+ return {
730
+ min: min,
731
+ max: max
732
+ };
733
+ }
734
+
735
+ function toAxis(p0, p1) {
736
+ var vx = p1.x - p0.x;
737
+ var vy = p1.y - p0.y;
738
+ var ln = Math.sqrt(vx * vx + vy * vy);
739
+
740
+ return {
741
+ vx: (p1.x - p0.x) / ln,
742
+ vy: (p1.y - p0.y) / ln,
743
+ origin: p0,
744
+ ln: ln
745
+ };
746
+ }
747
+
748
+ var HitBox = function() {
749
+ this._rotation = 0;
750
+ this._rect = {
751
+ x: 0,
752
+ y: 0,
753
+ w: 0,
754
+ h: 0
755
+ };
756
+ };
757
+
758
+ helpers.merge(HitBox.prototype, {
759
+ center: function() {
760
+ var r = this._rect;
761
+ return {
762
+ x: r.x + r.w / 2,
763
+ y: r.y + r.h / 2
764
+ };
765
+ },
766
+
767
+ update: function(center, rect, rotation) {
768
+ this._rotation = rotation;
769
+ this._rect = {
770
+ x: rect.x + center.x,
771
+ y: rect.y + center.y,
772
+ w: rect.w,
773
+ h: rect.h
774
+ };
775
+ },
776
+
777
+ contains: function(point) {
778
+ var me = this;
779
+ var margin = 1;
780
+ var rect = me._rect;
781
+
782
+ point = rotated(point, me.center(), -me._rotation);
783
+
784
+ return !(point.x < rect.x - margin
785
+ || point.y < rect.y - margin
786
+ || point.x > rect.x + rect.w + margin * 2
787
+ || point.y > rect.y + rect.h + margin * 2);
788
+ },
789
+
790
+ // Separating Axis Theorem
791
+ // https://gamedevelopment.tutsplus.com/tutorials/collision-detection-using-the-separating-axis-theorem--gamedev-169
792
+ intersects: function(other) {
793
+ var r0 = this._points();
794
+ var r1 = other._points();
795
+ var axes = [
796
+ toAxis(r0[0], r0[1]),
797
+ toAxis(r0[0], r0[3])
798
+ ];
799
+ var i, pr0, pr1;
800
+
801
+ if (this._rotation !== other._rotation) {
802
+ // Only separate with r1 axis if the rotation is different,
803
+ // else it's enough to separate r0 and r1 with r0 axis only!
804
+ axes.push(
805
+ toAxis(r1[0], r1[1]),
806
+ toAxis(r1[0], r1[3])
807
+ );
808
+ }
809
+
810
+ for (i = 0; i < axes.length; ++i) {
811
+ pr0 = projected(r0, axes[i]);
812
+ pr1 = projected(r1, axes[i]);
813
+
814
+ if (pr0.max < pr1.min || pr1.max < pr0.min) {
815
+ return false;
816
+ }
817
+ }
818
+
819
+ return true;
820
+ },
821
+
822
+ /**
823
+ * @private
824
+ */
825
+ _points: function() {
826
+ var me = this;
827
+ var rect = me._rect;
828
+ var angle = me._rotation;
829
+ var center = me.center();
830
+
831
+ return [
832
+ rotated({x: rect.x, y: rect.y}, center, angle),
833
+ rotated({x: rect.x + rect.w, y: rect.y}, center, angle),
834
+ rotated({x: rect.x + rect.w, y: rect.y + rect.h}, center, angle),
835
+ rotated({x: rect.x, y: rect.y + rect.h}, center, angle)
836
+ ];
837
+ }
838
+ });
839
+
840
+ function coordinates(el, model, geometry) {
841
+ var point = model.positioner(el, model);
842
+ var vx = point.vx;
843
+ var vy = point.vy;
844
+
845
+ if (!vx && !vy) {
846
+ // if aligned center, we don't want to offset the center point
847
+ return {x: point.x, y: point.y};
848
+ }
849
+
850
+ var w = geometry.w;
851
+ var h = geometry.h;
852
+
853
+ // take in account the label rotation
854
+ var rotation = model.rotation;
855
+ var dx = Math.abs(w / 2 * Math.cos(rotation)) + Math.abs(h / 2 * Math.sin(rotation));
856
+ var dy = Math.abs(w / 2 * Math.sin(rotation)) + Math.abs(h / 2 * Math.cos(rotation));
857
+
858
+ // scale the unit vector (vx, vy) to get at least dx or dy equal to
859
+ // w or h respectively (else we would calculate the distance to the
860
+ // ellipse inscribed in the bounding rect)
861
+ var vs = 1 / Math.max(Math.abs(vx), Math.abs(vy));
862
+ dx *= vx * vs;
863
+ dy *= vy * vs;
864
+
865
+ // finally, include the explicit offset
866
+ dx += model.offset * vx;
867
+ dy += model.offset * vy;
868
+
869
+ return {
870
+ x: point.x + dx,
871
+ y: point.y + dy
872
+ };
873
+ }
874
+
875
+ function collide(labels, collider) {
876
+ var i, j, s0, s1;
877
+
878
+ // IMPORTANT Iterate in the reverse order since items at the end of the
879
+ // list have an higher weight/priority and thus should be less impacted
880
+ // by the overlapping strategy.
881
+
882
+ for (i = labels.length - 1; i >= 0; --i) {
883
+ s0 = labels[i].$layout;
884
+
885
+ for (j = i - 1; j >= 0 && s0._visible; --j) {
886
+ s1 = labels[j].$layout;
887
+
888
+ if (s1._visible && s0._box.intersects(s1._box)) {
889
+ collider(s0, s1);
890
+ }
891
+ }
892
+ }
893
+
894
+ return labels;
895
+ }
896
+
897
+ function compute(labels) {
898
+ var i, ilen, label, state, geometry, center, proxy;
899
+
900
+ // Initialize labels for overlap detection
901
+ for (i = 0, ilen = labels.length; i < ilen; ++i) {
902
+ label = labels[i];
903
+ state = label.$layout;
904
+
905
+ if (state._visible) {
906
+ // Chart.js 3 removed el._model in favor of getProps(), making harder to
907
+ // abstract reading values in positioners. Also, using string arrays to
908
+ // read values (i.e. var {a,b,c} = el.getProps(["a","b","c"])) would make
909
+ // positioners inefficient in the normal case (i.e. not the final values)
910
+ // and the code a bit ugly, so let's use a Proxy instead.
911
+ proxy = new Proxy(label._el, {get: (el, p) => el.getProps([p], true)[p]});
912
+
913
+ geometry = label.geometry();
914
+ center = coordinates(proxy, label.model(), geometry);
915
+ state._box.update(center, geometry, label.rotation());
916
+ }
917
+ }
918
+
919
+ // Auto hide overlapping labels
920
+ return collide(labels, function(s0, s1) {
921
+ var h0 = s0._hidable;
922
+ var h1 = s1._hidable;
923
+
924
+ if ((h0 && h1) || h1) {
925
+ s1._visible = false;
926
+ } else if (h0) {
927
+ s0._visible = false;
928
+ }
929
+ });
930
+ }
931
+
932
+ var layout = {
933
+ prepare: function(datasets) {
934
+ var labels = [];
935
+ var i, j, ilen, jlen, label;
936
+
937
+ for (i = 0, ilen = datasets.length; i < ilen; ++i) {
938
+ for (j = 0, jlen = datasets[i].length; j < jlen; ++j) {
939
+ label = datasets[i][j];
940
+ labels.push(label);
941
+ label.$layout = {
942
+ _box: new HitBox(),
943
+ _hidable: false,
944
+ _visible: true,
945
+ _set: i,
946
+ _idx: j
947
+ };
948
+ }
949
+ }
950
+
951
+ // TODO New `z` option: labels with a higher z-index are drawn
952
+ // of top of the ones with a lower index. Lowest z-index labels
953
+ // are also discarded first when hiding overlapping labels.
954
+ labels.sort(function(a, b) {
955
+ var sa = a.$layout;
956
+ var sb = b.$layout;
957
+
958
+ return sa._idx === sb._idx
959
+ ? sb._set - sa._set
960
+ : sb._idx - sa._idx;
961
+ });
962
+
963
+ this.update(labels);
964
+
965
+ return labels;
966
+ },
967
+
968
+ update: function(labels) {
969
+ var dirty = false;
970
+ var i, ilen, label, model, state;
971
+
972
+ for (i = 0, ilen = labels.length; i < ilen; ++i) {
973
+ label = labels[i];
974
+ model = label.model();
975
+ state = label.$layout;
976
+ state._hidable = model && model.display === 'auto';
977
+ state._visible = label.visible();
978
+ dirty |= state._hidable;
979
+ }
980
+
981
+ if (dirty) {
982
+ compute(labels);
983
+ }
984
+ },
985
+
986
+ lookup: function(labels, point) {
987
+ var i, state;
988
+
989
+ // IMPORTANT Iterate in the reverse order since items at the end of
990
+ // the list have an higher z-index, thus should be picked first.
991
+
992
+ for (i = labels.length - 1; i >= 0; --i) {
993
+ state = labels[i].$layout;
994
+
995
+ if (state && state._visible && state._box.contains(point)) {
996
+ return labels[i];
997
+ }
998
+ }
999
+
1000
+ return null;
1001
+ },
1002
+
1003
+ draw: function(chart, labels) {
1004
+ var i, ilen, label, state, geometry, center;
1005
+
1006
+ for (i = 0, ilen = labels.length; i < ilen; ++i) {
1007
+ label = labels[i];
1008
+ state = label.$layout;
1009
+
1010
+ if (state._visible) {
1011
+ geometry = label.geometry();
1012
+ center = coordinates(label._el, label.model(), geometry);
1013
+ state._box.update(center, geometry, label.rotation());
1014
+ label.draw(chart, center);
1015
+ }
1016
+ }
1017
+ }
1018
+ };
1019
+
1020
+ var formatter = function(value) {
1021
+ if (helpers.isNullOrUndef(value)) {
1022
+ return null;
1023
+ }
1024
+
1025
+ var label = value;
1026
+ var keys, klen, k;
1027
+ if (helpers.isObject(value)) {
1028
+ if (!helpers.isNullOrUndef(value.label)) {
1029
+ label = value.label;
1030
+ } else if (!helpers.isNullOrUndef(value.r)) {
1031
+ label = value.r;
1032
+ } else {
1033
+ label = '';
1034
+ keys = Object.keys(value);
1035
+ for (k = 0, klen = keys.length; k < klen; ++k) {
1036
+ label += (k !== 0 ? ', ' : '') + keys[k] + ': ' + value[keys[k]];
1037
+ }
1038
+ }
1039
+ }
1040
+
1041
+ return '' + label;
1042
+ };
1043
+
1044
+ /**
1045
+ * IMPORTANT: make sure to also update tests and TypeScript definition
1046
+ * files (`/test/specs/defaults.spec.js` and `/types/options.d.ts`)
1047
+ */
1048
+
1049
+ var defaults = {
1050
+ align: 'center',
1051
+ anchor: 'center',
1052
+ backgroundColor: null,
1053
+ borderColor: null,
1054
+ borderRadius: 0,
1055
+ borderWidth: 0,
1056
+ clamp: false,
1057
+ clip: false,
1058
+ color: undefined,
1059
+ display: true,
1060
+ font: {
1061
+ family: undefined,
1062
+ lineHeight: 1.2,
1063
+ size: undefined,
1064
+ style: undefined,
1065
+ weight: null
1066
+ },
1067
+ formatter: formatter,
1068
+ labels: undefined,
1069
+ listeners: {},
1070
+ offset: 4,
1071
+ opacity: 1,
1072
+ padding: {
1073
+ top: 4,
1074
+ right: 4,
1075
+ bottom: 4,
1076
+ left: 4
1077
+ },
1078
+ rotation: 0,
1079
+ textAlign: 'start',
1080
+ textStrokeColor: undefined,
1081
+ textStrokeWidth: 0,
1082
+ textShadowBlur: 0,
1083
+ textShadowColor: undefined
1084
+ };
1085
+
1086
+ /**
1087
+ * @see https://github.com/chartjs/Chart.js/issues/4176
1088
+ */
1089
+
1090
+ var EXPANDO_KEY = '$datalabels';
1091
+ var DEFAULT_KEY = '$default';
1092
+
1093
+ function configure(dataset, options) {
1094
+ var override = dataset.datalabels;
1095
+ var listeners = {};
1096
+ var configs = [];
1097
+ var labels, keys;
1098
+
1099
+ if (override === false) {
1100
+ return null;
1101
+ }
1102
+ if (override === true) {
1103
+ override = {};
1104
+ }
1105
+
1106
+ options = helpers.merge({}, [options, override]);
1107
+ labels = options.labels || {};
1108
+ keys = Object.keys(labels);
1109
+ delete options.labels;
1110
+
1111
+ if (keys.length) {
1112
+ keys.forEach(function(key) {
1113
+ if (labels[key]) {
1114
+ configs.push(helpers.merge({}, [
1115
+ options,
1116
+ labels[key],
1117
+ {_key: key}
1118
+ ]));
1119
+ }
1120
+ });
1121
+ } else {
1122
+ // Default label if no "named" label defined.
1123
+ configs.push(options);
1124
+ }
1125
+
1126
+ // listeners: {<event-type>: {<label-key>: <fn>}}
1127
+ listeners = configs.reduce(function(target, config) {
1128
+ helpers.each(config.listeners || {}, function(fn, event) {
1129
+ target[event] = target[event] || {};
1130
+ target[event][config._key || DEFAULT_KEY] = fn;
1131
+ });
1132
+
1133
+ delete config.listeners;
1134
+ return target;
1135
+ }, {});
1136
+
1137
+ return {
1138
+ labels: configs,
1139
+ listeners: listeners
1140
+ };
1141
+ }
1142
+
1143
+ function dispatchEvent(chart, listeners, label) {
1144
+ if (!listeners) {
1145
+ return;
1146
+ }
1147
+
1148
+ var context = label.$context;
1149
+ var groups = label.$groups;
1150
+ var callback;
1151
+
1152
+ if (!listeners[groups._set]) {
1153
+ return;
1154
+ }
1155
+
1156
+ callback = listeners[groups._set][groups._key];
1157
+ if (!callback) {
1158
+ return;
1159
+ }
1160
+
1161
+ if (helpers.callback(callback, [context]) === true) {
1162
+ // Users are allowed to tweak the given context by injecting values that can be
1163
+ // used in scriptable options to display labels differently based on the current
1164
+ // event (e.g. highlight an hovered label). That's why we update the label with
1165
+ // the output context and schedule a new chart render by setting it dirty.
1166
+ chart[EXPANDO_KEY]._dirty = true;
1167
+ label.update(context);
1168
+ }
1169
+ }
1170
+
1171
+ function dispatchMoveEvents(chart, listeners, previous, label) {
1172
+ var enter, leave;
1173
+
1174
+ if (!previous && !label) {
1175
+ return;
1176
+ }
1177
+
1178
+ if (!previous) {
1179
+ enter = true;
1180
+ } else if (!label) {
1181
+ leave = true;
1182
+ } else if (previous !== label) {
1183
+ leave = enter = true;
1184
+ }
1185
+
1186
+ if (leave) {
1187
+ dispatchEvent(chart, listeners.leave, previous);
1188
+ }
1189
+ if (enter) {
1190
+ dispatchEvent(chart, listeners.enter, label);
1191
+ }
1192
+ }
1193
+
1194
+ function handleMoveEvents(chart, event) {
1195
+ var expando = chart[EXPANDO_KEY];
1196
+ var listeners = expando._listeners;
1197
+ var previous, label;
1198
+
1199
+ if (!listeners.enter && !listeners.leave) {
1200
+ return;
1201
+ }
1202
+
1203
+ if (event.type === 'mousemove') {
1204
+ label = layout.lookup(expando._labels, event);
1205
+ } else if (event.type !== 'mouseout') {
1206
+ return;
1207
+ }
1208
+
1209
+ previous = expando._hovered;
1210
+ expando._hovered = label;
1211
+ dispatchMoveEvents(chart, listeners, previous, label);
1212
+ }
1213
+
1214
+ function handleClickEvents(chart, event) {
1215
+ var expando = chart[EXPANDO_KEY];
1216
+ var handlers = expando._listeners.click;
1217
+ var label = handlers && layout.lookup(expando._labels, event);
1218
+ if (label) {
1219
+ dispatchEvent(chart, handlers, label);
1220
+ }
1221
+ }
1222
+
1223
+ var plugin = {
1224
+ id: 'datalabels',
1225
+
1226
+ defaults: defaults,
1227
+
1228
+ beforeInit: function(chart) {
1229
+ chart[EXPANDO_KEY] = {
1230
+ _actives: []
1231
+ };
1232
+ },
1233
+
1234
+ beforeUpdate: function(chart) {
1235
+ var expando = chart[EXPANDO_KEY];
1236
+ expando._listened = false;
1237
+ expando._listeners = {}; // {<event-type>: {<dataset-index>: {<label-key>: <fn>}}}
1238
+ expando._datasets = []; // per dataset labels: [Label[]]
1239
+ expando._labels = []; // layouted labels: Label[]
1240
+ },
1241
+
1242
+ afterDatasetUpdate: function(chart, args, options) {
1243
+ var datasetIndex = args.index;
1244
+ var expando = chart[EXPANDO_KEY];
1245
+ var labels = expando._datasets[datasetIndex] = [];
1246
+ var visible = chart.isDatasetVisible(datasetIndex);
1247
+ var dataset = chart.data.datasets[datasetIndex];
1248
+ var config = configure(dataset, options);
1249
+ var elements = args.meta.data || [];
1250
+ var ctx = chart.ctx;
1251
+ var i, j, ilen, jlen, cfg, key, el, label;
1252
+
1253
+ ctx.save();
1254
+
1255
+ for (i = 0, ilen = elements.length; i < ilen; ++i) {
1256
+ el = elements[i];
1257
+ el[EXPANDO_KEY] = [];
1258
+
1259
+ if (visible && el && chart.getDataVisibility(i) && !el.skip) {
1260
+ for (j = 0, jlen = config.labels.length; j < jlen; ++j) {
1261
+ cfg = config.labels[j];
1262
+ key = cfg._key;
1263
+
1264
+ label = new Label(cfg, ctx, el, i);
1265
+ label.$groups = {
1266
+ _set: datasetIndex,
1267
+ _key: key || DEFAULT_KEY
1268
+ };
1269
+ label.$context = {
1270
+ active: false,
1271
+ chart: chart,
1272
+ dataIndex: i,
1273
+ dataset: dataset,
1274
+ datasetIndex: datasetIndex
1275
+ };
1276
+
1277
+ label.update(label.$context);
1278
+ el[EXPANDO_KEY].push(label);
1279
+ labels.push(label);
1280
+ }
1281
+ }
1282
+ }
1283
+
1284
+ ctx.restore();
1285
+
1286
+ // Store listeners at the chart level and per event type to optimize
1287
+ // cases where no listeners are registered for a specific event.
1288
+ helpers.merge(expando._listeners, config.listeners, {
1289
+ merger: function(event, target, source) {
1290
+ target[event] = target[event] || {};
1291
+ target[event][args.index] = source[event];
1292
+ expando._listened = true;
1293
+ }
1294
+ });
1295
+ },
1296
+
1297
+ afterUpdate: function(chart, options) {
1298
+ chart[EXPANDO_KEY]._labels = layout.prepare(
1299
+ chart[EXPANDO_KEY]._datasets,
1300
+ options);
1301
+ },
1302
+
1303
+ // Draw labels on top of all dataset elements
1304
+ // https://github.com/chartjs/chartjs-plugin-datalabels/issues/29
1305
+ // https://github.com/chartjs/chartjs-plugin-datalabels/issues/32
1306
+ afterDatasetsDraw: function(chart) {
1307
+ layout.draw(chart, chart[EXPANDO_KEY]._labels);
1308
+ },
1309
+
1310
+ beforeEvent: function(chart, args) {
1311
+ // If there is no listener registered for this chart, `listened` will be false,
1312
+ // meaning we can immediately ignore the incoming event and avoid useless extra
1313
+ // computation for users who don't implement label interactions.
1314
+ if (chart[EXPANDO_KEY]._listened) {
1315
+ var event = args.event;
1316
+ switch (event.type) {
1317
+ case 'mousemove':
1318
+ case 'mouseout':
1319
+ handleMoveEvents(chart, event);
1320
+ break;
1321
+ case 'click':
1322
+ handleClickEvents(chart, event);
1323
+ break;
1324
+ }
1325
+ }
1326
+ },
1327
+
1328
+ afterEvent: function(chart) {
1329
+ var expando = chart[EXPANDO_KEY];
1330
+ var previous = expando._actives;
1331
+ var actives = expando._actives = chart.getActiveElements();
1332
+ var updates = utils.arrayDiff(previous, actives);
1333
+ var i, ilen, j, jlen, update, label, labels;
1334
+
1335
+ for (i = 0, ilen = updates.length; i < ilen; ++i) {
1336
+ update = updates[i];
1337
+ if (update[1]) {
1338
+ labels = update[0].element[EXPANDO_KEY] || [];
1339
+ for (j = 0, jlen = labels.length; j < jlen; ++j) {
1340
+ label = labels[j];
1341
+ label.$context.active = (update[1] === 1);
1342
+ label.update(label.$context);
1343
+ }
1344
+ }
1345
+ }
1346
+
1347
+ if (expando._dirty || updates.length) {
1348
+ layout.update(expando._labels);
1349
+ chart.render();
1350
+ }
1351
+
1352
+ delete expando._dirty;
1353
+ }
1354
+ };
1355
+
1356
+ return plugin;
1357
+
1358
+ })));
admin/js/daterangepicker.js ADDED
@@ -0,0 +1,1595 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @version: 3.1
3
+ * @author: Dan Grossman http://www.dangrossman.info/
4
+ * @copyright: Copyright (c) 2012-2019 Dan Grossman. All rights reserved.
5
+ * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php
6
+ * @website: http://www.daterangepicker.com/
7
+ */
8
+ // Following the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js
9
+ (function (root, factory) {
10
+ if (typeof define === 'function' && define.amd) {
11
+ // AMD. Make globaly available as well
12
+ define(['moment', 'jquery'], function (moment, jquery) {
13
+ if (!jquery.fn) jquery.fn = {}; // webpack server rendering
14
+ if (typeof moment !== 'function' && moment.hasOwnProperty('default')) moment = moment['default']
15
+ return factory(moment, jquery);
16
+ });
17
+ } else if (typeof module === 'object' && module.exports) {
18
+ // Node / Browserify
19
+ //isomorphic issue
20
+ var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined;
21
+ if (!jQuery) {
22
+ jQuery = require('jquery');
23
+ if (!jQuery.fn) jQuery.fn = {};
24
+ }
25
+ var moment = (typeof window != 'undefined' && typeof window.moment != 'undefined') ? window.moment : require('moment');
26
+ module.exports = factory(moment, jQuery);
27
+ } else {
28
+ // Browser globals
29
+ root.daterangepicker = factory(root.moment, root.jQuery);
30
+ }
31
+ }(typeof window !== 'undefined' ? window : this, function(moment, $) {
32
+ var DateRangePicker = function(element, options, cb) {
33
+
34
+ //default settings for options
35
+ this.parentEl = 'body';
36
+ this.element = $(element);
37
+ this.startDate = moment().startOf('day');
38
+ this.endDate = moment().endOf('day');
39
+ this.minDate = false;
40
+ this.maxDate = false;
41
+ this.maxSpan = false;
42
+ this.autoApply = false;
43
+ this.singleDatePicker = false;
44
+ this.showDropdowns = false;
45
+ this.minYear = moment().subtract(100, 'year').format('YYYY');
46
+ this.maxYear = moment().add(100, 'year').format('YYYY');
47
+ this.showWeekNumbers = false;
48
+ this.showISOWeekNumbers = false;
49
+ this.showCustomRangeLabel = true;
50
+ this.timePicker = false;
51
+ this.timePicker24Hour = false;
52
+ this.timePickerIncrement = 1;
53
+ this.timePickerSeconds = false;
54
+ this.linkedCalendars = true;
55
+ this.autoUpdateInput = true;
56
+ this.alwaysShowCalendars = false;
57
+ this.ranges = {};
58
+
59
+ this.opens = 'left';
60
+ if (this.element.hasClass('pull-right'))
61
+ this.opens = 'left';
62
+
63
+ this.drops = 'down';
64
+ if (this.element.hasClass('dropup'))
65
+ this.drops = 'up';
66
+
67
+ this.buttonClasses = 'btn btn-sm';
68
+ this.applyButtonClasses = 'btn-primary';
69
+ this.cancelButtonClasses = 'btn-default';
70
+
71
+ this.locale = {
72
+ direction: 'ltr',
73
+ format: moment.localeData().longDateFormat('L'),
74
+ separator: ' - ',
75
+ applyLabel: 'Apply',
76
+ cancelLabel: 'Cancel',
77
+ weekLabel: 'W',
78
+ customRangeLabel: 'Custom Range',
79
+ daysOfWeek: moment.weekdaysMin(),
80
+ monthNames: moment.monthsShort(),
81
+ firstDay: moment.localeData().firstDayOfWeek()
82
+ };
83
+
84
+ this.callback = function() { };
85
+
86
+ //some state information
87
+ this.isShowing = false;
88
+ this.leftCalendar = {};
89
+ this.rightCalendar = {};
90
+
91
+ //custom options from user
92
+ if (typeof options !== 'object' || options === null)
93
+ options = {};
94
+
95
+ //allow setting options with data attributes
96
+ //data-api options will be overwritten with custom javascript options
97
+ options = $.extend(this.element.data(), options);
98
+
99
+ //html template for the picker UI
100
+ if (typeof options.template !== 'string' && !(options.template instanceof $))
101
+ options.template =
102
+ '<div class="daterangepicker">' +
103
+ '<div class="daterangepickerflex">' +
104
+ '<div class="daterangepicker-left">' +
105
+ '<div class="ranges"></div>' +
106
+ '</div>' +
107
+ '<div class="daterangepicker-right">' +
108
+ '<div class="drp-calendar left">' +
109
+ '<div class="calendar-table"></div>' +
110
+ '<div class="calendar-time"></div>' +
111
+ '</div>' +
112
+ '<div class="drp-calendar right">' +
113
+ '<div class="calendar-table"></div>' +
114
+ '<div class="calendar-time"></div>' +
115
+ '</div>' +
116
+ '<div class="drp-buttons">' +
117
+ '<div class="drpbtmbuttonflex">' +
118
+ '<div class="drpbtmbuttonflex-left">' +
119
+ '<div class="btmslectdatetitle">Selected Date Range</div>' +
120
+ '<span class="drp-selected"></span>' +
121
+ '</div>' +
122
+ '<div class="drpbtmbuttonflex-right">' +
123
+ '<button class="cancelBtn" type="button"></button>' +
124
+ '<button class="applyBtn" type="button"></button> ' +
125
+ '</div>' +
126
+ '</div>'
127
+ '</div>' +
128
+ '</div>' +
129
+ '</div>'
130
+ '</div>';
131
+
132
+ this.parentEl = (options.parentEl && $(options.parentEl).length) ? $(options.parentEl) : $(this.parentEl);
133
+ this.container = $(options.template).appendTo(this.parentEl);
134
+
135
+ //
136
+ // handle all the possible options overriding defaults
137
+ //
138
+
139
+ if (typeof options.locale === 'object') {
140
+
141
+ if (typeof options.locale.direction === 'string')
142
+ this.locale.direction = options.locale.direction;
143
+
144
+ if (typeof options.locale.format === 'string')
145
+ this.locale.format = options.locale.format;
146
+
147
+ if (typeof options.locale.separator === 'string')
148
+ this.locale.separator = options.locale.separator;
149
+
150
+ if (typeof options.locale.daysOfWeek === 'object')
151
+ this.locale.daysOfWeek = options.locale.daysOfWeek.slice();
152
+
153
+ if (typeof options.locale.monthNames === 'object')
154
+ this.locale.monthNames = options.locale.monthNames.slice();
155
+
156
+ if (typeof options.locale.firstDay === 'number')
157
+ this.locale.firstDay = options.locale.firstDay;
158
+
159
+ if (typeof options.locale.applyLabel === 'string')
160
+ this.locale.applyLabel = options.locale.applyLabel;
161
+
162
+ if (typeof options.locale.cancelLabel === 'string')
163
+ this.locale.cancelLabel = options.locale.cancelLabel;
164
+
165
+ if (typeof options.locale.weekLabel === 'string')
166
+ this.locale.weekLabel = options.locale.weekLabel;
167
+
168
+ if (typeof options.locale.customRangeLabel === 'string'){
169
+ //Support unicode chars in the custom range name.
170
+ var elem = document.createElement('textarea');
171
+ elem.innerHTML = options.locale.customRangeLabel;
172
+ var rangeHtml = elem.value;
173
+ this.locale.customRangeLabel = rangeHtml;
174
+ }
175
+ }
176
+ this.container.addClass(this.locale.direction);
177
+
178
+ if (typeof options.startDate === 'string')
179
+ this.startDate = moment(options.startDate, this.locale.format);
180
+
181
+ if (typeof options.endDate === 'string')
182
+ this.endDate = moment(options.endDate, this.locale.format);
183
+
184
+ if (typeof options.minDate === 'string')
185
+ this.minDate = moment(options.minDate, this.locale.format);
186
+
187
+ if (typeof options.maxDate === 'string')
188
+ this.maxDate = moment(options.maxDate, this.locale.format);
189
+
190
+ if (typeof options.startDate === 'object')
191
+ this.startDate = moment(options.startDate);
192
+
193
+ if (typeof options.endDate === 'object')
194
+ this.endDate = moment(options.endDate);
195
+
196
+ if (typeof options.minDate === 'object')
197
+ this.minDate = moment(options.minDate);
198
+
199
+ if (typeof options.maxDate === 'object')
200
+ this.maxDate = moment(options.maxDate);
201
+
202
+ // sanity check for bad options
203
+ if (this.minDate && this.startDate.isBefore(this.minDate))
204
+ this.startDate = this.minDate.clone();
205
+
206
+ // sanity check for bad options
207
+ if (this.maxDate && this.endDate.isAfter(this.maxDate))
208
+ this.endDate = this.maxDate.clone();
209
+
210
+ if (typeof options.applyButtonClasses === 'string')
211
+ this.applyButtonClasses = options.applyButtonClasses;
212
+
213
+ if (typeof options.applyClass === 'string') //backwards compat
214
+ this.applyButtonClasses = options.applyClass;
215
+
216
+ if (typeof options.cancelButtonClasses === 'string')
217
+ this.cancelButtonClasses = options.cancelButtonClasses;
218
+
219
+ if (typeof options.cancelClass === 'string') //backwards compat
220
+ this.cancelButtonClasses = options.cancelClass;
221
+
222
+ if (typeof options.maxSpan === 'object')
223
+ this.maxSpan = options.maxSpan;
224
+
225
+ if (typeof options.dateLimit === 'object') //backwards compat
226
+ this.maxSpan = options.dateLimit;
227
+
228
+ if (typeof options.opens === 'string')
229
+ this.opens = options.opens;
230
+
231
+ if (typeof options.drops === 'string')
232
+ this.drops = options.drops;
233
+
234
+ if (typeof options.showWeekNumbers === 'boolean')
235
+ this.showWeekNumbers = options.showWeekNumbers;
236
+
237
+ if (typeof options.showISOWeekNumbers === 'boolean')
238
+ this.showISOWeekNumbers = options.showISOWeekNumbers;
239
+
240
+ if (typeof options.buttonClasses === 'string')
241
+ this.buttonClasses = options.buttonClasses;
242
+
243
+ if (typeof options.buttonClasses === 'object')
244
+ this.buttonClasses = options.buttonClasses.join(' ');
245
+
246
+ if (typeof options.showDropdowns === 'boolean')
247
+ this.showDropdowns = options.showDropdowns;
248
+
249
+ if (typeof options.minYear === 'number')
250
+ this.minYear = options.minYear;
251
+
252
+ if (typeof options.maxYear === 'number')
253
+ this.maxYear = options.maxYear;
254
+
255
+ if (typeof options.showCustomRangeLabel === 'boolean')
256
+ this.showCustomRangeLabel = options.showCustomRangeLabel;
257
+
258
+ if (typeof options.singleDatePicker === 'boolean') {
259
+ this.singleDatePicker = options.singleDatePicker;
260
+ if (this.singleDatePicker)
261
+ this.endDate = this.startDate.clone();
262
+ }
263
+
264
+ if (typeof options.timePicker === 'boolean')
265
+ this.timePicker = options.timePicker;
266
+
267
+ if (typeof options.timePickerSeconds === 'boolean')
268
+ this.timePickerSeconds = options.timePickerSeconds;
269
+
270
+ if (typeof options.timePickerIncrement === 'number')
271
+ this.timePickerIncrement = options.timePickerIncrement;
272
+
273
+ if (typeof options.timePicker24Hour === 'boolean')
274
+ this.timePicker24Hour = options.timePicker24Hour;
275
+
276
+ if (typeof options.autoApply === 'boolean')
277
+ this.autoApply = options.autoApply;
278
+
279
+ if (typeof options.autoUpdateInput === 'boolean')
280
+ this.autoUpdateInput = options.autoUpdateInput;
281
+
282
+ if (typeof options.linkedCalendars === 'boolean')
283
+ this.linkedCalendars = options.linkedCalendars;
284
+
285
+ if (typeof options.isInvalidDate === 'function')
286
+ this.isInvalidDate = options.isInvalidDate;
287
+
288
+ if (typeof options.isCustomDate === 'function')
289
+ this.isCustomDate = options.isCustomDate;
290
+
291
+ if (typeof options.alwaysShowCalendars === 'boolean')
292
+ this.alwaysShowCalendars = options.alwaysShowCalendars;
293
+
294
+ // update day names order to firstDay
295
+ if (this.locale.firstDay != 0) {
296
+ var iterator = this.locale.firstDay;
297
+ while (iterator > 0) {
298
+ this.locale.daysOfWeek.push(this.locale.daysOfWeek.shift());
299
+ iterator--;
300
+ }
301
+ }
302
+
303
+ var start, end, range;
304
+
305
+ //if no start/end dates set, check if an input element contains initial values
306
+ if (typeof options.startDate === 'undefined' && typeof options.endDate === 'undefined') {
307
+ if ($(this.element).is(':text')) {
308
+ var val = $(this.element).val(),
309
+ split = val.split(this.locale.separator);
310
+
311
+ start = end = null;
312
+
313
+ if (split.length == 2) {
314
+ start = moment(split[0], this.locale.format);
315
+ end = moment(split[1], this.locale.format);
316
+ } else if (this.singleDatePicker && val !== "") {
317
+ start = moment(val, this.locale.format);
318
+ end = moment(val, this.locale.format);
319
+ }
320
+ if (start !== null && end !== null) {
321
+ this.setStartDate(start);
322
+ this.setEndDate(end);
323
+ }
324
+ }
325
+ }
326
+
327
+ if (typeof options.ranges === 'object') {
328
+ for (range in options.ranges) {
329
+
330
+ if (typeof options.ranges[range][0] === 'string')
331
+ start = moment(options.ranges[range][0], this.locale.format);
332
+ else
333
+ start = moment(options.ranges[range][0]);
334
+
335
+ if (typeof options.ranges[range][1] === 'string')
336
+ end = moment(options.ranges[range][1], this.locale.format);
337
+ else
338
+ end = moment(options.ranges[range][1]);
339
+
340
+ // If the start or end date exceed those allowed by the minDate or maxSpan
341
+ // options, shorten the range to the allowable period.
342
+ if (this.minDate && start.isBefore(this.minDate))
343
+ start = this.minDate.clone();
344
+
345
+ var maxDate = this.maxDate;
346
+ if (this.maxSpan && maxDate && start.clone().add(this.maxSpan).isAfter(maxDate))
347
+ maxDate = start.clone().add(this.maxSpan);
348
+ if (maxDate && end.isAfter(maxDate))
349
+ end = maxDate.clone();
350
+
351
+ // If the end of the range is before the minimum or the start of the range is
352
+ // after the maximum, don't display this range option at all.
353
+ if ((this.minDate && end.isBefore(this.minDate, this.timepicker ? 'minute' : 'day'))
354
+ || (maxDate && start.isAfter(maxDate, this.timepicker ? 'minute' : 'day')))
355
+ continue;
356
+
357
+ //Support unicode chars in the range names.
358
+ var elem = document.createElement('textarea');
359
+ elem.innerHTML = range;
360
+ var rangeHtml = elem.value;
361
+
362
+ this.ranges[rangeHtml] = [start, end];
363
+ }
364
+
365
+ var list = '<ul>';
366
+ for (range in this.ranges) {
367
+ list += '<li data-range-key="' + range + '">' + range + '</li>';
368
+ }
369
+ if (this.showCustomRangeLabel) {
370
+ list += '<li data-range-key="' + this.locale.customRangeLabel + '">' + this.locale.customRangeLabel + '</li>';
371
+ }
372
+ list += '</ul>';
373
+ this.container.find('.ranges').prepend(list);
374
+ }
375
+
376
+ if (typeof cb === 'function') {
377
+ this.callback = cb;
378
+ }
379
+
380
+ if (!this.timePicker) {
381
+ this.startDate = this.startDate.startOf('day');
382
+ this.endDate = this.endDate.endOf('day');
383
+ this.container.find('.calendar-time').hide();
384
+ }
385
+
386
+ //can't be used together for now
387
+ if (this.timePicker && this.autoApply)
388
+ this.autoApply = false;
389
+
390
+ if (this.autoApply) {
391
+ this.container.addClass('auto-apply');
392
+ }
393
+
394
+ if (typeof options.ranges === 'object')
395
+ this.container.addClass('show-ranges');
396
+
397
+ if (this.singleDatePicker) {
398
+ this.container.addClass('single');
399
+ this.container.find('.drp-calendar.left').addClass('single');
400
+ this.container.find('.drp-calendar.left').show();
401
+ this.container.find('.drp-calendar.right').hide();
402
+ if (!this.timePicker && this.autoApply) {
403
+ this.container.addClass('auto-apply');
404
+ }
405
+ }
406
+
407
+ if ((typeof options.ranges === 'undefined' && !this.singleDatePicker) || this.alwaysShowCalendars) {
408
+ //this.container.addClass('show-calendar');
409
+ this.container.addClass('flexdaterange');
410
+
411
+ }
412
+
413
+ this.container.addClass('opens' + this.opens);
414
+
415
+ //apply CSS classes and labels to buttons
416
+ this.container.find('.applyBtn, .cancelBtn').addClass(this.buttonClasses);
417
+ if (this.applyButtonClasses.length)
418
+ this.container.find('.applyBtn').addClass(this.applyButtonClasses);
419
+ if (this.cancelButtonClasses.length)
420
+ this.container.find('.cancelBtn').addClass(this.cancelButtonClasses);
421
+ this.container.find('.applyBtn').html(this.locale.applyLabel);
422
+ this.container.find('.cancelBtn').html(this.locale.cancelLabel);
423
+
424
+ //
425
+ // event listeners
426
+ //
427
+
428
+ this.container.find('.drp-calendar')
429
+ .on('click.daterangepicker', '.prev', $.proxy(this.clickPrev, this))
430
+ .on('click.daterangepicker', '.next', $.proxy(this.clickNext, this))
431
+ .on('mousedown.daterangepicker', 'td.available', $.proxy(this.clickDate, this))
432
+ .on('mouseenter.daterangepicker', 'td.available', $.proxy(this.hoverDate, this))
433
+ .on('change.daterangepicker', 'select.yearselect', $.proxy(this.monthOrYearChanged, this))
434
+ .on('change.daterangepicker', 'select.monthselect', $.proxy(this.monthOrYearChanged, this))
435
+ .on('change.daterangepicker', 'select.hourselect,select.minuteselect,select.secondselect,select.ampmselect', $.proxy(this.timeChanged, this));
436
+
437
+ this.container.find('.ranges')
438
+ .on('click.daterangepicker', 'li', $.proxy(this.clickRange, this));
439
+
440
+ this.container.find('.drp-buttons')
441
+ .on('click.daterangepicker', 'button.applyBtn', $.proxy(this.clickApply, this))
442
+ .on('click.daterangepicker', 'button.cancelBtn', $.proxy(this.clickCancel, this));
443
+
444
+ if (this.element.is('input') || this.element.is('button')) {
445
+ this.element.on({
446
+ 'click.daterangepicker': $.proxy(this.show, this),
447
+ 'focus.daterangepicker': $.proxy(this.show, this),
448
+ 'keyup.daterangepicker': $.proxy(this.elementChanged, this),
449
+ 'keydown.daterangepicker': $.proxy(this.keydown, this) //IE 11 compatibility
450
+ });
451
+ } else {
452
+ this.element.on('click.daterangepicker', $.proxy(this.toggle, this));
453
+ this.element.on('keydown.daterangepicker', $.proxy(this.toggle, this));
454
+ }
455
+
456
+ //
457
+ // if attached to a text input, set the initial value
458
+ //
459
+
460
+ this.updateElement();
461
+
462
+ };
463
+
464
+ DateRangePicker.prototype = {
465
+
466
+ constructor: DateRangePicker,
467
+
468
+ setStartDate: function(startDate) {
469
+ if (typeof startDate === 'string')
470
+ this.startDate = moment(startDate, this.locale.format);
471
+
472
+ if (typeof startDate === 'object')
473
+ this.startDate = moment(startDate);
474
+
475
+ if (!this.timePicker)
476
+ this.startDate = this.startDate.startOf('day');
477
+
478
+ if (this.timePicker && this.timePickerIncrement)
479
+ this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
480
+
481
+ if (this.minDate && this.startDate.isBefore(this.minDate)) {
482
+ this.startDate = this.minDate.clone();
483
+ if (this.timePicker && this.timePickerIncrement)
484
+ this.startDate.minute(Math.round(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
485
+ }
486
+
487
+ if (this.maxDate && this.startDate.isAfter(this.maxDate)) {
488
+ this.startDate = this.maxDate.clone();
489
+ if (this.timePicker && this.timePickerIncrement)
490
+ this.startDate.minute(Math.floor(this.startDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
491
+ }
492
+
493
+ if (!this.isShowing)
494
+ this.updateElement();
495
+
496
+ this.updateMonthsInView();
497
+ },
498
+
499
+ setEndDate: function(endDate) {
500
+ if (typeof endDate === 'string')
501
+ this.endDate = moment(endDate, this.locale.format);
502
+
503
+ if (typeof endDate === 'object')
504
+ this.endDate = moment(endDate);
505
+
506
+ if (!this.timePicker)
507
+ this.endDate = this.endDate.endOf('day');
508
+
509
+ if (this.timePicker && this.timePickerIncrement)
510
+ this.endDate.minute(Math.round(this.endDate.minute() / this.timePickerIncrement) * this.timePickerIncrement);
511
+
512
+ if (this.endDate.isBefore(this.startDate))
513
+ this.endDate = this.startDate.clone();
514
+
515
+ if (this.maxDate && this.endDate.isAfter(this.maxDate))
516
+ this.endDate = this.maxDate.clone();
517
+
518
+ if (this.maxSpan && this.startDate.clone().add(this.maxSpan).isBefore(this.endDate))
519
+ this.endDate = this.startDate.clone().add(this.maxSpan);
520
+
521
+ this.previousRightTime = this.endDate.clone();
522
+
523
+ this.container.find('.drp-selected').html(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
524
+
525
+ if (!this.isShowing)
526
+ this.updateElement();
527
+
528
+ this.updateMonthsInView();
529
+ },
530
+
531
+ isInvalidDate: function() {
532
+ return false;
533
+ },
534
+
535
+ isCustomDate: function() {
536
+ return false;
537
+ },
538
+
539
+ updateView: function() {
540
+ if (this.timePicker) {
541
+ this.renderTimePicker('left');
542
+ this.renderTimePicker('right');
543
+ if (!this.endDate) {
544
+ this.container.find('.right .calendar-time select').prop('disabled', true).addClass('disabled');
545
+ } else {
546
+ this.container.find('.right .calendar-time select').prop('disabled', false).removeClass('disabled');
547
+ }
548
+ }
549
+ if (this.endDate)
550
+ this.container.find('.drp-selected').html(this.startDate.format(this.locale.format) + this.locale.separator + this.endDate.format(this.locale.format));
551
+ this.updateMonthsInView();
552
+ this.updateCalendars();
553
+ this.updateFormInputs();
554
+ },
555
+
556
+ updateMonthsInView: function() {
557
+ if (this.endDate) {
558
+
559
+ //if both dates are visible already, do nothing
560
+ if (!this.singleDatePicker && this.leftCalendar.month && this.rightCalendar.month &&
561
+ (this.startDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.startDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
562
+ &&
563
+ (this.endDate.format('YYYY-MM') == this.leftCalendar.month.format('YYYY-MM') || this.endDate.format('YYYY-MM') == this.rightCalendar.month.format('YYYY-MM'))
564
+ ) {
565
+ return;
566
+ }
567
+
568
+ this.leftCalendar.month = this.startDate.clone().date(2);
569
+ if (!this.linkedCalendars && (this.endDate.month() != this.startDate.month() || this.endDate.year() != this.startDate.year())) {
570
+ this.rightCalendar.month = this.endDate.clone().date(2);
571
+ } else {
572
+ this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
573
+ }
574
+
575
+ } else {
576
+ if (this.leftCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM') && this.rightCalendar.month.format('YYYY-MM') != this.startDate.format('YYYY-MM')) {
577
+ this.leftCalendar.month = this.startDate.clone().date(2);
578
+ this.rightCalendar.month = this.startDate.clone().date(2).add(1, 'month');
579
+ }
580
+ }
581
+ if (this.maxDate && this.linkedCalendars && !this.singleDatePicker && this.rightCalendar.month > this.maxDate) {
582
+ this.rightCalendar.month = this.maxDate.clone().date(2);
583
+ this.leftCalendar.month = this.maxDate.clone().date(2).subtract(1, 'month');
584
+ }
585
+ },
586
+
587
+ updateCalendars: function() {
588
+
589
+ if (this.timePicker) {
590
+ var hour, minute, second;
591
+ if (this.endDate) {
592
+ hour = parseInt(this.container.find('.left .hourselect').val(), 10);
593
+ minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
594
+ if (isNaN(minute)) {
595
+ minute = parseInt(this.container.find('.left .minuteselect option:last').val(), 10);
596
+ }
597
+ second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
598
+ if (!this.timePicker24Hour) {
599
+ var ampm = this.container.find('.left .ampmselect').val();
600
+ if (ampm === 'PM' && hour < 12)
601
+ hour += 12;
602
+ if (ampm === 'AM' && hour === 12)
603
+ hour = 0;
604
+ }
605
+ } else {
606
+ hour = parseInt(this.container.find('.right .hourselect').val(), 10);
607
+ minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
608
+ if (isNaN(minute)) {
609
+ minute = parseInt(this.container.find('.right .minuteselect option:last').val(), 10);
610
+ }
611
+ second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
612
+ if (!this.timePicker24Hour) {
613
+ var ampm = this.container.find('.right .ampmselect').val();
614
+ if (ampm === 'PM' && hour < 12)
615
+ hour += 12;
616
+ if (ampm === 'AM' && hour === 12)
617
+ hour = 0;
618
+ }
619
+ }
620
+ this.leftCalendar.month.hour(hour).minute(minute).second(second);
621
+ this.rightCalendar.month.hour(hour).minute(minute).second(second);
622
+ }
623
+
624
+ this.renderCalendar('left');
625
+ this.renderCalendar('right');
626
+
627
+ //highlight any predefined range matching the current start and end dates
628
+ this.container.find('.ranges li').removeClass('active');
629
+ this.container.find('.ranges li:last').addClass('cstmrang-li');
630
+ if (this.endDate == null) return;
631
+
632
+ this.calculateChosenLabel();
633
+ },
634
+
635
+ renderCalendar: function(side) {
636
+
637
+ //
638
+ // Build the matrix of dates that will populate the calendar
639
+ //
640
+
641
+ var calendar = side == 'left' ? this.leftCalendar : this.rightCalendar;
642
+ var month = calendar.month.month();
643
+ var year = calendar.month.year();
644
+ var hour = calendar.month.hour();
645
+ var minute = calendar.month.minute();
646
+ var second = calendar.month.second();
647
+ var daysInMonth = moment([year, month]).daysInMonth();
648
+ var firstDay = moment([year, month, 1]);
649
+ var lastDay = moment([year, month, daysInMonth]);
650
+ var lastMonth = moment(firstDay).subtract(1, 'month').month();
651
+ var lastYear = moment(firstDay).subtract(1, 'month').year();
652
+ var daysInLastMonth = moment([lastYear, lastMonth]).daysInMonth();
653
+ var dayOfWeek = firstDay.day();
654
+
655
+ //initialize a 6 rows x 7 columns array for the calendar
656
+ var calendar = [];
657
+ calendar.firstDay = firstDay;
658
+ calendar.lastDay = lastDay;
659
+
660
+ for (var i = 0; i < 6; i++) {
661
+ calendar[i] = [];
662
+ }
663
+
664
+ //populate the calendar with date objects
665
+ var startDay = daysInLastMonth - dayOfWeek + this.locale.firstDay + 1;
666
+ if (startDay > daysInLastMonth)
667
+ startDay -= 7;
668
+
669
+ if (dayOfWeek == this.locale.firstDay)
670
+ startDay = daysInLastMonth - 6;
671
+
672
+ var curDate = moment([lastYear, lastMonth, startDay, 12, minute, second]);
673
+
674
+ var col, row;
675
+ for (var i = 0, col = 0, row = 0; i < 42; i++, col++, curDate = moment(curDate).add(24, 'hour')) {
676
+ if (i > 0 && col % 7 === 0) {
677
+ col = 0;
678
+ row++;
679
+ }
680
+ calendar[row][col] = curDate.clone().hour(hour).minute(minute).second(second);
681
+ curDate.hour(12);
682
+
683
+ if (this.minDate && calendar[row][col].format('YYYY-MM-DD') == this.minDate.format('YYYY-MM-DD') && calendar[row][col].isBefore(this.minDate) && side == 'left') {
684
+ calendar[row][col] = this.minDate.clone();
685
+ }
686
+
687
+ if (this.maxDate && calendar[row][col].format('YYYY-MM-DD') == this.maxDate.format('YYYY-MM-DD') && calendar[row][col].isAfter(this.maxDate) && side == 'right') {
688
+ calendar[row][col] = this.maxDate.clone();
689
+ }
690
+
691
+ }
692
+
693
+ //make the calendar object available to hoverDate/clickDate
694
+ if (side == 'left') {
695
+ this.leftCalendar.calendar = calendar;
696
+ } else {
697
+ this.rightCalendar.calendar = calendar;
698
+ }
699
+
700
+ //
701
+ // Display the calendar
702
+ //
703
+
704
+ var minDate = side == 'left' ? this.minDate : this.startDate;
705
+ var maxDate = this.maxDate;
706
+ var selected = side == 'left' ? this.startDate : this.endDate;
707
+ var arrow = this.locale.direction == 'ltr' ? {left: 'chevron-left', right: 'chevron-right'} : {left: 'chevron-right', right: 'chevron-left'};
708
+
709
+ var html = '<table class="table-condensed">';
710
+ html += '<thead>';
711
+ html += '<tr>';
712
+
713
+ // add empty cell for week number
714
+ if (this.showWeekNumbers || this.showISOWeekNumbers)
715
+ html += '<th></th>';
716
+
717
+ if ((!minDate || minDate.isBefore(calendar.firstDay)) && (!this.linkedCalendars || side == 'left')) {
718
+ html += '<th class="prev available"><span></span></th>';
719
+ } else {
720
+ html += '<th></th>';
721
+ }
722
+
723
+ var dateHtml = this.locale.monthNames[calendar[1][1].month()] + calendar[1][1].format(" YYYY");
724
+
725
+ if (this.showDropdowns) {
726
+ var currentMonth = calendar[1][1].month();
727
+ var currentYear = calendar[1][1].year();
728
+ var maxYear = (maxDate && maxDate.year()) || (this.maxYear);
729
+ var minYear = (minDate && minDate.year()) || (this.minYear);
730
+ var inMinYear = currentYear == minYear;
731
+ var inMaxYear = currentYear == maxYear;
732
+
733
+ var monthHtml = '<select class="monthselect">';
734
+ for (var m = 0; m < 12; m++) {
735
+ if ((!inMinYear || (minDate && m >= minDate.month())) && (!inMaxYear || (maxDate && m <= maxDate.month()))) {
736
+ monthHtml += "<option value='" + m + "'" +
737
+ (m === currentMonth ? " selected='selected'" : "") +
738
+ ">" + this.locale.monthNames[m] + "</option>";
739
+ } else {
740
+ monthHtml += "<option value='" + m + "'" +
741
+ (m === currentMonth ? " selected='selected'" : "") +
742
+ " disabled='disabled'>" + this.locale.monthNames[m] + "</option>";
743
+ }
744
+ }
745
+ monthHtml += "</select>";
746
+
747
+ var yearHtml = '<select class="yearselect">';
748
+ for (var y = minYear; y <= maxYear; y++) {
749
+ yearHtml += '<option value="' + y + '"' +
750
+ (y === currentYear ? ' selected="selected"' : '') +
751
+ '>' + y + '</option>';
752
+ }
753
+ yearHtml += '</select>';
754
+
755
+ dateHtml = monthHtml + yearHtml;
756
+ }
757
+
758
+ html += '<th colspan="5" class="month">' + dateHtml + '</th>';
759
+ if ((!maxDate || maxDate.isAfter(calendar.lastDay)) && (!this.linkedCalendars || side == 'right' || this.singleDatePicker)) {
760
+ html += '<th class="next available"><span></span></th>';
761
+ } else {
762
+ html += '<th></th>';
763
+ }
764
+
765
+ html += '</tr>';
766
+ html += '<tr>';
767
+
768
+ // add week number label
769
+ if (this.showWeekNumbers || this.showISOWeekNumbers)
770
+ html += '<th class="week">' + this.locale.weekLabel + '</th>';
771
+
772
+ $.each(this.locale.daysOfWeek, function(index, dayOfWeek) {
773
+ html += '<th>' + dayOfWeek + '</th>';
774
+ });
775
+
776
+ html += '</tr>';
777
+ html += '</thead>';
778
+ html += '<tbody>';
779
+
780
+ //adjust maxDate to reflect the maxSpan setting in order to
781
+ //grey out end dates beyond the maxSpan
782
+ if (this.endDate == null && this.maxSpan) {
783
+ var maxLimit = this.startDate.clone().add(this.maxSpan).endOf('day');
784
+ if (!maxDate || maxLimit.isBefore(maxDate)) {
785
+ maxDate = maxLimit;
786
+ }
787
+ }
788
+
789
+ for (var row = 0; row < 6; row++) {
790
+ html += '<tr>';
791
+
792
+ // add week number
793
+ if (this.showWeekNumbers)
794
+ html += '<td class="week">' + calendar[row][0].week() + '</td>';
795
+ else if (this.showISOWeekNumbers)
796
+ html += '<td class="week">' + calendar[row][0].isoWeek() + '</td>';
797
+
798
+ for (var col = 0; col < 7; col++) {
799
+
800
+ var classes = [];
801
+
802
+ //highlight today's date
803
+ if (calendar[row][col].isSame(new Date(), "day"))
804
+ classes.push('today');
805
+
806
+ //highlight weekends
807
+ if (calendar[row][col].isoWeekday() > 5)
808
+ classes.push('weekend');
809
+
810
+ //grey out the dates in other months displayed at beginning and end of this calendar
811
+ if (calendar[row][col].month() != calendar[1][1].month())
812
+ classes.push('off', 'ends');
813
+
814
+ //don't allow selection of dates before the minimum date
815
+ if (this.minDate && calendar[row][col].isBefore(this.minDate, 'day'))
816
+ classes.push('off', 'disabled');
817
+
818
+ //don't allow selection of dates after the maximum date
819
+ if (maxDate && calendar[row][col].isAfter(maxDate, 'day'))
820
+ classes.push('off', 'disabled');
821
+
822
+ //don't allow selection of date if a custom function decides it's invalid
823
+ if (this.isInvalidDate(calendar[row][col]))
824
+ classes.push('off', 'disabled');
825
+
826
+ //highlight the currently selected start date
827
+ if (calendar[row][col].format('YYYY-MM-DD') == this.startDate.format('YYYY-MM-DD'))
828
+ classes.push('active', 'start-date');
829
+
830
+ //highlight the currently selected end date
831
+ if (this.endDate != null && calendar[row][col].format('YYYY-MM-DD') == this.endDate.format('YYYY-MM-DD'))
832
+ classes.push('active', 'end-date');
833
+
834
+ //highlight dates in-between the selected dates
835
+ if (this.endDate != null && calendar[row][col] > this.startDate && calendar[row][col] < this.endDate)
836
+ classes.push('in-range');
837
+
838
+ //apply custom classes for this date
839
+ var isCustom = this.isCustomDate(calendar[row][col]);
840
+ if (isCustom !== false) {
841
+ if (typeof isCustom === 'string')
842
+ classes.push(isCustom);
843
+ else
844
+ Array.prototype.push.apply(classes, isCustom);
845
+ }
846
+
847
+ var cname = '', disabled = false;
848
+ for (var i = 0; i < classes.length; i++) {
849
+ cname += classes[i] + ' ';
850
+ if (classes[i] == 'disabled')
851
+ disabled = true;
852
+ }
853
+ if (!disabled)
854
+ cname += 'available';
855
+
856
+ html += '<td class="' + cname.replace(/^\s+|\s+$/g, '') + '" data-title="' + 'r' + row + 'c' + col + '">' + calendar[row][col].date() + '</td>';
857
+
858
+ }
859
+ html += '</tr>';
860
+ }
861
+
862
+ html += '</tbody>';
863
+ html += '</table>';
864
+
865
+ this.container.find('.drp-calendar.' + side + ' .calendar-table').html(html);
866
+
867
+ },
868
+
869
+ renderTimePicker: function(side) {
870
+
871
+ // Don't bother updating the time picker if it's currently disabled
872
+ // because an end date hasn't been clicked yet
873
+ if (side == 'right' && !this.endDate) return;
874
+
875
+ var html, selected, minDate, maxDate = this.maxDate;
876
+
877
+ if (this.maxSpan && (!this.maxDate || this.startDate.clone().add(this.maxSpan).isBefore(this.maxDate)))
878
+ maxDate = this.startDate.clone().add(this.maxSpan);
879
+
880
+ if (side == 'left') {
881
+ selected = this.startDate.clone();
882
+ minDate = this.minDate;
883
+ } else if (side == 'right') {
884
+ selected = this.endDate.clone();
885
+ minDate = this.startDate;
886
+
887
+ //Preserve the time already selected
888
+ var timeSelector = this.container.find('.drp-calendar.right .calendar-time');
889
+ if (timeSelector.html() != '') {
890
+
891
+ selected.hour(!isNaN(selected.hour()) ? selected.hour() : timeSelector.find('.hourselect option:selected').val());
892
+ selected.minute(!isNaN(selected.minute()) ? selected.minute() : timeSelector.find('.minuteselect option:selected').val());
893
+ selected.second(!isNaN(selected.second()) ? selected.second() : timeSelector.find('.secondselect option:selected').val());
894
+
895
+ if (!this.timePicker24Hour) {
896
+ var ampm = timeSelector.find('.ampmselect option:selected').val();
897
+ if (ampm === 'PM' && selected.hour() < 12)
898
+ selected.hour(selected.hour() + 12);
899
+ if (ampm === 'AM' && selected.hour() === 12)
900
+ selected.hour(0);
901
+ }
902
+
903
+ }
904
+
905
+ if (selected.isBefore(this.startDate))
906
+ selected = this.startDate.clone();
907
+
908
+ if (maxDate && selected.isAfter(maxDate))
909
+ selected = maxDate.clone();
910
+
911
+ }
912
+
913
+ //
914
+ // hours
915
+ //
916
+
917
+ html = '<select class="hourselect">';
918
+
919
+ var start = this.timePicker24Hour ? 0 : 1;
920
+ var end = this.timePicker24Hour ? 23 : 12;
921
+
922
+ for (var i = start; i <= end; i++) {
923
+ var i_in_24 = i;
924
+ if (!this.timePicker24Hour)
925
+ i_in_24 = selected.hour() >= 12 ? (i == 12 ? 12 : i + 12) : (i == 12 ? 0 : i);
926
+
927
+ var time = selected.clone().hour(i_in_24);
928
+ var disabled = false;
929
+ if (minDate && time.minute(59).isBefore(minDate))
930
+ disabled = true;
931
+ if (maxDate && time.minute(0).isAfter(maxDate))
932
+ disabled = true;
933
+
934
+ if (i_in_24 == selected.hour() && !disabled) {
935
+ html += '<option value="' + i + '" selected="selected">' + i + '</option>';
936
+ } else if (disabled) {
937
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + i + '</option>';
938
+ } else {
939
+ html += '<option value="' + i + '">' + i + '</option>';
940
+ }
941
+ }
942
+
943
+ html += '</select> ';
944
+
945
+ //
946
+ // minutes
947
+ //
948
+
949
+ html += ': <select class="minuteselect">';
950
+
951
+ for (var i = 0; i < 60; i += this.timePickerIncrement) {
952
+ var padded = i < 10 ? '0' + i : i;
953
+ var time = selected.clone().minute(i);
954
+
955
+ var disabled = false;
956
+ if (minDate && time.second(59).isBefore(minDate))
957
+ disabled = true;
958
+ if (maxDate && time.second(0).isAfter(maxDate))
959
+ disabled = true;
960
+
961
+ if (selected.minute() == i && !disabled) {
962
+ html += '<option value="' + i + '" selected="selected">' + padded + '</option>';
963
+ } else if (disabled) {
964
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>';
965
+ } else {
966
+ html += '<option value="' + i + '">' + padded + '</option>';
967
+ }
968
+ }
969
+
970
+ html += '</select> ';
971
+
972
+ //
973
+ // seconds
974
+ //
975
+
976
+ if (this.timePickerSeconds) {
977
+ html += ': <select class="secondselect">';
978
+
979
+ for (var i = 0; i < 60; i++) {
980
+ var padded = i < 10 ? '0' + i : i;
981
+ var time = selected.clone().second(i);
982
+
983
+ var disabled = false;
984
+ if (minDate && time.isBefore(minDate))
985
+ disabled = true;
986
+ if (maxDate && time.isAfter(maxDate))
987
+ disabled = true;
988
+
989
+ if (selected.second() == i && !disabled) {
990
+ html += '<option value="' + i + '" selected="selected">' + padded + '</option>';
991
+ } else if (disabled) {
992
+ html += '<option value="' + i + '" disabled="disabled" class="disabled">' + padded + '</option>';
993
+ } else {
994
+ html += '<option value="' + i + '">' + padded + '</option>';
995
+ }
996
+ }
997
+
998
+ html += '</select> ';
999
+ }
1000
+
1001
+ //
1002
+ // AM/PM
1003
+ //
1004
+
1005
+ if (!this.timePicker24Hour) {
1006
+ html += '<select class="ampmselect">';
1007
+
1008
+ var am_html = '';
1009
+ var pm_html = '';
1010
+
1011
+ if (minDate && selected.clone().hour(12).minute(0).second(0).isBefore(minDate))
1012
+ am_html = ' disabled="disabled" class="disabled"';
1013
+
1014
+ if (maxDate && selected.clone().hour(0).minute(0).second(0).isAfter(maxDate))
1015
+ pm_html = ' disabled="disabled" class="disabled"';
1016
+
1017
+ if (selected.hour() >= 12) {
1018
+ html += '<option value="AM"' + am_html + '>AM</option><option value="PM" selected="selected"' + pm_html + '>PM</option>';
1019
+ } else {
1020
+ html += '<option value="AM" selected="selected"' + am_html + '>AM</option><option value="PM"' + pm_html + '>PM</option>';
1021
+ }
1022
+
1023
+ html += '</select>';
1024
+ }
1025
+
1026
+ this.container.find('.drp-calendar.' + side + ' .calendar-time').html(html);
1027
+
1028
+ },
1029
+
1030
+ updateFormInputs: function() {
1031
+
1032
+ if (this.singleDatePicker || (this.endDate && (this.startDate.isBefore(this.endDate) || this.startDate.isSame(this.endDate)))) {
1033
+ this.container.find('button.applyBtn').prop('disabled', false);
1034
+ } else {
1035
+ this.container.find('button.applyBtn').prop('disabled', true);
1036
+ }
1037
+
1038
+ },
1039
+
1040
+ move: function() {
1041
+ var parentOffset = { top: 0, left: 0 },
1042
+ containerTop,
1043
+ drops = this.drops;
1044
+
1045
+ var parentRightEdge = $(window).width();
1046
+ if (!this.parentEl.is('body')) {
1047
+ parentOffset = {
1048
+ top: this.parentEl.offset().top - this.parentEl.scrollTop(),
1049
+ left: this.parentEl.offset().left - this.parentEl.scrollLeft()
1050
+ };
1051
+ parentRightEdge = this.parentEl[0].clientWidth + this.parentEl.offset().left;
1052
+ }
1053
+
1054
+ switch (drops) {
1055
+ case 'auto':
1056
+ containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
1057
+ if (containerTop + this.container.outerHeight() >= this.parentEl[0].scrollHeight) {
1058
+ containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
1059
+ drops = 'up';
1060
+ }
1061
+ break;
1062
+ case 'up':
1063
+ containerTop = this.element.offset().top - this.container.outerHeight() - parentOffset.top;
1064
+ break;
1065
+ default:
1066
+ containerTop = this.element.offset().top + this.element.outerHeight() - parentOffset.top;
1067
+ break;
1068
+ }
1069
+
1070
+ // Force the container to it's actual width
1071
+ this.container.css({
1072
+ top: 0,
1073
+ left: 0,
1074
+ right: 'auto'
1075
+ });
1076
+ var containerWidth = this.container.outerWidth();
1077
+
1078
+ this.container.toggleClass('drop-up', drops == 'up');
1079
+
1080
+ if (this.opens == 'left') {
1081
+ var containerRight = parentRightEdge - this.element.offset().left - this.element.outerWidth();
1082
+ if (containerWidth + containerRight > $(window).width()) {
1083
+ this.container.css({
1084
+ top: containerTop,
1085
+ right: 'auto',
1086
+ left: 9
1087
+ });
1088
+ } else {
1089
+ this.container.css({
1090
+ top: containerTop,
1091
+ right: containerRight,
1092
+ left: 'auto'
1093
+ });
1094
+ }
1095
+ } else if (this.opens == 'center') {
1096
+ var containerLeft = this.element.offset().left - parentOffset.left + this.element.outerWidth() / 2
1097
+ - containerWidth / 2;
1098
+ if (containerLeft < 0) {
1099
+ this.container.css({
1100
+ top: containerTop,
1101
+ right: 'auto',
1102
+ left: 9
1103
+ });
1104
+ } else if (containerLeft + containerWidth > $(window).width()) {
1105
+ this.container.css({
1106
+ top: containerTop,
1107
+ left: 'auto',
1108
+ right: 0
1109
+ });
1110
+ } else {
1111
+ this.container.css({
1112
+ top: containerTop,
1113
+ left: containerLeft,
1114
+ right: 'auto'
1115
+ });
1116
+ }
1117
+ } else {
1118
+ var containerLeft = this.element.offset().left - parentOffset.left;
1119
+ if (containerLeft + containerWidth > $(window).width()) {
1120
+ this.container.css({
1121
+ top: containerTop,
1122
+ left: 'auto',
1123
+ right: 0
1124
+ });
1125
+ } else {
1126
+ this.container.css({
1127
+ top: containerTop,
1128
+ left: containerLeft,
1129
+ right: 'auto'
1130
+ });
1131
+ }
1132
+ }
1133
+ },
1134
+
1135
+ show: function(e) {
1136
+ if (this.isShowing) return;
1137
+
1138
+ // Create a click proxy that is private to this instance of datepicker, for unbinding
1139
+ this._outsideClickProxy = $.proxy(function(e) { this.outsideClick(e); }, this);
1140
+
1141
+ // Bind global datepicker mousedown for hiding and
1142
+ $(document)
1143
+ .on('mousedown.daterangepicker', this._outsideClickProxy)
1144
+ // also support mobile devices
1145
+ .on('touchend.daterangepicker', this._outsideClickProxy)
1146
+ // also explicitly play nice with Bootstrap dropdowns, which stopPropagation when clicking them
1147
+ .on('click.daterangepicker', '[data-toggle=dropdown]', this._outsideClickProxy)
1148
+ // and also close when focus changes to outside the picker (eg. tabbing between controls)
1149
+ .on('focusin.daterangepicker', this._outsideClickProxy);
1150
+
1151
+ // Reposition the picker if the window is resized while it's open
1152
+ $(window).on('resize.daterangepicker', $.proxy(function(e) { this.move(e); }, this));
1153
+
1154
+ this.oldStartDate = this.startDate.clone();
1155
+ this.oldEndDate = this.endDate.clone();
1156
+ this.previousRightTime = this.endDate.clone();
1157
+
1158
+ this.updateView();
1159
+ this.container.show();
1160
+ this.move();
1161
+ this.element.trigger('show.daterangepicker', this);
1162
+ this.isShowing = true;
1163
+ },
1164
+
1165
+ hide: function(e) {
1166
+ if (!this.isShowing) return;
1167
+
1168
+ //incomplete date selection, revert to last values
1169
+ if (!this.endDate) {
1170
+ this.startDate = this.oldStartDate.clone();
1171
+ this.endDate = this.oldEndDate.clone();
1172
+ }
1173
+
1174
+ //if a new date range was selected, invoke the user callback function
1175
+ if (!this.startDate.isSame(this.oldStartDate) || !this.endDate.isSame(this.oldEndDate))
1176
+ this.callback(this.startDate.clone(), this.endDate.clone(), this.chosenLabel);
1177
+
1178
+ //if picker is attached to a text input, update it
1179
+ this.updateElement();
1180
+
1181
+ $(document).off('.daterangepicker');
1182
+ $(window).off('.daterangepicker');
1183
+ this.container.hide();
1184
+ this.element.trigger('hide.daterangepicker', this);
1185
+ this.isShowing = false;
1186
+ },
1187
+
1188
+ toggle: function(e) {
1189
+ if (this.isShowing) {
1190
+ this.hide();
1191
+ } else {
1192
+ this.show();
1193
+ }
1194
+ },
1195
+
1196
+ outsideClick: function(e) {
1197
+ var target = $(e.target);
1198
+ // if the page is clicked anywhere except within the daterangerpicker/button
1199
+ // itself then call this.hide()
1200
+ if (
1201
+ // ie modal dialog fix
1202
+ e.type == "focusin" ||
1203
+ target.closest(this.element).length ||
1204
+ target.closest(this.container).length ||
1205
+ target.closest('.calendar-table').length
1206
+ ) return;
1207
+ this.hide();
1208
+ this.element.trigger('outsideClick.daterangepicker', this);
1209
+ },
1210
+
1211
+ showCalendars: function() {
1212
+ this.container.addClass('show-calendar');
1213
+ this.move();
1214
+ this.element.trigger('showCalendar.daterangepicker', this);
1215
+ },
1216
+
1217
+ hideCalendars: function() {
1218
+ //this.container.removeClass('show-calendar');
1219
+ this.element.trigger('hideCalendar.daterangepicker', this);
1220
+
1221
+ },
1222
+
1223
+ clickRange: function(e) {
1224
+ var label = e.target.getAttribute('data-range-key');
1225
+ this.chosenLabel = label;
1226
+ if (label == this.locale.customRangeLabel) {
1227
+ this.showCalendars();
1228
+ } else {
1229
+ var dates = this.ranges[label];
1230
+ this.startDate = dates[0];
1231
+ this.endDate = dates[1];
1232
+
1233
+ if (!this.timePicker) {
1234
+ this.startDate.startOf('day');
1235
+ this.endDate.endOf('day');
1236
+ }
1237
+
1238
+ if (!this.alwaysShowCalendars)
1239
+ this.hideCalendars();
1240
+ this.clickApply();
1241
+ }
1242
+ },
1243
+
1244
+ clickPrev: function(e) {
1245
+ var cal = $(e.target).parents('.drp-calendar');
1246
+ if (cal.hasClass('left')) {
1247
+ this.leftCalendar.month.subtract(1, 'month');
1248
+ if (this.linkedCalendars)
1249
+ this.rightCalendar.month.subtract(1, 'month');
1250
+ } else {
1251
+ this.rightCalendar.month.subtract(1, 'month');
1252
+ }
1253
+ this.updateCalendars();
1254
+ },
1255
+
1256
+ clickNext: function(e) {
1257
+ var cal = $(e.target).parents('.drp-calendar');
1258
+ if (cal.hasClass('left')) {
1259
+ this.leftCalendar.month.add(1, 'month');
1260
+ } else {
1261
+ this.rightCalendar.month.add(1, 'month');
1262
+ if (this.linkedCalendars)
1263
+ this.leftCalendar.month.add(1, 'month');
1264
+ }
1265
+ this.updateCalendars();
1266
+ },
1267
+
1268
+ hoverDate: function(e) {
1269
+
1270
+ //ignore dates that can't be selected
1271
+ if (!$(e.target).hasClass('available')) return;
1272
+
1273
+ var title = $(e.target).attr('data-title');
1274
+ var row = title.substr(1, 1);
1275
+ var col = title.substr(3, 1);
1276
+ var cal = $(e.target).parents('.drp-calendar');
1277
+ var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1278
+
1279
+ //highlight the dates between the start date and the date being hovered as a potential end date
1280
+ var leftCalendar = this.leftCalendar;
1281
+ var rightCalendar = this.rightCalendar;
1282
+ var startDate = this.startDate;
1283
+ if (!this.endDate) {
1284
+ this.container.find('.drp-calendar tbody td').each(function(index, el) {
1285
+
1286
+ //skip week numbers, only look at dates
1287
+ if ($(el).hasClass('week')) return;
1288
+
1289
+ var title = $(el).attr('data-title');
1290
+ var row = title.substr(1, 1);
1291
+ var col = title.substr(3, 1);
1292
+ var cal = $(el).parents('.drp-calendar');
1293
+ var dt = cal.hasClass('left') ? leftCalendar.calendar[row][col] : rightCalendar.calendar[row][col];
1294
+
1295
+ if ((dt.isAfter(startDate) && dt.isBefore(date)) || dt.isSame(date, 'day')) {
1296
+ $(el).addClass('in-range');
1297
+ } else {
1298
+ $(el).removeClass('in-range');
1299
+ }
1300
+
1301
+ });
1302
+ }
1303
+
1304
+ },
1305
+
1306
+ clickDate: function(e) {
1307
+
1308
+ if (!$(e.target).hasClass('available')) return;
1309
+
1310
+ var title = $(e.target).attr('data-title');
1311
+ var row = title.substr(1, 1);
1312
+ var col = title.substr(3, 1);
1313
+ var cal = $(e.target).parents('.drp-calendar');
1314
+ var date = cal.hasClass('left') ? this.leftCalendar.calendar[row][col] : this.rightCalendar.calendar[row][col];
1315
+
1316
+ //
1317
+ // this function needs to do a few things:
1318
+ // * alternate between selecting a start and end date for the range,
1319
+ // * if the time picker is enabled, apply the hour/minute/second from the select boxes to the clicked date
1320
+ // * if autoapply is enabled, and an end date was chosen, apply the selection
1321
+ // * if single date picker mode, and time picker isn't enabled, apply the selection immediately
1322
+ // * if one of the inputs above the calendars was focused, cancel that manual input
1323
+ //
1324
+
1325
+ if (this.endDate || date.isBefore(this.startDate, 'day')) { //picking start
1326
+ if (this.timePicker) {
1327
+ var hour = parseInt(this.container.find('.left .hourselect').val(), 10);
1328
+ if (!this.timePicker24Hour) {
1329
+ var ampm = this.container.find('.left .ampmselect').val();
1330
+ if (ampm === 'PM' && hour < 12)
1331
+ hour += 12;
1332
+ if (ampm === 'AM' && hour === 12)
1333
+ hour = 0;
1334
+ }
1335
+ var minute = parseInt(this.container.find('.left .minuteselect').val(), 10);
1336
+ if (isNaN(minute)) {
1337
+ minute = parseInt(this.container.find('.left .minuteselect option:last').val(), 10);
1338
+ }
1339
+ var second = this.timePickerSeconds ? parseInt(this.container.find('.left .secondselect').val(), 10) : 0;
1340
+ date = date.clone().hour(hour).minute(minute).second(second);
1341
+ }
1342
+ this.endDate = null;
1343
+ this.setStartDate(date.clone());
1344
+ } else if (!this.endDate && date.isBefore(this.startDate)) {
1345
+ //special case: clicking the same date for start/end,
1346
+ //but the time of the end date is before the start date
1347
+ this.setEndDate(this.startDate.clone());
1348
+ } else { // picking end
1349
+ if (this.timePicker) {
1350
+ var hour = parseInt(this.container.find('.right .hourselect').val(), 10);
1351
+ if (!this.timePicker24Hour) {
1352
+ var ampm = this.container.find('.right .ampmselect').val();
1353
+ if (ampm === 'PM' && hour < 12)
1354
+ hour += 12;
1355
+ if (ampm === 'AM' && hour === 12)
1356
+ hour = 0;
1357
+ }
1358
+ var minute = parseInt(this.container.find('.right .minuteselect').val(), 10);
1359
+ if (isNaN(minute)) {
1360
+ minute = parseInt(this.container.find('.right .minuteselect option:last').val(), 10);
1361
+ }
1362
+ var second = this.timePickerSeconds ? parseInt(this.container.find('.right .secondselect').val(), 10) : 0;
1363
+ date = date.clone().hour(hour).minute(minute).second(second);
1364
+ }
1365
+ this.setEndDate(date.clone());
1366
+ if (this.autoApply) {
1367
+ this.calculateChosenLabel();
1368
+ this.clickApply();
1369
+ }
1370
+ }
1371
+
1372
+ if (this.singleDatePicker) {
1373
+ this.setEndDate(this.startDate);
1374
+ if (!this.timePicker && this.autoApply)
1375
+ this.clickApply();
1376
+ }
1377
+
1378
+ this.updateView();
1379
+
1380
+ //This is to cancel the blur event handler if the mouse was in one of the inputs
1381
+ e.stopPropagation();
1382
+
1383
+ },
1384
+
1385
+ calculateChosenLabel: function () {
1386
+ var customRange = true;
1387
+ var i = 0;
1388
+ for (var range in this.ranges) {
1389
+ if (this.timePicker) {
1390
+ var format = this.timePickerSeconds ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD HH:mm";
1391
+ //ignore times when comparing dates if time picker seconds is not enabled
1392
+ if (this.startDate.format(format) == this.ranges[range][0].format(format) && this.endDate.format(format) == this.ranges[range][1].format(format)) {
1393
+ customRange = false;
1394
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').attr('data-range-key');
1395
+ break;
1396
+ }
1397
+ } else {
1398
+ //ignore times when comparing dates if time picker is not enabled
1399
+ if (this.startDate.format('YYYY-MM-DD') == this.ranges[range][0].format('YYYY-MM-DD') && this.endDate.format('YYYY-MM-DD') == this.ranges[range][1].format('YYYY-MM-DD')) {
1400
+ customRange = false;
1401
+ this.chosenLabel = this.container.find('.ranges li:eq(' + i + ')').addClass('active').attr('data-range-key');
1402
+ break;
1403
+ }
1404
+ }
1405
+ i++;
1406
+ }
1407
+ if (customRange) {
1408
+ if (this.showCustomRangeLabel) {
1409
+ this.chosenLabel = this.container.find('.ranges li:last').addClass('active').attr('data-range-key');
1410
+ } else {
1411
+ this.chosenLabel = null;
1412
+ }
1413
+ this.showCalendars();
1414
+ }
1415
+ },
1416
+
1417
+ clickApply: function(e) {
1418
+ this.hide();
1419
+ this.element.trigger('apply.daterangepicker', this);
1420
+ },
1421
+
1422
+ clickCancel: function(e) {
1423
+ this.startDate = this.oldStartDate;
1424
+ this.endDate = this.oldEndDate;
1425
+ this.hide();
1426
+ this.element.trigger('cancel.daterangepicker', this);
1427
+ },
1428
+
1429
+ monthOrYearChanged: function(e) {
1430
+ var isLeft = $(e.target).closest('.drp-calendar').hasClass('left'),
1431
+ leftOrRight = isLeft ? 'left' : 'right',
1432
+ cal = this.container.find('.drp-calendar.'+leftOrRight);
1433
+
1434
+ // Month must be Number for new moment versions
1435
+ var month = parseInt(cal.find('.monthselect').val(), 10);
1436
+ var year = cal.find('.yearselect').val();
1437
+
1438
+ if (!isLeft) {
1439
+ if (year < this.startDate.year() || (year == this.startDate.year() && month < this.startDate.month())) {
1440
+ month = this.startDate.month();
1441
+ year = this.startDate.year();
1442
+ }
1443
+ }
1444
+
1445
+ if (this.minDate) {
1446
+ if (year < this.minDate.year() || (year == this.minDate.year() && month < this.minDate.month())) {
1447
+ month = this.minDate.month();
1448
+ year = this.minDate.year();
1449
+ }
1450
+ }
1451
+
1452
+ if (this.maxDate) {
1453
+ if (year > this.maxDate.year() || (year == this.maxDate.year() && month > this.maxDate.month())) {
1454
+ month = this.maxDate.month();
1455
+ year = this.maxDate.year();
1456
+ }
1457
+ }
1458
+
1459
+ if (isLeft) {
1460
+ this.leftCalendar.month.month(month).year(year);
1461
+ if (this.linkedCalendars)
1462
+ this.rightCalendar.month = this.leftCalendar.month.clone().add(1, 'month');
1463
+ } else {
1464
+ this.rightCalendar.month.month(month).year(year);
1465
+ if (this.linkedCalendars)
1466
+ this.leftCalendar.month = this.rightCalendar.month.clone().subtract(1, 'month');
1467
+ }
1468
+ this.updateCalendars();
1469
+ },
1470
+
1471
+ timeChanged: function(e) {
1472
+
1473
+ var cal = $(e.target).closest('.drp-calendar'),
1474
+ isLeft = cal.hasClass('left');
1475
+
1476
+ var hour = parseInt(cal.find('.hourselect').val(), 10);
1477
+ var minute = parseInt(cal.find('.minuteselect').val(), 10);
1478
+ if (isNaN(minute)) {
1479
+ minute = parseInt(cal.find('.minuteselect option:last').val(), 10);
1480
+ }
1481
+ var second = this.timePickerSeconds ? parseInt(cal.find('.secondselect').val(), 10) : 0;
1482
+
1483
+ if (!this.timePicker24Hour) {
1484
+ var ampm = cal.find('.ampmselect').val();
1485
+ if (ampm === 'PM' && hour < 12)
1486
+ hour += 12;
1487
+ if (ampm === 'AM' && hour === 12)
1488
+ hour = 0;
1489
+ }
1490
+
1491
+ if (isLeft) {
1492
+ var start = this.startDate.clone();
1493
+ start.hour(hour);
1494
+ start.minute(minute);
1495
+ start.second(second);
1496
+ this.setStartDate(start);
1497
+ if (this.singleDatePicker) {
1498
+ this.endDate = this.startDate.clone();
1499
+ } else if (this.endDate && this.endDate.format('YYYY-MM-DD') == start.format('YYYY-MM-DD') && this.endDate.isBefore(start)) {
1500
+ this.setEndDate(start.clone());
1501
+ }
1502
+ } else if (this.endDate) {
1503
+ var end = this.endDate.clone();
1504
+ end.hour(hour);
1505
+ end.minute(minute);
1506
+ end.second(second);
1507
+ this.setEndDate(end);
1508
+ }
1509
+
1510
+ //update the calendars so all clickable dates reflect the new time component
1511
+ this.updateCalendars();
1512
+
1513
+ //update the form inputs above the calendars with the new time
1514
+ this.updateFormInputs();
1515
+
1516
+ //re-render the time pickers because changing one selection can affect what's enabled in another
1517
+ this.renderTimePicker('left');
1518
+ this.renderTimePicker('right');
1519
+
1520
+ },
1521
+
1522
+ elementChanged: function() {
1523
+ if (!this.element.is('input')) return;
1524
+ if (!this.element.val().length) return;
1525
+
1526
+ var dateString = this.element.val().split(this.locale.separator),
1527
+ start = null,
1528
+ end = null;
1529
+
1530
+ if (dateString.length === 2) {
1531
+ start = moment(dateString[0], this.locale.format);
1532
+ end = moment(dateString[1], this.locale.format);
1533
+ }
1534
+
1535
+ if (this.singleDatePicker || start === null || end === null) {
1536
+ start = moment(this.element.val(), this.locale.format);
1537
+ end = start;
1538
+ }
1539
+
1540
+ if (!start.isValid() || !end.isValid()) return;
1541
+
1542
+ this.setStartDate(start);
1543
+ this.setEndDate(end);
1544
+ this.updateView();
1545
+ },
1546
+
1547
+ keydown: function(e) {
1548
+ //hide on tab or enter
1549
+ if ((e.keyCode === 9) || (e.keyCode === 13)) {
1550
+ this.hide();
1551
+ }
1552
+
1553
+ //hide on esc and prevent propagation
1554
+ if (e.keyCode === 27) {
1555
+ e.preventDefault();
1556
+ e.stopPropagation();
1557
+
1558
+ this.hide();
1559
+ }
1560
+ },
1561
+
1562
+ updateElement: function() {
1563
+ if (this.element.is('input') && this.autoUpdateInput) {
1564
+ var newValue = this.startDate.format(this.locale.format);
1565
+ if (!this.singleDatePicker) {
1566
+ newValue += this.locale.separator + this.endDate.format(this.locale.format);
1567
+ }
1568
+ if (newValue !== this.element.val()) {
1569
+ this.element.val(newValue).trigger('change');
1570
+ }
1571
+ }
1572
+ },
1573
+
1574
+ remove: function() {
1575
+ this.container.remove();
1576
+ this.element.off('.daterangepicker');
1577
+ this.element.removeData();
1578
+ }
1579
+
1580
+ };
1581
+
1582
+ $.fn.daterangepicker = function(options, callback) {
1583
+ var implementOptions = $.extend(true, {}, $.fn.daterangepicker.defaultOptions, options);
1584
+ this.each(function() {
1585
+ var el = $(this);
1586
+ if (el.data('daterangepicker'))
1587
+ el.data('daterangepicker').remove();
1588
+ el.data('daterangepicker', new DateRangePicker(el, implementOptions, callback));
1589
+ });
1590
+ return this;
1591
+ };
1592
+
1593
+ return DateRangePicker;
1594
+
1595
+ }));
admin/js/jquery.basictable.min.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ /*
2
+ * @license jQuery Basictable | MIT | Jerry Low | https://www.github.com/jerrylow/basictable
3
+ */
4
+ !function(t){t.fn.basictable=function(a){var e=function(a,e){var r=[];if(e.tableWrap&&a.wrap('<div class="bt-wrapper"></div>'),e.header){var i="";i=a.find("thead tr th").length?"thead th":a.find("tbody tr th").length?"tbody tr th":a.find("th").length?"tr:first th":"tr:first td",t.each(a.find(i),function(){var a=t(this),e=parseInt(a.attr("colspan"),10)||1,n=a.closest("tr").index();r[n]||(r[n]=[]);for(var i=0;i<e;i++)r[n].push(a)})}t.each(a.find("tbody tr"),function(){n(t(this),r,e)}),t.each(a.find("tfoot tr"),function(){n(t(this),r,e)})},n=function(a,e,n){a.children().each(function(){var a=t(this);if(""!==a.html()&&"&nbsp;"!==a.html()||n.showEmptyCells){for(var r=a.index(),i="",o=0;o<e.length;o++){0!=o&&(i+=": "),i+=e[o][r].text()}a.attr("data-th",i),n.contentWrap&&!a.children().hasClass("bt-content")&&a.wrapInner('<span class="bt-content" />')}else a.addClass("bt-hide")})},r=function(a,e){e.forceResponsive?null!==e.breakpoint&&t(window).width()<=e.breakpoint||null!==e.containerBreakpoint&&a.parent().width()<=e.containerBreakpoint?i(a,e):o(a,e):a.removeClass("bt").outerWidth()>a.parent().width()?i(a,e):o(a,e)},i=function(t,a){t.addClass("bt"),a.header||t.addClass("bt--no-header"),a.tableWrap&&t.parent(".bt-wrapper").addClass("active")},o=function(t,a){t.removeClass("bt bt--no-header"),a.tableWrap&&t.parent(".bt-wrapper").removeClass("active")},s=function(a,e){var n;a.removeClass("bt bt--no-header"),a.find("td").removeAttr("data-th"),e.tableWrap&&a.unwrap(),e.contentWrap&&(n=a,t.each(n.find("td"),function(){var a=t(this),e=a.children(".bt-content").html();a.html(e)})),a.removeData("basictable")};this.each(function(){var n=t(this);if(0===n.length||n.data("basictable")){if(n.data("basictable")){var l=n.data("basictable");"destroy"===a?s(n,l):"restart"===a?(s(n,l),n.data("basictable",l),e(n,l),r(n,l)):"start"===a?i(n,l):"stop"===a?o(n,l):r(n,l)}return!1}var c=t.extend({},t.fn.basictable.defaults,a),d={breakpoint:c.breakpoint,containerBreakpoint:c.containerBreakpoint,contentWrap:c.contentWrap,forceResponsive:c.forceResponsive,noResize:c.noResize,tableWrap:c.tableWrap,showEmptyCells:c.showEmptyCells,header:c.header};null===d.breakpoint&&null===d.containerBreakpoint&&(d.breakpoint=568),n.data("basictable",d),e(n,n.data("basictable")),d.noResize||(r(n,n.data("basictable")),t(window).bind("resize.basictable",function(){var t;(t=n).data("basictable")&&r(t,t.data("basictable"))}))})},t.fn.basictable.defaults={breakpoint:null,containerBreakpoint:null,contentWrap:!0,forceResponsive:!0,noResize:!1,tableWrap:!1,showEmptyCells:!1,header:!0}}(jQuery);
admin/js/moment.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var e,i;function c(){return e.apply(null,arguments)}function o(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function u(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function l(e){return void 0===e}function d(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function h(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function f(e,t){var n,s=[];for(n=0;n<e.length;++n)s.push(t(e[n],n));return s}function m(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function _(e,t){for(var n in t)m(t,n)&&(e[n]=t[n]);return m(t,"toString")&&(e.toString=t.toString),m(t,"valueOf")&&(e.valueOf=t.valueOf),e}function y(e,t,n,s){return Ot(e,t,n,s,!0).utc()}function g(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function p(e){if(null==e._isValid){var t=g(e),n=i.call(t.parsedDateParts,function(e){return null!=e}),s=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(s=s&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return s;e._isValid=s}return e._isValid}function v(e){var t=y(NaN);return null!=e?_(g(t),e):g(t).userInvalidated=!0,t}i=Array.prototype.some?Array.prototype.some:function(e){for(var t=Object(this),n=t.length>>>0,s=0;s<n;s++)if(s in t&&e.call(this,t[s],s,t))return!0;return!1};var r=c.momentProperties=[];function w(e,t){var n,s,i;if(l(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),l(t._i)||(e._i=t._i),l(t._f)||(e._f=t._f),l(t._l)||(e._l=t._l),l(t._strict)||(e._strict=t._strict),l(t._tzm)||(e._tzm=t._tzm),l(t._isUTC)||(e._isUTC=t._isUTC),l(t._offset)||(e._offset=t._offset),l(t._pf)||(e._pf=g(t)),l(t._locale)||(e._locale=t._locale),0<r.length)for(n=0;n<r.length;n++)l(i=t[s=r[n]])||(e[s]=i);return e}var t=!1;function M(e){w(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===t&&(t=!0,c.updateOffset(this),t=!1)}function S(e){return e instanceof M||null!=e&&null!=e._isAMomentObject}function D(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function k(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=D(t)),n}function a(e,t,n){var s,i=Math.min(e.length,t.length),r=Math.abs(e.length-t.length),a=0;for(s=0;s<i;s++)(n&&e[s]!==t[s]||!n&&k(e[s])!==k(t[s]))&&a++;return a+r}function Y(e){!1===c.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function n(i,r){var a=!0;return _(function(){if(null!=c.deprecationHandler&&c.deprecationHandler(null,i),a){for(var e,t=[],n=0;n<arguments.length;n++){if(e="","object"==typeof arguments[n]){for(var s in e+="\n["+n+"] ",arguments[0])e+=s+": "+arguments[0][s]+", ";e=e.slice(0,-2)}else e=arguments[n];t.push(e)}Y(i+"\nArguments: "+Array.prototype.slice.call(t).join("")+"\n"+(new Error).stack),a=!1}return r.apply(this,arguments)},r)}var s,O={};function T(e,t){null!=c.deprecationHandler&&c.deprecationHandler(e,t),O[e]||(Y(t),O[e]=!0)}function x(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function b(e,t){var n,s=_({},e);for(n in t)m(t,n)&&(u(e[n])&&u(t[n])?(s[n]={},_(s[n],e[n]),_(s[n],t[n])):null!=t[n]?s[n]=t[n]:delete s[n]);for(n in e)m(e,n)&&!m(t,n)&&u(e[n])&&(s[n]=_({},s[n]));return s}function P(e){null!=e&&this.set(e)}c.suppressDeprecationWarnings=!1,c.deprecationHandler=null,s=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)m(e,t)&&n.push(t);return n};var W={};function H(e,t){var n=e.toLowerCase();W[n]=W[n+"s"]=W[t]=e}function R(e){return"string"==typeof e?W[e]||W[e.toLowerCase()]:void 0}function C(e){var t,n,s={};for(n in e)m(e,n)&&(t=R(n))&&(s[t]=e[n]);return s}var F={};function L(e,t){F[e]=t}function U(e,t,n){var s=""+Math.abs(e),i=t-s.length;return(0<=e?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+s}var N=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,G=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,V={},E={};function I(e,t,n,s){var i=s;"string"==typeof s&&(i=function(){return this[s]()}),e&&(E[e]=i),t&&(E[t[0]]=function(){return U(i.apply(this,arguments),t[1],t[2])}),n&&(E[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function A(e,t){return e.isValid()?(t=j(t,e.localeData()),V[t]=V[t]||function(s){var e,i,t,r=s.match(N);for(e=0,i=r.length;e<i;e++)E[r[e]]?r[e]=E[r[e]]:r[e]=(t=r[e]).match(/\[[\s\S]/)?t.replace(/^\[|\]$/g,""):t.replace(/\\/g,"");return function(e){var t,n="";for(t=0;t<i;t++)n+=x(r[t])?r[t].call(e,s):r[t];return n}}(t),V[t](e)):e.localeData().invalidDate()}function j(e,t){var n=5;function s(e){return t.longDateFormat(e)||e}for(G.lastIndex=0;0<=n&&G.test(e);)e=e.replace(G,s),G.lastIndex=0,n-=1;return e}var Z=/\d/,z=/\d\d/,$=/\d{3}/,q=/\d{4}/,J=/[+-]?\d{6}/,B=/\d\d?/,Q=/\d\d\d\d?/,X=/\d\d\d\d\d\d?/,K=/\d{1,3}/,ee=/\d{1,4}/,te=/[+-]?\d{1,6}/,ne=/\d+/,se=/[+-]?\d+/,ie=/Z|[+-]\d\d:?\d\d/gi,re=/Z|[+-]\d\d(?::?\d\d)?/gi,ae=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,oe={};function ue(e,n,s){oe[e]=x(n)?n:function(e,t){return e&&s?s:n}}function le(e,t){return m(oe,e)?oe[e](t._strict,t._locale):new RegExp(de(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i})))}function de(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var he={};function ce(e,n){var t,s=n;for("string"==typeof e&&(e=[e]),d(n)&&(s=function(e,t){t[n]=k(e)}),t=0;t<e.length;t++)he[e[t]]=s}function fe(e,i){ce(e,function(e,t,n,s){n._w=n._w||{},i(e,n._w,n,s)})}var me=0,_e=1,ye=2,ge=3,pe=4,ve=5,we=6,Me=7,Se=8;function De(e){return ke(e)?366:365}function ke(e){return e%4==0&&e%100!=0||e%400==0}I("Y",0,0,function(){var e=this.year();return e<=9999?""+e:"+"+e}),I(0,["YY",2],0,function(){return this.year()%100}),I(0,["YYYY",4],0,"year"),I(0,["YYYYY",5],0,"year"),I(0,["YYYYYY",6,!0],0,"year"),H("year","y"),L("year",1),ue("Y",se),ue("YY",B,z),ue("YYYY",ee,q),ue("YYYYY",te,J),ue("YYYYYY",te,J),ce(["YYYYY","YYYYYY"],me),ce("YYYY",function(e,t){t[me]=2===e.length?c.parseTwoDigitYear(e):k(e)}),ce("YY",function(e,t){t[me]=c.parseTwoDigitYear(e)}),ce("Y",function(e,t){t[me]=parseInt(e,10)}),c.parseTwoDigitYear=function(e){return k(e)+(68<k(e)?1900:2e3)};var Ye,Oe=Te("FullYear",!0);function Te(t,n){return function(e){return null!=e?(be(this,t,e),c.updateOffset(this,n),this):xe(this,t)}}function xe(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function be(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&ke(e.year())&&1===e.month()&&29===e.date()?e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),Pe(n,e.month())):e._d["set"+(e._isUTC?"UTC":"")+t](n))}function Pe(e,t){if(isNaN(e)||isNaN(t))return NaN;var n,s=(t%(n=12)+n)%n;return e+=(t-s)/12,1===s?ke(e)?29:28:31-s%7%2}Ye=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t<this.length;++t)if(this[t]===e)return t;return-1},I("M",["MM",2],"Mo",function(){return this.month()+1}),I("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),I("MMMM",0,0,function(e){return this.localeData().months(this,e)}),H("month","M"),L("month",8),ue("M",B),ue("MM",B,z),ue("MMM",function(e,t){return t.monthsShortRegex(e)}),ue("MMMM",function(e,t){return t.monthsRegex(e)}),ce(["M","MM"],function(e,t){t[_e]=k(e)-1}),ce(["MMM","MMMM"],function(e,t,n,s){var i=n._locale.monthsParse(e,s,n._strict);null!=i?t[_e]=i:g(n).invalidMonth=e});var We=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,He="January_February_March_April_May_June_July_August_September_October_November_December".split("_");var Re="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function Ce(e,t){var n;if(!e.isValid())return e;if("string"==typeof t)if(/^\d+$/.test(t))t=k(t);else if(!d(t=e.localeData().monthsParse(t)))return e;return n=Math.min(e.date(),Pe(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n),e}function Fe(e){return null!=e?(Ce(this,e),c.updateOffset(this,!0),this):xe(this,"Month")}var Le=ae;var Ue=ae;function Ne(){function e(e,t){return t.length-e.length}var t,n,s=[],i=[],r=[];for(t=0;t<12;t++)n=y([2e3,t]),s.push(this.monthsShort(n,"")),i.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(s.sort(e),i.sort(e),r.sort(e),t=0;t<12;t++)s[t]=de(s[t]),i[t]=de(i[t]);for(t=0;t<24;t++)r[t]=de(r[t]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+s.join("|")+")","i")}function Ge(e){var t=new Date(Date.UTC.apply(null,arguments));return e<100&&0<=e&&isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e),t}function Ve(e,t,n){var s=7+t-n;return-((7+Ge(e,0,s).getUTCDay()-t)%7)+s-1}function Ee(e,t,n,s,i){var r,a,o=1+7*(t-1)+(7+n-s)%7+Ve(e,s,i);return o<=0?a=De(r=e-1)+o:o>De(e)?(r=e+1,a=o-De(e)):(r=e,a=o),{year:r,dayOfYear:a}}function Ie(e,t,n){var s,i,r=Ve(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+Ae(i=e.year()-1,t,n):a>Ae(e.year(),t,n)?(s=a-Ae(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function Ae(e,t,n){var s=Ve(e,t,n),i=Ve(e+1,t,n);return(De(e)-s+i)/7}I("w",["ww",2],"wo","week"),I("W",["WW",2],"Wo","isoWeek"),H("week","w"),H("isoWeek","W"),L("week",5),L("isoWeek",5),ue("w",B),ue("ww",B,z),ue("W",B),ue("WW",B,z),fe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=k(e)});I("d",0,"do","day"),I("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),I("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),I("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),I("e",0,0,"weekday"),I("E",0,0,"isoWeekday"),H("day","d"),H("weekday","e"),H("isoWeekday","E"),L("day",11),L("weekday",11),L("isoWeekday",11),ue("d",B),ue("e",B),ue("E",B),ue("dd",function(e,t){return t.weekdaysMinRegex(e)}),ue("ddd",function(e,t){return t.weekdaysShortRegex(e)}),ue("dddd",function(e,t){return t.weekdaysRegex(e)}),fe(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:g(n).invalidWeekday=e}),fe(["d","e","E"],function(e,t,n,s){t[s]=k(e)});var je="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_");var Ze="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_");var ze="Su_Mo_Tu_We_Th_Fr_Sa".split("_");var $e=ae;var qe=ae;var Je=ae;function Be(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=y([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=de(o[t]),u[t]=de(u[t]),l[t]=de(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function Qe(){return this.hours()%12||12}function Xe(e,t){I(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function Ke(e,t){return t._meridiemParse}I("H",["HH",2],0,"hour"),I("h",["hh",2],0,Qe),I("k",["kk",2],0,function(){return this.hours()||24}),I("hmm",0,0,function(){return""+Qe.apply(this)+U(this.minutes(),2)}),I("hmmss",0,0,function(){return""+Qe.apply(this)+U(this.minutes(),2)+U(this.seconds(),2)}),I("Hmm",0,0,function(){return""+this.hours()+U(this.minutes(),2)}),I("Hmmss",0,0,function(){return""+this.hours()+U(this.minutes(),2)+U(this.seconds(),2)}),Xe("a",!0),Xe("A",!1),H("hour","h"),L("hour",13),ue("a",Ke),ue("A",Ke),ue("H",B),ue("h",B),ue("k",B),ue("HH",B,z),ue("hh",B,z),ue("kk",B,z),ue("hmm",Q),ue("hmmss",X),ue("Hmm",Q),ue("Hmmss",X),ce(["H","HH"],ge),ce(["k","kk"],function(e,t,n){var s=k(e);t[ge]=24===s?0:s}),ce(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),ce(["h","hh"],function(e,t,n){t[ge]=k(e),g(n).bigHour=!0}),ce("hmm",function(e,t,n){var s=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s)),g(n).bigHour=!0}),ce("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s,2)),t[ve]=k(e.substr(i)),g(n).bigHour=!0}),ce("Hmm",function(e,t,n){var s=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s))}),ce("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[ge]=k(e.substr(0,s)),t[pe]=k(e.substr(s,2)),t[ve]=k(e.substr(i))});var et,tt=Te("Hours",!0),nt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:He,monthsShort:Re,week:{dow:0,doy:6},weekdays:je,weekdaysMin:ze,weekdaysShort:Ze,meridiemParse:/[ap]\.?m?\.?/i},st={},it={};function rt(e){return e?e.toLowerCase().replace("_","-"):e}function at(e){var t=null;if(!st[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=et._abbr,require("./locale/"+e),ot(t)}catch(e){}return st[e]}function ot(e,t){var n;return e&&((n=l(t)?lt(e):ut(e,t))?et=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),et._abbr}function ut(e,t){if(null!==t){var n,s=nt;if(t.abbr=e,null!=st[e])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=st[e]._config;else if(null!=t.parentLocale)if(null!=st[t.parentLocale])s=st[t.parentLocale]._config;else{if(null==(n=at(t.parentLocale)))return it[t.parentLocale]||(it[t.parentLocale]=[]),it[t.parentLocale].push({name:e,config:t}),null;s=n._config}return st[e]=new P(b(s,t)),it[e]&&it[e].forEach(function(e){ut(e.name,e.config)}),ot(e),st[e]}return delete st[e],null}function lt(e){var t;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return et;if(!o(e)){if(t=at(e))return t;e=[e]}return function(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=rt(e[r]).split("-")).length,n=(n=rt(e[r+1]))?n.split("-"):null;0<t;){if(s=at(i.slice(0,t).join("-")))return s;if(n&&n.length>=t&&a(i,n,!0)>=t-1)break;t--}r++}return et}(e)}function dt(e){var t,n=e._a;return n&&-2===g(e).overflow&&(t=n[_e]<0||11<n[_e]?_e:n[ye]<1||n[ye]>Pe(n[me],n[_e])?ye:n[ge]<0||24<n[ge]||24===n[ge]&&(0!==n[pe]||0!==n[ve]||0!==n[we])?ge:n[pe]<0||59<n[pe]?pe:n[ve]<0||59<n[ve]?ve:n[we]<0||999<n[we]?we:-1,g(e)._overflowDayOfYear&&(t<me||ye<t)&&(t=ye),g(e)._overflowWeeks&&-1===t&&(t=Me),g(e)._overflowWeekday&&-1===t&&(t=Se),g(e).overflow=t),e}function ht(e,t,n){return null!=e?e:null!=t?t:n}function ct(e){var t,n,s,i,r,a=[];if(!e._d){var o,u;for(o=e,u=new Date(c.now()),s=o._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],e._w&&null==e._a[ye]&&null==e._a[_e]&&function(e){var t,n,s,i,r,a,o,u;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)r=1,a=4,n=ht(t.GG,e._a[me],Ie(Tt(),1,4).year),s=ht(t.W,1),((i=ht(t.E,1))<1||7<i)&&(u=!0);else{r=e._locale._week.dow,a=e._locale._week.doy;var l=Ie(Tt(),r,a);n=ht(t.gg,e._a[me],l.year),s=ht(t.w,l.week),null!=t.d?((i=t.d)<0||6<i)&&(u=!0):null!=t.e?(i=t.e+r,(t.e<0||6<t.e)&&(u=!0)):i=r}s<1||s>Ae(n,r,a)?g(e)._overflowWeeks=!0:null!=u?g(e)._overflowWeekday=!0:(o=Ee(n,s,i,r,a),e._a[me]=o.year,e._dayOfYear=o.dayOfYear)}(e),null!=e._dayOfYear&&(r=ht(e._a[me],s[me]),(e._dayOfYear>De(r)||0===e._dayOfYear)&&(g(e)._overflowDayOfYear=!0),n=Ge(r,0,e._dayOfYear),e._a[_e]=n.getUTCMonth(),e._a[ye]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=a[t]=s[t];for(;t<7;t++)e._a[t]=a[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[ge]&&0===e._a[pe]&&0===e._a[ve]&&0===e._a[we]&&(e._nextDay=!0,e._a[ge]=0),e._d=(e._useUTC?Ge:function(e,t,n,s,i,r,a){var o=new Date(e,t,n,s,i,r,a);return e<100&&0<=e&&isFinite(o.getFullYear())&&o.setFullYear(e),o}).apply(null,a),i=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[ge]=24),e._w&&void 0!==e._w.d&&e._w.d!==i&&(g(e).weekdayMismatch=!0)}}var ft=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,mt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_t=/Z|[+-]\d\d(?::?\d\d)?/,yt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],gt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],pt=/^\/?Date\((\-?\d+)/i;function vt(e){var t,n,s,i,r,a,o=e._i,u=ft.exec(o)||mt.exec(o);if(u){for(g(e).iso=!0,t=0,n=yt.length;t<n;t++)if(yt[t][1].exec(u[1])){i=yt[t][0],s=!1!==yt[t][2];break}if(null==i)return void(e._isValid=!1);if(u[3]){for(t=0,n=gt.length;t<n;t++)if(gt[t][1].exec(u[3])){r=(u[2]||" ")+gt[t][0];break}if(null==r)return void(e._isValid=!1)}if(!s&&null!=r)return void(e._isValid=!1);if(u[4]){if(!_t.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=i+(r||"")+(a||""),kt(e)}else e._isValid=!1}var wt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;function Mt(e,t,n,s,i,r){var a=[function(e){var t=parseInt(e,10);{if(t<=49)return 2e3+t;if(t<=999)return 1900+t}return t}(e),Re.indexOf(t),parseInt(n,10),parseInt(s,10),parseInt(i,10)];return r&&a.push(parseInt(r,10)),a}var St={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Dt(e){var t,n,s,i=wt.exec(e._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").trim());if(i){var r=Mt(i[4],i[3],i[2],i[5],i[6],i[7]);if(t=i[1],n=r,s=e,t&&Ze.indexOf(t)!==new Date(n[0],n[1],n[2]).getDay()&&(g(s).weekdayMismatch=!0,!(s._isValid=!1)))return;e._a=r,e._tzm=function(e,t,n){if(e)return St[e];if(t)return 0;var s=parseInt(n,10),i=s%100;return(s-i)/100*60+i}(i[8],i[9],i[10]),e._d=Ge.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),g(e).rfc2822=!0}else e._isValid=!1}function kt(e){if(e._f!==c.ISO_8601)if(e._f!==c.RFC_2822){e._a=[],g(e).empty=!0;var t,n,s,i,r,a,o,u,l=""+e._i,d=l.length,h=0;for(s=j(e._f,e._locale).match(N)||[],t=0;t<s.length;t++)i=s[t],(n=(l.match(le(i,e))||[])[0])&&(0<(r=l.substr(0,l.indexOf(n))).length&&g(e).unusedInput.push(r),l=l.slice(l.indexOf(n)+n.length),h+=n.length),E[i]?(n?g(e).empty=!1:g(e).unusedTokens.push(i),a=i,u=e,null!=(o=n)&&m(he,a)&&he[a](o,u._a,u,a)):e._strict&&!n&&g(e).unusedTokens.push(i);g(e).charsLeftOver=d-h,0<l.length&&g(e).unusedInput.push(l),e._a[ge]<=12&&!0===g(e).bigHour&&0<e._a[ge]&&(g(e).bigHour=void 0),g(e).parsedDateParts=e._a.slice(0),g(e).meridiem=e._meridiem,e._a[ge]=function(e,t,n){var s;if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):(null!=e.isPM&&((s=e.isPM(n))&&t<12&&(t+=12),s||12!==t||(t=0)),t)}(e._locale,e._a[ge],e._meridiem),ct(e),dt(e)}else Dt(e);else vt(e)}function Yt(e){var t,n,s,i,r=e._i,a=e._f;return e._locale=e._locale||lt(e._l),null===r||void 0===a&&""===r?v({nullInput:!0}):("string"==typeof r&&(e._i=r=e._locale.preparse(r)),S(r)?new M(dt(r)):(h(r)?e._d=r:o(a)?function(e){var t,n,s,i,r;if(0===e._f.length)return g(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<e._f.length;i++)r=0,t=w({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],kt(t),p(t)&&(r+=g(t).charsLeftOver,r+=10*g(t).unusedTokens.length,g(t).score=r,(null==s||r<s)&&(s=r,n=t));_(e,n||t)}(e):a?kt(e):l(n=(t=e)._i)?t._d=new Date(c.now()):h(n)?t._d=new Date(n.valueOf()):"string"==typeof n?(s=t,null===(i=pt.exec(s._i))?(vt(s),!1===s._isValid&&(delete s._isValid,Dt(s),!1===s._isValid&&(delete s._isValid,c.createFromInputFallback(s)))):s._d=new Date(+i[1])):o(n)?(t._a=f(n.slice(0),function(e){return parseInt(e,10)}),ct(t)):u(n)?function(e){if(!e._d){var t=C(e._i);e._a=f([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),ct(e)}}(t):d(n)?t._d=new Date(n):c.createFromInputFallback(t),p(e)||(e._d=null),e))}function Ot(e,t,n,s,i){var r,a={};return!0!==n&&!1!==n||(s=n,n=void 0),(u(e)&&function(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}(e)||o(e)&&0===e.length)&&(e=void 0),a._isAMomentObject=!0,a._useUTC=a._isUTC=i,a._l=n,a._i=e,a._f=t,a._strict=s,(r=new M(dt(Yt(a))))._nextDay&&(r.add(1,"d"),r._nextDay=void 0),r}function Tt(e,t,n,s){return Ot(e,t,n,s,!1)}c.createFromInputFallback=n("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(e){e._d=new Date(e._i+(e._useUTC?" UTC":""))}),c.ISO_8601=function(){},c.RFC_2822=function(){};var xt=n("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=Tt.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:v()}),bt=n("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=Tt.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:v()});function Pt(e,t){var n,s;if(1===t.length&&o(t[0])&&(t=t[0]),!t.length)return Tt();for(n=t[0],s=1;s<t.length;++s)t[s].isValid()&&!t[s][e](n)||(n=t[s]);return n}var Wt=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ht(e){var t=C(e),n=t.year||0,s=t.quarter||0,i=t.month||0,r=t.week||0,a=t.day||0,o=t.hour||0,u=t.minute||0,l=t.second||0,d=t.millisecond||0;this._isValid=function(e){for(var t in e)if(-1===Ye.call(Wt,t)||null!=e[t]&&isNaN(e[t]))return!1;for(var n=!1,s=0;s<Wt.length;++s)if(e[Wt[s]]){if(n)return!1;parseFloat(e[Wt[s]])!==k(e[Wt[s]])&&(n=!0)}return!0}(t),this._milliseconds=+d+1e3*l+6e4*u+1e3*o*60*60,this._days=+a+7*r,this._months=+i+3*s+12*n,this._data={},this._locale=lt(),this._bubble()}function Rt(e){return e instanceof Ht}function Ct(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Ft(e,n){I(e,0,0,function(){var e=this.utcOffset(),t="+";return e<0&&(e=-e,t="-"),t+U(~~(e/60),2)+n+U(~~e%60,2)})}Ft("Z",":"),Ft("ZZ",""),ue("Z",re),ue("ZZ",re),ce(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Ut(re,e)});var Lt=/([\+\-]|\d\d)/gi;function Ut(e,t){var n=(t||"").match(e);if(null===n)return null;var s=((n[n.length-1]||[])+"").match(Lt)||["-",0,0],i=60*s[1]+k(s[2]);return 0===i?0:"+"===s[0]?i:-i}function Nt(e,t){var n,s;return t._isUTC?(n=t.clone(),s=(S(e)||h(e)?e.valueOf():Tt(e).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+s),c.updateOffset(n,!1),n):Tt(e).local()}function Gt(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function Vt(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}c.updateOffset=function(){};var Et=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,It=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function At(e,t){var n,s,i,r=e,a=null;return Rt(e)?r={ms:e._milliseconds,d:e._days,M:e._months}:d(e)?(r={},t?r[t]=e:r.milliseconds=e):(a=Et.exec(e))?(n="-"===a[1]?-1:1,r={y:0,d:k(a[ye])*n,h:k(a[ge])*n,m:k(a[pe])*n,s:k(a[ve])*n,ms:k(Ct(1e3*a[we]))*n}):(a=It.exec(e))?(n="-"===a[1]?-1:(a[1],1),r={y:jt(a[2],n),M:jt(a[3],n),w:jt(a[4],n),d:jt(a[5],n),h:jt(a[6],n),m:jt(a[7],n),s:jt(a[8],n)}):null==r?r={}:"object"==typeof r&&("from"in r||"to"in r)&&(i=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=Nt(t,e),e.isBefore(t)?n=Zt(e,t):((n=Zt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(Tt(r.from),Tt(r.to)),(r={}).ms=i.milliseconds,r.M=i.months),s=new Ht(r),Rt(e)&&m(e,"_locale")&&(s._locale=e._locale),s}function jt(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function Zt(e,t){var n={milliseconds:0,months:0};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function zt(s,i){return function(e,t){var n;return null===t||isNaN(+t)||(T(i,"moment()."+i+"(period, number) is deprecated. Please use moment()."+i+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),n=e,e=t,t=n),$t(this,At(e="string"==typeof e?+e:e,t),s),this}}function $t(e,t,n,s){var i=t._milliseconds,r=Ct(t._days),a=Ct(t._months);e.isValid()&&(s=null==s||s,a&&Ce(e,xe(e,"Month")+a*n),r&&be(e,"Date",xe(e,"Date")+r*n),i&&e._d.setTime(e._d.valueOf()+i*n),s&&c.updateOffset(e,r||a))}At.fn=Ht.prototype,At.invalid=function(){return At(NaN)};var qt=zt(1,"add"),Jt=zt(-1,"subtract");function Bt(e,t){var n=12*(t.year()-e.year())+(t.month()-e.month()),s=e.clone().add(n,"months");return-(n+(t-s<0?(t-s)/(s-e.clone().add(n-1,"months")):(t-s)/(e.clone().add(n+1,"months")-s)))||0}function Qt(e){var t;return void 0===e?this._locale._abbr:(null!=(t=lt(e))&&(this._locale=t),this)}c.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",c.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Xt=n("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});function Kt(){return this._locale}function en(e,t){I(0,[e,e.length],0,t)}function tn(e,t,n,s,i){var r;return null==e?Ie(this,s,i).year:((r=Ae(e,s,i))<t&&(t=r),function(e,t,n,s,i){var r=Ee(e,t,n,s,i),a=Ge(r.year,0,r.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}.call(this,e,t,n,s,i))}I(0,["gg",2],0,function(){return this.weekYear()%100}),I(0,["GG",2],0,function(){return this.isoWeekYear()%100}),en("gggg","weekYear"),en("ggggg","weekYear"),en("GGGG","isoWeekYear"),en("GGGGG","isoWeekYear"),H("weekYear","gg"),H("isoWeekYear","GG"),L("weekYear",1),L("isoWeekYear",1),ue("G",se),ue("g",se),ue("GG",B,z),ue("gg",B,z),ue("GGGG",ee,q),ue("gggg",ee,q),ue("GGGGG",te,J),ue("ggggg",te,J),fe(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=k(e)}),fe(["gg","GG"],function(e,t,n,s){t[s]=c.parseTwoDigitYear(e)}),I("Q",0,"Qo","quarter"),H("quarter","Q"),L("quarter",7),ue("Q",Z),ce("Q",function(e,t){t[_e]=3*(k(e)-1)}),I("D",["DD",2],"Do","date"),H("date","D"),L("date",9),ue("D",B),ue("DD",B,z),ue("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),ce(["D","DD"],ye),ce("Do",function(e,t){t[ye]=k(e.match(B)[0])});var nn=Te("Date",!0);I("DDD",["DDDD",3],"DDDo","dayOfYear"),H("dayOfYear","DDD"),L("dayOfYear",4),ue("DDD",K),ue("DDDD",$),ce(["DDD","DDDD"],function(e,t,n){n._dayOfYear=k(e)}),I("m",["mm",2],0,"minute"),H("minute","m"),L("minute",14),ue("m",B),ue("mm",B,z),ce(["m","mm"],pe);var sn=Te("Minutes",!1);I("s",["ss",2],0,"second"),H("second","s"),L("second",15),ue("s",B),ue("ss",B,z),ce(["s","ss"],ve);var rn,an=Te("Seconds",!1);for(I("S",0,0,function(){return~~(this.millisecond()/100)}),I(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),I(0,["SSS",3],0,"millisecond"),I(0,["SSSS",4],0,function(){return 10*this.millisecond()}),I(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),I(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),I(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),I(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),I(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),H("millisecond","ms"),L("millisecond",16),ue("S",K,Z),ue("SS",K,z),ue("SSS",K,$),rn="SSSS";rn.length<=9;rn+="S")ue(rn,ne);function on(e,t){t[we]=k(1e3*("0."+e))}for(rn="S";rn.length<=9;rn+="S")ce(rn,on);var un=Te("Milliseconds",!1);I("z",0,0,"zoneAbbr"),I("zz",0,0,"zoneName");var ln=M.prototype;function dn(e){return e}ln.add=qt,ln.calendar=function(e,t){var n=e||Tt(),s=Nt(n,this).startOf("day"),i=c.calendarFormat(this,s)||"sameElse",r=t&&(x(t[i])?t[i].call(this,n):t[i]);return this.format(r||this.localeData().calendar(i,this,Tt(n)))},ln.clone=function(){return new M(this)},ln.diff=function(e,t,n){var s,i,r;if(!this.isValid())return NaN;if(!(s=Nt(e,this)).isValid())return NaN;switch(i=6e4*(s.utcOffset()-this.utcOffset()),t=R(t)){case"year":r=Bt(this,s)/12;break;case"month":r=Bt(this,s);break;case"quarter":r=Bt(this,s)/3;break;case"second":r=(this-s)/1e3;break;case"minute":r=(this-s)/6e4;break;case"hour":r=(this-s)/36e5;break;case"day":r=(this-s-i)/864e5;break;case"week":r=(this-s-i)/6048e5;break;default:r=this-s}return n?r:D(r)},ln.endOf=function(e){return void 0===(e=R(e))||"millisecond"===e?this:("date"===e&&(e="day"),this.startOf(e).add(1,"isoWeek"===e?"week":e).subtract(1,"ms"))},ln.format=function(e){e||(e=this.isUtc()?c.defaultFormatUtc:c.defaultFormat);var t=A(this,e);return this.localeData().postformat(t)},ln.from=function(e,t){return this.isValid()&&(S(e)&&e.isValid()||Tt(e).isValid())?At({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},ln.fromNow=function(e){return this.from(Tt(),e)},ln.to=function(e,t){return this.isValid()&&(S(e)&&e.isValid()||Tt(e).isValid())?At({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},ln.toNow=function(e){return this.to(Tt(),e)},ln.get=function(e){return x(this[e=R(e)])?this[e]():this},ln.invalidAt=function(){return g(this).overflow},ln.isAfter=function(e,t){var n=S(e)?e:Tt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=R(l(t)?"millisecond":t))?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(t).valueOf())},ln.isBefore=function(e,t){var n=S(e)?e:Tt(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=R(l(t)?"millisecond":t))?this.valueOf()<n.valueOf():this.clone().endOf(t).valueOf()<n.valueOf())},ln.isBetween=function(e,t,n,s){return("("===(s=s||"()")[0]?this.isAfter(e,n):!this.isBefore(e,n))&&(")"===s[1]?this.isBefore(t,n):!this.isAfter(t,n))},ln.isSame=function(e,t){var n,s=S(e)?e:Tt(e);return!(!this.isValid()||!s.isValid())&&("millisecond"===(t=R(t||"millisecond"))?this.valueOf()===s.valueOf():(n=s.valueOf(),this.clone().startOf(t).valueOf()<=n&&n<=this.clone().endOf(t).valueOf()))},ln.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},ln.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},ln.isValid=function(){return p(this)},ln.lang=Xt,ln.locale=Qt,ln.localeData=Kt,ln.max=bt,ln.min=xt,ln.parsingFlags=function(){return _({},g(this))},ln.set=function(e,t){if("object"==typeof e)for(var n=function(e){var t=[];for(var n in e)t.push({unit:n,priority:F[n]});return t.sort(function(e,t){return e.priority-t.priority}),t}(e=C(e)),s=0;s<n.length;s++)this[n[s].unit](e[n[s].unit]);else if(x(this[e=R(e)]))return this[e](t);return this},ln.startOf=function(e){switch(e=R(e)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":case"date":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===e&&this.weekday(0),"isoWeek"===e&&this.isoWeekday(1),"quarter"===e&&this.month(3*Math.floor(this.month()/3)),this},ln.subtract=Jt,ln.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},ln.toObject=function(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}},ln.toDate=function(){return new Date(this.valueOf())},ln.toISOString=function(e){if(!this.isValid())return null;var t=!0!==e,n=t?this.clone().utc():this;return n.year()<0||9999<n.year()?A(n,t?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):x(Date.prototype.toISOString)?t?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",A(n,"Z")):A(n,t?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},ln.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e="moment",t="";this.isLocal()||(e=0===this.utcOffset()?"moment.utc":"moment.parseZone",t="Z");var n="["+e+'("]',s=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=t+'[")]';return this.format(n+s+"-MM-DD[T]HH:mm:ss.SSS"+i)},ln.toJSON=function(){return this.isValid()?this.toISOString():null},ln.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},ln.unix=function(){return Math.floor(this.valueOf()/1e3)},ln.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},ln.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},ln.year=Oe,ln.isLeapYear=function(){return ke(this.year())},ln.weekYear=function(e){return tn.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},ln.isoWeekYear=function(e){return tn.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},ln.quarter=ln.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},ln.month=Fe,ln.daysInMonth=function(){return Pe(this.year(),this.month())},ln.week=ln.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},ln.isoWeek=ln.isoWeeks=function(e){var t=Ie(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},ln.weeksInYear=function(){var e=this.localeData()._week;return Ae(this.year(),e.dow,e.doy)},ln.isoWeeksInYear=function(){return Ae(this.year(),1,4)},ln.date=nn,ln.day=ln.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t,n,s=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(t=e,n=this.localeData(),e="string"!=typeof t?t:isNaN(t)?"number"==typeof(t=n.weekdaysParse(t))?t:null:parseInt(t,10),this.add(e-s,"d")):s},ln.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")},ln.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null!=e){var t=(n=e,s=this.localeData(),"string"==typeof n?s.weekdaysParse(n)%7||7:isNaN(n)?null:n);return this.day(this.day()%7?t:t-7)}return this.day()||7;var n,s},ln.dayOfYear=function(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")},ln.hour=ln.hours=tt,ln.minute=ln.minutes=sn,ln.second=ln.seconds=an,ln.millisecond=ln.milliseconds=un,ln.utcOffset=function(e,t,n){var s,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null!=e){if("string"==typeof e){if(null===(e=Ut(re,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(s=Gt(this)),this._offset=e,this._isUTC=!0,null!=s&&this.add(s,"m"),i!==e&&(!t||this._changeInProgress?$t(this,At(e-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,c.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?i:Gt(this)},ln.utc=function(e){return this.utcOffset(0,e)},ln.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Gt(this),"m")),this},ln.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var e=Ut(ie,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},ln.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?Tt(e).utcOffset():0,(this.utcOffset()-e)%60==0)},ln.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},ln.isLocal=function(){return!!this.isValid()&&!this._isUTC},ln.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},ln.isUtc=Vt,ln.isUTC=Vt,ln.zoneAbbr=function(){return this._isUTC?"UTC":""},ln.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},ln.dates=n("dates accessor is deprecated. Use date instead.",nn),ln.months=n("months accessor is deprecated. Use month instead",Fe),ln.years=n("years accessor is deprecated. Use year instead",Oe),ln.zone=n("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),ln.isDSTShifted=n("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!l(this._isDSTShifted))return this._isDSTShifted;var e={};if(w(e,this),(e=Yt(e))._a){var t=e._isUTC?y(e._a):Tt(e._a);this._isDSTShifted=this.isValid()&&0<a(e._a,t.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted});var hn=P.prototype;function cn(e,t,n,s){var i=lt(),r=y().set(s,t);return i[n](r,e)}function fn(e,t,n){if(d(e)&&(t=e,e=void 0),e=e||"",null!=t)return cn(e,t,n,"month");var s,i=[];for(s=0;s<12;s++)i[s]=cn(e,s,n,"month");return i}function mn(e,t,n,s){"boolean"==typeof e?d(t)&&(n=t,t=void 0):(t=e,e=!1,d(n=t)&&(n=t,t=void 0)),t=t||"";var i,r=lt(),a=e?r._week.dow:0;if(null!=n)return cn(t,(n+a)%7,s,"day");var o=[];for(i=0;i<7;i++)o[i]=cn(t,(i+a)%7,s,"day");return o}hn.calendar=function(e,t,n){var s=this._calendar[e]||this._calendar.sameElse;return x(s)?s.call(t,n):s},hn.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])},hn.invalidDate=function(){return this._invalidDate},hn.ordinal=function(e){return this._ordinal.replace("%d",e)},hn.preparse=dn,hn.postformat=dn,hn.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return x(i)?i(e,t,n,s):i.replace(/%d/i,e)},hn.pastFuture=function(e,t){var n=this._relativeTime[0<e?"future":"past"];return x(n)?n(t):n.replace(/%s/i,t)},hn.set=function(e){var t,n;for(n in e)x(t=e[n])?this[n]=t:this["_"+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},hn.months=function(e,t){return e?o(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||We).test(t)?"format":"standalone"][e.month()]:o(this._months)?this._months:this._months.standalone},hn.monthsShort=function(e,t){return e?o(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[We.test(t)?"format":"standalone"][e.month()]:o(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},hn.monthsParse=function(e,t,n){var s,i,r;if(this._monthsParseExact)return function(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],s=0;s<12;++s)r=y([2e3,s]),this._shortMonthsParse[s]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[s]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===t?-1!==(i=Ye.call(this._shortMonthsParse,a))?i:null:-1!==(i=Ye.call(this._longMonthsParse,a))?i:null:"MMM"===t?-1!==(i=Ye.call(this._shortMonthsParse,a))?i:-1!==(i=Ye.call(this._longMonthsParse,a))?i:null:-1!==(i=Ye.call(this._longMonthsParse,a))?i:-1!==(i=Ye.call(this._shortMonthsParse,a))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(i=y([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(r="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[s].test(e))return s;if(n&&"MMM"===t&&this._shortMonthsParse[s].test(e))return s;if(!n&&this._monthsParse[s].test(e))return s}},hn.monthsRegex=function(e){return this._monthsParseExact?(m(this,"_monthsRegex")||Ne.call(this),e?this._monthsStrictRegex:this._monthsRegex):(m(this,"_monthsRegex")||(this._monthsRegex=Ue),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},hn.monthsShortRegex=function(e){return this._monthsParseExact?(m(this,"_monthsRegex")||Ne.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(m(this,"_monthsShortRegex")||(this._monthsShortRegex=Le),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},hn.week=function(e){return Ie(e,this._week.dow,this._week.doy).week},hn.firstDayOfYear=function(){return this._week.doy},hn.firstDayOfWeek=function(){return this._week.dow},hn.weekdays=function(e,t){return e?o(this._weekdays)?this._weekdays[e.day()]:this._weekdays[this._weekdays.isFormat.test(t)?"format":"standalone"][e.day()]:o(this._weekdays)?this._weekdays:this._weekdays.standalone},hn.weekdaysMin=function(e){return e?this._weekdaysMin[e.day()]:this._weekdaysMin},hn.weekdaysShort=function(e){return e?this._weekdaysShort[e.day()]:this._weekdaysShort},hn.weekdaysParse=function(e,t,n){var s,i,r;if(this._weekdaysParseExact)return function(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=y([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=Ye.call(this._weekdaysParse,a))?i:null:"ddd"===t?-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:"dddd"===t?-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:"ddd"===t?-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:null:-1!==(i=Ye.call(this._minWeekdaysParse,a))?i:-1!==(i=Ye.call(this._weekdaysParse,a))?i:-1!==(i=Ye.call(this._shortWeekdaysParse,a))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(i=y([2e3,1]).day(s),n&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=new RegExp("^"+this.weekdays(i,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[s]=new RegExp("^"+this.weekdaysShort(i,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[s]=new RegExp("^"+this.weekdaysMin(i,"").replace(".",".?")+"$","i")),this._weekdaysParse[s]||(r="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[s]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[s].test(e))return s;if(n&&"ddd"===t&&this._shortWeekdaysParse[s].test(e))return s;if(n&&"dd"===t&&this._minWeekdaysParse[s].test(e))return s;if(!n&&this._weekdaysParse[s].test(e))return s}},hn.weekdaysRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Be.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(m(this,"_weekdaysRegex")||(this._weekdaysRegex=$e),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},hn.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Be.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(m(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=qe),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},hn.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(m(this,"_weekdaysRegex")||Be.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(m(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=Je),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},hn.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},hn.meridiem=function(e,t,n){return 11<e?n?"pm":"PM":n?"am":"AM"},ot("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===k(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),c.lang=n("moment.lang is deprecated. Use moment.locale instead.",ot),c.langData=n("moment.langData is deprecated. Use moment.localeData instead.",lt);var _n=Math.abs;function yn(e,t,n,s){var i=At(t,n);return e._milliseconds+=s*i._milliseconds,e._days+=s*i._days,e._months+=s*i._months,e._bubble()}function gn(e){return e<0?Math.floor(e):Math.ceil(e)}function pn(e){return 4800*e/146097}function vn(e){return 146097*e/4800}function wn(e){return function(){return this.as(e)}}var Mn=wn("ms"),Sn=wn("s"),Dn=wn("m"),kn=wn("h"),Yn=wn("d"),On=wn("w"),Tn=wn("M"),xn=wn("y");function bn(e){return function(){return this.isValid()?this._data[e]:NaN}}var Pn=bn("milliseconds"),Wn=bn("seconds"),Hn=bn("minutes"),Rn=bn("hours"),Cn=bn("days"),Fn=bn("months"),Ln=bn("years");var Un=Math.round,Nn={ss:44,s:45,m:45,h:22,d:26,M:11};var Gn=Math.abs;function Vn(e){return(0<e)-(e<0)||+e}function En(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n=Gn(this._milliseconds)/1e3,s=Gn(this._days),i=Gn(this._months);t=D((e=D(n/60))/60),n%=60,e%=60;var r=D(i/12),a=i%=12,o=s,u=t,l=e,d=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var c=h<0?"-":"",f=Vn(this._months)!==Vn(h)?"-":"",m=Vn(this._days)!==Vn(h)?"-":"",_=Vn(this._milliseconds)!==Vn(h)?"-":"";return c+"P"+(r?f+r+"Y":"")+(a?f+a+"M":"")+(o?m+o+"D":"")+(u||l||d?"T":"")+(u?_+u+"H":"")+(l?_+l+"M":"")+(d?_+d+"S":"")}var In=Ht.prototype;return In.isValid=function(){return this._isValid},In.abs=function(){var e=this._data;return this._milliseconds=_n(this._milliseconds),this._days=_n(this._days),this._months=_n(this._months),e.milliseconds=_n(e.milliseconds),e.seconds=_n(e.seconds),e.minutes=_n(e.minutes),e.hours=_n(e.hours),e.months=_n(e.months),e.years=_n(e.years),this},In.add=function(e,t){return yn(this,e,t,1)},In.subtract=function(e,t){return yn(this,e,t,-1)},In.as=function(e){if(!this.isValid())return NaN;var t,n,s=this._milliseconds;if("month"===(e=R(e))||"year"===e)return t=this._days+s/864e5,n=this._months+pn(t),"month"===e?n:n/12;switch(t=this._days+Math.round(vn(this._months)),e){case"week":return t/7+s/6048e5;case"day":return t+s/864e5;case"hour":return 24*t+s/36e5;case"minute":return 1440*t+s/6e4;case"second":return 86400*t+s/1e3;case"millisecond":return Math.floor(864e5*t)+s;default:throw new Error("Unknown unit "+e)}},In.asMilliseconds=Mn,In.asSeconds=Sn,In.asMinutes=Dn,In.asHours=kn,In.asDays=Yn,In.asWeeks=On,In.asMonths=Tn,In.asYears=xn,In.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*k(this._months/12):NaN},In._bubble=function(){var e,t,n,s,i,r=this._milliseconds,a=this._days,o=this._months,u=this._data;return 0<=r&&0<=a&&0<=o||r<=0&&a<=0&&o<=0||(r+=864e5*gn(vn(o)+a),o=a=0),u.milliseconds=r%1e3,e=D(r/1e3),u.seconds=e%60,t=D(e/60),u.minutes=t%60,n=D(t/60),u.hours=n%24,o+=i=D(pn(a+=D(n/24))),a-=gn(vn(i)),s=D(o/12),o%=12,u.days=a,u.months=o,u.years=s,this},In.clone=function(){return At(this)},In.get=function(e){return e=R(e),this.isValid()?this[e+"s"]():NaN},In.milliseconds=Pn,In.seconds=Wn,In.minutes=Hn,In.hours=Rn,In.days=Cn,In.weeks=function(){return D(this.days()/7)},In.months=Fn,In.years=Ln,In.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t,n,s,i,r,a,o,u,l,d,h,c=this.localeData(),f=(n=!e,s=c,i=At(t=this).abs(),r=Un(i.as("s")),a=Un(i.as("m")),o=Un(i.as("h")),u=Un(i.as("d")),l=Un(i.as("M")),d=Un(i.as("y")),(h=r<=Nn.ss&&["s",r]||r<Nn.s&&["ss",r]||a<=1&&["m"]||a<Nn.m&&["mm",a]||o<=1&&["h"]||o<Nn.h&&["hh",o]||u<=1&&["d"]||u<Nn.d&&["dd",u]||l<=1&&["M"]||l<Nn.M&&["MM",l]||d<=1&&["y"]||["yy",d])[2]=n,h[3]=0<+t,h[4]=s,function(e,t,n,s,i){return i.relativeTime(t||1,!!n,e,s)}.apply(null,h));return e&&(f=c.pastFuture(+this,f)),c.postformat(f)},In.toISOString=En,In.toString=En,In.toJSON=En,In.locale=Qt,In.localeData=Kt,In.toIsoString=n("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",En),In.lang=Xt,I("X",0,0,"unix"),I("x",0,0,"valueOf"),ue("x",se),ue("X",/[+-]?\d+(\.\d{1,3})?/),ce("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),ce("x",function(e,t,n){n._d=new Date(k(e))}),c.version="2.22.1",e=Tt,c.fn=ln,c.min=function(){return Pt("isBefore",[].slice.call(arguments,0))},c.max=function(){return Pt("isAfter",[].slice.call(arguments,0))},c.now=function(){return Date.now?Date.now():+new Date},c.utc=y,c.unix=function(e){return Tt(1e3*e)},c.months=function(e,t){return fn(e,t,"months")},c.isDate=h,c.locale=ot,c.invalid=v,c.duration=At,c.isMoment=S,c.weekdays=function(e,t,n){return mn(e,t,n,"weekdays")},c.parseZone=function(){return Tt.apply(null,arguments).parseZone()},c.localeData=lt,c.isDuration=Rt,c.monthsShort=function(e,t){return fn(e,t,"monthsShort")},c.weekdaysMin=function(e,t,n){return mn(e,t,n,"weekdaysMin")},c.defineLocale=ut,c.updateLocale=function(e,t){if(null!=t){var n,s,i=nt;null!=(s=at(e))&&(i=s._config),(n=new P(t=b(i,t))).parentLocale=st[e],st[e]=n,ot(e)}else null!=st[e]&&(null!=st[e].parentLocale?st[e]=st[e].parentLocale:null!=st[e]&&delete st[e]);return st[e]},c.locales=function(){return s(st)},c.weekdaysShort=function(e,t,n){return mn(e,t,n,"weekdaysShort")},c.normalizeUnits=R,c.relativeTimeRounding=function(e){return void 0===e?Un:"function"==typeof e&&(Un=e,!0)},c.relativeTimeThreshold=function(e,t){return void 0!==Nn[e]&&(void 0===t?Nn[e]:(Nn[e]=t,"s"===e&&(Nn.ss=t-1),!0))},c.calendarFormat=function(e,t){var n=e.diff(t,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},c.prototype=ln,c.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"YYYY-[W]WW",MONTH:"YYYY-MM"},c});
admin/js/onboarding-custom.js CHANGED
@@ -7,15 +7,15 @@ function loaderSection(isShow) {
7
  }
8
  var tvc_time_out="";
9
  function add_message(type, msg, is_close = true){
10
- let tvc_popup_box = document.getElementById('tvc_popup_box');
11
  tvc_popup_box.classList.remove("tvc_popup_box_close");
12
  tvc_popup_box.classList.add("tvc_popup_box");
13
  if(type == "success"){
14
- document.getElementById('tvc_popup_box').innerHTML ="<div class='alert tvc-alert-success'>"+msg+"</div>";
15
  }else if(type == "error"){
16
- document.getElementById('tvc_popup_box').innerHTML ="<div class='alert tvc-alert-error'>"+msg+"</div>";
17
  }else if(type == "warning"){
18
- document.getElementById('tvc_popup_box').innerHTML ="<div class='alert tvc-alert-warning'>"+msg+"</div>";
19
  }
20
  if(is_close){
21
  tvc_time_out = setTimeout(function(){ //tvc_popup_box.style.display = "none";
7
  }
8
  var tvc_time_out="";
9
  function add_message(type, msg, is_close = true){
10
+ let tvc_popup_box = document.getElementById('tvc_onboarding_popup_box');
11
  tvc_popup_box.classList.remove("tvc_popup_box_close");
12
  tvc_popup_box.classList.add("tvc_popup_box");
13
  if(type == "success"){
14
+ document.getElementById('tvc_onboarding_popup_box').innerHTML ="<div class='alert tvc-alert-success'>"+msg+"</div>";
15
  }else if(type == "error"){
16
+ document.getElementById('tvc_onboarding_popup_box').innerHTML ="<div class='alert tvc-alert-error'>"+msg+"</div>";
17
  }else if(type == "warning"){
18
+ document.getElementById('tvc_onboarding_popup_box').innerHTML ="<div class='alert tvc-alert-warning'>"+msg+"</div>";
19
  }
20
  if(is_close){
21
  tvc_time_out = setTimeout(function(){ //tvc_popup_box.style.display = "none";
admin/js/tvc-ee-custom.js CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  var tvc_helper = {
2
  tvc_alert:function(msg_type=null, msg_subject=null, msg, auto_close=false, tvc_time=7000){
3
  document.getElementById('tvc_msg_title').innerHTML ="";
@@ -44,6 +50,7 @@ var tvc_helper = {
44
  tvc_close_msg:function(){
45
  let tvc_popup_box = document.getElementById('tvc_popup_box');
46
  tvc_popup_box.classList.add("tvc_popup_box_close");
 
47
  //tvc_popup_box.style.display = "none";
48
  },
49
  loaderSection:function(isShow) {
@@ -52,5 +59,520 @@ var tvc_helper = {
52
  }else{
53
  $('#feed-spinner').hide();
54
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
  };
1
+ $(function() {
2
+ var conversion_funnel_chart = "";
3
+ var conversion_bar_chart = "";
4
+ var checkout_funnel_chart = "";
5
+ var checkout_bar_chart = "";
6
+ });
7
  var tvc_helper = {
8
  tvc_alert:function(msg_type=null, msg_subject=null, msg, auto_close=false, tvc_time=7000){
9
  document.getElementById('tvc_msg_title').innerHTML ="";
50
  tvc_close_msg:function(){
51
  let tvc_popup_box = document.getElementById('tvc_popup_box');
52
  tvc_popup_box.classList.add("tvc_popup_box_close");
53
+ tvc_popup_box.classList.remove("tvc_popup_box");
54
  //tvc_popup_box.style.display = "none";
55
  },
56
  loaderSection:function(isShow) {
59
  }else{
60
  $('#feed-spinner').hide();
61
  }
62
+ },
63
+ get_google_analytics_reports:function(post_data){
64
+ //console.log(post_data);
65
+ this.cleare_dashboard();
66
+ this.add_loader_for_analytics_reports();
67
+ this.google_analytics_reports_call_api(post_data);
68
+ //this.google_ads_reports_call_api(post_data);
69
+ },
70
+ google_ads_reports_call_api:function(post_data){
71
+
72
+ post_data['action']='get_google_ads_reports';
73
+ var v_this = this;
74
+ $.ajax({
75
+ type: "POST",
76
+ dataType: "json",
77
+ url: tvc_ajax_url,
78
+ data: post_data,
79
+ success: function (response) {
80
+ if(response.error == false){
81
+ if(Object.keys(response.data).length > 0){
82
+ v_this.set_google_analytics_reports_value(response.data, post_data);
83
+ }
84
+ }else{
85
+ v_this.tvc_alert("error","",response.error);
86
+ }
87
+ //console.log(JSON.parse(response.data));
88
+ v_this.remove_loader_for_analytics_reports();
89
+ }
90
+ });
91
+ },
92
+ google_analytics_reports_call_api:function(post_data){
93
+ var v_this = this;
94
+ $.ajax({
95
+ type: "POST",
96
+ dataType: "json",
97
+ url: tvc_ajax_url,
98
+ data: post_data,
99
+ success: function (response) {
100
+ if(response.error == false){
101
+ if(Object.keys(response.data).length > 0){
102
+ v_this.set_google_analytics_reports_value(response.data, post_data);
103
+ }
104
+ }else if(response.error == true && response.errors != undefined){
105
+ const errors = response.errors;
106
+ v_this.tvc_alert("error","Error",errors);
107
+ }else{
108
+ v_this.tvc_alert("error","Error","Analytics report data not fetched");
109
+ }
110
+ v_this.remove_loader_for_analytics_reports();
111
+ }
112
+ });
113
+ },
114
+ set_google_analytics_reports_value:function(data, post_data){
115
+ var v_this = this;
116
+ data = JSON.parse(data);
117
+ console.log(data);
118
+ var basic_data = data.dashboard_data_point;
119
+ var currency_code = post_data.ga_currency;
120
+ var plugin_url = post_data.plugin_url;
121
+ var s_1_div_id ={
122
+ 'conversion_rate':{
123
+ 'id':'transactionsPerSession',
124
+ 'type':'rate'
125
+ },'revenue':{
126
+ 'id':'transactionRevenue',
127
+ 'type':'currency'
128
+ },'total_transactions':{
129
+ 'id':'transactions',
130
+ 'type':'number'
131
+ },'avg_order_value':{
132
+ 'id':'revenuePerTransaction',
133
+ 'type':'currency'
134
+ },'added_to_cart':{
135
+ 'id':'productAddsToCart',
136
+ 'type':'number'
137
+ },'sessions':{
138
+ 'id':'sessions',
139
+ 'type':'number'
140
+ },'users':{
141
+ 'id':'users',
142
+ 'type':'number'
143
+ },'new_users':{
144
+ 'id':'newUsers',
145
+ 'type':'number'
146
+ },'product_views':{
147
+ 'id':'productDetailViews',
148
+ 'type':'number'
149
+ },'removed_from_cart':{
150
+ 'id':'productRemovesFromCart',
151
+ 'type':'number'
152
+ },'transaction_shipping':{
153
+ 'id':'transactionShipping',
154
+ 'type':'currency'
155
+ },'transaction_tax':{
156
+ 'id':'transactionTax',
157
+ 'type':'currency'
158
+ }
159
+ };
160
+ var reports_typs = {
161
+ basec_data:{
162
+ is_free:true
163
+ },product_performance_report:{
164
+ is_free:false
165
+ },medium_performance_report:{
166
+ is_free:false
167
+ },conversion_funnel:{
168
+ is_free:false
169
+ },checkout_funnel:{
170
+ is_free:false
171
+ }
172
+ };
173
+ var paln_type = 'free';
174
+ if(post_data.plan_id != 1){
175
+ paln_type='paid';
176
+ }
177
+ if(Object.keys(s_1_div_id).length > 0){
178
+ var temp_val =""; var temp_div_id = "";
179
+ $.each(s_1_div_id, function (propKey, propValue) {
180
+ if(basic_data.hasOwnProperty(propValue['id'])){
181
+ temp_val = basic_data[propValue['id']];
182
+ temp_div_id = "#s1_"+propValue['id']+" > .dash-smry-value";
183
+ v_this.display_field_val(temp_div_id, propValue, temp_val, propValue['type'], currency_code);
184
+ }
185
+ if(basic_data.hasOwnProperty('compare_'+propValue['id'])){
186
+ temp_val = basic_data['compare_'+propValue['id']];
187
+ temp_div_id = "#s1_"+propValue['id']+" > .dash-smry-compare-val";
188
+ v_this.display_field_val(temp_div_id, propValue, temp_val, 'rate', currency_code, plugin_url);
189
+
190
+ //$("#s1_"+propValue['id']+" > .dash-smry-value").html(temp_val);
191
+ }
192
+
193
+ });
194
+ }
195
+
196
+ if(data.hasOwnProperty('product_performance_report') && ( reports_typs.product_performance_report.is_free || paln_type == 'paid')){
197
+ var p_p_r = data.product_performance_report.products;
198
+ //console.log(p_p_r);
199
+ var table_row = '';
200
+ var product_revenue_per = 0;
201
+ if(p_p_r != undefined && Object.keys(p_p_r).length > 0){
202
+ $.each(p_p_r, function (propKey, propValue) {
203
+ table_row = '';
204
+ product_revenue_per = ((propValue['itemRevenue']*100)/basic_data.transactionRevenue).toFixed(1);
205
+ if(product_revenue_per == 'NaN'){product_revenue_per = 0;}
206
+ product_revenue_per = data
207
+ table_row += '<tr><td class="prdnm-cell">'+propValue['productName']+'</td>';
208
+ table_row += '<td>'+propValue['productDetailViews']+'</td>';
209
+ table_row += '<td>'+propValue['productAddsToCart']+'</td>';
210
+ table_row += '<td>'+propValue['uniquePurchases']+'</td>';
211
+ table_row += '<td>'+propValue['itemQuantity']+'</td>';
212
+ table_row += '<td>'+propValue['itemRevenue']+'<span class="tddshpertg"></span></td>';
213
+ table_row += '<td>'+propValue['revenuePerItem']+'</td>';
214
+ table_row += '<td>'+propValue['productRefundAmount']+'</td>';
215
+ table_row += '<td>'+propValue['cartToDetailRate']+'%</td>';
216
+ table_row += '<td>'+propValue['buyToDetailRate']+'%</td></tr>';
217
+ $("#product_performance_report table tbody").append(table_row);
218
+ })
219
+ }else{
220
+ $("#product_performance_report table tbody").append("<tr><td colspan='10'>Data not available</td></tr>");
221
+ }
222
+ }
223
+
224
+ if(data.hasOwnProperty('medium_performance_report') && ( reports_typs.medium_performance_report.is_free || paln_type == 'paid')){
225
+ var m_p_r = data.medium_performance_report.mediums;
226
+ //console.log(m_p_r);
227
+ var table_row = '';
228
+
229
+ $.each(m_p_r, function (propKey, propValue) {
230
+ table_row = '';
231
+ table_row += '<tr><td class="prdnm-cell">'+((propValue["medium"]!=undefined)?propValue["medium"]:0)+'</td>';
232
+ table_row += '<td>'+((propValue["transactionsPerSession"]!=undefined)?propValue["transactionsPerSession"]:0)+'</td>';
233
+ table_row += '<td>'+((propValue["transactionRevenue"]!=undefined)?propValue["transactionRevenue"]:0)+'</td>';
234
+ table_row += '<td>'+((propValue["transactions"]!=undefined)?propValue["transactions"]:0)+'</td>';
235
+ table_row += '<td>'+((propValue["revenuePerTransaction"]!=undefined)?propValue["revenuePerTransaction"]:0)+'</td>';
236
+ table_row += '<td>'+((propValue["productAddsToCart"]!=undefined)?propValue["productAddsToCart"]:0)+'</td>';
237
+ table_row += '<td>'+((propValue["productRemovesFromCart"]!=undefined)?propValue["productRemovesFromCart"]:0)+'</td>';
238
+ table_row += '<td>'+((propValue["productDetailViews"]!=undefined)?propValue["productDetailViews"]:0)+'</td>';
239
+ table_row += '<td>'+((propValue["users"]!=undefined)?propValue["users"]:0)+'</td>';
240
+ table_row += '<td>'+((propValue["sessions"]!=undefined)?propValue["sessions"]:0)+'</td></tr>';
241
+ $("#medium_performance_report table tbody").append(table_row);
242
+ })
243
+ }
244
+ if(reports_typs.conversion_funnel.is_free || paln_type == 'paid'){
245
+ this.set_ecommerce_conversion_funnel(basic_data,data.ecommerce_funnel.shoppingStage);
246
+ this.set_ecommerce_checkout_funnel(data.ecommerce_funnel.shoppingStage);
247
+ }
248
+ },
249
+ set_ecommerce_conversion_funnel:function(data,shoppingStage){
250
+ /**
251
+ * Ecommerce Conversion Funnel
252
+ **/
253
+ var conversion_s1 = ((shoppingStage.PRODUCT_VIEW*100)/shoppingStage.ALL_VISITS).toFixed(2) || 0;
254
+ if(conversion_s1 == 'NaN'){conversion_s1 = 0;}
255
+ $(".conversion_s1").html(conversion_s1+"%");
256
+ var conversion_s2 = ((shoppingStage.ADD_TO_CART*100)/shoppingStage.PRODUCT_VIEW).toFixed(2) || 0;
257
+ if(conversion_s2 == 'NaN'){conversion_s2 = 0;}
258
+ $(".conversion_s2").html(conversion_s2+"%");
259
+ var conversion_s3 = ((shoppingStage.CHECKOUT*100)/shoppingStage.ADD_TO_CART).toFixed(2) || 0;
260
+ if(conversion_s3 == 'NaN'){conversion_s3 = 0;}
261
+ $(".conversion_s3").html(conversion_s3+"%");
262
+ var conversion_s4 = ((shoppingStage.TRANSACTION*100)/shoppingStage.CHECKOUT).toFixed(2) || 0;
263
+ if(conversion_s4 == 'NaN'){conversion_s4 = 0;}
264
+ $(".conversion_s4").html(conversion_s4+"%");
265
+
266
+ conversion_funnel_chart =document.getElementById('ecomfunchart').getContext('2d');
267
+ var conversion_bluechartgradient = conversion_funnel_chart.createLinearGradient(0, 0, 0, 800);
268
+ conversion_bluechartgradient.addColorStop(0, '#002BFC');
269
+ conversion_bluechartgradient.addColorStop(1, '#00CFF6');
270
+
271
+ conversion_bar_chart = new Chart(conversion_funnel_chart, {
272
+ type: 'bar',
273
+ scaleSteps : 5,
274
+ data: {
275
+ labels: ["Total Sessions", "Product View", "Add to Cart", "Checkouts", "Order Confirmation"],
276
+ datasets: [{
277
+ labels:false,
278
+ data: [shoppingStage.ALL_VISITS, shoppingStage.PRODUCT_VIEW, shoppingStage.ADD_TO_CART, shoppingStage.CHECKOUT, shoppingStage.TRANSACTION],
279
+ backgroundColor: conversion_bluechartgradient,
280
+ hoverBackgroundColor: conversion_bluechartgradient,
281
+ hoverBorderWidth: 0,
282
+ hoverBorderColor: 'blue',
283
+ datalabels: {
284
+ /*formatter: (value, ctx) => {
285
+ let sum = 0;
286
+ let dataArr = ctx.chart.data.datasets[0].data;
287
+ dataArr.map(data => {
288
+ sum += data;
289
+ });
290
+ let percentage = (value*300 / sum).toFixed(0)+"%";
291
+ return percentage;
292
+ },*/
293
+ color: '#515151',
294
+ anchor:'end',
295
+ align:'top',
296
+ offset:'10',
297
+ }
298
+ }]
299
+ },
300
+ plugins: [ChartDataLabels],
301
+ options: {
302
+ responsive:true,
303
+ plugins:{
304
+ legend:false,
305
+ },
306
+ scales: {
307
+ y:{
308
+ fontColor: "#ffffff",
309
+ fontStyle: "normal",
310
+ beginAtZero: true,
311
+ maxTicksLimit: 6,
312
+ padding: 0,
313
+ grid:{
314
+ borderWidth:0,
315
+ },
316
+ ticks: {
317
+ stepSize: 1000,
318
+ callback: function(value) {
319
+ var ranges = [
320
+ { divider: 1e6, suffix: 'M' },
321
+ { divider: 1e3, suffix: 'k' }
322
+ ];
323
+ function formatNumber(n) {
324
+ for (var i = 0; i < ranges.length; i++) {
325
+ if (n >= ranges[i].divider) {
326
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
327
+ }
328
+ }
329
+ return n;
330
+ }
331
+ return '' + formatNumber(value);
332
+ }
333
+ }
334
+ },
335
+ x:{
336
+ padding: 0,
337
+ fontColor: "#ffffff",
338
+ fontStyle: "normal",
339
+ grid: {
340
+ display:false
341
+ }
342
+ }
343
+ },
344
+
345
+ }
346
+ });
347
+
348
+ },
349
+ set_ecommerce_checkout_funnel:function(data){
350
+ /**
351
+ * Ecommerce Checkout Funnel
352
+ **/
353
+
354
+ checkout_funnel_chart = document.getElementById('ecomcheckoutfunchart').getContext('2d');
355
+ var bluechartgradient = checkout_funnel_chart.createLinearGradient(0, 0, 0, 800);
356
+ bluechartgradient.addColorStop(0, '#002BFC');
357
+ bluechartgradient.addColorStop(1, '#00CFF6');
358
+
359
+ checkout_bar_chart = new Chart(checkout_funnel_chart, {
360
+ type: 'bar',
361
+ scaleSteps : 6,
362
+ data: {
363
+ labels: [ "Checkout Step 1", "Checkout Step 2","Checkout Step 3", "Purchase"],
364
+ datasets: [{
365
+ labels:false,
366
+ data: [data.CHECKOUT_1,data.CHECKOUT_2,data.CHECKOUT_3,data.TRANSACTION],
367
+ backgroundColor: bluechartgradient,
368
+ hoverBackgroundColor: bluechartgradient,
369
+ hoverBorderWidth: 0,
370
+ hoverBorderColor: 'blue',
371
+ datalabels: {
372
+ /*formatter: (value, ctx) => {
373
+ let sum = 0;
374
+ let dataArr = ctx.chart.data.datasets[0].data;
375
+ dataArr.map(data => {
376
+ sum += data;
377
+ });
378
+ let percentage = (value*300 / sum).toFixed(0)+"%";
379
+ return percentage;
380
+ },*/
381
+ color: '#515151',
382
+ anchor:'end',
383
+ align:'top',
384
+ offset:'10',
385
+ }
386
+ }]
387
+ },
388
+ plugins: [ChartDataLabels],
389
+ options: {
390
+ responsive:true,
391
+ plugins:{
392
+ legend:false,
393
+ },
394
+ scales: {
395
+ y:{
396
+ fontColor: "#ffffff",
397
+ fontStyle: "normal",
398
+ beginAtZero: true,
399
+ maxTicksLimit: 6,
400
+ padding: 0,
401
+ grid:{
402
+ borderWidth:0,
403
+ },
404
+ ticks: {
405
+ stepSize: 1000,
406
+ callback: function(value) {
407
+ var ranges = [
408
+ { divider: 1e6, suffix: 'M' },
409
+ { divider: 1e3, suffix: 'k' }
410
+ ];
411
+ function formatNumber(n) {
412
+ for (var i = 0; i < ranges.length; i++) {
413
+ if (n >= ranges[i].divider) {
414
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
415
+ }
416
+ }
417
+ return n;
418
+ }
419
+ return '' + formatNumber(value);
420
+ }
421
+ }
422
+ },
423
+ x:{
424
+ padding: 0,
425
+ fontColor: "#ffffff",
426
+ fontStyle: "normal",
427
+ grid: {
428
+ display:false
429
+ }
430
+ }
431
+ },
432
+
433
+ }
434
+ });
435
+
436
+ },
437
+ display_field_val:function(div_id, field, field_val, field_type, currency_code, plugin_url){
438
+ if(field_type == "currency"){
439
+ var currency = this.get_currency_symbols(currency_code);
440
+ $(div_id).html(currency +''+field_val);
441
+ }else if(field_type == "rate"){
442
+ field_val = parseFloat(field_val).toFixed(2);
443
+ var img = "";
444
+ if(plugin_url != "" && plugin_url != undefined){
445
+ img = '<img src="'+plugin_url+'/admin/images/red-down.png">';
446
+ if(field_val >0){
447
+ img = '<img src="'+plugin_url+'/admin/images/green-up.png">';
448
+ }
449
+ }
450
+ $(div_id).html(img+field_val+'%');
451
+ }else {
452
+ $(div_id).html(field_val);
453
+ }
454
+
455
+ },
456
+ remove_loader_for_analytics_reports:function(){
457
+ var reg_section = this.get_analytics_reports_section();
458
+ if(Object.keys(reg_section).length > 0){
459
+ $.each(reg_section, function (propKey, propValue) {
460
+ if(propValue.hasOwnProperty('main-class') && propValue.hasOwnProperty('loading-type')){
461
+ if(propValue['loading-type'] == 'bgcolor'){
462
+ //$("."+propValue['main-class']).addClass("is_loading");
463
+ if(Object.keys(propValue['ajax_fields']).length > 0){
464
+ $.each(propValue['ajax_fields'], function (propKey, propValue) {
465
+ $("."+propValue['class']).removeClass("loading-bg-effect");
466
+ });
467
+ }
468
+ }else if(propValue['loading-type'] == 'gif'){
469
+ $("."+propValue['main-class']).removeClass("is_loading");
470
+ }
471
+
472
+ }
473
+ });
474
+
475
+ }
476
+ },
477
+ cleare_dashboard:function(){
478
+ $("#product_performance_report table tbody").html("");
479
+ $("#medium_performance_report table tbody").html("");
480
+ var canvas = document.getElementById('ecomfunchart');
481
+ if( canvas != null){
482
+ var is_blank = this.is_canvas_blank(canvas);
483
+ if(!is_blank){
484
+ conversion_bar_chart.destroy();
485
+ //const canvas = document.getElementById('ecomfunchart');
486
+ //canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
487
+ }
488
+ }
489
+ canvas = document.getElementById('ecomcheckoutfunchart');
490
+ if(canvas != null){
491
+ var is_blank = this.is_canvas_blank(canvas);
492
+ if(!is_blank){
493
+ checkout_bar_chart.destroy();
494
+ }
495
+ }
496
+ },
497
+ add_loader_for_analytics_reports:function(){
498
+ var reg_section = this.get_analytics_reports_section();
499
+ if(Object.keys(reg_section).length > 0){
500
+ $.each(reg_section, function (propKey, propValue) {
501
+ if(propValue.hasOwnProperty('main-class') && propValue.hasOwnProperty('loading-type')){
502
+ if(propValue['loading-type'] == 'bgcolor'){
503
+ //$("."+propValue['main-class']).addClass("is_loading");
504
+ if(Object.keys(propValue['ajax_fields']).length > 0){
505
+ $.each(propValue['ajax_fields'], function (propKey, propValue) {
506
+ $("."+propValue['class']).addClass("loading-bg-effect");
507
+ });
508
+ }
509
+ }else if(propValue['loading-type'] == 'gif'){
510
+ $("."+propValue['main-class']).addClass("is_loading");
511
+ }
512
+
513
+ }
514
+ });
515
+ }
516
+ },
517
+ get_analytics_reports_section:function(){
518
+ return {
519
+ 'dashboard_summary':{
520
+ 'loading-type':'bgcolor',
521
+ 'main-class':'dashsmry-item',
522
+ 'sub-clsass':'dashsmrybx',
523
+ 'ajax_fields':{
524
+ 'field_1':{
525
+ 'class':'dash-smry-title'
526
+ },'field_2':{
527
+ 'class':'dash-smry-value'
528
+ },'field_3':{
529
+ 'class':'dash-smry-compare-val'
530
+ },'field_4':{
531
+ 'class':'dshsmryprdtxt'
532
+ }
533
+ }
534
+ },'ecommerce_funnel':{
535
+ 'loading-type':'gif',
536
+ 'main-class':'ecom-funn-chrt-bx',
537
+ },'checkout_funnel':{
538
+ 'loading-type':'gif',
539
+ 'main-class':'ecom-checkout-funn-chrt-bx',
540
+ },'product_performance_report':{
541
+ 'loading-type':'gif',
542
+ 'main-class':'product_performance_report',
543
+ },'medium_performance_report':{
544
+ 'loading-type':'gif',
545
+ 'main-class':'medium_performance_report',
546
+ }
547
+ };
548
+ },get_currency_symbols:function(code){
549
+ var currency_symbols = {
550
+ 'USD': '$', // US Dollar
551
+ 'EUR': '€', // Euro
552
+ 'CRC': '₡', // Costa Rican Colón
553
+ 'GBP': '£', // British Pound Sterling
554
+ 'ILS': '₪', // Israeli New Sheqel
555
+ 'INR': '₹', // Indian Rupee
556
+ 'JPY': '¥', // Japanese Yen
557
+ 'KRW': '₩', // South Korean Won
558
+ 'NGN': '₦', // Nigerian Naira
559
+ 'PHP': '₱', // Philippine Peso
560
+ 'PLN': 'zł', // Polish Zloty
561
+ 'PYG': '₲', // Paraguayan Guarani
562
+ 'THB': '฿', // Thai Baht
563
+ 'UAH': '₴', // Ukrainian Hryvnia
564
+ 'VND': '₫', // Vietnamese Dong
565
+ };
566
+ if(currency_symbols[code]!==undefined) {
567
+ return currency_symbols[code];
568
+ }else{
569
+ return code;
570
+ }
571
+ },is_canvas_blank:function (canvas) {
572
+ const context = canvas.getContext('2d');
573
+ const pixelBuffer = new Uint32Array(
574
+ context.getImageData(0, 0, canvas.width, canvas.height).data.buffer
575
+ );
576
+ return !pixelBuffer.some(color => color !== 0);
577
  }
578
  };
admin/partials/class-conversios-footer.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @since 4.0.2
4
+ * Description: Conversios Onboarding page, It's call while active the plugin
5
+ */
6
+ if ( ! class_exists( 'Conversios_Footer' ) ) {
7
+ class Conversios_Footer {
8
+ public function __construct( ){
9
+ add_action('add_conversios_footer',array($this, 'before_end_footer'));
10
+ }
11
+ public function before_end_footer(){
12
+ ?>
13
+ </div>
14
+ <?php
15
+ }
16
+ }
17
+ }
18
+ new Conversios_Footer();
admin/partials/class-conversios-header.php ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @since 4.0.2
4
+ * Description: Conversios Onboarding page, It's call while active the plugin
5
+ */
6
+ if ( ! class_exists( 'Conversios_Header' ) ) {
7
+ class Conversios_Header extends TVC_Admin_Helper{
8
+ protected $site_url;
9
+ protected $conversios_site_url;
10
+ protected $subscription_data;
11
+ protected $plan_id=1;
12
+ public function __construct( ){
13
+ $this->site_url = "admin.php?page=";
14
+ $this->conversios_site_url = $this->get_conversios_site_url();
15
+ $this->subscription_data = $this->get_user_subscription_data();
16
+ if(isset($this->subscription_data->plan_id) && !in_array($this->subscription_data->plan_id, array("1"))){
17
+ $this->plan_id = $this->subscription_data->plan_id;
18
+ }
19
+ add_action('add_conversios_header',array($this, 'before_start_header'));
20
+ add_action('add_conversios_header',array($this, 'header_notices'));
21
+ add_action('add_conversios_header',array($this, 'conversios_header'));
22
+ add_action('add_conversios_header',array($this, 'header_menu'));
23
+ }
24
+
25
+ /**
26
+ * before start header section
27
+ *
28
+ * @since 4.1.4
29
+ */
30
+ public function before_start_header(){
31
+ ?>
32
+ <div class="bodyrightpart conversios-body-part">
33
+ <?php
34
+ }
35
+ /**
36
+ * header notices section
37
+ *
38
+ * @since 4.1.4
39
+ */
40
+ public function header_notices(){
41
+ if($this->plan_id == 1){
42
+ ?>
43
+ <!--- Promotion box start -->
44
+ <div class="promobandtop">
45
+ <div class="container-fluid">
46
+ <div class="row">
47
+ <div class="promoleft">
48
+ <div class="promobandmsg">
49
+ Level up your game by getting detail insights on every products. Make the informed decision for your next campaign.
50
+ </div>
51
+ </div>
52
+ <div class="promoright">
53
+ <div class="prmoupgrdbtn">
54
+ <a target="_blank" href="<?php echo $this->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=Top+Bar+upgrading+to+pro&utm_campaign=Upsell+at+Conversios'; ?>" class="upgradebtn">Upgrade</a>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ <!--- Promotion box end -->
61
+ <?php
62
+ }
63
+ echo $this->call_tvc_site_verified_and_domain_claim();
64
+ }
65
+ /**
66
+ * header section
67
+ *
68
+ * @since 4.1.4
69
+ */
70
+ public function conversios_header(){
71
+ $plan_name = "Free Plan";
72
+ if(isset($this->subscription_data->plan_name) && !in_array($this->subscription_data->plan_id, array("1"))){
73
+ $plan_name = $this->subscription_data->plan_name;
74
+ }
75
+ $ee_msg_list = $this->get_ee_msg_nofification_list();
76
+
77
+ $active_count = 0;
78
+ if(!empty($ee_msg_list)){
79
+ $html = "";
80
+ foreach ($ee_msg_list as $key => $value) {
81
+ if( isset($value["active"]) && $value["active"] == 1 ){
82
+ $active_count++;
83
+ $m_date = isset($value["date"])?"<span class=\"tvc-msg_date\">".$value["date"]."</span>":"";
84
+ $m_title = isset($value["title"])?"<h4 class=\"tvc-msg_title\">".$value["title"]."</h4>":"";
85
+ $m_html = isset($value["html"])?"<span class=\"tvc-msg_text\">".base64_decode($value["html"])."</span>":"";
86
+ $target = (isset($value["link_type"]) && $value["link_type"] == "external")?"target=\"_blank\"":"";
87
+ $m_link = isset($value["link"])?"<a ".$target." href=".$value["link"]." class=\"tvc-notification-button is-secondary\">".$value["link_title"]."</a>":"";
88
+
89
+ $html.="<li>
90
+ <section class=\"tvc-msg plain\">
91
+ <div class=\"tvc-msg_wrapper\">
92
+ <div class=\"tvc-msg_content\">".$m_title.$m_html."</div>
93
+ <div class=\"tvc-msg_actions\">
94
+ ".$m_link."
95
+ <div class=\"tvc-dropdown\">
96
+ <button type=\"button\" data-id=".$key." class=\"tvc-notification-button is-tertiary is-dismissible-notification\">Dismiss</button>
97
+ </div>
98
+ ".$m_date."
99
+ </div>
100
+ </div>
101
+ </section>
102
+ </li>";
103
+ }
104
+ }
105
+ }
106
+ ?>
107
+ <!-- header start -->
108
+ <header class="header">
109
+ <div class="hedertop">
110
+ <div class="row align-items-center">
111
+ <div class="hdrtpleft">
112
+ <div class="brandlogo">
113
+ <a target="_blank" href="<?php echo $this->conversios_site_url; ?>"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/logo.png'; ?>" alt="" /></a>
114
+ </div>
115
+ <div class="hdrcntcbx">
116
+ For any query, contact us at <span>+1 (415) 968-6313</span>
117
+ </div>
118
+ </div>
119
+ <div class="hdrtpright">
120
+ <div class="hustleplanbtn">
121
+ <a href="<?php echo $this->site_url.'conversios-account'; ?>"><button class="cvrs-btn greenbtn"><?php echo $plan_name; ?></button></a>
122
+ </div>
123
+ <?php /*
124
+ <div class="hdrnotiwrp">
125
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/notification-icon.png'; ?>" alt="" />
126
+ <div class="notialrt"><?php echo $active_count; ?></div>
127
+ </div>
128
+ */ ?>
129
+ </div>
130
+ <div class="hdrcntcbx mblhdrcntcbx">
131
+ For any query, contact us at <span>+1 (415) 968-6313</span>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </header>
136
+ <!-- header end -->
137
+ <?php
138
+ }
139
+
140
+ /* add active tab class */
141
+ protected function is_active_menu($page=""){
142
+ if($page!="" && isset($_GET['page']) && $_GET['page'] == $page){
143
+ return "active";
144
+ }
145
+ return ;
146
+ }
147
+ public function conversios_menu_list(){
148
+ //slug => arra();
149
+ $conversios_menu_list = array(
150
+ 'conversios' => array(
151
+ 'title'=>'Dashboard',
152
+ 'icon'=>ENHANCAD_PLUGIN_URL.'/admin/images/conversios-menu.png',
153
+ 'acitve_icon'=>ENHANCAD_PLUGIN_URL.'/admin/images/active-conversios-menu.png'
154
+ ),
155
+ 'conversios-google-analytics'=>array('title'=>'Google Analytics'),
156
+ 'conversios-google-ads'=>array('title'=>'Google Ads'),
157
+ 'conversios-google-shopping-feed'=>array('title'=>'Google Shopping')
158
+ );
159
+ if($this->plan_id == 1){
160
+ $conversios_menu_list['conversios-pricings'] = array('title'=>'Free Vs Pro','icon'=>'');
161
+ }
162
+ return apply_filters('conversios_menu_list', $conversios_menu_list, $conversios_menu_list);
163
+ }
164
+ /**
165
+ * header menu section
166
+ *
167
+ * @since 4.1.4
168
+ */
169
+ public function header_menu(){
170
+ $menu_list = $this->conversios_menu_list();
171
+ if(!empty($menu_list)){
172
+ ?>
173
+ <div class="navinfowrap">
174
+ <div class="navinfotopnav">
175
+ <ul>
176
+ <?php
177
+ foreach ($menu_list as $key => $value) {
178
+ if(isset($value['title']) && $value['title']){
179
+ $is_active = $this->is_active_menu($key);
180
+ $icon = "";
181
+ if(!isset($value['icon']) && !isset($value['acitve_icon'])){
182
+ $icon = ENHANCAD_PLUGIN_URL.'/admin/images/'.$key.'-menu.png';
183
+ if($is_active == 'active'){
184
+ $icon = ENHANCAD_PLUGIN_URL.'/admin/images/'.$is_active.'-'.$key.'-menu.png';
185
+ }
186
+ }else{
187
+ $icon = (isset($value['icon']))?$value['icon']:((isset($value['acitve_icon']))?$value['acitve_icon']:"");
188
+ if($is_active == 'active' && isset($value['acitve_icon'])){
189
+ $icon =$value['acitve_icon'];
190
+ }
191
+ }
192
+ ?>
193
+ <li class="<?php echo $is_active; ?>">
194
+ <a href="<?php echo $this->site_url.$key; ?>">
195
+ <?php if($icon != ""){?>
196
+ <span class="navinfoicon"><img src="<?php echo $icon; ?>" /></span>
197
+ <?php } ?>
198
+ <span class="navinfonavtext"><?php echo $value['title']; ?></span>
199
+ </a>
200
+ </li>
201
+ <?php
202
+ }
203
+ }?>
204
+ </ul>
205
+ </div>
206
+ </div>
207
+ <?php
208
+ }
209
+
210
+ }
211
+
212
+ }
213
+ }
214
+ new Conversios_Header();
admin/partials/enhanced-ecommerce-google-analytics-admin-display.php CHANGED
@@ -20,7 +20,7 @@ class TVC_Tabs {
20
  protected $google_detail;
21
  public function __construct() {
22
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
23
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
24
  $this->google_detail = $this->TVC_Admin_Helper->get_ee_options_data();
25
  $this->create_tabs();
26
  }
@@ -32,8 +32,8 @@ class TVC_Tabs {
32
  }
33
  }
34
  /* add active tab class */
35
- protected function is_active_tabs($tab_name=""){
36
- if($tab_name!="" && isset($_GET['tab']) && $_GET['tab'] == $tab_name){
37
  return "active";
38
  }
39
  return ;
@@ -54,53 +54,53 @@ class TVC_Tabs {
54
  ?>
55
  <ul class="nav nav-pills nav-justified">
56
  <li class="nav-item">
57
- <div class="tvc-tooltip <?php echo (empty($_GET['tab']))?'active':$this->is_active_tabs('general_settings'); ?>">
58
  <?php if(isset($setting_status['google_analytic']) && $setting_status['google_analytic'] == false ){?>
59
  <?php echo (isset($setting_status['google_analytic_msg'])?'<span class="tvc-tooltiptext tvc-tooltip-right">'.$setting_status['google_analytic_msg'].'</span>':"") ?>
60
  <?php }?>
61
- <div class="nav-item nav-link <?php echo (empty($_GET['tab']))?'active':$this->is_active_tabs('general_settings'); ?>">
62
  <?php if(isset($setting_status['google_analytic']) ){
63
  echo $this->info_htnml($setting_status['google_analytic']);
64
  }?>
65
- <a href="<?php echo $this->site_url.'general_settings'; ?>" class=""> Google Analytics</a>
66
  </div>
67
  </div>
68
  </li>
69
  <li class="nav-item">
70
- <div class="tvc-tooltip <?php echo $this->is_active_tabs('google_ads'); ?>">
71
  <?php if(isset($setting_status['google_ads']) && $setting_status['google_ads'] == false ){?>
72
  <?php echo (isset($setting_status['google_ads'])?'<span class="tvc-tooltiptext tvc-tooltip-right">'.$setting_status['google_ads_msg'].'</span>':"") ?>
73
  <?php } ?>
74
- <div class="nav-link <?php echo $this->is_active_tabs('google_ads'); ?>">
75
  <?php if(isset($setting_status['google_ads']) ){
76
  echo $this->info_htnml($setting_status['google_ads']);
77
  }?>
78
- <a href="<?php echo $this->site_url.'google_ads'; ?>" class="">Google Ads</a>
79
  </div>
80
  </li>
81
  <?php
82
- $sub_tab_active="";
83
- if(isset($_GET['tab']) && ($_GET['tab'] == 'google_shopping_feed' || $_GET['tab'] == 'gaa_config_page' || $_GET['tab'] == 'sync_product_page' || $_GET['tab'] == 'shopping_campaigns_page' || $_GET['tab'] == 'add_campaign_page')){
84
  $sub_tab_active="active";
85
- }
86
  ?>
87
  <li class="nav-item">
88
- <div class="tvc-tooltip <?php echo (($sub_tab_active)?$sub_tab_active:$this->is_active_tabs('google_shopping_feed')); ?>">
89
  <?php if(isset($setting_status['google_shopping']) && $setting_status['google_shopping'] == false ){
90
  echo (isset($setting_status['google_shopping_msg'])?'<span class="tvc-tooltiptext tvc-tooltip-right">'.$setting_status['google_shopping_msg'].'</span>':"");
91
  } ?>
92
- <div class="nav-link <?php echo (($sub_tab_active)?$sub_tab_active:$this->is_active_tabs('google_shopping_feed')); ?>">
93
  <?php if(isset($setting_status['google_shopping']) ){
94
  echo $this->info_htnml($setting_status['google_shopping']);
95
  } ?>
96
- <a href="<?php echo $this->site_url.'google_shopping_feed'; ?>" class="">Google Shopping</a>
97
  </div>
98
  </li>
99
  <?php if($plan_id ==1){?>
100
  <li class="nav-item tvc-new-freevspro-nav-item">
101
- <div class="nav-link <?php echo $this->is_active_tabs('pricings'); ?>">
102
  <span class="tvc-new-freevspro">New</span>
103
- <a href="<?php echo $this->site_url.'pricings'; ?>" class="">Free Vs Pro</a>
104
  </div>
105
  </li>
106
  <?php } ?>
20
  protected $google_detail;
21
  public function __construct() {
22
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
23
+ $this->site_url = "admin.php?page=conversios";
24
  $this->google_detail = $this->TVC_Admin_Helper->get_ee_options_data();
25
  $this->create_tabs();
26
  }
32
  }
33
  }
34
  /* add active tab class */
35
+ protected function is_active_tabs($page=""){
36
+ if($page!="" && isset($_GET['page']) && $_GET['page'] == $page){
37
  return "active";
38
  }
39
  return ;
54
  ?>
55
  <ul class="nav nav-pills nav-justified">
56
  <li class="nav-item">
57
+ <div class="tvc-tooltip <?php echo $this->is_active_tabs('conversios'); ?>">
58
  <?php if(isset($setting_status['google_analytic']) && $setting_status['google_analytic'] == false ){?>
59
  <?php echo (isset($setting_status['google_analytic_msg'])?'<span class="tvc-tooltiptext tvc-tooltip-right">'.$setting_status['google_analytic_msg'].'</span>':"") ?>
60
  <?php }?>
61
+ <div class="nav-item nav-link <?php echo $this->is_active_tabs('conversios'); ?>">
62
  <?php if(isset($setting_status['google_analytic']) ){
63
  echo $this->info_htnml($setting_status['google_analytic']);
64
  }?>
65
+ <a href="<?php echo $this->site_url; ?>" class=""> Google Analytics</a>
66
  </div>
67
  </div>
68
  </li>
69
  <li class="nav-item">
70
+ <div class="tvc-tooltip <?php echo $this->is_active_tabs('conversios-google-ads'); ?>">
71
  <?php if(isset($setting_status['google_ads']) && $setting_status['google_ads'] == false ){?>
72
  <?php echo (isset($setting_status['google_ads'])?'<span class="tvc-tooltiptext tvc-tooltip-right">'.$setting_status['google_ads_msg'].'</span>':"") ?>
73
  <?php } ?>
74
+ <div class="nav-link <?php echo $this->is_active_tabs('conversios-google-ads'); ?>">
75
  <?php if(isset($setting_status['google_ads']) ){
76
  echo $this->info_htnml($setting_status['google_ads']);
77
  }?>
78
+ <a href="<?php echo $this->site_url.'-google-ads'; ?>" class="">Google Ads</a>
79
  </div>
80
  </li>
81
  <?php
82
+ /*$sub_tab_active="";
83
+ if(isset($_GET['tab']) && $_GET['tab'] == 'conversios-google-shopping-feed'){
84
  $sub_tab_active="active";
85
+ }*/
86
  ?>
87
  <li class="nav-item">
88
+ <div class="tvc-tooltip <?php echo $this->is_active_tabs('conversios-google-shopping-feed'); ?>">
89
  <?php if(isset($setting_status['google_shopping']) && $setting_status['google_shopping'] == false ){
90
  echo (isset($setting_status['google_shopping_msg'])?'<span class="tvc-tooltiptext tvc-tooltip-right">'.$setting_status['google_shopping_msg'].'</span>':"");
91
  } ?>
92
+ <div class="nav-link <?php echo $this->is_active_tabs('conversios-google-shopping-feed'); ?>">
93
  <?php if(isset($setting_status['google_shopping']) ){
94
  echo $this->info_htnml($setting_status['google_shopping']);
95
  } ?>
96
+ <a href="<?php echo $this->site_url.'-google-shopping-feed'; ?>" class="">Google Shopping</a>
97
  </div>
98
  </li>
99
  <?php if($plan_id ==1){?>
100
  <li class="nav-item tvc-new-freevspro-nav-item">
101
+ <div class="nav-link <?php echo $this->is_active_tabs('conversios-pricings'); ?>">
102
  <span class="tvc-new-freevspro">New</span>
103
+ <a href="<?php echo $this->site_url.'-pricings'; ?>" class="">Free Vs Pro</a>
104
  </div>
105
  </li>
106
  <?php } ?>
admin/partials/general-fields.php CHANGED
@@ -115,10 +115,10 @@ if (isset($_GET['connect']) && isset($_GET['subscription_id'])) {
115
  $TVC_Admin_Helper->update_remarketing_snippets();
116
  if(isset($googleDetail->google_merchant_center_id) || isset($googleDetail->google_ads_id) ){
117
  if( $googleDetail->google_merchant_center_id != "" && $googleDetail->google_ads_id != ""){
118
- wp_redirect("admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=sync_product_page&welcome_msg=true");
119
  exit;
120
  }else{
121
- wp_redirect("admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=gaa_config_page&welcome_msg=true");
122
  exit;
123
  }
124
  }
@@ -420,7 +420,7 @@ $(document).ready(function () {
420
  $.ajax({
421
  type: "POST",
422
  dataType: "json",
423
- url: myAjaxNonces.ajaxurl,
424
  data: data,
425
  beforeSend: function(){
426
  tvc_helper.loaderSection(true);
115
  $TVC_Admin_Helper->update_remarketing_snippets();
116
  if(isset($googleDetail->google_merchant_center_id) || isset($googleDetail->google_ads_id) ){
117
  if( $googleDetail->google_merchant_center_id != "" && $googleDetail->google_ads_id != ""){
118
+ wp_redirect("admin.php?page=conversios-google-shopping-feed&tab=sync_product_page&welcome_msg=true");
119
  exit;
120
  }else{
121
+ wp_redirect("admin.php?page=conversios-google-shopping-feed&tab=gaa_config_page&welcome_msg=true");
122
  exit;
123
  }
124
  }
420
  $.ajax({
421
  type: "POST",
422
  dataType: "json",
423
+ url: tvc_ajax_url,
424
  data: data,
425
  beforeSend: function(){
426
  tvc_helper.loaderSection(true);
admin/partials/pricings.php CHANGED
@@ -207,10 +207,10 @@ class TVC_Pricings {
207
  </div>
208
  <div class="row-feature clearfix">
209
  <div class="column">Actionable Dashboard</div>
210
- <div class="column">(Upcoming)</div>
211
- <div class="column">(Upcoming)</div>
212
- <div class="column popular">(Upcoming)</div>
213
- <div class="column">(Upcoming)</div>
214
  </div>
215
  <div class="row-feature clearfix">
216
  <div class="column">Menu tracking</div>
207
  </div>
208
  <div class="row-feature clearfix">
209
  <div class="column">Actionable Dashboard</div>
210
+ <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(Limited)</div>
211
+ <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(Complete)</div>
212
+ <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br>(Complete)</div>
213
+ <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(Complete)</div>
214
  </div>
215
  <div class="row-feature clearfix">
216
  <div class="column">Menu tracking</div>
enhanced-ecommerce-google-analytics.php CHANGED
@@ -15,7 +15,7 @@
15
  * Plugin Name: Enhanced E-commerce for Woocommerce store
16
  * Plugin URI: https://www.tatvic.com/tatvic-labs/woocommerce-extension/
17
  * Description: Automates eCommerce tracking in Google Analytics, dynamic remarkting in Google Ads, and provides complete Google Shopping features.
18
- * Version: 4.1.3
19
  * Author: Tatvic
20
  * Author URI: www.tatvic.com
21
  * License: GPL-2.0+
@@ -37,7 +37,7 @@ if ( ! defined( 'WPINC' ) ) {
37
  * Start at version 1.0.0 and use SemVer - https://semver.org
38
  * Rename this for your plugin and update it as you release new versions.
39
  */
40
- define( 'PLUGIN_TVC_VERSION', '4.1.3' );
41
  $fullName = plugin_basename( __FILE__ );
42
  $dir = str_replace('/enhanced-ecommerce-google-analytics.php','',$fullName);
43
  if ( ! defined( 'ENHANCAD_PLUGIN_NAME' ) ) {
15
  * Plugin Name: Enhanced E-commerce for Woocommerce store
16
  * Plugin URI: https://www.tatvic.com/tatvic-labs/woocommerce-extension/
17
  * Description: Automates eCommerce tracking in Google Analytics, dynamic remarkting in Google Ads, and provides complete Google Shopping features.
18
+ * Version: 4.2.0
19
  * Author: Tatvic
20
  * Author URI: www.tatvic.com
21
  * License: GPL-2.0+
37
  * Start at version 1.0.0 and use SemVer - https://semver.org
38
  * Rename this for your plugin and update it as you release new versions.
39
  */
40
+ define( 'PLUGIN_TVC_VERSION', '4.2.0' );
41
  $fullName = plugin_basename( __FILE__ );
42
  $dir = str_replace('/enhanced-ecommerce-google-analytics.php','',$fullName);
43
  if ( ! defined( 'ENHANCAD_PLUGIN_NAME' ) ) {
includes/class-enhanced-ecommerce-google-analytics.php CHANGED
@@ -125,14 +125,21 @@ class Enhanced_Ecommerce_Google_Analytics {
125
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-enhanced-ecommerce-google-analytics-admin.php';
126
 
127
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-enhanced-ecommerce-google-analytics-settings.php';
128
-
 
 
129
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-conversios-onboarding.php';
130
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/helper/class-onboarding-helper.php';
131
-
 
 
 
 
132
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-tvc-admin-auto-product-sync-helper.php';
133
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-survey.php';
134
 
135
 
 
136
  /**
137
  * The class responsible for defining all actions that occur in the public-facing
138
  * side of the site.
@@ -171,9 +178,8 @@ class Enhanced_Ecommerce_Google_Analytics {
171
  * @access private
172
  */
173
  private function define_admin_hooks() {
174
-
175
  $plugin_admin = new Enhanced_Ecommerce_Google_Analytics_Admin( $this->get_plugin_name(), $this->get_version() );
176
- $this->loader->add_action( 'admin_menu', $plugin_admin, 'display_admin_page' );
177
  // $this->loader->add_action("admin_menu", $plugin_admin, "add_new_menu");
178
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
179
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
@@ -193,28 +199,6 @@ class Enhanced_Ecommerce_Google_Analytics {
193
  */
194
  private function define_public_hooks() {
195
  $plugin_public = new Enhanced_Ecommerce_Google_Analytics_Public( $this->get_plugin_name(), $this->get_version() );
196
- /* $this->loader->add_action("wp_head", $plugin_public, "enqueue_scripts");
197
- $this->loader->add_action("wp_head", $plugin_public, "ee_settings");
198
- $this->loader->add_action("wp_head", $plugin_public, "add_google_site_verification_tag",1);
199
-
200
- $this->loader->add_action("wp_footer", $plugin_public, "t_products_impre_clicks");
201
- $this->loader->add_action("woocommerce_after_shop_loop_item", $plugin_public, "bind_product_metadata");
202
- $this->loader->add_action("woocommerce_thankyou", $plugin_public, "ecommerce_tracking_code");
203
- $this->loader->add_action("woocommerce_after_single_product", $plugin_public, "product_detail_view");
204
- $this->loader->add_action("woocommerce_after_cart",$plugin_public, "remove_cart_tracking");
205
- //check out step 1,2,3
206
- $this->loader->add_action("woocommerce_before_checkout_billing_form", $plugin_public, "checkout_step_1_tracking");
207
- $this->loader->add_action("woocommerce_after_checkout_billing_form", $plugin_public, "checkout_step_2_tracking");
208
- $this->loader->add_action("woocommerce_after_checkout_billing_form", $plugin_public, "checkout_step_3_tracking");
209
- $this->loader->add_action("woocommerce_after_add_to_cart_button", $plugin_public, "add_to_cart");
210
-
211
- //Advanced Store data Tracking
212
- //add version details in footer
213
- $this->loader->add_action("wp_footer", $plugin_public, "add_plugin_details");
214
-
215
- //Add Dev ID
216
- $this->loader->add_action("wp_head", $plugin_public, "add_dev_id");
217
- $this->loader->add_action("wp_footer",$plugin_public, "tvc_store_meta_data");*/
218
  }
219
 
220
  /**
@@ -274,7 +258,7 @@ class Enhanced_Ecommerce_Google_Analytics {
274
  public function tvc_plugin_action_links($links) {
275
  $deactivate_link = $links['deactivate'];
276
  unset($links['deactivate']);
277
- $setting_url = 'admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=general_settings';
278
  $links[] = '<a href="' . get_admin_url(null, $setting_url) . '">Settings</a>';
279
 
280
  $links[] = '<a href="https://wordpress.org/plugins/enhanced-e-commerce-for-woocommerce-store/#faq" target="_blank">FAQ</a>';
125
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-enhanced-ecommerce-google-analytics-admin.php';
126
 
127
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-enhanced-ecommerce-google-analytics-settings.php';
128
+ /**
129
+ * New conversios UI file list
130
+ */
131
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-conversios-onboarding.php';
132
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/helper/class-onboarding-helper.php';
133
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/helper/class-dashboard-helper.php';
134
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-conversios-admin.php';
135
+ /**
136
+ * End New conversios UI file list
137
+ */
138
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-tvc-admin-auto-product-sync-helper.php';
139
  require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-survey.php';
140
 
141
 
142
+
143
  /**
144
  * The class responsible for defining all actions that occur in the public-facing
145
  * side of the site.
178
  * @access private
179
  */
180
  private function define_admin_hooks() {
 
181
  $plugin_admin = new Enhanced_Ecommerce_Google_Analytics_Admin( $this->get_plugin_name(), $this->get_version() );
182
+ //$this->loader->add_action( 'admin_menu', $plugin_admin, 'display_admin_page' );
183
  // $this->loader->add_action("admin_menu", $plugin_admin, "add_new_menu");
184
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
185
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
199
  */
200
  private function define_public_hooks() {
201
  $plugin_public = new Enhanced_Ecommerce_Google_Analytics_Public( $this->get_plugin_name(), $this->get_version() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  }
203
 
204
  /**
258
  public function tvc_plugin_action_links($links) {
259
  $deactivate_link = $links['deactivate'];
260
  unset($links['deactivate']);
261
+ $setting_url = 'admin.php?page=conversios-google-analytics';
262
  $links[] = '<a href="' . get_admin_url(null, $setting_url) . '">Settings</a>';
263
 
264
  $links[] = '<a href="https://wordpress.org/plugins/enhanced-e-commerce-for-woocommerce-store/#faq" target="_blank">FAQ</a>';
includes/class-tvc-register-scripts.php CHANGED
@@ -12,58 +12,23 @@ if ( ! class_exists( 'TVC_Register_Scripts' ) ) :
12
  * Register Scripts Class
13
  */
14
  class TVC_Register_Scripts {
15
- // @private storage of scripts version
16
- private $_version_stamp;
17
- public function __construct() {
18
- $this->_version_stamp = defined( 'WP_DEBUG' ) && WP_DEBUG ? time() : PLUGIN_TVC_VERSION;
19
-
20
- add_action( 'admin_enqueue_scripts', array( $this, 'tvc_register_required_nonce' ) );
21
  // only load the next hooks when on the Settings page
22
- if ( isset($_GET['page']) && $_GET['page'] == 'enhanced-ecommerce-google-analytics-admin-display' ) {
23
  add_action( 'admin_enqueue_scripts', array( $this, 'tvc_register_required_options_page_scripts' ) );
24
- add_action( 'admin_enqueue_scripts', array( $this, 'tvc_register_required_options_page_nonce' ) );
25
  }
26
- }
27
 
28
- /**
29
- * Generate the required nonce's.
30
- */
31
- public function tvc_register_required_nonce() {
32
- // make a unique nonce for all Ajax requests
33
- wp_localize_script(
34
- 'tvc_script_ajax',
35
- 'myAjaxNonces',
36
- array(
37
- // URL to wp-admin/admin-ajax.php to process the request
38
- 'ajaxurl' => admin_url( 'admin-ajax.php' ),
39
- // generate the nonce's
40
- 'campaignCategoryListsNonce' => wp_create_nonce( 'tvcajax-campaign-category-lists-nonce' ),
41
- 'campaignStatusNonce' => wp_create_nonce( 'tvcajax-update-campaign-status-nonce' ),
42
- 'campaignDeleteNonce' => wp_create_nonce( 'tvcajax-delete-campaign-nonce' ),
43
- 'categoryListsNonce' => wp_create_nonce( 'tvcajax-category-lists-nonce' )
44
- )
45
- );
46
- }
47
  /**
48
  * Registers all required java scripts for the feed manager Settings page.
49
  */
50
  public function tvc_register_required_options_page_scripts() {
51
  // enqueue notice handling script
52
- wp_enqueue_script( 'tvc_data-handling-script', ENHANCAD_PLUGIN_URL . '/includes/data/js/tvc_ajaxdatahandling.js', array( 'jquery' ), $this->_version_stamp, true );
53
- }
54
- /**
55
- * Generate the nonce's for the Settings page.
56
- */
57
- public function tvc_register_required_options_page_nonce() {
58
- // make a unique nonce for all Ajax requests
59
- wp_localize_script(
60
- 'tvc_data-handling-script',
61
- 'myAjaxNonces',
62
- array(
63
- // URL to wp-admin/admin-ajax.php to process the request
64
- 'ajaxurl' => admin_url( 'admin-ajax.php' )
65
- )
66
- );
67
  }
68
  }
69
  // End of TVC_Register_Scripts class
12
  * Register Scripts Class
13
  */
14
  class TVC_Register_Scripts {
15
+ public function __construct() {
 
 
 
 
 
16
  // only load the next hooks when on the Settings page
17
+ if ( isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) {
18
  add_action( 'admin_enqueue_scripts', array( $this, 'tvc_register_required_options_page_scripts' ) );
 
19
  }
20
+ }
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  /**
23
  * Registers all required java scripts for the feed manager Settings page.
24
  */
25
  public function tvc_register_required_options_page_scripts() {
26
  // enqueue notice handling script
27
+ ?>
28
+ <script>
29
+ var tvc_ajax_url = '<?php echo admin_url( 'admin-ajax.php' ); ?>';
30
+ </script>
31
+ <?php
 
 
 
 
 
 
 
 
 
 
32
  }
33
  }
34
  // End of TVC_Register_Scripts class
includes/setup/CustomApi.php CHANGED
@@ -367,9 +367,73 @@ class CustomApi{
367
  return $e->getMessage();
368
  }
369
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  public function verifyLicenceKey($licence_key, $subscription_id) {
371
  try {
372
- echo $url = $this->apiDomain . '/licence/verify-key';
373
  $data = [
374
  'key' => $licence_key,
375
  'domain' => get_site_url()
@@ -703,11 +767,9 @@ class CustomApi{
703
  }
704
  }
705
 
706
- public function generateAccessToken($access_token, $refresh_token) {
707
-
708
  $request = "https://www.googleapis.com/oauth2/v1/tokeninfo?"
709
- . "access_token=" . $access_token;
710
-
711
  $ch = curl_init();
712
  curl_setopt($ch, CURLOPT_URL, $request);
713
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
@@ -715,7 +777,7 @@ class CustomApi{
715
  $response = curl_exec($ch);
716
  $result = json_decode($response);
717
  if (isset($result->error) && $result->error) {
718
- $credentials_file = ENHANCAD_PLUGIN_DIR . 'includes/setup/json/client-secrets.json';
719
  $str = file_get_contents($credentials_file);
720
  $credentials = $str ? json_decode($str, true) : [];
721
  $url = 'https://www.googleapis.com/oauth2/v4/token';
@@ -729,7 +791,6 @@ class CustomApi{
729
  'client_secret' => $clientSecret,
730
  'refresh_token' => $refreshToken,
731
  ];
732
-
733
  $postData = json_encode($data);
734
  $ch = curl_init();
735
  curl_setopt_array($ch, array(
@@ -739,16 +800,13 @@ class CustomApi{
739
  CURLOPT_HTTPHEADER => $header,
740
  CURLOPT_POSTFIELDS => $postData
741
  ));
742
- $response = curl_exec($ch);
743
-
744
- $response = json_decode($response);
745
-
746
  if(isset($response->access_token)){
747
  return $response->access_token;
748
  }else{
749
  return $access_token;
750
- }
751
-
752
  } else {
753
  return $access_token;
754
  }
367
  return $e->getMessage();
368
  }
369
  }
370
+ /**
371
+ * @since 4.1.4
372
+ * Get view ID for GA3 reporting API
373
+ */
374
+ public function get_analytics_viewid_currency($postData) {
375
+ try {
376
+ $header = array(
377
+ "Authorization: Bearer MTIzNA==",
378
+ "content-type: application/json"
379
+ );
380
+ $curl_url = $this->apiDomain . "/actionable-dashboard/analytics-viewid-currency";
381
+ $postData['access_token']= $this->generateAccessToken($this->get_tvc_access_token(), $this->get_tvc_refresh_token());
382
+ $postData = json_encode($postData);
383
+ $ch = curl_init();
384
+ curl_setopt_array($ch, array(
385
+ CURLOPT_URL => esc_url($curl_url),
386
+ CURLOPT_RETURNTRANSFER => true,
387
+ CURLOPT_TIMEOUT => 1000,
388
+ CURLOPT_HTTPHEADER => $header,
389
+ CURLOPT_POSTFIELDS => $postData
390
+ ));
391
+ $response = curl_exec($ch);
392
+ $response = json_decode($response);
393
+ return $response;
394
+ } catch (Exception $e) {
395
+ return $e->getMessage();
396
+ }
397
+ }
398
+ /**
399
+ * @since 4.1.4
400
+ * Get google analytics reports call using reporting API
401
+ */
402
+ public function get_google_analytics_reports($postData) {
403
+ try {
404
+ $curl_url = $this->apiDomain . "/actionable-dashboard/google-analytics-reports";
405
+ $header = array(
406
+ "Authorization: Bearer MTIzNA==",
407
+ "content-type: application/json"
408
+ );
409
+ $access_token = $this->generateAccessToken($this->get_tvc_access_token(), $this->get_tvc_refresh_token());
410
+ if($access_token != ""){
411
+ $postData['access_token']= $access_token;
412
+ $postData = json_encode($postData);
413
+ $ch = curl_init();
414
+ curl_setopt_array($ch, array(
415
+ CURLOPT_URL => esc_url($curl_url),
416
+ CURLOPT_RETURNTRANSFER => true,
417
+ CURLOPT_TIMEOUT => 2000,
418
+ CURLOPT_HTTPHEADER => $header,
419
+ CURLOPT_POSTFIELDS => $postData
420
+ ));
421
+ $response = curl_exec($ch);
422
+ $response = json_decode($response);
423
+ return $response;
424
+ }else{
425
+ $return = new \stdClass();
426
+ $return->error = true;
427
+ $return->message = 'access_token_not_generate';
428
+ return $return;
429
+ }
430
+ } catch (Exception $e) {
431
+ return $e->getMessage();
432
+ }
433
+ }
434
  public function verifyLicenceKey($licence_key, $subscription_id) {
435
  try {
436
+ $url = $this->apiDomain . '/licence/verify-key';
437
  $data = [
438
  'key' => $licence_key,
439
  'domain' => get_site_url()
767
  }
768
  }
769
 
770
+ public function generateAccessToken($access_token, $refresh_token) {
 
771
  $request = "https://www.googleapis.com/oauth2/v1/tokeninfo?"
772
+ ."access_token=".$access_token;
 
773
  $ch = curl_init();
774
  curl_setopt($ch, CURLOPT_URL, $request);
775
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
777
  $response = curl_exec($ch);
778
  $result = json_decode($response);
779
  if (isset($result->error) && $result->error) {
780
+ $credentials_file = ENHANCAD_PLUGIN_DIR.'includes/setup/json/client-secrets.json';
781
  $str = file_get_contents($credentials_file);
782
  $credentials = $str ? json_decode($str, true) : [];
783
  $url = 'https://www.googleapis.com/oauth2/v4/token';
791
  'client_secret' => $clientSecret,
792
  'refresh_token' => $refreshToken,
793
  ];
 
794
  $postData = json_encode($data);
795
  $ch = curl_init();
796
  curl_setopt_array($ch, array(
800
  CURLOPT_HTTPHEADER => $header,
801
  CURLOPT_POSTFIELDS => $postData
802
  ));
803
+ $response = curl_exec($ch);
804
+ $response = json_decode($response);
 
 
805
  if(isset($response->access_token)){
806
  return $response->access_token;
807
  }else{
808
  return $access_token;
809
+ }
 
810
  } else {
811
  return $access_token;
812
  }
includes/setup/ShoppingApi.php CHANGED
@@ -13,7 +13,6 @@ class ShoppingApi {
13
  $this->customApiObj = new CustomApi();
14
  //$queries = new TVC_Queries();
15
  $this->apiDomain = TVC_API_CALL_URL;
16
- //$this->apiDomain = 'http://127.0.0.1:8000/api';
17
  $this->token = 'MTIzNA==';
18
  $this->merchantId = $this->TVC_Admin_Helper->get_merchantId();
19
  $this->customerId = $this->TVC_Admin_Helper->get_currentCustomerId();
@@ -118,7 +117,6 @@ class ShoppingApi {
118
  'from_date' => $from_date,
119
  'to_date' => $to_date
120
  ];
121
-
122
  $args = array(
123
  'headers' => array(
124
  'Authorization' => "Bearer $this->token",
13
  $this->customApiObj = new CustomApi();
14
  //$queries = new TVC_Queries();
15
  $this->apiDomain = TVC_API_CALL_URL;
 
16
  $this->token = 'MTIzNA==';
17
  $this->merchantId = $this->TVC_Admin_Helper->get_merchantId();
18
  $this->customerId = $this->TVC_Admin_Helper->get_currentCustomerId();
117
  'from_date' => $from_date,
118
  'to_date' => $to_date
119
  ];
 
120
  $args = array(
121
  'headers' => array(
122
  'Authorization' => "Bearer $this->token",
includes/setup/account.php CHANGED
@@ -146,7 +146,7 @@ $(document).ready(function () {
146
  $.ajax({
147
  type: "POST",
148
  dataType: "json",
149
- url: myAjaxNonces.ajaxurl,
150
  data: data,
151
  beforeSend: function(){
152
  tvc_helper.loaderSection(true);
146
  $.ajax({
147
  type: "POST",
148
  dataType: "json",
149
+ url: tvc_ajax_url,
150
  data: data,
151
  beforeSend: function(){
152
  tvc_helper.loaderSection(true);
includes/setup/add-campaign.php CHANGED
@@ -324,7 +324,7 @@ class AddCampaign {
324
  var customer_id = "<?php echo $this->currentCustomerId; ?>";
325
  jQuery('#feed-spinner').css('display', 'block');
326
  jQuery.post(
327
- myAjaxNonces.ajaxurl,
328
  {
329
  action: 'tvcajax-get-campaign-categories',
330
  countryCode: sales_country,
324
  var customer_id = "<?php echo $this->currentCustomerId; ?>";
325
  jQuery('#feed-spinner').css('display', 'block');
326
  jQuery.post(
327
+ tvc_ajax_url,
328
  {
329
  action: 'tvcajax-get-campaign-categories',
330
  countryCode: sales_country,
includes/setup/class-conversios-dashboard.php ADDED
@@ -0,0 +1,971 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @since 4.1.4
4
+ * Description: Conversios Onboarding page, It's call while active the plugin
5
+ */
6
+ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
7
+ class Conversios_Dashboard {
8
+ protected $screen;
9
+ protected $TVC_Admin_Helper;
10
+ protected $CustomApi;
11
+ protected $subscription_id;
12
+ protected $ga_traking_type;
13
+ protected $ga3_property_id;
14
+ protected $ga3_ua_analytic_account_id;
15
+ protected $ga3_view_id;
16
+ protected $ga_currency;
17
+ protected $ga_currency_symbols;
18
+ protected $ga4_measurement_id;
19
+ protected $ga4_property_id;
20
+ protected $subscription_data;
21
+ protected $plan_id=1;
22
+ protected $is_need_to_update_api_data_wp_db = false;
23
+ protected $pro_plan_site;
24
+ protected $report_data;
25
+ public function __construct( ){
26
+ $this->TVC_Admin_Helper = new TVC_Admin_Helper();
27
+ $this->CustomApi = new CustomApi();
28
+ $this->subscription_id = $this->TVC_Admin_Helper->get_subscriptionId();
29
+ $this->subscription_data = $this->TVC_Admin_Helper->get_user_subscription_data();
30
+ $this->pro_plan_site = $this->TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=dashboard&utm_campaign=Upsell+at+Conversios';
31
+ if(isset($this->subscription_data->plan_id) && !in_array($this->subscription_data->plan_id, array("1"))){
32
+ $this->plan_id = $this->subscription_data->plan_id;
33
+ }
34
+
35
+ if( $this->subscription_id != "" ){
36
+ $this->ga_traking_type = $this->subscription_data->tracking_option; // UA,GA4,BOTH
37
+ $this->ga3_property_id = $this->subscription_data->property_id; // GA3
38
+ $this->ga3_ua_analytic_account_id = $this->subscription_data->ua_analytic_account_id;
39
+ $this->set_ga3_view_id_and_ga3_currency();
40
+ $this->ga4_measurement_id = $this->subscription_data->measurement_id; //GA4 ID
41
+ }else{
42
+ wp_redirect("admin.php?page=conversios_onboarding");
43
+ exit;
44
+ }
45
+
46
+ $this->includes();
47
+ $this->screen = get_current_screen();
48
+ $this->init();
49
+ $this->load_html();
50
+ }
51
+
52
+ public function includes() {
53
+ if (!class_exists('CustomApi.php')) {
54
+ require_once(ENHANCAD_PLUGIN_DIR . 'includes/setup/CustomApi.php');
55
+ }
56
+ }
57
+
58
+ public function init(){
59
+
60
+ }
61
+
62
+ public function set_ga3_view_id_and_ga3_currency(){
63
+ //$this->view_id = get_option('ee_ga_view_id');
64
+ if(isset($this->subscription_data->view_id) && isset($this->subscription_data->analytics_currency) && $this->subscription_data->view_id!="" && $this->subscription_data->analytics_currency!=""){
65
+ $this->ga3_view_id = $this->subscription_data->view_id;
66
+ $this->ga_currency = $this->subscription_data->analytics_currency;
67
+ $this->ga_currency_symbols = $this->TVC_Admin_Helper->get_currency_symbols($this->ga_currency);
68
+
69
+ }else{
70
+ $data = array(
71
+ "subscription_id"=>$this->subscription_id,
72
+ "property_id"=>$this->ga3_property_id,
73
+ "ua_analytic_account_id"=>$this->ga3_ua_analytic_account_id
74
+ );
75
+ $api_rs = $this->CustomApi->get_analytics_viewid_currency($data);
76
+ if (isset($api_rs->error) && $api_rs->error == '') {
77
+ if(isset($api_rs->data) && $api_rs->data != ""){
78
+ $data = json_decode($api_rs->data);
79
+ $this->ga3_view_id = $data->view_id;
80
+ $this->ga_currency =$data->analytics_currency;
81
+ $this->ga_currency_symbols = $this->TVC_Admin_Helper->get_currency_symbols($this->ga_currency);
82
+ $this->is_need_to_update_api_data_wp_db = true;
83
+ }
84
+ }
85
+ }
86
+ }
87
+ public function load_html(){
88
+ do_action('conversios_start_html_'.$_GET['page']);
89
+ $this->current_html();
90
+ $this->current_js();
91
+ do_action('conversios_end_html_'.$_GET['page']);
92
+ }
93
+
94
+ /**
95
+ * Page custom js code
96
+ *
97
+ * @since 4.1.4
98
+ */
99
+ public function current_js(){
100
+ ?>
101
+ <script>
102
+ $( document ).ready(function() {
103
+ /**
104
+ * daterage script
105
+ **/
106
+ var plan_id = '<?php echo $this->plan_id; ?>';
107
+ //$(function() {
108
+ var start = moment().subtract(30, 'days');
109
+ var end = moment();
110
+ function cb(start, end) {
111
+ var start_date = start.format('DD/MM/YYYY') || 0,
112
+ end_date = end.format('DD/MM/YYYY') || 0;
113
+ $('span.daterangearea').html(start_date + ' - ' + end_date);
114
+
115
+ /*var date_range = $.trim($(".report_range_val").text()).split('-');
116
+
117
+ var start_date = $.trim(date_range[0].replace(/\//g,"-")) || 0,
118
+ end_date = $.trim(date_range[1].replace(/\//g,"-")) || 0;*/
119
+ var data = {
120
+ action:'get_google_analytics_reports',
121
+ subscription_id:'<?php echo $this->subscription_id; ?>',
122
+ plan_id:plan_id,
123
+ ga_traking_type:'<?php echo $this->ga_traking_type; ?>',
124
+ view_id :'<?php echo $this->ga3_view_id; ?>',
125
+ ga4_property_id:'<?php echo $this->ga4_property_id; ?>',
126
+ ga_currency :'<?php echo $this->ga_currency; ?>',
127
+ plugin_url:'<?php echo ENHANCAD_PLUGIN_URL; ?>',
128
+ start_date :$.trim(start_date.replace(/\//g,"-")),
129
+ end_date :$.trim(end_date.replace(/\//g,"-")),
130
+ conversios_nonce:'<?php echo wp_create_nonce( 'conversios_nonce' ); ?>'
131
+ };
132
+ // Call API
133
+ tvc_helper.get_google_analytics_reports(data);
134
+ }
135
+ $('#reportrange').daterangepicker({
136
+ startDate: start,
137
+ endDate: end,
138
+ ranges: {
139
+ 'Today': [moment(), moment()],
140
+ 'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
141
+ 'Last 7 Days': [moment().subtract(6, 'days'), moment()],
142
+ 'Last 30 Days': [moment().subtract(29, 'days'), moment()],
143
+ 'This Month': [moment().startOf('month'), moment().endOf('month')],
144
+ 'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
145
+ }
146
+ }, cb);
147
+ cb(start, end);
148
+ //});
149
+ //upgrds plan popup
150
+ $(".upgrdsbrs-btn").on( "click", function() {
151
+ $('.upgradsbscrptn-pp').addClass('showpopup');
152
+ $('body').addClass('scrlnone');
153
+ });
154
+ $('body').click(function(evt){
155
+ if($(evt.target).closest('.upgrdsbrs-btn, .upgradsbscrptnpp-cntr').length){
156
+ return;
157
+ }
158
+ $('.upgradsbscrptn-pp').removeClass('showpopup');
159
+ $('body').removeClass('scrlnone');
160
+ });
161
+ $(".clsbtntrgr").on( "click", function() {
162
+ $(this).closest('.pp-modal').removeClass('showpopup');
163
+ $('body').removeClass('scrlnone');
164
+ });
165
+ //upcoming_featur popup
166
+ $(".upcoming-featur-btn").on( "click", function() {
167
+ $('.upcoming_featur-btn-pp').addClass('showpopup');
168
+ $('body').addClass('scrlnone');
169
+ });
170
+ $('body').click(function(evt){
171
+ if($(evt.target).closest('.upcoming-featur-btn, .upgradsbscrptnpp-cntr').length){
172
+ return;
173
+ }
174
+ $('.upcoming_featur-btn-pp').removeClass('showpopup');
175
+ $('body').removeClass('scrlnone');
176
+ });
177
+ /**
178
+ * Custom js code for API call
179
+ **/
180
+
181
+ //var start_date = moment().subtract(5, 'months').format('YYYY-MM-DD');
182
+ //var end_date = moment().subtract(1, 'days').format('YYYY-MM-DD');
183
+
184
+
185
+ $(".prmoclsbtn").on( "click", function() {
186
+ $(this).parents('.promobandtop').fadeOut()
187
+ });
188
+
189
+ /**
190
+ * click chart
191
+ **/
192
+ /*var click_ctx = document.getElementById('clickChart').getContext('2d');
193
+ var clcikgradientFill = click_ctx.createLinearGradient(0, 0, 0, 500);
194
+ clcikgradientFill.addColorStop(0.4, 'rgba(153, 170, 255, 0.9)');
195
+ clcikgradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.7)');
196
+ var myChart = new Chart(click_ctx, {
197
+
198
+ type: 'line',
199
+ data: {
200
+ labels: ['01/07/2021', '02/07/2021', '03/07/2021', '04/07/2021', '05/07/2021', '06/07/2021', '07/07/2021', '08/07/2021', '09/07/2021', '10/07/2021', '11/07/2021', '12/07/2021'],
201
+ datasets: [{
202
+ borderColor: '#002BFC',
203
+ pointBorderColor: '#002BFC',
204
+ pointBackgroundColor: '#fff',
205
+ pointBorderWidth: 1,
206
+ pointRadius: 2,
207
+ fill: true,
208
+ backgroundColor: clcikgradientFill,
209
+ borderWidth: 1,
210
+ data: [0,2500,2800,3000,4000,3200,4500,4400,3800,3400,3400,3950,2800,6000]
211
+ }]
212
+ },
213
+ options: {
214
+ animation: {
215
+ easing: "easeInOutBack"
216
+ },
217
+ plugins:{
218
+ legend:false
219
+ },
220
+ scales: {
221
+ y:{
222
+ fontColor: "#ffffff",
223
+ fontStyle: "normal",
224
+ beginAtZero: true,
225
+ maxTicksLimit: 5,
226
+ padding: 30,
227
+ grid:{
228
+ borderWidth:0,
229
+ },
230
+ ticks: {
231
+ stepSize: 1000,
232
+ callback: function(value) {
233
+ var ranges = [
234
+ { divider: 1e6, suffix: 'M' },
235
+ { divider: 1e3, suffix: 'k' }
236
+ ];
237
+ function formatNumber(n) {
238
+ for (var i = 0; i < ranges.length; i++) {
239
+ if (n >= ranges[i].divider) {
240
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
241
+ }
242
+ }
243
+ return n;
244
+ }
245
+ return '' + formatNumber(value);
246
+ }
247
+ }
248
+ },
249
+ x:{
250
+ padding: 10,
251
+ fontColor: "#ffffff",
252
+ fontStyle: "normal",
253
+ grid: {
254
+ display:false
255
+ }
256
+ }
257
+
258
+ },
259
+ }
260
+ });
261
+ /**
262
+ * cost chart
263
+ **/
264
+ /*var ctx = document.getElementById('myChart').getContext("2d");
265
+ var costgradientFill = ctx.createLinearGradient(0, 0, 0, 500);
266
+ costgradientFill.addColorStop(0.4, 'rgba(110, 245, 197, 0.9)');
267
+ costgradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.7)');
268
+
269
+ var myChart = new Chart(ctx, {
270
+ type: 'line',
271
+ data: {
272
+ labels: ['01/07/2021', '02/07/2021', '03/07/2021', '04/07/2021', '05/07/2021', '06/07/2021', '07/07/2021', '08/07/2021', '09/07/2021', '10/07/2021', '11/07/2021', '12/07/2021','13/07/2021',],
273
+ datasets: [{
274
+ borderColor: '#14AE77',
275
+ pointBorderColor: '#14AE77',
276
+ pointBackgroundColor: '#fff',
277
+ pointBorderWidth: 1,
278
+ pointRadius: 2,
279
+ fill: true,
280
+ backgroundColor: costgradientFill,
281
+ borderWidth: 1,
282
+ data: [0,2500,2800,3000,4000,3200,4500,4400,3800,3400,3400,3950,2800,6000]
283
+ }]
284
+ },
285
+ options: {
286
+ animation: {
287
+ easing: "easeInOutBack"
288
+ },
289
+ plugins:{
290
+ legend:false
291
+ },
292
+ scales: {
293
+ y:{
294
+ fontColor: "#ffffff",
295
+ fontStyle: "normal",
296
+ beginAtZero: true,
297
+ maxTicksLimit: 5,
298
+ padding: 30,
299
+ grid:{
300
+ borderWidth:0,
301
+ },
302
+ ticks: {
303
+ stepSize: 1000,
304
+ callback: function(value) {
305
+ var ranges = [
306
+ { divider: 1e6, suffix: 'M' },
307
+ { divider: 1e3, suffix: 'k' }
308
+ ];
309
+ function formatNumber(n) {
310
+ for (var i = 0; i < ranges.length; i++) {
311
+ if (n >= ranges[i].divider) {
312
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
313
+ }
314
+ }
315
+ return n;
316
+ }
317
+ return '' + formatNumber(value);
318
+ }
319
+ }
320
+ },
321
+ x:{
322
+ padding: 10,
323
+ fontColor: "#ffffff",
324
+ fontStyle: "normal",
325
+ grid: {
326
+ display:false
327
+ }
328
+ }
329
+
330
+ },
331
+ }
332
+ });
333
+ /**
334
+ * Conversions chart
335
+ **/
336
+ /*var conversions_ctx = document.getElementById('conversionsChart').getContext('2d');
337
+ var conversionsgradientFill = click_ctx.createLinearGradient(0, 0, 0, 500);
338
+ conversionsgradientFill.addColorStop(0.4, 'rgba(255, 229, 139, 0.9)');
339
+ conversionsgradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.7)');
340
+ var myChart = new Chart(conversions_ctx, {
341
+
342
+ type: 'line',
343
+ data: {
344
+ labels: ['01/07/2021', '02/07/2021', '03/07/2021', '04/07/2021', '05/07/2021', '06/07/2021', '07/07/2021', '08/07/2021', '09/07/2021', '10/07/2021', '11/07/2021', '12/07/2021'],
345
+ datasets: [{
346
+ borderColor: '#C0980E',
347
+ pointBorderColor: '#C0980E',
348
+ pointBackgroundColor: '#fff',
349
+ pointBorderWidth: 1,
350
+ pointRadius: 2,
351
+ fill: true,
352
+ backgroundColor: conversionsgradientFill,
353
+ borderWidth: 1,
354
+ data: [0,2500,2800,3000,4000,3200,4500,4400,3800,3400,3400,3950,2800,6000]
355
+ }]
356
+ },
357
+ options: {
358
+ animation: {
359
+ easing: "easeInOutBack"
360
+ },
361
+ plugins:{
362
+ legend:false
363
+ },
364
+ scales: {
365
+ y:{
366
+ fontColor: "#ffffff",
367
+ fontStyle: "normal",
368
+ beginAtZero: true,
369
+ maxTicksLimit: 5,
370
+ padding: 30,
371
+ grid:{
372
+ borderWidth:0,
373
+ },
374
+ ticks: {
375
+ stepSize: 1000,
376
+ callback: function(value) {
377
+ var ranges = [
378
+ { divider: 1e6, suffix: 'M' },
379
+ { divider: 1e3, suffix: 'k' }
380
+ ];
381
+ function formatNumber(n) {
382
+ for (var i = 0; i < ranges.length; i++) {
383
+ if (n >= ranges[i].divider) {
384
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
385
+ }
386
+ }
387
+ return n;
388
+ }
389
+ return '' + formatNumber(value);
390
+ }
391
+ }
392
+ },
393
+ x:{
394
+ padding: 10,
395
+ fontColor: "#ffffff",
396
+ fontStyle: "normal",
397
+ grid: {
398
+ display:false
399
+ }
400
+ }
401
+
402
+ },
403
+ }
404
+ });
405
+ /**
406
+ * Sales chart
407
+ **/
408
+ /*var sales_ctx = document.getElementById('salesChart').getContext('2d');
409
+ var salesgradientFill = click_ctx.createLinearGradient(0, 0, 0, 500);
410
+ salesgradientFill.addColorStop(0.4, 'rgba(107, 232, 255, 0.9)');
411
+ salesgradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.75)');
412
+
413
+ var myChart = new Chart(sales_ctx, {
414
+
415
+ type: 'line',
416
+ data: {
417
+ labels: ['01/07/2021', '02/07/2021', '03/07/2021', '04/07/2021', '05/07/2021', '06/07/2021', '07/07/2021', '08/07/2021', '09/07/2021', '10/07/2021', '11/07/2021', '12/07/2021'],
418
+ datasets: [{
419
+ borderColor: '#159EB8',
420
+ pointBorderColor: '#159EB8',
421
+ pointBackgroundColor: '#fff',
422
+ pointBorderWidth: 1,
423
+ pointRadius: 2,
424
+ fill: true,
425
+ backgroundColor: salesgradientFill,
426
+ borderWidth: 1,
427
+ data: [0,2500,2800,3000,4000,3200,4500,4400,3800,3400,3400,3950,2800,6000]
428
+ }]
429
+ },
430
+ options: {
431
+ animation: {
432
+ easing: "easeInOutBack"
433
+ },
434
+ plugins:{
435
+ legend:false
436
+ },
437
+ scales: {
438
+ y:{
439
+ fontColor: "#ffffff",
440
+ fontStyle: "normal",
441
+ beginAtZero: true,
442
+ maxTicksLimit: 5,
443
+ padding: 30,
444
+ grid:{
445
+ borderWidth:0,
446
+ },
447
+ ticks: {
448
+ stepSize: 1000,
449
+ callback: function(value) {
450
+ var ranges = [
451
+ { divider: 1e6, suffix: 'M' },
452
+ { divider: 1e3, suffix: 'k' }
453
+ ];
454
+ function formatNumber(n) {
455
+ for (var i = 0; i < ranges.length; i++) {
456
+ if (n >= ranges[i].divider) {
457
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
458
+ }
459
+ }
460
+ return n;
461
+ }
462
+ return '' + formatNumber(value);
463
+ }
464
+ }
465
+
466
+ },
467
+ x:{
468
+ padding: 10,
469
+ fontColor: "#ffffff",
470
+ fontStyle: "normal",
471
+ grid: {
472
+ display:false
473
+ }
474
+ }
475
+ },
476
+ }
477
+ });
478
+ /**
479
+ * End chart
480
+ * table responcive
481
+ **/
482
+ $('.mbl-table').basictable({
483
+ breakpoint: 768
484
+ });
485
+
486
+
487
+ });
488
+ </script>
489
+ <?php
490
+ }
491
+ protected function add_upgrdsbrs_btn_calss($featur_name){
492
+ if($this->plan_id == 1){
493
+ return "upgrdsbrs-btn";
494
+ }else if($featur_name != ""){
495
+ $upcoming_featur = array('download_pdf','schedule_email');
496
+ if(in_array($featur_name, $upcoming_featur)){
497
+ return "upcoming-featur-btn";
498
+ }
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Main html code
504
+ *
505
+ * @since 4.1.4
506
+ */
507
+ public function current_html(){
508
+ ?>
509
+ <div class="dashbrdpage-wrap">
510
+ <div class="dflex align-items-center mt24 dshbrdtoparea">
511
+ <div class="dashtp-left">
512
+ <button class="dashtpleft-btn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/download-icon.png'; ?>" alt="" />Download PDF</button>
513
+ <button class="dashtpleft-btn <?php echo $this->add_upgrdsbrs_btn_calss('schedule_email'); ?>"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/clock-icon.png'; ?>" alt="" />Schedule Email</button>
514
+ </div>
515
+ <div class="dashtp-right">
516
+
517
+ <?php /*
518
+ <div class="dshtprightselect">
519
+ <select>
520
+ <option>All</option>
521
+ <option>All</option>
522
+ <option>All</option>
523
+ </select>
524
+ </div>*/ ?>
525
+ <?php if($this->plan_id != 1){?>
526
+ <div id="reportrange" class="dshtpdaterange" >
527
+ <div class="dateclndicn">
528
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/claendar-icon.png'; ?>" alt="" />
529
+ </div>
530
+ <span class="daterangearea report_range_val"></span>
531
+ <div class="careticn"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/caret-down.png'; ?>" alt="" /></div>
532
+ </div>
533
+ <?php } else{ ?>
534
+ <div class="dshtpdaterange <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>">
535
+ <div class="dateclndicn">
536
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/claendar-icon.png'; ?>" alt="" />
537
+ </div>
538
+ <span class="daterangearea report_range_val"></span>
539
+ <div class="careticn"><img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/caret-down.png'; ?>" alt="" /></div>
540
+ </div>
541
+ <?php }?>
542
+
543
+ </div>
544
+ </div>
545
+ <?php if($this->ga_traking_type == "GA4"){?>
546
+ <div class="temp_note"><p>The reporting dashboard feature is only available for Google Analytics 3 properties currently. We are working on Google Analytics 4 dashboard, we will update you once it is live.</p></div>
547
+ <?php } ?>
548
+ <!--- dashboard summary section start -->
549
+ <div class="wht-rnd-shdwbx mt24 dashsmry-wrap">
550
+ <div class="dashsmry-item">
551
+ <div class="dashsmrybx" id="s1_transactionsPerSession">
552
+ <div class="dshsmrycattxt dash-smry-title">Conversion Rate</div>
553
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
554
+ <div class="updownsmry dash-smry-compare-val">
555
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/green-up.png'; ?>" alt="" />
556
+ %
557
+ </div>
558
+ <div class="dshsmryprdtxt">From Previous Period</div>
559
+ </div>
560
+ <div class="dashsmrybx" id="s1_transactionRevenue">
561
+ <div class="dshsmrycattxt dash-smry-title">Revenue </div>
562
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
563
+ <div class="updownsmry dash-smry-compare-val">
564
+ %
565
+ </div>
566
+ <div class="dshsmryprdtxt">From Previous Period</div>
567
+ </div>
568
+ <div class="dashsmrybx mblsmry3bx" id="s1_transactions">
569
+ <div class="dshsmrycattxt dash-smry-title">Total Transactions </div>
570
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
571
+ <div class="updownsmry dash-smry-compare-val">
572
+ %
573
+ </div>
574
+ <div class="dshsmryprdtxt">From Previous Period</div>
575
+ </div>
576
+ <div class="dashsmrybx mblsmry3bx" id="s1_revenuePerTransaction">
577
+ <div class="dshsmrycattxt dash-smry-title">Avg. Order Value</div>
578
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
579
+ <div class="updownsmry dash-smry-compare-val">
580
+ %
581
+ </div>
582
+ <div class="dshsmryprdtxt">From Previous Period</div>
583
+ </div>
584
+ <div class="dashsmrybx mblsmry3bx flwdthmblbx" id="s1_productAddsToCart">
585
+ <div class="dshsmrycattxt dash-smry-title">Added to Cart</div>
586
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
587
+ <div class="updownsmry dash-smry-compare-val">
588
+ %
589
+ </div>
590
+ <div class="dshsmryprdtxt">From Previous Period</div>
591
+ </div>
592
+ </div>
593
+ <div class="dashsmry-item">
594
+ <div class="dashsmrybx" id="s1_productRemovesFromCart">
595
+ <div class="dshsmrycattxt dash-smry-title">Removed from Cart</div>
596
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
597
+ <div class="updownsmry dash-smry-compare-val">
598
+ %
599
+ </div>
600
+ <div class="dshsmryprdtxt">From Previous Period</div>
601
+ </div>
602
+ <div class="dashsmrybx" id="s1_sessions">
603
+ <div class="dshsmrycattxt dash-smry-title">Sessions</div>
604
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
605
+ <div class="updownsmry dash-smry-compare-val">
606
+ %
607
+ </div>
608
+ <div class="dshsmryprdtxt">From Previous Period</div>
609
+ </div>
610
+ <div class="dashsmrybx" id="s1_users">
611
+ <div class="dshsmrycattxt dash-smry-title">Total Users</div>
612
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
613
+ <div class="updownsmry dash-smry-compare-val">
614
+ %
615
+ </div>
616
+ <div class="dshsmryprdtxt">From Previous Period</div>
617
+ </div>
618
+ <div class="dashsmrybx" id="s1_newUsers">
619
+ <div class="dshsmrycattxt dash-smry-title">New Users</div>
620
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
621
+ <div class="updownsmry dash-smry-compare-val">
622
+ %
623
+ </div>
624
+ <div class="dshsmryprdtxt">From Previous Period</div>
625
+ </div>
626
+ <div class="dashsmrybx" id="s1_productDetailViews">
627
+ <div class="dshsmrycattxt dash-smry-title">Product Views</div>
628
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
629
+ <div class="updownsmry dash-smry-compare-val">
630
+ %
631
+ </div>
632
+ <div class="dshsmryprdtxt">From Previous Period</div>
633
+ </div>
634
+
635
+ </div>
636
+ <?php /*
637
+ <div class="dashsmry-item">
638
+ <div class="dashsmrybx" id="s1_transactionShipping">
639
+ <div class="dshsmrycattxt dash-smry-title">Shipping</div>
640
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
641
+ <div class="updownsmry dash-smry-compare-val">
642
+ %
643
+ </div>
644
+ <div class="dshsmryprdtxt">From Previous Period</div>
645
+ </div>
646
+ <div class="dashsmrybx" id="s1_transactionTax">
647
+ <div class="dshsmrycattxt dash-smry-title">TAX</div>
648
+ <div class="dshsmrylrgtxt dash-smry-value">-</div>
649
+ <div class="updownsmry dash-smry-compare-val">
650
+ %
651
+ </div>
652
+ <div class="dshsmryprdtxt">From Previous Period</div>
653
+ </div>
654
+
655
+ </div> */?>
656
+ </div>
657
+ <!--- dashboard summary section end -->
658
+
659
+ <!--- dashboard ecommerce cahrt section start -->
660
+ <div class="mt24 dshchrtwrp ecomfunnchart">
661
+ <div class="row">
662
+ <?php if($this->plan_id != 1){?>
663
+ <div class="col50">
664
+ <div class="chartbx ecomfunnchrtbx ecom-funn-chrt-bx">
665
+ <div class="chartcntnbx">
666
+ <h5>Ecommerce Conversion Funnel</h5>
667
+ <div class="chartarea">
668
+ <canvas id="ecomfunchart" width="400" height="300"></canvas>
669
+ </div>
670
+ <hr>
671
+ <div class="ecomchartinfo">
672
+ <div class="ecomchrtinfoflex">
673
+ <div class="ecomchartinfoitem">
674
+ <div class="ecomchartinfolabel">Sessions</div>
675
+ <div class="chartpercarrow conversion_s1"></div>
676
+ </div>
677
+ <div class="ecomchartinfoitem">
678
+ <div class="ecomchartinfolabel">Product View</div>
679
+ <div class="chartpercarrow conversion_s2"></div>
680
+ </div>
681
+ <div class="ecomchartinfoitem">
682
+ <div class="ecomchartinfolabel">Add to Cart</div>
683
+ <div class="chartpercarrow conversion_s3"></div>
684
+ </div>
685
+ <div class="ecomchartinfoitem">
686
+ <div class="ecomchartinfolabel">Checkouts</div>
687
+ <div class="chartpercarrow conversion_s4"></div>
688
+ </div>
689
+ <div class="ecomchartinfoitem">
690
+ <div class="ecomchartinfolabel">Order Confirmation</div>
691
+ </div>
692
+ </div>
693
+ </div>
694
+ </div>
695
+ </div>
696
+ </div>
697
+ <?php }else{ ?>
698
+ <div class="col50">
699
+ <div class="chartbx ecomfunnchrtbx">
700
+ <div class="chartcntnbx prochrtftr">
701
+ <h5>Ecommerce Conversion Funnel</h5>
702
+ <div class="chartarea">
703
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/ecom-chart.jpg'; ?>" alt="" />
704
+ </div>
705
+ </div>
706
+ <div class="prochrtovrbox">
707
+ <div class="prochrtcntn">
708
+ <div class="prochrttop">
709
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
710
+ Locked
711
+ </div>
712
+ <h5>Conversion Funnel</h5>
713
+ <p>This Report will help you visualize drop offs at each stage of your shopping funnel starting from home page to product page, cart page to checkout page and to final order confirmation page. Find out the major drop offs at each stage and take informed data driven decisions to increase the conversions and better marketing ROI.</p>
714
+ <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank">Upgrade Now</a>
715
+ </div>
716
+ </div>
717
+ </div>
718
+ </div>
719
+ <?php }?>
720
+
721
+ <?php if($this->plan_id != 1){?>
722
+ <div class="col50">
723
+ <div class="chartbx ecomfunnchrtbx ecom-checkout-funn-chrt-bx">
724
+ <div class="chartcntnbx">
725
+ <h5>Ecommerce Checkout Funnel</h5>
726
+ <div class="chartarea">
727
+ <canvas id="ecomcheckoutfunchart" width="400" height="300"></canvas>
728
+ </div>
729
+ </div>
730
+ </div>
731
+ </div>
732
+ <?php }else{ ?>
733
+ <div class="col50">
734
+ <div class="chartbx ecomfunnchrtbx">
735
+ <div class="chartcntnbx prochrtftr">
736
+ <h5>Checkout Funnel</h5>
737
+ <div class="chartarea">
738
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/ecom-chart.jpg'; ?>" alt="" />
739
+ </div>
740
+ </div>
741
+ <div class="prochrtovrbox">
742
+ <div class="prochrtcntn">
743
+ <div class="prochrttop">
744
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
745
+ Locked
746
+ </div>
747
+ <h5>Checkout Funnel</h5>
748
+ <p>This Report will help you in finding out the performance of your checkout page and leakages at each checkout step. Identify the small areas of improvements and fix them for smooth customer experience on your ecommerce site.</p>
749
+ <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank">Upgrade Now</a>
750
+ </div>
751
+ </div>
752
+ </div>
753
+ </div>
754
+ <?php } ?>
755
+
756
+ </div>
757
+ </div>
758
+ <!--- dashboard ecommerce cahrt section over -->
759
+
760
+ <!--- Product Performance section start -->
761
+ <?php if($this->plan_id != 1){?>
762
+ <div class="mt24 whiteroundedbx dshreport-sec">
763
+ <div class="row dsh-reprttop">
764
+ <div class="dshrprttp-left">
765
+ <h4>Product Performance Report</h4>
766
+ <a href="" class="viewallbtn">View all <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'; ?>" alt="" /></a>
767
+ </div>
768
+ </div>
769
+ <div class="dashtablewrp product_performance_report" id="product_performance_report">
770
+ <table class="dshreporttble mbl-table" >
771
+ <thead>
772
+ <tr>
773
+ <th class="prdnm-cell">Product Name</th>
774
+ <th>Views</th>
775
+ <th>Added to Cart</th>
776
+ <th>Orders</th>
777
+ <th>Qty</th>
778
+ <th>Revenue (<?php echo $this->ga_currency_symbols; ?>)</th>
779
+ <th>Avg Price (<?php echo $this->ga_currency_symbols; ?>)</th>
780
+ <th>Refund Amount (<?php echo $this->ga_currency_symbols; ?>)</th>
781
+ <th>Cart to details (%)</th>
782
+ <th>Buy to details (%)</th>
783
+ </tr>
784
+ </thead>
785
+ <tbody>
786
+ </tbody>
787
+ </table>
788
+ </div>
789
+ </div>
790
+ <?php }else{ ?>
791
+ <div class="mt24 whiteroundedbx dshreport-sec">
792
+ <div class="row dsh-reprttop">
793
+ <div class="col">
794
+ <div class="chartbx ecomfunnchrtbx">
795
+ <div class="chartcntnbx prochrtftr">
796
+ <h5>Product Performance Report</h5>
797
+ <div class="chartarea">
798
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'; ?>" alt="" />
799
+ </div>
800
+ </div>
801
+ <div class="prochrtovrbox">
802
+ <div class="prochrtcntn">
803
+ <div class="prochrttop">
804
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
805
+ Locked
806
+ </div>
807
+ <h5>Product Performance Report</h5>
808
+ <p>This report will help you understand how products in your store are performing and based on it you can take informed merchandising decision to further increase your revenue.</p>
809
+ <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank">Upgrade Now</a>
810
+ </div>
811
+ </div>
812
+ </div>
813
+ </div>
814
+ </div>
815
+ </div>
816
+ <?php } ?>
817
+ <!--- Product Performance section over -->
818
+
819
+ <!--- Source Performance Report section start -->
820
+ <?php if($this->plan_id != 1){?>
821
+ <div class="mt24 whiteroundedbx dshreport-sec">
822
+ <div class="row dsh-reprttop">
823
+ <div class="dshrprttp-left">
824
+ <h4>Source/Medium Performance Report</h4>
825
+ <a href="" class="viewallbtn">View all <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'; ?>" alt="" /></a>
826
+ </div>
827
+ </div>
828
+ <div class="dashtablewrp medium_performance_report" id="medium_performance_report">
829
+ <table class="dshreporttble mbl-table" >
830
+ <thead>
831
+ <tr>
832
+ <th class="prdnm-cell">Source/Medium</th>
833
+ <th>Conversion (%)</th>
834
+ <th>Revenue (<?php echo $this->ga_currency_symbols; ?>)</th>
835
+ <th>Total transactions</th>
836
+ <th>Avg Order value (<?php echo $this->ga_currency_symbols; ?>)</th>
837
+ <th>Added to carts</th>
838
+ <th>removed from cart</th>
839
+ <th>Product views</th>
840
+ <th>Users</th>
841
+ <th>Sessions</th>
842
+ </tr>
843
+ </thead>
844
+ <tbody>
845
+
846
+ </tbody>
847
+ </table>
848
+ </div>
849
+ </div>
850
+ <?php }else{ ?>
851
+ <div class="mt24 whiteroundedbx dshreport-sec">
852
+ <div class="row dsh-reprttop">
853
+ <div class="col">
854
+ <div class="chartbx ecomfunnchrtbx">
855
+ <div class="chartcntnbx prochrtftr">
856
+ <h5>Source/Medium Performance Report</h5>
857
+ <div class="chartarea">
858
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'; ?>" alt="" />
859
+ </div>
860
+ </div>
861
+ <div class="prochrtovrbox">
862
+ <div class="prochrtcntn">
863
+ <div class="prochrttop">
864
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
865
+ Locked
866
+ </div>
867
+ <h5>Source/Medium Performance Report</h5>
868
+ <p>Find out the performance of each of your traffic channels. You can access which campaigns or channels are attributing sales, add to carts, product views etc. You can also see your shopping and checkout behavior funnels for each of the channels and take informed decisions of managing your ad spends for each channel.</p>
869
+ <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank">Upgrade Now</a>
870
+ </div>
871
+ </div>
872
+ </div>
873
+ </div>
874
+ </div>
875
+ </div>
876
+ <?php } ?>
877
+ <!--- Source Performance Report section over -->
878
+
879
+
880
+ <!--- Shopping and Google Ads Performance section start -->
881
+ <?php /*
882
+ <div class="mt24 whiteroundedbx ggladsperfom-sec">
883
+ <h4>Shopping and Google Ads Performance</h4>
884
+ <div class="row">
885
+ <div class="col50">
886
+ <div class="chartbx ggladschrtbx">
887
+ <div class="chartcntnbx">
888
+ <h5>Clicks</h5>
889
+ <div class="chartarea">
890
+ <canvas id="clickChart" width="400" height="300" class="chartcntainer"></canvas>
891
+ </div>
892
+ </div>
893
+ </div>
894
+ </div>
895
+ <div class="col50">
896
+ <div class="chartbx ggladschrtbx">
897
+ <div class="chartcntnbx">
898
+ <h5>Cost</h5>
899
+ <div class="chartarea">
900
+ <canvas id="myChart" width="400" height="300" class="chartcntainer"></canvas>
901
+ </div>
902
+ </div>
903
+ </div>
904
+ </div>
905
+
906
+ <div class="col50">
907
+ <div class="chartbx ggladschrtbx">
908
+ <div class="chartcntnbx">
909
+ <h5>Conversions</h5>
910
+ <div class="chartarea">
911
+ <canvas id="conversionsChart" width="400" height="300" class="chartcntainer"></canvas>
912
+ </div>
913
+ </div>
914
+ </div>
915
+ </div>
916
+ <div class="col50">
917
+ <div class="chartbx ggladschrtbx">
918
+ <div class="chartcntnbx">
919
+ <h5>Sales</h5>
920
+ <div class="chartarea">
921
+ <canvas id="salesChart" width="400" height="300" class="chartcntainer"></canvas>
922
+ </div>
923
+ </div>
924
+ </div>
925
+ </div>
926
+ </div>
927
+ </div> */ ?>
928
+ <!--- Shopping and Google Ads Performance section start -->
929
+ <!-- UPGRADE SUBSCRIPTION -->
930
+ <div id="upgradsbscrptn" class="pp-modal whitepopup upgradsbscrptn-pp">
931
+ <div class="sycnprdct-ppcnt">
932
+ <div class="ppwhitebg pp-content upgradsbscrptnpp-cntr">
933
+ <div class="ppclsbtn absltpsclsbtn clsbtntrgr">
934
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/close-white.png'; ?>" alt="">
935
+ </div>
936
+ <div class="upgradsbscrptnpp-hdr">
937
+ <h5>Upgrade to Pro..!!</h5>
938
+ </div>
939
+ <div class="ppmodal-body">
940
+ <p>This feature is only available in the paid plan. Please upgrade to get the full range of reports and more.</p>
941
+ <div class="ppupgrdbtnwrap">
942
+ <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank">Upgrade Now</a>
943
+ </div>
944
+ </div>
945
+ </div>
946
+ </div>
947
+ </div>
948
+ <!-- Upcoming featur -->
949
+ <div id="upcoming_featur" class="pp-modal whitepopup upcoming_featur-btn-pp">
950
+ <div class="sycnprdct-ppcnt">
951
+ <div class="ppwhitebg pp-content upgradsbscrptnpp-cntr">
952
+ <div class="ppclsbtn absltpsclsbtn clsbtntrgr">
953
+ <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/close-white.png'; ?>" alt="">
954
+ </div>
955
+ <div class="upgradsbscrptnpp-hdr">
956
+ <h5>Upcoming..!!</h5>
957
+ </div>
958
+ <div class="ppmodal-body">
959
+ <p>We are currently working on this feature and we will reach out to you once this is live.</p>
960
+ <p>We aim to give you a capability to schedule the reports directly in your inbox whenever you want.</p>
961
+ </div>
962
+ </div>
963
+ </div>
964
+ </div>
965
+
966
+ </div>
967
+ <?php
968
+ }
969
+ }
970
+ }
971
+ new Conversios_Dashboard();
includes/setup/class-tvc-product-sync-helper.php CHANGED
@@ -21,7 +21,7 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
21
  $this->currentCustomerId = $this->TVC_Admin_Helper->get_currentCustomerId();
22
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
23
  $this->country = $this->TVC_Admin_Helper->get_woo_country();
24
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
25
  add_action('admin_init', array($this,'add_table_in_db'));
26
  }
27
  public function includes(){
@@ -652,13 +652,12 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
652
  var customer_id = '<?php echo $this->currentCustomerId?>';
653
  var parent = "";
654
  jQuery.post(
655
- myAjaxNonces.ajaxurl,
656
  {
657
  action: "tvcajax-gmc-category-lists",
658
  countryCode: country_id,
659
  customerId: customer_id,
660
- parent: parent,
661
- gmcCategoryListsNonce: myAjaxNonces.gmcCategoryListsNonce
662
  },
663
  function( response ) {
664
  var categories = JSON.parse(response);
@@ -695,13 +694,12 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
695
  var country_id = "<?php echo $this->country; ?>";
696
  var customer_id = '<?php echo $this->currentCustomerId?>';
697
  jQuery.post(
698
- myAjaxNonces.ajaxurl,
699
  {
700
  action: "tvcajax-gmc-category-lists",
701
  countryCode: country_id,
702
  customerId: customer_id,
703
- parent: GmcParent,
704
- gmcCategoryListsNonce: myAjaxNonces.gmcCategoryListsNonce
705
  },
706
  function( response ) {
707
  var categories = JSON.parse(response);
21
  $this->currentCustomerId = $this->TVC_Admin_Helper->get_currentCustomerId();
22
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
23
  $this->country = $this->TVC_Admin_Helper->get_woo_country();
24
+ $this->site_url = "admin.php?page=conversios-google-shopping-feed&tab=";
25
  add_action('admin_init', array($this,'add_table_in_db'));
26
  }
27
  public function includes(){
652
  var customer_id = '<?php echo $this->currentCustomerId?>';
653
  var parent = "";
654
  jQuery.post(
655
+ tvc_ajax_url,
656
  {
657
  action: "tvcajax-gmc-category-lists",
658
  countryCode: country_id,
659
  customerId: customer_id,
660
+ parent: parent
 
661
  },
662
  function( response ) {
663
  var categories = JSON.parse(response);
694
  var country_id = "<?php echo $this->country; ?>";
695
  var customer_id = '<?php echo $this->currentCustomerId?>';
696
  jQuery.post(
697
+ tvc_ajax_url,
698
  {
699
  action: "tvcajax-gmc-category-lists",
700
  countryCode: country_id,
701
  customerId: customer_id,
702
+ parent: GmcParent
 
703
  },
704
  function( response ) {
705
  var categories = JSON.parse(response);
includes/setup/google-shopping-feed-gaa-config.php CHANGED
@@ -10,7 +10,7 @@ class GAAConfiguration {
10
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
11
  $this->TVCProductSyncHelper = new TVCProductSyncHelper();
12
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
13
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
14
  $this->url = $this->TVC_Admin_Helper->get_onboarding_page_url();
15
  $this->html_run();
16
  }
@@ -111,7 +111,7 @@ class GAAConfiguration {
111
  <h2 class="ga-title">Smart Shopping Campaigns:</h2>
112
  </div>
113
  <div class="col-6 col-md-6">
114
- <a href="admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=add_campaign_page" class="btn btn-primary btn-success">Create Smart Shopping Campaign</a>
115
  </div>
116
  </div>
117
  <?php }else{ ?>
@@ -159,9 +159,8 @@ if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merc
159
  var is_need_to_domain_claim = "<?php echo $is_need_to_domain_claim; ?>";
160
  if(is_need_to_domain_claim == 1 || is_need_to_domain_claim == true){
161
  event.preventDefault();
162
- jQuery.post(myAjaxNonces.ajaxurl,{
163
- action: "tvc_call_domain_claim",
164
- apiDomainClaimNonce: myAjaxNonces.apiDomainClaimNonce
165
  },function( response ){
166
 
167
  });
10
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
11
  $this->TVCProductSyncHelper = new TVCProductSyncHelper();
12
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
13
+ $this->site_url = "admin.php?page=conversios-google-shopping-feed&tab=";
14
  $this->url = $this->TVC_Admin_Helper->get_onboarding_page_url();
15
  $this->html_run();
16
  }
111
  <h2 class="ga-title">Smart Shopping Campaigns:</h2>
112
  </div>
113
  <div class="col-6 col-md-6">
114
+ <a href="admin.php?page=conversios-google-shopping-feed&tab=add_campaign_page" class="btn btn-primary btn-success">Create Smart Shopping Campaign</a>
115
  </div>
116
  </div>
117
  <?php }else{ ?>
159
  var is_need_to_domain_claim = "<?php echo $is_need_to_domain_claim; ?>";
160
  if(is_need_to_domain_claim == 1 || is_need_to_domain_claim == true){
161
  event.preventDefault();
162
+ jQuery.post(tvc_ajax_url,{
163
+ action: "tvc_call_domain_claim"
 
164
  },function( response ){
165
 
166
  });
includes/setup/google-shopping-feed-shopping-campaigns.php CHANGED
@@ -26,7 +26,7 @@ class CampaignsConfiguration
26
  $this->to_date = (isset($_POST['to_date']) && $_POST['to_date'] != '') ? $_POST['to_date'] : "";
27
  $this->country = $this->TVC_Admin_Helper->get_woo_country();
28
  $this->currency_symbol = $this->TVC_Admin_Helper->get_user_currency_symbol();
29
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
30
  $this->html_run();
31
  }
32
 
@@ -633,13 +633,12 @@ class CampaignsConfiguration
633
  if(confirm) {
634
  jQuery("#feed-spinner").css("display", "block");
635
  jQuery.post(
636
- myAjaxNonces.ajaxurl,
637
  {
638
  action: "tvcajax-delete-campaign",
639
  merchantId: merchantId,
640
  customerId: customerId,
641
- campaignId: campaignId,
642
- campaignDeleteNonce: myAjaxNonces.campaignDeleteNonce
643
  },
644
  function( response ) {
645
  jQuery("#feed-spinner").css("display", "none");
@@ -663,7 +662,7 @@ class CampaignsConfiguration
663
  var campaign_name = jQuery("#campaign_name_"+currentRow).val();
664
  jQuery("#feed-spinner").css("display", "block");
665
  jQuery.post(
666
- myAjaxNonces.ajaxurl,
667
  {
668
  action: "tvcajax-update-campaign-status",
669
  merchantId: merchantId,
@@ -672,8 +671,7 @@ class CampaignsConfiguration
672
  campaignName: campaign_name,
673
  budget: budget,
674
  budgetId: budgetId,
675
- status: campaign_status == true ? 2 : 3,
676
- campaignStatusNonce: myAjaxNonces.campaignStatusNonce
677
  },
678
  function( response ) {
679
  jQuery("#feed-spinner").css("display", "none");
26
  $this->to_date = (isset($_POST['to_date']) && $_POST['to_date'] != '') ? $_POST['to_date'] : "";
27
  $this->country = $this->TVC_Admin_Helper->get_woo_country();
28
  $this->currency_symbol = $this->TVC_Admin_Helper->get_user_currency_symbol();
29
+ $this->site_url = "admin.php?page=conversios-google-shopping-feed&tab=";
30
  $this->html_run();
31
  }
32
 
633
  if(confirm) {
634
  jQuery("#feed-spinner").css("display", "block");
635
  jQuery.post(
636
+ tvc_ajax_url,
637
  {
638
  action: "tvcajax-delete-campaign",
639
  merchantId: merchantId,
640
  customerId: customerId,
641
+ campaignId: campaignId
 
642
  },
643
  function( response ) {
644
  jQuery("#feed-spinner").css("display", "none");
662
  var campaign_name = jQuery("#campaign_name_"+currentRow).val();
663
  jQuery("#feed-spinner").css("display", "block");
664
  jQuery.post(
665
+ tvc_ajax_url,
666
  {
667
  action: "tvcajax-update-campaign-status",
668
  merchantId: merchantId,
671
  campaignName: campaign_name,
672
  budget: budget,
673
  budgetId: budgetId,
674
+ status: campaign_status == true ? 2 : 3
 
675
  },
676
  function( response ) {
677
  jQuery("#feed-spinner").css("display", "none");
includes/setup/google-shopping-feed-sync-product.php CHANGED
@@ -12,7 +12,7 @@ public function __construct(){
12
  $this->TVC_Admin_DB_Helper = new TVC_Admin_DB_Helper();
13
  $this->TVCProductSyncHelper = new TVCProductSyncHelper();
14
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
15
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
16
  $this->TVC_Admin_Helper->need_auto_update_db();
17
  $this->html_run();
18
  }
@@ -231,9 +231,8 @@ $(document).ready(function() {
231
  var is_need_to_domain_claim = "<?php echo $is_need_to_domain_claim; ?>";
232
  if(is_need_to_domain_claim == 1 || is_need_to_domain_claim == true){
233
  event.preventDefault();
234
- jQuery.post(myAjaxNonces.ajaxurl,{
235
- action: "tvc_call_domain_claim",
236
- apiDomainClaimNonce: myAjaxNonces.apiDomainClaimNonce
237
  },function( response ){
238
 
239
  });
@@ -247,9 +246,8 @@ function call_tvc_api_sync_up(){
247
  $("#refresh_api").css("visibility","hidden");
248
  $(tvs_this).after('<div class="tvc-nb-spinner" id="tvc-nb-spinner"></div>');
249
  tvc_helper.tvc_alert("error","Attention !","Sync up is in the process do not refresh the page. it may take few minutes, if GMC product sync count is large.");
250
- jQuery.post(myAjaxNonces.ajaxurl,{
251
- action: "tvc_call_api_sync",
252
- apiSyncupNonce: myAjaxNonces.apiSyncupNonce
253
  },function( response ){
254
  var rsp = JSON.parse(response);
255
  if(rsp.error == false){
12
  $this->TVC_Admin_DB_Helper = new TVC_Admin_DB_Helper();
13
  $this->TVCProductSyncHelper = new TVCProductSyncHelper();
14
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
15
+ $this->site_url = "admin.php?page=conversios-google-shopping-feed&tab=";
16
  $this->TVC_Admin_Helper->need_auto_update_db();
17
  $this->html_run();
18
  }
231
  var is_need_to_domain_claim = "<?php echo $is_need_to_domain_claim; ?>";
232
  if(is_need_to_domain_claim == 1 || is_need_to_domain_claim == true){
233
  event.preventDefault();
234
+ jQuery.post(tvc_ajax_url,{
235
+ action: "tvc_call_domain_claim"
 
236
  },function( response ){
237
 
238
  });
246
  $("#refresh_api").css("visibility","hidden");
247
  $(tvs_this).after('<div class="tvc-nb-spinner" id="tvc-nb-spinner"></div>');
248
  tvc_helper.tvc_alert("error","Attention !","Sync up is in the process do not refresh the page. it may take few minutes, if GMC product sync count is large.");
249
+ jQuery.post(tvc_ajax_url,{
250
+ action: "tvc_call_api_sync"
 
251
  },function( response ){
252
  var rsp = JSON.parse(response);
253
  if(rsp.error == false){
includes/setup/google-shopping-feed.php CHANGED
@@ -6,7 +6,7 @@ class GoogleShoppingFeed {
6
  public function __construct() {
7
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
8
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
9
- $this->site_url = "admin.php?page=enhanced-ecommerce-google-analytics-admin-display&tab=";
10
  $this->TVC_Admin_Helper->need_auto_update_db();
11
  $this->create_form();
12
  }
@@ -259,9 +259,8 @@ class GoogleShoppingFeed {
259
  var tvs_this = event.target;
260
  $("#refresh_call_site_verified").css("visibility","hidden");
261
  $(tvs_this).after('<div class="domain-claim-spinner tvc-nb-spinner" id="site-verified-spinner"></div>');
262
- jQuery.post(myAjaxNonces.ajaxurl,{
263
- action: "tvc_call_site_verified",
264
- apiDomainClaimNonce: myAjaxNonces.SiteVerifiedNonce
265
  },function( response ){
266
  var rsp = JSON.parse(response);
267
  if(rsp.status == "success"){
@@ -277,9 +276,8 @@ class GoogleShoppingFeed {
277
  var tvs_this = event.target;
278
  $("#refresh_call_domain_claim").css("visibility","hidden");
279
  $(tvs_this).after('<div class="domain-claim-spinner tvc-nb-spinner" id="domain-claim-spinner"></div>');
280
- jQuery.post(myAjaxNonces.ajaxurl,{
281
- action: "tvc_call_domain_claim",
282
- apiDomainClaimNonce: myAjaxNonces.apiDomainClaimNonce
283
  },function( response ){
284
  var rsp = JSON.parse(response);
285
  if(rsp.status == "success"){
@@ -304,9 +302,8 @@ class GoogleShoppingFeed {
304
  $("#refresh_api").css("visibility","hidden");
305
  $(tvs_this).after('<div class="tvc-nb-spinner" id="tvc-nb-spinner"></div>');
306
  tvc_helper.tvc_alert("error","Attention !","Sync up is in the process do not refresh the page. it may take few minutes, if GMC product sync count is large.");
307
- jQuery.post(myAjaxNonces.ajaxurl,{
308
- action: "tvc_call_api_sync",
309
- apiSyncupNonce: myAjaxNonces.apiSyncupNonce
310
  },function( response ){
311
  var rsp = JSON.parse(response);
312
  if(rsp.error == false){
6
  public function __construct() {
7
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
8
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();
9
+ $this->site_url = "admin.php?page=conversios-google-shopping-feed&tab=";
10
  $this->TVC_Admin_Helper->need_auto_update_db();
11
  $this->create_form();
12
  }
259
  var tvs_this = event.target;
260
  $("#refresh_call_site_verified").css("visibility","hidden");
261
  $(tvs_this).after('<div class="domain-claim-spinner tvc-nb-spinner" id="site-verified-spinner"></div>');
262
+ jQuery.post(tvc_ajax_url,{
263
+ action: "tvc_call_site_verified"
 
264
  },function( response ){
265
  var rsp = JSON.parse(response);
266
  if(rsp.status == "success"){
276
  var tvs_this = event.target;
277
  $("#refresh_call_domain_claim").css("visibility","hidden");
278
  $(tvs_this).after('<div class="domain-claim-spinner tvc-nb-spinner" id="domain-claim-spinner"></div>');
279
+ jQuery.post(tvc_ajax_url,{
280
+ action: "tvc_call_domain_claim"
 
281
  },function( response ){
282
  var rsp = JSON.parse(response);
283
  if(rsp.status == "success"){
302
  $("#refresh_api").css("visibility","hidden");
303
  $(tvs_this).after('<div class="tvc-nb-spinner" id="tvc-nb-spinner"></div>');
304
  tvc_helper.tvc_alert("error","Attention !","Sync up is in the process do not refresh the page. it may take few minutes, if GMC product sync count is large.");
305
+ jQuery.post(tvc_ajax_url,{
306
+ action: "tvc_call_api_sync"
 
307
  },function( response ){
308
  var rsp = JSON.parse(response);
309
  if(rsp.error == false){
readme.txt CHANGED
@@ -6,10 +6,10 @@ Tags: Google Analytics tracking, Dynamic Remarketing, Google Shopping automation
6
  Author URI: https://conversios.io/
7
  Author: Tatvic
8
  Requires at least: 1.4.1
9
- Tested up to: 5.8
10
  Requires PHP: 5.6 or Higher
11
- Stable tag: 4.1.3
12
- Version: 4.1.3
13
  License: GPLv3
14
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
15
 
@@ -380,6 +380,14 @@ You can resolve the duplication of data by removing the manually implemented GA
380
 
381
  == Changelog ==
382
 
 
 
 
 
 
 
 
 
383
  = 4.1.3 - 06/09/2021 =
384
 
385
  * Domain claim notice issue is resolved
6
  Author URI: https://conversios.io/
7
  Author: Tatvic
8
  Requires at least: 1.4.1
9
+ Tested up to: 5.8.1
10
  Requires PHP: 5.6 or Higher
11
+ Stable tag: 4.2.0
12
+ Version: 4.2.0
13
  License: GPLv3
14
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
15
 
380
 
381
  == Changelog ==
382
 
383
+ = 4.2.0 - 20/09/2021 =
384
+
385
+ * In this release, we have rolled out the most demanded feature by you all - Reporting dashboard. Reporting dashboard will showcase all the important e-commerce KPI reports in the plugin's UI.
386
+
387
+ * Plugin UI enhancements
388
+
389
+ * Performance enhancements and bug fixes
390
+
391
  = 4.1.3 - 06/09/2021 =
392
 
393
  * Domain claim notice issue is resolved