Enhanced Ecommerce Google Analytics Plugin for WooCommerce - Version 4.5.2

Version Description

  • 23/12/2021 =

  • In this release, we have focused on code optimization and performance improvement.

Download this release

Release Info

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

Code changes from version 4.5.1 to 4.5.2

Files changed (39) hide show
  1. admin/class-conversios-admin.php +21 -22
  2. admin/class-conversios-onboarding.php +171 -182
  3. admin/class-enhanced-ecommerce-google-analytics-admin.php +34 -45
  4. admin/class-enhanced-ecommerce-google-analytics-settings.php +5 -5
  5. admin/class-survey.php +22 -29
  6. admin/class-tvc-admin-auto-product-sync-helper.php +33 -77
  7. admin/class-tvc-admin-helper.php +63 -72
  8. admin/css/bootstrap.rtl.min.css +0 -7
  9. admin/css/custom-style.css +2 -22
  10. admin/css/dataTables.bootstrap4.min.css +0 -1
  11. admin/css/dataTables.bootstrap5.min.css +5 -0
  12. admin/css/enhanced-ecommerce-google-analytics-admin.css +8 -4
  13. admin/css/style.css +24 -0
  14. admin/helper/class-dashboard-helper.php +16 -24
  15. admin/helper/class-onboarding-helper.php +303 -262
  16. admin/js/bootstrap.min.js +0 -7
  17. admin/js/chart.js +1495 -1530
  18. admin/js/dataTables.bootstrap5.min.js +14 -0
  19. admin/js/popper.min.js +0 -5
  20. admin/partials/class-conversios-header.php +22 -59
  21. admin/partials/general-fields.php +72 -102
  22. admin/partials/pricings.php +222 -226
  23. enhanced-ecommerce-google-analytics.php +2 -2
  24. includes/class-enhanced-ecommerce-google-analytics-activator.php +1 -1
  25. includes/class-enhanced-ecommerce-google-analytics.php +8 -10
  26. includes/class-tvc-register-scripts.php +2 -2
  27. includes/data/class-tvc-ajax-file.php +37 -207
  28. includes/setup/CustomApi.php +55 -249
  29. includes/setup/ShoppingApi.php +36 -36
  30. includes/setup/account.php +18 -25
  31. includes/setup/add-campaign.php +88 -118
  32. includes/setup/class-conversios-dashboard.php +161 -161
  33. includes/setup/class-tatvic-category-selector-element.php +9 -9
  34. includes/setup/class-tvc-product-sync-helper.php +55 -49
  35. includes/setup/google-ads.php +24 -26
  36. includes/setup/google-shopping-feed-gaa-config.php +23 -31
  37. includes/setup/google-shopping-feed-shopping-campaigns.php +284 -167
  38. includes/setup/google-shopping-feed-sync-product.php +42 -57
  39. includes/setup/google-shopping-feed.php +66 -69
admin/class-conversios-admin.php CHANGED
@@ -55,19 +55,19 @@ if ( ! class_exists( 'Conversios_Admin' ) ) {
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', esc_url(ENHANCAD_PLUGIN_URL.'/admin/css/slick.css') );
64
  wp_enqueue_style('conversios-slick-css');
65
- wp_register_style('conversios-daterangepicker-css', esc_url(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', esc_url(ENHANCAD_PLUGIN_URL . '/admin/css/style.css'), array(), $this->version, 'all' );
70
- wp_enqueue_style('conversios-responsive-css', esc_url(ENHANCAD_PLUGIN_URL . '/admin/css/responsive.css'), array(), $this->version, 'all');
71
  }
72
  }
73
 
@@ -78,19 +78,18 @@ if ( ! class_exists( 'Conversios_Admin' ) ) {
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', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js') );
84
 
85
- wp_enqueue_script( 'conversios-chart-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/chart.js') );
86
- wp_enqueue_script( 'conversios-chart-datalabels-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/chartjs-plugin-datalabels.js') );
87
- wp_enqueue_script( 'conversios-basictable-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/jquery.basictable.min.js') );
88
- wp_enqueue_script( 'conversios-moment-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/moment.min.js') );
89
- wp_enqueue_script( 'conversios-daterangepicker-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/daterangepicker.js') );
90
 
91
- wp_enqueue_script( 'tvc-ee-custom-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/tvc-ee-custom.js'), array( 'jquery' ), $this->version, false );
92
  }
93
- do_action('add_conversios_js_'.esc_attr($_GET['page']) );
94
  }
95
  }
96
 
@@ -110,7 +109,7 @@ if ( ! class_exists( 'Conversios_Admin' ) ) {
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',
@@ -170,10 +169,10 @@ if ( ! class_exists( 'Conversios_Admin' ) ) {
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();
@@ -207,7 +206,7 @@ if ( ! class_exists( 'Conversios_Admin' ) ) {
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{
55
  */
56
  public function enqueue_styles() {
57
  $screen = get_current_screen();
58
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos(sanitize_text_field($_GET['page']), 'conversios') !== false) ) {
59
  //developres hook to custom css
60
+ do_action('add_conversios_css_'.sanitize_text_field($_GET['page']));
61
  //conversios page css
62
+ if(sanitize_text_field($_GET['page']) == "conversios"){
63
+ wp_register_style('conversios-slick-css', esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/css/slick.css') );
64
  wp_enqueue_style('conversios-slick-css');
65
+ wp_register_style('conversios-daterangepicker-css', esc_url_raw(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', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/style.css'), array(), $this->version, 'all' );
70
+ wp_enqueue_style('conversios-responsive-css', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/responsive.css'), array(), $this->version, 'all');
71
  }
72
  }
73
 
78
  */
79
  public function enqueue_scripts() {
80
  $screen = get_current_screen();
81
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos(sanitize_text_field($_GET['page']), 'conversios') !== false) ) {
82
+ if(sanitize_text_field($_GET['page']) == "conversios"){
83
+ wp_enqueue_script( 'conversios-jquery-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js') );
84
 
85
+ wp_enqueue_script( 'conversios-chart-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/chart.js') );
86
+ wp_enqueue_script( 'conversios-chart-datalabels-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/chartjs-plugin-datalabels.js') );
87
+ wp_enqueue_script( 'conversios-basictable-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/jquery.basictable.min.js') );
88
+ wp_enqueue_script( 'conversios-moment-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/moment.min.js') );
89
+ wp_enqueue_script( 'conversios-daterangepicker-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/daterangepicker.js') );
90
 
91
+ wp_enqueue_script( 'tvc-ee-custom-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/tvc-ee-custom.js'), array( 'jquery' ), $this->version, false );
92
  }
 
93
  }
94
  }
95
 
109
  }
110
 
111
  add_menu_page(
112
+ esc_html__('Tatvic EE Plugin','conversios'), esc_html__('Tatvic EE Plugin','conversios'), 'manage_options', "conversios", array($this, 'showPage'), esc_url_raw(plugin_dir_url(__FILE__) . 'images/tatvic_logo.png'), 26
113
  );
114
  add_submenu_page(
115
  'conversios',
169
  */
170
  public function showPage() {
171
  do_action('add_conversios_header');
172
+ if (!empty(sanitize_text_field($_GET['page']))) {
173
+ $get_action = str_replace("-", "_", sanitize_text_field($_GET['page']));
174
  } else {
175
+ $get_action = esc_attr("conversios");
176
  }
177
  if (method_exists($this, $get_action)) {
178
  $this->$get_action();
206
  public function conversios_google_shopping_feed() {
207
  include(ENHANCAD_PLUGIN_DIR . 'includes/setup/help-html.php');
208
  include(ENHANCAD_PLUGIN_DIR . 'includes/setup/google-shopping-feed.php');
209
+ $action_tab = (isset($_GET['tab']))?sanitize_text_field($_GET['tab']):"";
210
  if($action_tab!=""){
211
  $this->$action_tab();
212
  }else{
admin/class-conversios-onboarding.php CHANGED
@@ -45,7 +45,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
45
  $g_mail = get_option('ee_customer_gmail');
46
  $this->tvc_data['g_mail']="";
47
  if($g_mail){
48
- $this->tvc_data['g_mail']=$g_mail;
49
  }
50
  }
51
  }
@@ -72,7 +72,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
72
  $contData = json_decode($getCountris);
73
  if (!empty($user_country)) {
74
  $data = "<select id='selectCountry' name='country' class='form-control slect2bx' readonly='true'>";
75
- $data .= "<option value=''>".__("Please select country","conversios")."</option>";
76
  foreach ($contData as $key => $value) {
77
  $selected = ($value->code == $user_country) ? "selected='selected'" : "";
78
  $data .= "<option value=" . esc_attr($value->code) . " " . $selected . " >" . esc_attr($value->name) . "</option>";
@@ -80,7 +80,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
80
  $data .= "</select>";
81
  } else {
82
  $data = "<select id='selectCountry' name='country' class='form-control slect2bx'>";
83
- $data .= "<option value=''>".__("Please select country","conversios")."</option>";
84
  foreach ($contData as $key => $value) {
85
  $data .= "<option value=" . esc_attr($value->code) . ">" . esc_attr($value->name) . "</option>";
86
  }
@@ -98,21 +98,21 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
98
  */
99
  public function welcome_screen() {
100
  $googleDetail = "";
101
- $defaulSelection = 1;
102
- $tracking_option = "UA";
103
  $login_customer_id ="";
104
- $completed_last_step ="step-0";
105
  $complete_step = array("step-0"=>1,"step-1"=>0,"step-2"=>0,"step-3"=>0);
106
 
107
- if ( isset($_GET['subscription_id']) && $_GET['subscription_id']){
108
- $this->subscriptionId = $_GET['subscription_id'];
109
- if ( isset($_GET['g_mail']) && $_GET['g_mail']){
110
- $this->tvc_data['g_mail'] = $_GET['g_mail'];
111
- $completed_last_step ="step-1";
112
- $complete_step["step-0"] = 1;
113
 
114
  $ee_additional_data = $this->TVC_Admin_Helper->get_ee_additional_data();
115
- $ee_additional_data['ee_last_login'] = current_time( 'timestamp' );
116
  $this->TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
117
 
118
  $this->is_refresh_token_expire = false;
@@ -126,8 +126,8 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
126
  if( property_exists($google_detail, "data") && $google_detail->data != "" ){
127
  $googleDetail = $google_detail->data;
128
  $this->tvc_data['subscription_id'] = $googleDetail->id;
129
- $this->tvc_data['access_token'] = $googleDetail->access_token;
130
- $this->tvc_data['refresh_token'] = $googleDetail->refresh_token;
131
  $this->plan_id = $googleDetail->plan_id;
132
  $login_customer_id = $googleDetail->customer_id;
133
  $tracking_option = $googleDetail->tracking_option;
@@ -185,33 +185,33 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
185
  #wpadminbar{display: none;}
186
  </style>
187
  <div class="bodyrightpart onbordingbody-wapper">
188
- <div class="loader-section" id="loader-section"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/ajax-loader.gif');?>" alt="loader"></div>
189
  <div class="alert-message" id="tvc_onboarding_popup_box"></div>
190
  <div class="onbordingbody">
191
  <div class="site-header">
192
  <div class="container">
193
- <div class="brand"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/logo.png');?>" alt="Conversios" /></div>
194
  </div>
195
  </div>
196
  <div class="onbording-wrapper">
197
  <div class="container">
198
  <div class="smallcontainer">
199
  <div class="onbordingtop">
200
- <h2><?php _e("Let’s get you started.","conversios"); ?></h2>
201
- <p><?php _e("Automate Google Analytics, Dynamic Remarketing & Google Shopping in just 5 minutes.","conversios"); ?></p>
202
  </div>
203
  <div class="row">
204
  <!-- onborading left start -->
205
  <div class="onboardingstepwrap">
206
  <!-- step-0 start -->
207
- <div class="onbordording-step onbrdstep-0 gglanystep <?php if($this->subscriptionId == "" || $this->tvc_data['g_mail']=="" || $this->is_refresh_token_expire == true ){ echo "activestep"; }else{echo "selectedactivestep";} ?>">
208
  <div class="stepdtltop" data-is-done="<?php echo esc_attr($complete_step['step-0']); ?>" id="google-signing" data-id="step_0">
209
  <div class="stepleftround">
210
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
211
  </div>
212
  <div class="stepdetwrap">
213
- <h4><?php _e("Connect Conversios with your website","conversios"); ?></h4>
214
- <p><?php echo esc_attr((isset($this->tvc_data['g_mail']) && $this->subscriptionId)?$this->tvc_data['g_mail']:""); ?></p>
215
  </div>
216
  </div>
217
  <div class="stepmoredtlwrp">
@@ -219,64 +219,64 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
219
  <?php if(!isset($this->tvc_data['g_mail']) || $this->tvc_data['g_mail'] == "" || $this->subscriptionId == ""){?>
220
  <div class="google_connect_url google-btn">
221
  <div class="google-icon-wrapper">
222
- <img class="google-icon" src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/g-logo.png'); ?>"/>
223
  </div>
224
- <p class="btn-text"><b><?php _e("Sign in with google","conversios"); ?></b></p>
225
  </div>
226
  <?php } else{?>
227
 
228
  <?php if($this->is_refresh_token_expire == true){?>
229
- <p class="alert alert-primary"><?php _e("It seems the token to access your Google accounts is expired. Sign in again to continue.","conversios"); ?></p>
230
  <div class="google_connect_url google-btn">
231
  <div class="google-icon-wrapper">
232
- <img class="google-icon" src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/g-logo.png'); ?>"/>
233
  </div>
234
- <p class="btn-text"><b><?php _e("Sign in with google","conversios"); ?></b></p>
235
  </div>
236
  <?php } else{ ?>
237
  <div class="google_connect_url google-btn">
238
  <div class="google-icon-wrapper">
239
- <img class="google-icon" src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/g-logo.png'); ?>"/>
240
  </div>
241
- <p class="btn-text mr-35"><b><?php _e("Reauthorize","conversios"); ?></b></p>
242
  </div>
243
  <?php } ?>
244
  <?php } ?>
245
- <p><?php _e("Make sure you sign in with the google account that has all privileges to access google analytics, google ads and google merchant center account.","conversios"); ?></p>
246
  </div>
247
  </div>
248
  </div>
249
  <!-- step-0 over -->
250
  <!-- step-1 start -->
251
- <div class="onbordording-step onbrdstep-1 gglanystep <?php echo ($complete_step['step-1']==1 && $this->tvc_data['g_mail'] && $this->is_refresh_token_expire == false )?'selectedactivestep':''; ?> <?php if($this->subscriptionId != "" && $this->tvc_data['g_mail'] && $this->is_refresh_token_expire == false){ echo "activestep"; } ?>">
252
- <div class="stepdtltop" data-is-done="<?php echo $complete_step['step-1']; ?>" id="google-analytics" data-id="step_1">
253
  <div class="stepleftround">
254
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
255
  </div>
256
  <div class="stepdetwrap">
257
- <h4><?php _e("Connect Google Analytics Account","conversios"); ?></h4>
258
- <p><?php _e("Tag your website with all important e-commerce events in Google Analytics.","conversios"); ?></p>
259
  </div>
260
  </div>
261
  <div class="stepmoredtlwrp">
262
  <div class="stepmoredtl">
263
  <form action="#">
264
  <div class="form-row">
265
- <h5><?php _e("How do you plan to tag your website?","conversios"); ?></h5>
266
  <div class="cstmrdobtn-item">
267
  <label for="univeral">
268
- <input type="radio" <?php echo $this->is_checked($tracking_option, "UA"); ?> name="analytic_tag_type" id="univeral" value="UA">
269
  <span class="checkmark"></span>
270
- <?php _e("Universal Analytics (Google Analytics 3)","conversios"); ?>
271
  </label>
272
  <div id="UA" class="slctunivr-filed">
273
  <div class="tvc-dropdown">
274
- <div class="tvc-dropdown-header" id="ua_web_property_id_option_val" data-accountid="<?php if($googleDetail->ua_analytic_account_id){ echo $googleDetail->ua_analytic_account_id; } ?>" data-val="<?php if($googleDetail->property_id){ echo esc_attr($googleDetail->property_id); } ?>"><?php if($googleDetail->property_id){
275
  echo esc_attr($googleDetail->property_id);
276
- }else{?><?php _e("Select Property Id","conversios"); ?><?php } ?></div>
277
  <div class="tvc-dropdown-content" id="ua_web_property_id_option">
278
- <div class="tvc-select-items"><option value=""><?php _e("Select Property Id","conversios"); ?></option></div>
279
- <div class="tvc-ua-option-more option"><?php _e("Load More","conversios"); ?></div>
280
  </div>
281
  </div>
282
 
@@ -286,19 +286,19 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
286
  <label for="gglanytc">
287
  <input type="radio" <?php echo $this->is_checked($tracking_option, "GA4"); ?> name="analytic_tag_type" id="gglanytc" value="GA4">
288
  <span class="checkmark"></span>
289
- <?php _e("Google Analytics 4","conversios"); ?>
290
  </label>
291
  <div id="GA4" class="slctunivr-filed">
292
 
293
  <div class="tvc-dropdown">
294
- <div class="tvc-dropdown-header" id="ga4_web_measurement_id_option_val" data-accountid="<?php if($googleDetail->ga4_analytic_account_id){ echo $googleDetail->ga4_analytic_account_id; } ?>" data-val="<?php if($googleDetail->measurement_id){ echo $googleDetail->measurement_id; } ?>">
295
  <?php if($googleDetail->measurement_id){
296
  echo esc_attr($googleDetail->measurement_id);
297
- }else{?><?php _e("Select Measurement Id","conversios"); ?>
298
  <?php } ?></div>
299
  <div class="tvc-dropdown-content" id="ga4_web_measurement_id_option">
300
- <div class="tvc-select-items"><option value=""><?php _e("Select Measurement Id","conversios"); ?></option></div>
301
- <div class="tvc-ga4-option-more option"><?php _e("Load More","conversios"); ?></div>
302
  </div>
303
  </div>
304
 
@@ -308,18 +308,18 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
308
  <label for="both">
309
  <input type="radio" <?php echo $this->is_checked($tracking_option, "BOTH"); ?> name="analytic_tag_type" id="both" value="BOTH">
310
  <span class="checkmark"></span>
311
- Both
312
  </label>
313
  <div id="BOTH" class="slctunivr-filed">
314
  <div class="botslectbxitem">
315
 
316
  <div class="tvc-dropdown">
317
- <div class="tvc-dropdown-header" id="both_ua_web_property_id_option_val" data-accountid="<?php if($googleDetail->ua_analytic_account_id){ echo $googleDetail->ua_analytic_account_id; } ?>" data-val="<?php if($googleDetail->property_id){ echo $googleDetail->property_id; } ?>"><?php if($googleDetail->property_id){
318
  echo esc_attr($googleDetail->property_id);
319
- }else{?><?php _e("Select Property Id","conversios"); ?><?php } ?></div>
320
  <div class="tvc-dropdown-content" id="both_ua_web_property_id_option">
321
- <div class="tvc-select-items"><option value=""><?php _e("Select Property Id","conversios"); ?></option></div>
322
- <div class="tvc-ua-option-more option"><?php _e("Load More","conversios"); ?></div>
323
  </div>
324
  </div>
325
 
@@ -327,44 +327,44 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
327
  <div class="botslectbxitem">
328
 
329
  <div class="tvc-dropdown">
330
- <div class="tvc-dropdown-header" id="both_ga4_web_measurement_id_option_val" data-accountid="<?php if($googleDetail->ga4_analytic_account_id){ echo $googleDetail->ga4_analytic_account_id; } ?>" data-val="<?php if($googleDetail->measurement_id){ echo $googleDetail->measurement_id; } ?>">
331
  <?php if($googleDetail->measurement_id){
332
  echo esc_attr($googleDetail->measurement_id);
333
- }else{?><?php _e("Select Measurement Id","conversios"); ?>
334
  <?php } ?></div>
335
  <div class="tvc-dropdown-content" id="both_ga4_web_measurement_id_option">
336
- <div class="tvc-select-items"><option value=""><?php _e("Select Measurement Id","conversios"); ?></option></div>
337
- <div class="tvc-ga4-option-more option"><?php _e("Load More","conversios"); ?></div>
338
  </div>
339
  </div>
340
  </div>
341
- <div id="old_tracking" data-tracking_option="<?php echo $tracking_option; ?>" data-measurement_id="<?php echo $googleDetail->measurement_id; ?>" data-property_id="<?php echo $googleDetail->property_id; ?>"></div>
342
  </div>
343
  </div>
344
  </div>
345
  <div class="form-row">
346
- <h5>Advance Settings (Optional)</h5>
347
  <div class="chckbxbgbx">
348
  <div class="cstmcheck-item">
349
  <label for="enhanced_e_commerce_tracking">
350
- <input type="checkbox" class="custom-control-input" name="enhanced_e_commerce_tracking" id="enhanced_e_commerce_tracking" <?php echo $is_e_e_tracking; ?>>
351
  <span class="checkmark"></span>
352
- <?php _e("Enhaced e-commerce tracking","conversios"); ?>
353
  </label>
354
  </div>
355
  <div class="cstmcheck-item">
356
  <label for="add_gtag_snippet">
357
- <input type="checkbox" class="custom-control-input" name="add_gtag_snippe" id="add_gtag_snippet" <?php echo $is_a_g_snippet; ?>>
358
  <span class="checkmark"></span>
359
- <?php _e("Add gtag.js snippet","conversios"); ?>
360
  </label>
361
  </div>
362
  </div>
363
  </div>
364
  <div class="stepsbmtbtn">
365
- <input type="hidden" id="subscriptionPropertyId" name="subscriptionPropertyId" value="<?php echo (property_exists($googleDetail,"property_id"))?$googleDetail->property_id:""; ?>">
366
- <input type="hidden" id="subscriptionMeasurementId" name="subscriptionMeasurementId" value="<?php echo (property_exists($googleDetail,"measurement_id"))?$googleDetail->measurement_id:""; ?>">
367
- <button type="button" disabled id="step_1" class="stepnextbtn stpnxttrgr"><?php _e("Next","conversios"); ?></button>
368
  </div>
369
  </form>
370
  </div>
@@ -373,13 +373,13 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
373
  <!-- step-1 over -->
374
  <!-- step-2 start -->
375
  <div class="onbordording-step onbrdstep-2 ggladsstep <?php echo ($complete_step['step-2']==1 && $this->is_refresh_token_expire == false)?'selectedactivestep':''; ?>">
376
- <div class="stepdtltop" data-is-done="<?php echo $complete_step['step-2']; ?>" id="google-ads" data-id="step_2">
377
  <div class="stepleftround">
378
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
379
  </div>
380
  <div class="stepdetwrap">
381
- <h4><?php _e("Select Google Ads account","conversios"); ?></h4>
382
- <p><?php _e("With dynamic reamarketing tags, you will be able to show ads to your past visitors with specific product information tailored to your customer’s previous site visit.","conversios"); ?></p>
383
  </div>
384
  </div>
385
  <div class="stepmoredtlwrp">
@@ -387,25 +387,25 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
387
  <form action="#">
388
  <div class="selcttopwrap" id="tvc_ads_section">
389
  <div class="ggladsselectbx">
390
- <input type="hidden" id="subscriptionGoogleAdsId" name="subscriptionGoogleAdsId" value="<?php echo property_exists($googleDetail,"google_ads_id")?$googleDetail->google_ads_id:""; ?>">
391
  <select class="slect2bx google_ads_sel" id="ads-account" name="customer_id">
392
- <option value=''><?php _e("Select Google Ads Account","conversios"); ?></option>
393
  </select>
394
  </div>
395
  <div class="orwrp">or</div>
396
  <div class="creatnewwrp">
397
- <button type="button" class="cretnewbtn newggladsbtn"><span class="plusicon"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/blue-plus.png'); ?>" alt="" /></span> <?php _e("Create New","conversios"); ?></button>
398
  </div>
399
  </div>
400
 
401
  <div class="selcttopwrap">
402
  <div class="onbrdpp-body alert alert-primary" style="display:none;" id="new_google_ads_section">
403
- <h4><?php _e("Account Created","conversios"); ?></h4>
404
- <p><?php _e("Your Google Ads Account has been created","conversios"); ?> <strong>(<b><span id="new_google_ads_id"></span></b>).</strong></p>
405
- <h5><?php _e("Steps to claim your Google Ads Account:","conversios"); ?></h5>
406
  <ol>
407
- <li><?php _e("Accept invitation mail from Google Ads sent to your email address","conversios"); ?> <em><?php echo (isset($this->tvc_data['g_mail']))?$this->tvc_data['g_mail']:""; ?></em></li>
408
- <li><?php _e("Log into your Google Ads account and set up your <em>billing preferences</em>","conversios"); ?></li>
409
  </ol>
410
  </div>
411
  </div>
@@ -417,44 +417,44 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
417
  $is_d_r_tags = (property_exists($googleDetail,"dynamic_remarketing_tags") && $googleDetail->dynamic_remarketing_tags == 1)?"checked":(($defaulSelection == 1)?"checked":"");
418
  $is_g_ad_c_tracking = (property_exists($googleDetail,"google_ads_conversion_tracking") && $googleDetail->google_ads_conversion_tracking == 1)?"checked":(($defaulSelection == 1)?"checked":"");
419
  ?>
420
- <h5><?php _e("Advance Settings (Optional)","conversios"); ?></h5>
421
  <div class="chckbxbgbx dsplcolmview">
422
  <div class="cstmcheck-item">
423
  <label for="remarketing_tag">
424
- <input type="checkbox" class="custom-control-input" name="remarketing_tag" id="remarketing_tag" value="1" <?php echo $is_r_tags; ?>>
425
  <span class="checkmark"></span>
426
- <?php _e("Enable Google Remarketing Tag","conversios"); ?>
427
  </label>
428
  </div>
429
  <div class="cstmcheck-item">
430
  <label for="dynamic_remarketing_tags">
431
- <input type="checkbox" class="custom-control-input" name="dynamic_remarketing_tags" id="dynamic_remarketing_tags" value="1" <?php echo $is_d_r_tags; ?>>
432
  <span class="checkmark"></span>
433
- <?php _e("Enable Dynamic Remarketing Tag","conversios"); ?>
434
  </label>
435
  </div>
436
  <div class="cstmcheck-item <?php if($this->plan_id == 1){?>cstmcheck-item-pro <?php } ?>">
437
  <label for="google_ads_conversion_tracking">
438
  <?php if($this->plan_id != 1){?>
439
- <input type="checkbox" class="custom-control-input" name="google_ads_conversion_tracking" id="google_ads_conversion_tracking" value="1" <?php echo $is_g_ad_c_tracking; ?>>
440
  <span class="checkmark"></span>
441
- <?php _e("Google Ads conversion tracking","conversios"); ?>
442
  <?php }else{?>
443
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/lock.svg'); ?>"><label><?php _e("Google Ads conversion tracking (Pro Plan)","conversios"); ?></label>
444
  <?php } ?>
445
  </label>
446
  </div>
447
  <div class="cstmcheck-item">
448
  <label for="link_google_analytics_with_google_ads">
449
- <input type="checkbox" class="custom-control-input" name="link_google_analytics_with_google_ads" id="link_google_analytics_with_google_ads" value="1" <?php echo $is_l_g_an_w_g_ad; ?>>
450
  <span class="checkmark"></span>
451
- <?php _e("Link Google Analytics with Google Ads","conversios"); ?>
452
  </label>
453
  </div>
454
  </div>
455
  </div>
456
  <div class="stepsbmtbtn">
457
- <button type="button" id="step_2" class="stepnextbtn stpnxttrgr"><?php _e("Next","conversios"); ?></button>
458
  <!-- add dslbbtn class for disable button -->
459
  </div>
460
  </form>
@@ -463,14 +463,14 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
463
  </div>
464
  <!-- step-2 over -->
465
  <!-- step-3 start -->
466
- <div class="onbordording-step onbrdstep-3 gglmrchntstep <?php echo ($complete_step['step-3']==1 && $this->is_refresh_token_expire == false )?'selectedactivestep':''; ?>">
467
- <div class="stepdtltop" data-is-done="<?php echo $complete_step['step-3']; ?>" id="gmc-account" data-id="step_3">
468
  <div class="stepleftround">
469
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
470
  </div>
471
  <div class="stepdetwrap">
472
- <h4><?php _e("Select Google Merchant Center Account","conversios"); ?></h4>
473
- <p><?php _e("Make your WooCommerce shop and products available to millions of shoppers across google.","conversios"); ?></p>
474
  </div>
475
  </div>
476
  <div class="stepmoredtlwrp">
@@ -480,37 +480,37 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
480
  <div class="form-group" style="display:none;" id="new_merchant_section">
481
  <div class="text-center">
482
  <div class="alert alert-primary" style="padding: 10px;" role="alert">
483
- <label class="form-label-control font-weight-bold"><?php _e("We have created new merchant center account with ID: ","conversios"); ?><span id="new_merchant_id"></span>. <?php _e("Click on finish button to save new account.","conversios"); ?></label>
484
  </div>
485
  </div>
486
  </div>
487
  <div id="tvc_merchant_section">
488
  <div class="ggladsselectbx">
489
  <select class="slect2bx" id="google_merchant_center_id" name="google_merchant_center_id">
490
- <option value=''><?php _e("Select Measurement Id","conversios"); ?></option>
491
  </select>
492
  </div>
493
  <div class="orwrp">or</div>
494
  <div class="creatnewwrp">
495
- <button type="button" class="cretnewbtn newmrchntbtn"><span class="plusicon"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/blue-plus.png'); ?>" alt="" /></span> <?php _e("Create New","conversios"); ?></button>
496
  </div>
497
  </div>
498
  </div>
499
  <div class="stepsbmtbtn">
500
- <button type="button" id="step_3" data-enchanter="finish" class="stepnextbtn finishbtn"><?php _e("Finish","conversios"); ?></button>
501
  <!-- add dslbbtn class for disable button -->
502
  </div>
503
- <input type="hidden" id="subscriptionMerchantCenId" name="subscriptionMerchantCenId" value="<?php echo property_exists($googleDetail,"google_merchant_center_id")?$googleDetail->google_merchant_center_id:""; ?>">
504
  <input type="hidden" id="loginCustomerId" name="loginCustomerId" value="<?php echo esc_attr($login_customer_id); ?>">
505
  <input type="hidden" id="subscriptionId" name="subscriptionId" value="<?php echo esc_attr($this->subscriptionId); ?>">
506
- <input type="hidden" id="plan_id" name="plan_id" value="<?php echo $this->plan_id; ?>">
507
  <input type="hidden" id="conversios_onboarding_nonce" name="conversios_onboarding_nonce" value="<?php echo wp_create_nonce( 'conversios_onboarding_nonce' ); ?>">
508
 
509
  <input type="hidden" id="ga_view_id" name="ga_view_id" value="<?php echo esc_attr(get_option('ee_ga_view_id')); ?>">
510
  </form>
511
  </div>
512
  <div class="stepnotewrp">
513
- <?php _e('If you are in the European Economic Area or Switzerland your Merchant Center account must be associated with a Comparison Shopping Service (CSS). Please find more information at <a href="">Google Merchant Center Help</a> website. If you create a new Merchant Center account through this application, it will be associated with Google Shopping, Google’s CSS, by default. You can change the CSS associated with your account at any time. Please find more information about our CSS Partners <a href="">here</a>. Once you have set up your Merchant Center account you can use our onboarding tool regardless of which CSS you use.','conversios'); ?>
514
  </div>
515
  </div>
516
  </div>
@@ -522,17 +522,17 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
522
  <div class="sidebrcontainer">
523
  <div class="onbrd-rdmbx">
524
  <div class="rdm-amnt">
525
- <small><?php _e("Redeem upto","conversios"); ?></small>
526
- <?php echo $off_credit_amt; ?>
527
  </div>
528
- <p><?php _e("Create your first Google Ads account with us and redeem upto","conversios"); ?> <?php echo $off_credit_amt; ?> <?php _e("on the spend you make in the next 31 days.","conversios"); ?></p>
529
- <a target="_blank" href="https://conversios.io/help-center/Google-Spend-Match.pdf" class="lrnmorbtn"><?php _e("Learn more","conversios"); ?> <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/arrow_right.png'); ?>" alt="" /></a>
530
  </div>
531
  <div class="onbrdrgt-nav">
532
  <ul>
533
- <li><a target="_blank" href="<?php echo esc_url("https://conversios.io/help-center/Installation-Manual.pdf"); ?>"><?php echo _e("Installation Manual","conversios"); ?></a></li>
534
- <li><a target="_blank" href="<?php echo esc_url("https://conversios.io/help-center/Google-shopping-Guide.pdf"); ?>" href=""><?php _e("Google Shopping Guide","conversios"); ?></a></li>
535
- <li><a target="_blank" href="<?php echo esc_url("https://wordpress.org/plugins/enhanced-e-commerce-for-woocommerce-store/faq/"); ?>" href=""><?php _e("FAQ","conversios"); ?></a></li>
536
  </ul>
537
  </div>
538
  </div>
@@ -549,21 +549,21 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
549
  <div class="onbrdppmain" role="document">
550
  <div class="onbrdnpp-cntner acccretppcntnr">
551
  <div class="onbrdnpp-hdr">
552
- <h4><?php _e("You have not selected Google Ads account.","conversios"); ?></h4>
553
- <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png');?>" alt="" /></div>
554
  </div>
555
  <div class="onbrdpp-body">
556
- <p><?php _e("If you do not select Google Ads account, you will not be able to use some of the major features like:","conversios"); ?></p>
557
  <ul>
558
- <li><?php _e("Dynamic Remarketing Tags","conversios"); ?> </li>
559
- <li><?php _e("Google Smart Shopping Campaigns","conversios"); ?></li>
560
- <li><?php _e("Google Analytics and Google Ads linking","conversios"); ?></li>
561
  </ul>
562
- <p><?php _e("Are you sure you want to continue without selecting Google Ads account?","conversios"); ?></p>
563
  </div>
564
  <div class="ppfooterbtn">
565
- <button type="button" class="ppblubtn btn-secondary" data-dismiss="modal" id="ads-skip-cancel"><?php _e("Cancel","conversios"); ?></button>
566
- <button type="button" class="ppblubtn btn-primary" data-dismiss="modal" id="ads-skip-continue"><?php _e("Continue","conversios"); ?></button>
567
  </div>
568
  </div>
569
  </div>
@@ -573,14 +573,14 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
573
  <div class="onbrdppmain">
574
  <div class="onbrdnpp-cntner ggladsppcntnr">
575
  <div class="onbrdnpp-hdr">
576
- <h4><?php _e("Enable Google Ads Account","conversios"); ?></h4>
577
- <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png');?>" alt="" /></div>
578
  </div>
579
  <div class="onbrdpp-body">
580
- <p><?php _e("You’ll receive an invite from Google on your email. Accept the invitation to enable your Google Ads Account.","conversios"); ?></p>
581
  </div>
582
  <div class="ppfooterbtn">
583
- <button type="button" id="ads-continue" class="ppblubtn sndinvitebtn"><?php _e("Send Invite","conversios"); ?></button>
584
  </div>
585
  </div>
586
  </div>
@@ -590,16 +590,16 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
590
  <div class="onbrdppmain">
591
  <div class="onbrdnpp-cntner acccretppcntnr">
592
  <div class="onbrdnpp-hdr">
593
- <h4><?php _e("You have not selected Google merchant center account.","conversios"); ?></h4>
594
- <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png');?>" alt="" /></div>
595
  </div>
596
  <div class="onbrdpp-body">
597
- <p><?php _e("If you do not select a merchant center account, you will not be able to use complete google shopping features.","conversios"); ?></p>
598
- <p><?php _e("Are you sure you want to continue without selecting a merchant center account?","conversios"); ?></p>
599
  </div>
600
  <div class="ppfooterbtn">
601
- <button type="button" class="ppblubtn btn-secondary" data-dismiss="modal" id="merchant-center-skip-cancel"><?php _e("Cancel","conversios"); ?></button>
602
- <button type="button" class="ppblubtn btn-primary" data-dismiss="modal" id="merchant-center-skip-continue"><?php _e("Continue","conversios"); ?></button>
603
  </div>
604
  </div>
605
  </div>
@@ -608,34 +608,34 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
608
  <div id="createmerchantpopup" class="pp-modal onbrd-popupwrp crtemrchntpp">
609
  <div class="onbrdppmain">
610
  <div class="onbrdnpp-cntner crtemrchntppcntnr">
611
- <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png'); ?>" alt="" /></div>
612
  <div class="onbrdpp-body">
613
  <div class="row">
614
  <div class="crtemrchnpp-lft">
615
  <div class="crtemrchpplft-top">
616
- <h4><?php _e("Create Google Merchant Center Account","conversios"); ?></h4>
617
- <p><?php _e("Before you can upload product data, you’ll need to verify and claim your store’s website URL. Claiming associates your website URL with your Google Merchant Center account.","conversios"); ?></p>
618
  </div>
619
  <div class="claimedbx">
620
- <?php _e("Your site will automatically be claimed and verified.","conversios"); ?>
621
  </div>
622
  <div class="mrchntformwrp">
623
  <form action="#">
624
  <div class="form-row">
625
- <input type="hidden" id="get-mail" name="g_email" value="<?php echo esc_attr(isset($this->tvc_data['g_mail'])?$this->tvc_data['g_mail']:""); ?>">
626
  <input type="text" value="<?php echo esc_attr($this->tvc_data['user_domain']); ?>" class="fromfiled" name="url" id="url" placeholder="Enter Website">
627
  <div class="cstmcheck-item mt15">
628
  <label for="adult_content">
629
  <input class="" type="checkbox" name="adult_content" id="adult_content">
630
  <span class="checkmark"></span>
631
- <?php _e("My site contains","conversios"); ?>
632
  </label>
633
- <strong><?php _e("Adult Content","conversios"); ?></strong>
634
  </div>
635
  </div>
636
  <div class="form-row">
637
- <input type="text" class="fromfiled" name="store_name" id="store_name" placeholder="<?php _e("Enter Store Name","conversios"); ?>" required>
638
- <div class="inputinfotxt"><?php _e("This name will appear in your Shopping Ads.","conversios"); ?></div>
639
  </div>
640
  <div class="form-row">
641
  <?php echo $this->get_countries($this->tvc_data['user_country']); ?>
@@ -645,27 +645,27 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
645
  <label for="terms_conditions">
646
  <input class="" type="checkbox" name="concent" id="terms_conditions">
647
  <span class="checkmark"></span>
648
- <?php _e("I accept the","conversios"); ?>
649
  </label>
650
- <a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/160173?hl=en"); ?>"><?php _e("terms & conditions","conversios"); ?></a>
651
  </div>
652
  </div>
653
  </form>
654
  </div>
655
  <div class="ppfooterbtn">
656
- <button type="button" id="create_merchant_account" class="cretemrchntbtn"><?php _e("Create Account","conversios"); ?>
657
  </button>
658
  </div>
659
  </div>
660
  <div class="crtemrchnpp-right">
661
- <h6><?php _e("To use Google Shopping, your website must meet these requirements:","conversios"); ?></h6>
662
  <ul>
663
- <li><a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/6149970?hl=en"); ?>"><?php _e("Google Shopping ads policies","conversios"); ?></a></li>
664
- <li><a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/6150127"); ?>"><?php _e("Accurate Contact Information","conversios"); ?></a></li>
665
- <li><a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/6150122"); ?>"><?php _e("Secure collection of process and personal data","conversios"); ?></a></li>
666
- <li><a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/6150127"); ?>"><?php _e("Return Policy","conversios"); ?></a></li>
667
- <li><a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/6150127"); ?>"><?php _e("Billing terms & conditions","conversios"); ?></a></li>
668
- <li><a target="_blank" href="<?php echo esc_url("https://support.google.com/merchants/answer/6150118"); ?>"><?php _e("Complete checkout process","conversios"); ?></a></li>
669
  </ul>
670
  </div>
671
  </div>
@@ -680,44 +680,44 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
680
  <div class="onbrdppmain">
681
  <div class="onbrdnpp-cntner congratppcntnr">
682
  <div class="onbrdnpp-hdr txtcnter">
683
- <h2><?php _e("Congratulations!!","conversios"); ?></h2>
684
- <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png'); ?>" alt="" /></div>
685
  </div>
686
  <div class="onbrdpp-body congratppbody">
687
- <p><?php _e("You have been successfully onboarded. Please check the account summary below and confirm.","conversios"); ?></p>
688
  <div class="congratppdtlwrp">
689
  <div class="cngrtppdtl-item" id="google_analytics_property_id_info">
690
  <div class="cngtrpplft">
691
- <span class="cngrtchckicon"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
692
- <?php _e("Google Analytics Property Id:","conversios"); ?>
693
  </div>
694
  <div class="cngtrpprgt" id="selected_google_analytics_property"></div>
695
  </div>
696
  <div class="cngrtppdtl-item" id="google_analytics_measurement_id_info">
697
  <div class="cngtrpplft">
698
- <span class="cngrtchckicon"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
699
- <?php _e("Google Analytics Measurement Id:","conversios"); ?>
700
  </div>
701
  <div class="cngtrpprgt" id="selected_google_analytics_measurement"></div>
702
  </div>
703
  <div class="cngrtppdtl-item" id="google_ads_info">
704
  <div class="cngtrpplft">
705
- <span class="cngrtchckicon"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
706
- <?php _e("Google Ads Account:","conversios"); ?>
707
  </div>
708
  <div class="cngtrpprgt" id="selected_google_ads_account"></div>
709
  </div>
710
  <div class="cngrtppdtl-item" id="google_merchant_center_info">
711
  <div class="cngtrpplft">
712
- <span class="cngrtchckicon"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
713
- <?php _e("Google Merchant Center Account","conversios"); ?>
714
  </div>
715
  <div class="cngtrpprgt" id="selected_google_merchant_center"></div>
716
  </div>
717
  </div>
718
  </div>
719
  <div class="ppfooterbtn">
720
- <button type="button" id="confirm_selection" class="ppblubtn"><?php _e("Confirm","conversios"); ?></button>
721
  </div>
722
  </div>
723
  </div>
@@ -734,9 +734,9 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
734
  <script>
735
  let tvc_data = <?php echo $j_tvc_data; ?>;
736
  var tvc_ajax_url = '<?php echo admin_url( 'admin-ajax.php' ); ?>';
737
- let subscription_id ="<?php echo $this->subscriptionId; ?>";
738
- let plan_id ="<?php echo $this->plan_id; ?>";
739
- let app_id ="<?php echo $this->app_id; ?>";
740
  /**
741
  * Convesios custom script
742
  */
@@ -1000,17 +1000,17 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
1000
  * onboarding page add scripts file
1001
  */
1002
  public function add_scripts(){
1003
- if(isset($_GET['page']) && $_GET['page'] == "conversios_onboarding"){
1004
- wp_register_style('conversios-select2-css',ENHANCAD_PLUGIN_URL . '/admin/css/select2.css');
1005
- wp_enqueue_style('conversios-style-css',ENHANCAD_PLUGIN_URL . '/admin/css/style.css', array(), $this->version, 'all');
1006
- wp_enqueue_style('conversios-responsive-css',ENHANCAD_PLUGIN_URL . '/admin/css/responsive.css', array(), $this->version, 'all');
1007
  wp_enqueue_style('conversios-select2-css');
1008
 
1009
 
1010
- wp_enqueue_script( 'conversios-jquery-js', ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js', array( 'jquery' ), $this->version, false );
1011
- wp_register_script('conversios-select2-js', ENHANCAD_PLUGIN_URL.'/admin/js/select2.min.js');
1012
  wp_enqueue_script('conversios-select2-js');
1013
- wp_enqueue_script( 'conversios-onboarding-js', ENHANCAD_PLUGIN_URL . '/admin/js/onboarding-custom.js', array( 'jquery' ), $this->version, false );
1014
  }
1015
  }
1016
  /**
@@ -1018,7 +1018,7 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
1018
  */
1019
  public function register() {
1020
  // Getting started - shows after installation.
1021
- if(isset($_GET['page']) && $_GET['page'] == "conversios_onboarding"){
1022
  add_dashboard_page(
1023
  esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
1024
  esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
@@ -1026,30 +1026,19 @@ if ( ! class_exists( 'Conversios_Onboarding' ) ) {
1026
  'conversios_onboarding',
1027
  array( $this, 'welcome_screen' )
1028
  );
1029
- /*add_submenu_page(
1030
- '__FILE__',
1031
- esc_html__('Welcome to Conversios Onboarding', 'conversios'),
1032
- esc_html__('Welcome to Conversios Onboarding', 'conversios'),
1033
- 'administrator',
1034
- 'conversios_onboarding',
1035
- array($this, 'welcome_screen'),10
1036
- );*/
1037
  }
1038
  }
1039
  /**
1040
  * Check if we should do any redirect.
1041
  */
1042
  public function maybe_redirect() {
1043
-
1044
- // Bail if no activation redirect.
1045
  if ( ! get_transient( '_conversios_activation_redirect' ) || isset( $_GET['conversios-redirect'] ) ) {
1046
  return;
1047
  }
1048
  // Delete the redirect transient.
1049
  delete_transient( '_conversios_activation_redirect' );
1050
-
1051
- // Bail if activating from network, or bulk.
1052
- if ( isset( $_GET['activate-multi'] ) ) { // WPCS: CSRF ok, input var ok.
1053
  return;
1054
  }
1055
 
45
  $g_mail = get_option('ee_customer_gmail');
46
  $this->tvc_data['g_mail']="";
47
  if($g_mail){
48
+ $this->tvc_data['g_mail']= sanitize_email($g_mail);
49
  }
50
  }
51
  }
72
  $contData = json_decode($getCountris);
73
  if (!empty($user_country)) {
74
  $data = "<select id='selectCountry' name='country' class='form-control slect2bx' readonly='true'>";
75
+ $data .= "<option value=''>".esc_html__("Please select country","conversios")."</option>";
76
  foreach ($contData as $key => $value) {
77
  $selected = ($value->code == $user_country) ? "selected='selected'" : "";
78
  $data .= "<option value=" . esc_attr($value->code) . " " . $selected . " >" . esc_attr($value->name) . "</option>";
80
  $data .= "</select>";
81
  } else {
82
  $data = "<select id='selectCountry' name='country' class='form-control slect2bx'>";
83
+ $data .= "<option value=''>".esc_html__("Please select country","conversios")."</option>";
84
  foreach ($contData as $key => $value) {
85
  $data .= "<option value=" . esc_attr($value->code) . ">" . esc_attr($value->name) . "</option>";
86
  }
98
  */
99
  public function welcome_screen() {
100
  $googleDetail = "";
101
+ $defaulSelection = esc_attr(1);
102
+ $tracking_option = esc_attr("UA");
103
  $login_customer_id ="";
104
+ $completed_last_step =esc_attr("step-0");
105
  $complete_step = array("step-0"=>1,"step-1"=>0,"step-2"=>0,"step-3"=>0);
106
 
107
+ if ( isset($_GET['subscription_id']) && sanitize_text_field($_GET['subscription_id'])){
108
+ $this->subscriptionId = sanitize_text_field($_GET['subscription_id']);
109
+ if ( isset($_GET['g_mail']) && sanitize_email($_GET['g_mail'])){
110
+ $this->tvc_data['g_mail'] = sanitize_email($_GET['g_mail']);
111
+ $completed_last_step =esc_attr("step-1");
112
+ $complete_step["step-0"] = esc_attr(1);
113
 
114
  $ee_additional_data = $this->TVC_Admin_Helper->get_ee_additional_data();
115
+ $ee_additional_data['ee_last_login'] = sanitize_text_field(current_time( 'timestamp' ));
116
  $this->TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
117
 
118
  $this->is_refresh_token_expire = false;
126
  if( property_exists($google_detail, "data") && $google_detail->data != "" ){
127
  $googleDetail = $google_detail->data;
128
  $this->tvc_data['subscription_id'] = $googleDetail->id;
129
+ $this->tvc_data['access_token'] = base64_encode($googleDetail->access_token);
130
+ $this->tvc_data['refresh_token'] = base64_encode($googleDetail->refresh_token);
131
  $this->plan_id = $googleDetail->plan_id;
132
  $login_customer_id = $googleDetail->customer_id;
133
  $tracking_option = $googleDetail->tracking_option;
185
  #wpadminbar{display: none;}
186
  </style>
187
  <div class="bodyrightpart onbordingbody-wapper">
188
+ <div class="loader-section" id="loader-section"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/ajax-loader.gif');?>" alt="loader"></div>
189
  <div class="alert-message" id="tvc_onboarding_popup_box"></div>
190
  <div class="onbordingbody">
191
  <div class="site-header">
192
  <div class="container">
193
+ <div class="brand"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/logo.png');?>" alt="Conversios" /></div>
194
  </div>
195
  </div>
196
  <div class="onbording-wrapper">
197
  <div class="container">
198
  <div class="smallcontainer">
199
  <div class="onbordingtop">
200
+ <h2><?php esc_html_e("Let’s get you started.","conversios"); ?></h2>
201
+ <p><?php esc_html_e("Automate Google Analytics, Dynamic Remarketing & Google Shopping in just 5 minutes.","conversios"); ?></p>
202
  </div>
203
  <div class="row">
204
  <!-- onborading left start -->
205
  <div class="onboardingstepwrap">
206
  <!-- step-0 start -->
207
+ <div class="onbordording-step onbrdstep-0 gglanystep <?php if($this->subscriptionId == "" || $this->tvc_data['g_mail']=="" || $this->is_refresh_token_expire == true ){ echo esc_attr("activestep"); }else{echo esc_attr("selectedactivestep"); } ?>">
208
  <div class="stepdtltop" data-is-done="<?php echo esc_attr($complete_step['step-0']); ?>" id="google-signing" data-id="step_0">
209
  <div class="stepleftround">
210
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
211
  </div>
212
  <div class="stepdetwrap">
213
+ <h4><?php esc_html_e("Connect Conversios with your website","conversios"); ?></h4>
214
+ <p><?php echo (isset($this->tvc_data['g_mail']) && esc_attr($this->subscriptionId) )?esc_attr($this->tvc_data['g_mail']):""; ?></p>
215
  </div>
216
  </div>
217
  <div class="stepmoredtlwrp">
219
  <?php if(!isset($this->tvc_data['g_mail']) || $this->tvc_data['g_mail'] == "" || $this->subscriptionId == ""){?>
220
  <div class="google_connect_url google-btn">
221
  <div class="google-icon-wrapper">
222
+ <img class="google-icon" src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/g-logo.png'); ?>"/>
223
  </div>
224
+ <p class="btn-text"><b><?php esc_html_e("Sign in with google","conversios"); ?></b></p>
225
  </div>
226
  <?php } else{?>
227
 
228
  <?php if($this->is_refresh_token_expire == true){?>
229
+ <p class="alert alert-primary"><?php esc_html_e("It seems the token to access your Google accounts is expired. Sign in again to continue.","conversios"); ?></p>
230
  <div class="google_connect_url google-btn">
231
  <div class="google-icon-wrapper">
232
+ <img class="google-icon" src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/g-logo.png'); ?>"/>
233
  </div>
234
+ <p class="btn-text"><b><?php esc_html_e("Sign in with google","conversios"); ?></b></p>
235
  </div>
236
  <?php } else{ ?>
237
  <div class="google_connect_url google-btn">
238
  <div class="google-icon-wrapper">
239
+ <img class="google-icon" src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/g-logo.png'); ?>"/>
240
  </div>
241
+ <p class="btn-text mr-35"><b><?php esc_html_e("Reauthorize","conversios"); ?></b></p>
242
  </div>
243
  <?php } ?>
244
  <?php } ?>
245
+ <p><?php esc_html_e("Make sure you sign in with the google account that has all privileges to access google analytics, google ads and google merchant center account.","conversios"); ?></p>
246
  </div>
247
  </div>
248
  </div>
249
  <!-- step-0 over -->
250
  <!-- step-1 start -->
251
+ <div class="onbordording-step onbrdstep-1 gglanystep <?php echo ($complete_step['step-1']==1 && $this->tvc_data['g_mail'] && $this->is_refresh_token_expire == false )?esc_attr('selectedactivestep'):''; ?> <?php if($this->subscriptionId != "" && $this->tvc_data['g_mail'] && $this->is_refresh_token_expire == false){ echo esc_attr("activestep"); } ?>">
252
+ <div class="stepdtltop" data-is-done="<?php echo esc_attr($complete_step['step-1']); ?>" id="google-analytics" data-id="step_1">
253
  <div class="stepleftround">
254
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
255
  </div>
256
  <div class="stepdetwrap">
257
+ <h4><?php esc_html_e("Connect Google Analytics Account","conversios"); ?></h4>
258
+ <p><?php esc_html_e("Tag your website with all important e-commerce events in Google Analytics.","conversios"); ?></p>
259
  </div>
260
  </div>
261
  <div class="stepmoredtlwrp">
262
  <div class="stepmoredtl">
263
  <form action="#">
264
  <div class="form-row">
265
+ <h5><?php esc_html_e("How do you plan to tag your website?","conversios"); ?></h5>
266
  <div class="cstmrdobtn-item">
267
  <label for="univeral">
268
+ <input type="radio" <?php echo esc_attr($this->is_checked($tracking_option, "UA")); ?> name="analytic_tag_type" id="univeral" value="UA">
269
  <span class="checkmark"></span>
270
+ <?php esc_html_e("Universal Analytics (Google Analytics 3)","conversios"); ?>
271
  </label>
272
  <div id="UA" class="slctunivr-filed">
273
  <div class="tvc-dropdown">
274
+ <div class="tvc-dropdown-header" id="ua_web_property_id_option_val" data-accountid="<?php if($googleDetail->ua_analytic_account_id){ echo esc_attr($googleDetail->ua_analytic_account_id); } ?>" data-val="<?php if($googleDetail->property_id){ echo esc_attr($googleDetail->property_id); } ?>"><?php if($googleDetail->property_id){
275
  echo esc_attr($googleDetail->property_id);
276
+ }else{?><?php esc_html_e("Select Property Id","conversios"); ?><?php } ?></div>
277
  <div class="tvc-dropdown-content" id="ua_web_property_id_option">
278
+ <div class="tvc-select-items"><option value=""><?php esc_html_e("Select Property Id","conversios"); ?></option></div>
279
+ <div class="tvc-ua-option-more option"><?php esc_html_e("Load More","conversios"); ?></div>
280
  </div>
281
  </div>
282
 
286
  <label for="gglanytc">
287
  <input type="radio" <?php echo $this->is_checked($tracking_option, "GA4"); ?> name="analytic_tag_type" id="gglanytc" value="GA4">
288
  <span class="checkmark"></span>
289
+ <?php esc_html_e("Google Analytics 4","conversios"); ?>
290
  </label>
291
  <div id="GA4" class="slctunivr-filed">
292
 
293
  <div class="tvc-dropdown">
294
+ <div class="tvc-dropdown-header" id="ga4_web_measurement_id_option_val" data-accountid="<?php if($googleDetail->ga4_analytic_account_id){ echo esc_attr($googleDetail->ga4_analytic_account_id); } ?>" data-val="<?php if($googleDetail->measurement_id){ echo esc_attr($googleDetail->measurement_id); } ?>">
295
  <?php if($googleDetail->measurement_id){
296
  echo esc_attr($googleDetail->measurement_id);
297
+ }else{?><?php esc_html_e("Select Measurement Id","conversios"); ?>
298
  <?php } ?></div>
299
  <div class="tvc-dropdown-content" id="ga4_web_measurement_id_option">
300
+ <div class="tvc-select-items"><option value=""><?php esc_html_e("Select Measurement Id","conversios"); ?></option></div>
301
+ <div class="tvc-ga4-option-more option"><?php esc_html_e("Load More","conversios"); ?></div>
302
  </div>
303
  </div>
304
 
308
  <label for="both">
309
  <input type="radio" <?php echo $this->is_checked($tracking_option, "BOTH"); ?> name="analytic_tag_type" id="both" value="BOTH">
310
  <span class="checkmark"></span>
311
+ <?php esc_html_e("Both","conversios"); ?>
312
  </label>
313
  <div id="BOTH" class="slctunivr-filed">
314
  <div class="botslectbxitem">
315
 
316
  <div class="tvc-dropdown">
317
+ <div class="tvc-dropdown-header" id="both_ua_web_property_id_option_val" data-accountid="<?php if($googleDetail->ua_analytic_account_id){ echo esc_attr($googleDetail->ua_analytic_account_id); } ?>" data-val="<?php if($googleDetail->property_id){ echo esc_attr($googleDetail->property_id); } ?>"><?php if($googleDetail->property_id){
318
  echo esc_attr($googleDetail->property_id);
319
+ }else{?><?php esc_html_e("Select Property Id","conversios"); ?><?php } ?></div>
320
  <div class="tvc-dropdown-content" id="both_ua_web_property_id_option">
321
+ <div class="tvc-select-items"><option value=""><?php esc_html_e("Select Property Id","conversios"); ?></option></div>
322
+ <div class="tvc-ua-option-more option"><?php esc_html_e("Load More","conversios"); ?></div>
323
  </div>
324
  </div>
325
 
327
  <div class="botslectbxitem">
328
 
329
  <div class="tvc-dropdown">
330
+ <div class="tvc-dropdown-header" id="both_ga4_web_measurement_id_option_val" data-accountid="<?php if($googleDetail->ga4_analytic_account_id){ echo esc_attr($googleDetail->ga4_analytic_account_id); } ?>" data-val="<?php if($googleDetail->measurement_id){ echo esc_attr($googleDetail->measurement_id); } ?>">
331
  <?php if($googleDetail->measurement_id){
332
  echo esc_attr($googleDetail->measurement_id);
333
+ }else{?><?php esc_html_e("Select Measurement Id","conversios"); ?>
334
  <?php } ?></div>
335
  <div class="tvc-dropdown-content" id="both_ga4_web_measurement_id_option">
336
+ <div class="tvc-select-items"><option value=""><?php esc_html_e("Select Measurement Id","conversios"); ?></option></div>
337
+ <div class="tvc-ga4-option-more option"><?php esc_html_e("Load More","conversios"); ?></div>
338
  </div>
339
  </div>
340
  </div>
341
+ <div id="old_tracking" data-tracking_option="<?php echo esc_attr($tracking_option); ?>" data-measurement_id="<?php echo esc_attr($googleDetail->measurement_id); ?>" data-property_id="<?php echo esc_attr($googleDetail->property_id); ?>"></div>
342
  </div>
343
  </div>
344
  </div>
345
  <div class="form-row">
346
+ <h5><?php esc_html_e("Advance Settings (Optional)","conversios"); ?></h5>
347
  <div class="chckbxbgbx">
348
  <div class="cstmcheck-item">
349
  <label for="enhanced_e_commerce_tracking">
350
+ <input type="checkbox" class="custom-control-input" name="enhanced_e_commerce_tracking" id="enhanced_e_commerce_tracking" <?php echo esc_attr($is_e_e_tracking); ?>>
351
  <span class="checkmark"></span>
352
+ <?php esc_html_e("Enhaced e-commerce tracking","conversios"); ?>
353
  </label>
354
  </div>
355
  <div class="cstmcheck-item">
356
  <label for="add_gtag_snippet">
357
+ <input type="checkbox" class="custom-control-input" name="add_gtag_snippe" id="add_gtag_snippet" <?php echo esc_attr($is_a_g_snippet); ?>>
358
  <span class="checkmark"></span>
359
+ <?php esc_html_e("Add gtag.js snippet","conversios"); ?>
360
  </label>
361
  </div>
362
  </div>
363
  </div>
364
  <div class="stepsbmtbtn">
365
+ <input type="hidden" id="subscriptionPropertyId" name="subscriptionPropertyId" value="<?php echo (property_exists($googleDetail,"property_id"))?esc_attr($googleDetail->property_id):""; ?>">
366
+ <input type="hidden" id="subscriptionMeasurementId" name="subscriptionMeasurementId" value="<?php echo (property_exists($googleDetail,"measurement_id"))?esc_attr($googleDetail->measurement_id):""; ?>">
367
+ <button type="button" disabled id="step_1" class="stepnextbtn stpnxttrgr"><?php esc_html_e("Next","conversios"); ?></button>
368
  </div>
369
  </form>
370
  </div>
373
  <!-- step-1 over -->
374
  <!-- step-2 start -->
375
  <div class="onbordording-step onbrdstep-2 ggladsstep <?php echo ($complete_step['step-2']==1 && $this->is_refresh_token_expire == false)?'selectedactivestep':''; ?>">
376
+ <div class="stepdtltop" data-is-done="<?php echo esc_attr($complete_step['step-2']); ?>" id="google-ads" data-id="step_2">
377
  <div class="stepleftround">
378
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
379
  </div>
380
  <div class="stepdetwrap">
381
+ <h4><?php esc_html_e("Select Google Ads account","conversios"); ?></h4>
382
+ <p><?php esc_html_e("With dynamic reamarketing tags, you will be able to show ads to your past visitors with specific product information tailored to your customer’s previous site visit.","conversios"); ?></p>
383
  </div>
384
  </div>
385
  <div class="stepmoredtlwrp">
387
  <form action="#">
388
  <div class="selcttopwrap" id="tvc_ads_section">
389
  <div class="ggladsselectbx">
390
+ <input type="hidden" id="subscriptionGoogleAdsId" name="subscriptionGoogleAdsId" value="<?php echo property_exists($googleDetail,"google_ads_id")?esc_attr($googleDetail->google_ads_id):""; ?>">
391
  <select class="slect2bx google_ads_sel" id="ads-account" name="customer_id">
392
+ <option value=''><?php esc_html_e("Select Google Ads Account","conversios"); ?></option>
393
  </select>
394
  </div>
395
  <div class="orwrp">or</div>
396
  <div class="creatnewwrp">
397
+ <button type="button" class="cretnewbtn newggladsbtn"><span class="plusicon"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/blue-plus.png'); ?>" alt="" /></span> <?php esc_html_e("Create New","conversios"); ?></button>
398
  </div>
399
  </div>
400
 
401
  <div class="selcttopwrap">
402
  <div class="onbrdpp-body alert alert-primary" style="display:none;" id="new_google_ads_section">
403
+ <h4><?php esc_html_e("Account Created","conversios"); ?></h4>
404
+ <p><?php esc_html_e("Your Google Ads Account has been created","conversios"); ?> <strong>(<b><span id="new_google_ads_id"></span></b>).</strong></p>
405
+ <h5><?php esc_html_e("Steps to claim your Google Ads Account:","conversios"); ?></h5>
406
  <ol>
407
+ <li><?php esc_html_e("Accept invitation mail from Google Ads sent to your email address","conversios"); ?> <em><?php echo (isset($this->tvc_data['g_mail']))?$this->tvc_data['g_mail']:""; ?></em></li>
408
+ <li><?php esc_html_e("Log into your Google Ads account and set up your <em>billing preferences</em>","conversios"); ?></li>
409
  </ol>
410
  </div>
411
  </div>
417
  $is_d_r_tags = (property_exists($googleDetail,"dynamic_remarketing_tags") && $googleDetail->dynamic_remarketing_tags == 1)?"checked":(($defaulSelection == 1)?"checked":"");
418
  $is_g_ad_c_tracking = (property_exists($googleDetail,"google_ads_conversion_tracking") && $googleDetail->google_ads_conversion_tracking == 1)?"checked":(($defaulSelection == 1)?"checked":"");
419
  ?>
420
+ <h5><?php esc_html_e("Advance Settings (Optional)","conversios"); ?></h5>
421
  <div class="chckbxbgbx dsplcolmview">
422
  <div class="cstmcheck-item">
423
  <label for="remarketing_tag">
424
+ <input type="checkbox" class="custom-control-input" name="remarketing_tag" id="remarketing_tag" value="1" <?php echo esc_attr($is_r_tags); ?>>
425
  <span class="checkmark"></span>
426
+ <?php esc_html_e("Enable Google Remarketing Tag","conversios"); ?>
427
  </label>
428
  </div>
429
  <div class="cstmcheck-item">
430
  <label for="dynamic_remarketing_tags">
431
+ <input type="checkbox" class="custom-control-input" name="dynamic_remarketing_tags" id="dynamic_remarketing_tags" value="1" <?php echo esc_attr($is_d_r_tags); ?>>
432
  <span class="checkmark"></span>
433
+ <?php esc_html_e("Enable Dynamic Remarketing Tag","conversios"); ?>
434
  </label>
435
  </div>
436
  <div class="cstmcheck-item <?php if($this->plan_id == 1){?>cstmcheck-item-pro <?php } ?>">
437
  <label for="google_ads_conversion_tracking">
438
  <?php if($this->plan_id != 1){?>
439
+ <input type="checkbox" class="custom-control-input" name="google_ads_conversion_tracking" id="google_ads_conversion_tracking" value="1" <?php echo esc_attr($is_g_ad_c_tracking); ?>>
440
  <span class="checkmark"></span>
441
+ <?php esc_html_e("Google Ads conversion tracking","conversios"); ?>
442
  <?php }else{?>
443
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/lock.svg'); ?>"><label><?php esc_html_e("Google Ads conversion tracking (Pro Plan)","conversios"); ?></label>
444
  <?php } ?>
445
  </label>
446
  </div>
447
  <div class="cstmcheck-item">
448
  <label for="link_google_analytics_with_google_ads">
449
+ <input type="checkbox" class="custom-control-input" name="link_google_analytics_with_google_ads" id="link_google_analytics_with_google_ads" value="1" <?php echo esc_attr($is_l_g_an_w_g_ad); ?>>
450
  <span class="checkmark"></span>
451
+ <?php esc_html_e("Link Google Analytics with Google Ads","conversios"); ?>
452
  </label>
453
  </div>
454
  </div>
455
  </div>
456
  <div class="stepsbmtbtn">
457
+ <button type="button" id="step_2" class="stepnextbtn stpnxttrgr"><?php esc_html_e("Next","conversios"); ?></button>
458
  <!-- add dslbbtn class for disable button -->
459
  </div>
460
  </form>
463
  </div>
464
  <!-- step-2 over -->
465
  <!-- step-3 start -->
466
+ <div class="onbordording-step onbrdstep-3 gglmrchntstep <?php echo ($complete_step['step-3']==1 && $this->is_refresh_token_expire == false )?esc_attr('selectedactivestep'):''; ?>">
467
+ <div class="stepdtltop" data-is-done="<?php echo esc_attr($complete_step['step-3']); ?>" id="gmc-account" data-id="step_3">
468
  <div class="stepleftround">
469
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/check-wbg.png'); ?>" alt="" />
470
  </div>
471
  <div class="stepdetwrap">
472
+ <h4><?php esc_html_e("Select Google Merchant Center Account","conversios"); ?></h4>
473
+ <p><?php esc_html_e("Make your WooCommerce shop and products available to millions of shoppers across google.","conversios"); ?></p>
474
  </div>
475
  </div>
476
  <div class="stepmoredtlwrp">
480
  <div class="form-group" style="display:none;" id="new_merchant_section">
481
  <div class="text-center">
482
  <div class="alert alert-primary" style="padding: 10px;" role="alert">
483
+ <label class="form-label-control font-weight-bold"><?php esc_html_e("We have created new merchant center account with ID: ","conversios"); ?><span id="new_merchant_id"></span>. <?php esc_html_e("Click on finish button to save new account.","conversios"); ?></label>
484
  </div>
485
  </div>
486
  </div>
487
  <div id="tvc_merchant_section">
488
  <div class="ggladsselectbx">
489
  <select class="slect2bx" id="google_merchant_center_id" name="google_merchant_center_id">
490
+ <option value=''><?php esc_html_e("Select Google Merchant Center","conversios"); ?></option>
491
  </select>
492
  </div>
493
  <div class="orwrp">or</div>
494
  <div class="creatnewwrp">
495
+ <button type="button" class="cretnewbtn newmrchntbtn"><span class="plusicon"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/blue-plus.png'); ?>" alt="" /></span> <?php esc_html_e("Create New","conversios"); ?></button>
496
  </div>
497
  </div>
498
  </div>
499
  <div class="stepsbmtbtn">
500
+ <button type="button" id="step_3" data-enchanter="finish" class="stepnextbtn finishbtn"><?php esc_html_e("Finish","conversios"); ?></button>
501
  <!-- add dslbbtn class for disable button -->
502
  </div>
503
+ <input type="hidden" id="subscriptionMerchantCenId" name="subscriptionMerchantCenId" value="<?php echo property_exists($googleDetail,"google_merchant_center_id")?esc_attr($googleDetail->google_merchant_center_id):""; ?>">
504
  <input type="hidden" id="loginCustomerId" name="loginCustomerId" value="<?php echo esc_attr($login_customer_id); ?>">
505
  <input type="hidden" id="subscriptionId" name="subscriptionId" value="<?php echo esc_attr($this->subscriptionId); ?>">
506
+ <input type="hidden" id="plan_id" name="plan_id" value="<?php echo esc_attr($this->plan_id); ?>">
507
  <input type="hidden" id="conversios_onboarding_nonce" name="conversios_onboarding_nonce" value="<?php echo wp_create_nonce( 'conversios_onboarding_nonce' ); ?>">
508
 
509
  <input type="hidden" id="ga_view_id" name="ga_view_id" value="<?php echo esc_attr(get_option('ee_ga_view_id')); ?>">
510
  </form>
511
  </div>
512
  <div class="stepnotewrp">
513
+ <?php esc_html_e('If you are in the European Economic Area or Switzerland your Merchant Center account must be associated with a Comparison Shopping Service (CSS). Please find more information at <a href="">Google Merchant Center Help</a> website. If you create a new Merchant Center account through this application, it will be associated with Google Shopping, Google’s CSS, by default. You can change the CSS associated with your account at any time. Please find more information about our CSS Partners <a href="">here</a>. Once you have set up your Merchant Center account you can use our onboarding tool regardless of which CSS you use.','conversios'); ?>
514
  </div>
515
  </div>
516
  </div>
522
  <div class="sidebrcontainer">
523
  <div class="onbrd-rdmbx">
524
  <div class="rdm-amnt">
525
+ <small><?php esc_html_e("Redeem upto","conversios"); ?></small>
526
+ <?php echo esc_attr($off_credit_amt); ?>
527
  </div>
528
+ <p><?php esc_html_e("Create your first Google Ads account with us and redeem upto","conversios"); ?> <?php echo esc_attr($off_credit_amt); ?> <?php esc_html_e("on the spend you make in the next 31 days.","conversios"); ?></p>
529
+ <a target="_blank" href="<?php echo esc_url_raw("https://conversios.io/help-center/Google-Spend-Match.pdf"); ?>" class="lrnmorbtn"><?php esc_html_e("Learn more","conversios"); ?> <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/arrow_right.png'); ?>" alt="" /></a>
530
  </div>
531
  <div class="onbrdrgt-nav">
532
  <ul>
533
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://conversios.io/help-center/Installation-Manual.pdf"); ?>"><?php echo esc_html_e("Installation Manual","conversios"); ?></a></li>
534
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://conversios.io/help-center/Google-shopping-Guide.pdf"); ?>" href=""><?php esc_html_e("Google Shopping Guide","conversios"); ?></a></li>
535
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://wordpress.org/plugins/enhanced-e-commerce-for-woocommerce-store/faq/"); ?>" href=""><?php esc_html_e("FAQ","conversios"); ?></a></li>
536
  </ul>
537
  </div>
538
  </div>
549
  <div class="onbrdppmain" role="document">
550
  <div class="onbrdnpp-cntner acccretppcntnr">
551
  <div class="onbrdnpp-hdr">
552
+ <h4><?php esc_html_e("You have not selected Google Ads account.","conversios"); ?></h4>
553
+ <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png');?>" alt="" /></div>
554
  </div>
555
  <div class="onbrdpp-body">
556
+ <p><?php esc_html_e("If you do not select Google Ads account, you will not be able to use some of the major features like:","conversios"); ?></p>
557
  <ul>
558
+ <li><?php esc_html_e("Dynamic Remarketing Tags","conversios"); ?> </li>
559
+ <li><?php esc_html_e("Google Smart Shopping Campaigns","conversios"); ?></li>
560
+ <li><?php esc_html_e("Google Analytics and Google Ads linking","conversios"); ?></li>
561
  </ul>
562
+ <p><?php esc_html_e("Are you sure you want to continue without selecting Google Ads account?","conversios"); ?></p>
563
  </div>
564
  <div class="ppfooterbtn">
565
+ <button type="button" class="ppblubtn btn-secondary" data-dismiss="modal" id="ads-skip-cancel"><?php esc_html_e("Cancel","conversios"); ?></button>
566
+ <button type="button" class="ppblubtn btn-primary" data-dismiss="modal" id="ads-skip-continue"><?php esc_html_e("Continue","conversios"); ?></button>
567
  </div>
568
  </div>
569
  </div>
573
  <div class="onbrdppmain">
574
  <div class="onbrdnpp-cntner ggladsppcntnr">
575
  <div class="onbrdnpp-hdr">
576
+ <h4><?php esc_html_e("Enable Google Ads Account","conversios"); ?></h4>
577
+ <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png');?>" alt="" /></div>
578
  </div>
579
  <div class="onbrdpp-body">
580
+ <p><?php esc_html_e("You’ll receive an invite from Google on your email. Accept the invitation to enable your Google Ads Account.","conversios"); ?></p>
581
  </div>
582
  <div class="ppfooterbtn">
583
+ <button type="button" id="ads-continue" class="ppblubtn sndinvitebtn"><?php esc_html_e("Send Invite","conversios"); ?></button>
584
  </div>
585
  </div>
586
  </div>
590
  <div class="onbrdppmain">
591
  <div class="onbrdnpp-cntner acccretppcntnr">
592
  <div class="onbrdnpp-hdr">
593
+ <h4><?php esc_html_e("You have not selected Google merchant center account.","conversios"); ?></h4>
594
+ <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png');?>" alt="" /></div>
595
  </div>
596
  <div class="onbrdpp-body">
597
+ <p><?php esc_html_e("If you do not select a merchant center account, you will not be able to use complete google shopping features.","conversios"); ?></p>
598
+ <p><?php esc_html_e("Are you sure you want to continue without selecting a merchant center account?","conversios"); ?></p>
599
  </div>
600
  <div class="ppfooterbtn">
601
+ <button type="button" class="ppblubtn btn-secondary" data-dismiss="modal" id="merchant-center-skip-cancel"><?php esc_html_e("Cancel","conversios"); ?></button>
602
+ <button type="button" class="ppblubtn btn-primary" data-dismiss="modal" id="merchant-center-skip-continue"><?php esc_html_e("Continue","conversios"); ?></button>
603
  </div>
604
  </div>
605
  </div>
608
  <div id="createmerchantpopup" class="pp-modal onbrd-popupwrp crtemrchntpp">
609
  <div class="onbrdppmain">
610
  <div class="onbrdnpp-cntner crtemrchntppcntnr">
611
+ <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png'); ?>" alt="" /></div>
612
  <div class="onbrdpp-body">
613
  <div class="row">
614
  <div class="crtemrchnpp-lft">
615
  <div class="crtemrchpplft-top">
616
+ <h4><?php esc_html_e("Create Google Merchant Center Account","conversios"); ?></h4>
617
+ <p><?php esc_html_e("Before you can upload product data, you’ll need to verify and claim your store’s website URL. Claiming associates your website URL with your Google Merchant Center account.","conversios"); ?></p>
618
  </div>
619
  <div class="claimedbx">
620
+ <?php esc_html_e("Your site will automatically be claimed and verified.","conversios"); ?>
621
  </div>
622
  <div class="mrchntformwrp">
623
  <form action="#">
624
  <div class="form-row">
625
+ <input type="hidden" id="get-mail" name="g_email" value="<?php echo isset($this->tvc_data['g_mail'])?esc_attr($this->tvc_data['g_mail']):""; ?>">
626
  <input type="text" value="<?php echo esc_attr($this->tvc_data['user_domain']); ?>" class="fromfiled" name="url" id="url" placeholder="Enter Website">
627
  <div class="cstmcheck-item mt15">
628
  <label for="adult_content">
629
  <input class="" type="checkbox" name="adult_content" id="adult_content">
630
  <span class="checkmark"></span>
631
+ <?php esc_html_e("My site contains","conversios"); ?>
632
  </label>
633
+ <strong><?php esc_html_e("Adult Content","conversios"); ?></strong>
634
  </div>
635
  </div>
636
  <div class="form-row">
637
+ <input type="text" class="fromfiled" name="store_name" id="store_name" placeholder="<?php esc_html_e("Enter Store Name","conversios"); ?>" required>
638
+ <div class="inputinfotxt"><?php esc_html_e("This name will appear in your Shopping Ads.","conversios"); ?></div>
639
  </div>
640
  <div class="form-row">
641
  <?php echo $this->get_countries($this->tvc_data['user_country']); ?>
645
  <label for="terms_conditions">
646
  <input class="" type="checkbox" name="concent" id="terms_conditions">
647
  <span class="checkmark"></span>
648
+ <?php esc_html_e("I accept the","conversios"); ?>
649
  </label>
650
+ <a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/160173?hl=en"); ?>"><?php esc_html_e("terms & conditions","conversios"); ?></a>
651
  </div>
652
  </div>
653
  </form>
654
  </div>
655
  <div class="ppfooterbtn">
656
+ <button type="button" id="create_merchant_account" class="cretemrchntbtn"><?php esc_html_e("Create Account","conversios"); ?>
657
  </button>
658
  </div>
659
  </div>
660
  <div class="crtemrchnpp-right">
661
+ <h6><?php esc_html_e("To use Google Shopping, your website must meet these requirements:","conversios"); ?></h6>
662
  <ul>
663
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/6149970?hl=en"); ?>"><?php esc_html_e("Google Shopping ads policies","conversios"); ?></a></li>
664
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/6150127"); ?>"><?php esc_html_e("Accurate Contact Information","conversios"); ?></a></li>
665
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/6150122"); ?>"><?php esc_html_e("Secure collection of process and personal data","conversios"); ?></a></li>
666
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/6150127"); ?>"><?php esc_html_e("Return Policy","conversios"); ?></a></li>
667
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/6150127"); ?>"><?php esc_html_e("Billing terms & conditions","conversios"); ?></a></li>
668
+ <li><a target="_blank" href="<?php echo esc_url_raw("https://support.google.com/merchants/answer/6150118"); ?>"><?php esc_html_e("Complete checkout process","conversios"); ?></a></li>
669
  </ul>
670
  </div>
671
  </div>
680
  <div class="onbrdppmain">
681
  <div class="onbrdnpp-cntner congratppcntnr">
682
  <div class="onbrdnpp-hdr txtcnter">
683
+ <h2><?php esc_html_e("Congratulations!!","conversios"); ?></h2>
684
+ <div class="ppclsbtn clsbtntrgr"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-icon.png'); ?>" alt="" /></div>
685
  </div>
686
  <div class="onbrdpp-body congratppbody">
687
+ <p><?php esc_html_e("You have been successfully onboarded. Please check the account summary below and confirm.","conversios"); ?></p>
688
  <div class="congratppdtlwrp">
689
  <div class="cngrtppdtl-item" id="google_analytics_property_id_info">
690
  <div class="cngtrpplft">
691
+ <span class="cngrtchckicon"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
692
+ <?php esc_html_e("Google Analytics Property Id:","conversios"); ?>
693
  </div>
694
  <div class="cngtrpprgt" id="selected_google_analytics_property"></div>
695
  </div>
696
  <div class="cngrtppdtl-item" id="google_analytics_measurement_id_info">
697
  <div class="cngtrpplft">
698
+ <span class="cngrtchckicon"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
699
+ <?php esc_html_e("Google Analytics Measurement Id:","conversios"); ?>
700
  </div>
701
  <div class="cngtrpprgt" id="selected_google_analytics_measurement"></div>
702
  </div>
703
  <div class="cngrtppdtl-item" id="google_ads_info">
704
  <div class="cngtrpplft">
705
+ <span class="cngrtchckicon"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
706
+ <?php esc_html_e("Google Ads Account:","conversios"); ?>
707
  </div>
708
  <div class="cngtrpprgt" id="selected_google_ads_account"></div>
709
  </div>
710
  <div class="cngrtppdtl-item" id="google_merchant_center_info">
711
  <div class="cngtrpplft">
712
+ <span class="cngrtchckicon"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/green-check.png'); ?>" alt="" /></span>
713
+ <?php esc_html_e("Google Merchant Center Account","conversios"); ?>
714
  </div>
715
  <div class="cngtrpprgt" id="selected_google_merchant_center"></div>
716
  </div>
717
  </div>
718
  </div>
719
  <div class="ppfooterbtn">
720
+ <button type="button" id="confirm_selection" class="ppblubtn"><?php esc_html_e("Confirm","conversios"); ?></button>
721
  </div>
722
  </div>
723
  </div>
734
  <script>
735
  let tvc_data = <?php echo $j_tvc_data; ?>;
736
  var tvc_ajax_url = '<?php echo admin_url( 'admin-ajax.php' ); ?>';
737
+ let subscription_id ="<?php echo esc_attr($this->subscriptionId); ?>";
738
+ let plan_id ="<?php echo esc_attr($this->plan_id); ?>";
739
+ let app_id ="<?php echo esc_attr($this->app_id); ?>";
740
  /**
741
  * Convesios custom script
742
  */
1000
  * onboarding page add scripts file
1001
  */
1002
  public function add_scripts(){
1003
+ if(isset($_GET['page']) && sanitize_text_field($_GET['page']) == "conversios_onboarding"){
1004
+ wp_register_style('conversios-select2-css', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/select2.css'));
1005
+ wp_enqueue_style('conversios-style-css', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/style.css'), array(), $this->version, 'all');
1006
+ wp_enqueue_style('conversios-responsive-css', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/responsive.css'), array(), $this->version, 'all');
1007
  wp_enqueue_style('conversios-select2-css');
1008
 
1009
 
1010
+ wp_enqueue_script( 'conversios-jquery-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js'), array( 'jquery' ), $this->version, false );
1011
+ wp_register_script('conversios-select2-js', esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/js/select2.min.js') );
1012
  wp_enqueue_script('conversios-select2-js');
1013
+ wp_enqueue_script( 'conversios-onboarding-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/onboarding-custom.js') , array( 'jquery' ), $this->version, false );
1014
  }
1015
  }
1016
  /**
1018
  */
1019
  public function register() {
1020
  // Getting started - shows after installation.
1021
+ if(isset($_GET['page']) && sanitize_text_field($_GET['page']) == "conversios_onboarding"){
1022
  add_dashboard_page(
1023
  esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
1024
  esc_html__( 'Welcome to Conversios Onboarding', 'conversios' ),
1026
  'conversios_onboarding',
1027
  array( $this, 'welcome_screen' )
1028
  );
 
 
 
 
 
 
 
 
1029
  }
1030
  }
1031
  /**
1032
  * Check if we should do any redirect.
1033
  */
1034
  public function maybe_redirect() {
 
 
1035
  if ( ! get_transient( '_conversios_activation_redirect' ) || isset( $_GET['conversios-redirect'] ) ) {
1036
  return;
1037
  }
1038
  // Delete the redirect transient.
1039
  delete_transient( '_conversios_activation_redirect' );
1040
+
1041
+ if ( isset( $_GET['activate-multi'] ) ) {
 
1042
  return;
1043
  }
1044
 
admin/class-enhanced-ecommerce-google-analytics-admin.php CHANGED
@@ -1,5 +1,4 @@
1
  <?php
2
-
3
  /**
4
  * The admin-specific functionality of the plugin.
5
  *
@@ -8,16 +7,6 @@
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
 
@@ -61,8 +50,6 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
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,7 +59,7 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
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.","conversios").' <a href="admin.php?page=conversios"><b><u>'. __("CONFIGURE NOW","conversios").'</u></b></a></p>
76
  </div>';
77
  }
78
  }
@@ -83,7 +70,7 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
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.","conversios").' <a href="admin.php?page=conversios"><b><u>'. __("Automate now","conversios").'</u></b></a></p>
87
  </div>';
88
 
89
  }
@@ -92,13 +79,13 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
92
  if(isset($ee_additional_data['dismissed_ee_adimin_notic_c']) && $ee_additional_data['dismissed_ee_adimin_notic_c'] == 1){
93
  }else{
94
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_c">
95
- <p>'. __("Now access important eCommerce KPIs and Google Ads campaign performance data directly in your wordpress backend to improve your marketing ROI.","conversios").' <a href="admin.php?page=conversios"><b><u>'. __("View it from here.","conversios").'</u></b></a></p>
96
  </div>';
97
 
98
  }
99
  ?>
100
  <script>
101
- var tvc_ajax_url = '<?php echo admin_url( 'admin-ajax.php' ); ?>';
102
  (function( $ ) {
103
  $( function() {
104
  $( '.notice' ).on( 'click', '.notice-dismiss', function( event, el ) {
@@ -124,30 +111,34 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
124
  */
125
  public function enqueue_styles() {
126
  $screen = get_current_screen();
127
- if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) ) {
128
- if($_GET['page'] == "conversios_onboarding"){
129
  return;
130
  }
131
- wp_register_style('plugin-bootstrap', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/css/bootstrap.min.css') );
 
 
 
 
 
132
  wp_enqueue_style('plugin-bootstrap');
133
- wp_enqueue_style('custom-css', esc_url(ENHANCAD_PLUGIN_URL . '/admin/css/custom-style.css'), array(), $this->version, 'all' );
134
  //if(is_rtl()){ }
135
  if($this->is_current_tab_in(array('sync_product_page','gaa_config_page'))){
136
- wp_register_style('plugin-select2', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/select2/select2.min.css') );
137
  wp_enqueue_style('plugin-select2');
138
- wp_register_style('plugin-steps', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/jquery-steps/jquery.steps.css'));
139
  wp_enqueue_style('plugin-steps');
140
- wp_register_style('tvc-dataTables-css', esc_url(ENHANCAD_PLUGIN_URL.'/admin/css/dataTables.bootstrap4.min.css'));
141
  wp_enqueue_style('tvc-dataTables-css');
142
- }
143
- if($this->is_current_tab_in(array("shopping_campaigns_page","add_campaign_page"))){
144
 
145
- wp_register_style('plugin-select2', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/select2/select2.min.css') );
146
  wp_enqueue_style('plugin-select2');
147
- wp_register_style('tvc-bootstrap-datepicker-css', esc_url(ENHANCAD_PLUGIN_URL. '/includes/setup/plugins/datepicker/bootstrap-datepicker.min.css'));
148
  wp_enqueue_style('tvc-bootstrap-datepicker-css');
149
  }
150
- wp_enqueue_style($this->plugin_name, esc_url(plugin_dir_url(__FILE__) . 'css/enhanced-ecommerce-google-analytics-admin.css'), array(), $this->version, 'all');
151
  }
152
  }
153
 
@@ -158,38 +149,36 @@ class Enhanced_Ecommerce_Google_Analytics_Admin extends TVC_Admin_Helper {
158
  */
159
  public function enqueue_scripts() {
160
  $screen = get_current_screen();
161
- if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos($_GET['page'], 'conversios') !== false) ) {
162
- if($_GET['page'] == "conversios_onboarding"){
163
  return;
164
  }
165
- wp_enqueue_script( 'custom-jquery', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js'), array( 'jquery' ), $this->version, false );
166
- wp_register_script('popper_bootstrap', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/popper.min.js') );
167
  wp_enqueue_script('popper_bootstrap');
168
- wp_register_script('atvc_bootstrap', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/bootstrap.min.js') );
169
  wp_enqueue_script('atvc_bootstrap');
170
- //wp_register_script('tvc_bootstrap_mod', 'https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/js/bootstrap.bundle.min.js');
171
- //wp_enqueue_script('tvc_bootstrap_mod');
172
 
173
- wp_enqueue_script( 'tvc-ee-custom-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/tvc-ee-custom.js'), array( 'jquery' ), $this->version, false );
174
 
175
- wp_enqueue_script( 'tvc-ee-slick-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/slick.min.js'), array( 'jquery' ), $this->version, false );
176
 
177
  if($this->is_current_tab_in(array('sync_product_page','gaa_config_page'))){
178
- wp_register_script('plugin-select2', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/select2/select2.min.js') );
179
  wp_enqueue_script('plugin-select2');
180
- wp_register_script('plugin-step-js', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/jquery-steps/jquery.steps.js') );
181
  wp_enqueue_script('plugin-step-js');
182
  }
183
  if($this->is_current_tab_in(array('sync_product_page'))){
184
- wp_enqueue_script( 'tvc-ee-dataTables-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/jquery.dataTables.min.js'), array( 'jquery' ), $this->version, false );
185
- wp_enqueue_script( 'tvc-ee-dataTables-1-js', esc_url(ENHANCAD_PLUGIN_URL . '/admin/js/dataTables.bootstrap4.min.js'), array( 'jquery' ), $this->version, false );
186
  }
187
  if($this->is_current_tab_in(array("shopping_campaigns_page","add_campaign_page"))){
188
- wp_register_script('plugin-select2', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/select2/select2.min.js') );
189
  wp_enqueue_script('plugin-select2');
190
- wp_register_script('plugin-chart', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/chart/chart.js'));
191
  wp_enqueue_script('plugin-chart');
192
- wp_register_script('tvc-bootstrap-datepicker-js', esc_url(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/datepicker/bootstrap-datepicker.min.js'));
193
  wp_enqueue_script('tvc-bootstrap-datepicker-js');
194
  }
195
  }
1
  <?php
 
2
  /**
3
  * The admin-specific functionality of the plugin.
4
  *
7
  *
8
  * @package Enhanced_Ecommerce_Google_Analytics
9
  * @subpackage Enhanced_Ecommerce_Google_Analytics/admin
 
 
 
 
 
 
 
 
 
 
10
  * @author Tatvic
11
  */
12
 
50
  $this->site_url = "admin.php?page=conversios";
51
  $this->pro_plan_site = $this->get_pro_plan_site();
52
  $this->google_detail = $this->get_ee_options_data();
 
 
53
  }
54
  public function tvc_admin_notice(){
55
  // add fixed message notification
59
  }else{
60
  if(!$this->get_subscriptionId()){
61
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_a">
62
+ <p>'. esc_html__("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.","conversios").' <a href="'.esc_url_raw('admin.php?page=conversios').'"><b><u>'. esc_html__("CONFIGURE NOW","conversios").'</u></b></a></p>
63
  </div>';
64
  }
65
  }
70
  $googleDetail = $google_detail['setting'];
71
  if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id =="" && $this->subscriptionId != "" ){
72
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_b">
73
+ <p>'. esc_html__("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.","conversios").' <a href="'.esc_url_raw('admin.php?page=conversios').'"><b><u>'. esc_html__("Automate now","conversios").'</u></b></a></p>
74
  </div>';
75
 
76
  }
79
  if(isset($ee_additional_data['dismissed_ee_adimin_notic_c']) && $ee_additional_data['dismissed_ee_adimin_notic_c'] == 1){
80
  }else{
81
  echo '<div class="notice notice-info is-dismissible" data-id="ee_adimin_notic_c">
82
+ <p>'. esc_html__("Now access important eCommerce KPIs and Google Ads campaign performance data directly in your wordpress backend to improve your marketing ROI.","conversios").' <a href="'.esc_url_raw('admin.php?page=conversios').'"><b><u>'. esc_html__("View it from here.","conversios").'</u></b></a></p>
83
  </div>';
84
 
85
  }
86
  ?>
87
  <script>
88
+ var tvc_ajax_url = '<?php echo esc_url_raw(admin_url( 'admin-ajax.php' )); ?>';
89
  (function( $ ) {
90
  $( function() {
91
  $( '.notice' ).on( 'click', '.notice-dismiss', function( event, el ) {
111
  */
112
  public function enqueue_styles() {
113
  $screen = get_current_screen();
114
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos(sanitize_text_field($_GET['page']), 'conversios') !== false) ) {
115
+ if(sanitize_text_field($_GET['page']) == "conversios_onboarding"){
116
  return;
117
  }
118
+
119
+ if(is_rtl()){
120
+ wp_register_style('plugin-bootstrap', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/css/bootstrap.rtl.min.css') );
121
+ }else{
122
+ wp_register_style('plugin-bootstrap', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/css/bootstrap.min.css') );
123
+ }
124
  wp_enqueue_style('plugin-bootstrap');
125
+ wp_enqueue_style('custom-css', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/custom-style.css'), array(), esc_attr($this->version), 'all' );
126
  //if(is_rtl()){ }
127
  if($this->is_current_tab_in(array('sync_product_page','gaa_config_page'))){
128
+ wp_register_style('plugin-select2', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/select2.css') );
129
  wp_enqueue_style('plugin-select2');
130
+ wp_register_style('plugin-steps', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/jquery-steps/jquery.steps.css'));
131
  wp_enqueue_style('plugin-steps');
132
+ wp_register_style('tvc-dataTables-css', esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/css/dataTables.bootstrap5.min.css'));
133
  wp_enqueue_style('tvc-dataTables-css');
134
+ }else if($this->is_current_tab_in(array("shopping_campaigns_page","add_campaign_page"))){
 
135
 
136
+ wp_register_style('plugin-select2', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/css/select2.css') );
137
  wp_enqueue_style('plugin-select2');
138
+ wp_register_style('tvc-bootstrap-datepicker-css', esc_url_raw(ENHANCAD_PLUGIN_URL. '/includes/setup/plugins/datepicker/bootstrap-datepicker.min.css'));
139
  wp_enqueue_style('tvc-bootstrap-datepicker-css');
140
  }
141
+ wp_enqueue_style(esc_attr($this->plugin_name), esc_url_raw(plugin_dir_url(__FILE__) . 'css/enhanced-ecommerce-google-analytics-admin.css'), array(), esc_attr($this->version), 'all');
142
  }
143
  }
144
 
149
  */
150
  public function enqueue_scripts() {
151
  $screen = get_current_screen();
152
+ if ($screen->id == 'toplevel_page_conversios' || (isset($_GET['page']) && strpos(sanitize_text_field($_GET['page']), 'conversios') !== false) ) {
153
+ if(sanitize_text_field($_GET['page']) == "conversios_onboarding"){
154
  return;
155
  }
156
+ wp_enqueue_script( 'custom-jquery', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/jquery-3.5.1.min.js'), array( 'jquery' ), esc_attr($this->version), false );
157
+ wp_register_script('popper_bootstrap', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/js/popper.min.js') );
158
  wp_enqueue_script('popper_bootstrap');
159
+ wp_register_script('atvc_bootstrap', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/bootstrap/js/bootstrap.min.js') );
160
  wp_enqueue_script('atvc_bootstrap');
 
 
161
 
162
+ wp_enqueue_script( 'tvc-ee-custom-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/tvc-ee-custom.js'), array( 'jquery' ), esc_attr($this->version), false );
163
 
164
+ wp_enqueue_script( 'tvc-ee-slick-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/slick.min.js'), array( 'jquery' ), esc_attr($this->version), false );
165
 
166
  if($this->is_current_tab_in(array('sync_product_page','gaa_config_page'))){
167
+ wp_register_script('plugin-select2', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/select2.min.js') );
168
  wp_enqueue_script('plugin-select2');
169
+ wp_register_script('plugin-step-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/jquery-steps/jquery.steps.js') );
170
  wp_enqueue_script('plugin-step-js');
171
  }
172
  if($this->is_current_tab_in(array('sync_product_page'))){
173
+ wp_enqueue_script( 'tvc-ee-dataTables-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/jquery.dataTables.min.js'), array( 'jquery' ), esc_attr($this->version), false );
174
+ wp_enqueue_script( 'tvc-ee-dataTables-v5-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/dataTables.bootstrap5.min.js'), array( 'jquery' ), esc_attr($this->version), false );
175
  }
176
  if($this->is_current_tab_in(array("shopping_campaigns_page","add_campaign_page"))){
177
+ wp_register_script('plugin-select2', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/select2.min.js') );
178
  wp_enqueue_script('plugin-select2');
179
+ wp_register_script('plugin-chart', esc_url_raw(ENHANCAD_PLUGIN_URL . '/admin/js/chart.js'));
180
  wp_enqueue_script('plugin-chart');
181
+ wp_register_script('tvc-bootstrap-datepicker-js', esc_url_raw(ENHANCAD_PLUGIN_URL . '/includes/setup/plugins/datepicker/bootstrap-datepicker.min.js'));
182
  wp_enqueue_script('tvc-bootstrap-datepicker-js');
183
  }
184
  }
admin/class-enhanced-ecommerce-google-analytics-settings.php CHANGED
@@ -33,8 +33,8 @@ class Enhanced_Ecommerce_Google_Settings {
33
  }
34
  }
35
  }
36
- if(!add_option( $settings, serialize( $ee_options ) )){
37
- update_option($settings, serialize( $ee_options ));
38
  }
39
  }
40
  else {
@@ -65,7 +65,7 @@ class Enhanced_Ecommerce_Google_Settings {
65
  }
66
  }
67
 
68
- update_option($settings, serialize( $get_ee_settings ));
69
  }
70
  }
71
 
@@ -86,7 +86,7 @@ class Enhanced_Ecommerce_Google_Settings {
86
  }
87
  }
88
  }
89
- add_option( $settings, serialize( $ee_options ) );
90
  } else {
91
  $get_ee_settings = unserialize(get_option($settings));
92
  if(is_array($get_ee_settings)) {
@@ -115,7 +115,7 @@ class Enhanced_Ecommerce_Google_Settings {
115
  }
116
  }
117
  }
118
- update_option($settings, serialize( $get_ee_settings ));
119
  }
120
  }
121
  }
33
  }
34
  }
35
  }
36
+ if(!add_option( $settings, serialize(sanitize_option($settings, $ee_options) ) )){
37
+ update_option($settings, serialize( sanitize_option($settings, $ee_options) ));
38
  }
39
  }
40
  else {
65
  }
66
  }
67
 
68
+ update_option($settings, serialize( sanitize_option($settings, $get_ee_settings) ));
69
  }
70
  }
71
 
86
  }
87
  }
88
  }
89
+ add_option( $settings, serialize( sanitize_option($settings, $ee_options) ) );
90
  } else {
91
  $get_ee_settings = unserialize(get_option($settings));
92
  if(is_array($get_ee_settings)) {
115
  }
116
  }
117
  }
118
+ update_option($settings, serialize( sanitize_option($settings, $get_ee_settings) ));
119
  }
120
  }
121
  }
admin/class-survey.php CHANGED
@@ -25,7 +25,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
25
  $url = network_site_url( '/' );
26
  $is_local_url = false;
27
  // Trim it up
28
- $url = strtolower( trim( $url ) );
29
  if ( false === strpos( $url, 'http://' ) && false === strpos( $url, 'https://' ) ) {
30
  $url = 'http://' . $url;
31
  }
@@ -60,7 +60,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
60
  }
61
  }
62
  }
63
- return $is_local_url;
64
  }
65
  public function is_plugin_page() {
66
  $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
@@ -77,7 +77,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
77
  ?>
78
  <script type="text/javascript">
79
  jQuery(function($){
80
- var $deactivateLink = $('#the-list').find('[data-slug="<?php echo $this->plugin; ?>"] span.deactivate a'),
81
  $overlay = $('#ee-survey-<?php echo esc_attr($this->plugin); ?>'),
82
  $form = $overlay.find('form'),
83
  formOpen = false;
@@ -104,7 +104,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
104
  subscription_id:'<?php echo esc_attr($this->subscriptionId); ?>',
105
  radio_option_val: "skip",
106
  other_reason: "",
107
- site_url: '<?php echo esc_url( home_url() ); ?>',
108
  plugin_name: 'ee-woocommerce'
109
  }
110
  add_survey(data);
@@ -113,7 +113,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
113
  $form.submit(function(event) {
114
  event.preventDefault();
115
  if (! $form.find('input[type=radio]:checked').val()) {
116
- $form.find('.ee-survey-footer').prepend('<span class="error"><?php echo esc_js( __( 'Please select an option', 'google-analytics-for-wordpress' ) ); ?></span>');
117
  return;
118
  }
119
  var data = {
@@ -122,7 +122,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
122
  subscription_id:'<?php echo esc_attr($this->subscriptionId); ?>',
123
  radio_option_val: $form.find('.selected input[type=radio]').val(),
124
  other_reason: $form.find('.selected input[type=text]').val(),
125
- site_url: '<?php echo esc_url( home_url() ); ?>',
126
  plugin_name: 'ee-woocommerce'
127
  }
128
  add_survey(data);
@@ -241,7 +241,7 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
241
  }
242
  .ee-survey-wrap .dashicons{
243
  font-size: 24px;
244
- color: #3C434A;
245
  }
246
  </style>
247
  <?php
@@ -254,61 +254,54 @@ if ( ! class_exists( 'TVC_Survey' ) ) {
254
 
255
  $options = array(
256
  1 => array(
257
- "title" => esc_html__("No longer need the plugin","enhanced-e-commerce-for-woocommerce-store"),
258
  ),
259
  2 => array(
260
- 'title' => esc_html__("Switching to a different plugin","enhanced-e-commerce-for-woocommerce-store"),
261
- 'details' => esc_html__( 'Please share which plugin', 'google-analytics-for-wordpress' ),
262
  ),
263
  3 => array(
264
- 'title' => esc_html__("Couldn't get the plugin to work","enhanced-e-commerce-for-woocommerce-store"),
265
  ),
266
  4 => array(
267
- 'title' => esc_html__("It's a temporary deactivation","enhanced-e-commerce-for-woocommerce-store"),
268
  ),
269
  5 => array(
270
- 'title' => esc_html__("Other","enhanced-e-commerce-for-woocommerce-store"),
271
- 'details' => esc_html__( 'Please share the reason', 'google-analytics-for-wordpress' ),
272
  ),
273
  );
274
  ?>
275
  <div class="ee-survey-modal" id="ee-survey-<?php echo $this->plugin; ?>">
276
  <div class="ee-survey-wrap">
277
  <form class="ee-survey" method="post">
278
- <span class="ee-survey-title"><span class="dashicons dashicons-admin-customizer"></span><?php echo ' ' . esc_html__( 'Quick Feedback', 'google-analytics-for-wordpress' ); ?></span>
279
  <span class="ee-survey-desc">
280
  <?php
281
  // Translators: Placeholder for the plugin name.
282
- echo sprintf( esc_html__('If you have a moment, please share why you are deactivating %s:', 'google-analytics-for-wordpress' ), $this->name );
283
  ?>
284
  </span>
285
  <div class="ee-survey-options">
286
  <?php foreach ( $options as $id => $option ) :
287
- //$slug=preg_replace('/[^A-Za-z0-9-]+/', '-', $option['title']);
288
  $slug = sanitize_title($option['title']); ?>
289
  <div class="ee-survey-option">
290
- <label for="ee-survey-option-<?php echo $this->plugin; ?>-<?php echo $id; ?>" class="ee-survey-option-label">
291
- <input id="ee-survey-option-<?php echo $this->plugin; ?>-<?php echo $id; ?>" class="ee-survey-option-input" type="radio" name="code" value="<?php echo $slug; ?>" />
292
- <span class="ee-survey-option-reason"><?php echo $option['title']; ?></span>
293
  </label>
294
  <?php if ( ! empty( $option['details'] ) ) : ?>
295
- <input class="ee-survey-option-details" type="text" placeholder="<?php echo $option['details']; ?>" />
296
  <?php endif; ?>
297
  </div>
298
  <?php endforeach; ?>
299
  </div>
300
  <div class="ee-survey-footer">
301
  <button type="submit" class="ee-survey-submit button button-primary button-large">
302
- <?php
303
- // Translators: Adds an ampersand.
304
- echo sprintf( esc_html__('Submit %s Deactivate', 'google-analytics-for-wordpress' ), '&amp;' );
305
- ?>
306
  </button>
307
  <a href="#" class="ee-survey-deactivate">
308
- <?php
309
- // Translators: Adds an ampersand.
310
- echo sprintf( esc_html__('Skip %s Deactivate', 'google-analytics-for-wordpress' ), '&amp;' );
311
- ?>
312
  </a>
313
  </div>
314
  </form>
25
  $url = network_site_url( '/' );
26
  $is_local_url = false;
27
  // Trim it up
28
+ $url = esc_url(strtolower( trim( $url ) ) );
29
  if ( false === strpos( $url, 'http://' ) && false === strpos( $url, 'https://' ) ) {
30
  $url = 'http://' . $url;
31
  }
60
  }
61
  }
62
  }
63
+ return esc_url_raw($is_local_url);
64
  }
65
  public function is_plugin_page() {
66
  $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
77
  ?>
78
  <script type="text/javascript">
79
  jQuery(function($){
80
+ var $deactivateLink = $('#the-list').find('[data-slug="<?php echo esc_attr($this->plugin); ?>"] span.deactivate a'),
81
  $overlay = $('#ee-survey-<?php echo esc_attr($this->plugin); ?>'),
82
  $form = $overlay.find('form'),
83
  formOpen = false;
104
  subscription_id:'<?php echo esc_attr($this->subscriptionId); ?>',
105
  radio_option_val: "skip",
106
  other_reason: "",
107
+ site_url: '<?php echo esc_url_raw( home_url() ); ?>',
108
  plugin_name: 'ee-woocommerce'
109
  }
110
  add_survey(data);
113
  $form.submit(function(event) {
114
  event.preventDefault();
115
  if (! $form.find('input[type=radio]:checked').val()) {
116
+ $form.find('.ee-survey-footer').prepend('<span class="error"><?php echo esc_js( esc_html__( 'Please select an option', 'conversios' ) ); ?></span>');
117
  return;
118
  }
119
  var data = {
122
  subscription_id:'<?php echo esc_attr($this->subscriptionId); ?>',
123
  radio_option_val: $form.find('.selected input[type=radio]').val(),
124
  other_reason: $form.find('.selected input[type=text]').val(),
125
+ site_url: '<?php echo esc_url_raw( home_url() ); ?>',
126
  plugin_name: 'ee-woocommerce'
127
  }
128
  add_survey(data);
241
  }
242
  .ee-survey-wrap .dashicons{
243
  font-size: 24px;
244
+ color: #3C434A;
245
  }
246
  </style>
247
  <?php
254
 
255
  $options = array(
256
  1 => array(
257
+ "title" => esc_html__("No longer need the plugin","enhanced-e-commerce-for-woocommerce-store", "conversios"),
258
  ),
259
  2 => array(
260
+ 'title' => esc_html__("Switching to a different plugin","conversios"),
261
+ 'details' => esc_html__( 'Please share which plugin', 'conversios' ),
262
  ),
263
  3 => array(
264
+ 'title' => esc_html__("Couldn't get the plugin to work","conversios"),
265
  ),
266
  4 => array(
267
+ 'title' => esc_html__("It's a temporary deactivation","conversios"),
268
  ),
269
  5 => array(
270
+ 'title' => esc_html__("Other","conversios"),
271
+ 'details' => esc_html__( 'Please share the reason', 'conversios' ),
272
  ),
273
  );
274
  ?>
275
  <div class="ee-survey-modal" id="ee-survey-<?php echo $this->plugin; ?>">
276
  <div class="ee-survey-wrap">
277
  <form class="ee-survey" method="post">
278
+ <span class="ee-survey-title"><span class="dashicons dashicons-admin-customizer"></span><?php echo ' ' . esc_html__( 'Quick Feedback', 'conversios' ); ?></span>
279
  <span class="ee-survey-desc">
280
  <?php
281
  // Translators: Placeholder for the plugin name.
282
+ echo sprintf( esc_html__('If you have a moment, please share why you are deactivating %s:', 'conversios' ), esc_attr($this->name) );
283
  ?>
284
  </span>
285
  <div class="ee-survey-options">
286
  <?php foreach ( $options as $id => $option ) :
 
287
  $slug = sanitize_title($option['title']); ?>
288
  <div class="ee-survey-option">
289
+ <label for="ee-survey-option-<?php echo esc_attr($this->plugin); ?>-<?php echo esc_attr($id); ?>" class="ee-survey-option-label">
290
+ <input id="ee-survey-option-<?php echo esc_attr($this->plugin); ?>-<?php echo esc_attr($id); ?>" class="ee-survey-option-input" type="radio" name="code" value="<?php echo esc_attr($slug); ?>" />
291
+ <span class="ee-survey-option-reason"><?php echo esc_attr($option['title']); ?></span>
292
  </label>
293
  <?php if ( ! empty( $option['details'] ) ) : ?>
294
+ <input class="ee-survey-option-details" type="text" placeholder="<?php echo esc_attr($option['details']); ?>" />
295
  <?php endif; ?>
296
  </div>
297
  <?php endforeach; ?>
298
  </div>
299
  <div class="ee-survey-footer">
300
  <button type="submit" class="ee-survey-submit button button-primary button-large">
301
+ <?php echo sprintf( esc_html__('Submit %s Deactivate', 'conversios' ), '&amp;' ); ?>
 
 
 
302
  </button>
303
  <a href="#" class="ee-survey-deactivate">
304
+ <?php echo sprintf( esc_html__('Skip %s Deactivate', 'conversios' ), '&amp;' ); ?>
 
 
 
305
  </a>
306
  </div>
307
  </form>
admin/class-tvc-admin-auto-product-sync-helper.php CHANGED
@@ -47,9 +47,7 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
47
 
48
  }else{
49
  $sql_create = "CREATE TABLE ".esc_sql($tablename)." ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT , `w_product_id` BIGINT(20) NOT NULL , `w_cat_id` INT(10) NOT NULL , `g_cat_id` INT(10) NOT NULL , `g_attribute_mapping` LONGTEXT NOT NULL , `update_date` DATE NOT NULL , `status` INT(1) NOT NULL DEFAULT '1', PRIMARY KEY (`id`) );";
50
- if(maybe_create_table( $tablename, $sql_create )){
51
- //$this->import_last_sync_in_db();
52
- }
53
  }
54
  /* cteate table for save auto sync product call */
55
  $tablename = $wpdb->prefix ."ee_product_sync_call";
@@ -108,48 +106,6 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
108
  $output = array_values($output);
109
  return $output;
110
  }
111
- /*public function import_last_sync_in_db(){
112
- $ee_prod_mapped_cats = unserialize(get_option('ee_prod_mapped_cats'));
113
- $ee_prod_mapped_attrs = unserialize(get_option('ee_prod_mapped_attrs'));
114
- if($ee_prod_mapped_cats != "" && $ee_prod_mapped_attrs != ""){
115
- global $wpdb;
116
- //$table, $field_name = "*"
117
- $row_count = $this->TVC_Admin_DB_Helper->tvc_row_count('ee_product_sync_data');
118
- if($row_count == 0){
119
- if(!empty($ee_prod_mapped_cats)){
120
- foreach($ee_prod_mapped_cats as $mc_key => $mappedCat){
121
- $args= array(
122
- 'post_type' => 'product',
123
- 'numberposts' => -1,
124
- 'post_status' => 'publish',
125
- 'tax_query' => array( array(
126
- 'taxonomy' => 'product_cat',
127
- 'field' => 'term_id',
128
- 'terms' =>$mc_key,
129
- 'operator' => 'IN'
130
- )
131
- )
132
- );
133
- $all_products = get_posts($args);
134
- if(!empty($all_products)){
135
- foreach($all_products as $postkey => $postvalue){
136
- $t_data = array(
137
- 'w_product_id'=>$postvalue->ID,
138
- 'w_cat_id'=>$mc_key,
139
- 'g_cat_id'=>$mappedCat['id'],
140
- 'g_attribute_mapping'=> json_encode($ee_prod_mapped_attrs),
141
- 'update_date'=>date('Y-m-d')
142
- );
143
-
144
- $this->TVC_Admin_DB_Helper->tvc_add_row('ee_product_sync_data', $t_data);
145
- }
146
- wp_reset_postdata();
147
- }
148
- }
149
- }
150
- }
151
- }
152
- }*/
153
  /*
154
  * update last product sync data in DB table "ee_product_sync_data"
155
  */
@@ -161,8 +117,8 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
161
  $ee_product_sync_data =$wpdb->prefix ."ee_product_sync_data";
162
  foreach($ee_prod_mapped_cats as $mc_key => $mappedCat){
163
  //delete old product data of the category
164
- $query = "DELETE FROM $ee_product_sync_data where w_cat_id = %s";
165
- $wpdb->query($wpdb->prepare( "$query", $mc_key ));
166
  $args= array(
167
  'post_type' => 'product',
168
  'numberposts' => -1,
@@ -177,7 +133,7 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
177
  )
178
  );
179
  $all_products = get_posts($args);
180
- $where ="w_cat_id = ".$mc_key;
181
  $p_c_ids = $this->TVC_Admin_DB_Helper->tvc_get_results_in_array('ee_product_sync_data', $where, array('w_product_id','w_cat_id'), true);
182
  if(!empty($all_products)){
183
  foreach($all_products as $postkey => $postvalue){
@@ -186,7 +142,7 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
186
  'w_cat_id'=>esc_sql($mc_key),
187
  'g_cat_id'=>esc_sql($mappedCat['id']),
188
  'g_attribute_mapping'=> json_encode($ee_prod_mapped_attrs),
189
- 'update_date'=>date('Y-m-d')
190
  );
191
  //$table, $where, $field_name = "*"
192
  $p_c_id = $postvalue->ID."_".$mc_key;
@@ -244,26 +200,31 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
244
  $product = array_merge($temp_product,$product);
245
  // for variable
246
  if( !empty($prd) && $prd->get_type() == "variable" ){
247
- //$variation_attributes = $prd->get_variation_attributes();
248
- $p_variations = $prd->get_available_variations();
249
  if(!empty($p_variations)){
250
- foreach ($p_variations as $v_key => $v_value) {
251
- $postmeta_var = (object)$this->TVC_Admin_Helper->tvc_get_post_meta($v_value['variation_id']);
 
 
 
 
 
 
252
  $formArray_val = $formArray['title'];
253
  $product['title'] = (isset($postObj->$formArray_val))?$postObj->$formArray_val:get_the_title($postvalue->w_product_id);
254
  $tvc_temp_desc_key = $formArray['description'];
255
- $product['description'] = (isset($v_value['variation_description']) && $v_value['variation_description'] != "")?$v_value['variation_description']:$postObj->$tvc_temp_desc_key;
256
- $product['offer_id'] = esc_attr($v_value['variation_id']);
257
- $product['id'] = esc_attr($v_value['variation_id']);
258
  $product['item_group_id'] = esc_attr($postvalue->w_product_id);
259
  $productTypes = $this->get_product_category($postvalue->w_product_id);
260
  if(!empty($productTypes)){
261
  $product['productTypes'] = $productTypes;
262
  }
263
- $image_id = $v_value['image_id'];
264
  $product['image_link'] = wp_get_attachment_image_url($image_id, 'full');
265
- if(isset($v_value['attributes']) && !empty($v_value['attributes']) ){
266
- foreach($v_value['attributes'] as $va_key => $va_value ){
267
  $va_key = str_replace("_", " ", $va_key);
268
  if (strpos($va_key, 'color') !== false) {
269
  $product['color'] = $va_value;
@@ -317,7 +278,8 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
317
  $stock_status = str_replace($tvc_find,$tvc_replace,$stock_status);
318
  $product[$key] = $stock_status;
319
  }
320
- }else if(isset($postmeta_var->$value) && $postmeta_var->$value != ""){$product[$key] = $postmeta_var->$value;
 
321
  }else if(in_array($key, array("brand")) ){ //list of cutom option added
322
  $yith_product_brand = $this->TVC_Admin_Helper->add_additional_option_val_in_map_product_attribute($key, $postvalue->w_product_id);
323
  if($yith_product_brand != ""){
@@ -404,7 +366,7 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
404
  public function call_auto_sync_product($last_sync_product_id = array()){
405
  $product_count = $this->TVC_Admin_DB_Helper->tvc_row_count('ee_product_sync_data');
406
  //$count = 0;
407
- $pre_last_sync_product_id = $last_sync_product_id;
408
  if( $product_count > 0 ){
409
  $tvc_currency = esc_attr($this->TVC_Admin_Helper->get_woo_currency());
410
  $merchantId = esc_attr($this->TVC_Admin_Helper->get_merchantId());
@@ -414,7 +376,7 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
414
  $last_sync_product_id =esc_attr(( $last_sync_product_id > 0)?$last_sync_product_id:0);
415
  global $wpdb;
416
  $tablename = $wpdb->prefix .'ee_product_sync_data';
417
- $sql = "select * from ".esc_sql($tablename)." where id > ".esc_sql($last_sync_product_id)." LIMIT ".$this->batch_size;
418
  $products = $wpdb->get_results($sql, OBJECT);
419
  $entries = [];
420
  if(!empty($products)){
@@ -475,14 +437,14 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
475
  }
476
  $t_data = array(
477
  'sync_product_ids'=>json_encode($sync_product_ids),
478
- 'w_total_product'=>esc_attr($product_count),
479
- 'total_sync_product'=>esc_attr($total_sync_product),
480
- 'last_sync'=>$last_sync,
481
  'create_sync'=>date( 'Y-m-d H:i:s', current_time( 'timestamp') ),
482
- 'next_sync'=>$next_sync,
483
- 'last_sync_product_id'=>$last_sync_product_id,
484
- 'action_scheduler_id'=> $action_scheduler_id,
485
- 'status'=>$sync_status
486
  );
487
  $this->TVC_Admin_DB_Helper->tvc_add_row('ee_product_sync_call', $t_data);
488
  as_enqueue_async_action('ee_auto_product_sync_check', array('last_sync_product_id' => $last_sync_product_id));
@@ -555,12 +517,9 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
555
  public function get_tvc_access_token(){
556
  if(!empty($this->access_token)){
557
  return $this->access_token;
558
- }else if(isset($_SESSION['access_token']) && $_SESSION['access_token']){
559
- $this->access_token = $_SESSION['access_token'];
560
- return $this->access_token;
561
  }else{
562
  $google_detail = $this->TVC_Admin_Helper->get_ee_options_data();
563
- $this->access_token = $google_detail['setting']->access_token;
564
  return $this->access_token;
565
  }
566
  }
@@ -568,12 +527,9 @@ if ( ! class_exists( 'TVC_Admin_Auto_Product_sync_Helper' ) ) {
568
  public function get_tvc_refresh_token(){
569
  if(!empty($this->refresh_token)){
570
  return $this->refresh_token;
571
- }else if(isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']){
572
- $this->refresh_token = $_SESSION['refresh_token'];
573
- return $this->refresh_token;
574
  }else{
575
  $google_detail = $this->TVC_Admin_Helper->get_ee_options_data();
576
- $this->refresh_token = $google_detail['setting']->refresh_token;
577
  return $this->refresh_token;
578
  }
579
  }
47
 
48
  }else{
49
  $sql_create = "CREATE TABLE ".esc_sql($tablename)." ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT , `w_product_id` BIGINT(20) NOT NULL , `w_cat_id` INT(10) NOT NULL , `g_cat_id` INT(10) NOT NULL , `g_attribute_mapping` LONGTEXT NOT NULL , `update_date` DATE NOT NULL , `status` INT(1) NOT NULL DEFAULT '1', PRIMARY KEY (`id`) );";
50
+ if(maybe_create_table( $tablename, $sql_create )){ }
 
 
51
  }
52
  /* cteate table for save auto sync product call */
53
  $tablename = $wpdb->prefix ."ee_product_sync_call";
106
  $output = array_values($output);
107
  return $output;
108
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  /*
110
  * update last product sync data in DB table "ee_product_sync_data"
111
  */
117
  $ee_product_sync_data =$wpdb->prefix ."ee_product_sync_data";
118
  foreach($ee_prod_mapped_cats as $mc_key => $mappedCat){
119
  //delete old product data of the category
120
+ $query = "DELETE FROM ".esc_sql($ee_product_sync_data)." where w_cat_id = %s";
121
+ $wpdb->query($wpdb->prepare( "$query", esc_sql($mc_key) ));
122
  $args= array(
123
  'post_type' => 'product',
124
  'numberposts' => -1,
133
  )
134
  );
135
  $all_products = get_posts($args);
136
+ $where ="w_cat_id = ".esc_sql($mc_key);
137
  $p_c_ids = $this->TVC_Admin_DB_Helper->tvc_get_results_in_array('ee_product_sync_data', $where, array('w_product_id','w_cat_id'), true);
138
  if(!empty($all_products)){
139
  foreach($all_products as $postkey => $postvalue){
142
  'w_cat_id'=>esc_sql($mc_key),
143
  'g_cat_id'=>esc_sql($mappedCat['id']),
144
  'g_attribute_mapping'=> json_encode($ee_prod_mapped_attrs),
145
+ 'update_date'=>esc_sql(date('Y-m-d'))
146
  );
147
  //$table, $where, $field_name = "*"
148
  $p_c_id = $postvalue->ID."_".$mc_key;
200
  $product = array_merge($temp_product,$product);
201
  // for variable
202
  if( !empty($prd) && $prd->get_type() == "variable" ){
203
+ $p_variations = $prd->get_children();
 
204
  if(!empty($p_variations)){
205
+ foreach ($p_variations as $v_key => $variation_id) {
206
+ $variation = wc_get_product( $variation_id );
207
+ if(empty($variation)){
208
+ continue;
209
+ }
210
+ $variation_description = wc_format_content($variation->get_description());
211
+ unset($product['customAttributes']);
212
+ $postmeta_var = (object)$this->TVC_Admin_Helper->tvc_get_post_meta($variation_id);
213
  $formArray_val = $formArray['title'];
214
  $product['title'] = (isset($postObj->$formArray_val))?$postObj->$formArray_val:get_the_title($postvalue->w_product_id);
215
  $tvc_temp_desc_key = $formArray['description'];
216
+ $product['description'] = ( $variation_description != "")?$variation_description:$postObj->$tvc_temp_desc_key;
217
+ $product['offer_id'] = esc_attr($variation_id);
218
+ $product['id'] = esc_attr($variation_id);
219
  $product['item_group_id'] = esc_attr($postvalue->w_product_id);
220
  $productTypes = $this->get_product_category($postvalue->w_product_id);
221
  if(!empty($productTypes)){
222
  $product['productTypes'] = $productTypes;
223
  }
224
+ $image_id = $variation->get_image_id();
225
  $product['image_link'] = wp_get_attachment_image_url($image_id, 'full');
226
+ if(isset($variation_attributes) && !empty($variation_attributes) ){
227
+ foreach($variation_attributes as $va_key => $va_value ){
228
  $va_key = str_replace("_", " ", $va_key);
229
  if (strpos($va_key, 'color') !== false) {
230
  $product['color'] = $va_value;
278
  $stock_status = str_replace($tvc_find,$tvc_replace,$stock_status);
279
  $product[$key] = $stock_status;
280
  }
281
+ }else if(isset($postmeta_var->$value) && $postmeta_var->$value != ""){
282
+ $product[$key] = $postmeta_var->$value;
283
  }else if(in_array($key, array("brand")) ){ //list of cutom option added
284
  $yith_product_brand = $this->TVC_Admin_Helper->add_additional_option_val_in_map_product_attribute($key, $postvalue->w_product_id);
285
  if($yith_product_brand != ""){
366
  public function call_auto_sync_product($last_sync_product_id = array()){
367
  $product_count = $this->TVC_Admin_DB_Helper->tvc_row_count('ee_product_sync_data');
368
  //$count = 0;
369
+ $pre_last_sync_product_id = sanitize_text_field($last_sync_product_id);
370
  if( $product_count > 0 ){
371
  $tvc_currency = esc_attr($this->TVC_Admin_Helper->get_woo_currency());
372
  $merchantId = esc_attr($this->TVC_Admin_Helper->get_merchantId());
376
  $last_sync_product_id =esc_attr(( $last_sync_product_id > 0)?$last_sync_product_id:0);
377
  global $wpdb;
378
  $tablename = $wpdb->prefix .'ee_product_sync_data';
379
+ $sql = "select * from ".esc_sql($tablename)." where id > ".esc_sql($last_sync_product_id)." LIMIT ".esc_sql($this->batch_size);
380
  $products = $wpdb->get_results($sql, OBJECT);
381
  $entries = [];
382
  if(!empty($products)){
437
  }
438
  $t_data = array(
439
  'sync_product_ids'=>json_encode($sync_product_ids),
440
+ 'w_total_product'=>esc_sql($product_count),
441
+ 'total_sync_product'=>esc_sql($total_sync_product),
442
+ 'last_sync'=>esc_sql($last_sync),
443
  'create_sync'=>date( 'Y-m-d H:i:s', current_time( 'timestamp') ),
444
+ 'next_sync'=>esc_sql($next_sync),
445
+ 'last_sync_product_id'=> esc_sql($last_sync_product_id),
446
+ 'action_scheduler_id'=> esc_sql($action_scheduler_id),
447
+ 'status'=>esc_sql($sync_status)
448
  );
449
  $this->TVC_Admin_DB_Helper->tvc_add_row('ee_product_sync_call', $t_data);
450
  as_enqueue_async_action('ee_auto_product_sync_check', array('last_sync_product_id' => $last_sync_product_id));
517
  public function get_tvc_access_token(){
518
  if(!empty($this->access_token)){
519
  return $this->access_token;
 
 
 
520
  }else{
521
  $google_detail = $this->TVC_Admin_Helper->get_ee_options_data();
522
+ $this->access_token = base64_decode(sanitize_text_field($google_detail['setting']->access_token));
523
  return $this->access_token;
524
  }
525
  }
527
  public function get_tvc_refresh_token(){
528
  if(!empty($this->refresh_token)){
529
  return $this->refresh_token;
 
 
 
530
  }else{
531
  $google_detail = $this->TVC_Admin_Helper->get_ee_options_data();
532
+ $this->refresh_token = base64_decode(sanitize_text_field($google_detail['setting']->refresh_token));
533
  return $this->refresh_token;
534
  }
535
  }
admin/class-tvc-admin-helper.php CHANGED
@@ -37,8 +37,8 @@ Class TVC_Admin_Helper{
37
  * verstion auto updated
38
  */
39
  public function need_auto_update_db(){
40
- $old_ee_auto_update_id = "tvc_3.0.4";
41
- $new_ee_auto_update_id = "tvc_4.0.0";
42
  $ee_auto_update_id = get_option('ee_auto_update_id');
43
  if($ee_auto_update_id!=""){
44
  if( $ee_auto_update_id != $new_ee_auto_update_id){
@@ -50,10 +50,13 @@ Class TVC_Admin_Helper{
50
  $tablename = $wpdb->prefix ."ee_product_sync_call";
51
  $this->TVC_Admin_DB_Helper->tvc_safe_truncate_table($tablename);
52
  new TVC_Admin_Auto_Product_sync_Helper();
53
- update_option("ee_auto_update_id", $new_ee_auto_update_id);
 
 
 
54
  }
55
  }else{
56
- update_option("ee_auto_update_id", $old_ee_auto_update_id);
57
  }
58
  }
59
  /*
@@ -63,7 +66,7 @@ Class TVC_Admin_Helper{
63
  if($this->get_subscriptionId() != ""){
64
  $google_detail = $this->get_ee_options_data();
65
  if(isset($google_detail['sync_time']) && $google_detail['sync_time']){
66
- $current = current_time( 'timestamp' );
67
  //echo date( 'M-d-Y H:i', current_time( 'timestamp' ))."==>".date( 'M-d-Y H:i', $google_detail['sync_time']);
68
  $diffrent_hours = floor(( $current - $google_detail['sync_time'])/(60*60));
69
  if($diffrent_hours > 11){
@@ -94,6 +97,8 @@ Class TVC_Admin_Helper{
94
  $google_detail = $this->customApiObj->getGoogleAnalyticDetail();
95
  if(property_exists($google_detail,"error") && $google_detail->error == false){
96
  if(property_exists($google_detail,"data") && $google_detail->data != ""){
 
 
97
  $googleDetail = $google_detail->data;
98
  }
99
  }
@@ -112,10 +117,12 @@ Class TVC_Admin_Helper{
112
  $google_detail = $this->customApiObj->getGoogleAnalyticDetail();
113
  if(property_exists($google_detail,"error") && $google_detail->error == false){
114
  if(property_exists($google_detail,"data") && $google_detail->data != "") {
 
 
115
  $googleDetail = $google_detail->data;
116
  }
117
  }else{
118
- return array("error"=>true, "message"=>__("Please try after some time.","conversios"));
119
  }
120
  }
121
  $syncProductStat = [];
@@ -157,7 +164,7 @@ Class TVC_Admin_Helper{
157
  }
158
 
159
  $this->set_ee_options_data(array("setting" => $googleDetail, "prod_sync_status" => (object) $syncProductStat, "campaigns_list"=>$campaigns_list, "sync_time"=>current_time( 'timestamp' )));
160
- return array("error"=>false, "message"=>__("Details updated successfully.","conversios"));
161
  }
162
  /*
163
  * update remarketing snippets
@@ -173,7 +180,7 @@ Class TVC_Admin_Helper{
173
  $remarketing_snippets["id"]=$rs->data->id;
174
  }
175
  }
176
- update_option("ee_remarketing_snippets", serialize($remarketing_snippets));
177
  }
178
  }
179
  /*
@@ -199,7 +206,7 @@ Class TVC_Admin_Helper{
199
  foreach ($con_array as $key => $con_value) {
200
  $con_val_array = explode(":", $con_value);
201
  if(in_array("send_to", $con_val_array)){
202
- update_option("ee_conversio_send_to", $con_val_array[1]);
203
  break 2;
204
  }
205
  }
@@ -252,14 +259,14 @@ Class TVC_Admin_Helper{
252
  $googleStatus = "pending";
253
  }
254
  $t_data = array(
255
- 'gmc_id' => $merchant_id,
256
- 'name'=>$value->name,
257
- 'product_id'=>$value->productId,
258
- 'google_status'=>$googleStatus,
259
- 'image_link'=> $value->imageLink,
260
  'issues'=>json_encode($value->issues)
261
  );
262
- $where ="product_id = '".$value->productId."'";
263
  $row_count = $this->TVC_Admin_DB_Helper->tvc_check_row('ee_products_sync_list', $where);
264
  if($row_count == 0){
265
  $this->TVC_Admin_DB_Helper->tvc_add_row('ee_products_sync_list', $t_data);
@@ -288,13 +295,13 @@ Class TVC_Admin_Helper{
288
  * set API data in DB
289
  */
290
  public function set_ee_options_data($ee_options_data){
291
- update_option("ee_api_data", serialize($ee_options_data));
292
  }
293
  /*
294
  * set additional data in DB
295
  */
296
  public function set_ee_additional_data($ee_additional_data){
297
- update_option("ee_additional_data", serialize($ee_additional_data));
298
  }
299
  /*
300
  * get additional data from DB
@@ -324,13 +331,9 @@ Class TVC_Admin_Helper{
324
  public function get_subscriptionId(){
325
  if(!empty($this->subscriptionId)){
326
  return $this->subscriptionId;
327
- }else{
328
- $ee_options_settings = "";
329
- if(!isset($GLOBALS['tatvicData']['tvc_subscription'])){
330
- $ee_options_settings = $this->get_ee_options_settings();
331
- }
332
- $this->subscriptionId = (isset($GLOBALS['tatvicData']['tvc_subscription'])) ? $GLOBALS['tatvicData']['tvc_subscription'] : ((isset($ee_options_settings['subscription_id']))?$ee_options_settings['subscription_id']:"");
333
- return $this->subscriptionId;
334
  }
335
  }
336
  /*
@@ -342,11 +345,7 @@ Class TVC_Admin_Helper{
342
  }else{
343
  $tvc_merchant = "";
344
  $google_detail = $this->get_ee_options_data();
345
- if(!isset($GLOBALS['tatvicData']['tvc_merchant']) && isset($google_detail['setting']->google_merchant_center_id)){
346
- $tvc_merchant = $google_detail['setting']->google_merchant_center_id;
347
- }
348
- $this->merchantId = (isset($GLOBALS['tatvicData']['tvc_merchant'])) ? $GLOBALS['tatvicData']['tvc_merchant'] : $tvc_merchant;
349
- return $this->merchantId;
350
  }
351
  }
352
  /*
@@ -357,12 +356,8 @@ Class TVC_Admin_Helper{
357
  return $this->main_merchantId;
358
  }else{
359
  $main_merchantId = "";
360
- $google_detail = $this->get_ee_options_data();
361
- if(!isset($GLOBALS['tatvicData']['tvc_main_merchant_id']) && isset($google_detail['setting']->merchant_id)){
362
- $main_merchantId = $google_detail['setting']->merchant_id;
363
- }
364
- $this->main_merchantId = (isset($GLOBALS['tatvicData']['tvc_main_merchant_id'])) ? $GLOBALS['tatvicData']['tvc_main_merchant_id'] : $main_merchantId;
365
- return $this->main_merchantId;
366
  }
367
  }
368
  /*
@@ -469,12 +464,8 @@ Class TVC_Admin_Helper{
469
  if(!empty($this->currentCustomerId)){
470
  return $this->currentCustomerId;
471
  }else{
472
- $ee_options_settings = "";
473
- if(!isset($GLOBALS['tatvicData']['tvc_customer'])){
474
- $ee_options_settings = $this->get_ee_options_settings();
475
- }
476
- $this->currentCustomerId = (isset($GLOBALS['tatvicData']['tvc_customer'])) ? $GLOBALS['tatvicData']['tvc_customer'] : ((isset($ee_options_settings['google_ads_id']))?$ee_options_settings['google_ads_id']:"");
477
- return $this->currentCustomerId;
478
  }
479
  }
480
  public function get_user_currency_symbol(){
@@ -496,7 +487,7 @@ Class TVC_Admin_Helper{
496
  public function add_spinner_html(){
497
  $spinner_gif = ENHANCAD_PLUGIN_URL . '/admin/images/ajax-loader.gif';
498
  echo '<div class="feed-spinner" id="feed-spinner" style="display:none;">
499
- <img id="img-spinner" src="' . $spinner_gif . '" alt="Loading" />
500
  </div>';
501
  }
502
 
@@ -697,16 +688,16 @@ Class TVC_Admin_Helper{
697
  if(property_exists($googleDetail,"google_merchant_center_id") && property_exists($googleDetail,"is_site_verified") && property_exists($googleDetail,"is_domain_claim") && property_exists($googleDetail,"google_ads_id")){
698
  if( $googleDetail->google_merchant_center_id != "" && $googleDetail->google_ads_id != "" && $googleDetail->is_site_verified == 1 && $googleDetail->is_domain_claim == 1 ){
699
  $setting_status['google_shopping_conf']= true;
700
- $setting_status['google_shopping_conf_msg']= __("Google Shopping Configuration Success.","conversios");
701
  }else if($googleDetail->google_merchant_center_id == "" || $googleDetail->google_ads_id == "" ){
702
  $setting_status['google_shopping_conf']= false;
703
  $setting_status['google_shopping_conf_msg']= "Connect your merchant center account and make your products available to shoppers across Google <a href='".esc_url($this->get_onboarding_page_url())."'>click here</a>.";
704
  }else if($googleDetail->is_site_verified ==0 && $googleDetail->is_domain_claim ==0 ){
705
  $setting_status['google_shopping_conf']= false;
706
- $setting_status['google_shopping_conf_msg']= __("Site verification and domain claim for your merchant center account failed.","conversios");
707
  }else if($googleDetail->is_site_verified ==0 ){
708
  $setting_status['google_shopping_conf']= false;
709
- $setting_status['google_shopping_conf_msg']= __("Site verification and domain claim for your merchant center account failed.","conversios");
710
  }
711
  }else{
712
  $setting_status['google_shopping_conf']= false;
@@ -725,13 +716,13 @@ Class TVC_Admin_Helper{
725
 
726
  if($sync_product_total > 1 && $sync_product_approved > 1 && $sync_product_disapproved < 1){
727
  $setting_status['google_shopping_p_sync']= true;
728
- $setting_status['google_shopping_p_sync_msg']= __("Google Shopping product sync is a success.","conversios");
729
  }else if($sync_product_total < 1){
730
  $setting_status['google_shopping_p_sync']= false;
731
- $setting_status['google_shopping_p_sync_msg']= __("Sync your product data into Merchant center and get eligible for free listing across Google.","conversios");
732
  }else if($sync_product_disapproved > 0){
733
  $setting_status['google_shopping_p_sync']= false;
734
- $setting_status['google_shopping_p_sync_msg']= __("There seems to be some problem with your product data. Rectify the issues by selecting right attributes.","conversios");
735
  }
736
  }
737
  }else{
@@ -746,13 +737,13 @@ Class TVC_Admin_Helper{
746
  $totalCampaigns = count($campaigns_list);
747
  if($totalCampaigns < 1){
748
  $setting_status['google_shopping_p_campaigns']= false;
749
- $setting_status['google_shopping_p_campaigns_msg']= __("Reach out to customers based on their past site behavior by running start shopping campaign.","conversios");
750
  }else{
751
  $setting_status['google_shopping_p_campaigns']= true;
752
  }
753
  }else{
754
  $setting_status['google_shopping_p_campaigns']= false;
755
- $setting_status['google_shopping_p_campaigns_msg']= __("Reach out to customers based on their past site behavior by running start shopping campaign.","conversios");
756
  }
757
  }else{
758
  $setting_status['google_shopping_p_campaigns']= false;
@@ -763,9 +754,9 @@ Class TVC_Admin_Helper{
763
  }
764
 
765
  public function is_current_tab_in($tabs){
766
- if(isset($_GET['tab']) && is_array($tabs) && in_array($_GET['tab'], $tabs)){
767
  return true;
768
- }else if(isset($_GET['tab']) && $_GET['tab'] ==$tabs){
769
  return true;
770
  }
771
  return false;
@@ -808,16 +799,16 @@ Class TVC_Admin_Helper{
808
  $notice_text ="";
809
  $call_js_function_args="";
810
  if (isset($googleDetail->is_site_verified) && isset($googleDetail->is_domain_claim) && $googleDetail->is_site_verified == '0' && $googleDetail->is_domain_claim == '0') {
811
- $title = __("Site verification and Domain claim for merchant center account failed.","conversios");
812
- $message = __("Without a verified and claimed website, your product will get disapproved.","conversios");
813
  $call_js_function_args = "both";
814
  }else if(isset($googleDetail->is_site_verified) && $googleDetail->is_site_verified == '0'){
815
- $title = __("Site verification for merchant center account failed.","conversios");
816
- $message = __("Without a verified and claimed website, your product will get disapproved.","conversios");
817
  $call_js_function_args = "site_verified";
818
  }else if(isset($googleDetail->is_domain_claim) && $googleDetail->is_domain_claim == '0'){
819
- $title = __("Site verification for merchant center account failed.","conversios");
820
- $message = __("Without a verified and claimed website, your product will get disapproved.","conversios");
821
  $call_js_function_args = "domain_claim";
822
  }
823
  if($message!= "" && $title != ""){
@@ -829,7 +820,7 @@ Class TVC_Admin_Helper{
829
  </div>
830
  <div class="erralertrigt">
831
  <h6><?php echo $title; ?></h6>
832
- <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> <?php _e("to verify and claim the domain.","conversios"); ?></p>
833
  </div>
834
  </div>
835
  </div>
@@ -896,7 +887,7 @@ Class TVC_Admin_Helper{
896
  if(isset($google_detail['setting']) && $google_detail['setting']){
897
  $googleDetail = $google_detail['setting'];
898
  if($googleDetail->is_site_verified == '0'){
899
- return array('error'=>true, 'msg'=>__("First need to verified your site. Click on site verification refresh icon to verified your site.","conversios"));
900
  }else if(property_exists($googleDetail,"is_domain_claim") && $googleDetail->is_domain_claim == '0'){
901
  //'website_url' => $googleDetail->site_url,
902
  $postData = [
@@ -910,10 +901,10 @@ Class TVC_Admin_Helper{
910
  return array('error'=>true, 'msg'=>$claimWebsite->errors[0]);
911
  }else{
912
  $this->update_subscription_details_api_to_db();
913
- return array('error'=>false, 'msg'=>__("Domain claimed successfully.", "conversios"));
914
  }
915
  }else{
916
- return array('error'=>false, 'msg'=>__("Already domain claimed successfully.", "conversios"));
917
  }
918
  }
919
  }
@@ -965,7 +956,7 @@ Class TVC_Admin_Helper{
965
  return array('error'=>true, 'msg'=>$siteVerification_tag->errors[0]);
966
  }else{
967
  $this->update_subscription_details_api_to_db();
968
- return array('error'=>false, 'msg'=>__("Site verification successfully.","conversios"));
969
  }
970
  }else{
971
  return array('error'=>true, 'msg'=>$siteVerificationToken_tag->errors[0]);
@@ -973,11 +964,11 @@ Class TVC_Admin_Helper{
973
  // one more try
974
  }else{
975
  $this->update_subscription_details_api_to_db();
976
- return array('error'=>false, 'msg'=>__("Site verification successfully.","conversios"));
977
  }
978
  }
979
  }else{
980
- return array('error'=>false, 'msg'=>__("Already site verification successfully.","conversios"));
981
  }
982
  }
983
  }
@@ -1034,7 +1025,7 @@ Class TVC_Admin_Helper{
1034
  }
1035
  /* message notification */
1036
  public function set_ee_msg_nofification_list($ee_msg_list){
1037
- update_option("ee_msg_nofifications", serialize($ee_msg_list));
1038
  }
1039
  public function get_ee_msg_nofification_list(){
1040
  return unserialize(get_option('ee_msg_nofifications'));
@@ -1067,14 +1058,14 @@ Class TVC_Admin_Helper{
1067
  * add fixed notification
1068
  */
1069
  $nofifications["tvc_f_notif_1"] = array(
1070
- "tittle"=>__("Congratulations..!! You are one step closer.","conversios"),
1071
- "html"=>__("Thanks for installing the new avatar of Enhanced Ecommerce for WooCommerce plugin. Explore the full potential of Google Analytics, Google Ads and Google shopping by setting up all your Google accounts and take data driven decisions to scale your eCommerce business faster.", "conversios")
1072
  );
1073
  $nofifications["tvc_f_notif_2"] = array(
1074
- "tittle"=>__("Share your feedback.","conversios"),
1075
- "html"=>__("Your feedback is very important to us. Please write about your experience and the new feature requests here.","conversios"),
1076
  "link"=>"https://wordpress.org/support/plugin/enhanced-e-commerce-for-woocommerce-store/reviews/",
1077
- "link_title"=>__("Share Feedback", "conversios"),
1078
  "link_type"=>"external"
1079
  );
1080
  /*
@@ -1107,7 +1098,7 @@ Class TVC_Admin_Helper{
1107
  if($diffrent_day == 6 ){
1108
  $nofifications["tvc_expired_plan_not_".date("YYYY_m_d",$current)] = array(
1109
  "tittle"=>"Plan Expired..!!",
1110
- "html"=>__("Your plan is expired now. Contact “analytics2@tatvic.com” or call us at “(415) 968-6313” to renew your plan.", "conversios")
1111
  );
1112
  }
1113
  }
37
  * verstion auto updated
38
  */
39
  public function need_auto_update_db(){
40
+ $old_ee_auto_update_id = esc_attr(sanitize_text_field("tvc_4.0.0"));
41
+ $new_ee_auto_update_id = esc_attr(sanitize_text_field("tvc_4.5.1"));
42
  $ee_auto_update_id = get_option('ee_auto_update_id');
43
  if($ee_auto_update_id!=""){
44
  if( $ee_auto_update_id != $new_ee_auto_update_id){
50
  $tablename = $wpdb->prefix ."ee_product_sync_call";
51
  $this->TVC_Admin_DB_Helper->tvc_safe_truncate_table($tablename);
52
  new TVC_Admin_Auto_Product_sync_Helper();
53
+ update_option("ee_auto_update_id", sanitize_option("ee_auto_update_id", $new_ee_auto_update_id) );
54
+
55
+ //tvc_4.5.1
56
+ $this->update_subscription_details_api_to_db();
57
  }
58
  }else{
59
+ update_option("ee_auto_update_id", sanitize_option("ee_auto_update_id", $old_ee_auto_update_id) );
60
  }
61
  }
62
  /*
66
  if($this->get_subscriptionId() != ""){
67
  $google_detail = $this->get_ee_options_data();
68
  if(isset($google_detail['sync_time']) && $google_detail['sync_time']){
69
+ $current = sanitize_text_field(current_time( 'timestamp' ));
70
  //echo date( 'M-d-Y H:i', current_time( 'timestamp' ))."==>".date( 'M-d-Y H:i', $google_detail['sync_time']);
71
  $diffrent_hours = floor(( $current - $google_detail['sync_time'])/(60*60));
72
  if($diffrent_hours > 11){
97
  $google_detail = $this->customApiObj->getGoogleAnalyticDetail();
98
  if(property_exists($google_detail,"error") && $google_detail->error == false){
99
  if(property_exists($google_detail,"data") && $google_detail->data != ""){
100
+ $google_detail->data->access_token = base64_encode($google_detail->data->access_token);
101
+ $google_detail->data->refresh_token = base64_encode($google_detail->data->refresh_token);
102
  $googleDetail = $google_detail->data;
103
  }
104
  }
117
  $google_detail = $this->customApiObj->getGoogleAnalyticDetail();
118
  if(property_exists($google_detail,"error") && $google_detail->error == false){
119
  if(property_exists($google_detail,"data") && $google_detail->data != "") {
120
+ $google_detail->data->access_token = base64_encode($google_detail->data->access_token);
121
+ $google_detail->data->refresh_token = base64_encode($google_detail->data->refresh_token);
122
  $googleDetail = $google_detail->data;
123
  }
124
  }else{
125
+ return array("error"=>true, "message"=>esc_html__("Please try after some time.","conversios"));
126
  }
127
  }
128
  $syncProductStat = [];
164
  }
165
 
166
  $this->set_ee_options_data(array("setting" => $googleDetail, "prod_sync_status" => (object) $syncProductStat, "campaigns_list"=>$campaigns_list, "sync_time"=>current_time( 'timestamp' )));
167
+ return array("error"=>false, "message"=>esc_html__("Details updated successfully.","conversios"));
168
  }
169
  /*
170
  * update remarketing snippets
180
  $remarketing_snippets["id"]=$rs->data->id;
181
  }
182
  }
183
+ update_option("ee_remarketing_snippets", serialize(sanitize_option("ee_remarketing_snippets", $remarketing_snippets) ));
184
  }
185
  }
186
  /*
206
  foreach ($con_array as $key => $con_value) {
207
  $con_val_array = explode(":", $con_value);
208
  if(in_array("send_to", $con_val_array)){
209
+ update_option("ee_conversio_send_to", sanitize_option("ee_conversio_send_to", $con_val_array[1]) );
210
  break 2;
211
  }
212
  }
259
  $googleStatus = "pending";
260
  }
261
  $t_data = array(
262
+ 'gmc_id' => esc_sql($merchant_id),
263
+ 'name'=> esc_sql($value->name),
264
+ 'product_id'=> esc_sql($value->productId),
265
+ 'google_status'=> esc_sql($googleStatus),
266
+ 'image_link'=> esc_sql($value->imageLink),
267
  'issues'=>json_encode($value->issues)
268
  );
269
+ $where ="product_id = '".esc_sql($value->productId)."'";
270
  $row_count = $this->TVC_Admin_DB_Helper->tvc_check_row('ee_products_sync_list', $where);
271
  if($row_count == 0){
272
  $this->TVC_Admin_DB_Helper->tvc_add_row('ee_products_sync_list', $t_data);
295
  * set API data in DB
296
  */
297
  public function set_ee_options_data($ee_options_data){
298
+ update_option("ee_api_data", serialize(sanitize_option("ee_api_data", $ee_options_data) ));
299
  }
300
  /*
301
  * set additional data in DB
302
  */
303
  public function set_ee_additional_data($ee_additional_data){
304
+ update_option("ee_additional_data", serialize(sanitize_option("ee_additional_data",$ee_additional_data) ));
305
  }
306
  /*
307
  * get additional data from DB
331
  public function get_subscriptionId(){
332
  if(!empty($this->subscriptionId)){
333
  return $this->subscriptionId;
334
+ }else{
335
+ $ee_options_settings = $this->get_ee_options_settings();
336
+ return $this->subscriptionId =(isset($ee_options_settings['subscription_id']))?$ee_options_settings['subscription_id']:"";
 
 
 
 
337
  }
338
  }
339
  /*
345
  }else{
346
  $tvc_merchant = "";
347
  $google_detail = $this->get_ee_options_data();
348
+ return $this->merchantId = (isset($google_detail['setting']->google_merchant_center_id))?$google_detail['setting']->google_merchant_center_id:"";
 
 
 
 
349
  }
350
  }
351
  /*
356
  return $this->main_merchantId;
357
  }else{
358
  $main_merchantId = "";
359
+ $google_detail = $this->get_ee_options_data();
360
+ return $this->main_merchantId = (isset($google_detail['setting']->merchant_id))?$google_detail['setting']->merchant_id:"";
 
 
 
 
361
  }
362
  }
363
  /*
464
  if(!empty($this->currentCustomerId)){
465
  return $this->currentCustomerId;
466
  }else{
467
+ $ee_options_settings = $this->get_ee_options_settings();
468
+ return $this->currentCustomerId = (isset($ee_options_settings['google_ads_id']))?$ee_options_settings['google_ads_id']:"";
 
 
 
 
469
  }
470
  }
471
  public function get_user_currency_symbol(){
487
  public function add_spinner_html(){
488
  $spinner_gif = ENHANCAD_PLUGIN_URL . '/admin/images/ajax-loader.gif';
489
  echo '<div class="feed-spinner" id="feed-spinner" style="display:none;">
490
+ <img id="img-spinner" src="' . esc_url($spinner_gif) . '" alt="Loading" />
491
  </div>';
492
  }
493
 
688
  if(property_exists($googleDetail,"google_merchant_center_id") && property_exists($googleDetail,"is_site_verified") && property_exists($googleDetail,"is_domain_claim") && property_exists($googleDetail,"google_ads_id")){
689
  if( $googleDetail->google_merchant_center_id != "" && $googleDetail->google_ads_id != "" && $googleDetail->is_site_verified == 1 && $googleDetail->is_domain_claim == 1 ){
690
  $setting_status['google_shopping_conf']= true;
691
+ $setting_status['google_shopping_conf_msg']= esc_html__("Google Shopping Configuration Success.","conversios");
692
  }else if($googleDetail->google_merchant_center_id == "" || $googleDetail->google_ads_id == "" ){
693
  $setting_status['google_shopping_conf']= false;
694
  $setting_status['google_shopping_conf_msg']= "Connect your merchant center account and make your products available to shoppers across Google <a href='".esc_url($this->get_onboarding_page_url())."'>click here</a>.";
695
  }else if($googleDetail->is_site_verified ==0 && $googleDetail->is_domain_claim ==0 ){
696
  $setting_status['google_shopping_conf']= false;
697
+ $setting_status['google_shopping_conf_msg']= esc_html__("Site verification and domain claim for your merchant center account failed.","conversios");
698
  }else if($googleDetail->is_site_verified ==0 ){
699
  $setting_status['google_shopping_conf']= false;
700
+ $setting_status['google_shopping_conf_msg']= esc_html__("Site verification and domain claim for your merchant center account failed.","conversios");
701
  }
702
  }else{
703
  $setting_status['google_shopping_conf']= false;
716
 
717
  if($sync_product_total > 1 && $sync_product_approved > 1 && $sync_product_disapproved < 1){
718
  $setting_status['google_shopping_p_sync']= true;
719
+ $setting_status['google_shopping_p_sync_msg']= esc_html__("Google Shopping product sync is a success.","conversios");
720
  }else if($sync_product_total < 1){
721
  $setting_status['google_shopping_p_sync']= false;
722
+ $setting_status['google_shopping_p_sync_msg']= esc_html__("Sync your product data into Merchant center and get eligible for free listing across Google.","conversios");
723
  }else if($sync_product_disapproved > 0){
724
  $setting_status['google_shopping_p_sync']= false;
725
+ $setting_status['google_shopping_p_sync_msg']= esc_html__("There seems to be some problem with your product data. Rectify the issues by selecting right attributes.","conversios");
726
  }
727
  }
728
  }else{
737
  $totalCampaigns = count($campaigns_list);
738
  if($totalCampaigns < 1){
739
  $setting_status['google_shopping_p_campaigns']= false;
740
+ $setting_status['google_shopping_p_campaigns_msg']= esc_html__("Reach out to customers based on their past site behavior by running start shopping campaign.","conversios");
741
  }else{
742
  $setting_status['google_shopping_p_campaigns']= true;
743
  }
744
  }else{
745
  $setting_status['google_shopping_p_campaigns']= false;
746
+ $setting_status['google_shopping_p_campaigns_msg']= esc_html__("Reach out to customers based on their past site behavior by running start shopping campaign.","conversios");
747
  }
748
  }else{
749
  $setting_status['google_shopping_p_campaigns']= false;
754
  }
755
 
756
  public function is_current_tab_in($tabs){
757
+ if(isset($_GET['tab']) && is_array($tabs) && in_array(sanitize_text_field($_GET['tab']), $tabs)){
758
  return true;
759
+ }else if(isset($_GET['tab']) && sanitize_text_field($_GET['tab']) ==$tabs){
760
  return true;
761
  }
762
  return false;
799
  $notice_text ="";
800
  $call_js_function_args="";
801
  if (isset($googleDetail->is_site_verified) && isset($googleDetail->is_domain_claim) && $googleDetail->is_site_verified == '0' && $googleDetail->is_domain_claim == '0') {
802
+ $title = esc_html__("Site verification and Domain claim for merchant center account failed.","conversios");
803
+ $message = esc_html__("Without a verified and claimed website, your product will get disapproved.","conversios");
804
  $call_js_function_args = "both";
805
  }else if(isset($googleDetail->is_site_verified) && $googleDetail->is_site_verified == '0'){
806
+ $title = esc_html__("Site verification for merchant center account failed.","conversios");
807
+ $message = esc_html__("Without a verified and claimed website, your product will get disapproved.","conversios");
808
  $call_js_function_args = "site_verified";
809
  }else if(isset($googleDetail->is_domain_claim) && $googleDetail->is_domain_claim == '0'){
810
+ $title = esc_html__("Site verification for merchant center account failed.","conversios");
811
+ $message = esc_html__("Without a verified and claimed website, your product will get disapproved.","conversios");
812
  $call_js_function_args = "domain_claim";
813
  }
814
  if($message!= "" && $title != ""){
820
  </div>
821
  <div class="erralertrigt">
822
  <h6><?php echo $title; ?></h6>
823
+ <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> <?php esc_html_e("to verify and claim the domain.","conversios"); ?></p>
824
  </div>
825
  </div>
826
  </div>
887
  if(isset($google_detail['setting']) && $google_detail['setting']){
888
  $googleDetail = $google_detail['setting'];
889
  if($googleDetail->is_site_verified == '0'){
890
+ return array('error'=>true, 'msg'=>esc_html__("First need to verified your site. Click on site verification refresh icon to verified your site.","conversios"));
891
  }else if(property_exists($googleDetail,"is_domain_claim") && $googleDetail->is_domain_claim == '0'){
892
  //'website_url' => $googleDetail->site_url,
893
  $postData = [
901
  return array('error'=>true, 'msg'=>$claimWebsite->errors[0]);
902
  }else{
903
  $this->update_subscription_details_api_to_db();
904
+ return array('error'=>false, 'msg'=>esc_html__("Domain claimed successfully.", "conversios"));
905
  }
906
  }else{
907
+ return array('error'=>false, 'msg'=>esc_html__("Already domain claimed successfully.", "conversios"));
908
  }
909
  }
910
  }
956
  return array('error'=>true, 'msg'=>$siteVerification_tag->errors[0]);
957
  }else{
958
  $this->update_subscription_details_api_to_db();
959
+ return array('error'=>false, 'msg'=>esc_html__("Site verification successfully.","conversios"));
960
  }
961
  }else{
962
  return array('error'=>true, 'msg'=>$siteVerificationToken_tag->errors[0]);
964
  // one more try
965
  }else{
966
  $this->update_subscription_details_api_to_db();
967
+ return array('error'=>false, 'msg'=>esc_html__("Site verification successfully.","conversios"));
968
  }
969
  }
970
  }else{
971
+ return array('error'=>false, 'msg'=>esc_html__("Already site verification successfully.","conversios"));
972
  }
973
  }
974
  }
1025
  }
1026
  /* message notification */
1027
  public function set_ee_msg_nofification_list($ee_msg_list){
1028
+ update_option("ee_msg_nofifications", serialize(sanitize_option("ee_msg_nofifications", $ee_msg_list) ));
1029
  }
1030
  public function get_ee_msg_nofification_list(){
1031
  return unserialize(get_option('ee_msg_nofifications'));
1058
  * add fixed notification
1059
  */
1060
  $nofifications["tvc_f_notif_1"] = array(
1061
+ "tittle"=>esc_html__("Congratulations..!! You are one step closer.","conversios"),
1062
+ "html"=>esc_html__("Thanks for installing the new avatar of Enhanced Ecommerce for WooCommerce plugin. Explore the full potential of Google Analytics, Google Ads and Google shopping by setting up all your Google accounts and take data driven decisions to scale your eCommerce business faster.", "conversios")
1063
  );
1064
  $nofifications["tvc_f_notif_2"] = array(
1065
+ "tittle"=>esc_html__("Share your feedback.","conversios"),
1066
+ "html"=>esc_html__("Your feedback is very important to us. Please write about your experience and the new feature requests here.","conversios"),
1067
  "link"=>"https://wordpress.org/support/plugin/enhanced-e-commerce-for-woocommerce-store/reviews/",
1068
+ "link_title"=>esc_html__("Share Feedback", "conversios"),
1069
  "link_type"=>"external"
1070
  );
1071
  /*
1098
  if($diffrent_day == 6 ){
1099
  $nofifications["tvc_expired_plan_not_".date("YYYY_m_d",$current)] = array(
1100
  "tittle"=>"Plan Expired..!!",
1101
+ "html"=>esc_html__("Your plan is expired now. Contact “analytics2@tatvic.com” or call us at “(415) 968-6313” to renew your plan.", "conversios")
1102
  );
1103
  }
1104
  }
admin/css/bootstrap.rtl.min.css DELETED
@@ -1,7 +0,0 @@
1
- @charset "UTF-8";/*!
2
- * Bootstrap v5.0.1 (https://getbootstrap.com/)
3
- * Copyright 2011-2021 The Bootstrap Authors
4
- * Copyright 2011-2021 Twitter, Inc.
5
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
6
- */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0))}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-font-sans-serif);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-right:0;list-style:none}.list-inline{padding-right:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-left:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{width:100%;padding-left:var(--bs-gutter-x,.75rem);padding-right:var(--bs-gutter-x,.75rem);margin-left:auto;margin-right:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(var(--bs-gutter-y) * -1);margin-left:calc(var(--bs-gutter-x)/ -2);margin-right:calc(var(--bs-gutter-x)/ -2)}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-left:calc(var(--bs-gutter-x)/ 2);padding-right:calc(var(--bs-gutter-x)/ 2);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.3333333333%}.col-2{flex:0 0 auto;width:16.6666666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.3333333333%}.col-5{flex:0 0 auto;width:41.6666666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.3333333333%}.col-8{flex:0 0 auto;width:66.6666666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.3333333333%}.col-11{flex:0 0 auto;width:91.6666666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-right:8.3333333333%}.offset-2{margin-right:16.6666666667%}.offset-3{margin-right:25%}.offset-4{margin-right:33.3333333333%}.offset-5{margin-right:41.6666666667%}.offset-6{margin-right:50%}.offset-7{margin-right:58.3333333333%}.offset-8{margin-right:66.6666666667%}.offset-9{margin-right:75%}.offset-10{margin-right:83.3333333333%}.offset-11{margin-right:91.6666666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.3333333333%}.col-sm-2{flex:0 0 auto;width:16.6666666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.3333333333%}.col-sm-5{flex:0 0 auto;width:41.6666666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.3333333333%}.col-sm-8{flex:0 0 auto;width:66.6666666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.3333333333%}.col-sm-11{flex:0 0 auto;width:91.6666666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-right:0}.offset-sm-1{margin-right:8.3333333333%}.offset-sm-2{margin-right:16.6666666667%}.offset-sm-3{margin-right:25%}.offset-sm-4{margin-right:33.3333333333%}.offset-sm-5{margin-right:41.6666666667%}.offset-sm-6{margin-right:50%}.offset-sm-7{margin-right:58.3333333333%}.offset-sm-8{margin-right:66.6666666667%}.offset-sm-9{margin-right:75%}.offset-sm-10{margin-right:83.3333333333%}.offset-sm-11{margin-right:91.6666666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.3333333333%}.col-md-2{flex:0 0 auto;width:16.6666666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.3333333333%}.col-md-5{flex:0 0 auto;width:41.6666666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.3333333333%}.col-md-8{flex:0 0 auto;width:66.6666666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.3333333333%}.col-md-11{flex:0 0 auto;width:91.6666666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-right:0}.offset-md-1{margin-right:8.3333333333%}.offset-md-2{margin-right:16.6666666667%}.offset-md-3{margin-right:25%}.offset-md-4{margin-right:33.3333333333%}.offset-md-5{margin-right:41.6666666667%}.offset-md-6{margin-right:50%}.offset-md-7{margin-right:58.3333333333%}.offset-md-8{margin-right:66.6666666667%}.offset-md-9{margin-right:75%}.offset-md-10{margin-right:83.3333333333%}.offset-md-11{margin-right:91.6666666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.3333333333%}.col-lg-2{flex:0 0 auto;width:16.6666666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.3333333333%}.col-lg-5{flex:0 0 auto;width:41.6666666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.3333333333%}.col-lg-8{flex:0 0 auto;width:66.6666666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.3333333333%}.col-lg-11{flex:0 0 auto;width:91.6666666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-right:0}.offset-lg-1{margin-right:8.3333333333%}.offset-lg-2{margin-right:16.6666666667%}.offset-lg-3{margin-right:25%}.offset-lg-4{margin-right:33.3333333333%}.offset-lg-5{margin-right:41.6666666667%}.offset-lg-6{margin-right:50%}.offset-lg-7{margin-right:58.3333333333%}.offset-lg-8{margin-right:66.6666666667%}.offset-lg-9{margin-right:75%}.offset-lg-10{margin-right:83.3333333333%}.offset-lg-11{margin-right:91.6666666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.3333333333%}.col-xl-2{flex:0 0 auto;width:16.6666666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.3333333333%}.col-xl-5{flex:0 0 auto;width:41.6666666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.3333333333%}.col-xl-8{flex:0 0 auto;width:66.6666666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.3333333333%}.col-xl-11{flex:0 0 auto;width:91.6666666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-right:0}.offset-xl-1{margin-right:8.3333333333%}.offset-xl-2{margin-right:16.6666666667%}.offset-xl-3{margin-right:25%}.offset-xl-4{margin-right:33.3333333333%}.offset-xl-5{margin-right:41.6666666667%}.offset-xl-6{margin-right:50%}.offset-xl-7{margin-right:58.3333333333%}.offset-xl-8{margin-right:66.6666666667%}.offset-xl-9{margin-right:75%}.offset-xl-10{margin-right:83.3333333333%}.offset-xl-11{margin-right:91.6666666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.3333333333%}.col-xxl-2{flex:0 0 auto;width:16.6666666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.3333333333%}.col-xxl-5{flex:0 0 auto;width:41.6666666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.3333333333%}.col-xxl-8{flex:0 0 auto;width:66.6666666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.3333333333%}.col-xxl-11{flex:0 0 auto;width:91.6666666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-right:0}.offset-xxl-1{margin-right:8.3333333333%}.offset-xxl-2{margin-right:16.6666666667%}.offset-xxl-3{margin-right:25%}.offset-xxl-4{margin-right:33.3333333333%}.offset-xxl-5{margin-right:41.6666666667%}.offset-xxl-6{margin-right:50%}.offset-xxl-7{margin-right:58.3333333333%}.offset-xxl-8{margin-right:66.6666666667%}.offset-xxl-9{margin-right:75%}.offset-xxl-10{margin-right:83.3333333333%}.offset-xxl-11{margin-right:91.6666666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-bg:transparent;--bs-table-accent-bg:transparent;--bs-table-striped-color:#212529;--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:#212529;--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:#212529;--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:#212529;vertical-align:top;border-color:#dee2e6}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table>:not(:last-child)>:last-child>*{border-bottom-color:currentColor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-striped>tbody>tr:nth-of-type(odd){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-bg:#cfe2ff;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:#000;border-color:#bacbe6}.table-secondary{--bs-table-bg:#e2e3e5;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:#000;border-color:#cbccce}.table-success{--bs-table-bg:#d1e7dd;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:#000;border-color:#bcd0c7}.table-info{--bs-table-bg:#cff4fc;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:#000;border-color:#badce3}.table-warning{--bs-table-bg:#fff3cd;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:#000;border-color:#e6dbb9}.table-danger{--bs-table-bg:#f8d7da;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:#000;border-color:#dfc2c4}.table-light{--bs-table-bg:#f8f9fa;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:#000;border-color:#dfe0e1}.table-dark{--bs-table-bg:#212529;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:#fff;border-color:#373b3e}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-left:0;padding-right:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{max-width:3rem;height:auto;padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{height:1.5em;border-radius:.25rem}.form-control-color::-webkit-color-swatch{height:1.5em;border-radius:.25rem}.form-select{display:block;width:100%;padding:.375rem .75rem .375rem 2.25rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:left .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-left:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-right:.5rem;font-size:.875rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-right:1rem;font-size:1.25rem}.form-check{display:block;min-height:1.5rem;padding-right:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:right;margin-right:-1.5em}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{opacity:.5}.form-switch{padding-right:2.5em}.form-switch .form-check-input{width:2em;margin-right:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:right center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:left center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-check-inline{display:inline-block;margin-left:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-select{height:calc(3.5rem + 2px);padding:1rem .75rem}.form-floating>label{position:absolute;top:0;right:0;height:100%;padding:1rem .75rem;pointer-events:none;border:1px solid transparent;transform-origin:100% 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(-.15rem)}.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(-.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(-.15rem)}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-select:focus{z-index:3}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:3}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-left:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-top-left-radius:0;border-bottom-left-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-right:-1px;border-top-right-radius:0;border-bottom-right-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.25rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-left:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:left calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-left:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) left calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-left:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:left .75rem center,center left 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-right:.5em}.input-group .form-control.is-valid,.input-group .form-select.is-valid,.was-validated .input-group .form-control:valid,.was-validated .input-group .form-select:valid{z-index:1}.input-group .form-control.is-valid:focus,.input-group .form-select.is-valid:focus,.was-validated .input-group .form-control:valid:focus,.was-validated .input-group .form-select:valid:focus{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-left:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:left calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-left:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) left calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-left:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:left .75rem center,center left 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-right:.5em}.input-group .form-control.is-invalid,.input-group .form-select.is-invalid,.was-validated .input-group .form-control:invalid,.was-validated .input-group .form-select:invalid{z-index:2}.input-group .form-control.is-invalid:focus,.input-group .form-select.is-invalid:focus,.was-validated .input-group .form-control:invalid:focus,.was-validated .input-group .form-select:invalid:focus{z-index:3}.btn{display:inline-block;font-weight:400;line-height:1.5;color:#212529;text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529}.btn-check:focus+.btn,.btn:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{pointer-events:none;opacity:.65}.btn-primary{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-primary:hover{color:#fff;background-color:#0b5ed7;border-color:#0a58ca}.btn-check:focus+.btn-primary,.btn-primary:focus{color:#fff;background-color:#0b5ed7;border-color:#0a58ca;box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-check:active+.btn-primary,.btn-check:checked+.btn-primary,.btn-primary.active,.btn-primary:active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0a58ca;border-color:#0a53be}.btn-check:active+.btn-primary:focus,.btn-check:checked+.btn-primary:focus,.btn-primary.active:focus,.btn-primary:active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(49,132,253,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5c636a;border-color:#565e64}.btn-check:focus+.btn-secondary,.btn-secondary:focus{color:#fff;background-color:#5c636a;border-color:#565e64;box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-check:active+.btn-secondary,.btn-check:checked+.btn-secondary,.btn-secondary.active,.btn-secondary:active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#565e64;border-color:#51585e}.btn-check:active+.btn-secondary:focus,.btn-check:checked+.btn-secondary:focus,.btn-secondary.active:focus,.btn-secondary:active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-success{color:#fff;background-color:#198754;border-color:#198754}.btn-success:hover{color:#fff;background-color:#157347;border-color:#146c43}.btn-check:focus+.btn-success,.btn-success:focus{color:#fff;background-color:#157347;border-color:#146c43;box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-check:active+.btn-success,.btn-check:checked+.btn-success,.btn-success.active,.btn-success:active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#146c43;border-color:#13653f}.btn-check:active+.btn-success:focus,.btn-check:checked+.btn-success:focus,.btn-success.active:focus,.btn-success:active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(60,153,110,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#198754;border-color:#198754}.btn-info{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-info:hover{color:#000;background-color:#31d2f2;border-color:#25cff2}.btn-check:focus+.btn-info,.btn-info:focus{color:#000;background-color:#31d2f2;border-color:#25cff2;box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-check:active+.btn-info,.btn-check:checked+.btn-info,.btn-info.active,.btn-info:active,.show>.btn-info.dropdown-toggle{color:#000;background-color:#3dd5f3;border-color:#25cff2}.btn-check:active+.btn-info:focus,.btn-check:checked+.btn-info:focus,.btn-info.active:focus,.btn-info:active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(11,172,204,.5)}.btn-info.disabled,.btn-info:disabled{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-warning{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#000;background-color:#ffca2c;border-color:#ffc720}.btn-check:focus+.btn-warning,.btn-warning:focus{color:#000;background-color:#ffca2c;border-color:#ffc720;box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-check:active+.btn-warning,.btn-check:checked+.btn-warning,.btn-warning.active,.btn-warning:active,.show>.btn-warning.dropdown-toggle{color:#000;background-color:#ffcd39;border-color:#ffc720}.btn-check:active+.btn-warning:focus,.btn-check:checked+.btn-warning:focus,.btn-warning.active:focus,.btn-warning:active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(217,164,6,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#bb2d3b;border-color:#b02a37}.btn-check:focus+.btn-danger,.btn-danger:focus{color:#fff;background-color:#bb2d3b;border-color:#b02a37;box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-check:active+.btn-danger,.btn-check:checked+.btn-danger,.btn-danger.active,.btn-danger:active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#b02a37;border-color:#a52834}.btn-check:active+.btn-danger:focus,.btn-check:checked+.btn-danger:focus,.btn-danger.active:focus,.btn-danger:active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-light{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:focus+.btn-light,.btn-light:focus{color:#000;background-color:#f9fafb;border-color:#f9fafb;box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-check:active+.btn-light,.btn-check:checked+.btn-light,.btn-light.active,.btn-light:active,.show>.btn-light.dropdown-toggle{color:#000;background-color:#f9fafb;border-color:#f9fafb}.btn-check:active+.btn-light:focus,.btn-check:checked+.btn-light:focus,.btn-light.active:focus,.btn-light:active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(211,212,213,.5)}.btn-light.disabled,.btn-light:disabled{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-dark{color:#fff;background-color:#212529;border-color:#212529}.btn-dark:hover{color:#fff;background-color:#1c1f23;border-color:#1a1e21}.btn-check:focus+.btn-dark,.btn-dark:focus{color:#fff;background-color:#1c1f23;border-color:#1a1e21;box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-check:active+.btn-dark,.btn-check:checked+.btn-dark,.btn-dark.active,.btn-dark:active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1a1e21;border-color:#191c1f}.btn-check:active+.btn-dark:focus,.btn-check:checked+.btn-dark:focus,.btn-dark.active:focus,.btn-dark:active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(66,70,73,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#212529;border-color:#212529}.btn-outline-primary{color:#0d6efd;border-color:#0d6efd}.btn-outline-primary:hover{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:focus+.btn-outline-primary,.btn-outline-primary:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-check:active+.btn-outline-primary,.btn-check:checked+.btn-outline-primary,.btn-outline-primary.active,.btn-outline-primary.dropdown-toggle.show,.btn-outline-primary:active{color:#fff;background-color:#0d6efd;border-color:#0d6efd}.btn-check:active+.btn-outline-primary:focus,.btn-check:checked+.btn-outline-primary:focus,.btn-outline-primary.active:focus,.btn-outline-primary.dropdown-toggle.show:focus,.btn-outline-primary:active:focus{box-shadow:0 0 0 .25rem rgba(13,110,253,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#0d6efd;background-color:transparent}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:focus+.btn-outline-secondary,.btn-outline-secondary:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-check:active+.btn-outline-secondary,.btn-check:checked+.btn-outline-secondary,.btn-outline-secondary.active,.btn-outline-secondary.dropdown-toggle.show,.btn-outline-secondary:active{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-check:active+.btn-outline-secondary:focus,.btn-check:checked+.btn-outline-secondary:focus,.btn-outline-secondary.active:focus,.btn-outline-secondary.dropdown-toggle.show:focus,.btn-outline-secondary:active:focus{box-shadow:0 0 0 .25rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-success{color:#198754;border-color:#198754}.btn-outline-success:hover{color:#fff;background-color:#198754;border-color:#198754}.btn-check:focus+.btn-outline-success,.btn-outline-success:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-check:active+.btn-outline-success,.btn-check:checked+.btn-outline-success,.btn-outline-success.active,.btn-outline-success.dropdown-toggle.show,.btn-outline-success:active{color:#fff;background-color:#198754;border-color:#198754}.btn-check:active+.btn-outline-success:focus,.btn-check:checked+.btn-outline-success:focus,.btn-outline-success.active:focus,.btn-outline-success.dropdown-toggle.show:focus,.btn-outline-success:active:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#198754;background-color:transparent}.btn-outline-info{color:#0dcaf0;border-color:#0dcaf0}.btn-outline-info:hover{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:focus+.btn-outline-info,.btn-outline-info:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-check:active+.btn-outline-info,.btn-check:checked+.btn-outline-info,.btn-outline-info.active,.btn-outline-info.dropdown-toggle.show,.btn-outline-info:active{color:#000;background-color:#0dcaf0;border-color:#0dcaf0}.btn-check:active+.btn-outline-info:focus,.btn-check:checked+.btn-outline-info:focus,.btn-outline-info.active:focus,.btn-outline-info.dropdown-toggle.show:focus,.btn-outline-info:active:focus{box-shadow:0 0 0 .25rem rgba(13,202,240,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#0dcaf0;background-color:transparent}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:focus+.btn-outline-warning,.btn-outline-warning:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-check:active+.btn-outline-warning,.btn-check:checked+.btn-outline-warning,.btn-outline-warning.active,.btn-outline-warning.dropdown-toggle.show,.btn-outline-warning:active{color:#000;background-color:#ffc107;border-color:#ffc107}.btn-check:active+.btn-outline-warning:focus,.btn-check:checked+.btn-outline-warning:focus,.btn-outline-warning.active:focus,.btn-outline-warning.dropdown-toggle.show:focus,.btn-outline-warning:active:focus{box-shadow:0 0 0 .25rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:focus+.btn-outline-danger,.btn-outline-danger:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-check:active+.btn-outline-danger,.btn-check:checked+.btn-outline-danger,.btn-outline-danger.active,.btn-outline-danger.dropdown-toggle.show,.btn-outline-danger:active{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-check:active+.btn-outline-danger:focus,.btn-check:checked+.btn-outline-danger:focus,.btn-outline-danger.active:focus,.btn-outline-danger.dropdown-toggle.show:focus,.btn-outline-danger:active:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:focus+.btn-outline-light,.btn-outline-light:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-check:active+.btn-outline-light,.btn-check:checked+.btn-outline-light,.btn-outline-light.active,.btn-outline-light.dropdown-toggle.show,.btn-outline-light:active{color:#000;background-color:#f8f9fa;border-color:#f8f9fa}.btn-check:active+.btn-outline-light:focus,.btn-check:checked+.btn-outline-light:focus,.btn-outline-light.active:focus,.btn-outline-light.dropdown-toggle.show:focus,.btn-outline-light:active:focus{box-shadow:0 0 0 .25rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-dark{color:#212529;border-color:#212529}.btn-outline-dark:hover{color:#fff;background-color:#212529;border-color:#212529}.btn-check:focus+.btn-outline-dark,.btn-outline-dark:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-check:active+.btn-outline-dark,.btn-check:checked+.btn-outline-dark,.btn-outline-dark.active,.btn-outline-dark.dropdown-toggle.show,.btn-outline-dark:active{color:#fff;background-color:#212529;border-color:#212529}.btn-check:active+.btn-outline-dark:focus,.btn-check:checked+.btn-outline-dark:focus,.btn-outline-dark.active:focus,.btn-outline-dark.dropdown-toggle.show:focus,.btn-outline-dark:active:focus{box-shadow:0 0 0 .25rem rgba(33,37,41,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#212529;background-color:transparent}.btn-link{font-weight:400;color:#0d6efd;text-decoration:underline}.btn-link:hover{color:#0a58ca}.btn-link.disabled,.btn-link:disabled{color:#6c757d}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;border-radius:.2rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropend,.dropstart,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-left:.3em solid transparent;border-bottom:0;border-right:.3em solid transparent}.dropdown-toggle:empty::after{margin-right:0}.dropdown-menu{position:absolute;z-index:1000;display:none;min-width:10rem;padding:.5rem 0;margin:0;font-size:1rem;color:#212529;text-align:right;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu[data-bs-popper]{top:100%;right:0;margin-top:.125rem}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:0;border-left:.3em solid transparent;border-bottom:.3em solid;border-right:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-right:0}.dropend .dropdown-menu[data-bs-popper]{top:0;left:auto;right:100%;margin-top:0;margin-right:.125rem}.dropend .dropdown-toggle::after{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-left:0;border-bottom:.3em solid transparent;border-right:.3em solid}.dropend .dropdown-toggle:empty::after{margin-right:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;left:100%;right:auto;margin-top:0;margin-left:.125rem}.dropstart .dropdown-toggle::after{display:inline-block;margin-right:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-left:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-right:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid rgba(0,0,0,.15)}.dropdown-item{display:block;width:100%;padding:.25rem 1rem;clear:both;font-weight:400;color:#212529;text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#1e2125;background-color:#e9ecef}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#0d6efd}.dropdown-item.disabled,.dropdown-item:disabled{color:#adb5bd;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1rem;color:#212529}.dropdown-menu-dark{color:#dee2e6;background-color:#343a40;border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item{color:#dee2e6}.dropdown-menu-dark .dropdown-item:focus,.dropdown-menu-dark .dropdown-item:hover{color:#fff;background-color:rgba(255,255,255,.15)}.dropdown-menu-dark .dropdown-item.active,.dropdown-menu-dark .dropdown-item:active{color:#fff;background-color:#0d6efd}.dropdown-menu-dark .dropdown-item.disabled,.dropdown-menu-dark .dropdown-item:disabled{color:#adb5bd}.dropdown-menu-dark .dropdown-divider{border-color:rgba(0,0,0,.15)}.dropdown-menu-dark .dropdown-item-text{color:#dee2e6}.dropdown-menu-dark .dropdown-header{color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-right:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-right-radius:0;border-bottom-right-radius:0}.dropdown-toggle-split{padding-left:.5625rem;padding-right:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-right:0}.dropstart .dropdown-toggle-split::before{margin-left:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-left:.375rem;padding-right:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-left:.75rem;padding-right:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-left-radius:0;border-bottom-right-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-right-radius:0;border-top-left-radius:0}.nav{display:flex;flex-wrap:wrap;padding-right:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem;color:#0d6efd;text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:#0a58ca}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-link{margin-bottom:-1px;background:0 0;border:1px solid transparent;border-top-right-radius:.25rem;border-top-left-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6;isolation:isolate}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.nav-pills .nav-link{background:0 0;border:0;border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#0d6efd}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding-top:.5rem;padding-bottom:.5rem}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:.3125rem;padding-bottom:.3125rem;margin-left:1rem;font-size:1.25rem;text-decoration:none;white-space:nowrap}.navbar-nav{display:flex;flex-direction:column;padding-right:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-left:0;padding-right:0}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem;transition:box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 .25rem}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-left:.5rem;padding-right:.5rem}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.55)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.55);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.55)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.55)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.55);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.55)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:flex;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-left:0;margin-right:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-right-radius:calc(.25rem - 1px);border-top-left-radius:calc(.25rem - 1px)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:1rem 1rem}.card-title{margin-bottom:.5rem}.card-subtitle{margin-top:-.25rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-right:1rem}.card-header{padding:.5rem 1rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-footer{padding:.5rem 1rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-left:-.5rem;margin-bottom:-.5rem;margin-right:-.5rem;border-bottom:0}.card-header-pills{margin-left:-.5rem;margin-right:-.5rem}.card-img-overlay{position:absolute;top:0;left:0;bottom:0;right:0;padding:1rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-right-radius:calc(.25rem - 1px);border-top-left-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.card-group>.card{margin-bottom:.75rem}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-right:0;border-right:0}.card-group>.card:not(:last-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:not(:first-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-right-radius:0}}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:1rem 1.25rem;font-size:1rem;color:#212529;text-align:right;background-color:#fff;border:0;border-radius:0;overflow-anchor:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,border-radius .15s ease}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:#0c63e4;background-color:#e7f1ff;box-shadow:inset 0 -1px 0 rgba(0,0,0,.125)}.accordion-button:not(.collapsed)::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");transform:rotate(180deg)}.accordion-button::after{flex-shrink:0;width:1.25rem;height:1.25rem;margin-right:auto;content:"";background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-size:1.25rem;transition:transform .2s ease-in-out}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.accordion-header{margin-bottom:0}.accordion-item{background-color:#fff;border:1px solid rgba(0,0,0,.125)}.accordion-item:first-of-type{border-top-right-radius:.25rem;border-top-left-radius:.25rem}.accordion-item:first-of-type .accordion-button{border-top-right-radius:calc(.25rem - 1px);border-top-left-radius:calc(.25rem - 1px)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-left-radius:calc(.25rem - 1px);border-bottom-right-radius:calc(.25rem - 1px)}.accordion-item:last-of-type .accordion-collapse{border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem}.accordion-body{padding:1rem 1.25rem}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-left:0;border-right:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button{border-radius:0}.breadcrumb{display:flex;flex-wrap:wrap;padding:0 0;margin-bottom:1rem;list-style:none}.breadcrumb-item+.breadcrumb-item{padding-right:.5rem}.breadcrumb-item+.breadcrumb-item::before{float:right;padding-left:.5rem;color:#6c757d;content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:#6c757d}.pagination{display:flex;padding-right:0;list-style:none}.page-link{position:relative;display:block;color:#0d6efd;text-decoration:none;background-color:#fff;border:1px solid #dee2e6;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:#0a58ca;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:3;color:#0a58ca;background-color:#e9ecef;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.page-item:not(:first-child) .page-link{margin-right:-1px}.page-item.active .page-link{z-index:3;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;background-color:#fff;border-color:#dee2e6}.page-link{padding:.375rem .75rem}.page-item:first-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item:last-child .page-link{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem}.pagination-lg .page-item:first-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem}.pagination-sm .page-item:first-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.badge{display:inline-block;padding:.35em .65em;font-size:.75em;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{position:relative;padding:1rem 1rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-left:3rem}.alert-dismissible .btn-close{position:absolute;top:0;left:0;z-index:2;padding:1.25rem 1rem}.alert-primary{color:#084298;background-color:#cfe2ff;border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{color:#41464b;background-color:#e2e3e5;border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{color:#0f5132;background-color:#d1e7dd;border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{color:#055160;background-color:#cff4fc;border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{color:#664d03;background-color:#fff3cd;border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{color:#842029;background-color:#f8d7da;border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{color:#636464;background-color:#fefefe;border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{color:#141619;background-color:#d3d3d4;border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@-webkit-keyframes progress-bar-stripes{0%{background-position-x:1rem}}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:#fff;text-align:center;white-space:nowrap;background-color:#0d6efd;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(-45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:1s linear infinite progress-bar-stripes;animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.list-group{display:flex;flex-direction:column;padding-right:0;margin-bottom:0;border-radius:.25rem}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>li::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.5rem 1rem;color:#212529;text-decoration:none;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-right-radius:inherit;border-top-left-radius:inherit}.list-group-item:last-child{border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#0d6efd;border-color:#0d6efd}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:-1px;border-top-width:1px}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child{border-bottom-right-radius:.25rem;border-top-left-radius:0}.list-group-horizontal>.list-group-item:last-child{border-top-left-radius:.25rem;border-bottom-right-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:1px;border-right-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-right:-1px;border-right-width:1px}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child{border-bottom-right-radius:.25rem;border-top-left-radius:0}.list-group-horizontal-sm>.list-group-item:last-child{border-top-left-radius:.25rem;border-bottom-right-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:1px;border-right-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-right:-1px;border-right-width:1px}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child{border-bottom-right-radius:.25rem;border-top-left-radius:0}.list-group-horizontal-md>.list-group-item:last-child{border-top-left-radius:.25rem;border-bottom-right-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:1px;border-right-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-right:-1px;border-right-width:1px}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child{border-bottom-right-radius:.25rem;border-top-left-radius:0}.list-group-horizontal-lg>.list-group-item:last-child{border-top-left-radius:.25rem;border-bottom-right-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:1px;border-right-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-right:-1px;border-right-width:1px}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child{border-bottom-right-radius:.25rem;border-top-left-radius:0}.list-group-horizontal-xl>.list-group-item:last-child{border-top-left-radius:.25rem;border-bottom-right-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:1px;border-right-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-right:-1px;border-right-width:1px}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child{border-bottom-right-radius:.25rem;border-top-left-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child{border-top-left-radius:.25rem;border-bottom-right-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:1px;border-right-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-right:-1px;border-right-width:1px}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 1px}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.25rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{width:350px;max-width:100%;font-size:.875rem;pointer-events:auto;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .5rem 1rem rgba(0,0,0,.15);border-radius:.25rem}.toast:not(.showing):not(.show){opacity:0}.toast.hide{display:none}.toast-container{width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:.75rem}.toast-header{display:flex;align-items:center;padding:.5rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-right-radius:calc(.25rem - 1px);border-top-left-radius:calc(.25rem - 1px)}.toast-header .btn-close{margin-left:-.375rem;margin-right:.75rem}.toast-body{padding:.75rem;word-wrap:break-word}.modal{position:fixed;top:0;right:0;z-index:1060;display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - 1rem)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-right-radius:calc(.3rem - 1px);border-top-left-radius:calc(.3rem - 1px)}.modal-header .btn-close{padding:.5rem .5rem;margin:-.5rem auto -.5rem -.5rem}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;flex:1 1 auto;padding:1rem}.modal-footer{display:flex;flex-wrap:wrap;flex-shrink:0;align-items:center;justify-content:flex-end;padding:.75rem;border-top:1px solid #dee2e6;border-bottom-left-radius:calc(.3rem - 1px);border-bottom-right-radius:calc(.3rem - 1px)}.modal-footer>*{margin:.25rem}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{height:calc(100% - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}.modal-fullscreen .modal-footer{border-radius:0}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}.modal-fullscreen-sm-down .modal-footer{border-radius:0}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}.modal-fullscreen-md-down .modal-footer{border-radius:0}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}.modal-fullscreen-lg-down .modal-footer{border-radius:0}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}.modal-fullscreen-xl-down .modal-footer{border-radius:0}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}.modal-fullscreen-xxl-down .modal-footer{border-radius:0}}.tooltip{position:absolute;z-index:1080;display:block;margin:0;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:right;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .tooltip-arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[data-popper-placement^=right],.bs-tooltip-end{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{left:-1px;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.bs-tooltip-auto[data-popper-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[data-popper-placement^=left],.bs-tooltip-start{padding:0 .4rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{right:-1px;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1070;display:block;max-width:276px;font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:right;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .popover-arrow{position:absolute;display:block;width:1rem;height:.5rem}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{right:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-.5rem - 1px)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;right:50%;display:block;width:1rem;margin-right:-.5rem;content:"";border-bottom:1px solid #f0f0f0}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{left:calc(-.5rem - 1px);width:.5rem;height:1rem}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.popover-header{padding:.5rem 1rem;margin-bottom:0;font-size:1rem;background-color:#f0f0f0;border-bottom:1px solid #d8d8d8;border-top-right-radius:calc(.3rem - 1px);border-top-left-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:1rem 1rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:right;width:100%;margin-left:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{right:0}.carousel-control-next{left:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;left:0;bottom:0;right:0;z-index:2;display:flex;justify-content:center;padding:0;margin-left:15%;margin-bottom:1rem;margin-right:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-left:3px;margin-right:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;left:15%;bottom:1.25rem;right:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}@-webkit-keyframes spinner-border{to{transform:rotate(360deg)}}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;border:.25em solid currentColor;border-left-color:transparent;border-radius:50%;-webkit-animation:.75s linear infinite spinner-border;animation:.75s linear infinite spinner-border}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:-.125em;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:.75s linear infinite spinner-grow;animation:.75s linear infinite spinner-grow}.spinner-grow-sm{width:1rem;height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{-webkit-animation-duration:1.5s;animation-duration:1.5s}}.offcanvas{position:fixed;bottom:0;z-index:1050;display:flex;flex-direction:column;max-width:100%;visibility:hidden;background-color:#fff;background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:1rem 1rem}.offcanvas-header .btn-close{padding:.5rem .5rem;margin:-.5rem auto -.5rem -.5rem}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:1rem 1rem;overflow-y:auto}.offcanvas-start{top:0;right:0;width:400px;border-left:1px solid rgba(0,0,0,.2);transform:translateX(100%)}.offcanvas-end{top:0;left:0;width:400px;border-right:1px solid rgba(0,0,0,.2);transform:translateX(-100%)}.offcanvas-top{top:0;left:0;right:0;height:30vh;max-height:100%;border-bottom:1px solid rgba(0,0,0,.2);transform:translateY(-100%)}.offcanvas-bottom{left:0;right:0;height:30vh;max-height:100%;border-top:1px solid rgba(0,0,0,.2);transform:translateY(100%)}.offcanvas.show{transform:none}.clearfix::after{display:block;clear:both;content:""}.link-primary{color:#0d6efd}.link-primary:focus,.link-primary:hover{color:#0a58ca}.link-secondary{color:#6c757d}.link-secondary:focus,.link-secondary:hover{color:#565e64}.link-success{color:#198754}.link-success:focus,.link-success:hover{color:#146c43}.link-info{color:#0dcaf0}.link-info:focus,.link-info:hover{color:#3dd5f3}.link-warning{color:#ffc107}.link-warning:focus,.link-warning:hover{color:#ffcd39}.link-danger{color:#dc3545}.link-danger:focus,.link-danger:hover{color:#b02a37}.link-light{color:#f8f9fa}.link-light:focus,.link-light:hover{color:#f9fafb}.link-dark{color:#212529}.link-dark:focus,.link-dark:hover{color:#1a1e21}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;right:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:calc(3 / 4 * 100%)}.ratio-16x9{--bs-aspect-ratio:calc(9 / 16 * 100%)}.ratio-21x9{--bs-aspect-ratio:calc(9 / 21 * 100%)}.fixed-top{position:fixed;top:0;left:0;right:0;z-index:1030}.fixed-bottom{position:fixed;left:0;bottom:0;right:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;left:0;bottom:0;right:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:right!important}.float-end{float:left!important}.float-none{float:none!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{right:0!important}.start-50{right:50%!important}.start-100{right:100%!important}.end-0{left:0!important}.end-50{left:50%!important}.end-100{left:100%!important}.translate-middle{transform:translate(50%,-50%)!important}.translate-middle-x{transform:translateX(50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:1px solid #dee2e6!important}.border-0{border:0!important}.border-top{border-top:1px solid #dee2e6!important}.border-top-0{border-top:0!important}.border-end{border-left:1px solid #dee2e6!important}.border-end-0{border-left:0!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-bottom-0{border-bottom:0!important}.border-start{border-right:1px solid #dee2e6!important}.border-start-0{border-right:0!important}.border-primary{border-color:#0d6efd!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#198754!important}.border-info{border-color:#0dcaf0!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#212529!important}.border-white{border-color:#fff!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-left:0!important;margin-right:0!important}.mx-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-3{margin-left:1rem!important;margin-right:1rem!important}.mx-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-5{margin-left:3rem!important;margin-right:3rem!important}.mx-auto{margin-left:auto!important;margin-right:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-left:0!important}.me-1{margin-left:.25rem!important}.me-2{margin-left:.5rem!important}.me-3{margin-left:1rem!important}.me-4{margin-left:1.5rem!important}.me-5{margin-left:3rem!important}.me-auto{margin-left:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-right:0!important}.ms-1{margin-right:.25rem!important}.ms-2{margin-right:.5rem!important}.ms-3{margin-right:1rem!important}.ms-4{margin-right:1.5rem!important}.ms-5{margin-right:3rem!important}.ms-auto{margin-right:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-left:0!important;padding-right:0!important}.px-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-3{padding-left:1rem!important;padding-right:1rem!important}.px-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-5{padding-left:3rem!important;padding-right:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-left:0!important}.pe-1{padding-left:.25rem!important}.pe-2{padding-left:.5rem!important}.pe-3{padding-left:1rem!important}.pe-4{padding-left:1.5rem!important}.pe-5{padding-left:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-right:0!important}.ps-1{padding-right:.25rem!important}.ps-2{padding-right:.5rem!important}.ps-3{padding-right:1rem!important}.ps-4{padding-right:1.5rem!important}.ps-5{padding-right:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:right!important}.text-end{text-align:left!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-primary{color:#0d6efd!important}.text-secondary{color:#6c757d!important}.text-success{color:#198754!important}.text-info{color:#0dcaf0!important}.text-warning{color:#ffc107!important}.text-danger{color:#dc3545!important}.text-light{color:#f8f9fa!important}.text-dark{color:#212529!important}.text-white{color:#fff!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-reset{color:inherit!important}.bg-primary{background-color:#0d6efd!important}.bg-secondary{background-color:#6c757d!important}.bg-success{background-color:#198754!important}.bg-info{background-color:#0dcaf0!important}.bg-warning{background-color:#ffc107!important}.bg-danger{background-color:#dc3545!important}.bg-light{background-color:#f8f9fa!important}.bg-dark{background-color:#212529!important}.bg-body{background-color:#fff!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:.25rem!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:.2rem!important}.rounded-2{border-radius:.25rem!important}.rounded-3{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-top{border-top-right-radius:.25rem!important;border-top-left-radius:.25rem!important}.rounded-end{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-bottom{border-bottom-left-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-start{border-bottom-right-radius:.25rem!important;border-top-right-radius:.25rem!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:right!important}.float-sm-end{float:left!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-left:0!important;margin-right:0!important}.mx-sm-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-sm-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-sm-3{margin-left:1rem!important;margin-right:1rem!important}.mx-sm-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-sm-5{margin-left:3rem!important;margin-right:3rem!important}.mx-sm-auto{margin-left:auto!important;margin-right:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-left:0!important}.me-sm-1{margin-left:.25rem!important}.me-sm-2{margin-left:.5rem!important}.me-sm-3{margin-left:1rem!important}.me-sm-4{margin-left:1.5rem!important}.me-sm-5{margin-left:3rem!important}.me-sm-auto{margin-left:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-right:0!important}.ms-sm-1{margin-right:.25rem!important}.ms-sm-2{margin-right:.5rem!important}.ms-sm-3{margin-right:1rem!important}.ms-sm-4{margin-right:1.5rem!important}.ms-sm-5{margin-right:3rem!important}.ms-sm-auto{margin-right:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-left:0!important;padding-right:0!important}.px-sm-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-sm-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-sm-3{padding-left:1rem!important;padding-right:1rem!important}.px-sm-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-sm-5{padding-left:3rem!important;padding-right:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-left:0!important}.pe-sm-1{padding-left:.25rem!important}.pe-sm-2{padding-left:.5rem!important}.pe-sm-3{padding-left:1rem!important}.pe-sm-4{padding-left:1.5rem!important}.pe-sm-5{padding-left:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-right:0!important}.ps-sm-1{padding-right:.25rem!important}.ps-sm-2{padding-right:.5rem!important}.ps-sm-3{padding-right:1rem!important}.ps-sm-4{padding-right:1.5rem!important}.ps-sm-5{padding-right:3rem!important}.text-sm-start{text-align:right!important}.text-sm-end{text-align:left!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:right!important}.float-md-end{float:left!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-left:0!important;margin-right:0!important}.mx-md-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-md-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-md-3{margin-left:1rem!important;margin-right:1rem!important}.mx-md-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-md-5{margin-left:3rem!important;margin-right:3rem!important}.mx-md-auto{margin-left:auto!important;margin-right:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-left:0!important}.me-md-1{margin-left:.25rem!important}.me-md-2{margin-left:.5rem!important}.me-md-3{margin-left:1rem!important}.me-md-4{margin-left:1.5rem!important}.me-md-5{margin-left:3rem!important}.me-md-auto{margin-left:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-right:0!important}.ms-md-1{margin-right:.25rem!important}.ms-md-2{margin-right:.5rem!important}.ms-md-3{margin-right:1rem!important}.ms-md-4{margin-right:1.5rem!important}.ms-md-5{margin-right:3rem!important}.ms-md-auto{margin-right:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-left:0!important;padding-right:0!important}.px-md-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-md-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-md-3{padding-left:1rem!important;padding-right:1rem!important}.px-md-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-md-5{padding-left:3rem!important;padding-right:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-left:0!important}.pe-md-1{padding-left:.25rem!important}.pe-md-2{padding-left:.5rem!important}.pe-md-3{padding-left:1rem!important}.pe-md-4{padding-left:1.5rem!important}.pe-md-5{padding-left:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-right:0!important}.ps-md-1{padding-right:.25rem!important}.ps-md-2{padding-right:.5rem!important}.ps-md-3{padding-right:1rem!important}.ps-md-4{padding-right:1.5rem!important}.ps-md-5{padding-right:3rem!important}.text-md-start{text-align:right!important}.text-md-end{text-align:left!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:right!important}.float-lg-end{float:left!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-left:0!important;margin-right:0!important}.mx-lg-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-lg-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-lg-3{margin-left:1rem!important;margin-right:1rem!important}.mx-lg-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-lg-5{margin-left:3rem!important;margin-right:3rem!important}.mx-lg-auto{margin-left:auto!important;margin-right:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-left:0!important}.me-lg-1{margin-left:.25rem!important}.me-lg-2{margin-left:.5rem!important}.me-lg-3{margin-left:1rem!important}.me-lg-4{margin-left:1.5rem!important}.me-lg-5{margin-left:3rem!important}.me-lg-auto{margin-left:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-right:0!important}.ms-lg-1{margin-right:.25rem!important}.ms-lg-2{margin-right:.5rem!important}.ms-lg-3{margin-right:1rem!important}.ms-lg-4{margin-right:1.5rem!important}.ms-lg-5{margin-right:3rem!important}.ms-lg-auto{margin-right:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-left:0!important;padding-right:0!important}.px-lg-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-lg-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-lg-3{padding-left:1rem!important;padding-right:1rem!important}.px-lg-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-lg-5{padding-left:3rem!important;padding-right:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-left:0!important}.pe-lg-1{padding-left:.25rem!important}.pe-lg-2{padding-left:.5rem!important}.pe-lg-3{padding-left:1rem!important}.pe-lg-4{padding-left:1.5rem!important}.pe-lg-5{padding-left:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-right:0!important}.ps-lg-1{padding-right:.25rem!important}.ps-lg-2{padding-right:.5rem!important}.ps-lg-3{padding-right:1rem!important}.ps-lg-4{padding-right:1.5rem!important}.ps-lg-5{padding-right:3rem!important}.text-lg-start{text-align:right!important}.text-lg-end{text-align:left!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:right!important}.float-xl-end{float:left!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-left:0!important;margin-right:0!important}.mx-xl-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-xl-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-xl-3{margin-left:1rem!important;margin-right:1rem!important}.mx-xl-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-xl-5{margin-left:3rem!important;margin-right:3rem!important}.mx-xl-auto{margin-left:auto!important;margin-right:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-left:0!important}.me-xl-1{margin-left:.25rem!important}.me-xl-2{margin-left:.5rem!important}.me-xl-3{margin-left:1rem!important}.me-xl-4{margin-left:1.5rem!important}.me-xl-5{margin-left:3rem!important}.me-xl-auto{margin-left:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-right:0!important}.ms-xl-1{margin-right:.25rem!important}.ms-xl-2{margin-right:.5rem!important}.ms-xl-3{margin-right:1rem!important}.ms-xl-4{margin-right:1.5rem!important}.ms-xl-5{margin-right:3rem!important}.ms-xl-auto{margin-right:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-left:0!important;padding-right:0!important}.px-xl-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-xl-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-xl-3{padding-left:1rem!important;padding-right:1rem!important}.px-xl-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-xl-5{padding-left:3rem!important;padding-right:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-left:0!important}.pe-xl-1{padding-left:.25rem!important}.pe-xl-2{padding-left:.5rem!important}.pe-xl-3{padding-left:1rem!important}.pe-xl-4{padding-left:1.5rem!important}.pe-xl-5{padding-left:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-right:0!important}.ps-xl-1{padding-right:.25rem!important}.ps-xl-2{padding-right:.5rem!important}.ps-xl-3{padding-right:1rem!important}.ps-xl-4{padding-right:1.5rem!important}.ps-xl-5{padding-right:3rem!important}.text-xl-start{text-align:right!important}.text-xl-end{text-align:left!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:right!important}.float-xxl-end{float:left!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-left:0!important;margin-right:0!important}.mx-xxl-1{margin-left:.25rem!important;margin-right:.25rem!important}.mx-xxl-2{margin-left:.5rem!important;margin-right:.5rem!important}.mx-xxl-3{margin-left:1rem!important;margin-right:1rem!important}.mx-xxl-4{margin-left:1.5rem!important;margin-right:1.5rem!important}.mx-xxl-5{margin-left:3rem!important;margin-right:3rem!important}.mx-xxl-auto{margin-left:auto!important;margin-right:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-left:0!important}.me-xxl-1{margin-left:.25rem!important}.me-xxl-2{margin-left:.5rem!important}.me-xxl-3{margin-left:1rem!important}.me-xxl-4{margin-left:1.5rem!important}.me-xxl-5{margin-left:3rem!important}.me-xxl-auto{margin-left:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-right:0!important}.ms-xxl-1{margin-right:.25rem!important}.ms-xxl-2{margin-right:.5rem!important}.ms-xxl-3{margin-right:1rem!important}.ms-xxl-4{margin-right:1.5rem!important}.ms-xxl-5{margin-right:3rem!important}.ms-xxl-auto{margin-right:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-left:0!important;padding-right:0!important}.px-xxl-1{padding-left:.25rem!important;padding-right:.25rem!important}.px-xxl-2{padding-left:.5rem!important;padding-right:.5rem!important}.px-xxl-3{padding-left:1rem!important;padding-right:1rem!important}.px-xxl-4{padding-left:1.5rem!important;padding-right:1.5rem!important}.px-xxl-5{padding-left:3rem!important;padding-right:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-left:0!important}.pe-xxl-1{padding-left:.25rem!important}.pe-xxl-2{padding-left:.5rem!important}.pe-xxl-3{padding-left:1rem!important}.pe-xxl-4{padding-left:1.5rem!important}.pe-xxl-5{padding-left:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-right:0!important}.ps-xxl-1{padding-right:.25rem!important}.ps-xxl-2{padding-right:.5rem!important}.ps-xxl-3{padding-right:1rem!important}.ps-xxl-4{padding-right:1.5rem!important}.ps-xxl-5{padding-right:3rem!important}.text-xxl-start{text-align:right!important}.text-xxl-end{text-align:left!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}
7
- /*# sourceMappingURL=bootstrap.rtl.min.css.map */
 
 
 
 
 
 
 
admin/css/custom-style.css CHANGED
@@ -887,26 +887,6 @@ section {
887
  height: 40px !important;
888
  }
889
 
890
- .select2.select2-container--default .select2-selection__arrow b {
891
- border: solid #888888 !important;
892
- border-style: solid;
893
- border-width: 0 1px 1px 0 !important;
894
- vertical-align: middle;
895
- -webkit-transform: rotate(45deg);
896
- transform: rotate(45deg);
897
- padding: 3px;
898
- margin-left: -8px !important;
899
- margin-top: -5.5px !important;
900
- }
901
-
902
- .select2.select2-container--default.select2-container--open .select2-selection__arrow b {
903
- border: solid #888888;
904
- border-style: solid;
905
- border-width: 0 1px 1px 0 !important;
906
- -webkit-transform: rotate(-135deg);
907
- transform: rotate(-135deg);
908
- margin-top: 0 !important;
909
- }
910
 
911
  .select2-dropdown {
912
  border-color: #707070 !important;
@@ -1477,6 +1457,7 @@ textarea.form-control, .select2.select2-container--default textarea.select2-sele
1477
  .wizard-section.campaign-wizard .wizard-content .wizard .content .card-wrapper .row .col-6 .form-group {
1478
  padding-left: 15px;
1479
  padding-right: 15px;
 
1480
  }
1481
 
1482
  .wizard-section.campaign-wizard .wizard-content .wizard .content .card-wrapper .row .col-6 .form-group .form-label-control {
@@ -1976,8 +1957,7 @@ textarea.form-control, .select2.select2-container--default textarea.select2-sele
1976
  .back-btn {
1977
  padding: 0;
1978
  color: #25283D;
1979
- position: absolute;
1980
- top: -25px;
1981
  left: 0;
1982
  font-size: 12px;
1983
  }
887
  height: 40px !important;
888
  }
889
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
890
 
891
  .select2-dropdown {
892
  border-color: #707070 !important;
1457
  .wizard-section.campaign-wizard .wizard-content .wizard .content .card-wrapper .row .col-6 .form-group {
1458
  padding-left: 15px;
1459
  padding-right: 15px;
1460
+ margin-bottom: 10px;
1461
  }
1462
 
1463
  .wizard-section.campaign-wizard .wizard-content .wizard .content .card-wrapper .row .col-6 .form-group .form-label-control {
1957
  .back-btn {
1958
  padding: 0;
1959
  color: #25283D;
1960
+ position: relative;
 
1961
  left: 0;
1962
  font-size: 12px;
1963
  }
admin/css/dataTables.bootstrap4.min.css DELETED
@@ -1 +0,0 @@
1
- table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:.85em}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}table.dataTable>thead>tr>th:active,table.dataTable>thead>tr>td:active{outline:none}table.dataTable>thead>tr>th:not(.sorting_disabled),table.dataTable>thead>tr>td:not(.sorting_disabled){padding-right:30px}table.dataTable>thead .sorting,table.dataTable>thead .sorting_asc,table.dataTable>thead .sorting_desc,table.dataTable>thead .sorting_asc_disabled,table.dataTable>thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable>thead .sorting:before,table.dataTable>thead .sorting:after,table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_asc:after,table.dataTable>thead .sorting_desc:before,table.dataTable>thead .sorting_desc:after,table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_asc_disabled:after,table.dataTable>thead .sorting_desc_disabled:before,table.dataTable>thead .sorting_desc_disabled:after{position:absolute;bottom:.9em;display:block;opacity:.3}table.dataTable>thead .sorting:before,table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_desc:before,table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_desc_disabled:before{right:1em;content:"↑"}table.dataTable>thead .sorting:after,table.dataTable>thead .sorting_asc:after,table.dataTable>thead .sorting_desc:after,table.dataTable>thead .sorting_asc_disabled:after,table.dataTable>thead .sorting_desc_disabled:after{right:.5em;content:"↓"}table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_desc:after{opacity:1}table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_desc_disabled:after{opacity:0}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody table thead .sorting:before,div.dataTables_scrollBody table thead .sorting_asc:before,div.dataTables_scrollBody table thead .sorting_desc:before,div.dataTables_scrollBody table thead .sorting:after,div.dataTables_scrollBody table thead .sorting_asc:after,div.dataTables_scrollBody table thead .sorting_desc:after{display:none}div.dataTables_scrollBody table tbody tr:first-child th,div.dataTables_scrollBody table tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:center !important}}table.dataTable.table-sm>thead>tr>th:not(.sorting_disabled){padding-right:20px}table.dataTable.table-sm .sorting:before,table.dataTable.table-sm .sorting_asc:before,table.dataTable.table-sm .sorting_desc:before{top:5px;right:.85em}table.dataTable.table-sm .sorting:after,table.dataTable.table-sm .sorting_asc:after,table.dataTable.table-sm .sorting_desc:after{top:5px}table.table-bordered.dataTable{border-right-width:0}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:1px}table.table-bordered.dataTable tbody th,table.table-bordered.dataTable tbody td{border-bottom-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:last-child{padding-right:0}
 
admin/css/dataTables.bootstrap5.min.css ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ td.dt-control{background:url("https://www.datatables.net/examples/resources/details_open.png") no-repeat center center;cursor:pointer}tr.dt-hasChild td.dt-control{background:url("https://www.datatables.net/examples/resources/details_close.png") no-repeat center center}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}/*! Bootstrap 5 integration for DataTables
2
+ *
3
+ * ©2020 SpryMedia Ltd, all rights reserved.
4
+ * License: MIT datatables.net/license/mit
5
+ */table.dataTable{clear:both;margin-top:6px !important;margin-bottom:6px !important;max-width:none !important;border-collapse:separate !important;border-spacing:0}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;box-sizing:content-box}table.dataTable td.dataTables_empty,table.dataTable th.dataTables_empty{text-align:center}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length label{font-weight:normal;text-align:left;white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:auto;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:normal;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_info{padding-top:.85em}div.dataTables_wrapper div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_wrapper div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap;justify-content:flex-end}div.dataTables_wrapper div.dataTables_processing{position:absolute;top:50%;left:50%;width:200px;margin-left:-100px;margin-top:-26px;text-align:center;padding:1em 0}table.dataTable>thead>tr>th:active,table.dataTable>thead>tr>td:active{outline:none}table.dataTable>thead>tr>th:not(.sorting_disabled),table.dataTable>thead>tr>td:not(.sorting_disabled){padding-right:30px}table.dataTable>thead .sorting,table.dataTable>thead .sorting_asc,table.dataTable>thead .sorting_desc,table.dataTable>thead .sorting_asc_disabled,table.dataTable>thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable>thead .sorting:before,table.dataTable>thead .sorting:after,table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_asc:after,table.dataTable>thead .sorting_desc:before,table.dataTable>thead .sorting_desc:after,table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_asc_disabled:after,table.dataTable>thead .sorting_desc_disabled:before,table.dataTable>thead .sorting_desc_disabled:after{position:absolute;bottom:.5em;display:block;opacity:.3}table.dataTable>thead .sorting:before,table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_desc:before,table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_desc_disabled:before{right:1em;content:"↑"}table.dataTable>thead .sorting:after,table.dataTable>thead .sorting_asc:after,table.dataTable>thead .sorting_desc:after,table.dataTable>thead .sorting_asc_disabled:after,table.dataTable>thead .sorting_desc_disabled:after{right:.5em;content:"↓"}table.dataTable>thead .sorting_asc:before,table.dataTable>thead .sorting_desc:after{opacity:1}table.dataTable>thead .sorting_asc_disabled:before,table.dataTable>thead .sorting_desc_disabled:after{opacity:0}div.dataTables_scrollHead table.dataTable{margin-bottom:0 !important}div.dataTables_scrollBody>table{border-top:none;margin-top:0 !important;margin-bottom:0 !important}div.dataTables_scrollBody>table>thead .sorting:before,div.dataTables_scrollBody>table>thead .sorting_asc:before,div.dataTables_scrollBody>table>thead .sorting_desc:before,div.dataTables_scrollBody>table>thead .sorting:after,div.dataTables_scrollBody>table>thead .sorting_asc:after,div.dataTables_scrollBody>table>thead .sorting_desc:after{display:none}div.dataTables_scrollBody>table>tbody tr:first-child th,div.dataTables_scrollBody>table>tbody tr:first-child td{border-top:none}div.dataTables_scrollFoot>.dataTables_scrollFootInner{box-sizing:content-box}div.dataTables_scrollFoot>.dataTables_scrollFootInner>table{margin-top:0 !important;border-top:none}@media screen and (max-width: 767px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}div.dataTables_wrapper div.dataTables_paginate ul.pagination{justify-content:center !important}}table.dataTable.table-sm>thead>tr>th:not(.sorting_disabled){padding-right:20px}table.dataTable.table-sm .sorting:before,table.dataTable.table-sm .sorting_asc:before,table.dataTable.table-sm .sorting_desc:before{top:5px;right:.85em}table.dataTable.table-sm .sorting:after,table.dataTable.table-sm .sorting_asc:after,table.dataTable.table-sm .sorting_desc:after{top:5px}table.table-bordered.dataTable{border-right-width:0}table.table-bordered.dataTable thead tr:first-child th,table.table-bordered.dataTable thead tr:first-child td{border-top-width:1px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-left-width:0}table.table-bordered.dataTable th:first-child,table.table-bordered.dataTable th:first-child,table.table-bordered.dataTable td:first-child,table.table-bordered.dataTable td:first-child{border-left-width:1px}table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable th:last-child,table.table-bordered.dataTable td:last-child,table.table-bordered.dataTable td:last-child{border-right-width:1px}table.table-bordered.dataTable th,table.table-bordered.dataTable td{border-bottom-width:1px}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}div.table-responsive>div.dataTables_wrapper>div.row{margin:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:first-child{padding-left:0}div.table-responsive>div.dataTables_wrapper>div.row>div[class^=col-]:last-child{padding-right:0}table.dataTable.table-striped>tbody>tr:nth-of-type(2n+1){--bs-table-accent-bg: transparent}table.dataTable.table-striped>tbody>tr.odd{--bs-table-accent-bg: var(--bs-table-striped-bg)}
admin/css/enhanced-ecommerce-google-analytics-admin.css CHANGED
@@ -361,7 +361,6 @@ button.tvc_animate_btn:hover::after, button.tvc_animate_btn:focus::after {
361
  #staticBackdrop .modal-body, #tvc_google_connect .modal-body{padding: 2rem;}
362
  .tvc-nb-spinner {
363
  display: inline-block;
364
- position: absolute;
365
  top: 3px;
366
  right: 10px;
367
  margin-left: 10px;
@@ -410,6 +409,8 @@ button.tvc_animate_btn:hover::after, button.tvc_animate_btn:focus::after {
410
  #googleShoppingFeed .tvc-api-sunc{
411
  margin-bottom: 5px;
412
  }
 
 
413
  .tvc-api-sunc span{
414
  color: #03a2b7;
415
  font-weight: 700;
@@ -437,13 +438,13 @@ button.tvc_animate_btn:hover::after, button.tvc_animate_btn:focus::after {
437
  .popup-modal .modal-dialog .modal-content{
438
  margin: 20px auto;
439
  }
440
- .popup-modal .modal-dialog .modal-content .close{
441
  position: absolute;
442
  top: 0px;
443
  right: 10px;
444
  transition: all 200ms;
445
  font-size: 30px;
446
-
447
  text-decoration: none;
448
  }
449
  .popup-modal .tvc-text{
@@ -571,7 +572,8 @@ button.tvc_animate_btn:hover::after, button.tvc_animate_btn:focus::after {
571
  #productSync .card-wrapper .row .col-6 .gmc-image-heading{padding: 5px 0;}
572
 
573
  .tvc_plugin_container .notice, div.error, div.updated {margin: 0;}
574
- .select2-container--default .select2-selection--single .select2-selection__arrow b{top: 35%; border: solid #2f3d4a !important; border-width: 0 2px 2px 0 !important;}
 
575
  .tvc-notification{position: relative;}
576
  .tvc-notification-dropdown-menu{
577
  width: 420px;
@@ -639,7 +641,9 @@ button.tvc_animate_btn:hover::after, button.tvc_animate_btn:focus::after {
639
  #sync-product .dataTables_length label{margin: 0;}
640
  #sync-product div.dataTables_filter input{border-radius: 15px;}
641
  .tvc-licence .input-group .form-control{height: 40px; letter-spacing: 8px; font-size: 18px;}
 
642
  button#ee_submit_plugin, button#google-add{ min-width: 80px;}
 
643
  .ga-text span{font-weight:normal;}
644
  .tvc-help-slider, .tvc-b-value-slider{
645
  height: 120px;
361
  #staticBackdrop .modal-body, #tvc_google_connect .modal-body{padding: 2rem;}
362
  .tvc-nb-spinner {
363
  display: inline-block;
 
364
  top: 3px;
365
  right: 10px;
366
  margin-left: 10px;
409
  #googleShoppingFeed .tvc-api-sunc{
410
  margin-bottom: 5px;
411
  }
412
+ #googleShoppingFeed input[type=text], #googleShoppingFeed input[type=number]{margin-bottom: 10px;}
413
+ #googleShoppingFeed input[type=radio]{margin-right: 4px;}
414
  .tvc-api-sunc span{
415
  color: #03a2b7;
416
  font-weight: 700;
438
  .popup-modal .modal-dialog .modal-content{
439
  margin: 20px auto;
440
  }
441
+ .popup-modal .modal-dialog .modal-content .btn-close{
442
  position: absolute;
443
  top: 0px;
444
  right: 10px;
445
  transition: all 200ms;
446
  font-size: 30px;
447
+ background:none;
448
  text-decoration: none;
449
  }
450
  .popup-modal .tvc-text{
572
  #productSync .card-wrapper .row .col-6 .gmc-image-heading{padding: 5px 0;}
573
 
574
  .tvc_plugin_container .notice, div.error, div.updated {margin: 0;}
575
+ .select2-container--default .select2-selection--single .select2-selection__arrow b{top: 35%;}
576
+ .select2-container--default .select2-selection--single .select2-selection__rendered{line-height: 30px;}
577
  .tvc-notification{position: relative;}
578
  .tvc-notification-dropdown-menu{
579
  width: 420px;
641
  #sync-product .dataTables_length label{margin: 0;}
642
  #sync-product div.dataTables_filter input{border-radius: 15px;}
643
  .tvc-licence .input-group .form-control{height: 40px; letter-spacing: 8px; font-size: 18px;}
644
+
645
  button#ee_submit_plugin, button#google-add{ min-width: 80px;}
646
+ button#google-add{margin-top: 20px;}
647
  .ga-text span{font-weight:normal;}
648
  .tvc-help-slider, .tvc-b-value-slider{
649
  height: 120px;
admin/css/style.css CHANGED
@@ -349,6 +349,30 @@ button:disabled,button[disabled], button:disabled:hover,button[disabled]:hover{b
349
  .fa-check-circle:before{content:"";background-image:url(../images/icon/tick-round.svg);width:24px;height:24px;background-repeat:no-repeat;background-size:100%; display: block; margin: 0 auto;}
350
  .fa-exclamation-circle:before{content:"";background-image:url(../images/exclaimation.png);width:24px;height:24px;background-repeat:no-repeat;background-size:100%;display: block; margin: 0 auto;}
351
  .fa-question-circle:before{content:"";background-image:url(../images/help-icon.png);width:16px;height:16px;background-repeat:no-repeat;background-size:100%;position:absolute;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  /*rc */
353
 
354
  /* comman style----------------------*/
349
  .fa-check-circle:before{content:"";background-image:url(../images/icon/tick-round.svg);width:24px;height:24px;background-repeat:no-repeat;background-size:100%; display: block; margin: 0 auto;}
350
  .fa-exclamation-circle:before{content:"";background-image:url(../images/exclaimation.png);width:24px;height:24px;background-repeat:no-repeat;background-size:100%;display: block; margin: 0 auto;}
351
  .fa-question-circle:before{content:"";background-image:url(../images/help-icon.png);width:16px;height:16px;background-repeat:no-repeat;background-size:100%;position:absolute;}
352
+
353
+ .form-check .form-check-input{
354
+ height: 1em;
355
+ margin-top: 0.25em;
356
+ vertical-align: top;
357
+ background-color: #fff;
358
+ background-repeat: no-repeat;
359
+ background-size: contain;
360
+ border: 1px solid rgba(0,0,0,.25);
361
+ -webkit-appearance: none;
362
+ -moz-appearance: none;
363
+ appearance: none;
364
+ -webkit-print-color-adjust: exact;
365
+ }
366
+ .form-check-input:checked {
367
+ background-color: #0d6efd;
368
+ border-color: #0d6efd;
369
+ }
370
+ .form-switch .form-check-input:checked::before{
371
+ content: none;
372
+ }
373
+ .select2-container--open{
374
+ z-index: 9999;
375
+ }
376
  /*rc */
377
 
378
  /* comman style----------------------*/
admin/helper/class-dashboard-helper.php CHANGED
@@ -43,18 +43,16 @@ if(!class_exists('Conversios_Dashboard_Helper')){
43
  }
44
  }
45
  public function get_google_ads_campaign_performance(){
46
- $nonce = (isset($_POST['conversios_nonce']))?$_POST['conversios_nonce']:"";
47
  if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
48
- $post_data = (object)$_POST;
49
- //$start_date = isset($post_data->start_date)?$post_data->start_date:date('Y-m-d',strtotime('-31 days'));
50
- $start_date = str_replace(' ', '',(isset($_POST['start_date']))?$_POST['start_date']:"");
51
  if($start_date != ""){
52
  $date = DateTime::createFromFormat('d-m-Y', $start_date);
53
  $start_date = $date->format('Y-m-d');
54
  }
55
  $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
56
 
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');
@@ -72,28 +70,24 @@ if(!class_exists('Conversios_Dashboard_Helper')){
72
  $errormsg= isset($api_rs->errors[0])?$api_rs->errors[0]:"";
73
  $return = array('error'=>true,'errors'=>$errormsg);
74
  }
75
- //print_r($account_performance_res);
76
 
77
  }else{
78
- $return = array('error'=>true,'errors'=>__("Admin security nonce is not verified.","conversios"));
79
  }
80
  echo json_encode($return);
81
  wp_die();
82
  }
83
  public function get_google_ads_reports_chart(){
84
- $nonce = (isset($_POST['conversios_nonce']))?$_POST['conversios_nonce']:"";
85
  if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
86
- $post_data = (object)$_POST;
87
- //$start_date = isset($post_data->start_date)?$post_data->start_date:date('Y-m-d',strtotime('-31 days'));
88
- $start_date = str_replace(' ', '',(isset($_POST['start_date']))?$_POST['start_date']:"");
89
  if($start_date != ""){
90
  $date = DateTime::createFromFormat('d-m-Y', $start_date);
91
  $start_date = $date->format('Y-m-d');
92
  }
93
  $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
94
 
95
- //$end_date = isset($post_data->end_date)?$post_data->end_date:date('Y-m-d',strtotime('-1day'));
96
- $end_date = str_replace(' ', '',(isset($_POST['end_date']))?$_POST['end_date']:"");
97
  if($end_date != ""){
98
  $date = DateTime::createFromFormat('d-m-Y', $end_date);
99
  $end_date = $date->format('Y-m-d');
@@ -108,31 +102,29 @@ if(!class_exists('Conversios_Dashboard_Helper')){
108
  }
109
  }else{
110
  $return = array('error'=>true,'errors'=>$api_rs->error);
111
- }
112
- //print_r($account_performance_res);
113
-
114
  }else{
115
- $return = array('error'=>true,'errors'=>__("Admin security nonce is not verified.","conversios"));
116
  }
117
  echo json_encode($return);
118
  wp_die();
119
  }
120
  public function get_google_analytics_reports(){
121
- $nonce = (isset($_POST['conversios_nonce']))?$_POST['conversios_nonce']:"";
122
  if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
123
  $post_data = (object)$_POST;
124
  $ga_traking_type = sanitize_text_field(isset($post_data->ga_traking_type)?$post_data->ga_traking_type:"");
125
  $subscription_id = sanitize_text_field(isset($post_data->subscription_id)?$post_data->subscription_id:"");
126
  $view_id = sanitize_text_field(isset($post_data->view_id)?$post_data->view_id:"");
127
 
128
- $start_date = str_replace(' ', '',(isset($_POST['start_date']))?$_POST['start_date']:"");
129
  if($start_date != ""){
130
  $date = DateTime::createFromFormat('d-m-Y', $start_date);
131
  $start_date = $date->format('Y-m-d');
132
  }
133
  $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
134
 
135
- $end_date = str_replace(' ', '',(isset($_POST['end_date']))?$_POST['end_date']:"");
136
  if($end_date != ""){
137
  $date = DateTime::createFromFormat('d-m-Y', $end_date);
138
  $end_date = $date->format('Y-m-d');
@@ -143,8 +135,8 @@ if(!class_exists('Conversios_Dashboard_Helper')){
143
  $return = array();
144
  if($subscription_id != "" && $view_id !="" &&( $ga_traking_type == "UA" || $ga_traking_type == "BOTH" )){
145
  $data = array(
146
- 'subscription_id'=>$subscription_id,
147
- 'view_id'=>$view_id,
148
  'start_date'=>$start_date,
149
  'end_date'=>$end_date
150
  );
@@ -158,10 +150,10 @@ if(!class_exists('Conversios_Dashboard_Helper')){
158
  $return = array('error'=>true,'errors'=>$api_rs->message);
159
  }
160
  }else if($subscription_id != "" && ( $ga_traking_type == "GA4" || $ga_traking_type == "BOTH" )){
161
- $return = array('error'=>true,'errors'=>__("GA4 Coming soon...","conversios"));
162
  }
163
  }else{
164
- $return = array('error'=>true,'errors'=>__("Admin security nonce is not verified.","conversios"));
165
  }
166
  echo json_encode($return);
167
  wp_die();
43
  }
44
  }
45
  public function get_google_ads_campaign_performance(){
46
+ $nonce = (isset($_POST['conversios_nonce']))?sanitize_text_field($_POST['conversios_nonce']):"";
47
  if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
48
+ $start_date = str_replace(' ', '',(isset($_POST['start_date']))?sanitize_text_field($_POST['start_date']):"");
 
 
49
  if($start_date != ""){
50
  $date = DateTime::createFromFormat('d-m-Y', $start_date);
51
  $start_date = $date->format('Y-m-d');
52
  }
53
  $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
54
 
55
+ $end_date = str_replace(' ', '',(isset($_POST['end_date']))?sanitize_text_field($_POST['end_date']):"");
56
  if($end_date != ""){
57
  $date = DateTime::createFromFormat('d-m-Y', $end_date);
58
  $end_date = $date->format('Y-m-d');
70
  $errormsg= isset($api_rs->errors[0])?$api_rs->errors[0]:"";
71
  $return = array('error'=>true,'errors'=>$errormsg);
72
  }
 
73
 
74
  }else{
75
+ $return = array('error'=>true,'errors'=>esc_html__("Admin security nonce is not verified.","conversios"));
76
  }
77
  echo json_encode($return);
78
  wp_die();
79
  }
80
  public function get_google_ads_reports_chart(){
81
+ $nonce = (isset($_POST['conversios_nonce']))?sanitize_text_field($_POST['conversios_nonce']):"";
82
  if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
83
+ $start_date = str_replace(' ', '',(isset($_POST['start_date']))?sanitize_text_field($_POST['start_date']):"");
 
 
84
  if($start_date != ""){
85
  $date = DateTime::createFromFormat('d-m-Y', $start_date);
86
  $start_date = $date->format('Y-m-d');
87
  }
88
  $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
89
 
90
+ $end_date = str_replace(' ', '',(isset($_POST['end_date']))?sanitize_text_field($_POST['end_date']):"");
 
91
  if($end_date != ""){
92
  $date = DateTime::createFromFormat('d-m-Y', $end_date);
93
  $end_date = $date->format('Y-m-d');
102
  }
103
  }else{
104
  $return = array('error'=>true,'errors'=>$api_rs->error);
105
+ }
 
 
106
  }else{
107
+ $return = array('error'=>true,'errors'=>esc_html__("Admin security nonce is not verified.","conversios"));
108
  }
109
  echo json_encode($return);
110
  wp_die();
111
  }
112
  public function get_google_analytics_reports(){
113
+ $nonce = (isset($_POST['conversios_nonce']))?sanitize_text_field($_POST['conversios_nonce']):"";
114
  if($this->admin_safe_ajax_call($nonce, 'conversios_nonce')){
115
  $post_data = (object)$_POST;
116
  $ga_traking_type = sanitize_text_field(isset($post_data->ga_traking_type)?$post_data->ga_traking_type:"");
117
  $subscription_id = sanitize_text_field(isset($post_data->subscription_id)?$post_data->subscription_id:"");
118
  $view_id = sanitize_text_field(isset($post_data->view_id)?$post_data->view_id:"");
119
 
120
+ $start_date = str_replace(' ', '',(isset($_POST['start_date']))?sanitize_text_field($_POST['start_date']):"");
121
  if($start_date != ""){
122
  $date = DateTime::createFromFormat('d-m-Y', $start_date);
123
  $start_date = $date->format('Y-m-d');
124
  }
125
  $start_date == (false !==strtotime( $start_date ))?date('Y-m-d', strtotime($start_date)):date( 'Y-m-d', strtotime( '-1 month' ));
126
 
127
+ $end_date = str_replace(' ', '',(isset($_POST['end_date']))?sanitize_text_field($_POST['end_date']):"");
128
  if($end_date != ""){
129
  $date = DateTime::createFromFormat('d-m-Y', $end_date);
130
  $end_date = $date->format('Y-m-d');
135
  $return = array();
136
  if($subscription_id != "" && $view_id !="" &&( $ga_traking_type == "UA" || $ga_traking_type == "BOTH" )){
137
  $data = array(
138
+ 'subscription_id'=>sanitize_text_field($subscription_id),
139
+ 'view_id'=>sanitize_text_field($view_id),
140
  'start_date'=>$start_date,
141
  'end_date'=>$end_date
142
  );
150
  $return = array('error'=>true,'errors'=>$api_rs->message);
151
  }
152
  }else if($subscription_id != "" && ( $ga_traking_type == "GA4" || $ga_traking_type == "BOTH" )){
153
+ $return = array('error'=>true,'errors'=>esc_html__("GA4 Coming soon...","conversios"));
154
  }
155
  }else{
156
+ $return = array('error'=>true,'errors'=>esc_html__("Admin security nonce is not verified.","conversios"));
157
  }
158
  echo json_encode($return);
159
  wp_die();
admin/helper/class-onboarding-helper.php CHANGED
@@ -60,14 +60,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
60
  * @since 4.0.2
61
  */
62
  public function get_analytics_web_properties(){
63
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
64
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
65
  $tvc_data = (object)$_POST['tvc_data'];
66
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
67
  echo json_encode($api_obj->getAnalyticsWebProperties($_POST));
68
  wp_die();
69
  }else{
70
- echo "Admin security nonce is not verified.";
71
  }
72
  }
73
 
@@ -76,10 +76,10 @@ if(!class_exists('Conversios_Onboarding_Helper')):
76
  * @since 4.0.2
77
  */
78
  public function save_analytics_data(){
79
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
80
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
81
  $tvc_data = (object)$_POST['tvc_data'];
82
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
83
  /*sendingblue*/
84
  $data = array();
85
  $data["email"] = $tvc_data->g_mail;
@@ -92,7 +92,7 @@ if(!class_exists('Conversios_Onboarding_Helper')):
92
  echo json_encode($api_obj->saveAnalyticsData($_POST));
93
  wp_die();
94
  }else{
95
- echo "Admin security nonce is not verified.";
96
  }
97
  }
98
 
@@ -101,14 +101,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
101
  * @since 4.0.2
102
  */
103
  public function list_googl_ads_account(){
104
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
105
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
106
  $tvc_data = (object)$_POST['tvc_data'];
107
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
108
  echo json_encode($api_obj->getGoogleAdsAccountList($_POST));
109
  wp_die();
110
  }else{
111
- echo "Admin security nonce is not verified.";
112
  }
113
  }
114
  /**
@@ -116,14 +116,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
116
  * @since 4.0.2
117
  */
118
  public function create_google_ads_account(){
119
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
120
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
121
  $tvc_data = (object)$_POST['tvc_data'];
122
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
123
  echo json_encode($api_obj->createGoogleAdsAccount($_POST));
124
  wp_die();
125
  }else{
126
- echo "Admin security nonce is not verified.";
127
  }
128
  }
129
 
@@ -132,14 +132,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
132
  * @since 4.0.2
133
  */
134
  public function save_google_ads_data(){
135
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
136
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
137
  $tvc_data = (object)$_POST['tvc_data'];
138
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
139
  /*sendingblue*/
140
  $data = array();
141
- $data["email"] = $tvc_data->g_mail;
142
- $data["attributes"]["PRODUCT"] = "Woocommerce Free Plugin";
143
  $data["attributes"]["SET_ADS"] = true;
144
  $data["listIds"]=[40,41];
145
  $data["updateEnabled"]=true;
@@ -148,7 +148,7 @@ if(!class_exists('Conversios_Onboarding_Helper')):
148
  echo json_encode($api_obj->saveGoogleAdsData($_POST));
149
  wp_die();
150
  }else{
151
- echo "Admin security nonce is not verified.";
152
  }
153
  }
154
 
@@ -157,14 +157,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
157
  * @since 4.0.2
158
  */
159
  public function link_analytic_to_ads_account(){
160
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
161
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
162
  $tvc_data = (object)$_POST['tvc_data'];
163
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
164
  echo json_encode($api_obj->linkAnalyticToAdsAccount($_POST));
165
  wp_die();
166
  }else{
167
- echo "Admin security nonce is not verified.";
168
  }
169
  }
170
 
@@ -173,14 +173,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
173
  * @since 4.0.2
174
  */
175
  public function list_google_merchant_account(){
176
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
177
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
178
  $tvc_data = (object)$_POST['tvc_data'];
179
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
180
  echo json_encode($api_obj->listMerchantCenterAccount($_POST));
181
  wp_die();
182
  }else{
183
- echo "Admin security nonce is not verified.";
184
  }
185
  }
186
  /**
@@ -188,14 +188,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
188
  * @since 4.0.2
189
  */
190
  public function create_google_merchant_center_account(){
191
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
192
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
193
  $tvc_data = (object)$_POST['tvc_data'];
194
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
195
  echo json_encode($api_obj->createMerchantAccount($_POST));
196
  wp_die();
197
  }else{
198
- echo "Admin security nonce is not verified.";
199
  }
200
  }
201
 
@@ -204,14 +204,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
204
  * @since 4.0.2
205
  */
206
  public function save_merchant_data(){
207
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
208
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
209
  $tvc_data = (object)$_POST['tvc_data'];
210
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
211
  /*sendingblue*/
212
  $data = array();
213
- $data["email"] = $tvc_data->g_mail;
214
- $data["attributes"]["PRODUCT"] = "Woocommerce Free Plugin";
215
  $data["attributes"]["SET_GMC"] = true;
216
  $data["listIds"]=[40,41];
217
  $data["updateEnabled"]=true;
@@ -220,7 +220,7 @@ if(!class_exists('Conversios_Onboarding_Helper')):
220
  echo json_encode($api_obj->saveMechantData($_POST));
221
  wp_die();
222
  }else{
223
- echo "Admin security nonce is not verified.";
224
  }
225
  }
226
  /**
@@ -228,16 +228,16 @@ if(!class_exists('Conversios_Onboarding_Helper')):
228
  * @since 4.0.2
229
  */
230
  public function get_conversion_list(){
231
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
232
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
233
  $tvc_data = (object)$_POST['tvc_data'];
234
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
235
  unset($_POST['tvc_data']);
236
  unset($_POST['conversios_onboarding_nonce']);
237
  echo json_encode($api_obj->getConversionList($_POST));
238
  wp_die();
239
  }else{
240
- echo "Admin security nonce is not verified.";
241
  }
242
  }
243
 
@@ -246,14 +246,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
246
  * @since 4.0.2
247
  */
248
  public function link_google_ads_to_merchant_center(){
249
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
250
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
251
  $tvc_data = (object)$_POST['tvc_data'];
252
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
253
  echo json_encode($api_obj->linkGoogleAdsToMerchantCenter($_POST));
254
  wp_die();
255
  }else{
256
- echo "Admin security nonce is not verified.";
257
  }
258
  }
259
  /**
@@ -261,14 +261,14 @@ if(!class_exists('Conversios_Onboarding_Helper')):
261
  * @since 4.0.2
262
  */
263
  public function get_subscription_details(){
264
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
265
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
266
  $tvc_data = (object)$_POST['tvc_data'];
267
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
268
- echo json_encode($api_obj->getSubscriptionDetails($_POST['tvc_data'], $_POST['subscription_id']));
269
  wp_die();
270
  }else{
271
- echo "Admin security nonce is not verified.";
272
  }
273
  }
274
 
@@ -277,17 +277,17 @@ if(!class_exists('Conversios_Onboarding_Helper')):
277
  * @since 4.0.2
278
  */
279
  public function update_setup_time_to_subscription(){
280
- $nonce = (isset($_POST['conversios_onboarding_nonce']))?$_POST['conversios_onboarding_nonce']:"";
281
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
282
  $tvc_data = (object)$_POST['tvc_data'];
283
- $api_obj = new Conversios_Onboarding_ApiCall(esc_attr($tvc_data->access_token), esc_attr($tvc_data->refresh_token));
284
- $return_url = $this->save_wp_setting_from_subscription_api($api_obj, $tvc_data, $_POST['subscription_id']);
285
  $return_rs = $api_obj->updateSetupTimeToSubscription($_POST);
286
  $return_rs->return_url = $return_url;
287
  echo json_encode($return_rs);
288
  wp_die();
289
  }else{
290
- echo "Admin security nonce is not verified.";
291
  }
292
  }
293
 
@@ -306,7 +306,7 @@ if(!class_exists('Conversios_Onboarding_Helper')):
306
  $ee_additional_data = $TVC_Admin_Helper->get_ee_additional_data();
307
  if(isset($ee_additional_data['temp_active_licence_key']) && $ee_additional_data['temp_active_licence_key'] != ""){
308
  $licence_key = $ee_additional_data['temp_active_licence_key'];
309
- $TVC_Admin_Helper->active_licence($licence_key, $_GET['subscription_id']);
310
  unset($ee_additional_data['temp_active_licence_key']);
311
  $TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
312
  }
@@ -315,37 +315,37 @@ if(!class_exists('Conversios_Onboarding_Helper')):
315
  * for save conversion send to in WP DB
316
  */
317
  $googleDetail = $google_detail->data;
318
- if($googleDetail->plan_id != 1 && $googleDetail->google_ads_conversion_tracking == 1){
319
  $TVC_Admin_Helper->update_conversion_send_to();
320
  }
321
  /**
322
  * for site verifecation
323
  */
324
- if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id){
325
  $this->site_verification_and_domain_claim($googleDetail);
326
  }
327
 
328
- $_POST['subscription_id'] = esc_attr($googleDetail->id);
329
- $_POST['ga_eeT'] = (isset($googleDetail->enhanced_e_commerce_tracking) && $googleDetail->enhanced_e_commerce_tracking == "1") ? "on" : "";
330
 
331
- $_POST['ga_ST'] = (isset($googleDetail->add_gtag_snippet) && $googleDetail->add_gtag_snippet == "1") ? "on" : "";
332
- $_POST['gm_id'] = esc_attr($googleDetail->measurement_id);
333
- $_POST['ga_id'] = esc_attr($googleDetail->property_id);
334
- $_POST['google_ads_id'] = esc_attr($googleDetail->google_ads_id);
335
- $_POST['google_merchant_id'] = esc_attr($googleDetail->google_merchant_center_id);
336
  $_POST['tracking_option'] = esc_attr($googleDetail->tracking_option);
337
- $_POST['ga_gUser'] = 'on';
338
  //$_POST['ga_gCkout'] = 'on';
339
- $_POST['ga_Impr'] = 6;
340
- $_POST['ga_IPA'] = 'on';
341
- $_POST['ga_OPTOUT'] = 'on';
342
- $_POST['ga_PrivacyPolicy'] = 'on';
343
  $_POST['google-analytic'] = '';
344
  //update option in wordpress local database
345
- update_option('google_ads_conversion_tracking', $googleDetail->google_ads_conversion_tracking);
346
- update_option('ads_tracking_id', esc_attr($googleDetail->google_ads_id));
347
- update_option('ads_ert', $googleDetail->remarketing_tags);
348
- update_option('ads_edrt', $googleDetail->dynamic_remarketing_tags);
349
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
350
  /*
351
  * function call for save API data in WP DB
@@ -359,18 +359,18 @@ if(!class_exists('Conversios_Onboarding_Helper')):
359
  /**
360
  * save gmail and view ID in WP DB
361
  */
362
- if(property_exists($tvc_data,"g_mail") && $tvc_data->g_mail){
363
- update_option('ee_customer_gmail', $tvc_data->g_mail);
364
  }
365
- if(isset($_POST['ga_view_id']) && $_POST['ga_view_id']){
366
- update_option('ee_ga_view_id', esc_attr($_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;
@@ -385,10 +385,10 @@ if(!class_exists('Conversios_Onboarding_Helper')):
385
  $ee_additional_data = $TVC_Admin_Helper->get_ee_additional_data();
386
  $customApiObj = new CustomApi();
387
  $postData = [
388
- 'merchant_id' => esc_attr($googleDetail->merchant_id),
389
- 'website_url' => get_site_url(),
390
- 'subscription_id' => esc_attr($googleDetail->id),
391
- 'account_id' => esc_attr($googleDetail->google_merchant_center_id)
392
  ];
393
  //is site verified
394
  if ($googleDetail->is_site_verified == '0') {
@@ -397,7 +397,7 @@ if(!class_exists('Conversios_Onboarding_Helper')):
397
  if (isset($siteVerificationToken->error) && !empty($siteVerificationToken->errors)) {
398
  goto call_method_tag;
399
  } else {
400
- $myFile = ABSPATH.$siteVerificationToken->data->token;
401
  if (!file_exists($myFile)) {
402
  $fh = fopen($myFile, 'w+');
403
  chmod($myFile,0777);
@@ -447,7 +447,7 @@ if(!class_exists('Conversios_Onboarding_Helper')):
447
  * @since 4.0.2
448
  */
449
  function add_sendinblue_contant($data, $api_obj){
450
- $api_obj->TVC_CALL_API("POST", "https://api.sendinblue.com/v3/contacts", json_encode($data));
451
  }
452
 
453
  }
@@ -464,76 +464,88 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
464
  public function __construct($access_token, $refresh_token) {
465
  $merchantInfo = json_decode(file_get_contents(ENHANCAD_PLUGIN_DIR.'includes/setup/json/merchant-info.json'), true);
466
  $this->refresh_token = $refresh_token;
467
- $this->access_token = $this->generateAccessToken($access_token, $this->refresh_token);
468
  $this->apiDomain = TVC_API_CALL_URL;
469
  $this->token = 'MTIzNA==';
470
- $this->merchantId = $merchantInfo['merchantId'];
471
  }
 
 
 
 
 
 
 
 
 
 
 
472
 
473
- function TVC_CALL_API($method, $url, $data, $headers = false){
474
- $curl = curl_init();
475
- switch ($method){
476
- case "POST":
477
- curl_setopt($curl, CURLOPT_POST, 1);
478
- if ($data)
479
- curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
480
- break;
481
- case "PUT":
482
- curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
483
- if ($data)
484
- curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
485
- break;
486
- default:
487
- if ($data)
488
- $url = sprintf("%s?%s", $url, http_build_query($data));
489
  }
490
- // OPTIONS:
491
- curl_setopt($curl, CURLOPT_URL, $url);
492
- if(!$headers){
493
- curl_setopt($curl, CURLOPT_HTTPHEADER, array(
494
- 'api-key: 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',
495
- 'Content-Type: application/json',
496
- ));
497
- }else{
498
- curl_setopt($curl, CURLOPT_HTTPHEADER, array(
499
- 'api-key: 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',
500
- 'Content-Type: application/json',
501
- $headers
502
- ));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  }
504
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
505
- curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
506
- // EXECUTE:
507
- $result = curl_exec($curl);
508
- curl_close($curl);
509
- return $result;
510
  }
511
 
512
  public function updateTokenToSubscription($tvc_data) {
513
  try {
514
- $tvc_data = json_decode(base64_decode($tvc_data));
515
- $url = $this->apiDomain . '/customer-subscriptions/update-token';
516
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
517
- $data = [
518
- 'subscription_id' => "",//$this->subscription_id,
519
- 'gmail' => sanitize_text_field($tvc_data->g_mail),
520
- 'access_token' => $this->access_token,
521
- 'refresh_token' => $this->refresh_token,
522
- 'domain' => $tvc_data->user_domain
523
- ];
524
- $curl_url = $url;
525
- $data = json_encode($data);
526
- $ch = curl_init();
527
- curl_setopt_array($ch, array(
528
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
529
- CURLOPT_RETURNTRANSFER => true,
530
- CURLOPT_TIMEOUT => 0,
531
- CURLOPT_HTTPHEADER => $header,
532
- CURLOPT_POSTFIELDS => $data
533
- ));
534
- $response = curl_exec($ch);
535
- $response = json_decode($response);
536
- return $response;
537
  } catch (Exception $e) {
538
  return $e->getMessage();
539
  }
@@ -542,17 +554,18 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
542
  public function getSubscriptionDetails($tvc_data, $subscription_id){
543
  try{
544
  $tvc_data = (object)$tvc_data;
 
545
  $url = $this->apiDomain . '/customer-subscriptions/subscription-detail';
546
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken:$this->access_token");
547
  $data = [
548
- 'subscription_id' => $subscription_id,//$this->subscription_id,
549
- 'domain' => $tvc_data->user_domain
550
  ];
551
  $curl_url = $url;
552
  $postData = json_encode($data);
553
  $ch = curl_init();
554
  curl_setopt_array($ch, array(
555
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
556
  CURLOPT_RETURNTRANSFER => true,
557
  CURLOPT_TIMEOUT => 0,
558
  CURLOPT_HTTPHEADER => $header,
@@ -568,13 +581,11 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
568
 
569
  public function getAnalyticsWebProperties($postData) {
570
  try {
571
- // print_r($postData);
572
- //$tvc_data = json_decode(base64_decode($postData['tvc_data']));
573
- //unset($postData['tvc_data']);
574
  $url = $this->apiDomain . '/google-analytics/account-list';
575
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken:$this->access_token");
 
576
  $max_results = 10;
577
- $page = (isset($postData['page']) && $postData['page'] >1)?$postData['page']:"1";
578
  if($page > 1){
579
  //set index
580
  $page = (($page-1) * $max_results)+1;
@@ -582,14 +593,14 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
582
  $data = [
583
  'type' => sanitize_text_field($postData['type']),
584
  'page'=>sanitize_text_field($page),
585
- 'max_results'=>$max_results
586
  ];
587
- //print_r($data);
588
  $curl_url = $url;
589
  $postData = json_encode($data);
590
  $ch = curl_init();
591
  curl_setopt_array($ch, array(
592
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
593
  CURLOPT_RETURNTRANSFER => true,
594
  CURLOPT_TIMEOUT => 0,
595
  CURLOPT_HTTPHEADER => $header,
@@ -608,11 +619,12 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
608
  if($this->refresh_token != ""){
609
  //$tvc_data = json_decode(base64_decode($postData['tvc_data']));
610
  $url = $this->apiDomain . '/adwords/list';
611
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "RefreshToken:$this->refresh_token");
 
612
  $curl_url = $url;
613
  $ch = curl_init();
614
  curl_setopt_array($ch, array(
615
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
616
  CURLOPT_RETURNTRANSFER => true,
617
  CURLOPT_TIMEOUT => 0,
618
  CURLOPT_HTTPHEADER => $header,
@@ -633,13 +645,14 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
633
  $url = $this->apiDomain . '/gmc/user-merchant-center/list';
634
  $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
635
  $data = [
636
- 'access_token' => $this->access_token,
637
  ];
 
638
  $curl_url = $url;
639
  $postData = json_encode($data);
640
  $ch = curl_init();
641
  curl_setopt_array($ch, array(
642
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
643
  CURLOPT_RETURNTRANSFER => true,
644
  CURLOPT_TIMEOUT => 0,
645
  CURLOPT_HTTPHEADER => $header,
@@ -658,24 +671,29 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
658
  $url = $this->apiDomain . '/adwords/create-ads-account';
659
  $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
660
  $data = [
661
- 'email' => sanitize_text_field($tvc_data->g_mail),
662
  'currency' => sanitize_text_field($tvc_data->currency_code),
663
- 'time_zone' => $tvc_data->timezone_string, //'Asia/Kolkata',
664
  'domain' => sanitize_text_field($tvc_data->user_domain)
665
  ];
666
- $curl_url = $url;
667
- $postData = json_encode($data);
668
- $ch = curl_init();
669
- curl_setopt_array($ch, array(
670
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
671
- CURLOPT_RETURNTRANSFER => true,
672
- CURLOPT_TIMEOUT => 0,
673
- CURLOPT_HTTPHEADER => $header,
674
- CURLOPT_POSTFIELDS => $postData
675
- ));
676
- $response = curl_exec($ch);
677
- $response = json_decode($response);
678
- return $response;
 
 
 
 
 
679
  } catch (Exception $e) {
680
  return $e->getMessage();
681
  }
@@ -687,19 +705,19 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
687
  $data = [
688
  'merchant_id' => $this->merchantId, //'256922349',
689
  'name' => sanitize_text_field($postData['store_name']),
690
- 'website_url' => sanitize_text_field($postData['website_url']),
691
  'customer_id' => sanitize_text_field($postData['customer_id']),
692
- 'adult_content' => isset($postData['adult_content']) && $postData['adult_content'] == 'true' ? true : false,
693
- 'country' => $postData['country'],
694
  'users' => [
695
  [
696
- "email_address" => sanitize_text_field($postData['email_address']), //"sarjit@pivotdrive.ca"
697
  "admin" => true
698
  ]
699
  ],
700
  'business_information' => [
701
  'address' => [
702
- 'country' => $postData['country']
703
  ]
704
  ]
705
  ];
@@ -707,7 +725,7 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
707
  $data = json_encode($data);
708
  $ch = curl_init();
709
  curl_setopt_array($ch, array(
710
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
711
  CURLOPT_RETURNTRANSFER => true,
712
  CURLOPT_TIMEOUT => 0,
713
  CURLOPT_HTTPHEADER => $header,
@@ -725,16 +743,16 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
725
  $url = $this->apiDomain . '/customers/login';
726
  $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
727
  $data = [
728
- 'email' => sanitize_text_field($tvc_data->g_mail),
729
- 'access_token' => $this->access_token,
730
- 'refresh_token' => $this->refresh_token,
731
  'sign_in_type' => sanitize_text_field($tvc_data->sign_in_type)
732
  ];
733
  $curl_url = $url;
734
  $data = json_encode($data);
735
  $ch = curl_init();
736
  curl_setopt_array($ch, array(
737
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
738
  CURLOPT_RETURNTRANSFER => true,
739
  CURLOPT_TIMEOUT => 0,
740
  CURLOPT_HTTPHEADER => $header,
@@ -767,18 +785,24 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
767
  'exception_tracking' => sanitize_text_field((isset($postData['exception_tracking']) && $postData['exception_tracking']=='true')?1:0),
768
  'enhanced_link_attribution_tracking' => sanitize_text_field((isset($postData['enhanced_link_attribution_tracking']) && $postData['enhanced_link_attribution_tracking'] == 'true')? 1 : 0)
769
  );
770
- $curl_url = $url;
771
- $data = json_encode($data);
772
- $ch = curl_init();
773
- curl_setopt_array($ch, array(
774
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
775
- CURLOPT_RETURNTRANSFER => true,
776
- CURLOPT_TIMEOUT => 0,
777
- CURLOPT_HTTPHEADER => $header,
778
- CURLOPT_POSTFIELDS => $data
779
- ));
780
- $response = curl_exec($ch);
781
- return json_decode($response);
 
 
 
 
 
 
782
  } catch (Exception $e) {
783
  return $e->getMessage();
784
  }
@@ -796,18 +820,24 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
796
  'google_ads_conversion_tracking' => sanitize_text_field((isset($postData['google_ads_conversion_tracking']) && $postData['google_ads_conversion_tracking'] == 'true') ? 1 : 0),
797
  'link_google_analytics_with_google_ads' => sanitize_text_field((isset($postData['link_google_analytics_with_google_ads']) && $postData['link_google_analytics_with_google_ads'] == 'true') ? 1 : 0)
798
  ];
799
- $curl_url = $url;
800
- $data = json_encode($data);
801
- $ch = curl_init();
802
- curl_setopt_array($ch, array(
803
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
804
- CURLOPT_RETURNTRANSFER => true,
805
- CURLOPT_TIMEOUT => 0,
806
- CURLOPT_HTTPHEADER => $header,
807
- CURLOPT_POSTFIELDS => $data
808
- ));
809
- $response = curl_exec($ch);
810
- return json_decode($response);
 
 
 
 
 
 
811
  } catch (Exception $e) {
812
  return $e->getMessage();
813
  }
@@ -824,18 +854,24 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
824
  'website_url' => sanitize_text_field($postData['website_url']),
825
  'customer_id' => sanitize_text_field($postData['customer_id'])
826
  ];
827
- $curl_url = $url;
828
- $postData = json_encode($data);
829
- $ch = curl_init();
830
- curl_setopt_array($ch, array(
831
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
832
- CURLOPT_RETURNTRANSFER => true,
833
- CURLOPT_TIMEOUT => 0,
834
- CURLOPT_HTTPHEADER => $header,
835
- CURLOPT_POSTFIELDS => $postData
836
- ));
837
- $response = curl_exec($ch);
838
- return json_decode($response);
 
 
 
 
 
 
839
  } catch (Exception $e) {
840
  return $e->getMessage();
841
  }
@@ -844,7 +880,9 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
844
  public function linkAnalyticToAdsAccount($postData) {
845
  try {
846
  $url = $this->apiDomain . '/google-analytics/link-ads-to-analytics';
847
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken:$this->access_token", "RefreshToken:$this->refresh_token");
 
 
848
  if ($postData['type'] == "UA") {
849
  $data = [
850
  'type' => sanitize_text_field($postData['type']),
@@ -867,7 +905,7 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
867
  $data = json_encode($data);
868
  $ch = curl_init();
869
  curl_setopt_array($ch, array(
870
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
871
  CURLOPT_RETURNTRANSFER => true,
872
  CURLOPT_TIMEOUT => 0,
873
  CURLOPT_HTTPHEADER => $header,
@@ -882,9 +920,10 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
882
  public function linkGoogleAdsToMerchantCenter($postData) {
883
  try {
884
  $url = $this->apiDomain . '/adwords/link-ads-to-merchant-center';
885
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken:$this->access_token");
 
886
  $data = [
887
- 'merchant_id' => sanitize_text_field(($postData['merchant_id'] == 'NewMerchant') ? $this->merchantId: $postData['merchant_id']),
888
  'account_id' => sanitize_text_field($postData['account_id']),
889
  'adwords_id' => sanitize_text_field($postData['adwords_id'])
890
  ];
@@ -892,7 +931,7 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
892
  $data = json_encode($data);
893
  $ch = curl_init();
894
  curl_setopt_array($ch, array(
895
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
896
  CURLOPT_RETURNTRANSFER => true,
897
  CURLOPT_TIMEOUT => 0,
898
  CURLOPT_HTTPHEADER => $header,
@@ -912,11 +951,11 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
912
  'subscription_id' => sanitize_text_field((isset($postData['subscription_id']))?$postData['subscription_id'] : ''),
913
  'setup_end_time' => date('Y-m-d H:i:s')
914
  ];
915
- $this->curl_url = $url;
916
  $data = json_encode($data);
917
  $ch = curl_init();
918
  curl_setopt_array($ch, array(
919
- CURLOPT_URL => $this->curl_url, //esc_url($this->curl_url),
920
  CURLOPT_RETURNTRANSFER => true,
921
  CURLOPT_TIMEOUT => 0,
922
  CURLOPT_HTTPHEADER => $this->header,
@@ -926,48 +965,45 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
926
  $this->response = json_decode($this->response);
927
  return $this->response;
928
  } catch (Exception $e) {
929
- return $e->getMessage();
930
  }
931
  }
932
 
933
  public function getConversionList($data) {
934
  try {
935
  $url = $this->apiDomain . '/google-ads/conversion-list';
936
- $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
937
- $curl_url = $url;
938
- $postData = json_encode($data);
939
- $ch = curl_init();
940
- curl_setopt_array($ch, array(
941
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
942
- CURLOPT_RETURNTRANSFER => true,
943
- CURLOPT_TIMEOUT => 0,
944
- CURLOPT_HTTPHEADER => $header,
945
- CURLOPT_POSTFIELDS => $postData
946
- ));
947
- $response = curl_exec($ch);
948
- $response = json_decode($response);
949
  $return = new \stdClass();
950
-
951
- if(isset($response->data) && count($response->data) > 0){
 
952
  $return->error = false;
953
- $return->message = "Google Ads conversion tracking setting success.";
954
- }else{
955
- if(isset($response->error) && $response->error == false){
956
- $response = $this->createConversion($data);
957
  if(isset($response->error) && $response->error == false){
958
  $return->error = false;
959
- $return->message = $response->message;
960
  }else{
961
  $return->error = true;
962
  $errors = json_decode($response->errors[0]);
963
- $return->errors = $errors->message;
964
  }
965
- }else{
966
- $return->error = true;
967
- $return->errors = $response->errors[0];
968
- }
 
 
 
969
  }
970
- return $return;
971
  } catch (Exception $e) {
972
  return $e->getMessage();
973
  }
@@ -981,19 +1017,24 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
981
  'customer_id' => sanitize_text_field((isset($postData['customer_id']))?$postData['customer_id'] : ''),
982
  'name' => "Order Conversion"
983
  ];
984
- $curl_url = $url;
985
- $postData = json_encode($data);
986
- $ch = curl_init();
987
- curl_setopt_array($ch, array(
988
- CURLOPT_URL => $curl_url, //esc_url($curl_url),
989
- CURLOPT_RETURNTRANSFER => true,
990
- CURLOPT_TIMEOUT => 0,
991
- CURLOPT_HTTPHEADER => $header,
992
- CURLOPT_POSTFIELDS => $postData
993
- ));
994
- $response = curl_exec($ch);
995
- $response = json_decode($response);
996
- return $response;
 
 
 
 
 
997
  } catch (Exception $e) {
998
  return $e->getMessage();
999
  }
@@ -1003,7 +1044,7 @@ if(!class_exists('Conversios_Onboarding_ApiCall') ){
1003
  . "access_token=" . $access_token;
1004
 
1005
  $ch = curl_init();
1006
- curl_setopt($ch, CURLOPT_URL, $request);
1007
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1008
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1009
  $response = curl_exec($ch);
60
  * @since 4.0.2
61
  */
62
  public function get_analytics_web_properties(){
63
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
64
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
65
  $tvc_data = (object)$_POST['tvc_data'];
66
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
67
  echo json_encode($api_obj->getAnalyticsWebProperties($_POST));
68
  wp_die();
69
  }else{
70
+ echo esc_html__("Admin security nonce is not verified.","conversios");
71
  }
72
  }
73
 
76
  * @since 4.0.2
77
  */
78
  public function save_analytics_data(){
79
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
80
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
81
  $tvc_data = (object)$_POST['tvc_data'];
82
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
83
  /*sendingblue*/
84
  $data = array();
85
  $data["email"] = $tvc_data->g_mail;
92
  echo json_encode($api_obj->saveAnalyticsData($_POST));
93
  wp_die();
94
  }else{
95
+ echo esc_html__("Admin security nonce is not verified.","conversios");
96
  }
97
  }
98
 
101
  * @since 4.0.2
102
  */
103
  public function list_googl_ads_account(){
104
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
105
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
106
  $tvc_data = (object)$_POST['tvc_data'];
107
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
108
  echo json_encode($api_obj->getGoogleAdsAccountList($_POST));
109
  wp_die();
110
  }else{
111
+ echo esc_html__("Admin security nonce is not verified.","conversios");
112
  }
113
  }
114
  /**
116
  * @since 4.0.2
117
  */
118
  public function create_google_ads_account(){
119
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
120
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
121
  $tvc_data = (object)$_POST['tvc_data'];
122
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
123
  echo json_encode($api_obj->createGoogleAdsAccount($_POST));
124
  wp_die();
125
  }else{
126
+ echo esc_html__("Admin security nonce is not verified.","conversios");
127
  }
128
  }
129
 
132
  * @since 4.0.2
133
  */
134
  public function save_google_ads_data(){
135
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
136
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
137
  $tvc_data = (object)$_POST['tvc_data'];
138
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
139
  /*sendingblue*/
140
  $data = array();
141
+ $data["email"] = sanitize_email($tvc_data->g_mail);
142
+ $data["attributes"]["PRODUCT"] = sanitize_text_field("Woocommerce Free Plugin");
143
  $data["attributes"]["SET_ADS"] = true;
144
  $data["listIds"]=[40,41];
145
  $data["updateEnabled"]=true;
148
  echo json_encode($api_obj->saveGoogleAdsData($_POST));
149
  wp_die();
150
  }else{
151
+ echo esc_html__("Admin security nonce is not verified.","conversios");
152
  }
153
  }
154
 
157
  * @since 4.0.2
158
  */
159
  public function link_analytic_to_ads_account(){
160
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
161
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
162
  $tvc_data = (object)$_POST['tvc_data'];
163
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
164
  echo json_encode($api_obj->linkAnalyticToAdsAccount($_POST));
165
  wp_die();
166
  }else{
167
+ echo esc_html__("Admin security nonce is not verified.","conversios");
168
  }
169
  }
170
 
173
  * @since 4.0.2
174
  */
175
  public function list_google_merchant_account(){
176
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
177
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
178
  $tvc_data = (object)$_POST['tvc_data'];
179
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
180
  echo json_encode($api_obj->listMerchantCenterAccount($_POST));
181
  wp_die();
182
  }else{
183
+ echo esc_html__("Admin security nonce is not verified.","conversios");
184
  }
185
  }
186
  /**
188
  * @since 4.0.2
189
  */
190
  public function create_google_merchant_center_account(){
191
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
192
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
193
  $tvc_data = (object)$_POST['tvc_data'];
194
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
195
  echo json_encode($api_obj->createMerchantAccount($_POST));
196
  wp_die();
197
  }else{
198
+ echo esc_html__("Admin security nonce is not verified.","conversios");
199
  }
200
  }
201
 
204
  * @since 4.0.2
205
  */
206
  public function save_merchant_data(){
207
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
208
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
209
  $tvc_data = (object)$_POST['tvc_data'];
210
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
211
  /*sendingblue*/
212
  $data = array();
213
+ $data["email"] = sanitize_email($tvc_data->g_mail);
214
+ $data["attributes"]["PRODUCT"] = sanitize_text_field("Woocommerce Free Plugin");
215
  $data["attributes"]["SET_GMC"] = true;
216
  $data["listIds"]=[40,41];
217
  $data["updateEnabled"]=true;
220
  echo json_encode($api_obj->saveMechantData($_POST));
221
  wp_die();
222
  }else{
223
+ echo esc_html__("Admin security nonce is not verified.","conversios");
224
  }
225
  }
226
  /**
228
  * @since 4.0.2
229
  */
230
  public function get_conversion_list(){
231
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
232
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
233
  $tvc_data = (object)$_POST['tvc_data'];
234
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
235
  unset($_POST['tvc_data']);
236
  unset($_POST['conversios_onboarding_nonce']);
237
  echo json_encode($api_obj->getConversionList($_POST));
238
  wp_die();
239
  }else{
240
+ echo esc_html__("Admin security nonce is not verified.","conversios");
241
  }
242
  }
243
 
246
  * @since 4.0.2
247
  */
248
  public function link_google_ads_to_merchant_center(){
249
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
250
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
251
  $tvc_data = (object)$_POST['tvc_data'];
252
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
253
  echo json_encode($api_obj->linkGoogleAdsToMerchantCenter($_POST));
254
  wp_die();
255
  }else{
256
+ echo esc_html__("Admin security nonce is not verified.","conversios");
257
  }
258
  }
259
  /**
261
  * @since 4.0.2
262
  */
263
  public function get_subscription_details(){
264
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
265
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
266
  $tvc_data = (object)$_POST['tvc_data'];
267
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
268
+ echo json_encode($api_obj->getSubscriptionDetails($_POST['tvc_data'], sanitize_text_field($_POST['subscription_id']) ));
269
  wp_die();
270
  }else{
271
+ echo esc_html__("Admin security nonce is not verified.","conversios");
272
  }
273
  }
274
 
277
  * @since 4.0.2
278
  */
279
  public function update_setup_time_to_subscription(){
280
+ $nonce = (isset($_POST['conversios_onboarding_nonce']))?sanitize_text_field($_POST['conversios_onboarding_nonce']):"";
281
  if($this->admin_safe_ajax_call($nonce, 'conversios_onboarding_nonce')){
282
  $tvc_data = (object)$_POST['tvc_data'];
283
+ $api_obj = new Conversios_Onboarding_ApiCall(sanitize_text_field($tvc_data->access_token), sanitize_text_field($tvc_data->refresh_token));
284
+ $return_url = $this->save_wp_setting_from_subscription_api($api_obj, $tvc_data, sanitize_text_field($_POST['subscription_id']) );
285
  $return_rs = $api_obj->updateSetupTimeToSubscription($_POST);
286
  $return_rs->return_url = $return_url;
287
  echo json_encode($return_rs);
288
  wp_die();
289
  }else{
290
+ echo esc_html__("Admin security nonce is not verified.","conversios");
291
  }
292
  }
293
 
306
  $ee_additional_data = $TVC_Admin_Helper->get_ee_additional_data();
307
  if(isset($ee_additional_data['temp_active_licence_key']) && $ee_additional_data['temp_active_licence_key'] != ""){
308
  $licence_key = $ee_additional_data['temp_active_licence_key'];
309
+ $TVC_Admin_Helper->active_licence($licence_key, sanitize_text_field($_GET['subscription_id']));
310
  unset($ee_additional_data['temp_active_licence_key']);
311
  $TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
312
  }
315
  * for save conversion send to in WP DB
316
  */
317
  $googleDetail = $google_detail->data;
318
+ if($googleDetail->plan_id != 1 && sanitize_text_field($googleDetail->google_ads_conversion_tracking) == 1){
319
  $TVC_Admin_Helper->update_conversion_send_to();
320
  }
321
  /**
322
  * for site verifecation
323
  */
324
+ if(isset($googleDetail->google_merchant_center_id) && sanitize_text_field($googleDetail->google_merchant_center_id)){
325
  $this->site_verification_and_domain_claim($googleDetail);
326
  }
327
 
328
+ $_POST['subscription_id'] = sanitize_text_field($googleDetail->id);
329
+ $_POST['ga_eeT'] = (isset($googleDetail->enhanced_e_commerce_tracking) && sanitize_text_field($googleDetail->enhanced_e_commerce_tracking) == "1") ? "on" : "";
330
 
331
+ $_POST['ga_ST'] = (isset($googleDetail->add_gtag_snippet) && sanitize_text_field($googleDetail->add_gtag_snippet) == "1") ? "on" : "";
332
+ $_POST['gm_id'] = sanitize_text_field($googleDetail->measurement_id);
333
+ $_POST['ga_id'] = sanitize_text_field($googleDetail->property_id);
334
+ $_POST['google_ads_id'] = sanitize_text_field($googleDetail->google_ads_id);
335
+ $_POST['google_merchant_id'] = sanitize_text_field($googleDetail->google_merchant_center_id);
336
  $_POST['tracking_option'] = esc_attr($googleDetail->tracking_option);
337
+ $_POST['ga_gUser'] = esc_attr('on');
338
  //$_POST['ga_gCkout'] = 'on';
339
+ $_POST['ga_Impr'] = esc_attr(6);
340
+ $_POST['ga_IPA'] = esc_attr('on');
341
+ $_POST['ga_OPTOUT'] = esc_attr('on');
342
+ $_POST['ga_PrivacyPolicy'] = esc_attr('on');
343
  $_POST['google-analytic'] = '';
344
  //update option in wordpress local database
345
+ update_option('google_ads_conversion_tracking', sanitize_option('google_ads_conversion_tracking', $googleDetail->google_ads_conversion_tracking));
346
+ update_option('ads_tracking_id', sanitize_option('ads_tracking_id', $googleDetail->google_ads_id));
347
+ update_option('ads_ert', sanitize_option('ads_ert', $googleDetail->remarketing_tags));
348
+ update_option('ads_edrt', sanitize_option('ads_edrt', $googleDetail->dynamic_remarketing_tags));
349
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
350
  /*
351
  * function call for save API data in WP DB
359
  /**
360
  * save gmail and view ID in WP DB
361
  */
362
+ if(property_exists($tvc_data,"g_mail") && sanitize_email($tvc_data->g_mail)){
363
+ update_option('ee_customer_gmail', sanitize_option('ee_customer_gmail', $tvc_data->g_mail));
364
  }
365
+ if(isset($_POST['ga_view_id']) && sanitize_text_field($_POST['ga_view_id'])){
366
+ update_option('ee_ga_view_id', sanitize_text_field($_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( sanitize_text_field($googleDetail->google_merchant_center_id) != "" && sanitize_text_field($googleDetail->google_ads_id) != ""){
371
+ $return_url = esc_url_raw("admin.php?page=conversios-google-shopping-feed&tab=sync_product_page&welcome_msg=true");
372
  }else{
373
+ $return_url = esc_url_raw("admin.php?page=conversios-google-shopping-feed&tab=gaa_config_page&welcome_msg=true");
374
  }
375
  }
376
  return $return_url;
385
  $ee_additional_data = $TVC_Admin_Helper->get_ee_additional_data();
386
  $customApiObj = new CustomApi();
387
  $postData = [
388
+ 'merchant_id' => sanitize_text_field($googleDetail->merchant_id),
389
+ 'website_url' => esc_url_raw(get_site_url()),
390
+ 'subscription_id' => sanitize_text_field($googleDetail->id),
391
+ 'account_id' => sanitize_text_field($googleDetail->google_merchant_center_id)
392
  ];
393
  //is site verified
394
  if ($googleDetail->is_site_verified == '0') {
397
  if (isset($siteVerificationToken->error) && !empty($siteVerificationToken->errors)) {
398
  goto call_method_tag;
399
  } else {
400
+ $myFile =ABSPATH.$siteVerificationToken->data->token;
401
  if (!file_exists($myFile)) {
402
  $fh = fopen($myFile, 'w+');
403
  chmod($myFile,0777);
447
  * @since 4.0.2
448
  */
449
  function add_sendinblue_contant($data, $api_obj){
450
+ $api_obj->TVC_CALL_API_sendinblue("POST", "https://api.sendinblue.com/v3/contacts", $data);
451
  }
452
 
453
  }
464
  public function __construct($access_token, $refresh_token) {
465
  $merchantInfo = json_decode(file_get_contents(ENHANCAD_PLUGIN_DIR.'includes/setup/json/merchant-info.json'), true);
466
  $this->refresh_token = $refresh_token;
467
+ $this->access_token = base64_encode( $this->generateAccessToken( base64_decode($access_token), base64_decode($this->refresh_token) ) );
468
  $this->apiDomain = TVC_API_CALL_URL;
469
  $this->token = 'MTIzNA==';
470
+ $this->merchantId = sanitize_text_field($merchantInfo['merchantId']);
471
  }
472
+ public function tc_wp_remot_call_post($url, $args){
473
+ try {
474
+ if(!empty($args)){
475
+ // Send remote request
476
+ $args['timeout']= esc_attr("1000");
477
+ $request = wp_remote_post($url, $args);
478
+ // Retrieve information
479
+ $response_code = wp_remote_retrieve_response_code($request);
480
+
481
+ $response_message = wp_remote_retrieve_response_message($request);
482
+ $response_body = json_decode(wp_remote_retrieve_body($request));
483
 
484
+ if ((isset($response_body->error) && $response_body->error == '')) {
485
+ return new WP_REST_Response($response_body->data);
486
+ } else {
487
+ return new WP_Error($response_code, $response_message, $response_body);
488
+ }
489
+ }
490
+ } catch (Exception $e) {
491
+ return $e->getMessage();
 
 
 
 
 
 
 
 
492
  }
493
+ }
494
+ public function TVC_CALL_API_sendinblue($method, $url, $data, $headers = false){
495
+ try {
496
+ $args = array(
497
+ 'headers' => array(
498
+ 'api-key' => sanitize_text_field("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"),
499
+ 'Content-Type' => 'application/json'
500
+ ),
501
+ 'method' => $method,
502
+ 'body' => $data
503
+ );
504
+ // Send remote request
505
+ $request = wp_remote_post($url, $args);
506
+ // Retrieve information
507
+ $response_code = wp_remote_retrieve_response_code($request);
508
+ $response_message = wp_remote_retrieve_response_message($request);
509
+ $response_body = json_decode(wp_remote_retrieve_body($request));
510
+
511
+ if ((isset($response_body->error) && $response_body->error == '')) {
512
+ return new WP_REST_Response(
513
+ array('status' => $response_code, 'message' => $response_message, 'data' => $response_body->data)
514
+ );
515
+ } else {
516
+ return new WP_Error($response_code, $response_message, $response_body);
517
+ }
518
+ } catch (Exception $e) {
519
+ return $e->getMessage();
520
  }
521
+
 
 
 
 
 
522
  }
523
 
524
  public function updateTokenToSubscription($tvc_data) {
525
  try {
526
+ $tvc_data = json_decode(base64_decode($tvc_data));
527
+ $url = $this->apiDomain . '/customer-subscriptions/update-token';
528
+ $header = array("Authorization: Bearer $this->token", "content-type: application/json");
529
+ $data = [
530
+ 'subscription_id' => "",//$this->subscription_id,
531
+ 'gmail' => sanitize_email($tvc_data->g_mail),
532
+ 'access_token' => base64_decode(sanitize_text_field($this->access_token)),
533
+ 'refresh_token' => base64_decode(sanitize_text_field($this->refresh_token)),
534
+ 'domain' => sanitize_text_field($tvc_data->user_domain)
535
+ ];
536
+ $curl_url = $url;
537
+ $data = json_encode($data);
538
+ $ch = curl_init();
539
+ curl_setopt_array($ch, array(
540
+ CURLOPT_URL => $curl_url, //esc_url($curl_url),
541
+ CURLOPT_RETURNTRANSFER => true,
542
+ CURLOPT_TIMEOUT => 0,
543
+ CURLOPT_HTTPHEADER => $header,
544
+ CURLOPT_POSTFIELDS => $data
545
+ ));
546
+ $response = curl_exec($ch);
547
+ $response = json_decode($response);
548
+ return $response;
549
  } catch (Exception $e) {
550
  return $e->getMessage();
551
  }
554
  public function getSubscriptionDetails($tvc_data, $subscription_id){
555
  try{
556
  $tvc_data = (object)$tvc_data;
557
+ $access_token = base64_decode(sanitize_text_field($this->access_token));
558
  $url = $this->apiDomain . '/customer-subscriptions/subscription-detail';
559
+ $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken: $access_token");
560
  $data = [
561
+ 'subscription_id' => sanitize_text_field($subscription_id),//$this->subscription_id,
562
+ 'domain' => sanitize_text_field($tvc_data->user_domain)
563
  ];
564
  $curl_url = $url;
565
  $postData = json_encode($data);
566
  $ch = curl_init();
567
  curl_setopt_array($ch, array(
568
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
569
  CURLOPT_RETURNTRANSFER => true,
570
  CURLOPT_TIMEOUT => 0,
571
  CURLOPT_HTTPHEADER => $header,
581
 
582
  public function getAnalyticsWebProperties($postData) {
583
  try {
 
 
 
584
  $url = $this->apiDomain . '/google-analytics/account-list';
585
+ $access_token = base64_decode(sanitize_text_field($this->access_token));
586
+ $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken: $access_token");
587
  $max_results = 10;
588
+ $page = (isset($postData['page']) && sanitize_text_field($postData['page']) >1)?sanitize_text_field($postData['page']):"1";
589
  if($page > 1){
590
  //set index
591
  $page = (($page-1) * $max_results)+1;
593
  $data = [
594
  'type' => sanitize_text_field($postData['type']),
595
  'page'=>sanitize_text_field($page),
596
+ 'max_results'=>sanitize_text_field($max_results)
597
  ];
598
+
599
  $curl_url = $url;
600
  $postData = json_encode($data);
601
  $ch = curl_init();
602
  curl_setopt_array($ch, array(
603
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
604
  CURLOPT_RETURNTRANSFER => true,
605
  CURLOPT_TIMEOUT => 0,
606
  CURLOPT_HTTPHEADER => $header,
619
  if($this->refresh_token != ""){
620
  //$tvc_data = json_decode(base64_decode($postData['tvc_data']));
621
  $url = $this->apiDomain . '/adwords/list';
622
+ $refresh_token = base64_decode(sanitize_text_field($this->refresh_token));
623
+ $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "RefreshToken:$refresh_token");
624
  $curl_url = $url;
625
  $ch = curl_init();
626
  curl_setopt_array($ch, array(
627
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
628
  CURLOPT_RETURNTRANSFER => true,
629
  CURLOPT_TIMEOUT => 0,
630
  CURLOPT_HTTPHEADER => $header,
645
  $url = $this->apiDomain . '/gmc/user-merchant-center/list';
646
  $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
647
  $data = [
648
+ 'access_token' => base64_decode(sanitize_text_field($this->access_token)),
649
  ];
650
+
651
  $curl_url = $url;
652
  $postData = json_encode($data);
653
  $ch = curl_init();
654
  curl_setopt_array($ch, array(
655
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
656
  CURLOPT_RETURNTRANSFER => true,
657
  CURLOPT_TIMEOUT => 0,
658
  CURLOPT_HTTPHEADER => $header,
671
  $url = $this->apiDomain . '/adwords/create-ads-account';
672
  $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
673
  $data = [
674
+ 'email' => sanitize_email($tvc_data->g_mail),
675
  'currency' => sanitize_text_field($tvc_data->currency_code),
676
+ 'time_zone' => sanitize_text_field($tvc_data->timezone_string), //'Asia/Kolkata',
677
  'domain' => sanitize_text_field($tvc_data->user_domain)
678
  ];
679
+ $args = array(
680
+ 'headers' =>$header,
681
+ 'method' => 'POST',
682
+ 'body' => $data
683
+ );
684
+ $result = $this->tc_wp_remot_call_post(esc_url_raw($url), $args);
685
+ $return = new \stdClass();
686
+ if($result->status == 200){
687
+ $return->status = $result->status;
688
+ $return->data = $result->data;
689
+ $return->error = false;
690
+ return $return;
691
+ }else{
692
+ $return->error = true;
693
+ $return->data = $result->data;
694
+ $return->status = $result->status;
695
+ return $return;
696
+ }
697
  } catch (Exception $e) {
698
  return $e->getMessage();
699
  }
705
  $data = [
706
  'merchant_id' => $this->merchantId, //'256922349',
707
  'name' => sanitize_text_field($postData['store_name']),
708
+ 'website_url' => esc_url_raw(sanitize_text_field($postData['website_url'])),
709
  'customer_id' => sanitize_text_field($postData['customer_id']),
710
+ 'adult_content' => isset($postData['adult_content']) && sanitize_text_field($postData['adult_content']) == 'true' ? true : false,
711
+ 'country' => sanitize_text_field($postData['country']),
712
  'users' => [
713
  [
714
+ "email_address" => sanitize_email($postData['email_address']), //"sarjit@pivotdrive.ca"
715
  "admin" => true
716
  ]
717
  ],
718
  'business_information' => [
719
  'address' => [
720
+ 'country' => sanitize_text_field($postData['country'])
721
  ]
722
  ]
723
  ];
725
  $data = json_encode($data);
726
  $ch = curl_init();
727
  curl_setopt_array($ch, array(
728
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
729
  CURLOPT_RETURNTRANSFER => true,
730
  CURLOPT_TIMEOUT => 0,
731
  CURLOPT_HTTPHEADER => $header,
743
  $url = $this->apiDomain . '/customers/login';
744
  $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
745
  $data = [
746
+ 'email' => sanitize_email($tvc_data->g_mail),
747
+ 'access_token' => base64_decode(sanitize_text_field($this->access_token)),
748
+ 'refresh_token' => base64_decode(sanitize_text_field($this->refresh_token)),
749
  'sign_in_type' => sanitize_text_field($tvc_data->sign_in_type)
750
  ];
751
  $curl_url = $url;
752
  $data = json_encode($data);
753
  $ch = curl_init();
754
  curl_setopt_array($ch, array(
755
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
756
  CURLOPT_RETURNTRANSFER => true,
757
  CURLOPT_TIMEOUT => 0,
758
  CURLOPT_HTTPHEADER => $header,
785
  'exception_tracking' => sanitize_text_field((isset($postData['exception_tracking']) && $postData['exception_tracking']=='true')?1:0),
786
  'enhanced_link_attribution_tracking' => sanitize_text_field((isset($postData['enhanced_link_attribution_tracking']) && $postData['enhanced_link_attribution_tracking'] == 'true')? 1 : 0)
787
  );
788
+ $args = array(
789
+ 'headers' =>$header,
790
+ 'method' => 'POST',
791
+ 'body' => $data
792
+ );
793
+ $result = $this->tc_wp_remot_call_post(esc_url_raw($url), $args);
794
+ $return = new \stdClass();
795
+ if($result->status == 200){
796
+ $return->status = $result->status;
797
+ $return->data = $result->data;
798
+ $return->error = false;
799
+ return $return;
800
+ }else{
801
+ $return->error = true;
802
+ $return->data = $result->data;
803
+ $return->status = $result->status;
804
+ return $return;
805
+ }
806
  } catch (Exception $e) {
807
  return $e->getMessage();
808
  }
820
  'google_ads_conversion_tracking' => sanitize_text_field((isset($postData['google_ads_conversion_tracking']) && $postData['google_ads_conversion_tracking'] == 'true') ? 1 : 0),
821
  'link_google_analytics_with_google_ads' => sanitize_text_field((isset($postData['link_google_analytics_with_google_ads']) && $postData['link_google_analytics_with_google_ads'] == 'true') ? 1 : 0)
822
  ];
823
+ $args = array(
824
+ 'headers' =>$header,
825
+ 'method' => 'POST',
826
+ 'body' => $data
827
+ );
828
+ $result = $this->tc_wp_remot_call_post(esc_url_raw($url), $args);
829
+ $return = new \stdClass();
830
+ if($result->status == 200){
831
+ $return->status = $result->status;
832
+ $return->data = $result->data;
833
+ $return->error = false;
834
+ return $return;
835
+ }else{
836
+ $return->error = true;
837
+ $return->data = $result->data;
838
+ $return->status = $result->status;
839
+ return $return;
840
+ }
841
  } catch (Exception $e) {
842
  return $e->getMessage();
843
  }
854
  'website_url' => sanitize_text_field($postData['website_url']),
855
  'customer_id' => sanitize_text_field($postData['customer_id'])
856
  ];
857
+ $args = array(
858
+ 'headers' =>$header,
859
+ 'method' => 'POST',
860
+ 'body' => $data
861
+ );
862
+ $result = $this->tc_wp_remot_call_post(esc_url_raw($url), $args);
863
+ $return = new \stdClass();
864
+ if($result->status == 200){
865
+ $return->status = $result->status;
866
+ $return->data = $result->data;
867
+ $return->error = false;
868
+ return $return;
869
+ }else{
870
+ $return->error = true;
871
+ $return->data = $result->data;
872
+ $return->status = $result->status;
873
+ return $return;
874
+ }
875
  } catch (Exception $e) {
876
  return $e->getMessage();
877
  }
880
  public function linkAnalyticToAdsAccount($postData) {
881
  try {
882
  $url = $this->apiDomain . '/google-analytics/link-ads-to-analytics';
883
+ $access_token = base64_decode(sanitize_text_field($this->access_token));
884
+ $refresh_token = base64_decode(sanitize_text_field($this->refresh_token));
885
+ $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken:$access_token", "RefreshToken:$refresh_token");
886
  if ($postData['type'] == "UA") {
887
  $data = [
888
  'type' => sanitize_text_field($postData['type']),
905
  $data = json_encode($data);
906
  $ch = curl_init();
907
  curl_setopt_array($ch, array(
908
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
909
  CURLOPT_RETURNTRANSFER => true,
910
  CURLOPT_TIMEOUT => 0,
911
  CURLOPT_HTTPHEADER => $header,
920
  public function linkGoogleAdsToMerchantCenter($postData) {
921
  try {
922
  $url = $this->apiDomain . '/adwords/link-ads-to-merchant-center';
923
+ $access_token = base64_decode(sanitize_text_field($this->access_token));
924
+ $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json", "AccessToken:$access_token");
925
  $data = [
926
+ 'merchant_id' => sanitize_text_field(($postData['merchant_id']) == 'NewMerchant' ? $this->merchantId: $postData['merchant_id']),
927
  'account_id' => sanitize_text_field($postData['account_id']),
928
  'adwords_id' => sanitize_text_field($postData['adwords_id'])
929
  ];
931
  $data = json_encode($data);
932
  $ch = curl_init();
933
  curl_setopt_array($ch, array(
934
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($curl_url),
935
  CURLOPT_RETURNTRANSFER => true,
936
  CURLOPT_TIMEOUT => 0,
937
  CURLOPT_HTTPHEADER => $header,
951
  'subscription_id' => sanitize_text_field((isset($postData['subscription_id']))?$postData['subscription_id'] : ''),
952
  'setup_end_time' => date('Y-m-d H:i:s')
953
  ];
954
+ $curl_url = $url;
955
  $data = json_encode($data);
956
  $ch = curl_init();
957
  curl_setopt_array($ch, array(
958
+ CURLOPT_URL => esc_url_raw($curl_url), //esc_url($this->curl_url),
959
  CURLOPT_RETURNTRANSFER => true,
960
  CURLOPT_TIMEOUT => 0,
961
  CURLOPT_HTTPHEADER => $this->header,
965
  $this->response = json_decode($this->response);
966
  return $this->response;
967
  } catch (Exception $e) {
968
+ return $e->getMessage();
969
  }
970
  }
971
 
972
  public function getConversionList($data) {
973
  try {
974
  $url = $this->apiDomain . '/google-ads/conversion-list';
975
+ $header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
976
+ $args = array(
977
+ 'headers' =>$header,
978
+ 'method' => 'POST',
979
+ 'body' => $data
980
+ );
981
+ $result = $this->tc_wp_remot_call_post(esc_url_raw($url), $args);
 
 
 
 
 
 
982
  $return = new \stdClass();
983
+ if($result->status == 200){
984
+ $return->status = $result->status;
985
+ $return->data =$result->data;
986
  $return->error = false;
987
+ if(isset($result->data) && count($result->data) > 0){
988
+ $return->message = esc_html__("Google Ads conversion tracking setting success.","conversios");
989
+ }else{
990
+ $response = $this->createConversion($data);
991
  if(isset($response->error) && $response->error == false){
992
  $return->error = false;
993
+ $return->message = esc_html__("Google Ads conversion tracking setting success.","conversios");
994
  }else{
995
  $return->error = true;
996
  $errors = json_decode($response->errors[0]);
997
+ $return->errors = $errors->message;
998
  }
999
+ }
1000
+ return $return;
1001
+ }else{
1002
+ $return->error = true;
1003
+ $return->data = $result->data;
1004
+ $return->status = $result->status;
1005
+ return $return;
1006
  }
 
1007
  } catch (Exception $e) {
1008
  return $e->getMessage();
1009
  }
1017
  'customer_id' => sanitize_text_field((isset($postData['customer_id']))?$postData['customer_id'] : ''),
1018
  'name' => "Order Conversion"
1019
  ];
1020
+ $args = array(
1021
+ 'headers' =>$header,
1022
+ 'method' => 'POST',
1023
+ 'body' => $data
1024
+ );
1025
+ $result = $this->tc_wp_remot_call_post(esc_url_raw($url), $args);
1026
+ $return = new \stdClass();
1027
+ if($result->status == 200){
1028
+ $return->status = $result->status;
1029
+ $return->data = $result->data;
1030
+ $return->error = false;
1031
+ return $return;
1032
+ }else{
1033
+ $return->error = true;
1034
+ $return->data = $result->data;
1035
+ $return->status = $result->status;
1036
+ return $return;
1037
+ }
1038
  } catch (Exception $e) {
1039
  return $e->getMessage();
1040
  }
1044
  . "access_token=" . $access_token;
1045
 
1046
  $ch = curl_init();
1047
+ curl_setopt($ch, CURLOPT_URL, esc_url_raw($request));
1048
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
1049
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
1050
  $response = curl_exec($ch);
admin/js/bootstrap.min.js DELETED
@@ -1,7 +0,0 @@
1
- /*!
2
- * Bootstrap v4.5.1 (https://getbootstrap.com/)
3
- * Copyright 2011-2020 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
4
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5
- */
6
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).bootstrap={},t.jQuery,t.Popper)}(this,(function(t,e,n){"use strict";function i(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function o(t,e,n){return e&&i(t.prototype,e),n&&i(t,n),t}function s(){return(s=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n=arguments[e];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(t[i]=n[i])}return t}).apply(this,arguments)}e=e&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e,n=n&&Object.prototype.hasOwnProperty.call(n,"default")?n.default:n;function r(t){var n=this,i=!1;return e(this).one(a.TRANSITION_END,(function(){i=!0})),setTimeout((function(){i||a.triggerTransitionEnd(n)}),t),this}var a={TRANSITION_END:"bsTransitionEnd",getUID:function(t){do{t+=~~(1e6*Math.random())}while(document.getElementById(t));return t},getSelectorFromElement:function(t){var e=t.getAttribute("data-target");if(!e||"#"===e){var n=t.getAttribute("href");e=n&&"#"!==n?n.trim():""}try{return document.querySelector(e)?e:null}catch(t){return null}},getTransitionDurationFromElement:function(t){if(!t)return 0;var n=e(t).css("transition-duration"),i=e(t).css("transition-delay"),o=parseFloat(n),s=parseFloat(i);return o||s?(n=n.split(",")[0],i=i.split(",")[0],1e3*(parseFloat(n)+parseFloat(i))):0},reflow:function(t){return t.offsetHeight},triggerTransitionEnd:function(t){e(t).trigger("transitionend")},supportsTransitionEnd:function(){return Boolean("transitionend")},isElement:function(t){return(t[0]||t).nodeType},typeCheckConfig:function(t,e,n){for(var i in n)if(Object.prototype.hasOwnProperty.call(n,i)){var o=n[i],s=e[i],r=s&&a.isElement(s)?"element":null===(l=s)||"undefined"==typeof l?""+l:{}.toString.call(l).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(o).test(r))throw new Error(t.toUpperCase()+': Option "'+i+'" provided type "'+r+'" but expected type "'+o+'".')}var l},findShadowRoot:function(t){if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){var e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?a.findShadowRoot(t.parentNode):null},jQueryDetection:function(){if("undefined"==typeof e)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=e.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||t[0]>=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};a.jQueryDetection(),e.fn.emulateTransitionEnd=r,e.event.special[a.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(e(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var l="alert",c=e.fn[l],h=function(){function t(t){this._element=t}var n=t.prototype;return n.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},n.dispose=function(){e.removeData(this._element,"bs.alert"),this._element=null},n._getRootElement=function(t){var n=a.getSelectorFromElement(t),i=!1;return n&&(i=document.querySelector(n)),i||(i=e(t).closest(".alert")[0]),i},n._triggerCloseEvent=function(t){var n=e.Event("close.bs.alert");return e(t).trigger(n),n},n._removeElement=function(t){var n=this;if(e(t).removeClass("show"),e(t).hasClass("fade")){var i=a.getTransitionDurationFromElement(t);e(t).one(a.TRANSITION_END,(function(e){return n._destroyElement(t,e)})).emulateTransitionEnd(i)}else this._destroyElement(t)},n._destroyElement=function(t){e(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.alert");o||(o=new t(this),i.data("bs.alert",o)),"close"===n&&o[n](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}}]),t}();e(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',h._handleDismiss(new h)),e.fn[l]=h._jQueryInterface,e.fn[l].Constructor=h,e.fn[l].noConflict=function(){return e.fn[l]=c,h._jQueryInterface};var u=e.fn.button,d=function(){function t(t){this._element=t}var n=t.prototype;return n.toggle=function(){var t=!0,n=!0,i=e(this._element).closest('[data-toggle="buttons"]')[0];if(i){var o=this._element.querySelector('input:not([type="hidden"])');if(o){if("radio"===o.type)if(o.checked&&this._element.classList.contains("active"))t=!1;else{var s=i.querySelector(".active");s&&e(s).removeClass("active")}t&&("checkbox"!==o.type&&"radio"!==o.type||(o.checked=!this._element.classList.contains("active")),e(o).trigger("change")),o.focus(),n=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(n&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&e(this._element).toggleClass("active"))},n.dispose=function(){e.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.button");i||(i=new t(this),e(this).data("bs.button",i)),"toggle"===n&&i[n]()}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}}]),t}();e(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=t.target,i=n;if(e(n).hasClass("btn")||(n=e(n).closest(".btn")[0]),!n||n.hasAttribute("disabled")||n.classList.contains("disabled"))t.preventDefault();else{var o=n.querySelector('input:not([type="hidden"])');if(o&&(o.hasAttribute("disabled")||o.classList.contains("disabled")))return void t.preventDefault();("LABEL"!==i.tagName||o&&"checkbox"!==o.type)&&d._jQueryInterface.call(e(n),"toggle")}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var n=e(t.target).closest(".btn")[0];e(n).toggleClass("focus",/^focus(in)?$/.test(t.type))})),e(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e<n;e++){var i=t[e],o=i.querySelector('input:not([type="hidden"])');o.checked||o.hasAttribute("checked")?i.classList.add("active"):i.classList.remove("active")}for(var s=0,r=(t=[].slice.call(document.querySelectorAll('[data-toggle="button"]'))).length;s<r;s++){var a=t[s];"true"===a.getAttribute("aria-pressed")?a.classList.add("active"):a.classList.remove("active")}})),e.fn.button=d._jQueryInterface,e.fn.button.Constructor=d,e.fn.button.noConflict=function(){return e.fn.button=u,d._jQueryInterface};var f="carousel",g=".bs.carousel",m=e.fn[f],p={interval:5e3,keyboard:!0,slide:!1,pause:"hover",wrap:!0,touch:!0},_={interval:"(number|boolean)",keyboard:"boolean",slide:"(boolean|string)",pause:"(string|boolean)",wrap:"boolean",touch:"boolean"},v={TOUCH:"touch",PEN:"pen"},b=function(){function t(t,e){this._items=null,this._interval=null,this._activeElement=null,this._isPaused=!1,this._isSliding=!1,this.touchTimeout=null,this.touchStartX=0,this.touchDeltaX=0,this._config=this._getConfig(e),this._element=t,this._indicatorsElement=this._element.querySelector(".carousel-indicators"),this._touchSupported="ontouchstart"in document.documentElement||navigator.maxTouchPoints>0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var n=t.prototype;return n.next=function(){this._isSliding||this._slide("next")},n.nextWhenVisible=function(){!document.hidden&&e(this._element).is(":visible")&&"hidden"!==e(this._element).css("visibility")&&this.next()},n.prev=function(){this._isSliding||this._slide("prev")},n.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(a.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},n.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},n.to=function(t){var n=this;this._activeElement=this._element.querySelector(".active.carousel-item");var i=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)e(this._element).one("slid.bs.carousel",(function(){return n.to(t)}));else{if(i===t)return this.pause(),void this.cycle();var o=t>i?"next":"prev";this._slide(o,this._items[t])}},n.dispose=function(){e(this._element).off(g),e.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},n._getConfig=function(t){return t=s({},p,t),a.typeCheckConfig(f,t,_),t},n._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},n._addEventListeners=function(){var t=this;this._config.keyboard&&e(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&e(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},n._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var n=function(e){t._pointerEvent&&v[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},i=function(e){t._pointerEvent&&v[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};e(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(e(this._element).on("pointerdown.bs.carousel",(function(t){return n(t)})),e(this._element).on("pointerup.bs.carousel",(function(t){return i(t)})),this._element.classList.add("pointer-event")):(e(this._element).on("touchstart.bs.carousel",(function(t){return n(t)})),e(this._element).on("touchmove.bs.carousel",(function(e){return function(e){e.originalEvent.touches&&e.originalEvent.touches.length>1?t.touchDeltaX=0:t.touchDeltaX=e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),e(this._element).on("touchend.bs.carousel",(function(t){return i(t)})))}},n._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},n._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},n._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),s=this._items.length-1;if((i&&0===o||n&&o===s)&&!this._config.wrap)return e;var r=(o+("prev"===t?-1:1))%this._items.length;return-1===r?this._items[this._items.length-1]:this._items[r]},n._triggerSlideEvent=function(t,n){var i=this._getItemIndex(t),o=this._getItemIndex(this._element.querySelector(".active.carousel-item")),s=e.Event("slide.bs.carousel",{relatedTarget:t,direction:n,from:o,to:i});return e(this._element).trigger(s),s},n._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var n=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));e(n).removeClass("active");var i=this._indicatorsElement.children[this._getItemIndex(t)];i&&e(i).addClass("active")}},n._slide=function(t,n){var i,o,s,r=this,l=this._element.querySelector(".active.carousel-item"),c=this._getItemIndex(l),h=n||l&&this._getItemByDirection(t,l),u=this._getItemIndex(h),d=Boolean(this._interval);if("next"===t?(i="carousel-item-left",o="carousel-item-next",s="left"):(i="carousel-item-right",o="carousel-item-prev",s="right"),h&&e(h).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(h,s).isDefaultPrevented()&&l&&h){this._isSliding=!0,d&&this.pause(),this._setActiveIndicatorElement(h);var f=e.Event("slid.bs.carousel",{relatedTarget:h,direction:s,from:c,to:u});if(e(this._element).hasClass("slide")){e(h).addClass(o),a.reflow(h),e(l).addClass(i),e(h).addClass(i);var g=parseInt(h.getAttribute("data-interval"),10);g?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=g):this._config.interval=this._config.defaultInterval||this._config.interval;var m=a.getTransitionDurationFromElement(l);e(l).one(a.TRANSITION_END,(function(){e(h).removeClass(i+" "+o).addClass("active"),e(l).removeClass("active "+o+" "+i),r._isSliding=!1,setTimeout((function(){return e(r._element).trigger(f)}),0)})).emulateTransitionEnd(m)}else e(l).removeClass("active"),e(h).addClass("active"),this._isSliding=!1,e(this._element).trigger(f);d&&this.cycle()}},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.carousel"),o=s({},p,e(this).data());"object"==typeof n&&(o=s({},o,n));var r="string"==typeof n?n:o.slide;if(i||(i=new t(this,o),e(this).data("bs.carousel",i)),"number"==typeof n)i.to(n);else if("string"==typeof r){if("undefined"==typeof i[r])throw new TypeError('No method named "'+r+'"');i[r]()}else o.interval&&o.ride&&(i.pause(),i.cycle())}))},t._dataApiClickHandler=function(n){var i=a.getSelectorFromElement(this);if(i){var o=e(i)[0];if(o&&e(o).hasClass("carousel")){var r=s({},e(o).data(),e(this).data()),l=this.getAttribute("data-slide-to");l&&(r.interval=!1),t._jQueryInterface.call(e(o),r),l&&e(o).data("bs.carousel").to(l),n.preventDefault()}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return p}}]),t}();e(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",b._dataApiClickHandler),e(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),n=0,i=t.length;n<i;n++){var o=e(t[n]);b._jQueryInterface.call(o,o.data())}})),e.fn[f]=b._jQueryInterface,e.fn[f].Constructor=b,e.fn[f].noConflict=function(){return e.fn[f]=m,b._jQueryInterface};var y="collapse",E=e.fn[y],w={toggle:!0,parent:""},T={toggle:"boolean",parent:"(string|element)"},C=function(){function t(t,e){this._isTransitioning=!1,this._element=t,this._config=this._getConfig(e),this._triggerArray=[].slice.call(document.querySelectorAll('[data-toggle="collapse"][href="#'+t.id+'"],[data-toggle="collapse"][data-target="#'+t.id+'"]'));for(var n=[].slice.call(document.querySelectorAll('[data-toggle="collapse"]')),i=0,o=n.length;i<o;i++){var s=n[i],r=a.getSelectorFromElement(s),l=[].slice.call(document.querySelectorAll(r)).filter((function(e){return e===t}));null!==r&&l.length>0&&(this._selector=r,this._triggerArray.push(s))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var n=t.prototype;return n.toggle=function(){e(this._element).hasClass("show")?this.hide():this.show()},n.show=function(){var n,i,o=this;if(!this._isTransitioning&&!e(this._element).hasClass("show")&&(this._parent&&0===(n=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof o._config.parent?t.getAttribute("data-parent")===o._config.parent:t.classList.contains("collapse")}))).length&&(n=null),!(n&&(i=e(n).not(this._selector).data("bs.collapse"))&&i._isTransitioning))){var s=e.Event("show.bs.collapse");if(e(this._element).trigger(s),!s.isDefaultPrevented()){n&&(t._jQueryInterface.call(e(n).not(this._selector),"hide"),i||e(n).data("bs.collapse",null));var r=this._getDimension();e(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[r]=0,this._triggerArray.length&&e(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var l="scroll"+(r[0].toUpperCase()+r.slice(1)),c=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,(function(){e(o._element).removeClass("collapsing").addClass("collapse show"),o._element.style[r]="",o.setTransitioning(!1),e(o._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(c),this._element.style[r]=this._element[l]+"px"}}},n.hide=function(){var t=this;if(!this._isTransitioning&&e(this._element).hasClass("show")){var n=e.Event("hide.bs.collapse");if(e(this._element).trigger(n),!n.isDefaultPrevented()){var i=this._getDimension();this._element.style[i]=this._element.getBoundingClientRect()[i]+"px",a.reflow(this._element),e(this._element).addClass("collapsing").removeClass("collapse show");var o=this._triggerArray.length;if(o>0)for(var s=0;s<o;s++){var r=this._triggerArray[s],l=a.getSelectorFromElement(r);if(null!==l)e([].slice.call(document.querySelectorAll(l))).hasClass("show")||e(r).addClass("collapsed").attr("aria-expanded",!1)}this.setTransitioning(!0);this._element.style[i]="";var c=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,(function(){t.setTransitioning(!1),e(t._element).removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")})).emulateTransitionEnd(c)}}},n.setTransitioning=function(t){this._isTransitioning=t},n.dispose=function(){e.removeData(this._element,"bs.collapse"),this._config=null,this._parent=null,this._element=null,this._triggerArray=null,this._isTransitioning=null},n._getConfig=function(t){return(t=s({},w,t)).toggle=Boolean(t.toggle),a.typeCheckConfig(y,t,T),t},n._getDimension=function(){return e(this._element).hasClass("width")?"width":"height"},n._getParent=function(){var n,i=this;a.isElement(this._config.parent)?(n=this._config.parent,"undefined"!=typeof this._config.parent.jquery&&(n=this._config.parent[0])):n=document.querySelector(this._config.parent);var o='[data-toggle="collapse"][data-parent="'+this._config.parent+'"]',s=[].slice.call(n.querySelectorAll(o));return e(s).each((function(e,n){i._addAriaAndCollapsedClass(t._getTargetFromElement(n),[n])})),n},n._addAriaAndCollapsedClass=function(t,n){var i=e(t).hasClass("show");n.length&&e(n).toggleClass("collapsed",!i).attr("aria-expanded",i)},t._getTargetFromElement=function(t){var e=a.getSelectorFromElement(t);return e?document.querySelector(e):null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.collapse"),r=s({},w,i.data(),"object"==typeof n&&n?n:{});if(!o&&r.toggle&&"string"==typeof n&&/show|hide/.test(n)&&(r.toggle=!1),o||(o=new t(this,r),i.data("bs.collapse",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return w}}]),t}();e(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',(function(t){"A"===t.currentTarget.tagName&&t.preventDefault();var n=e(this),i=a.getSelectorFromElement(this),o=[].slice.call(document.querySelectorAll(i));e(o).each((function(){var t=e(this),i=t.data("bs.collapse")?"toggle":n.data();C._jQueryInterface.call(t,i)}))})),e.fn[y]=C._jQueryInterface,e.fn[y].Constructor=C,e.fn[y].noConflict=function(){return e.fn[y]=E,C._jQueryInterface};var S="dropdown",k=e.fn[S],D=new RegExp("38|40|27"),N={offset:0,flip:!0,boundary:"scrollParent",reference:"toggle",display:"dynamic",popperConfig:null},A={offset:"(number|string|function)",flip:"boolean",boundary:"(string|element)",reference:"(string|element)",display:"string",popperConfig:"(null|object)"},I=function(){function t(t,e){this._element=t,this._popper=null,this._config=this._getConfig(e),this._menu=this._getMenuElement(),this._inNavbar=this._detectNavbar(),this._addEventListeners()}var i=t.prototype;return i.toggle=function(){if(!this._element.disabled&&!e(this._element).hasClass("disabled")){var n=e(this._menu).hasClass("show");t._clearMenus(),n||this.show(!0)}},i.show=function(i){if(void 0===i&&(i=!1),!(this._element.disabled||e(this._element).hasClass("disabled")||e(this._menu).hasClass("show"))){var o={relatedTarget:this._element},s=e.Event("show.bs.dropdown",o),r=t._getParentFromElement(this._element);if(e(r).trigger(s),!s.isDefaultPrevented()){if(!this._inNavbar&&i){if("undefined"==typeof n)throw new TypeError("Bootstrap's dropdowns require Popper.js (https://popper.js.org/)");var l=this._element;"parent"===this._config.reference?l=r:a.isElement(this._config.reference)&&(l=this._config.reference,"undefined"!=typeof this._config.reference.jquery&&(l=this._config.reference[0])),"scrollParent"!==this._config.boundary&&e(r).addClass("position-static"),this._popper=new n(l,this._menu,this._getPopperConfig())}"ontouchstart"in document.documentElement&&0===e(r).closest(".navbar-nav").length&&e(document.body).children().on("mouseover",null,e.noop),this._element.focus(),this._element.setAttribute("aria-expanded",!0),e(this._menu).toggleClass("show"),e(r).toggleClass("show").trigger(e.Event("shown.bs.dropdown",o))}}},i.hide=function(){if(!this._element.disabled&&!e(this._element).hasClass("disabled")&&e(this._menu).hasClass("show")){var n={relatedTarget:this._element},i=e.Event("hide.bs.dropdown",n),o=t._getParentFromElement(this._element);e(o).trigger(i),i.isDefaultPrevented()||(this._popper&&this._popper.destroy(),e(this._menu).toggleClass("show"),e(o).toggleClass("show").trigger(e.Event("hidden.bs.dropdown",n)))}},i.dispose=function(){e.removeData(this._element,"bs.dropdown"),e(this._element).off(".bs.dropdown"),this._element=null,this._menu=null,null!==this._popper&&(this._popper.destroy(),this._popper=null)},i.update=function(){this._inNavbar=this._detectNavbar(),null!==this._popper&&this._popper.scheduleUpdate()},i._addEventListeners=function(){var t=this;e(this._element).on("click.bs.dropdown",(function(e){e.preventDefault(),e.stopPropagation(),t.toggle()}))},i._getConfig=function(t){return t=s({},this.constructor.Default,e(this._element).data(),t),a.typeCheckConfig(S,t,this.constructor.DefaultType),t},i._getMenuElement=function(){if(!this._menu){var e=t._getParentFromElement(this._element);e&&(this._menu=e.querySelector(".dropdown-menu"))}return this._menu},i._getPlacement=function(){var t=e(this._element.parentNode),n="bottom-start";return t.hasClass("dropup")?n=e(this._menu).hasClass("dropdown-menu-right")?"top-end":"top-start":t.hasClass("dropright")?n="right-start":t.hasClass("dropleft")?n="left-start":e(this._menu).hasClass("dropdown-menu-right")&&(n="bottom-end"),n},i._detectNavbar=function(){return e(this._element).closest(".navbar").length>0},i._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=s({},e.offsets,t._config.offset(e.offsets,t._element)||{}),e}:e.offset=this._config.offset,e},i._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),s({},t,this._config.popperConfig)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.dropdown");if(i||(i=new t(this,"object"==typeof n?n:null),e(this).data("bs.dropdown",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},t._clearMenus=function(n){if(!n||3!==n.which&&("keyup"!==n.type||9===n.which))for(var i=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),o=0,s=i.length;o<s;o++){var r=t._getParentFromElement(i[o]),a=e(i[o]).data("bs.dropdown"),l={relatedTarget:i[o]};if(n&&"click"===n.type&&(l.clickEvent=n),a){var c=a._menu;if(e(r).hasClass("show")&&!(n&&("click"===n.type&&/input|textarea/i.test(n.target.tagName)||"keyup"===n.type&&9===n.which)&&e.contains(r,n.target))){var h=e.Event("hide.bs.dropdown",l);e(r).trigger(h),h.isDefaultPrevented()||("ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),i[o].setAttribute("aria-expanded","false"),a._popper&&a._popper.destroy(),e(c).removeClass("show"),e(r).removeClass("show").trigger(e.Event("hidden.bs.dropdown",l)))}}}},t._getParentFromElement=function(t){var e,n=a.getSelectorFromElement(t);return n&&(e=document.querySelector(n)),e||t.parentNode},t._dataApiKeydownHandler=function(n){if(!(/input|textarea/i.test(n.target.tagName)?32===n.which||27!==n.which&&(40!==n.which&&38!==n.which||e(n.target).closest(".dropdown-menu").length):!D.test(n.which))&&!this.disabled&&!e(this).hasClass("disabled")){var i=t._getParentFromElement(this),o=e(i).hasClass("show");if(o||27!==n.which){if(n.preventDefault(),n.stopPropagation(),!o||o&&(27===n.which||32===n.which))return 27===n.which&&e(i.querySelector('[data-toggle="dropdown"]')).trigger("focus"),void e(this).trigger("click");var s=[].slice.call(i.querySelectorAll(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)")).filter((function(t){return e(t).is(":visible")}));if(0!==s.length){var r=s.indexOf(n.target);38===n.which&&r>0&&r--,40===n.which&&r<s.length-1&&r++,r<0&&(r=0),s[r].focus()}}}},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return N}},{key:"DefaultType",get:function(){return A}}]),t}();e(document).on("keydown.bs.dropdown.data-api",'[data-toggle="dropdown"]',I._dataApiKeydownHandler).on("keydown.bs.dropdown.data-api",".dropdown-menu",I._dataApiKeydownHandler).on("click.bs.dropdown.data-api keyup.bs.dropdown.data-api",I._clearMenus).on("click.bs.dropdown.data-api",'[data-toggle="dropdown"]',(function(t){t.preventDefault(),t.stopPropagation(),I._jQueryInterface.call(e(this),"toggle")})).on("click.bs.dropdown.data-api",".dropdown form",(function(t){t.stopPropagation()})),e.fn[S]=I._jQueryInterface,e.fn[S].Constructor=I,e.fn[S].noConflict=function(){return e.fn[S]=k,I._jQueryInterface};var O=e.fn.modal,j={backdrop:!0,keyboard:!0,focus:!0,show:!0},x={backdrop:"(boolean|string)",keyboard:"boolean",focus:"boolean",show:"boolean"},P=function(){function t(t,e){this._config=this._getConfig(e),this._element=t,this._dialog=t.querySelector(".modal-dialog"),this._backdrop=null,this._isShown=!1,this._isBodyOverflowing=!1,this._ignoreBackdropClick=!1,this._isTransitioning=!1,this._scrollbarWidth=0}var n=t.prototype;return n.toggle=function(t){return this._isShown?this.hide():this.show(t)},n.show=function(t){var n=this;if(!this._isShown&&!this._isTransitioning){e(this._element).hasClass("fade")&&(this._isTransitioning=!0);var i=e.Event("show.bs.modal",{relatedTarget:t});e(this._element).trigger(i),this._isShown||i.isDefaultPrevented()||(this._isShown=!0,this._checkScrollbar(),this._setScrollbar(),this._adjustDialog(),this._setEscapeEvent(),this._setResizeEvent(),e(this._element).on("click.dismiss.bs.modal",'[data-dismiss="modal"]',(function(t){return n.hide(t)})),e(this._dialog).on("mousedown.dismiss.bs.modal",(function(){e(n._element).one("mouseup.dismiss.bs.modal",(function(t){e(t.target).is(n._element)&&(n._ignoreBackdropClick=!0)}))})),this._showBackdrop((function(){return n._showElement(t)})))}},n.hide=function(t){var n=this;if(t&&t.preventDefault(),this._isShown&&!this._isTransitioning){var i=e.Event("hide.bs.modal");if(e(this._element).trigger(i),this._isShown&&!i.isDefaultPrevented()){this._isShown=!1;var o=e(this._element).hasClass("fade");if(o&&(this._isTransitioning=!0),this._setEscapeEvent(),this._setResizeEvent(),e(document).off("focusin.bs.modal"),e(this._element).removeClass("show"),e(this._element).off("click.dismiss.bs.modal"),e(this._dialog).off("mousedown.dismiss.bs.modal"),o){var s=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,(function(t){return n._hideModal(t)})).emulateTransitionEnd(s)}else this._hideModal()}}},n.dispose=function(){[window,this._element,this._dialog].forEach((function(t){return e(t).off(".bs.modal")})),e(document).off("focusin.bs.modal"),e.removeData(this._element,"bs.modal"),this._config=null,this._element=null,this._dialog=null,this._backdrop=null,this._isShown=null,this._isBodyOverflowing=null,this._ignoreBackdropClick=null,this._isTransitioning=null,this._scrollbarWidth=null},n.handleUpdate=function(){this._adjustDialog()},n._getConfig=function(t){return t=s({},j,t),a.typeCheckConfig("modal",t,x),t},n._triggerBackdropTransition=function(){var t=this;if("static"===this._config.backdrop){var n=e.Event("hidePrevented.bs.modal");if(e(this._element).trigger(n),n.defaultPrevented)return;var i=this._element.scrollHeight>document.documentElement.clientHeight;i||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var o=a.getTransitionDurationFromElement(this._dialog);e(this._element).off(a.TRANSITION_END),e(this._element).one(a.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),i||e(t._element).one(a.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,o)})).emulateTransitionEnd(o),this._element.focus()}else this.hide()},n._showElement=function(t){var n=this,i=e(this._element).hasClass("fade"),o=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),e(this._dialog).hasClass("modal-dialog-scrollable")&&o?o.scrollTop=0:this._element.scrollTop=0,i&&a.reflow(this._element),e(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var s=e.Event("shown.bs.modal",{relatedTarget:t}),r=function(){n._config.focus&&n._element.focus(),n._isTransitioning=!1,e(n._element).trigger(s)};if(i){var l=a.getTransitionDurationFromElement(this._dialog);e(this._dialog).one(a.TRANSITION_END,r).emulateTransitionEnd(l)}else r()},n._enforceFocus=function(){var t=this;e(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(n){document!==n.target&&t._element!==n.target&&0===e(t._element).has(n.target).length&&t._element.focus()}))},n._setEscapeEvent=function(){var t=this;this._isShown?e(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||e(this._element).off("keydown.dismiss.bs.modal")},n._setResizeEvent=function(){var t=this;this._isShown?e(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):e(window).off("resize.bs.modal")},n._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){e(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),e(t._element).trigger("hidden.bs.modal")}))},n._removeBackdrop=function(){this._backdrop&&(e(this._backdrop).remove(),this._backdrop=null)},n._showBackdrop=function(t){var n=this,i=e(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",i&&this._backdrop.classList.add(i),e(this._backdrop).appendTo(document.body),e(this._element).on("click.dismiss.bs.modal",(function(t){n._ignoreBackdropClick?n._ignoreBackdropClick=!1:t.target===t.currentTarget&&n._triggerBackdropTransition()})),i&&a.reflow(this._backdrop),e(this._backdrop).addClass("show"),!t)return;if(!i)return void t();var o=a.getTransitionDurationFromElement(this._backdrop);e(this._backdrop).one(a.TRANSITION_END,t).emulateTransitionEnd(o)}else if(!this._isShown&&this._backdrop){e(this._backdrop).removeClass("show");var s=function(){n._removeBackdrop(),t&&t()};if(e(this._element).hasClass("fade")){var r=a.getTransitionDurationFromElement(this._backdrop);e(this._backdrop).one(a.TRANSITION_END,s).emulateTransitionEnd(r)}else s()}else t&&t()},n._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},n._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},n._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)<window.innerWidth,this._scrollbarWidth=this._getScrollbarWidth()},n._setScrollbar=function(){var t=this;if(this._isBodyOverflowing){var n=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top")),i=[].slice.call(document.querySelectorAll(".sticky-top"));e(n).each((function(n,i){var o=i.style.paddingRight,s=e(i).css("padding-right");e(i).data("padding-right",o).css("padding-right",parseFloat(s)+t._scrollbarWidth+"px")})),e(i).each((function(n,i){var o=i.style.marginRight,s=e(i).css("margin-right");e(i).data("margin-right",o).css("margin-right",parseFloat(s)-t._scrollbarWidth+"px")}));var o=document.body.style.paddingRight,s=e(document.body).css("padding-right");e(document.body).data("padding-right",o).css("padding-right",parseFloat(s)+this._scrollbarWidth+"px")}e(document.body).addClass("modal-open")},n._resetScrollbar=function(){var t=[].slice.call(document.querySelectorAll(".fixed-top, .fixed-bottom, .is-fixed, .sticky-top"));e(t).each((function(t,n){var i=e(n).data("padding-right");e(n).removeData("padding-right"),n.style.paddingRight=i||""}));var n=[].slice.call(document.querySelectorAll(".sticky-top"));e(n).each((function(t,n){var i=e(n).data("margin-right");"undefined"!=typeof i&&e(n).css("margin-right",i).removeData("margin-right")}));var i=e(document.body).data("padding-right");e(document.body).removeData("padding-right"),document.body.style.paddingRight=i||""},n._getScrollbarWidth=function(){var t=document.createElement("div");t.className="modal-scrollbar-measure",document.body.appendChild(t);var e=t.getBoundingClientRect().width-t.clientWidth;return document.body.removeChild(t),e},t._jQueryInterface=function(n,i){return this.each((function(){var o=e(this).data("bs.modal"),r=s({},j,e(this).data(),"object"==typeof n&&n?n:{});if(o||(o=new t(this,r),e(this).data("bs.modal",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](i)}else r.show&&o.show(i)}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return j}}]),t}();e(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',(function(t){var n,i=this,o=a.getSelectorFromElement(this);o&&(n=document.querySelector(o));var r=e(n).data("bs.modal")?"toggle":s({},e(n).data(),e(this).data());"A"!==this.tagName&&"AREA"!==this.tagName||t.preventDefault();var l=e(n).one("show.bs.modal",(function(t){t.isDefaultPrevented()||l.one("hidden.bs.modal",(function(){e(i).is(":visible")&&i.focus()}))}));P._jQueryInterface.call(e(n),r,this)})),e.fn.modal=P._jQueryInterface,e.fn.modal.Constructor=P,e.fn.modal.noConflict=function(){return e.fn.modal=O,P._jQueryInterface};var R=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],L={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},q=/^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi,F=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;function Q(t,e,n){if(0===t.length)return t;if(n&&"function"==typeof n)return n(t);for(var i=(new window.DOMParser).parseFromString(t,"text/html"),o=Object.keys(e),s=[].slice.call(i.body.querySelectorAll("*")),r=function(t,n){var i=s[t],r=i.nodeName.toLowerCase();if(-1===o.indexOf(i.nodeName.toLowerCase()))return i.parentNode.removeChild(i),"continue";var a=[].slice.call(i.attributes),l=[].concat(e["*"]||[],e[r]||[]);a.forEach((function(t){(function(t,e){var n=t.nodeName.toLowerCase();if(-1!==e.indexOf(n))return-1===R.indexOf(n)||Boolean(t.nodeValue.match(q)||t.nodeValue.match(F));for(var i=e.filter((function(t){return t instanceof RegExp})),o=0,s=i.length;o<s;o++)if(n.match(i[o]))return!0;return!1})(t,l)||i.removeAttribute(t.nodeName)}))},a=0,l=s.length;a<l;a++)r(a);return i.body.innerHTML}var B="tooltip",H=e.fn[B],U=new RegExp("(^|\\s)bs-tooltip\\S+","g"),M=["sanitize","whiteList","sanitizeFn"],W={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},V={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"},z={animation:!0,template:'<div class="tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:L,popperConfig:null},K={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},X=function(){function t(t,e){if("undefined"==typeof n)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var i=t.prototype;return i.enable=function(){this._isEnabled=!0},i.disable=function(){this._isEnabled=!1},i.toggleEnabled=function(){this._isEnabled=!this._isEnabled},i.toggle=function(t){if(this._isEnabled)if(t){var n=this.constructor.DATA_KEY,i=e(t.currentTarget).data(n);i||(i=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(n,i)),i._activeTrigger.click=!i._activeTrigger.click,i._isWithActiveTrigger()?i._enter(null,i):i._leave(null,i)}else{if(e(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},i.dispose=function(){clearTimeout(this._timeout),e.removeData(this.element,this.constructor.DATA_KEY),e(this.element).off(this.constructor.EVENT_KEY),e(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&e(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},i.show=function(){var t=this;if("none"===e(this.element).css("display"))throw new Error("Please use show on visible elements");var i=e.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){e(this.element).trigger(i);var o=a.findShadowRoot(this.element),s=e.contains(null!==o?o:this.element.ownerDocument.documentElement,this.element);if(i.isDefaultPrevented()||!s)return;var r=this.getTipElement(),l=a.getUID(this.constructor.NAME);r.setAttribute("id",l),this.element.setAttribute("aria-describedby",l),this.setContent(),this.config.animation&&e(r).addClass("fade");var c="function"==typeof this.config.placement?this.config.placement.call(this,r,this.element):this.config.placement,h=this._getAttachment(c);this.addAttachmentClass(h);var u=this._getContainer();e(r).data(this.constructor.DATA_KEY,this),e.contains(this.element.ownerDocument.documentElement,this.tip)||e(r).appendTo(u),e(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new n(this.element,r,this._getPopperConfig(h)),e(r).addClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().on("mouseover",null,e.noop);var d=function(){t.config.animation&&t._fixTransition();var n=t._hoverState;t._hoverState=null,e(t.element).trigger(t.constructor.Event.SHOWN),"out"===n&&t._leave(null,t)};if(e(this.tip).hasClass("fade")){var f=a.getTransitionDurationFromElement(this.tip);e(this.tip).one(a.TRANSITION_END,d).emulateTransitionEnd(f)}else d()}},i.hide=function(t){var n=this,i=this.getTipElement(),o=e.Event(this.constructor.Event.HIDE),s=function(){"show"!==n._hoverState&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),e(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()};if(e(this.element).trigger(o),!o.isDefaultPrevented()){if(e(i).removeClass("show"),"ontouchstart"in document.documentElement&&e(document.body).children().off("mouseover",null,e.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,e(this.tip).hasClass("fade")){var r=a.getTransitionDurationFromElement(i);e(i).one(a.TRANSITION_END,s).emulateTransitionEnd(r)}else s();this._hoverState=""}},i.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},i.isWithContent=function(){return Boolean(this.getTitle())},i.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-tooltip-"+t)},i.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},i.setContent=function(){var t=this.getTipElement();this.setElementContent(e(t.querySelectorAll(".tooltip-inner")),this.getTitle()),e(t).removeClass("fade show")},i.setElementContent=function(t,n){"object"!=typeof n||!n.nodeType&&!n.jquery?this.config.html?(this.config.sanitize&&(n=Q(n,this.config.whiteList,this.config.sanitizeFn)),t.html(n)):t.text(n):this.config.html?e(n).parent().is(t)||t.empty().append(n):t.text(e(n).text())},i.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},i._getPopperConfig=function(t){var e=this;return s({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},i._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=s({},e.offsets,t.config.offset(e.offsets,t.element)||{}),e}:e.offset=this.config.offset,e},i._getContainer=function(){return!1===this.config.container?document.body:a.isElement(this.config.container)?e(this.config.container):e(document).find(this.config.container)},i._getAttachment=function(t){return V[t.toUpperCase()]},i._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(n){if("click"===n)e(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==n){var i="hover"===n?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,o="hover"===n?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;e(t.element).on(i,t.config.selector,(function(e){return t._enter(e)})).on(o,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},e(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=s({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},i._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},i._enter=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),e(n.getTipElement()).hasClass("show")||"show"===n._hoverState?n._hoverState="show":(clearTimeout(n._timeout),n._hoverState="show",n.config.delay&&n.config.delay.show?n._timeout=setTimeout((function(){"show"===n._hoverState&&n.show()}),n.config.delay.show):n.show())},i._leave=function(t,n){var i=this.constructor.DATA_KEY;(n=n||e(t.currentTarget).data(i))||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),e(t.currentTarget).data(i,n)),t&&(n._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),n._isWithActiveTrigger()||(clearTimeout(n._timeout),n._hoverState="out",n.config.delay&&n.config.delay.hide?n._timeout=setTimeout((function(){"out"===n._hoverState&&n.hide()}),n.config.delay.hide):n.hide())},i._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},i._getConfig=function(t){var n=e(this.element).data();return Object.keys(n).forEach((function(t){-1!==M.indexOf(t)&&delete n[t]})),"number"==typeof(t=s({},this.constructor.Default,n,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),a.typeCheckConfig(B,t,this.constructor.DefaultType),t.sanitize&&(t.template=Q(t.template,t.whiteList,t.sanitizeFn)),t},i._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},i._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(U);null!==n&&n.length&&t.removeClass(n.join(""))},i._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},i._fixTransition=function(){var t=this.getTipElement(),n=this.config.animation;null===t.getAttribute("x-placement")&&(e(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=n)},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.tooltip"),o="object"==typeof n&&n;if((i||!/dispose|hide/.test(n))&&(i||(i=new t(this,o),e(this).data("bs.tooltip",i)),"string"==typeof n)){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return z}},{key:"NAME",get:function(){return B}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return K}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return W}}]),t}();e.fn[B]=X._jQueryInterface,e.fn[B].Constructor=X,e.fn[B].noConflict=function(){return e.fn[B]=H,X._jQueryInterface};var Y="popover",$=e.fn[Y],J=new RegExp("(^|\\s)bs-popover\\S+","g"),G=s({},X.Default,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'}),Z=s({},X.DefaultType,{content:"(string|element|function)"}),tt={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},et=function(t){var n,i;function s(){return t.apply(this,arguments)||this}i=t,(n=s).prototype=Object.create(i.prototype),n.prototype.constructor=n,n.__proto__=i;var r=s.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){e(this.getTipElement()).addClass("bs-popover-"+t)},r.getTipElement=function(){return this.tip=this.tip||e(this.config.template)[0],this.tip},r.setContent=function(){var t=e(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var n=this._getContent();"function"==typeof n&&(n=n.call(this.element)),this.setElementContent(t.find(".popover-body"),n),t.removeClass("fade show")},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=e(this.getTipElement()),n=t.attr("class").match(J);null!==n&&n.length>0&&t.removeClass(n.join(""))},s._jQueryInterface=function(t){return this.each((function(){var n=e(this).data("bs.popover"),i="object"==typeof t?t:null;if((n||!/dispose|hide/.test(t))&&(n||(n=new s(this,i),e(this).data("bs.popover",n)),"string"==typeof t)){if("undefined"==typeof n[t])throw new TypeError('No method named "'+t+'"');n[t]()}}))},o(s,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return G}},{key:"NAME",get:function(){return Y}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return tt}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return Z}}]),s}(X);e.fn[Y]=et._jQueryInterface,e.fn[Y].Constructor=et,e.fn[Y].noConflict=function(){return e.fn[Y]=$,et._jQueryInterface};var nt="scrollspy",it=e.fn[nt],ot={offset:10,method:"auto",target:""},st={offset:"number",method:"string",target:"(string|element)"},rt=function(){function t(t,n){var i=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(n),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,e(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return i._process(t)})),this.refresh(),this._process()}var n=t.prototype;return n.refresh=function(){var t=this,n=this._scrollElement===this._scrollElement.window?"offset":"position",i="auto"===this._config.method?n:this._config.method,o="position"===i?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var n,s=a.getSelectorFromElement(t);if(s&&(n=document.querySelector(s)),n){var r=n.getBoundingClientRect();if(r.width||r.height)return[e(n)[i]().top+o,s]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},n.dispose=function(){e.removeData(this._element,"bs.scrollspy"),e(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},n._getConfig=function(t){if("string"!=typeof(t=s({},ot,"object"==typeof t&&t?t:{})).target&&a.isElement(t.target)){var n=e(t.target).attr("id");n||(n=a.getUID(nt),e(t.target).attr("id",n)),t.target="#"+n}return a.typeCheckConfig(nt,t,st),t},n._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},n._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},n._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},n._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t<this._offsets[0]&&this._offsets[0]>0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;){this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||t<this._offsets[o+1])&&this._activate(this._targets[o])}}},n._activate=function(t){this._activeTarget=t,this._clear();var n=this._selector.split(",").map((function(e){return e+'[data-target="'+t+'"],'+e+'[href="'+t+'"]'})),i=e([].slice.call(document.querySelectorAll(n.join(","))));i.hasClass("dropdown-item")?(i.closest(".dropdown").find(".dropdown-toggle").addClass("active"),i.addClass("active")):(i.addClass("active"),i.parents(".nav, .list-group").prev(".nav-link, .list-group-item").addClass("active"),i.parents(".nav, .list-group").prev(".nav-item").children(".nav-link").addClass("active")),e(this._scrollElement).trigger("activate.bs.scrollspy",{relatedTarget:t})},n._clear=function(){[].slice.call(document.querySelectorAll(this._selector)).filter((function(t){return t.classList.contains("active")})).forEach((function(t){return t.classList.remove("active")}))},t._jQueryInterface=function(n){return this.each((function(){var i=e(this).data("bs.scrollspy");if(i||(i=new t(this,"object"==typeof n&&n),e(this).data("bs.scrollspy",i)),"string"==typeof n){if("undefined"==typeof i[n])throw new TypeError('No method named "'+n+'"');i[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"Default",get:function(){return ot}}]),t}();e(window).on("load.bs.scrollspy.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-spy="scroll"]')),n=t.length;n--;){var i=e(t[n]);rt._jQueryInterface.call(i,i.data())}})),e.fn[nt]=rt._jQueryInterface,e.fn[nt].Constructor=rt,e.fn[nt].noConflict=function(){return e.fn[nt]=it,rt._jQueryInterface};var at=e.fn.tab,lt=function(){function t(t){this._element=t}var n=t.prototype;return n.show=function(){var t=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&e(this._element).hasClass("active")||e(this._element).hasClass("disabled"))){var n,i,o=e(this._element).closest(".nav, .list-group")[0],s=a.getSelectorFromElement(this._element);if(o){var r="UL"===o.nodeName||"OL"===o.nodeName?"> li > .active":".active";i=(i=e.makeArray(e(o).find(r)))[i.length-1]}var l=e.Event("hide.bs.tab",{relatedTarget:this._element}),c=e.Event("show.bs.tab",{relatedTarget:i});if(i&&e(i).trigger(l),e(this._element).trigger(c),!c.isDefaultPrevented()&&!l.isDefaultPrevented()){s&&(n=document.querySelector(s)),this._activate(this._element,o);var h=function(){var n=e.Event("hidden.bs.tab",{relatedTarget:t._element}),o=e.Event("shown.bs.tab",{relatedTarget:i});e(i).trigger(n),e(t._element).trigger(o)};n?this._activate(n,n.parentNode,h):h()}}},n.dispose=function(){e.removeData(this._element,"bs.tab"),this._element=null},n._activate=function(t,n,i){var o=this,s=(!n||"UL"!==n.nodeName&&"OL"!==n.nodeName?e(n).children(".active"):e(n).find("> li > .active"))[0],r=i&&s&&e(s).hasClass("fade"),l=function(){return o._transitionComplete(t,s,i)};if(s&&r){var c=a.getTransitionDurationFromElement(s);e(s).removeClass("show").one(a.TRANSITION_END,l).emulateTransitionEnd(c)}else l()},n._transitionComplete=function(t,n,i){if(n){e(n).removeClass("active");var o=e(n.parentNode).find("> .dropdown-menu .active")[0];o&&e(o).removeClass("active"),"tab"===n.getAttribute("role")&&n.setAttribute("aria-selected",!1)}if(e(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),a.reflow(t),t.classList.contains("fade")&&t.classList.add("show"),t.parentNode&&e(t.parentNode).hasClass("dropdown-menu")){var s=e(t).closest(".dropdown")[0];if(s){var r=[].slice.call(s.querySelectorAll(".dropdown-toggle"));e(r).addClass("active")}t.setAttribute("aria-expanded",!0)}i&&i()},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.tab");if(o||(o=new t(this),i.data("bs.tab",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n]()}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}}]),t}();e(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),lt._jQueryInterface.call(e(this),"show")})),e.fn.tab=lt._jQueryInterface,e.fn.tab.Constructor=lt,e.fn.tab.noConflict=function(){return e.fn.tab=at,lt._jQueryInterface};var ct=e.fn.toast,ht={animation:"boolean",autohide:"boolean",delay:"number"},ut={animation:!0,autohide:!0,delay:500},dt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var n=t.prototype;return n.show=function(){var t=this,n=e.Event("show.bs.toast");if(e(this._element).trigger(n),!n.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var i=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),e(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),a.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var o=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,i).emulateTransitionEnd(o)}else i()}},n.hide=function(){if(this._element.classList.contains("show")){var t=e.Event("hide.bs.toast");e(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},n.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),e(this._element).off("click.dismiss.bs.toast"),e.removeData(this._element,"bs.toast"),this._element=null,this._config=null},n._getConfig=function(t){return t=s({},ut,e(this._element).data(),"object"==typeof t&&t?t:{}),a.typeCheckConfig("toast",t,this.constructor.DefaultType),t},n._setListeners=function(){var t=this;e(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},n._close=function(){var t=this,n=function(){t._element.classList.add("hide"),e(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var i=a.getTransitionDurationFromElement(this._element);e(this._element).one(a.TRANSITION_END,n).emulateTransitionEnd(i)}else n()},n._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(n){return this.each((function(){var i=e(this),o=i.data("bs.toast");if(o||(o=new t(this,"object"==typeof n&&n),i.data("bs.toast",o)),"string"==typeof n){if("undefined"==typeof o[n])throw new TypeError('No method named "'+n+'"');o[n](this)}}))},o(t,null,[{key:"VERSION",get:function(){return"4.5.1"}},{key:"DefaultType",get:function(){return ht}},{key:"Default",get:function(){return ut}}]),t}();e.fn.toast=dt._jQueryInterface,e.fn.toast.Constructor=dt,e.fn.toast.noConflict=function(){return e.fn.toast=ct,dt._jQueryInterface},t.Alert=h,t.Button=d,t.Carousel=b,t.Collapse=C,t.Dropdown=I,t.Modal=P,t.Popover=et,t.Scrollspy=rt,t.Tab=lt,t.Toast=dt,t.Tooltip=X,t.Util=a,Object.defineProperty(t,"__esModule",{value:!0})}));
7
- //# sourceMappingURL=bootstrap.min.js.map
 
 
 
 
 
 
 
admin/js/chart.js CHANGED
@@ -1,5 +1,5 @@
1
  /*!
2
- * Chart.js v3.5.1
3
  * https://www.chartjs.org
4
  * (c) 2021 Chart.js Contributors
5
  * Released under the MIT License
@@ -73,23 +73,21 @@ class Animator {
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
  }
@@ -112,18 +110,18 @@ class Animator {
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) {
@@ -981,10 +979,10 @@ class Defaults {
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 = {};
@@ -1168,6 +1166,9 @@ function _limitValue(value, min, max) {
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)) {
@@ -1883,12 +1884,10 @@ var Interaction = {
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
  };
@@ -1984,13 +1983,18 @@ function resolve(inputs, context, index, info) {
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) {
@@ -2204,6 +2208,7 @@ function placeBoxes(boxes, chartArea, params, stacks) {
2204
  chartArea.y = y;
2205
  }
2206
  defaults.set('layout', {
 
2207
  padding: {
2208
  top: 0,
2209
  right: 0,
@@ -2297,7 +2302,7 @@ var layouts = {
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
  };
@@ -2340,8 +2345,7 @@ function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback,
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
  }
@@ -2400,16 +2404,14 @@ function _descriptors(proxy, defaults = {scriptable: true, indexable: true}) {
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) {
@@ -2434,7 +2436,7 @@ function _resolveScriptable(prop, value, target, receiver) {
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;
@@ -2899,7 +2901,7 @@ function propertyFn(property) {
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
  };
@@ -3059,36 +3061,54 @@ function splitByStyles(line, segments, points, segmentOptions) {
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;
@@ -3190,6 +3210,7 @@ toPadding: toPadding,
3190
  toFont: toFont,
3191
  resolve: resolve,
3192
  _addGrace: _addGrace,
 
3193
  PI: PI,
3194
  TAU: TAU,
3195
  PITAU: PITAU,
@@ -3216,6 +3237,7 @@ _normalizeAngle: _normalizeAngle,
3216
  _angleBetween: _angleBetween,
3217
  _limitValue: _limitValue,
3218
  _int16Range: _int16Range,
 
3219
  getRtlAdapter: getRtlAdapter,
3220
  overrideTextDirection: overrideTextDirection,
3221
  restoreTextDirection: restoreTextDirection,
@@ -3245,12 +3267,17 @@ class BasePlatform {
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';
@@ -3319,15 +3346,23 @@ function fromNativeEvent(event, chart) {
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});
@@ -3336,12 +3371,13 @@ function createAttachObserver(chart, type, listener) {
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});
@@ -3541,51 +3577,48 @@ class Animation {
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 = []);
@@ -3834,7 +3867,7 @@ function getSortedDatasetIndices(chart, filterVisible) {
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;
@@ -3887,8 +3920,8 @@ 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;
@@ -3911,8 +3944,8 @@ function updateStacks(controller, parsed) {
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) {
@@ -3920,7 +3953,7 @@ function getFirstScaleId(chart, axis) {
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,
@@ -3932,7 +3965,7 @@ function createDatasetContext(parent, index) {
3932
  );
3933
  }
3934
  function createDataContext(parent, index, element) {
3935
- return Object.assign(Object.create(parent), {
3936
  active: false,
3937
  dataIndex: index,
3938
  parsed: undefined,
@@ -3960,6 +3993,8 @@ function clearStacks(meta, items) {
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;
@@ -3981,12 +4016,11 @@ class DatasetController {
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) {
@@ -3995,10 +4029,9 @@ class DatasetController {
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'));
@@ -4006,11 +4039,11 @@ class DatasetController {
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];
@@ -4040,40 +4073,37 @@ class DatasetController {
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) {
@@ -4081,38 +4111,37 @@ class DatasetController {
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) {
@@ -4127,7 +4156,7 @@ class DatasetController {
4127
  meta._sorted = sorted;
4128
  }
4129
  if (_stacked) {
4130
- updateStacks(me, parsed);
4131
  }
4132
  }
4133
  parsePrimitiveData(meta, data, start, count) {
@@ -4198,35 +4227,31 @@ class DatasetController {
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
  }
@@ -4236,7 +4261,7 @@ class DatasetController {
4236
  if (_skip()) {
4237
  continue;
4238
  }
4239
- me.updateRangeFromParsed(range, scale, parsed, stack);
4240
  break;
4241
  }
4242
  }
@@ -4258,35 +4283,30 @@ class DatasetController {
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);
@@ -4313,21 +4333,20 @@ class DatasetController {
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;
@@ -4340,21 +4359,20 @@ class DatasetController {
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;
@@ -4363,9 +4381,8 @@ class DatasetController {
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) {
@@ -4373,10 +4390,10 @@ class DatasetController {
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) {
@@ -4431,28 +4448,26 @@ class DatasetController {
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;
@@ -4464,21 +4479,20 @@ class DatasetController {
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);
@@ -4493,6 +4507,7 @@ class DatasetController {
4493
  const [method, arg1, arg2] = args;
4494
  this[method](arg1, arg2);
4495
  }
 
4496
  }
4497
  _onDataPush() {
4498
  const count = arguments.length;
@@ -4505,8 +4520,13 @@ class DatasetController {
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]);
@@ -4532,14 +4552,13 @@ class Element {
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
  }
@@ -4827,13 +4846,13 @@ function getTitleHeight(options, fallback) {
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'
@@ -4932,13 +4951,12 @@ class Scale extends Element {
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;
@@ -4957,15 +4975,14 @@ class Scale extends Element {
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
  }
@@ -4973,18 +4990,19 @@ class Scale extends Element {
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() {
@@ -5002,73 +5020,71 @@ class Scale extends Element {
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]);
@@ -5077,28 +5093,26 @@ class Scale extends Element {
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');
@@ -5120,12 +5134,11 @@ class Scale extends Element {
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() {
@@ -5135,27 +5148,26 @@ class Scale extends Element {
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)),
@@ -5163,7 +5175,7 @@ class Scale extends Element {
5163
  ));
5164
  labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
5165
  }
5166
- me.labelRotation = labelRotation;
5167
  }
5168
  afterCalculateLabelRotation() {
5169
  callback(this.options.afterCalculateLabelRotation, [this]);
@@ -5172,56 +5184,54 @@ class Scale extends Element {
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) {
@@ -5240,8 +5250,8 @@ class Scale extends Element {
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;
@@ -5252,17 +5262,16 @@ class Scale extends Element {
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() {
@@ -5276,9 +5285,8 @@ class Scale extends Element {
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)) {
@@ -5287,18 +5295,17 @@ class Scale extends Element {
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
  }
@@ -5361,12 +5368,11 @@ class Scale extends Element {
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;
@@ -5382,27 +5388,25 @@ class Scale extends Element {
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
  }
@@ -5414,18 +5418,17 @@ class Scale extends Element {
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) {
@@ -5434,36 +5437,36 @@ class Scale extends Element {
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;
@@ -5475,7 +5478,7 @@ class Scale extends Element {
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;
@@ -5485,7 +5488,7 @@ class Scale extends Element {
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 || [];
@@ -5494,7 +5497,7 @@ class Scale extends Element {
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
  }
@@ -5523,37 +5526,36 @@ class Scale extends Element {
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') {
@@ -5562,18 +5564,18 @@ class Scale extends Element {
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') {
@@ -5582,13 +5584,13 @@ class Scale extends Element {
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;
@@ -5669,9 +5671,8 @@ class Scale extends Element {
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
  }
@@ -5684,16 +5685,15 @@ class Scale extends Element {
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') {
@@ -5704,7 +5704,7 @@ class Scale extends Element {
5704
  x += widest;
5705
  }
5706
  } else {
5707
- x = me.right - tickAndPadding;
5708
  if (crossAlign === 'near') {
5709
  textAlign = 'right';
5710
  } else if (crossAlign === 'center') {
@@ -5712,12 +5712,12 @@ class Scale extends Element {
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') {
@@ -5728,7 +5728,7 @@ class Scale extends Element {
5728
  x -= widest;
5729
  }
5730
  } else {
5731
- x = me.left + tickAndPadding;
5732
  if (crossAlign === 'near') {
5733
  textAlign = 'left';
5734
  } else if (crossAlign === 'center') {
@@ -5736,7 +5736,7 @@ class Scale extends Element {
5736
  x += widest / 2;
5737
  } else {
5738
  textAlign = 'right';
5739
- x = me.right;
5740
  }
5741
  }
5742
  } else {
@@ -5745,16 +5745,15 @@ class Scale extends Element {
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() {
@@ -5767,24 +5766,22 @@ class Scale extends Element {
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) {
@@ -5827,23 +5824,22 @@ class Scale extends Element {
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();
@@ -5856,17 +5852,16 @@ class Scale extends Element {
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];
@@ -5911,57 +5906,54 @@ class Scale extends Element {
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
  }
@@ -5972,9 +5964,8 @@ class Scale extends Element {
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
 
@@ -5989,15 +5980,14 @@ class TypedRegistry {
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
  }
@@ -6006,7 +5996,7 @@ class TypedRegistry {
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;
@@ -6109,15 +6099,14 @@ class Registry {
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
  });
@@ -6152,16 +6141,15 @@ class PluginService {
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
  }
@@ -6291,6 +6279,12 @@ function mergeScaleConfig(config, options) {
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 || {};
@@ -6513,18 +6507,23 @@ function getResolver(resolverCache, scopes, prefixes) {
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) {
@@ -6564,9 +6563,21 @@ 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);
@@ -6576,9 +6587,10 @@ class Chart {
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;
@@ -6608,16 +6620,17 @@ class Chart {
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() {
@@ -6643,16 +6656,15 @@ class Chart {
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);
@@ -6670,24 +6682,23 @@ class Chart {
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
  }
@@ -6699,10 +6710,9 @@ class Chart {
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;
@@ -6740,8 +6750,8 @@ class Chart {
6740
  scale = new scaleClass({
6741
  id,
6742
  type: scaleType,
6743
- ctx: me.ctx,
6744
- chart: me
6745
  });
6746
  scales[scale.id] = scale;
6747
  }
@@ -6753,56 +6763,53 @@ class Chart {
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();
@@ -6813,155 +6820,187 @@ class Chart {
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) {
@@ -6976,36 +7015,34 @@ class Chart {
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();
@@ -7013,7 +7050,7 @@ class Chart {
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];
@@ -7023,9 +7060,8 @@ class Chart {
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 = {
@@ -7047,7 +7083,7 @@ class Chart {
7047
  return meta;
7048
  }
7049
  getContext() {
7050
- return this.$context || (this.$context = {chart: this, type: 'chart'});
7051
  }
7052
  getVisibleDatasetCount() {
7053
  return this.getSortedVisibleDatasetMetas().length;
@@ -7071,17 +7107,16 @@ class Chart {
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) {
@@ -7091,36 +7126,33 @@ class Chart {
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);
@@ -7134,73 +7166,70 @@ class Chart {
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';
@@ -7221,10 +7250,9 @@ class Chart {
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
  }
@@ -7236,66 +7264,63 @@ class Chart {
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
  }
@@ -7378,19 +7403,20 @@ 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 = () => {
@@ -7559,6 +7585,11 @@ function swap(orig, v1, v2) {
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);
@@ -7593,10 +7624,9 @@ class BarController extends DatasetController {
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 + ']'
@@ -7607,32 +7637,29 @@ class BarController extends DatasetController {
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,
@@ -7644,17 +7671,18 @@ class BarController extends DatasetController {
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 = [];
@@ -7698,38 +7726,36 @@ class BarController extends DatasetController {
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;
@@ -7745,7 +7771,7 @@ class BarController extends DatasetController {
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;
@@ -7771,22 +7797,21 @@ class BarController extends DatasetController {
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 {
@@ -7797,15 +7822,14 @@ class BarController extends DatasetController {
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
  }
@@ -7845,34 +7869,41 @@ class BubbleController extends DatasetController {
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;
@@ -7882,35 +7913,33 @@ class BubbleController extends DatasetController {
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);
@@ -7995,9 +8024,18 @@ class DoughnutController extends DatasetController {
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() {
@@ -8009,10 +8047,9 @@ class DoughnutController extends DatasetController {
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);
@@ -8025,66 +8062,63 @@ class DoughnutController extends DatasetController {
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,
@@ -8092,12 +8126,12 @@ class DoughnutController extends DatasetController {
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;
@@ -8120,9 +8154,8 @@ class DoughnutController extends DatasetController {
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 {
@@ -8131,9 +8164,8 @@ class DoughnutController extends DatasetController {
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) {
@@ -8141,9 +8173,6 @@ class DoughnutController extends DatasetController {
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
  }
@@ -8266,75 +8295,76 @@ class LineController extends DatasetController {
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() {
@@ -8412,9 +8442,8 @@ class PolarAreaController extends DatasetController {
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 {
@@ -8428,38 +8457,36 @@ class PolarAreaController extends DatasetController {
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) {
@@ -8477,9 +8504,9 @@ class PolarAreaController extends DatasetController {
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() {
@@ -8587,24 +8614,22 @@ PieController.defaults = {
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 = {
@@ -8612,18 +8637,17 @@ class RadarController extends DatasetController {
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;
@@ -8634,7 +8658,7 @@ class RadarController extends DatasetController {
8634
  skip: isNaN(x) || isNaN(y),
8635
  options
8636
  };
8637
- me.updateElement(point, i, properties, mode);
8638
  }
8639
  }
8640
  }
@@ -8879,8 +8903,9 @@ class ArcElement extends Element {
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) {
@@ -8905,29 +8930,28 @@ class ArcElement extends Element {
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
  }
@@ -9088,7 +9112,7 @@ function strokePathDirect(ctx, line, start, count) {
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);
@@ -9099,6 +9123,7 @@ class LineElement extends Element {
9099
  super();
9100
  this.animated = true;
9101
  this.options = undefined;
 
9102
  this._loop = undefined;
9103
  this._fullLoop = undefined;
9104
  this._path = undefined;
@@ -9112,20 +9137,18 @@ class LineElement extends Element {
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;
@@ -9145,11 +9168,10 @@ class LineElement extends Element {
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
  }
@@ -9176,30 +9198,27 @@ class LineElement extends Element {
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
  }
@@ -9265,15 +9284,14 @@ class PointElement extends Element {
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 || {};
@@ -9375,8 +9393,8 @@ function inRange(bar, x, y, useFinalPosition) {
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;
@@ -9405,27 +9423,27 @@ class BarElement extends Element {
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
  }
@@ -9454,7 +9472,7 @@ BarElement.defaults = {
9454
  borderSkipped: 'start',
9455
  borderWidth: 0,
9456
  borderRadius: 0,
9457
- enableBorderRadius: true,
9458
  pointStyle: undefined
9459
  };
9460
  BarElement.defaultRoutes = {
@@ -9812,11 +9830,11 @@ function pointsFromSegments(boundary, line) {
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];
@@ -9826,16 +9844,15 @@ function buildStackLine(source) {
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
  }
@@ -9874,7 +9891,7 @@ function findPoint(line, sourcePoint, property) {
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;
@@ -10186,77 +10203,72 @@ class Legend extends Element {
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;
@@ -10270,17 +10282,16 @@ class Legend extends Element {
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;
@@ -10298,35 +10309,34 @@ class Legend extends Element {
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
  }
@@ -10336,26 +10346,24 @@ class Legend extends Element {
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;
@@ -10413,24 +10421,24 @@ class Legend extends Element {
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;
@@ -10438,21 +10446,21 @@ class Legend extends Element {
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;
@@ -10460,32 +10468,31 @@ class Legend extends Element {
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));
@@ -10502,38 +10509,38 @@ class Legend extends Element {
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
  }
@@ -10656,23 +10663,22 @@ class Title extends Element {
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() {
@@ -10703,16 +10709,15 @@ class Title extends Element {
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,
@@ -10938,7 +10943,7 @@ function getTooltipSize(tooltip, options) {
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);
@@ -11017,7 +11022,7 @@ 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') {
@@ -11027,9 +11032,9 @@ function getBackgroundPoint(options, size, alignment, chart) {
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),
@@ -11048,7 +11053,7 @@ 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'
@@ -11095,31 +11100,28 @@ class Tooltip extends Element {
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));
@@ -11130,7 +11132,6 @@ class Tooltip extends Element {
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) => {
@@ -11140,9 +11141,9 @@ class Tooltip extends Element {
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;
@@ -11151,11 +11152,10 @@ class Tooltip extends Element {
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));
@@ -11163,16 +11163,15 @@ class Tooltip extends Element {
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));
@@ -11182,42 +11181,41 @@ class Tooltip extends Element {
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,
@@ -11228,13 +11226,13 @@ class Tooltip extends Element {
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) {
@@ -11245,7 +11243,8 @@ class Tooltip extends Element {
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;
@@ -11265,9 +11264,9 @@ class Tooltip extends Element {
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
  }
@@ -11287,13 +11286,12 @@ class Tooltip extends Element {
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);
@@ -11310,12 +11308,11 @@ class Tooltip extends Element {
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;
@@ -11339,8 +11336,8 @@ class Tooltip extends Element {
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();
@@ -11372,16 +11369,15 @@ class Tooltip extends Element {
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;
@@ -11391,20 +11387,20 @@ class Tooltip extends Element {
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) {
@@ -11415,17 +11411,16 @@ class Tooltip extends Element {
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';
@@ -11442,32 +11437,32 @@ class Tooltip extends Element {
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) {
@@ -11475,59 +11470,57 @@ class Tooltip extends Element {
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
  }
@@ -11536,10 +11529,9 @@ class Tooltip extends Element {
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
  }
@@ -11550,35 +11542,34 @@ class Tooltip extends Element {
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;
@@ -11634,41 +11625,37 @@ var plugin_tooltip = {
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',
@@ -11779,13 +11766,19 @@ 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;
@@ -11796,6 +11789,20 @@ class CategoryScale extends Scale {
11796
  super(cfg);
11797
  this._startValue = undefined;
11798
  this._valueRange = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11799
  }
11800
  parse(raw, index) {
11801
  if (isNullOrUndef(raw)) {
@@ -11803,72 +11810,65 @@ class CategoryScale extends Scale {
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;
@@ -11955,7 +11955,7 @@ function generateTicks$1(generationOptions, dataRange) {
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});
@@ -11990,10 +11990,9 @@ class LinearScaleBase extends Scale {
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) {
@@ -12015,18 +12014,21 @@ class LinearScaleBase extends Scale {
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) {
@@ -12038,10 +12040,9 @@ class LinearScaleBase extends Scale {
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,
@@ -12051,61 +12052,58 @@ class LinearScaleBase extends Scale {
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) {
@@ -12165,20 +12163,18 @@ class LogarithmicScale extends Scale {
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);
@@ -12197,59 +12193,57 @@ class LogarithmicScale extends Scale {
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';
@@ -12447,7 +12441,7 @@ 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'
@@ -12463,64 +12457,58 @@ class RadialLinearScale extends LinearScaleBase {
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;
@@ -12528,38 +12516,34 @@ class RadialLinearScale extends LinearScaleBase {
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
  }
@@ -12579,13 +12563,12 @@ class RadialLinearScale extends LinearScaleBase {
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();
@@ -12593,28 +12576,27 @@ class RadialLinearScale extends LinearScaleBase {
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;
@@ -12623,10 +12605,10 @@ class RadialLinearScale extends LinearScaleBase {
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
  }
@@ -12635,27 +12617,26 @@ class RadialLinearScale extends LinearScaleBase {
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;
@@ -12864,11 +12845,10 @@ class TimeScale extends Scale {
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);
@@ -12878,15 +12858,15 @@ class TimeScale extends Scale {
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();
@@ -12899,61 +12879,58 @@ class TimeScale extends Scale {
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;
@@ -12967,7 +12944,7 @@ class TimeScale extends Scale {
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
  }
@@ -12977,27 +12954,25 @@ class TimeScale extends Scale {
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;
@@ -13007,72 +12982,65 @@ class TimeScale extends Scale {
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));
@@ -13126,11 +13094,10 @@ class TimeSeriesScale extends TimeScale {
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) {
@@ -13161,29 +13128,27 @@ class TimeSeriesScale extends TimeScale {
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';
1
  /*!
2
+ * Chart.js v3.6.2
3
  * https://www.chartjs.org
4
  * (c) 2021 Chart.js Contributors
5
  * Released under the MIT License
73
  }));
74
  }
75
  _refresh() {
76
+ if (this._request) {
 
77
  return;
78
  }
79
+ this._running = true;
80
+ this._request = requestAnimFrame.call(window, () => {
81
+ this._update();
82
+ this._request = null;
83
+ if (this._running) {
84
+ this._refresh();
85
  }
86
  });
87
  }
88
  _update(date = Date.now()) {
 
89
  let remaining = 0;
90
+ this._charts.forEach((anims, chart) => {
91
  if (!anims.running || !anims.items.length) {
92
  return;
93
  }
110
  }
111
  if (draw) {
112
  chart.draw();
113
+ this._notify(chart, anims, date, 'progress');
114
  }
115
  if (!items.length) {
116
  anims.running = false;
117
+ this._notify(chart, anims, date, 'complete');
118
  anims.initial = false;
119
  }
120
  remaining += items.length;
121
  });
122
+ this._lastDate = date;
123
  if (remaining === 0) {
124
+ this._running = false;
125
  }
126
  }
127
  _getAnims(chart) {
979
  'touchmove'
980
  ];
981
  this.font = {
982
+ family: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
983
  size: 12,
984
  style: 'normal',
985
+ lineHeight: 1.2,
986
  weight: null
987
  };
988
  this.hover = {};
1166
  function _int16Range(value) {
1167
  return _limitValue(value, -32768, 32767);
1168
  }
1169
+ function _isBetween(value, start, end, epsilon = 1e-6) {
1170
+ return value >= Math.min(start, end) - epsilon && value <= Math.max(start, end) + epsilon;
1171
+ }
1172
 
1173
  function toFontString(font) {
1174
  if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
1884
  return getNearestItems(chart, position, axis, options.intersect, useFinalPosition);
1885
  },
1886
  x(chart, e, options, useFinalPosition) {
1887
+ return getAxisItems(chart, e, {axis: 'x', intersect: options.intersect}, useFinalPosition);
 
1888
  },
1889
  y(chart, e, options, useFinalPosition) {
1890
+ return getAxisItems(chart, e, {axis: 'y', intersect: options.intersect}, useFinalPosition);
 
1891
  }
1892
  }
1893
  };
1983
  }
1984
  }
1985
  }
1986
+ function _addGrace(minmax, grace, beginAtZero) {
1987
  const {min, max} = minmax;
1988
+ const change = toDimension(grace, (max - min) / 2);
1989
+ const keepZero = (value, add) => beginAtZero && value === 0 ? 0 : value + add;
1990
  return {
1991
+ min: keepZero(min, -Math.abs(change)),
1992
+ max: keepZero(max, change)
1993
  };
1994
  }
1995
+ function createContext(parentContext, context) {
1996
+ return Object.assign(Object.create(parentContext), context);
1997
+ }
1998
 
1999
  const STATIC_POSITIONS = ['left', 'top', 'right', 'bottom'];
2000
  function filterByPosition(array, position) {
2208
  chartArea.y = y;
2209
  }
2210
  defaults.set('layout', {
2211
+ autoPadding: true,
2212
  padding: {
2213
  top: 0,
2214
  right: 0,
2302
  each(boxes.chartArea, (layout) => {
2303
  const box = layout.box;
2304
  Object.assign(box, chart.chartArea);
2305
+ box.update(chartArea.w, chartArea.h, {left: 0, top: 0, right: 0, bottom: 0});
2306
  });
2307
  }
2308
  };
2345
  },
2346
  set(target, prop, value) {
2347
  const storage = target._storage || (target._storage = getTarget());
2348
+ target[prop] = storage[prop] = value;
 
2349
  delete target._keys;
2350
  return true;
2351
  }
2404
  };
2405
  }
2406
  const readKey = (prefix, name) => prefix ? prefix + _capitalize(name) : name;
2407
+ const needsSubResolver = (prop, value) => isObject(value) && prop !== 'adapters' &&
2408
+ (Object.getPrototypeOf(value) === null || value.constructor === Object);
2409
  function _cached(target, prop, resolve) {
2410
+ if (Object.prototype.hasOwnProperty.call(target, prop)) {
2411
+ return target[prop];
 
 
 
 
 
2412
  }
2413
+ const value = resolve();
2414
+ target[prop] = value;
2415
  return value;
2416
  }
2417
  function _resolveWithContext(target, prop, receiver) {
2436
  _stack.add(prop);
2437
  value = value(_context, _subProxy || receiver);
2438
  _stack.delete(prop);
2439
+ if (needsSubResolver(prop, value)) {
2440
  value = createSubResolver(_proxy._scopes, _proxy, prop, value);
2441
  }
2442
  return value;
2901
  };
2902
  }
2903
  return {
2904
+ between: _isBetween,
2905
  compare: (a, b) => a - b,
2906
  normalize: x => x
2907
  };
3061
  return doSplitByStyles(line, segments, points, segmentOptions);
3062
  }
3063
  function doSplitByStyles(line, segments, points, segmentOptions) {
3064
+ const chartContext = line._chart.getContext();
3065
  const baseStyle = readStyle(line.options);
3066
+ const {_datasetIndex: datasetIndex, options: {spanGaps}} = line;
3067
  const count = points.length;
3068
  const result = [];
3069
+ let prevStyle = baseStyle;
3070
  let start = segments[0].start;
3071
  let i = start;
3072
+ function addStyle(s, e, l, st) {
3073
+ const dir = spanGaps ? -1 : 1;
3074
+ if (s === e) {
3075
+ return;
3076
+ }
3077
+ s += count;
3078
+ while (points[s % count].skip) {
3079
+ s -= dir;
3080
+ }
3081
+ while (points[e % count].skip) {
3082
+ e += dir;
3083
+ }
3084
+ if (s % count !== e % count) {
3085
+ result.push({start: s % count, end: e % count, loop: l, style: st});
3086
+ prevStyle = st;
3087
+ start = e % count;
3088
+ }
3089
+ }
3090
  for (const segment of segments) {
3091
+ start = spanGaps ? start : segment.start;
3092
  let prev = points[start % count];
3093
  let style;
3094
  for (i = start + 1; i <= segment.end; i++) {
3095
  const pt = points[i % count];
3096
+ style = readStyle(segmentOptions.setContext(createContext(chartContext, {
3097
  type: 'segment',
3098
  p0: prev,
3099
  p1: pt,
3100
  p0DataIndex: (i - 1) % count,
3101
  p1DataIndex: i % count,
3102
+ datasetIndex
3103
+ })));
3104
  if (styleChanged(style, prevStyle)) {
3105
+ addStyle(start, i - 1, segment.loop, prevStyle);
 
 
3106
  }
3107
  prev = pt;
3108
  prevStyle = style;
3109
  }
3110
  if (start < i - 1) {
3111
+ addStyle(start, i - 1, segment.loop, prevStyle);
 
3112
  }
3113
  }
3114
  return result;
3210
  toFont: toFont,
3211
  resolve: resolve,
3212
  _addGrace: _addGrace,
3213
+ createContext: createContext,
3214
  PI: PI,
3215
  TAU: TAU,
3216
  PITAU: PITAU,
3237
  _angleBetween: _angleBetween,
3238
  _limitValue: _limitValue,
3239
  _int16Range: _int16Range,
3240
+ _isBetween: _isBetween,
3241
  getRtlAdapter: getRtlAdapter,
3242
  overrideTextDirection: overrideTextDirection,
3243
  restoreTextDirection: restoreTextDirection,
3267
  isAttached(canvas) {
3268
  return true;
3269
  }
3270
+ updateConfig(config) {
3271
+ }
3272
  }
3273
 
3274
  class BasicPlatform extends BasePlatform {
3275
  acquireContext(item) {
3276
  return item && item.getContext && item.getContext('2d') || null;
3277
  }
3278
+ updateConfig(config) {
3279
+ config.options.animation = false;
3280
+ }
3281
  }
3282
 
3283
  const EXPANDO_KEY = '$chartjs';
3346
  y: y !== undefined ? y : null,
3347
  };
3348
  }
3349
+ function nodeListContains(nodeList, canvas) {
3350
+ for (const node of nodeList) {
3351
+ if (node === canvas || node.contains(canvas)) {
3352
+ return true;
3353
+ }
3354
+ }
3355
+ }
3356
  function createAttachObserver(chart, type, listener) {
3357
  const canvas = chart.canvas;
3358
  const observer = new MutationObserver(entries => {
3359
+ let trigger = false;
3360
  for (const entry of entries) {
3361
+ trigger = trigger || nodeListContains(entry.addedNodes, canvas);
3362
+ trigger = trigger && !nodeListContains(entry.removedNodes, canvas);
3363
+ }
3364
+ if (trigger) {
3365
+ listener();
3366
  }
3367
  });
3368
  observer.observe(document, {childList: true, subtree: true});
3371
  function createDetachObserver(chart, type, listener) {
3372
  const canvas = chart.canvas;
3373
  const observer = new MutationObserver(entries => {
3374
+ let trigger = false;
3375
  for (const entry of entries) {
3376
+ trigger = trigger || nodeListContains(entry.removedNodes, canvas);
3377
+ trigger = trigger && !nodeListContains(entry.addedNodes, canvas);
3378
+ }
3379
+ if (trigger) {
3380
+ listener();
3381
  }
3382
  });
3383
  observer.observe(document, {childList: true, subtree: true});
3577
  return this._active;
3578
  }
3579
  update(cfg, to, date) {
3580
+ if (this._active) {
3581
+ this._notify(false);
3582
+ const currentValue = this._target[this._prop];
3583
+ const elapsed = date - this._start;
3584
+ const remain = this._duration - elapsed;
3585
+ this._start = date;
3586
+ this._duration = Math.floor(Math.max(remain, cfg.duration));
3587
+ this._total += elapsed;
3588
+ this._loop = !!cfg.loop;
3589
+ this._to = resolve([cfg.to, to, currentValue, cfg.from]);
3590
+ this._from = resolve([cfg.from, currentValue, to]);
 
3591
  }
3592
  }
3593
  cancel() {
3594
+ if (this._active) {
3595
+ this.tick(Date.now());
3596
+ this._active = false;
3597
+ this._notify(false);
 
3598
  }
3599
  }
3600
  tick(date) {
3601
+ const elapsed = date - this._start;
3602
+ const duration = this._duration;
3603
+ const prop = this._prop;
3604
+ const from = this._from;
3605
+ const loop = this._loop;
3606
+ const to = this._to;
 
3607
  let factor;
3608
+ this._active = from !== to && (loop || (elapsed < duration));
3609
+ if (!this._active) {
3610
+ this._target[prop] = to;
3611
+ this._notify(true);
3612
  return;
3613
  }
3614
  if (elapsed < 0) {
3615
+ this._target[prop] = from;
3616
  return;
3617
  }
3618
  factor = (elapsed / duration) % 2;
3619
  factor = loop && factor > 1 ? 2 - factor : factor;
3620
+ factor = this._easing(Math.min(1, Math.max(0, factor)));
3621
+ this._target[prop] = this._fn(from, to, factor);
3622
  }
3623
  wait() {
3624
  const promises = this._promises || (this._promises = []);
3867
  }
3868
  return keys;
3869
  }
3870
+ function applyStack(stack, value, dsIndex, options = {}) {
3871
  const keys = stack.keys;
3872
  const singleMode = options.mode === 'single';
3873
  let i, ilen, datasetIndex, otherValue;
3920
  const subStack = stacks[stackKey] || (stacks[stackKey] = {});
3921
  return subStack[indexValue] || (subStack[indexValue] = {});
3922
  }
3923
+ function getLastIndexInStack(stack, vScale, positive, type) {
3924
+ for (const meta of vScale.getMatchingVisibleMetas(type).reverse()) {
3925
  const value = stack[meta.index];
3926
  if ((positive && value > 0) || (!positive && value < 0)) {
3927
  return meta.index;
3944
  const itemStacks = item._stacks || (item._stacks = {});
3945
  stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
3946
  stack[datasetIndex] = value;
3947
+ stack._top = getLastIndexInStack(stack, vScale, true, meta.type);
3948
+ stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);
3949
  }
3950
  }
3951
  function getFirstScaleId(chart, axis) {
3953
  return Object.keys(scales).filter(key => scales[key].axis === axis).shift();
3954
  }
3955
  function createDatasetContext(parent, index) {
3956
+ return createContext(parent,
3957
  {
3958
  active: false,
3959
  dataset: undefined,
3965
  );
3966
  }
3967
  function createDataContext(parent, index, element) {
3968
+ return createContext(parent, {
3969
  active: false,
3970
  dataIndex: index,
3971
  parsed: undefined,
3993
  }
3994
  const isDirectUpdateMode = (mode) => mode === 'reset' || mode === 'none';
3995
  const cloneIfNotShared = (cached, shared) => shared ? cached : Object.assign({}, cached);
3996
+ const createStack = (canStack, meta, chart) => canStack && !meta.hidden && meta._stacked
3997
+ && {keys: getSortedDatasetIndices(chart, true), values: null};
3998
  class DatasetController {
3999
  constructor(chart, datasetIndex) {
4000
  this.chart = chart;
4016
  this.initialize();
4017
  }
4018
  initialize() {
4019
+ const meta = this._cachedMeta;
4020
+ this.configure();
4021
+ this.linkScales();
 
4022
  meta._stacked = isStacked(meta.vScale, meta);
4023
+ this.addElements();
4024
  }
4025
  updateIndex(datasetIndex) {
4026
  if (this.index !== datasetIndex) {
4029
  this.index = datasetIndex;
4030
  }
4031
  linkScales() {
4032
+ const chart = this.chart;
4033
+ const meta = this._cachedMeta;
4034
+ const dataset = this.getDataset();
 
4035
  const chooseId = (axis, x, y, r) => axis === 'x' ? x : axis === 'r' ? r : y;
4036
  const xid = meta.xAxisID = valueOrDefault(dataset.xAxisID, getFirstScaleId(chart, 'x'));
4037
  const yid = meta.yAxisID = valueOrDefault(dataset.yAxisID, getFirstScaleId(chart, 'y'));
4039
  const indexAxis = meta.indexAxis;
4040
  const iid = meta.iAxisID = chooseId(indexAxis, xid, yid, rid);
4041
  const vid = meta.vAxisID = chooseId(indexAxis, yid, xid, rid);
4042
+ meta.xScale = this.getScaleForId(xid);
4043
+ meta.yScale = this.getScaleForId(yid);
4044
+ meta.rScale = this.getScaleForId(rid);
4045
+ meta.iScale = this.getScaleForId(iid);
4046
+ meta.vScale = this.getScaleForId(vid);
4047
  }
4048
  getDataset() {
4049
  return this.chart.data.datasets[this.index];
4073
  }
4074
  }
4075
  _dataCheck() {
4076
+ const dataset = this.getDataset();
 
4077
  const data = dataset.data || (dataset.data = []);
4078
+ const _data = this._data;
4079
  if (isObject(data)) {
4080
+ this._data = convertObjectDataToArray(data);
4081
  } else if (_data !== data) {
4082
  if (_data) {
4083
+ unlistenArrayEvents(_data, this);
4084
+ const meta = this._cachedMeta;
4085
  clearStacks(meta);
4086
  meta._parsed = [];
4087
  }
4088
  if (data && Object.isExtensible(data)) {
4089
+ listenArrayEvents(data, this);
4090
  }
4091
+ this._syncList = [];
4092
+ this._data = data;
4093
  }
4094
  }
4095
  addElements() {
4096
+ const meta = this._cachedMeta;
4097
+ this._dataCheck();
4098
+ if (this.datasetElementType) {
4099
+ meta.dataset = new this.datasetElementType();
 
4100
  }
4101
  }
4102
  buildOrUpdateElements(resetNewElements) {
4103
+ const meta = this._cachedMeta;
4104
+ const dataset = this.getDataset();
 
4105
  let stackChanged = false;
4106
+ this._dataCheck();
4107
  const oldStacked = meta._stacked;
4108
  meta._stacked = isStacked(meta.vScale, meta);
4109
  if (meta.stack !== dataset.stack) {
4111
  clearStacks(meta);
4112
  meta.stack = dataset.stack;
4113
  }
4114
+ this._resyncElements(resetNewElements);
4115
  if (stackChanged || oldStacked !== meta._stacked) {
4116
+ updateStacks(this, meta._parsed);
4117
  }
4118
  }
4119
  configure() {
4120
+ const config = this.chart.config;
4121
+ const scopeKeys = config.datasetScopeKeys(this._type);
4122
+ const scopes = config.getOptionScopes(this.getDataset(), scopeKeys, true);
4123
+ this.options = config.createResolver(scopes, this.getContext());
4124
+ this._parsing = this.options.parsing;
4125
+ this._cachedDataOpts = {};
4126
  }
4127
  parse(start, count) {
4128
+ const {_cachedMeta: meta, _data: data} = this;
 
4129
  const {iScale, _stacked} = meta;
4130
  const iAxis = iScale.axis;
4131
  let sorted = start === 0 && count === data.length ? true : meta._sorted;
4132
  let prev = start > 0 && meta._parsed[start - 1];
4133
  let i, cur, parsed;
4134
+ if (this._parsing === false) {
4135
  meta._parsed = data;
4136
  meta._sorted = true;
4137
  parsed = data;
4138
  } else {
4139
  if (isArray(data[start])) {
4140
+ parsed = this.parseArrayData(meta, data, start, count);
4141
  } else if (isObject(data[start])) {
4142
+ parsed = this.parseObjectData(meta, data, start, count);
4143
  } else {
4144
+ parsed = this.parsePrimitiveData(meta, data, start, count);
4145
  }
4146
  const isNotInOrderComparedToPrev = () => cur[iAxis] === null || (prev && cur[iAxis] < prev[iAxis]);
4147
  for (i = 0; i < count; ++i) {
4156
  meta._sorted = sorted;
4157
  }
4158
  if (_stacked) {
4159
+ updateStacks(this, parsed);
4160
  }
4161
  }
4162
  parsePrimitiveData(meta, data, start, count) {
4227
  const values = stack && parsed._stacks[scale.axis];
4228
  if (stack && values) {
4229
  stack.values = values;
4230
+ value = applyStack(stack, parsedValue, this._cachedMeta.index);
 
 
4231
  }
4232
  range.min = Math.min(range.min, value);
4233
  range.max = Math.max(range.max, value);
4234
  }
4235
  getMinMax(scale, canStack) {
4236
+ const meta = this._cachedMeta;
 
4237
  const _parsed = meta._parsed;
4238
  const sorted = meta._sorted && scale === meta.iScale;
4239
  const ilen = _parsed.length;
4240
+ const otherScale = this._getOtherScale(scale);
4241
+ const stack = createStack(canStack, meta, this.chart);
4242
  const range = {min: Number.POSITIVE_INFINITY, max: Number.NEGATIVE_INFINITY};
4243
  const {min: otherMin, max: otherMax} = getUserBounds(otherScale);
4244
+ let i, parsed;
4245
  function _skip() {
4246
  parsed = _parsed[i];
4247
+ const otherValue = parsed[otherScale.axis];
4248
+ return !isNumberFinite(parsed[scale.axis]) || otherMin > otherValue || otherMax < otherValue;
 
4249
  }
4250
  for (i = 0; i < ilen; ++i) {
4251
  if (_skip()) {
4252
  continue;
4253
  }
4254
+ this.updateRangeFromParsed(range, scale, parsed, stack);
4255
  if (sorted) {
4256
  break;
4257
  }
4261
  if (_skip()) {
4262
  continue;
4263
  }
4264
+ this.updateRangeFromParsed(range, scale, parsed, stack);
4265
  break;
4266
  }
4267
  }
4283
  return false;
4284
  }
4285
  getLabelAndValue(index) {
4286
+ const meta = this._cachedMeta;
 
4287
  const iScale = meta.iScale;
4288
  const vScale = meta.vScale;
4289
+ const parsed = this.getParsed(index);
4290
  return {
4291
  label: iScale ? '' + iScale.getLabelForValue(parsed[iScale.axis]) : '',
4292
  value: vScale ? '' + vScale.getLabelForValue(parsed[vScale.axis]) : ''
4293
  };
4294
  }
4295
  _update(mode) {
4296
+ const meta = this._cachedMeta;
4297
+ this.update(mode || 'default');
4298
+ meta._clip = toClip(valueOrDefault(this.options.clip, defaultClip(meta.xScale, meta.yScale, this.getMaxOverflow())));
 
 
 
4299
  }
4300
  update(mode) {}
4301
  draw() {
4302
+ const ctx = this._ctx;
4303
+ const chart = this.chart;
4304
+ const meta = this._cachedMeta;
 
4305
  const elements = meta.data || [];
4306
  const area = chart.chartArea;
4307
  const active = [];
4308
+ const start = this._drawStart || 0;
4309
+ const count = this._drawCount || (elements.length - start);
4310
  let i;
4311
  if (meta.dataset) {
4312
  meta.dataset.draw(ctx, area, start, count);
4333
  : this.resolveDataElementOptions(index || 0, mode);
4334
  }
4335
  getContext(index, active, mode) {
4336
+ const dataset = this.getDataset();
 
4337
  let context;
4338
+ if (index >= 0 && index < this._cachedMeta.data.length) {
4339
+ const element = this._cachedMeta.data[index];
4340
  context = element.$context ||
4341
+ (element.$context = createDataContext(this.getContext(), index, element));
4342
+ context.parsed = this.getParsed(index);
4343
  context.raw = dataset.data[index];
4344
  context.index = context.dataIndex = index;
4345
  } else {
4346
+ context = this.$context ||
4347
+ (this.$context = createDatasetContext(this.chart.getContext(), this.index));
4348
  context.dataset = dataset;
4349
+ context.index = context.datasetIndex = this.index;
4350
  }
4351
  context.active = !!active;
4352
  context.mode = mode;
4359
  return this._resolveElementOptions(this.dataElementType.id, mode, index);
4360
  }
4361
  _resolveElementOptions(elementType, mode = 'default', index) {
 
4362
  const active = mode === 'active';
4363
+ const cache = this._cachedDataOpts;
4364
  const cacheKey = elementType + '-' + mode;
4365
  const cached = cache[cacheKey];
4366
+ const sharing = this.enableOptionSharing && defined(index);
4367
  if (cached) {
4368
  return cloneIfNotShared(cached, sharing);
4369
  }
4370
+ const config = this.chart.config;
4371
+ const scopeKeys = config.datasetElementScopeKeys(this._type, elementType);
4372
  const prefixes = active ? [`${elementType}Hover`, 'hover', elementType, ''] : [elementType, ''];
4373
+ const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
4374
  const names = Object.keys(defaults.elements[elementType]);
4375
+ const context = () => this.getContext(index, active);
4376
  const values = config.resolveNamedOptions(scopes, names, context, prefixes);
4377
  if (values.$shared) {
4378
  values.$shared = sharing;
4381
  return values;
4382
  }
4383
  _resolveAnimations(index, transition, active) {
4384
+ const chart = this.chart;
4385
+ const cache = this._cachedDataOpts;
 
4386
  const cacheKey = `animation-${transition}`;
4387
  const cached = cache[cacheKey];
4388
  if (cached) {
4390
  }
4391
  let options;
4392
  if (chart.options.animation !== false) {
4393
+ const config = this.chart.config;
4394
+ const scopeKeys = config.datasetAnimationScopeKeys(this._type, transition);
4395
+ const scopes = config.getOptionScopes(this.getDataset(), scopeKeys);
4396
+ options = config.createResolver(scopes, this.getContext(index, active, transition));
4397
  }
4398
  const animations = new Animations(chart, options && options.animations);
4399
  if (options && options._cacheable) {
4448
  }
4449
  }
4450
  _resyncElements(resetNewElements) {
4451
+ const data = this._data;
4452
+ const elements = this._cachedMeta.data;
4453
+ for (const [method, arg1, arg2] of this._syncList) {
4454
+ this[method](arg1, arg2);
 
4455
  }
4456
+ this._syncList = [];
4457
  const numMeta = elements.length;
4458
  const numData = data.length;
4459
  const count = Math.min(numData, numMeta);
4460
  if (count) {
4461
+ this.parse(0, count);
4462
  }
4463
  if (numData > numMeta) {
4464
+ this._insertElements(numMeta, numData - numMeta, resetNewElements);
4465
  } else if (numData < numMeta) {
4466
+ this._removeElements(numData, numMeta - numData);
4467
  }
4468
  }
4469
  _insertElements(start, count, resetNewElements = true) {
4470
+ const meta = this._cachedMeta;
 
4471
  const data = meta.data;
4472
  const end = start + count;
4473
  let i;
4479
  };
4480
  move(data);
4481
  for (i = start; i < end; ++i) {
4482
+ data[i] = new this.dataElementType();
4483
  }
4484
+ if (this._parsing) {
4485
  move(meta._parsed);
4486
  }
4487
+ this.parse(start, count);
4488
  if (resetNewElements) {
4489
+ this.updateElements(data, start, count, 'reset');
4490
  }
4491
  }
4492
  updateElements(element, start, count, mode) {}
4493
  _removeElements(start, count) {
4494
+ const meta = this._cachedMeta;
4495
+ if (this._parsing) {
 
4496
  const removed = meta._parsed.splice(start, count);
4497
  if (meta._stacked) {
4498
  clearStacks(meta, removed);
4507
  const [method, arg1, arg2] = args;
4508
  this[method](arg1, arg2);
4509
  }
4510
+ this.chart._dataChanges.push([this.index, ...args]);
4511
  }
4512
  _onDataPush() {
4513
  const count = arguments.length;
4520
  this._sync(['_removeElements', 0, 1]);
4521
  }
4522
  _onDataSplice(start, count) {
4523
+ if (count) {
4524
+ this._sync(['_removeElements', start, count]);
4525
+ }
4526
+ const newCount = arguments.length - 2;
4527
+ if (newCount) {
4528
+ this._sync(['_insertElements', start, newCount]);
4529
+ }
4530
  }
4531
  _onDataUnshift() {
4532
  this._sync(['_insertElements', 0, arguments.length]);
4552
  return isNumber(this.x) && isNumber(this.y);
4553
  }
4554
  getProps(props, final) {
 
4555
  const anims = this.$animations;
4556
  if (!final || !anims) {
4557
+ return this;
4558
  }
4559
  const ret = {};
4560
  props.forEach(prop => {
4561
+ ret[prop] = anims[prop] && anims[prop].active() ? anims[prop]._to : this[prop];
4562
  });
4563
  return ret;
4564
  }
4846
  return (lines * font.lineHeight) + padding.height;
4847
  }
4848
  function createScaleContext(parent, scale) {
4849
+ return createContext(parent, {
4850
  scale,
4851
  type: 'scale'
4852
  });
4853
  }
4854
  function createTickContext(parent, index, tick) {
4855
+ return createContext(parent, {
4856
  tick,
4857
  index,
4858
  type: 'tick'
4951
  this.$context = undefined;
4952
  }
4953
  init(options) {
4954
+ this.options = options.setContext(this.getContext());
4955
+ this.axis = options.axis;
4956
+ this._userMin = this.parse(options.min);
4957
+ this._userMax = this.parse(options.max);
4958
+ this._suggestedMin = this.parse(options.suggestedMin);
4959
+ this._suggestedMax = this.parse(options.suggestedMax);
 
4960
  }
4961
  parse(raw, index) {
4962
  return raw;
4975
  };
4976
  }
4977
  getMinMax(canStack) {
4978
+ let {min, max, minDefined, maxDefined} = this.getUserBounds();
 
4979
  let range;
4980
  if (minDefined && maxDefined) {
4981
  return {min, max};
4982
  }
4983
+ const metas = this.getMatchingVisibleMetas();
4984
  for (let i = 0, ilen = metas.length; i < ilen; ++i) {
4985
+ range = metas[i].controller.getMinMax(this, canStack);
4986
  if (!minDefined) {
4987
  min = Math.min(min, range.min);
4988
  }
4990
  max = Math.max(max, range.max);
4991
  }
4992
  }
4993
+ min = maxDefined && min > max ? max : min;
4994
+ max = minDefined && min > max ? min : max;
4995
  return {
4996
  min: finiteOrDefault(min, finiteOrDefault(max, min)),
4997
  max: finiteOrDefault(max, finiteOrDefault(min, max))
4998
  };
4999
  }
5000
  getPadding() {
 
5001
  return {
5002
+ left: this.paddingLeft || 0,
5003
+ top: this.paddingTop || 0,
5004
+ right: this.paddingRight || 0,
5005
+ bottom: this.paddingBottom || 0
5006
  };
5007
  }
5008
  getTicks() {
5020
  callback(this.options.beforeUpdate, [this]);
5021
  }
5022
  update(maxWidth, maxHeight, margins) {
5023
+ const {beginAtZero, grace, ticks: tickOpts} = this.options;
 
5024
  const sampleSize = tickOpts.sampleSize;
5025
+ this.beforeUpdate();
5026
+ this.maxWidth = maxWidth;
5027
+ this.maxHeight = maxHeight;
5028
+ this._margins = margins = Object.assign({
5029
  left: 0,
5030
  right: 0,
5031
  top: 0,
5032
  bottom: 0
5033
  }, margins);
5034
+ this.ticks = null;
5035
+ this._labelSizes = null;
5036
+ this._gridLineItems = null;
5037
+ this._labelItems = null;
5038
+ this.beforeSetDimensions();
5039
+ this.setDimensions();
5040
+ this.afterSetDimensions();
5041
+ this._maxLength = this.isHorizontal()
5042
+ ? this.width + margins.left + margins.right
5043
+ : this.height + margins.top + margins.bottom;
5044
+ if (!this._dataLimitsCached) {
5045
+ this.beforeDataLimits();
5046
+ this.determineDataLimits();
5047
+ this.afterDataLimits();
5048
+ this._range = _addGrace(this, grace, beginAtZero);
5049
+ this._dataLimitsCached = true;
5050
+ }
5051
+ this.beforeBuildTicks();
5052
+ this.ticks = this.buildTicks() || [];
5053
+ this.afterBuildTicks();
5054
+ const samplingEnabled = sampleSize < this.ticks.length;
5055
+ this._convertTicksToLabels(samplingEnabled ? sample(this.ticks, sampleSize) : this.ticks);
5056
+ this.configure();
5057
+ this.beforeCalculateLabelRotation();
5058
+ this.calculateLabelRotation();
5059
+ this.afterCalculateLabelRotation();
5060
  if (tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto')) {
5061
+ this.ticks = autoSkip(this, this.ticks);
5062
+ this._labelSizes = null;
5063
  }
5064
  if (samplingEnabled) {
5065
+ this._convertTicksToLabels(this.ticks);
5066
  }
5067
+ this.beforeFit();
5068
+ this.fit();
5069
+ this.afterFit();
5070
+ this.afterUpdate();
5071
  }
5072
  configure() {
5073
+ let reversePixels = this.options.reverse;
 
5074
  let startPixel, endPixel;
5075
+ if (this.isHorizontal()) {
5076
+ startPixel = this.left;
5077
+ endPixel = this.right;
5078
  } else {
5079
+ startPixel = this.top;
5080
+ endPixel = this.bottom;
5081
  reversePixels = !reversePixels;
5082
  }
5083
+ this._startPixel = startPixel;
5084
+ this._endPixel = endPixel;
5085
+ this._reversePixels = reversePixels;
5086
+ this._length = endPixel - startPixel;
5087
+ this._alignToPixels = this.options.alignToPixels;
5088
  }
5089
  afterUpdate() {
5090
  callback(this.options.afterUpdate, [this]);
5093
  callback(this.options.beforeSetDimensions, [this]);
5094
  }
5095
  setDimensions() {
5096
+ if (this.isHorizontal()) {
5097
+ this.width = this.maxWidth;
5098
+ this.left = 0;
5099
+ this.right = this.width;
 
5100
  } else {
5101
+ this.height = this.maxHeight;
5102
+ this.top = 0;
5103
+ this.bottom = this.height;
5104
  }
5105
+ this.paddingLeft = 0;
5106
+ this.paddingTop = 0;
5107
+ this.paddingRight = 0;
5108
+ this.paddingBottom = 0;
5109
  }
5110
  afterSetDimensions() {
5111
  callback(this.options.afterSetDimensions, [this]);
5112
  }
5113
  _callHooks(name) {
5114
+ this.chart.notifyPlugins(name, this.getContext());
5115
+ callback(this.options[name], [this]);
 
5116
  }
5117
  beforeDataLimits() {
5118
  this._callHooks('beforeDataLimits');
5134
  callback(this.options.beforeTickToLabelConversion, [this]);
5135
  }
5136
  generateTickLabels(ticks) {
5137
+ const tickOpts = this.options.ticks;
 
5138
  let i, ilen, tick;
5139
  for (i = 0, ilen = ticks.length; i < ilen; i++) {
5140
  tick = ticks[i];
5141
+ tick.label = callback(tickOpts.callback, [tick.value, i, ticks], this);
5142
  }
5143
  }
5144
  afterTickToLabelConversion() {
5148
  callback(this.options.beforeCalculateLabelRotation, [this]);
5149
  }
5150
  calculateLabelRotation() {
5151
+ const options = this.options;
 
5152
  const tickOpts = options.ticks;
5153
+ const numTicks = this.ticks.length;
5154
  const minRotation = tickOpts.minRotation || 0;
5155
  const maxRotation = tickOpts.maxRotation;
5156
  let labelRotation = minRotation;
5157
  let tickWidth, maxHeight, maxLabelDiagonal;
5158
+ if (!this._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !this.isHorizontal()) {
5159
+ this.labelRotation = minRotation;
5160
  return;
5161
  }
5162
+ const labelSizes = this._getLabelSizes();
5163
  const maxLabelWidth = labelSizes.widest.width;
5164
  const maxLabelHeight = labelSizes.highest.height;
5165
+ const maxWidth = _limitValue(this.chart.width - maxLabelWidth, 0, this.maxWidth);
5166
+ tickWidth = options.offset ? this.maxWidth / numTicks : maxWidth / (numTicks - 1);
5167
  if (maxLabelWidth + 6 > tickWidth) {
5168
  tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
5169
+ maxHeight = this.maxHeight - getTickMarkLength(options.grid)
5170
+ - tickOpts.padding - getTitleHeight(options.title, this.chart.options.font);
5171
  maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
5172
  labelRotation = toDegrees(Math.min(
5173
  Math.asin(_limitValue((labelSizes.highest.height + 6) / tickWidth, -1, 1)),
5175
  ));
5176
  labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
5177
  }
5178
+ this.labelRotation = labelRotation;
5179
  }
5180
  afterCalculateLabelRotation() {
5181
  callback(this.options.afterCalculateLabelRotation, [this]);
5184
  callback(this.options.beforeFit, [this]);
5185
  }
5186
  fit() {
 
5187
  const minSize = {
5188
  width: 0,
5189
  height: 0
5190
  };
5191
+ const {chart, options: {ticks: tickOpts, title: titleOpts, grid: gridOpts}} = this;
5192
+ const display = this._isVisible();
5193
+ const isHorizontal = this.isHorizontal();
5194
  if (display) {
5195
  const titleHeight = getTitleHeight(titleOpts, chart.options.font);
5196
  if (isHorizontal) {
5197
+ minSize.width = this.maxWidth;
5198
  minSize.height = getTickMarkLength(gridOpts) + titleHeight;
5199
  } else {
5200
+ minSize.height = this.maxHeight;
5201
  minSize.width = getTickMarkLength(gridOpts) + titleHeight;
5202
  }
5203
+ if (tickOpts.display && this.ticks.length) {
5204
+ const {first, last, widest, highest} = this._getLabelSizes();
5205
  const tickPadding = tickOpts.padding * 2;
5206
+ const angleRadians = toRadians(this.labelRotation);
5207
  const cos = Math.cos(angleRadians);
5208
  const sin = Math.sin(angleRadians);
5209
  if (isHorizontal) {
5210
  const labelHeight = tickOpts.mirror ? 0 : sin * widest.width + cos * highest.height;
5211
+ minSize.height = Math.min(this.maxHeight, minSize.height + labelHeight + tickPadding);
5212
  } else {
5213
  const labelWidth = tickOpts.mirror ? 0 : cos * widest.width + sin * highest.height;
5214
+ minSize.width = Math.min(this.maxWidth, minSize.width + labelWidth + tickPadding);
5215
  }
5216
+ this._calculatePadding(first, last, sin, cos);
5217
  }
5218
  }
5219
+ this._handleMargins();
5220
  if (isHorizontal) {
5221
+ this.width = this._length = chart.width - this._margins.left - this._margins.right;
5222
+ this.height = minSize.height;
5223
  } else {
5224
+ this.width = minSize.width;
5225
+ this.height = this._length = chart.height - this._margins.top - this._margins.bottom;
5226
  }
5227
  }
5228
  _calculatePadding(first, last, sin, cos) {
5229
+ const {ticks: {align, padding}, position} = this.options;
5230
+ const isRotated = this.labelRotation !== 0;
5231
+ const labelsBelowTicks = position !== 'top' && this.axis === 'x';
5232
+ if (this.isHorizontal()) {
5233
+ const offsetLeft = this.getPixelForTick(0) - this.left;
5234
+ const offsetRight = this.right - this.getPixelForTick(this.ticks.length - 1);
 
5235
  let paddingLeft = 0;
5236
  let paddingRight = 0;
5237
  if (isRotated) {
5250
  paddingLeft = first.width / 2;
5251
  paddingRight = last.width / 2;
5252
  }
5253
+ this.paddingLeft = Math.max((paddingLeft - offsetLeft + padding) * this.width / (this.width - offsetLeft), 0);
5254
+ this.paddingRight = Math.max((paddingRight - offsetRight + padding) * this.width / (this.width - offsetRight), 0);
5255
  } else {
5256
  let paddingTop = last.height / 2;
5257
  let paddingBottom = first.height / 2;
5262
  paddingTop = last.height;
5263
  paddingBottom = 0;
5264
  }
5265
+ this.paddingTop = paddingTop + padding;
5266
+ this.paddingBottom = paddingBottom + padding;
5267
  }
5268
  }
5269
  _handleMargins() {
5270
+ if (this._margins) {
5271
+ this._margins.left = Math.max(this.paddingLeft, this._margins.left);
5272
+ this._margins.top = Math.max(this.paddingTop, this._margins.top);
5273
+ this._margins.right = Math.max(this.paddingRight, this._margins.right);
5274
+ this._margins.bottom = Math.max(this.paddingBottom, this._margins.bottom);
 
5275
  }
5276
  }
5277
  afterFit() {
5285
  return this.options.fullSize;
5286
  }
5287
  _convertTicksToLabels(ticks) {
5288
+ this.beforeTickToLabelConversion();
5289
+ this.generateTickLabels(ticks);
 
5290
  let i, ilen;
5291
  for (i = 0, ilen = ticks.length; i < ilen; i++) {
5292
  if (isNullOrUndef(ticks[i].label)) {
5295
  i--;
5296
  }
5297
  }
5298
+ this.afterTickToLabelConversion();
5299
  }
5300
  _getLabelSizes() {
5301
+ let labelSizes = this._labelSizes;
 
5302
  if (!labelSizes) {
5303
+ const sampleSize = this.options.ticks.sampleSize;
5304
+ let ticks = this.ticks;
5305
  if (sampleSize < ticks.length) {
5306
  ticks = sample(ticks, sampleSize);
5307
  }
5308
+ this._labelSizes = labelSizes = this._computeLabelSizes(ticks, ticks.length);
5309
  }
5310
  return labelSizes;
5311
  }
5368
  return this.getPixelForValue(ticks[index].value);
5369
  }
5370
  getPixelForDecimal(decimal) {
5371
+ if (this._reversePixels) {
 
5372
  decimal = 1 - decimal;
5373
  }
5374
+ const pixel = this._startPixel + decimal * this._length;
5375
+ return _int16Range(this._alignToPixels ? _alignPixel(this.chart, pixel, 0) : pixel);
5376
  }
5377
  getDecimalForPixel(pixel) {
5378
  const decimal = (pixel - this._startPixel) / this._length;
5388
  0;
5389
  }
5390
  getContext(index) {
5391
+ const ticks = this.ticks || [];
 
5392
  if (index >= 0 && index < ticks.length) {
5393
  const tick = ticks[index];
5394
  return tick.$context ||
5395
+ (tick.$context = createTickContext(this.getContext(), index, tick));
5396
  }
5397
+ return this.$context ||
5398
+ (this.$context = createScaleContext(this.chart.getContext(), this));
5399
  }
5400
  _tickSize() {
5401
+ const optionTicks = this.options.ticks;
5402
+ const rot = toRadians(this.labelRotation);
 
5403
  const cos = Math.abs(Math.cos(rot));
5404
  const sin = Math.abs(Math.sin(rot));
5405
+ const labelSizes = this._getLabelSizes();
5406
  const padding = optionTicks.autoSkipPadding || 0;
5407
  const w = labelSizes ? labelSizes.widest.width + padding : 0;
5408
  const h = labelSizes ? labelSizes.highest.height + padding : 0;
5409
+ return this.isHorizontal()
5410
  ? h * cos > w * sin ? w / cos : h / sin
5411
  : h * sin < w * cos ? h / cos : w / sin;
5412
  }
5418
  return this.getMatchingVisibleMetas().length > 0;
5419
  }
5420
  _computeGridLineItems(chartArea) {
5421
+ const axis = this.axis;
5422
+ const chart = this.chart;
5423
+ const options = this.options;
 
5424
  const {grid, position} = options;
5425
  const offset = grid.offset;
5426
+ const isHorizontal = this.isHorizontal();
5427
+ const ticks = this.ticks;
5428
  const ticksLength = ticks.length + (offset ? 1 : 0);
5429
  const tl = getTickMarkLength(grid);
5430
  const items = [];
5431
+ const borderOpts = grid.setContext(this.getContext());
5432
  const axisWidth = borderOpts.drawBorder ? borderOpts.borderWidth : 0;
5433
  const axisHalfWidth = axisWidth / 2;
5434
  const alignBorderValue = function(pixel) {
5437
  let borderValue, i, lineValue, alignedLineValue;
5438
  let tx1, ty1, tx2, ty2, x1, y1, x2, y2;
5439
  if (position === 'top') {
5440
+ borderValue = alignBorderValue(this.bottom);
5441
+ ty1 = this.bottom - tl;
5442
  ty2 = borderValue - axisHalfWidth;
5443
  y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
5444
  y2 = chartArea.bottom;
5445
  } else if (position === 'bottom') {
5446
+ borderValue = alignBorderValue(this.top);
5447
  y1 = chartArea.top;
5448
  y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
5449
  ty1 = borderValue + axisHalfWidth;
5450
+ ty2 = this.top + tl;
5451
  } else if (position === 'left') {
5452
+ borderValue = alignBorderValue(this.right);
5453
+ tx1 = this.right - tl;
5454
  tx2 = borderValue - axisHalfWidth;
5455
  x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
5456
  x2 = chartArea.right;
5457
  } else if (position === 'right') {
5458
+ borderValue = alignBorderValue(this.left);
5459
  x1 = chartArea.left;
5460
  x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
5461
  tx1 = borderValue + axisHalfWidth;
5462
+ tx2 = this.left + tl;
5463
  } else if (axis === 'x') {
5464
  if (position === 'center') {
5465
  borderValue = alignBorderValue((chartArea.top + chartArea.bottom) / 2 + 0.5);
5466
  } else if (isObject(position)) {
5467
  const positionAxisID = Object.keys(position)[0];
5468
  const value = position[positionAxisID];
5469
+ borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
5470
  }
5471
  y1 = chartArea.top;
5472
  y2 = chartArea.bottom;
5478
  } else if (isObject(position)) {
5479
  const positionAxisID = Object.keys(position)[0];
5480
  const value = position[positionAxisID];
5481
+ borderValue = alignBorderValue(this.chart.scales[positionAxisID].getPixelForValue(value));
5482
  }
5483
  tx1 = borderValue - axisHalfWidth;
5484
  tx2 = tx1 - tl;
5488
  const limit = valueOrDefault(options.ticks.maxTicksLimit, ticksLength);
5489
  const step = Math.max(1, Math.ceil(ticksLength / limit));
5490
  for (i = 0; i < ticksLength; i += step) {
5491
+ const optsAtIndex = grid.setContext(this.getContext(i));
5492
  const lineWidth = optsAtIndex.lineWidth;
5493
  const lineColor = optsAtIndex.color;
5494
  const borderDash = grid.borderDash || [];
5497
  const tickColor = optsAtIndex.tickColor;
5498
  const tickBorderDash = optsAtIndex.tickBorderDash || [];
5499
  const tickBorderDashOffset = optsAtIndex.tickBorderDashOffset;
5500
+ lineValue = getPixelForGridLine(this, i, offset);
5501
  if (lineValue === undefined) {
5502
  continue;
5503
  }
5526
  tickBorderDashOffset,
5527
  });
5528
  }
5529
+ this._ticksLength = ticksLength;
5530
+ this._borderValue = borderValue;
5531
  return items;
5532
  }
5533
  _computeLabelItems(chartArea) {
5534
+ const axis = this.axis;
5535
+ const options = this.options;
 
5536
  const {position, ticks: optionTicks} = options;
5537
+ const isHorizontal = this.isHorizontal();
5538
+ const ticks = this.ticks;
5539
  const {align, crossAlign, padding, mirror} = optionTicks;
5540
  const tl = getTickMarkLength(options.grid);
5541
  const tickAndPadding = tl + padding;
5542
  const hTickAndPadding = mirror ? -padding : tickAndPadding;
5543
+ const rotation = -toRadians(this.labelRotation);
5544
  const items = [];
5545
  let i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
5546
  let textBaseline = 'middle';
5547
  if (position === 'top') {
5548
+ y = this.bottom - hTickAndPadding;
5549
+ textAlign = this._getXAxisLabelAlignment();
5550
  } else if (position === 'bottom') {
5551
+ y = this.top + hTickAndPadding;
5552
+ textAlign = this._getXAxisLabelAlignment();
5553
  } else if (position === 'left') {
5554
+ const ret = this._getYAxisLabelAlignment(tl);
5555
  textAlign = ret.textAlign;
5556
  x = ret.x;
5557
  } else if (position === 'right') {
5558
+ const ret = this._getYAxisLabelAlignment(tl);
5559
  textAlign = ret.textAlign;
5560
  x = ret.x;
5561
  } else if (axis === 'x') {
5564
  } else if (isObject(position)) {
5565
  const positionAxisID = Object.keys(position)[0];
5566
  const value = position[positionAxisID];
5567
+ y = this.chart.scales[positionAxisID].getPixelForValue(value) + tickAndPadding;
5568
  }
5569
+ textAlign = this._getXAxisLabelAlignment();
5570
  } else if (axis === 'y') {
5571
  if (position === 'center') {
5572
  x = ((chartArea.left + chartArea.right) / 2) - tickAndPadding;
5573
  } else if (isObject(position)) {
5574
  const positionAxisID = Object.keys(position)[0];
5575
  const value = position[positionAxisID];
5576
+ x = this.chart.scales[positionAxisID].getPixelForValue(value);
5577
  }
5578
+ textAlign = this._getYAxisLabelAlignment(tl).textAlign;
5579
  }
5580
  if (axis === 'y') {
5581
  if (align === 'start') {
5584
  textBaseline = 'bottom';
5585
  }
5586
  }
5587
+ const labelSizes = this._getLabelSizes();
5588
  for (i = 0, ilen = ticks.length; i < ilen; ++i) {
5589
  tick = ticks[i];
5590
  label = tick.label;
5591
+ const optsAtIndex = optionTicks.setContext(this.getContext(i));
5592
+ pixel = this.getPixelForTick(i) + optionTicks.labelOffset;
5593
+ font = this._resolveTickFontOptions(i);
5594
  lineHeight = font.lineHeight;
5595
  lineCount = isArray(label) ? label.length : 1;
5596
  const halfCount = lineCount / 2;
5671
  return items;
5672
  }
5673
  _getXAxisLabelAlignment() {
5674
+ const {position, ticks} = this.options;
5675
+ const rotation = -toRadians(this.labelRotation);
 
5676
  if (rotation) {
5677
  return position === 'top' ? 'left' : 'right';
5678
  }
5685
  return align;
5686
  }
5687
  _getYAxisLabelAlignment(tl) {
5688
+ const {position, ticks: {crossAlign, mirror, padding}} = this.options;
5689
+ const labelSizes = this._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 = this.right + padding;
5697
  if (crossAlign === 'near') {
5698
  textAlign = 'left';
5699
  } else if (crossAlign === 'center') {
5704
  x += widest;
5705
  }
5706
  } else {
5707
+ x = this.right - tickAndPadding;
5708
  if (crossAlign === 'near') {
5709
  textAlign = 'right';
5710
  } else if (crossAlign === 'center') {
5712
  x -= (widest / 2);
5713
  } else {
5714
  textAlign = 'left';
5715
+ x = this.left;
5716
  }
5717
  }
5718
  } else if (position === 'right') {
5719
  if (mirror) {
5720
+ x = this.left + padding;
5721
  if (crossAlign === 'near') {
5722
  textAlign = 'right';
5723
  } else if (crossAlign === 'center') {
5728
  x -= widest;
5729
  }
5730
  } else {
5731
+ x = this.left + tickAndPadding;
5732
  if (crossAlign === 'near') {
5733
  textAlign = 'left';
5734
  } else if (crossAlign === 'center') {
5736
  x += widest / 2;
5737
  } else {
5738
  textAlign = 'right';
5739
+ x = this.right;
5740
  }
5741
  }
5742
  } else {
5745
  return {textAlign, x};
5746
  }
5747
  _computeLabelArea() {
5748
+ if (this.options.ticks.mirror) {
 
5749
  return;
5750
  }
5751
+ const chart = this.chart;
5752
+ const position = this.options.position;
5753
  if (position === 'left' || position === 'right') {
5754
+ return {top: 0, left: this.left, bottom: chart.height, right: this.right};
5755
  } if (position === 'top' || position === 'bottom') {
5756
+ return {top: this.top, left: 0, bottom: this.bottom, right: chart.width};
5757
  }
5758
  }
5759
  drawBackground() {
5766
  }
5767
  }
5768
  getLineWidthForValue(value) {
5769
+ const grid = this.options.grid;
5770
+ if (!this._isVisible() || !grid.display) {
 
5771
  return 0;
5772
  }
5773
+ const ticks = this.ticks;
5774
  const index = ticks.findIndex(t => t.value === value);
5775
  if (index >= 0) {
5776
+ const opts = grid.setContext(this.getContext(index));
5777
  return opts.lineWidth;
5778
  }
5779
  return 0;
5780
  }
5781
  drawGrid(chartArea) {
5782
+ const grid = this.options.grid;
5783
+ const ctx = this.ctx;
5784
+ const items = this._gridLineItems || (this._gridLineItems = this._computeGridLineItems(chartArea));
 
5785
  let i, ilen;
5786
  const drawLine = (p1, p2, style) => {
5787
  if (!style.width || !style.color) {
5824
  }
5825
  }
5826
  drawBorder() {
5827
+ const {chart, ctx, options: {grid}} = this;
5828
+ const borderOpts = grid.setContext(this.getContext());
 
5829
  const axisWidth = grid.drawBorder ? borderOpts.borderWidth : 0;
5830
  if (!axisWidth) {
5831
  return;
5832
  }
5833
+ const lastLineWidth = grid.setContext(this.getContext(0)).lineWidth;
5834
+ const borderValue = this._borderValue;
5835
  let x1, x2, y1, y2;
5836
+ if (this.isHorizontal()) {
5837
+ x1 = _alignPixel(chart, this.left, axisWidth) - axisWidth / 2;
5838
+ x2 = _alignPixel(chart, this.right, lastLineWidth) + lastLineWidth / 2;
5839
  y1 = y2 = borderValue;
5840
  } else {
5841
+ y1 = _alignPixel(chart, this.top, axisWidth) - axisWidth / 2;
5842
+ y2 = _alignPixel(chart, this.bottom, lastLineWidth) + lastLineWidth / 2;
5843
  x1 = x2 = borderValue;
5844
  }
5845
  ctx.save();
5852
  ctx.restore();
5853
  }
5854
  drawLabels(chartArea) {
5855
+ const optionTicks = this.options.ticks;
 
5856
  if (!optionTicks.display) {
5857
  return;
5858
  }
5859
+ const ctx = this.ctx;
5860
+ const area = this._computeLabelArea();
5861
  if (area) {
5862
  clipArea(ctx, area);
5863
  }
5864
+ const items = this._labelItems || (this._labelItems = this._computeLabelItems(chartArea));
5865
  let i, ilen;
5866
  for (i = 0, ilen = items.length; i < ilen; ++i) {
5867
  const item = items[i];
5906
  });
5907
  }
5908
  draw(chartArea) {
5909
+ if (!this._isVisible()) {
 
5910
  return;
5911
  }
5912
+ this.drawBackground();
5913
+ this.drawGrid(chartArea);
5914
+ this.drawBorder();
5915
+ this.drawTitle();
5916
+ this.drawLabels(chartArea);
5917
  }
5918
  _layers() {
5919
+ const opts = this.options;
 
5920
  const tz = opts.ticks && opts.ticks.z || 0;
5921
  const gz = valueOrDefault(opts.grid && opts.grid.z, -1);
5922
+ if (!this._isVisible() || this.draw !== Scale.prototype.draw) {
5923
  return [{
5924
  z: tz,
5925
+ draw: (chartArea) => {
5926
+ this.draw(chartArea);
5927
  }
5928
  }];
5929
  }
5930
  return [{
5931
  z: gz,
5932
+ draw: (chartArea) => {
5933
+ this.drawBackground();
5934
+ this.drawGrid(chartArea);
5935
+ this.drawTitle();
5936
  }
5937
  }, {
5938
  z: gz + 1,
5939
+ draw: () => {
5940
+ this.drawBorder();
5941
  }
5942
  }, {
5943
  z: tz,
5944
+ draw: (chartArea) => {
5945
+ this.drawLabels(chartArea);
5946
  }
5947
  }];
5948
  }
5949
  getMatchingVisibleMetas(type) {
5950
+ const metas = this.chart.getSortedVisibleDatasetMetas();
5951
+ const axisID = this.axis + 'AxisID';
 
5952
  const result = [];
5953
  let i, ilen;
5954
  for (i = 0, ilen = metas.length; i < ilen; ++i) {
5955
  const meta = metas[i];
5956
+ if (meta[axisID] === this.id && (!type || meta.type === type)) {
5957
  result.push(meta);
5958
  }
5959
  }
5964
  return toFont(opts.font);
5965
  }
5966
  _maxDigits() {
5967
+ const fontSize = this._resolveTickFontOptions(0).lineHeight;
5968
+ return (this.isHorizontal() ? this.width : this.height) / fontSize;
 
5969
  }
5970
  }
5971
 
5980
  return Object.prototype.isPrototypeOf.call(this.type.prototype, type.prototype);
5981
  }
5982
  register(item) {
 
5983
  const proto = Object.getPrototypeOf(item);
5984
  let parentScope;
5985
  if (isIChartComponent(proto)) {
5986
+ parentScope = this.register(proto);
5987
  }
5988
+ const items = this.items;
5989
  const id = item.id;
5990
+ const scope = this.scope + '.' + id;
5991
  if (!id) {
5992
  throw new Error('class does not have id: ' + item);
5993
  }
5996
  }
5997
  items[id] = item;
5998
  registerDefaults(item, scope, parentScope);
5999
+ if (this.override) {
6000
  defaults.override(item.id, item.overrides);
6001
  }
6002
  return scope;
6099
  this._each('unregister', args, this.scales);
6100
  }
6101
  _each(method, args, typedRegistry) {
 
6102
  [...args].forEach(arg => {
6103
+ const reg = typedRegistry || this._getRegistryForType(arg);
6104
+ if (typedRegistry || reg.isForType(arg) || (reg === this.plugins && arg.id)) {
6105
+ this._exec(method, reg, arg);
6106
  } else {
6107
  each(arg, item => {
6108
+ const itemReg = typedRegistry || this._getRegistryForType(item);
6109
+ this._exec(method, itemReg, item);
6110
  });
6111
  }
6112
  });
6141
  this._init = [];
6142
  }
6143
  notify(chart, hook, args, filter) {
 
6144
  if (hook === 'beforeInit') {
6145
+ this._init = this._createDescriptors(chart, true);
6146
+ this._notify(this._init, chart, 'install');
6147
  }
6148
+ const descriptors = filter ? this._descriptors(chart).filter(filter) : this._descriptors(chart);
6149
+ const result = this._notify(descriptors, chart, hook, args);
6150
  if (hook === 'destroy') {
6151
+ this._notify(descriptors, chart, 'stop');
6152
+ this._notify(this._init, chart, 'uninstall');
6153
  }
6154
  return result;
6155
  }
6279
  const scales = Object.create(null);
6280
  Object.keys(configScales).forEach(id => {
6281
  const scaleConf = configScales[id];
6282
+ if (!isObject(scaleConf)) {
6283
+ return console.error(`Invalid scale configuration for scale: ${id}`);
6284
+ }
6285
+ if (scaleConf._proxy) {
6286
+ return console.warn(`Ignoring resolver passed as options for scale: ${id}`);
6287
+ }
6288
  const axis = determineAxis(id, scaleConf);
6289
  const defaultId = getDefaultScaleIDFromAxis(axis, chartIndexAxis);
6290
  const defaultScaleOptions = chartDefaults.scales || {};
6507
  }
6508
  return cached;
6509
  }
6510
+ const hasFunction = value => isObject(value)
6511
+ && Object.getOwnPropertyNames(value).reduce((acc, key) => acc || isFunction(value[key]), false);
6512
  function needContext(proxy, names) {
6513
  const {isScriptable, isIndexable} = _descriptors(proxy);
6514
  for (const prop of names) {
6515
+ const scriptable = isScriptable(prop);
6516
+ const indexable = isIndexable(prop);
6517
+ const value = (indexable || scriptable) && proxy[prop];
6518
+ if ((scriptable && (isFunction(value) || hasFunction(value)))
6519
+ || (indexable && isArray(value))) {
6520
  return true;
6521
  }
6522
  }
6523
  return false;
6524
  }
6525
 
6526
+ var version = "3.6.2";
6527
 
6528
  const KNOWN_POSITIONS = ['top', 'bottom', 'left', 'right', 'chartArea'];
6529
  function positionIsHorizontal(position, axis) {
6563
  const canvas = getCanvas(key);
6564
  return Object.values(instances).filter((c) => c.canvas === canvas).pop();
6565
  };
6566
+ function moveNumericKeys(obj, start, move) {
6567
+ const keys = Object.keys(obj);
6568
+ for (const key of keys) {
6569
+ const intKey = +key;
6570
+ if (intKey >= start) {
6571
+ const value = obj[key];
6572
+ delete obj[key];
6573
+ if (move > 0 || intKey > start) {
6574
+ obj[intKey + move] = value;
6575
+ }
6576
+ }
6577
+ }
6578
+ }
6579
  class Chart {
6580
  constructor(item, userConfig) {
 
6581
  const config = this.config = new Config(userConfig);
6582
  const initialCanvas = getCanvas(item);
6583
  const existingChart = getChart(initialCanvas);
6587
  ' must be destroyed before the canvas can be reused.'
6588
  );
6589
  }
6590
+ const options = config.createResolver(config.chartOptionScopes(), this.getContext());
6591
  this.platform = new (config.platform || _detectPlatform(initialCanvas))();
6592
+ this.platform.updateConfig(config);
6593
+ const context = this.platform.acquireContext(initialCanvas, options.aspectRatio);
6594
  const canvas = context && context.canvas;
6595
  const height = canvas && canvas.height;
6596
  const width = canvas && canvas.width;
6620
  this._animationsDisabled = undefined;
6621
  this.$context = undefined;
6622
  this._doResize = debounce(mode => this.update(mode), options.resizeDelay || 0);
6623
+ this._dataChanges = [];
6624
+ instances[this.id] = this;
6625
  if (!context || !canvas) {
6626
  console.error("Failed to create chart: can't acquire context from the given item");
6627
  return;
6628
  }
6629
+ animator.listen(this, 'complete', onAnimationsComplete);
6630
+ animator.listen(this, 'progress', onAnimationProgress);
6631
+ this._initialize();
6632
+ if (this.attached) {
6633
+ this.update();
6634
  }
6635
  }
6636
  get aspectRatio() {
6656
  this.config.options = options;
6657
  }
6658
  _initialize() {
6659
+ this.notifyPlugins('beforeInit');
6660
+ if (this.options.responsive) {
6661
+ this.resize();
 
6662
  } else {
6663
+ retinaScale(this, this.options.devicePixelRatio);
6664
  }
6665
+ this.bindEvents();
6666
+ this.notifyPlugins('afterInit');
6667
+ return this;
6668
  }
6669
  clear() {
6670
  clearCanvas(this.canvas, this.ctx);
6682
  }
6683
  }
6684
  _resize(width, height) {
6685
+ const options = this.options;
6686
+ const canvas = this.canvas;
6687
+ const aspectRatio = options.maintainAspectRatio && this.aspectRatio;
6688
+ const newSize = this.platform.getMaximumSize(canvas, width, height, aspectRatio);
6689
+ const newRatio = options.devicePixelRatio || this.platform.getDevicePixelRatio();
6690
+ const mode = this.width ? 'resize' : 'attach';
6691
+ this.width = newSize.width;
6692
+ this.height = newSize.height;
6693
+ this._aspectRatio = this.aspectRatio;
6694
+ if (!retinaScale(this, newRatio, true)) {
 
6695
  return;
6696
  }
6697
+ this.notifyPlugins('resize', {size: newSize});
6698
+ callback(options.onResize, [this, newSize], this);
6699
+ if (this.attached) {
6700
+ if (this._doResize(mode)) {
6701
+ this.render();
6702
  }
6703
  }
6704
  }
6710
  });
6711
  }
6712
  buildOrUpdateScales() {
6713
+ const options = this.options;
 
6714
  const scaleOpts = options.scales;
6715
+ const scales = this.scales;
6716
  const updated = Object.keys(scales).reduce((obj, id) => {
6717
  obj[id] = false;
6718
  return obj;
6750
  scale = new scaleClass({
6751
  id,
6752
  type: scaleType,
6753
+ ctx: this.ctx,
6754
+ chart: this
6755
  });
6756
  scales[scale.id] = scale;
6757
  }
6763
  }
6764
  });
6765
  each(scales, (scale) => {
6766
+ layouts.configure(this, scale, scale.options);
6767
+ layouts.addBox(this, scale);
6768
  });
6769
  }
6770
  _updateMetasets() {
6771
+ const metasets = this._metasets;
6772
+ const numData = this.data.datasets.length;
 
6773
  const numMeta = metasets.length;
6774
  metasets.sort((a, b) => a.index - b.index);
6775
  if (numMeta > numData) {
6776
  for (let i = numData; i < numMeta; ++i) {
6777
+ this._destroyDatasetMeta(i);
6778
  }
6779
  metasets.splice(numData, numMeta - numData);
6780
  }
6781
+ this._sortedMetasets = metasets.slice(0).sort(compare2Level('order', 'index'));
6782
  }
6783
  _removeUnreferencedMetasets() {
6784
+ const {_metasets: metasets, data: {datasets}} = this;
 
6785
  if (metasets.length > datasets.length) {
6786
+ delete this._stacks;
6787
  }
6788
  metasets.forEach((meta, index) => {
6789
  if (datasets.filter(x => x === meta._dataset).length === 0) {
6790
+ this._destroyDatasetMeta(index);
6791
  }
6792
  });
6793
  }
6794
  buildOrUpdateControllers() {
 
6795
  const newControllers = [];
6796
+ const datasets = this.data.datasets;
6797
  let i, ilen;
6798
+ this._removeUnreferencedMetasets();
6799
  for (i = 0, ilen = datasets.length; i < ilen; i++) {
6800
  const dataset = datasets[i];
6801
+ let meta = this.getDatasetMeta(i);
6802
+ const type = dataset.type || this.config.type;
6803
  if (meta.type && meta.type !== type) {
6804
+ this._destroyDatasetMeta(i);
6805
+ meta = this.getDatasetMeta(i);
6806
  }
6807
  meta.type = type;
6808
+ meta.indexAxis = dataset.indexAxis || getIndexAxis(type, this.options);
6809
  meta.order = dataset.order || 0;
6810
  meta.index = i;
6811
  meta.label = '' + dataset.label;
6812
+ meta.visible = this.isDatasetVisible(i);
6813
  if (meta.controller) {
6814
  meta.controller.updateIndex(i);
6815
  meta.controller.linkScales();
6820
  dataElementType: registry.getElement(dataElementType),
6821
  datasetElementType: datasetElementType && registry.getElement(datasetElementType)
6822
  });
6823
+ meta.controller = new ControllerClass(this, i);
6824
  newControllers.push(meta.controller);
6825
  }
6826
  }
6827
+ this._updateMetasets();
6828
  return newControllers;
6829
  }
6830
  _resetElements() {
6831
+ each(this.data.datasets, (dataset, datasetIndex) => {
6832
+ this.getDatasetMeta(datasetIndex).controller.reset();
6833
+ }, this);
 
6834
  }
6835
  reset() {
6836
  this._resetElements();
6837
  this.notifyPlugins('reset');
6838
  }
6839
  update(mode) {
6840
+ const config = this.config;
 
6841
  config.update();
6842
+ const options = this._options = config.createResolver(config.chartOptionScopes(), this.getContext());
6843
+ const animsDisabled = this._animationsDisabled = !options.animation;
6844
+ this._updateScales();
6845
+ this._checkEventBindings();
6846
+ this._updateHiddenIndices();
6847
+ this._plugins.invalidate();
6848
+ if (this.notifyPlugins('beforeUpdate', {mode, cancelable: true}) === false) {
 
 
 
 
 
 
 
 
6849
  return;
6850
  }
6851
+ const newControllers = this.buildOrUpdateControllers();
6852
+ this.notifyPlugins('beforeElementsUpdate');
6853
  let minPadding = 0;
6854
+ for (let i = 0, ilen = this.data.datasets.length; i < ilen; i++) {
6855
+ const {controller} = this.getDatasetMeta(i);
6856
  const reset = !animsDisabled && newControllers.indexOf(controller) === -1;
6857
  controller.buildOrUpdateElements(reset);
6858
  minPadding = Math.max(+controller.getMaxOverflow(), minPadding);
6859
  }
6860
+ minPadding = this._minPadding = options.layout.autoPadding ? minPadding : 0;
6861
+ this._updateLayout(minPadding);
6862
  if (!animsDisabled) {
6863
  each(newControllers, (controller) => {
6864
  controller.reset();
6865
  });
6866
  }
6867
+ this._updateDatasets(mode);
6868
+ this.notifyPlugins('afterUpdate', {mode});
6869
+ this._layers.sort(compare2Level('z', '_idx'));
6870
+ if (this._lastEvent) {
6871
+ this._eventHandler(this._lastEvent, true);
6872
+ }
6873
+ this.render();
6874
+ }
6875
+ _updateScales() {
6876
+ each(this.scales, (scale) => {
6877
+ layouts.removeBox(this, scale);
6878
+ });
6879
+ this.ensureScalesHaveIDs();
6880
+ this.buildOrUpdateScales();
6881
+ }
6882
+ _checkEventBindings() {
6883
+ const options = this.options;
6884
+ const existingEvents = new Set(Object.keys(this._listeners));
6885
+ const newEvents = new Set(options.events);
6886
+ if (!setsEqual(existingEvents, newEvents) || !!this._responsiveListeners !== options.responsive) {
6887
+ this.unbindEvents();
6888
+ this.bindEvents();
6889
+ }
6890
+ }
6891
+ _updateHiddenIndices() {
6892
+ const {_hiddenIndices} = this;
6893
+ const changes = this._getUniformDataChanges() || [];
6894
+ for (const {method, start, count} of changes) {
6895
+ const move = method === '_removeElements' ? -count : count;
6896
+ moveNumericKeys(_hiddenIndices, start, move);
6897
  }
6898
+ }
6899
+ _getUniformDataChanges() {
6900
+ const _dataChanges = this._dataChanges;
6901
+ if (!_dataChanges || !_dataChanges.length) {
6902
+ return;
6903
+ }
6904
+ this._dataChanges = [];
6905
+ const datasetCount = this.data.datasets.length;
6906
+ const makeSet = (idx) => new Set(
6907
+ _dataChanges
6908
+ .filter(c => c[0] === idx)
6909
+ .map((c, i) => i + ',' + c.splice(1).join(','))
6910
+ );
6911
+ const changeSet = makeSet(0);
6912
+ for (let i = 1; i < datasetCount; i++) {
6913
+ if (!setsEqual(changeSet, makeSet(i))) {
6914
+ return;
6915
+ }
6916
+ }
6917
+ return Array.from(changeSet)
6918
+ .map(c => c.split(','))
6919
+ .map(a => ({method: a[1], start: +a[2], count: +a[3]}));
6920
  }
6921
  _updateLayout(minPadding) {
6922
+ if (this.notifyPlugins('beforeLayout', {cancelable: true}) === false) {
 
6923
  return;
6924
  }
6925
+ layouts.update(this, this.width, this.height, minPadding);
6926
+ const area = this.chartArea;
6927
  const noArea = area.width <= 0 || area.height <= 0;
6928
+ this._layers = [];
6929
+ each(this.boxes, (box) => {
6930
  if (noArea && box.position === 'chartArea') {
6931
  return;
6932
  }
6933
  if (box.configure) {
6934
  box.configure();
6935
  }
6936
+ this._layers.push(...box._layers());
6937
+ }, this);
6938
+ this._layers.forEach((item, index) => {
6939
  item._idx = index;
6940
  });
6941
+ this.notifyPlugins('afterLayout');
6942
  }
6943
  _updateDatasets(mode) {
6944
+ if (this.notifyPlugins('beforeDatasetsUpdate', {mode, cancelable: true}) === false) {
 
 
6945
  return;
6946
  }
6947
+ for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
6948
+ this.getDatasetMeta(i).controller.configure();
6949
+ }
6950
+ for (let i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
6951
+ this._updateDataset(i, isFunction(mode) ? mode({datasetIndex: i}) : mode);
6952
  }
6953
+ this.notifyPlugins('afterDatasetsUpdate', {mode});
6954
  }
6955
  _updateDataset(index, mode) {
6956
+ const meta = this.getDatasetMeta(index);
 
6957
  const args = {meta, index, mode, cancelable: true};
6958
+ if (this.notifyPlugins('beforeDatasetUpdate', args) === false) {
6959
  return;
6960
  }
6961
  meta.controller._update(mode);
6962
  args.cancelable = false;
6963
+ this.notifyPlugins('afterDatasetUpdate', args);
6964
  }
6965
  render() {
6966
+ if (this.notifyPlugins('beforeRender', {cancelable: true}) === false) {
 
6967
  return;
6968
  }
6969
+ if (animator.has(this)) {
6970
+ if (this.attached && !animator.running(this)) {
6971
+ animator.start(this);
6972
  }
6973
  } else {
6974
+ this.draw();
6975
+ onAnimationsComplete({chart: this});
6976
  }
6977
  }
6978
  draw() {
 
6979
  let i;
6980
+ if (this._resizeBeforeDraw) {
6981
+ const {width, height} = this._resizeBeforeDraw;
6982
+ this._resize(width, height);
6983
+ this._resizeBeforeDraw = null;
6984
  }
6985
+ this.clear();
6986
+ if (this.width <= 0 || this.height <= 0) {
6987
  return;
6988
  }
6989
+ if (this.notifyPlugins('beforeDraw', {cancelable: true}) === false) {
6990
  return;
6991
  }
6992
+ const layers = this._layers;
6993
  for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
6994
+ layers[i].draw(this.chartArea);
6995
  }
6996
+ this._drawDatasets();
6997
  for (; i < layers.length; ++i) {
6998
+ layers[i].draw(this.chartArea);
6999
  }
7000
+ this.notifyPlugins('afterDraw');
7001
  }
7002
  _getSortedDatasetMetas(filterVisible) {
7003
+ const metasets = this._sortedMetasets;
 
7004
  const result = [];
7005
  let i, ilen;
7006
  for (i = 0, ilen = metasets.length; i < ilen; ++i) {
7015
  return this._getSortedDatasetMetas(true);
7016
  }
7017
  _drawDatasets() {
7018
+ if (this.notifyPlugins('beforeDatasetsDraw', {cancelable: true}) === false) {
 
7019
  return;
7020
  }
7021
+ const metasets = this.getSortedVisibleDatasetMetas();
7022
  for (let i = metasets.length - 1; i >= 0; --i) {
7023
+ this._drawDataset(metasets[i]);
7024
  }
7025
+ this.notifyPlugins('afterDatasetsDraw');
7026
  }
7027
  _drawDataset(meta) {
7028
+ const ctx = this.ctx;
 
7029
  const clip = meta._clip;
7030
  const useClip = !clip.disabled;
7031
+ const area = this.chartArea;
7032
  const args = {
7033
  meta,
7034
  index: meta.index,
7035
  cancelable: true
7036
  };
7037
+ if (this.notifyPlugins('beforeDatasetDraw', args) === false) {
7038
  return;
7039
  }
7040
  if (useClip) {
7041
  clipArea(ctx, {
7042
  left: clip.left === false ? 0 : area.left - clip.left,
7043
+ right: clip.right === false ? this.width : area.right + clip.right,
7044
  top: clip.top === false ? 0 : area.top - clip.top,
7045
+ bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom
7046
  });
7047
  }
7048
  meta.controller.draw();
7050
  unclipArea(ctx);
7051
  }
7052
  args.cancelable = false;
7053
+ this.notifyPlugins('afterDatasetDraw', args);
7054
  }
7055
  getElementsAtEventForMode(e, mode, options, useFinalPosition) {
7056
  const method = Interaction.modes[mode];
7060
  return [];
7061
  }
7062
  getDatasetMeta(datasetIndex) {
7063
+ const dataset = this.data.datasets[datasetIndex];
7064
+ const metasets = this._metasets;
 
7065
  let meta = metasets.filter(x => x && x._dataset === dataset).pop();
7066
  if (!meta) {
7067
  meta = {
7083
  return meta;
7084
  }
7085
  getContext() {
7086
+ return this.$context || (this.$context = createContext(null, {chart: this, type: 'chart'}));
7087
  }
7088
  getVisibleDatasetCount() {
7089
  return this.getSortedVisibleDatasetMetas().length;
7107
  return !this._hiddenIndices[index];
7108
  }
7109
  _updateVisibility(datasetIndex, dataIndex, visible) {
 
7110
  const mode = visible ? 'show' : 'hide';
7111
+ const meta = this.getDatasetMeta(datasetIndex);
7112
  const anims = meta.controller._resolveAnimations(undefined, mode);
7113
  if (defined(dataIndex)) {
7114
  meta.data[dataIndex].hidden = !visible;
7115
+ this.update();
7116
  } else {
7117
+ this.setDatasetVisibility(datasetIndex, visible);
7118
  anims.update(meta, {visible});
7119
+ this.update((ctx) => ctx.datasetIndex === datasetIndex ? mode : undefined);
7120
  }
7121
  }
7122
  hide(datasetIndex, dataIndex) {
7126
  this._updateVisibility(datasetIndex, dataIndex, true);
7127
  }
7128
  _destroyDatasetMeta(datasetIndex) {
7129
+ const meta = this._metasets[datasetIndex];
 
7130
  if (meta && meta.controller) {
7131
  meta.controller._destroy();
 
7132
  }
7133
+ delete this._metasets[datasetIndex];
7134
  }
7135
  _stop() {
 
7136
  let i, ilen;
7137
+ this.stop();
7138
+ animator.remove(this);
7139
+ for (i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
7140
+ this._destroyDatasetMeta(i);
7141
  }
7142
  }
7143
  destroy() {
7144
+ const {canvas, ctx} = this;
7145
+ this._stop();
7146
+ this.config.clearCache();
 
7147
  if (canvas) {
7148
+ this.unbindEvents();
7149
  clearCanvas(canvas, ctx);
7150
+ this.platform.releaseContext(ctx);
7151
+ this.canvas = null;
7152
+ this.ctx = null;
7153
  }
7154
+ this.notifyPlugins('destroy');
7155
+ delete instances[this.id];
7156
  }
7157
  toBase64Image(...args) {
7158
  return this.canvas.toDataURL(...args);
7166
  }
7167
  }
7168
  bindUserEvents() {
7169
+ const listeners = this._listeners;
7170
+ const platform = this.platform;
 
7171
  const _add = (type, listener) => {
7172
+ platform.addEventListener(this, type, listener);
7173
  listeners[type] = listener;
7174
  };
7175
+ const listener = (e, x, y) => {
7176
  e.offsetX = x;
7177
  e.offsetY = y;
7178
+ this._eventHandler(e);
7179
  };
7180
+ each(this.options.events, (type) => _add(type, listener));
7181
  }
7182
  bindResponsiveEvents() {
7183
+ if (!this._responsiveListeners) {
7184
+ this._responsiveListeners = {};
 
7185
  }
7186
+ const listeners = this._responsiveListeners;
7187
+ const platform = this.platform;
7188
  const _add = (type, listener) => {
7189
+ platform.addEventListener(this, type, listener);
7190
  listeners[type] = listener;
7191
  };
7192
  const _remove = (type, listener) => {
7193
  if (listeners[type]) {
7194
+ platform.removeEventListener(this, type, listener);
7195
  delete listeners[type];
7196
  }
7197
  };
7198
  const listener = (width, height) => {
7199
+ if (this.canvas) {
7200
+ this.resize(width, height);
7201
  }
7202
  };
7203
  let detached;
7204
  const attached = () => {
7205
  _remove('attach', attached);
7206
+ this.attached = true;
7207
+ this.resize();
7208
  _add('resize', listener);
7209
  _add('detach', detached);
7210
  };
7211
  detached = () => {
7212
+ this.attached = false;
7213
  _remove('resize', listener);
7214
+ this._stop();
7215
+ this._resize(0, 0);
7216
  _add('attach', attached);
7217
  };
7218
+ if (platform.isAttached(this.canvas)) {
7219
  attached();
7220
  } else {
7221
  detached();
7222
  }
7223
  }
7224
  unbindEvents() {
7225
+ each(this._listeners, (listener, type) => {
7226
+ this.platform.removeEventListener(this, type, listener);
 
7227
  });
7228
+ this._listeners = {};
7229
+ each(this._responsiveListeners, (listener, type) => {
7230
+ this.platform.removeEventListener(this, type, listener);
7231
  });
7232
+ this._responsiveListeners = undefined;
7233
  }
7234
  updateHoverStyle(items, mode, enabled) {
7235
  const prefix = enabled ? 'set' : 'remove';
7250
  return this._active || [];
7251
  }
7252
  setActiveElements(activeElements) {
7253
+ const lastActive = this._active || [];
 
7254
  const active = activeElements.map(({datasetIndex, index}) => {
7255
+ const meta = this.getDatasetMeta(datasetIndex);
7256
  if (!meta) {
7257
  throw new Error('No dataset found at index ' + datasetIndex);
7258
  }
7264
  });
7265
  const changed = !_elementsEqual(active, lastActive);
7266
  if (changed) {
7267
+ this._active = active;
7268
+ this._updateHoverStyles(active, lastActive);
7269
  }
7270
  }
7271
  notifyPlugins(hook, args, filter) {
7272
  return this._plugins.notify(this, hook, args, filter);
7273
  }
7274
  _updateHoverStyles(active, lastActive, replay) {
7275
+ const hoverOptions = this.options.hover;
 
7276
  const diff = (a, b) => a.filter(x => !b.some(y => x.datasetIndex === y.datasetIndex && x.index === y.index));
7277
  const deactivated = diff(lastActive, active);
7278
  const activated = replay ? active : diff(active, lastActive);
7279
  if (deactivated.length) {
7280
+ this.updateHoverStyle(deactivated, hoverOptions.mode, false);
7281
  }
7282
  if (activated.length && hoverOptions.mode) {
7283
+ this.updateHoverStyle(activated, hoverOptions.mode, true);
7284
  }
7285
  }
7286
  _eventHandler(e, replay) {
 
7287
  const args = {event: e, replay, cancelable: true};
7288
+ const eventFilter = (plugin) => (plugin.options.events || this.options.events).includes(e.native.type);
7289
+ if (this.notifyPlugins('beforeEvent', args, eventFilter) === false) {
7290
  return;
7291
  }
7292
+ const changed = this._handleEvent(e, replay);
7293
  args.cancelable = false;
7294
+ this.notifyPlugins('afterEvent', args, eventFilter);
7295
  if (changed || args.changed) {
7296
+ this.render();
7297
  }
7298
+ return this;
7299
  }
7300
  _handleEvent(e, replay) {
7301
+ const {_active: lastActive = [], options} = this;
 
7302
  const hoverOptions = options.hover;
7303
  const useFinalPosition = replay;
7304
  let active = [];
7305
  let changed = false;
7306
  let lastEvent = null;
7307
  if (e.type !== 'mouseout') {
7308
+ active = this.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions, useFinalPosition);
7309
+ lastEvent = e.type === 'click' ? this._lastEvent : e;
7310
  }
7311
+ this._lastEvent = null;
7312
+ if (_isPointInArea(e, this.chartArea, this._minPadding)) {
7313
+ callback(options.onHover, [e, active, this], this);
7314
  if (e.type === 'mouseup' || e.type === 'click' || e.type === 'contextmenu') {
7315
+ callback(options.onClick, [e, active, this], this);
7316
  }
7317
  }
7318
  changed = !_elementsEqual(active, lastActive);
7319
  if (changed || replay) {
7320
+ this._active = active;
7321
+ this._updateHoverStyles(active, lastActive, replay);
7322
  }
7323
+ this._lastEvent = lastEvent;
7324
  return changed;
7325
  }
7326
  }
7403
  _date: DateAdapter
7404
  };
7405
 
7406
+ function getAllScaleValues(scale, type) {
7407
  if (!scale._cache.$bar) {
7408
+ const visibleMetas = scale.getMatchingVisibleMetas(type);
7409
  let values = [];
7410
+ for (let i = 0, ilen = visibleMetas.length; i < ilen; i++) {
7411
+ values = values.concat(visibleMetas[i].controller.getAllParsedValues(scale));
7412
  }
7413
  scale._cache.$bar = _arrayUnique(values.sort((a, b) => a - b));
7414
  }
7415
  return scale._cache.$bar;
7416
  }
7417
+ function computeMinSampleSize(meta) {
7418
+ const scale = meta.iScale;
7419
+ const values = getAllScaleValues(scale, meta.type);
7420
  let min = scale._length;
7421
  let i, ilen, curr, prev;
7422
  const updateMinAndPrev = () => {
7585
  function startEnd(v, start, end) {
7586
  return v === 'start' ? start : v === 'end' ? end : v;
7587
  }
7588
+ function setInflateAmount(properties, {inflateAmount}, ratio) {
7589
+ properties.inflateAmount = inflateAmount === 'auto'
7590
+ ? ratio === 1 ? 0.33 : 0
7591
+ : inflateAmount;
7592
+ }
7593
  class BarController extends DatasetController {
7594
  parsePrimitiveData(meta, data, start, count) {
7595
  return parseArrayOrPrimitive(meta, data, start, count);
7624
  return 0;
7625
  }
7626
  getLabelAndValue(index) {
7627
+ const meta = this._cachedMeta;
 
7628
  const {iScale, vScale} = meta;
7629
+ const parsed = this.getParsed(index);
7630
  const custom = parsed._custom;
7631
  const value = isFloatBar(custom)
7632
  ? '[' + custom.start + ', ' + custom.end + ']'
7637
  };
7638
  }
7639
  initialize() {
7640
+ this.enableOptionSharing = true;
 
7641
  super.initialize();
7642
+ const meta = this._cachedMeta;
7643
+ meta.stack = this.getDataset().stack;
7644
  }
7645
  update(mode) {
7646
+ const meta = this._cachedMeta;
7647
+ this.updateElements(meta.data, 0, meta.data.length, mode);
 
7648
  }
7649
  updateElements(bars, start, count, mode) {
 
7650
  const reset = mode === 'reset';
7651
+ const {index, _cachedMeta: {vScale}} = this;
7652
  const base = vScale.getBasePixel();
7653
  const horizontal = vScale.isHorizontal();
7654
+ const ruler = this._getRuler();
7655
+ const firstOpts = this.resolveDataElementOptions(start, mode);
7656
+ const sharedOptions = this.getSharedOptions(firstOpts);
7657
+ const includeOptions = this.includeOptions(mode, sharedOptions);
7658
+ this.updateSharedOptions(sharedOptions, mode, firstOpts);
7659
  for (let i = start; i < start + count; i++) {
7660
+ const parsed = this.getParsed(i);
7661
+ const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : this._calculateBarValuePixels(i);
7662
+ const ipixels = this._calculateBarIndexPixels(i, ruler);
7663
  const stack = (parsed._stacks || {})[vScale.axis];
7664
  const properties = {
7665
  horizontal,
7671
  width: horizontal ? Math.abs(vpixels.size) : ipixels.size
7672
  };
7673
  if (includeOptions) {
7674
+ properties.options = sharedOptions || this.resolveDataElementOptions(i, bars[i].active ? 'active' : mode);
7675
  }
7676
+ const options = properties.options || bars[i].options;
7677
+ setBorderSkipped(properties, options, stack, index);
7678
+ setInflateAmount(properties, options, ruler.ratio);
7679
+ this.updateElement(bars[i], i, properties, mode);
7680
  }
7681
  }
7682
  _getStacks(last, dataIndex) {
7683
+ const meta = this._cachedMeta;
 
7684
  const iScale = meta.iScale;
7685
+ const metasets = iScale.getMatchingVisibleMetas(this._type);
7686
  const stacked = iScale.options.stacked;
7687
  const ilen = metasets.length;
7688
  const stacks = [];
7726
  : index;
7727
  }
7728
  _getRuler() {
7729
+ const opts = this.options;
7730
+ const meta = this._cachedMeta;
 
7731
  const iScale = meta.iScale;
7732
  const pixels = [];
7733
  let i, ilen;
7734
  for (i = 0, ilen = meta.data.length; i < ilen; ++i) {
7735
+ pixels.push(iScale.getPixelForValue(this.getParsed(i)[iScale.axis], i));
7736
  }
7737
  const barThickness = opts.barThickness;
7738
+ const min = barThickness || computeMinSampleSize(meta);
7739
  return {
7740
  min,
7741
  pixels,
7742
  start: iScale._startPixel,
7743
  end: iScale._endPixel,
7744
+ stackCount: this._getStackCount(),
7745
  scale: iScale,
7746
  grouped: opts.grouped,
7747
  ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage
7748
  };
7749
  }
7750
  _calculateBarValuePixels(index) {
7751
+ const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this;
 
7752
  const actualBase = baseValue || 0;
7753
+ const parsed = this.getParsed(index);
7754
  const custom = parsed._custom;
7755
  const floating = isFloatBar(custom);
7756
  let value = parsed[vScale.axis];
7757
  let start = 0;
7758
+ let length = _stacked ? this.applyStack(vScale, parsed, _stacked) : value;
7759
  let head, size;
7760
  if (length !== value) {
7761
  start = length - value;
7771
  }
7772
  const startValue = !isNullOrUndef(baseValue) && !floating ? baseValue : start;
7773
  let base = vScale.getPixelForValue(startValue);
7774
+ if (this.chart.getDataVisibility(index)) {
7775
  head = vScale.getPixelForValue(start + length);
7776
  } else {
7777
  head = base;
7797
  };
7798
  }
7799
  _calculateBarIndexPixels(index, ruler) {
 
7800
  const scale = ruler.scale;
7801
+ const options = this.options;
7802
  const skipNull = options.skipNull;
7803
  const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);
7804
  let center, size;
7805
  if (ruler.grouped) {
7806
+ const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;
7807
  const range = options.barThickness === 'flex'
7808
  ? computeFlexCategoryTraits(index, ruler, options, stackCount)
7809
  : computeFitCategoryTraits(index, ruler, options, stackCount);
7810
+ const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);
7811
  center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
7812
  size = Math.min(maxBarThickness, range.chunk * range.ratio);
7813
  } else {
7814
+ center = scale.getPixelForValue(this.getParsed(index)[scale.axis], index);
7815
  size = Math.min(maxBarThickness, ruler.min * ruler.ratio);
7816
  }
7817
  return {
7822
  };
7823
  }
7824
  draw() {
7825
+ const meta = this._cachedMeta;
 
7826
  const vScale = meta.vScale;
7827
  const rects = meta.data;
7828
  const ilen = rects.length;
7829
  let i = 0;
7830
  for (; i < ilen; ++i) {
7831
+ if (this.getParsed(i)[vScale.axis] !== null) {
7832
+ rects[i].draw(this._ctx);
7833
  }
7834
  }
7835
  }
7869
  this.enableOptionSharing = true;
7870
  super.initialize();
7871
  }
7872
+ parsePrimitiveData(meta, data, start, count) {
7873
+ const parsed = super.parsePrimitiveData(meta, data, start, count);
7874
+ for (let i = 0; i < parsed.length; i++) {
7875
+ parsed[i]._custom = this.resolveDataElementOptions(i + start).radius;
7876
+ }
7877
+ return parsed;
7878
+ }
7879
+ parseArrayData(meta, data, start, count) {
7880
+ const parsed = super.parseArrayData(meta, data, start, count);
7881
+ for (let i = 0; i < parsed.length; i++) {
7882
+ const item = data[start + i];
7883
+ parsed[i]._custom = valueOrDefault(item[2], this.resolveDataElementOptions(i + start).radius);
7884
+ }
7885
+ return parsed;
7886
+ }
7887
  parseObjectData(meta, data, start, count) {
7888
+ const parsed = super.parseObjectData(meta, data, start, count);
7889
+ for (let i = 0; i < parsed.length; i++) {
7890
+ const item = data[start + i];
7891
+ parsed[i]._custom = valueOrDefault(item && item.r && +item.r, this.resolveDataElementOptions(i + start).radius);
 
 
 
 
 
 
 
7892
  }
7893
  return parsed;
7894
  }
7895
  getMaxOverflow() {
7896
+ const data = this._cachedMeta.data;
7897
  let max = 0;
7898
  for (let i = data.length - 1; i >= 0; --i) {
7899
+ max = Math.max(max, data[i].size(this.resolveDataElementOptions(i)) / 2);
7900
  }
7901
  return max > 0 && max;
7902
  }
7903
  getLabelAndValue(index) {
7904
+ const meta = this._cachedMeta;
 
7905
  const {xScale, yScale} = meta;
7906
+ const parsed = this.getParsed(index);
7907
  const x = xScale.getLabelForValue(parsed.x);
7908
  const y = yScale.getLabelForValue(parsed.y);
7909
  const r = parsed._custom;
7913
  };
7914
  }
7915
  update(mode) {
7916
+ const points = this._cachedMeta.data;
7917
+ this.updateElements(points, 0, points.length, mode);
 
7918
  }
7919
  updateElements(points, start, count, mode) {
 
7920
  const reset = mode === 'reset';
7921
+ const {iScale, vScale} = this._cachedMeta;
7922
+ const firstOpts = this.resolveDataElementOptions(start, mode);
7923
+ const sharedOptions = this.getSharedOptions(firstOpts);
7924
+ const includeOptions = this.includeOptions(mode, sharedOptions);
7925
  const iAxis = iScale.axis;
7926
  const vAxis = vScale.axis;
7927
  for (let i = start; i < start + count; i++) {
7928
  const point = points[i];
7929
+ const parsed = !reset && this.getParsed(i);
7930
  const properties = {};
7931
  const iPixel = properties[iAxis] = reset ? iScale.getPixelForDecimal(0.5) : iScale.getPixelForValue(parsed[iAxis]);
7932
  const vPixel = properties[vAxis] = reset ? vScale.getBasePixel() : vScale.getPixelForValue(parsed[vAxis]);
7933
  properties.skip = isNaN(iPixel) || isNaN(vPixel);
7934
  if (includeOptions) {
7935
+ properties.options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);
7936
  if (reset) {
7937
  properties.options.radius = 0;
7938
  }
7939
  }
7940
+ this.updateElement(point, i, properties, mode);
7941
  }
7942
+ this.updateSharedOptions(sharedOptions, mode, firstOpts);
7943
  }
7944
  resolveDataElementOptions(index, mode) {
7945
  const parsed = this.getParsed(index);
8024
  parse(start, count) {
8025
  const data = this.getDataset().data;
8026
  const meta = this._cachedMeta;
8027
+ if (this._parsing === false) {
8028
+ meta._parsed = data;
8029
+ } else {
8030
+ let getter = (i) => +data[i];
8031
+ if (isObject(data[start])) {
8032
+ const {key = 'value'} = this._parsing;
8033
+ getter = (i) => +resolveObjectKey(data[i], key);
8034
+ }
8035
+ let i, ilen;
8036
+ for (i = start, ilen = start + count; i < ilen; ++i) {
8037
+ meta._parsed[i] = getter(i);
8038
+ }
8039
  }
8040
  }
8041
  _getRotation() {
8047
  _getRotationExtents() {
8048
  let min = TAU;
8049
  let max = -TAU;
8050
+ for (let i = 0; i < this.chart.data.datasets.length; ++i) {
8051
+ if (this.chart.isDatasetVisible(i)) {
8052
+ const controller = this.chart.getDatasetMeta(i).controller;
 
8053
  const rotation = controller._getRotation();
8054
  const circumference = controller._getCircumference();
8055
  min = Math.min(min, rotation);
8062
  };
8063
  }
8064
  update(mode) {
8065
+ const chart = this.chart;
 
8066
  const {chartArea} = chart;
8067
+ const meta = this._cachedMeta;
8068
  const arcs = meta.data;
8069
+ const spacing = this.getMaxBorderWidth() + this.getMaxOffset(arcs) + this.options.spacing;
8070
  const maxSize = Math.max((Math.min(chartArea.width, chartArea.height) - spacing) / 2, 0);
8071
+ const cutout = Math.min(toPercentage(this.options.cutout, maxSize), 1);
8072
+ const chartWeight = this._getRingWeight(this.index);
8073
+ const {circumference, rotation} = this._getRotationExtents();
8074
  const {ratioX, ratioY, offsetX, offsetY} = getRatioAndOffset(rotation, circumference, cutout);
8075
  const maxWidth = (chartArea.width - spacing) / ratioX;
8076
  const maxHeight = (chartArea.height - spacing) / ratioY;
8077
  const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
8078
+ const outerRadius = toDimension(this.options.radius, maxRadius);
8079
  const innerRadius = Math.max(outerRadius * cutout, 0);
8080
+ const radiusLength = (outerRadius - innerRadius) / this._getVisibleDatasetWeightTotal();
8081
+ this.offsetX = offsetX * outerRadius;
8082
+ this.offsetY = offsetY * outerRadius;
8083
+ meta.total = this.calculateTotal();
8084
+ this.outerRadius = outerRadius - radiusLength * this._getRingWeightOffset(this.index);
8085
+ this.innerRadius = Math.max(this.outerRadius - radiusLength * chartWeight, 0);
8086
+ this.updateElements(arcs, 0, arcs.length, mode);
8087
  }
8088
  _circumference(i, reset) {
8089
+ const opts = this.options;
8090
+ const meta = this._cachedMeta;
8091
+ const circumference = this._getCircumference();
 
8092
  if ((reset && opts.animation.animateRotate) || !this.chart.getDataVisibility(i) || meta._parsed[i] === null || meta.data[i].hidden) {
8093
  return 0;
8094
  }
8095
+ return this.calculateCircumference(meta._parsed[i] * circumference / TAU);
8096
  }
8097
  updateElements(arcs, start, count, mode) {
 
8098
  const reset = mode === 'reset';
8099
+ const chart = this.chart;
8100
  const chartArea = chart.chartArea;
8101
  const opts = chart.options;
8102
  const animationOpts = opts.animation;
8103
  const centerX = (chartArea.left + chartArea.right) / 2;
8104
  const centerY = (chartArea.top + chartArea.bottom) / 2;
8105
  const animateScale = reset && animationOpts.animateScale;
8106
+ const innerRadius = animateScale ? 0 : this.innerRadius;
8107
+ const outerRadius = animateScale ? 0 : this.outerRadius;
8108
+ const firstOpts = this.resolveDataElementOptions(start, mode);
8109
+ const sharedOptions = this.getSharedOptions(firstOpts);
8110
+ const includeOptions = this.includeOptions(mode, sharedOptions);
8111
+ let startAngle = this._getRotation();
8112
  let i;
8113
  for (i = 0; i < start; ++i) {
8114
+ startAngle += this._circumference(i, reset);
8115
  }
8116
  for (i = start; i < start + count; ++i) {
8117
+ const circumference = this._circumference(i, reset);
8118
  const arc = arcs[i];
8119
  const properties = {
8120
+ x: centerX + this.offsetX,
8121
+ y: centerY + this.offsetY,
8122
  startAngle,
8123
  endAngle: startAngle + circumference,
8124
  circumference,
8126
  innerRadius
8127
  };
8128
  if (includeOptions) {
8129
+ properties.options = sharedOptions || this.resolveDataElementOptions(i, arc.active ? 'active' : mode);
8130
  }
8131
  startAngle += circumference;
8132
+ this.updateElement(arc, i, properties, mode);
8133
  }
8134
+ this.updateSharedOptions(sharedOptions, mode, firstOpts);
8135
  }
8136
  calculateTotal() {
8137
  const meta = this._cachedMeta;
8154
  return 0;
8155
  }
8156
  getLabelAndValue(index) {
8157
+ const meta = this._cachedMeta;
8158
+ const chart = this.chart;
 
8159
  const labels = chart.data.labels || [];
8160
  const value = formatNumber(meta._parsed[index], chart.options.locale);
8161
  return {
8164
  };
8165
  }
8166
  getMaxBorderWidth(arcs) {
 
8167
  let max = 0;
8168
+ const chart = this.chart;
8169
  let i, ilen, meta, controller, options;
8170
  if (!arcs) {
8171
  for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
8173
  meta = chart.getDatasetMeta(i);
8174
  arcs = meta.data;
8175
  controller = meta.controller;
 
 
 
8176
  break;
8177
  }
8178
  }
8295
  super.initialize();
8296
  }
8297
  update(mode) {
8298
+ const meta = this._cachedMeta;
 
8299
  const {dataset: line, data: points = [], _dataset} = meta;
8300
+ const animationsDisabled = this.chart._animationsDisabled;
8301
  let {start, count} = getStartAndCountOfVisiblePoints(meta, points, animationsDisabled);
8302
+ this._drawStart = start;
8303
+ this._drawCount = count;
8304
  if (scaleRangesChanged(meta)) {
8305
  start = 0;
8306
  count = points.length;
8307
  }
8308
+ line._chart = this.chart;
8309
+ line._datasetIndex = this.index;
8310
  line._decimated = !!_dataset._decimated;
8311
  line.points = points;
8312
+ const options = this.resolveDatasetElementOptions(mode);
8313
+ if (!this.options.showLine) {
8314
  options.borderWidth = 0;
8315
  }
8316
+ options.segment = this.options.segment;
8317
+ this.updateElement(line, undefined, {
8318
  animated: !animationsDisabled,
8319
  options
8320
  }, mode);
8321
+ this.updateElements(points, start, count, mode);
8322
  }
8323
  updateElements(points, start, count, mode) {
 
8324
  const reset = mode === 'reset';
8325
+ const {iScale, vScale, _stacked, _dataset} = this._cachedMeta;
8326
+ const firstOpts = this.resolveDataElementOptions(start, mode);
8327
+ const sharedOptions = this.getSharedOptions(firstOpts);
8328
+ const includeOptions = this.includeOptions(mode, sharedOptions);
8329
  const iAxis = iScale.axis;
8330
  const vAxis = vScale.axis;
8331
+ const {spanGaps, segment} = this.options;
8332
  const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY;
8333
+ const directUpdate = this.chart._animationsDisabled || reset || mode === 'none';
8334
+ let prevParsed = start > 0 && this.getParsed(start - 1);
8335
  for (let i = start; i < start + count; ++i) {
8336
  const point = points[i];
8337
+ const parsed = this.getParsed(i);
8338
  const properties = directUpdate ? point : {};
8339
  const nullData = isNullOrUndef(parsed[vAxis]);
8340
  const iPixel = properties[iAxis] = iScale.getPixelForValue(parsed[iAxis], i);
8341
+ const vPixel = properties[vAxis] = reset || nullData ? vScale.getBasePixel() : vScale.getPixelForValue(_stacked ? this.applyStack(vScale, parsed, _stacked) : parsed[vAxis], i);
8342
  properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData;
8343
  properties.stop = i > 0 && (parsed[iAxis] - prevParsed[iAxis]) > maxGapLength;
8344
+ if (segment) {
8345
+ properties.parsed = parsed;
8346
+ properties.raw = _dataset.data[i];
8347
+ }
8348
  if (includeOptions) {
8349
+ properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode);
8350
  }
8351
  if (!directUpdate) {
8352
+ this.updateElement(point, i, properties, mode);
8353
  }
8354
  prevParsed = parsed;
8355
  }
8356
+ this.updateSharedOptions(sharedOptions, mode, firstOpts);
8357
  }
8358
  getMaxOverflow() {
8359
+ const meta = this._cachedMeta;
 
8360
  const dataset = meta.dataset;
8361
  const border = dataset.options && dataset.options.borderWidth || 0;
8362
  const data = meta.data || [];
8363
  if (!data.length) {
8364
  return border;
8365
  }
8366
+ const firstPoint = data[0].size(this.resolveDataElementOptions(0));
8367
+ const lastPoint = data[data.length - 1].size(this.resolveDataElementOptions(data.length - 1));
8368
  return Math.max(border, firstPoint, lastPoint) / 2;
8369
  }
8370
  draw() {
8442
  this.outerRadius = undefined;
8443
  }
8444
  getLabelAndValue(index) {
8445
+ const meta = this._cachedMeta;
8446
+ const chart = this.chart;
 
8447
  const labels = chart.data.labels || [];
8448
  const value = formatNumber(meta._parsed[index].r, chart.options.locale);
8449
  return {
8457
  this.updateElements(arcs, 0, arcs.length, mode);
8458
  }
8459
  _updateRadius() {
8460
+ const chart = this.chart;
 
8461
  const chartArea = chart.chartArea;
8462
  const opts = chart.options;
8463
  const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
8464
  const outerRadius = Math.max(minSize / 2, 0);
8465
  const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
8466
  const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
8467
+ this.outerRadius = outerRadius - (radiusLength * this.index);
8468
+ this.innerRadius = this.outerRadius - radiusLength;
8469
  }
8470
  updateElements(arcs, start, count, mode) {
 
8471
  const reset = mode === 'reset';
8472
+ const chart = this.chart;
8473
+ const dataset = this.getDataset();
8474
  const opts = chart.options;
8475
  const animationOpts = opts.animation;
8476
+ const scale = this._cachedMeta.rScale;
8477
  const centerX = scale.xCenter;
8478
  const centerY = scale.yCenter;
8479
  const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;
8480
  let angle = datasetStartAngle;
8481
  let i;
8482
+ const defaultAngle = 360 / this.countVisibleElements();
8483
  for (i = 0; i < start; ++i) {
8484
+ angle += this._computeAngle(i, mode, defaultAngle);
8485
  }
8486
  for (i = start; i < start + count; i++) {
8487
  const arc = arcs[i];
8488
  let startAngle = angle;
8489
+ let endAngle = angle + this._computeAngle(i, mode, defaultAngle);
8490
  let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0;
8491
  angle = endAngle;
8492
  if (reset) {
8504
  outerRadius,
8505
  startAngle,
8506
  endAngle,
8507
+ options: this.resolveDataElementOptions(i, arc.active ? 'active' : mode)
8508
  };
8509
+ this.updateElement(arc, i, properties, mode);
8510
  }
8511
  }
8512
  countVisibleElements() {
8614
 
8615
  class RadarController extends DatasetController {
8616
  getLabelAndValue(index) {
8617
+ const vScale = this._cachedMeta.vScale;
8618
+ const parsed = this.getParsed(index);
 
8619
  return {
8620
  label: vScale.getLabels()[index],
8621
  value: '' + vScale.getLabelForValue(parsed[vScale.axis])
8622
  };
8623
  }
8624
  update(mode) {
8625
+ const meta = this._cachedMeta;
 
8626
  const line = meta.dataset;
8627
  const points = meta.data || [];
8628
  const labels = meta.iScale.getLabels();
8629
  line.points = points;
8630
  if (mode !== 'resize') {
8631
+ const options = this.resolveDatasetElementOptions(mode);
8632
+ if (!this.options.showLine) {
8633
  options.borderWidth = 0;
8634
  }
8635
  const properties = {
8637
  _fullLoop: labels.length === points.length,
8638
  options
8639
  };
8640
+ this.updateElement(line, undefined, properties, mode);
8641
  }
8642
+ this.updateElements(points, 0, points.length, mode);
8643
  }
8644
  updateElements(points, start, count, mode) {
8645
+ const dataset = this.getDataset();
8646
+ const scale = this._cachedMeta.rScale;
 
8647
  const reset = mode === 'reset';
8648
  for (let i = start; i < start + count; i++) {
8649
  const point = points[i];
8650
+ const options = this.resolveDataElementOptions(i, point.active ? 'active' : mode);
8651
  const pointPosition = scale.getPointPositionForValue(i, dataset.data[i]);
8652
  const x = reset ? scale.xCenter : pointPosition.x;
8653
  const y = reset ? scale.yCenter : pointPosition.y;
8658
  skip: isNaN(x) || isNaN(y),
8659
  options
8660
  };
8661
+ this.updateElement(point, i, properties, mode);
8662
  }
8663
  }
8664
  }
8903
  'circumference'
8904
  ], useFinalPosition);
8905
  const rAdjust = this.options.spacing / 2;
8906
+ const _circumference = valueOrDefault(circumference, endAngle - startAngle);
8907
+ const betweenAngles = _circumference >= TAU || _angleBetween(angle, startAngle, endAngle);
8908
+ const withinRadius = _isBetween(distance, innerRadius + rAdjust, outerRadius + rAdjust);
8909
  return (betweenAngles && withinRadius);
8910
  }
8911
  getCenterPoint(useFinalPosition) {
8930
  return this.getCenterPoint(useFinalPosition);
8931
  }
8932
  draw(ctx) {
8933
+ const {options, circumference} = this;
 
8934
  const offset = (options.offset || 0) / 2;
8935
  const spacing = (options.spacing || 0) / 2;
8936
+ this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
8937
+ this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
8938
+ if (circumference === 0 || this.innerRadius < 0 || this.outerRadius < 0) {
8939
  return;
8940
  }
8941
  ctx.save();
8942
  let radiusOffset = 0;
8943
  if (offset) {
8944
  radiusOffset = offset / 2;
8945
+ const halfAngle = (this.startAngle + this.endAngle) / 2;
8946
  ctx.translate(Math.cos(halfAngle) * radiusOffset, Math.sin(halfAngle) * radiusOffset);
8947
+ if (this.circumference >= PI) {
8948
  radiusOffset = offset;
8949
  }
8950
  }
8951
  ctx.fillStyle = options.backgroundColor;
8952
  ctx.strokeStyle = options.borderColor;
8953
+ const endAngle = drawArc(ctx, this, radiusOffset, spacing);
8954
+ drawBorder(ctx, this, radiusOffset, spacing, endAngle);
8955
  ctx.restore();
8956
  }
8957
  }
9112
  }
9113
  const usePath2D = typeof Path2D === 'function';
9114
  function draw(ctx, line, start, count) {
9115
+ if (usePath2D && !line.options.segment) {
9116
  strokePathWithCache(ctx, line, start, count);
9117
  } else {
9118
  strokePathDirect(ctx, line, start, count);
9123
  super();
9124
  this.animated = true;
9125
  this.options = undefined;
9126
+ this._chart = undefined;
9127
  this._loop = undefined;
9128
  this._fullLoop = undefined;
9129
  this._path = undefined;
9137
  }
9138
  }
9139
  updateControlPoints(chartArea, indexAxis) {
9140
+ const options = this.options;
9141
+ if ((options.tension || options.cubicInterpolationMode === 'monotone') && !options.stepped && !this._pointsUpdated) {
9142
+ const loop = options.spanGaps ? this._loop : this._fullLoop;
9143
+ _updateBezierControlPoints(this._points, options, chartArea, loop, indexAxis);
9144
+ this._pointsUpdated = true;
 
9145
  }
9146
  }
9147
  set points(points) {
9148
+ this._points = points;
9149
+ delete this._segments;
9150
+ delete this._path;
9151
+ this._pointsUpdated = false;
 
9152
  }
9153
  get points() {
9154
  return this._points;
9168
  return count && points[segments[count - 1].end];
9169
  }
9170
  interpolate(point, property) {
9171
+ const options = this.options;
 
9172
  const value = point[property];
9173
+ const points = this.points;
9174
+ const segments = _boundSegments(this, {property, start: value, end: value});
9175
  if (!segments.length) {
9176
  return;
9177
  }
9198
  return segmentMethod(ctx, this, segment, params);
9199
  }
9200
  path(ctx, start, count) {
9201
+ const segments = this.segments;
9202
+ const segmentMethod = _getSegmentMethod(this);
9203
+ let loop = this._loop;
 
9204
  start = start || 0;
9205
+ count = count || (this.points.length - start);
9206
  for (const segment of segments) {
9207
+ loop &= segmentMethod(ctx, this, segment, {start, end: start + count - 1});
9208
  }
9209
  return !!loop;
9210
  }
9211
  draw(ctx, chartArea, start, count) {
9212
+ const options = this.options || {};
9213
+ const points = this.points || [];
9214
+ if (points.length && options.borderWidth) {
9215
+ ctx.save();
9216
+ draw(ctx, this, start, count);
9217
+ ctx.restore();
9218
  }
9219
+ if (this.animated) {
9220
+ this._pointsUpdated = false;
9221
+ this._path = undefined;
 
 
 
9222
  }
9223
  }
9224
  }
9284
  return (radius + borderWidth) * 2;
9285
  }
9286
  draw(ctx, area) {
9287
+ const options = this.options;
9288
+ if (this.skip || options.radius < 0.1 || !_isPointInArea(this, area, this.size(options) / 2)) {
 
9289
  return;
9290
  }
9291
  ctx.strokeStyle = options.borderColor;
9292
  ctx.lineWidth = options.borderWidth;
9293
  ctx.fillStyle = options.backgroundColor;
9294
+ drawPoint(ctx, options, this.x, this.y);
9295
  }
9296
  getRange() {
9297
  const options = this.options || {};
9393
  const skipBoth = skipX && skipY;
9394
  const bounds = bar && !skipBoth && getBarBounds(bar, useFinalPosition);
9395
  return bounds
9396
+ && (skipX || _isBetween(x, bounds.left, bounds.right))
9397
+ && (skipY || _isBetween(y, bounds.top, bounds.bottom));
9398
  }
9399
  function hasRadius(radius) {
9400
  return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight;
9423
  this.base = undefined;
9424
  this.width = undefined;
9425
  this.height = undefined;
9426
+ this.inflateAmount = undefined;
9427
  if (cfg) {
9428
  Object.assign(this, cfg);
9429
  }
9430
  }
9431
  draw(ctx) {
9432
+ const {inflateAmount, options: {borderColor, backgroundColor}} = this;
9433
  const {inner, outer} = boundingRects(this);
9434
  const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath;
 
9435
  ctx.save();
9436
  if (outer.w !== inner.w || outer.h !== inner.h) {
9437
  ctx.beginPath();
9438
  addRectPath(ctx, inflateRect(outer, inflateAmount, inner));
9439
  ctx.clip();
9440
  addRectPath(ctx, inflateRect(inner, -inflateAmount, outer));
9441
+ ctx.fillStyle = borderColor;
9442
  ctx.fill('evenodd');
9443
  }
9444
  ctx.beginPath();
9445
+ addRectPath(ctx, inflateRect(inner, inflateAmount));
9446
+ ctx.fillStyle = backgroundColor;
9447
  ctx.fill();
9448
  ctx.restore();
9449
  }
9472
  borderSkipped: 'start',
9473
  borderWidth: 0,
9474
  borderRadius: 0,
9475
+ inflateAmount: 'auto',
9476
  pointStyle: undefined
9477
  };
9478
  BarElement.defaultRoutes = {
9830
  return points;
9831
  }
9832
  function buildStackLine(source) {
9833
+ const {scale, index, line} = source;
9834
  const points = [];
9835
  const segments = line.segments;
9836
  const sourcePoints = line.points;
9837
+ const linesBelow = getLinesBelow(scale, index);
9838
  linesBelow.push(createBoundaryLine({x: null, y: scale.bottom}, line));
9839
  for (let i = 0; i < segments.length; i++) {
9840
  const segment = segments[i];
9844
  }
9845
  return new LineElement({points, options: {}});
9846
  }
9847
+ function getLinesBelow(scale, index) {
 
9848
  const below = [];
9849
+ const metas = scale.getMatchingVisibleMetas('line');
9850
  for (let i = 0; i < metas.length; i++) {
9851
  const meta = metas[i];
9852
  if (meta.index === index) {
9853
  break;
9854
  }
9855
+ if (!meta.hidden) {
9856
  below.unshift(meta.dataset);
9857
  }
9858
  }
9891
  const segment = segments[i];
9892
  const firstValue = linePoints[segment.start][property];
9893
  const lastValue = linePoints[segment.end][property];
9894
+ if (_isBetween(pointValue, firstValue, lastValue)) {
9895
  first = pointValue === firstValue;
9896
  last = pointValue === lastValue;
9897
  break;
10203
  this.fullSize = undefined;
10204
  }
10205
  update(maxWidth, maxHeight, margins) {
10206
+ this.maxWidth = maxWidth;
10207
+ this.maxHeight = maxHeight;
10208
+ this._margins = margins;
10209
+ this.setDimensions();
10210
+ this.buildLabels();
10211
+ this.fit();
 
10212
  }
10213
  setDimensions() {
10214
+ if (this.isHorizontal()) {
10215
+ this.width = this.maxWidth;
10216
+ this.left = this._margins.left;
10217
+ this.right = this.width;
 
10218
  } else {
10219
+ this.height = this.maxHeight;
10220
+ this.top = this._margins.top;
10221
+ this.bottom = this.height;
10222
  }
10223
  }
10224
  buildLabels() {
10225
+ const labelOpts = this.options.labels || {};
10226
+ let legendItems = callback(labelOpts.generateLabels, [this.chart], this) || [];
 
10227
  if (labelOpts.filter) {
10228
+ legendItems = legendItems.filter((item) => labelOpts.filter(item, this.chart.data));
10229
  }
10230
  if (labelOpts.sort) {
10231
+ legendItems = legendItems.sort((a, b) => labelOpts.sort(a, b, this.chart.data));
10232
  }
10233
+ if (this.options.reverse) {
10234
  legendItems.reverse();
10235
  }
10236
+ this.legendItems = legendItems;
10237
  }
10238
  fit() {
10239
+ const {options, ctx} = this;
 
10240
  if (!options.display) {
10241
+ this.width = this.height = 0;
10242
  return;
10243
  }
10244
  const labelOpts = options.labels;
10245
  const labelFont = toFont(labelOpts.font);
10246
  const fontSize = labelFont.size;
10247
+ const titleHeight = this._computeTitleHeight();
10248
  const {boxWidth, itemHeight} = getBoxSize(labelOpts, fontSize);
10249
  let width, height;
10250
  ctx.font = labelFont.string;
10251
+ if (this.isHorizontal()) {
10252
+ width = this.maxWidth;
10253
+ height = this._fitRows(titleHeight, fontSize, boxWidth, itemHeight) + 10;
10254
  } else {
10255
+ height = this.maxHeight;
10256
+ width = this._fitCols(titleHeight, fontSize, boxWidth, itemHeight) + 10;
10257
  }
10258
+ this.width = Math.min(width, options.maxWidth || this.maxWidth);
10259
+ this.height = Math.min(height, options.maxHeight || this.maxHeight);
10260
  }
10261
  _fitRows(titleHeight, fontSize, boxWidth, itemHeight) {
10262
+ const {ctx, maxWidth, options: {labels: {padding}}} = this;
10263
+ const hitboxes = this.legendHitBoxes = [];
10264
+ const lineWidths = this.lineWidths = [0];
 
10265
  const lineHeight = itemHeight + padding;
10266
  let totalHeight = titleHeight;
10267
  ctx.textAlign = 'left';
10268
  ctx.textBaseline = 'middle';
10269
  let row = -1;
10270
  let top = -lineHeight;
10271
+ this.legendItems.forEach((legendItem, i) => {
10272
  const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
10273
  if (i === 0 || lineWidths[lineWidths.length - 1] + itemWidth + 2 * padding > maxWidth) {
10274
  totalHeight += lineHeight;
10282
  return totalHeight;
10283
  }
10284
  _fitCols(titleHeight, fontSize, boxWidth, itemHeight) {
10285
+ const {ctx, maxHeight, options: {labels: {padding}}} = this;
10286
+ const hitboxes = this.legendHitBoxes = [];
10287
+ const columnSizes = this.columnSizes = [];
 
10288
  const heightLimit = maxHeight - titleHeight;
10289
  let totalWidth = padding;
10290
  let currentColWidth = 0;
10291
  let currentColHeight = 0;
10292
  let left = 0;
10293
  let col = 0;
10294
+ this.legendItems.forEach((legendItem, i) => {
10295
  const itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
10296
  if (i > 0 && currentColHeight + itemHeight + 2 * padding > heightLimit) {
10297
  totalWidth += currentColWidth + padding;
10309
  return totalWidth;
10310
  }
10311
  adjustHitBoxes() {
10312
+ if (!this.options.display) {
 
10313
  return;
10314
  }
10315
+ const titleHeight = this._computeTitleHeight();
10316
+ const {legendHitBoxes: hitboxes, options: {align, labels: {padding}, rtl}} = this;
10317
+ const rtlHelper = getRtlAdapter(rtl, this.left, this.width);
10318
  if (this.isHorizontal()) {
10319
  let row = 0;
10320
+ let left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
10321
  for (const hitbox of hitboxes) {
10322
  if (row !== hitbox.row) {
10323
  row = hitbox.row;
10324
+ left = _alignStartEnd(align, this.left + padding, this.right - this.lineWidths[row]);
10325
  }
10326
+ hitbox.top += this.top + titleHeight + padding;
10327
  hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(left), hitbox.width);
10328
  left += hitbox.width + padding;
10329
  }
10330
  } else {
10331
  let col = 0;
10332
+ let top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
10333
  for (const hitbox of hitboxes) {
10334
  if (hitbox.col !== col) {
10335
  col = hitbox.col;
10336
+ top = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - this.columnSizes[col].height);
10337
  }
10338
  hitbox.top = top;
10339
+ hitbox.left += this.left + padding;
10340
  hitbox.left = rtlHelper.leftForLtr(rtlHelper.x(hitbox.left), hitbox.width);
10341
  top += hitbox.height + padding;
10342
  }
10346
  return this.options.position === 'top' || this.options.position === 'bottom';
10347
  }
10348
  draw() {
10349
+ if (this.options.display) {
10350
+ const ctx = this.ctx;
10351
+ clipArea(ctx, this);
10352
+ this._draw();
 
10353
  unclipArea(ctx);
10354
  }
10355
  }
10356
  _draw() {
10357
+ const {options: opts, columnSizes, lineWidths, ctx} = this;
 
10358
  const {align, labels: labelOpts} = opts;
10359
  const defaultColor = defaults.color;
10360
+ const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
10361
  const labelFont = toFont(labelOpts.font);
10362
  const {color: fontColor, padding} = labelOpts;
10363
  const fontSize = labelFont.size;
10364
  const halfFontSize = fontSize / 2;
10365
  let cursor;
10366
+ this.drawTitle();
10367
  ctx.textAlign = rtlHelper.textAlign('left');
10368
  ctx.textBaseline = 'middle';
10369
  ctx.lineWidth = 0.5;
10421
  textAlign: rtlHelper.textAlign(legendItem.textAlign)
10422
  });
10423
  };
10424
+ const isHorizontal = this.isHorizontal();
10425
  const titleHeight = this._computeTitleHeight();
10426
  if (isHorizontal) {
10427
  cursor = {
10428
+ x: _alignStartEnd(align, this.left + padding, this.right - lineWidths[0]),
10429
+ y: this.top + padding + titleHeight,
10430
  line: 0
10431
  };
10432
  } else {
10433
  cursor = {
10434
+ x: this.left + padding,
10435
+ y: _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[0].height),
10436
  line: 0
10437
  };
10438
  }
10439
+ overrideTextDirection(this.ctx, opts.textDirection);
10440
  const lineHeight = itemHeight + padding;
10441
+ this.legendItems.forEach((legendItem, i) => {
10442
  ctx.strokeStyle = legendItem.fontColor || fontColor;
10443
  ctx.fillStyle = legendItem.fontColor || fontColor;
10444
  const textWidth = ctx.measureText(legendItem.text).width;
10446
  const width = boxWidth + halfFontSize + textWidth;
10447
  let x = cursor.x;
10448
  let y = cursor.y;
10449
+ rtlHelper.setWidth(this.width);
10450
  if (isHorizontal) {
10451
+ if (i > 0 && x + width + padding > this.right) {
10452
  y = cursor.y += lineHeight;
10453
  cursor.line++;
10454
+ x = cursor.x = _alignStartEnd(align, this.left + padding, this.right - lineWidths[cursor.line]);
10455
  }
10456
+ } else if (i > 0 && y + lineHeight > this.bottom) {
10457
  x = cursor.x = x + columnSizes[cursor.line].width + padding;
10458
  cursor.line++;
10459
+ y = cursor.y = _alignStartEnd(align, this.top + titleHeight + padding, this.bottom - columnSizes[cursor.line].height);
10460
  }
10461
  const realX = rtlHelper.x(x);
10462
  drawLegendBox(realX, y, legendItem);
10463
+ x = _textX(textAlign, x + boxWidth + halfFontSize, isHorizontal ? x + width : this.right, opts.rtl);
10464
  fillText(rtlHelper.x(x), y, legendItem);
10465
  if (isHorizontal) {
10466
  cursor.x += width + padding;
10468
  cursor.y += lineHeight;
10469
  }
10470
  });
10471
+ restoreTextDirection(this.ctx, opts.textDirection);
10472
  }
10473
  drawTitle() {
10474
+ const opts = this.options;
 
10475
  const titleOpts = opts.title;
10476
  const titleFont = toFont(titleOpts.font);
10477
  const titlePadding = toPadding(titleOpts.padding);
10478
  if (!titleOpts.display) {
10479
  return;
10480
  }
10481
+ const rtlHelper = getRtlAdapter(opts.rtl, this.left, this.width);
10482
+ const ctx = this.ctx;
10483
  const position = titleOpts.position;
10484
  const halfFontSize = titleFont.size / 2;
10485
  const topPaddingPlusHalfFontSize = titlePadding.top + halfFontSize;
10486
  let y;
10487
+ let left = this.left;
10488
+ let maxWidth = this.width;
10489
  if (this.isHorizontal()) {
10490
+ maxWidth = Math.max(...this.lineWidths);
10491
+ y = this.top + topPaddingPlusHalfFontSize;
10492
+ left = _alignStartEnd(opts.align, left, this.right - maxWidth);
10493
  } else {
10494
+ const maxHeight = this.columnSizes.reduce((acc, size) => Math.max(acc, size.height), 0);
10495
+ y = topPaddingPlusHalfFontSize + _alignStartEnd(opts.align, this.top, this.bottom - maxHeight - opts.labels.padding - this._computeTitleHeight());
10496
  }
10497
  const x = _alignStartEnd(position, left, left + maxWidth);
10498
  ctx.textAlign = rtlHelper.textAlign(_toLeftRightCenter(position));
10509
  return titleOpts.display ? titleFont.lineHeight + titlePadding.height : 0;
10510
  }
10511
  _getLegendItemAt(x, y) {
 
10512
  let i, hitBox, lh;
10513
+ if (_isBetween(x, this.left, this.right)
10514
+ && _isBetween(y, this.top, this.bottom)) {
10515
+ lh = this.legendHitBoxes;
10516
  for (i = 0; i < lh.length; ++i) {
10517
  hitBox = lh[i];
10518
+ if (_isBetween(x, hitBox.left, hitBox.left + hitBox.width)
10519
+ && _isBetween(y, hitBox.top, hitBox.top + hitBox.height)) {
10520
+ return this.legendItems[i];
10521
  }
10522
  }
10523
  }
10524
  return null;
10525
  }
10526
  handleEvent(e) {
10527
+ const opts = this.options;
 
10528
  if (!isListened(e.type, opts)) {
10529
  return;
10530
  }
10531
+ const hoveredItem = this._getLegendItemAt(e.x, e.y);
10532
  if (e.type === 'mousemove') {
10533
+ const previous = this._hoveredItem;
10534
  const sameItem = itemsEqual(previous, hoveredItem);
10535
  if (previous && !sameItem) {
10536
+ callback(opts.onLeave, [e, previous, this], this);
10537
  }
10538
+ this._hoveredItem = hoveredItem;
10539
  if (hoveredItem && !sameItem) {
10540
+ callback(opts.onHover, [e, hoveredItem, this], this);
10541
  }
10542
  } else if (hoveredItem) {
10543
+ callback(opts.onClick, [e, hoveredItem, this], this);
10544
  }
10545
  }
10546
  }
10663
  this.fullSize = undefined;
10664
  }
10665
  update(maxWidth, maxHeight) {
10666
+ const opts = this.options;
10667
+ this.left = 0;
10668
+ this.top = 0;
 
10669
  if (!opts.display) {
10670
+ this.width = this.height = this.right = this.bottom = 0;
10671
  return;
10672
  }
10673
+ this.width = this.right = maxWidth;
10674
+ this.height = this.bottom = maxHeight;
10675
  const lineCount = isArray(opts.text) ? opts.text.length : 1;
10676
+ this._padding = toPadding(opts.padding);
10677
+ const textSize = lineCount * toFont(opts.font).lineHeight + this._padding.height;
10678
+ if (this.isHorizontal()) {
10679
+ this.height = textSize;
10680
  } else {
10681
+ this.width = textSize;
10682
  }
10683
  }
10684
  isHorizontal() {
10709
  return {titleX, titleY, maxWidth, rotation};
10710
  }
10711
  draw() {
10712
+ const ctx = this.ctx;
10713
+ const opts = this.options;
 
10714
  if (!opts.display) {
10715
  return;
10716
  }
10717
  const fontOpts = toFont(opts.font);
10718
  const lineHeight = fontOpts.lineHeight;
10719
+ const offset = lineHeight / 2 + this._padding.top;
10720
+ const {titleX, titleY, maxWidth, rotation} = this._drawArgs(offset);
10721
  renderText(ctx, opts.text, 0, 0, fontOpts, {
10722
  color: opts.color,
10723
  maxWidth,
10943
  each(tooltip.title, maxLineWidth);
10944
  ctx.font = bodyFont.string;
10945
  each(tooltip.beforeBody.concat(tooltip.afterBody), maxLineWidth);
10946
+ widthPadding = options.displayColors ? (boxWidth + 2 + options.boxPadding) : 0;
10947
  each(body, (bodyItem) => {
10948
  each(bodyItem.before, maxLineWidth);
10949
  each(bodyItem.lines, maxLineWidth);
11022
  const {caretSize, caretPadding, cornerRadius} = options;
11023
  const {xAlign, yAlign} = alignment;
11024
  const paddingAndSize = caretSize + caretPadding;
11025
+ const {topLeft, topRight, bottomLeft, bottomRight} = toTRBLCorners(cornerRadius);
11026
  let x = alignX(size, xAlign);
11027
  const y = alignY(size, yAlign, paddingAndSize);
11028
  if (yAlign === 'center') {
11032
  x -= paddingAndSize;
11033
  }
11034
  } else if (xAlign === 'left') {
11035
+ x -= Math.max(topLeft, bottomLeft) + caretSize;
11036
  } else if (xAlign === 'right') {
11037
+ x += Math.max(topRight, bottomRight) + caretSize;
11038
  }
11039
  return {
11040
  x: _limitValue(x, 0, chart.width - size.width),
11053
  return pushOrConcat([], splitNewlines(callback));
11054
  }
11055
  function createTooltipContext(parent, tooltip, tooltipItems) {
11056
+ return createContext(parent, {
11057
  tooltip,
11058
  tooltipItems,
11059
  type: 'tooltip'
11100
  this.$context = undefined;
11101
  }
11102
  _resolveAnimations() {
11103
+ const cached = this._cachedAnimations;
 
11104
  if (cached) {
11105
  return cached;
11106
  }
11107
+ const chart = this._chart;
11108
+ const options = this.options.setContext(this.getContext());
11109
  const opts = options.enabled && chart.options.animation && options.animations;
11110
+ const animations = new Animations(this._chart, opts);
11111
  if (opts._cacheable) {
11112
+ this._cachedAnimations = Object.freeze(animations);
11113
  }
11114
  return animations;
11115
  }
11116
  getContext() {
11117
+ return this.$context ||
11118
+ (this.$context = createTooltipContext(this._chart.getContext(), this, this._tooltipItems));
 
11119
  }
11120
  getTitle(context, options) {
 
11121
  const {callbacks} = options;
11122
+ const beforeTitle = callbacks.beforeTitle.apply(this, [context]);
11123
+ const title = callbacks.title.apply(this, [context]);
11124
+ const afterTitle = callbacks.afterTitle.apply(this, [context]);
11125
  let lines = [];
11126
  lines = pushOrConcat(lines, splitNewlines(beforeTitle));
11127
  lines = pushOrConcat(lines, splitNewlines(title));
11132
  return getBeforeAfterBodyLines(options.callbacks.beforeBody.apply(this, [tooltipItems]));
11133
  }
11134
  getBody(tooltipItems, options) {
 
11135
  const {callbacks} = options;
11136
  const bodyItems = [];
11137
  each(tooltipItems, (context) => {
11141
  after: []
11142
  };
11143
  const scoped = overrideCallbacks(callbacks, context);
11144
+ pushOrConcat(bodyItem.before, splitNewlines(scoped.beforeLabel.call(this, context)));
11145
+ pushOrConcat(bodyItem.lines, scoped.label.call(this, context));
11146
+ pushOrConcat(bodyItem.after, splitNewlines(scoped.afterLabel.call(this, context)));
11147
  bodyItems.push(bodyItem);
11148
  });
11149
  return bodyItems;
11152
  return getBeforeAfterBodyLines(options.callbacks.afterBody.apply(this, [tooltipItems]));
11153
  }
11154
  getFooter(tooltipItems, options) {
 
11155
  const {callbacks} = options;
11156
+ const beforeFooter = callbacks.beforeFooter.apply(this, [tooltipItems]);
11157
+ const footer = callbacks.footer.apply(this, [tooltipItems]);
11158
+ const afterFooter = callbacks.afterFooter.apply(this, [tooltipItems]);
11159
  let lines = [];
11160
  lines = pushOrConcat(lines, splitNewlines(beforeFooter));
11161
  lines = pushOrConcat(lines, splitNewlines(footer));
11163
  return lines;
11164
  }
11165
  _createItems(options) {
11166
+ const active = this._active;
11167
+ const data = this._chart.data;
 
11168
  const labelColors = [];
11169
  const labelPointStyles = [];
11170
  const labelTextColors = [];
11171
  let tooltipItems = [];
11172
  let i, len;
11173
  for (i = 0, len = active.length; i < len; ++i) {
11174
+ tooltipItems.push(createTooltipItem(this._chart, active[i]));
11175
  }
11176
  if (options.filter) {
11177
  tooltipItems = tooltipItems.filter((element, index, array) => options.filter(element, index, array, data));
11181
  }
11182
  each(tooltipItems, (context) => {
11183
  const scoped = overrideCallbacks(options.callbacks, context);
11184
+ labelColors.push(scoped.labelColor.call(this, context));
11185
+ labelPointStyles.push(scoped.labelPointStyle.call(this, context));
11186
+ labelTextColors.push(scoped.labelTextColor.call(this, context));
11187
  });
11188
+ this.labelColors = labelColors;
11189
+ this.labelPointStyles = labelPointStyles;
11190
+ this.labelTextColors = labelTextColors;
11191
+ this.dataPoints = tooltipItems;
11192
  return tooltipItems;
11193
  }
11194
  update(changed, replay) {
11195
+ const options = this.options.setContext(this.getContext());
11196
+ const active = this._active;
 
11197
  let properties;
11198
  let tooltipItems = [];
11199
  if (!active.length) {
11200
+ if (this.opacity !== 0) {
11201
  properties = {
11202
  opacity: 0
11203
  };
11204
  }
11205
  } else {
11206
+ const position = positioners[options.position].call(this, active, this._eventPosition);
11207
+ tooltipItems = this._createItems(options);
11208
+ this.title = this.getTitle(tooltipItems, options);
11209
+ this.beforeBody = this.getBeforeBody(tooltipItems, options);
11210
+ this.body = this.getBody(tooltipItems, options);
11211
+ this.afterBody = this.getAfterBody(tooltipItems, options);
11212
+ this.footer = this.getFooter(tooltipItems, options);
11213
+ const size = this._size = getTooltipSize(this, options);
11214
  const positionAndSize = Object.assign({}, position, size);
11215
+ const alignment = determineAlignment(this._chart, options, positionAndSize);
11216
+ const backgroundPoint = getBackgroundPoint(options, positionAndSize, alignment, this._chart);
11217
+ this.xAlign = alignment.xAlign;
11218
+ this.yAlign = alignment.yAlign;
11219
  properties = {
11220
  opacity: 1,
11221
  x: backgroundPoint.x,
11226
  caretY: position.y
11227
  };
11228
  }
11229
+ this._tooltipItems = tooltipItems;
11230
+ this.$context = undefined;
11231
  if (properties) {
11232
+ this._resolveAnimations().update(this, properties);
11233
  }
11234
  if (changed && options.external) {
11235
+ options.external.call(this, {chart: this._chart, tooltip: this, replay});
11236
  }
11237
  }
11238
  drawCaret(tooltipPoint, ctx, size, options) {
11243
  }
11244
  getCaretPosition(tooltipPoint, size, options) {
11245
  const {xAlign, yAlign} = this;
11246
+ const {caretSize, cornerRadius} = options;
11247
+ const {topLeft, topRight, bottomLeft, bottomRight} = toTRBLCorners(cornerRadius);
11248
  const {x: ptX, y: ptY} = tooltipPoint;
11249
  const {width, height} = size;
11250
  let x1, x2, x3, y1, y2, y3;
11264
  x3 = x1;
11265
  } else {
11266
  if (xAlign === 'left') {
11267
+ x2 = ptX + Math.max(topLeft, bottomLeft) + (caretSize);
11268
  } else if (xAlign === 'right') {
11269
+ x2 = ptX + width - Math.max(topRight, bottomRight) - caretSize;
11270
  } else {
11271
  x2 = this.caretX;
11272
  }
11286
  return {x1, x2, x3, y1, y2, y3};
11287
  }
11288
  drawTitle(pt, ctx, options) {
11289
+ const title = this.title;
 
11290
  const length = title.length;
11291
  let titleFont, titleSpacing, i;
11292
  if (length) {
11293
+ const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
11294
+ pt.x = getAlignedX(this, options.titleAlign, options);
11295
  ctx.textAlign = rtlHelper.textAlign(options.titleAlign);
11296
  ctx.textBaseline = 'middle';
11297
  titleFont = toFont(options.titleFont);
11308
  }
11309
  }
11310
  _drawColorBox(ctx, pt, i, rtlHelper, options) {
11311
+ const labelColors = this.labelColors[i];
11312
+ const labelPointStyle = this.labelPointStyles[i];
11313
+ const {boxHeight, boxWidth, boxPadding} = options;
 
11314
  const bodyFont = toFont(options.bodyFont);
11315
+ const colorX = getAlignedX(this, 'left', options);
11316
  const rtlColorX = rtlHelper.x(colorX);
11317
  const yOffSet = boxHeight < bodyFont.lineHeight ? (bodyFont.lineHeight - boxHeight) / 2 : 0;
11318
  const colorY = pt.y + yOffSet;
11336
  ctx.strokeStyle = labelColors.borderColor;
11337
  ctx.setLineDash(labelColors.borderDash || []);
11338
  ctx.lineDashOffset = labelColors.borderDashOffset || 0;
11339
+ const outerX = rtlHelper.leftForLtr(rtlColorX, boxWidth - boxPadding);
11340
+ const innerX = rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), boxWidth - boxPadding - 2);
11341
  const borderRadius = toTRBLCorners(labelColors.borderRadius);
11342
  if (Object.values(borderRadius).some(v => v !== 0)) {
11343
  ctx.beginPath();
11369
  ctx.fillRect(innerX, colorY + 1, boxWidth - 2, boxHeight - 2);
11370
  }
11371
  }
11372
+ ctx.fillStyle = this.labelTextColors[i];
11373
  }
11374
  drawBody(pt, ctx, options) {
11375
+ const {body} = this;
11376
+ const {bodySpacing, bodyAlign, displayColors, boxHeight, boxWidth, boxPadding} = options;
 
11377
  const bodyFont = toFont(options.bodyFont);
11378
  let bodyLineHeight = bodyFont.lineHeight;
11379
  let xLinePadding = 0;
11380
+ const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
11381
  const fillLineOfText = function(line) {
11382
  ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyLineHeight / 2);
11383
  pt.y += bodyLineHeight + bodySpacing;
11387
  ctx.textAlign = bodyAlign;
11388
  ctx.textBaseline = 'middle';
11389
  ctx.font = bodyFont.string;
11390
+ pt.x = getAlignedX(this, bodyAlignForCalculation, options);
11391
  ctx.fillStyle = options.bodyColor;
11392
+ each(this.beforeBody, fillLineOfText);
11393
  xLinePadding = displayColors && bodyAlignForCalculation !== 'right'
11394
+ ? bodyAlign === 'center' ? (boxWidth / 2 + boxPadding) : (boxWidth + 2 + boxPadding)
11395
  : 0;
11396
  for (i = 0, ilen = body.length; i < ilen; ++i) {
11397
  bodyItem = body[i];
11398
+ textColor = this.labelTextColors[i];
11399
  ctx.fillStyle = textColor;
11400
  each(bodyItem.before, fillLineOfText);
11401
  lines = bodyItem.lines;
11402
  if (displayColors && lines.length) {
11403
+ this._drawColorBox(ctx, pt, i, rtlHelper, options);
11404
  bodyLineHeight = Math.max(bodyFont.lineHeight, boxHeight);
11405
  }
11406
  for (j = 0, jlen = lines.length; j < jlen; ++j) {
11411
  }
11412
  xLinePadding = 0;
11413
  bodyLineHeight = bodyFont.lineHeight;
11414
+ each(this.afterBody, fillLineOfText);
11415
  pt.y -= bodySpacing;
11416
  }
11417
  drawFooter(pt, ctx, options) {
11418
+ const footer = this.footer;
 
11419
  const length = footer.length;
11420
  let footerFont, i;
11421
  if (length) {
11422
+ const rtlHelper = getRtlAdapter(options.rtl, this.x, this.width);
11423
+ pt.x = getAlignedX(this, options.footerAlign, options);
11424
  pt.y += options.footerMarginTop;
11425
  ctx.textAlign = rtlHelper.textAlign(options.footerAlign);
11426
  ctx.textBaseline = 'middle';
11437
  const {xAlign, yAlign} = this;
11438
  const {x, y} = pt;
11439
  const {width, height} = tooltipSize;
11440
+ const {topLeft, topRight, bottomLeft, bottomRight} = toTRBLCorners(options.cornerRadius);
11441
  ctx.fillStyle = options.backgroundColor;
11442
  ctx.strokeStyle = options.borderColor;
11443
  ctx.lineWidth = options.borderWidth;
11444
  ctx.beginPath();
11445
+ ctx.moveTo(x + topLeft, y);
11446
  if (yAlign === 'top') {
11447
  this.drawCaret(pt, ctx, tooltipSize, options);
11448
  }
11449
+ ctx.lineTo(x + width - topRight, y);
11450
+ ctx.quadraticCurveTo(x + width, y, x + width, y + topRight);
11451
  if (yAlign === 'center' && xAlign === 'right') {
11452
  this.drawCaret(pt, ctx, tooltipSize, options);
11453
  }
11454
+ ctx.lineTo(x + width, y + height - bottomRight);
11455
+ ctx.quadraticCurveTo(x + width, y + height, x + width - bottomRight, y + height);
11456
  if (yAlign === 'bottom') {
11457
  this.drawCaret(pt, ctx, tooltipSize, options);
11458
  }
11459
+ ctx.lineTo(x + bottomLeft, y + height);
11460
+ ctx.quadraticCurveTo(x, y + height, x, y + height - bottomLeft);
11461
  if (yAlign === 'center' && xAlign === 'left') {
11462
  this.drawCaret(pt, ctx, tooltipSize, options);
11463
  }
11464
+ ctx.lineTo(x, y + topLeft);
11465
+ ctx.quadraticCurveTo(x, y, x + topLeft, y);
11466
  ctx.closePath();
11467
  ctx.fill();
11468
  if (options.borderWidth > 0) {
11470
  }
11471
  }
11472
  _updateAnimationTarget(options) {
11473
+ const chart = this._chart;
11474
+ const anims = this.$animations;
 
11475
  const animX = anims && anims.x;
11476
  const animY = anims && anims.y;
11477
  if (animX || animY) {
11478
+ const position = positioners[options.position].call(this, this._active, this._eventPosition);
11479
  if (!position) {
11480
  return;
11481
  }
11482
+ const size = this._size = getTooltipSize(this, options);
11483
+ const positionAndSize = Object.assign({}, position, this._size);
11484
  const alignment = determineAlignment(chart, options, positionAndSize);
11485
  const point = getBackgroundPoint(options, positionAndSize, alignment, chart);
11486
  if (animX._to !== point.x || animY._to !== point.y) {
11487
+ this.xAlign = alignment.xAlign;
11488
+ this.yAlign = alignment.yAlign;
11489
+ this.width = size.width;
11490
+ this.height = size.height;
11491
+ this.caretX = position.x;
11492
+ this.caretY = position.y;
11493
+ this._resolveAnimations().update(this, point);
11494
  }
11495
  }
11496
  }
11497
  draw(ctx) {
11498
+ const options = this.options.setContext(this.getContext());
11499
+ let opacity = this.opacity;
 
11500
  if (!opacity) {
11501
  return;
11502
  }
11503
+ this._updateAnimationTarget(options);
11504
  const tooltipSize = {
11505
+ width: this.width,
11506
+ height: this.height
11507
  };
11508
  const pt = {
11509
+ x: this.x,
11510
+ y: this.y
11511
  };
11512
  opacity = Math.abs(opacity) < 1e-3 ? 0 : opacity;
11513
  const padding = toPadding(options.padding);
11514
+ const hasTooltipContent = this.title.length || this.beforeBody.length || this.body.length || this.afterBody.length || this.footer.length;
11515
  if (options.enabled && hasTooltipContent) {
11516
  ctx.save();
11517
  ctx.globalAlpha = opacity;
11518
+ this.drawBackground(pt, ctx, tooltipSize, options);
11519
  overrideTextDirection(ctx, options.textDirection);
11520
  pt.y += padding.top;
11521
+ this.drawTitle(pt, ctx, options);
11522
+ this.drawBody(pt, ctx, options);
11523
+ this.drawFooter(pt, ctx, options);
11524
  restoreTextDirection(ctx, options.textDirection);
11525
  ctx.restore();
11526
  }
11529
  return this._active || [];
11530
  }
11531
  setActiveElements(activeElements, eventPosition) {
11532
+ const lastActive = this._active;
 
11533
  const active = activeElements.map(({datasetIndex, index}) => {
11534
+ const meta = this._chart.getDatasetMeta(datasetIndex);
11535
  if (!meta) {
11536
  throw new Error('Cannot find a dataset at index ' + datasetIndex);
11537
  }
11542
  };
11543
  });
11544
  const changed = !_elementsEqual(lastActive, active);
11545
+ const positionChanged = this._positionChanged(active, eventPosition);
11546
  if (changed || positionChanged) {
11547
+ this._active = active;
11548
+ this._eventPosition = eventPosition;
11549
+ this.update(true);
11550
  }
11551
  }
11552
  handleEvent(e, replay) {
11553
+ const options = this.options;
11554
+ const lastActive = this._active || [];
 
11555
  let changed = false;
11556
  let active = [];
11557
  if (e.type !== 'mouseout') {
11558
+ active = this._chart.getElementsAtEventForMode(e, options.mode, options, replay);
11559
  if (options.reverse) {
11560
  active.reverse();
11561
  }
11562
  }
11563
+ const positionChanged = this._positionChanged(active, e);
11564
  changed = replay || !_elementsEqual(active, lastActive) || positionChanged;
11565
  if (changed) {
11566
+ this._active = active;
11567
  if (options.enabled || options.external) {
11568
+ this._eventPosition = {
11569
  x: e.x,
11570
  y: e.y
11571
  };
11572
+ this.update(true, replay);
11573
  }
11574
  }
11575
  return changed;
11625
  enabled: true,
11626
  external: null,
11627
  position: 'average',
11628
+ backgroundColor: 'rgba(0,0,0,0.8)',
11629
+ titleColor: '#fff',
 
11630
  titleFont: {
11631
+ weight: 'bold',
 
11632
  },
11633
  titleSpacing: 2,
11634
  titleMarginBottom: 6,
11635
  titleAlign: 'left',
11636
+ bodyColor: '#fff',
11637
  bodySpacing: 2,
11638
  bodyFont: {
 
 
11639
  },
11640
  bodyAlign: 'left',
11641
+ footerColor: '#fff',
11642
  footerSpacing: 2,
11643
  footerMarginTop: 6,
11644
  footerFont: {
11645
  weight: 'bold',
 
11646
  },
11647
  footerAlign: 'left',
11648
+ padding: 6,
11649
+ caretPadding: 2,
11650
+ caretSize: 5,
11651
  cornerRadius: 6,
11652
  boxHeight: (ctx, opts) => opts.bodyFont.size,
11653
  boxWidth: (ctx, opts) => opts.bodyFont.size,
11654
  multiKeyBackground: '#fff',
11655
+ displayColors: true,
11656
+ boxPadding: 0,
11657
+ borderColor: 'rgba(0,0,0,0)',
11658
+ borderWidth: 0,
11659
  animation: {
11660
  duration: 400,
11661
  easing: 'easeOutQuart',
11766
  Tooltip: plugin_tooltip
11767
  });
11768
 
11769
+ const addIfString = (labels, raw, index, addedLabels) => {
11770
+ if (typeof raw === 'string') {
11771
+ index = labels.push(raw) - 1;
11772
+ addedLabels.unshift({index, label: raw});
11773
+ } else if (isNaN(raw)) {
11774
+ index = null;
11775
+ }
11776
+ return index;
11777
+ };
11778
+ function findOrAddLabel(labels, raw, index, addedLabels) {
11779
  const first = labels.indexOf(raw);
11780
  if (first === -1) {
11781
+ return addIfString(labels, raw, index, addedLabels);
11782
  }
11783
  const last = labels.lastIndexOf(raw);
11784
  return first !== last ? index : first;
11789
  super(cfg);
11790
  this._startValue = undefined;
11791
  this._valueRange = 0;
11792
+ this._addedLabels = [];
11793
+ }
11794
+ init(scaleOptions) {
11795
+ const added = this._addedLabels;
11796
+ if (added.length) {
11797
+ const labels = this.getLabels();
11798
+ for (const {index, label} of added) {
11799
+ if (labels[index] === label) {
11800
+ labels.splice(index, 1);
11801
+ }
11802
+ }
11803
+ this._addedLabels = [];
11804
+ }
11805
+ super.init(scaleOptions);
11806
  }
11807
  parse(raw, index) {
11808
  if (isNullOrUndef(raw)) {
11810
  }
11811
  const labels = this.getLabels();
11812
  index = isFinite(index) && labels[index] === raw ? index
11813
+ : findOrAddLabel(labels, raw, valueOrDefault(index, raw), this._addedLabels);
11814
  return validIndex(index, labels.length - 1);
11815
  }
11816
  determineDataLimits() {
11817
+ const {minDefined, maxDefined} = this.getUserBounds();
11818
+ let {min, max} = this.getMinMax(true);
11819
+ if (this.options.bounds === 'ticks') {
 
11820
  if (!minDefined) {
11821
  min = 0;
11822
  }
11823
  if (!maxDefined) {
11824
+ max = this.getLabels().length - 1;
11825
  }
11826
  }
11827
+ this.min = min;
11828
+ this.max = max;
11829
  }
11830
  buildTicks() {
11831
+ const min = this.min;
11832
+ const max = this.max;
11833
+ const offset = this.options.offset;
 
11834
  const ticks = [];
11835
+ let labels = this.getLabels();
11836
  labels = (min === 0 && max === labels.length - 1) ? labels : labels.slice(min, max + 1);
11837
+ this._valueRange = Math.max(labels.length - (offset ? 0 : 1), 1);
11838
+ this._startValue = this.min - (offset ? 0.5 : 0);
11839
  for (let value = min; value <= max; value++) {
11840
  ticks.push({value});
11841
  }
11842
  return ticks;
11843
  }
11844
  getLabelForValue(value) {
11845
+ const labels = this.getLabels();
 
11846
  if (value >= 0 && value < labels.length) {
11847
  return labels[value];
11848
  }
11849
  return value;
11850
  }
11851
  configure() {
 
11852
  super.configure();
11853
+ if (!this.isHorizontal()) {
11854
+ this._reversePixels = !this._reversePixels;
11855
  }
11856
  }
11857
  getPixelForValue(value) {
 
11858
  if (typeof value !== 'number') {
11859
+ value = this.parse(value);
11860
  }
11861
+ return value === null ? NaN : this.getPixelForDecimal((value - this._startValue) / this._valueRange);
11862
  }
11863
  getPixelForTick(index) {
11864
+ const ticks = this.ticks;
 
11865
  if (index < 0 || index > ticks.length - 1) {
11866
  return null;
11867
  }
11868
+ return this.getPixelForValue(ticks[index].value);
11869
  }
11870
  getValueForPixel(pixel) {
11871
+ return Math.round(this._startValue + this.getDecimalForPixel(pixel) * this._valueRange);
 
11872
  }
11873
  getBasePixel() {
11874
  return this.bottom;
11955
  ticks.push({value: Math.round((niceMin + j * spacing) * factor) / factor});
11956
  }
11957
  if (maxDefined && includeBounds && niceMax !== max) {
11958
+ if (ticks.length && 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});
11990
  return +raw;
11991
  }
11992
  handleTickRangeOptions() {
11993
+ const {beginAtZero} = this.options;
11994
+ const {minDefined, maxDefined} = this.getUserBounds();
11995
+ let {min, max} = this;
 
11996
  const setMin = v => (min = minDefined ? min : v);
11997
  const setMax = v => (max = maxDefined ? max : v);
11998
  if (beginAtZero) {
12014
  setMin(min - offset);
12015
  }
12016
  }
12017
+ this.min = min;
12018
+ this.max = max;
12019
  }
12020
  getTickLimit() {
12021
+ const tickOpts = this.options.ticks;
 
12022
  let {maxTicksLimit, stepSize} = tickOpts;
12023
  let maxTicks;
12024
  if (stepSize) {
12025
+ maxTicks = Math.ceil(this.max / stepSize) - Math.floor(this.min / stepSize) + 1;
12026
+ if (maxTicks > 1000) {
12027
+ console.warn(`scales.${this.id}.ticks.stepSize: ${stepSize} would result generating up to ${maxTicks} ticks. Limiting to 1000.`);
12028
+ maxTicks = 1000;
12029
+ }
12030
  } else {
12031
+ maxTicks = this.computeTickLimit();
12032
  maxTicksLimit = maxTicksLimit || 11;
12033
  }
12034
  if (maxTicksLimit) {
12040
  return Number.POSITIVE_INFINITY;
12041
  }
12042
  buildTicks() {
12043
+ const opts = this.options;
 
12044
  const tickOpts = opts.ticks;
12045
+ let maxTicks = this.getTickLimit();
12046
  maxTicks = Math.max(2, maxTicks);
12047
  const numericGeneratorOptions = {
12048
  maxTicks,
12052
  precision: tickOpts.precision,
12053
  step: tickOpts.stepSize,
12054
  count: tickOpts.count,
12055
+ maxDigits: this._maxDigits(),
12056
+ horizontal: this.isHorizontal(),
12057
  minRotation: tickOpts.minRotation || 0,
12058
  includeBounds: tickOpts.includeBounds !== false
12059
  };
12060
+ const dataRange = this._range || this;
12061
  const ticks = generateTicks$1(numericGeneratorOptions, dataRange);
12062
  if (opts.bounds === 'ticks') {
12063
+ _setMinAndMaxByKey(ticks, this, 'value');
12064
  }
12065
  if (opts.reverse) {
12066
  ticks.reverse();
12067
+ this.start = this.max;
12068
+ this.end = this.min;
12069
  } else {
12070
+ this.start = this.min;
12071
+ this.end = this.max;
12072
  }
12073
  return ticks;
12074
  }
12075
  configure() {
12076
+ const ticks = this.ticks;
12077
+ let start = this.min;
12078
+ let end = this.max;
 
12079
  super.configure();
12080
+ if (this.options.offset && ticks.length) {
12081
  const offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
12082
  start -= offset;
12083
  end += offset;
12084
  }
12085
+ this._startValue = start;
12086
+ this._endValue = end;
12087
+ this._valueRange = end - start;
12088
  }
12089
  getLabelForValue(value) {
12090
+ return formatNumber(value, this.chart.options.locale, this.options.ticks.format);
12091
  }
12092
  }
12093
 
12094
  class LinearScale extends LinearScaleBase {
12095
  determineDataLimits() {
12096
+ const {min, max} = this.getMinMax(true);
12097
+ this.min = isNumberFinite(min) ? min : 0;
12098
+ this.max = isNumberFinite(max) ? max : 1;
12099
+ this.handleTickRangeOptions();
 
12100
  }
12101
  computeTickLimit() {
12102
+ const horizontal = this.isHorizontal();
12103
+ const length = horizontal ? this.width : this.height;
12104
+ const minRotation = toRadians(this.options.ticks.minRotation);
 
12105
  const ratio = (horizontal ? Math.sin(minRotation) : Math.cos(minRotation)) || 0.001;
12106
+ const tickFont = this._resolveTickFontOptions(0);
12107
  return Math.ceil(length / Math.min(40, tickFont.lineHeight / ratio));
12108
  }
12109
  getPixelForValue(value) {
12163
  return isNumberFinite(value) && value > 0 ? value : null;
12164
  }
12165
  determineDataLimits() {
12166
+ const {min, max} = this.getMinMax(true);
12167
+ this.min = isNumberFinite(min) ? Math.max(0, min) : null;
12168
+ this.max = isNumberFinite(max) ? Math.max(0, max) : null;
12169
+ if (this.options.beginAtZero) {
12170
+ this._zero = true;
 
12171
  }
12172
+ this.handleTickRangeOptions();
12173
  }
12174
  handleTickRangeOptions() {
12175
+ const {minDefined, maxDefined} = this.getUserBounds();
12176
+ let min = this.min;
12177
+ let max = this.max;
 
12178
  const setMin = v => (min = minDefined ? min : v);
12179
  const setMax = v => (max = maxDefined ? max : v);
12180
  const exp = (v, m) => Math.pow(10, Math.floor(log10(v)) + m);
12193
  if (max <= 0) {
12194
  setMax(exp(min, +1));
12195
  }
12196
+ if (this._zero && this.min !== this._suggestedMin && min === exp(this.min, 0)) {
12197
  setMin(exp(min, -1));
12198
  }
12199
+ this.min = min;
12200
+ this.max = max;
12201
  }
12202
  buildTicks() {
12203
+ const opts = this.options;
 
12204
  const generationOptions = {
12205
+ min: this._userMin,
12206
+ max: this._userMax
12207
  };
12208
+ const ticks = generateTicks(generationOptions, this);
12209
  if (opts.bounds === 'ticks') {
12210
+ _setMinAndMaxByKey(ticks, this, 'value');
12211
  }
12212
  if (opts.reverse) {
12213
  ticks.reverse();
12214
+ this.start = this.max;
12215
+ this.end = this.min;
12216
  } else {
12217
+ this.start = this.min;
12218
+ this.end = this.max;
12219
  }
12220
  return ticks;
12221
  }
12222
  getLabelForValue(value) {
12223
+ return value === undefined
12224
+ ? '0'
12225
+ : formatNumber(value, this.chart.options.locale, this.options.ticks.format);
12226
  }
12227
  configure() {
12228
+ const start = this.min;
 
12229
  super.configure();
12230
+ this._startValue = log10(start);
12231
+ this._valueRange = log10(this.max) - log10(start);
12232
  }
12233
  getPixelForValue(value) {
 
12234
  if (value === undefined || value === 0) {
12235
+ value = this.min;
12236
  }
12237
  if (value === null || isNaN(value)) {
12238
  return NaN;
12239
  }
12240
+ return this.getPixelForDecimal(value === this.min
12241
  ? 0
12242
+ : (log10(value) - this._startValue) / this._valueRange);
12243
  }
12244
  getValueForPixel(pixel) {
12245
+ const decimal = this.getDecimalForPixel(pixel);
12246
+ return Math.pow(10, this._startValue + decimal * this._valueRange);
 
12247
  }
12248
  }
12249
  LogarithmicScale.id = 'logarithmic';
12441
  return isNumber(param) ? param : 0;
12442
  }
12443
  function createPointLabelContext(parent, index, label) {
12444
+ return createContext(parent, {
12445
  label,
12446
  index,
12447
  type: 'pointLabel'
12457
  this._pointLabelItems = [];
12458
  }
12459
  setDimensions() {
12460
+ this.width = this.maxWidth;
12461
+ this.height = this.maxHeight;
12462
+ this.paddingTop = getTickBackdropHeight(this.options) / 2;
12463
+ this.xCenter = Math.floor(this.width / 2);
12464
+ this.yCenter = Math.floor((this.height - this.paddingTop) / 2);
12465
+ this.drawingArea = Math.min(this.height - this.paddingTop, this.width) / 2;
 
12466
  }
12467
  determineDataLimits() {
12468
+ const {min, max} = this.getMinMax(false);
12469
+ this.min = isNumberFinite(min) && !isNaN(min) ? min : 0;
12470
+ this.max = isNumberFinite(max) && !isNaN(max) ? max : 0;
12471
+ this.handleTickRangeOptions();
 
12472
  }
12473
  computeTickLimit() {
12474
  return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
12475
  }
12476
  generateTickLabels(ticks) {
12477
+ LinearScaleBase.prototype.generateTickLabels.call(this, ticks);
12478
+ this._pointLabels = this.getLabels().map((value, index) => {
12479
+ const label = callback(this.options.pointLabels.callback, [value, index], this);
 
12480
  return label || label === 0 ? label : '';
12481
  });
12482
  }
12483
  fit() {
12484
+ const opts = this.options;
 
12485
  if (opts.display && opts.pointLabels.display) {
12486
+ fitWithPointLabels(this);
12487
  } else {
12488
+ this.setCenterPoint(0, 0, 0, 0);
12489
  }
12490
  }
12491
  _setReductions(largestPossibleRadius, furthestLimits, furthestAngles) {
 
12492
  let radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
12493
+ let radiusReductionRight = Math.max(furthestLimits.r - this.width, 0) / Math.sin(furthestAngles.r);
12494
  let radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
12495
+ let radiusReductionBottom = -Math.max(furthestLimits.b - (this.height - this.paddingTop), 0) / Math.cos(furthestAngles.b);
12496
  radiusReductionLeft = numberOrZero(radiusReductionLeft);
12497
  radiusReductionRight = numberOrZero(radiusReductionRight);
12498
  radiusReductionTop = numberOrZero(radiusReductionTop);
12499
  radiusReductionBottom = numberOrZero(radiusReductionBottom);
12500
+ this.drawingArea = Math.max(largestPossibleRadius / 2, Math.min(
12501
  Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
12502
  Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2)));
12503
+ this.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
12504
  }
12505
  setCenterPoint(leftMovement, rightMovement, topMovement, bottomMovement) {
12506
+ const maxRight = this.width - rightMovement - this.drawingArea;
12507
+ const maxLeft = leftMovement + this.drawingArea;
12508
+ const maxTop = topMovement + this.drawingArea;
12509
+ const maxBottom = (this.height - this.paddingTop) - bottomMovement - this.drawingArea;
12510
+ this.xCenter = Math.floor(((maxLeft + maxRight) / 2) + this.left);
12511
+ this.yCenter = Math.floor(((maxTop + maxBottom) / 2) + this.top + this.paddingTop);
 
12512
  }
12513
  getIndexAngle(index) {
12514
  const angleMultiplier = TAU / this.getLabels().length;
12516
  return _normalizeAngle(index * angleMultiplier + toRadians(startAngle));
12517
  }
12518
  getDistanceFromCenterForValue(value) {
 
12519
  if (isNullOrUndef(value)) {
12520
  return NaN;
12521
  }
12522
+ const scalingFactor = this.drawingArea / (this.max - this.min);
12523
+ if (this.options.reverse) {
12524
+ return (this.max - value) * scalingFactor;
12525
  }
12526
+ return (value - this.min) * scalingFactor;
12527
  }
12528
  getValueForDistanceFromCenter(distance) {
12529
  if (isNullOrUndef(distance)) {
12530
  return NaN;
12531
  }
12532
+ const scaledDistance = distance / (this.drawingArea / (this.max - this.min));
12533
+ return this.options.reverse ? this.max - scaledDistance : this.min + scaledDistance;
 
12534
  }
12535
  getPointLabelContext(index) {
12536
+ const pointLabels = this._pointLabels || [];
 
12537
  if (index >= 0 && index < pointLabels.length) {
12538
  const pointLabel = pointLabels[index];
12539
+ return createPointLabelContext(this.getContext(), index, pointLabel);
12540
  }
12541
  }
12542
  getPointPosition(index, distanceFromCenter) {
12543
+ const angle = this.getIndexAngle(index) - HALF_PI;
 
12544
  return {
12545
+ x: Math.cos(angle) * distanceFromCenter + this.xCenter,
12546
+ y: Math.sin(angle) * distanceFromCenter + this.yCenter,
12547
  angle
12548
  };
12549
  }
12563
  };
12564
  }
12565
  drawBackground() {
12566
+ const {backgroundColor, grid: {circular}} = this.options;
 
12567
  if (backgroundColor) {
12568
+ const ctx = this.ctx;
12569
  ctx.save();
12570
  ctx.beginPath();
12571
+ pathRadiusLine(this, this.getDistanceFromCenterForValue(this._endValue), circular, this.getLabels().length);
12572
  ctx.closePath();
12573
  ctx.fillStyle = backgroundColor;
12574
  ctx.fill();
12576
  }
12577
  }
12578
  drawGrid() {
12579
+ const ctx = this.ctx;
12580
+ const opts = this.options;
 
12581
  const {angleLines, grid} = opts;
12582
+ const labelCount = this.getLabels().length;
12583
  let i, offset, position;
12584
  if (opts.pointLabels.display) {
12585
+ drawPointLabels(this, labelCount);
12586
  }
12587
  if (grid.display) {
12588
+ this.ticks.forEach((tick, index) => {
12589
  if (index !== 0) {
12590
+ offset = this.getDistanceFromCenterForValue(tick.value);
12591
+ const optsAtIndex = grid.setContext(this.getContext(index - 1));
12592
+ drawRadiusLine(this, optsAtIndex, offset, labelCount);
12593
  }
12594
  });
12595
  }
12596
  if (angleLines.display) {
12597
  ctx.save();
12598
+ for (i = this.getLabels().length - 1; i >= 0; i--) {
12599
+ const optsAtIndex = angleLines.setContext(this.getPointLabelContext(i));
12600
  const {color, lineWidth} = optsAtIndex;
12601
  if (!lineWidth || !color) {
12602
  continue;
12605
  ctx.strokeStyle = color;
12606
  ctx.setLineDash(optsAtIndex.borderDash);
12607
  ctx.lineDashOffset = optsAtIndex.borderDashOffset;
12608
+ offset = this.getDistanceFromCenterForValue(opts.ticks.reverse ? this.min : this.max);
12609
+ position = this.getPointPosition(i, offset);
12610
  ctx.beginPath();
12611
+ ctx.moveTo(this.xCenter, this.yCenter);
12612
  ctx.lineTo(position.x, position.y);
12613
  ctx.stroke();
12614
  }
12617
  }
12618
  drawBorder() {}
12619
  drawLabels() {
12620
+ const ctx = this.ctx;
12621
+ const opts = this.options;
 
12622
  const tickOpts = opts.ticks;
12623
  if (!tickOpts.display) {
12624
  return;
12625
  }
12626
+ const startAngle = this.getIndexAngle(0);
12627
  let offset, width;
12628
  ctx.save();
12629
+ ctx.translate(this.xCenter, this.yCenter);
12630
  ctx.rotate(startAngle);
12631
  ctx.textAlign = 'center';
12632
  ctx.textBaseline = 'middle';
12633
+ this.ticks.forEach((tick, index) => {
12634
  if (index === 0 && !opts.reverse) {
12635
  return;
12636
  }
12637
+ const optsAtIndex = tickOpts.setContext(this.getContext(index));
12638
  const tickFont = toFont(optsAtIndex.font);
12639
+ offset = this.getDistanceFromCenterForValue(this.ticks[index].value);
12640
  if (optsAtIndex.showLabelBackdrop) {
12641
  ctx.font = tickFont.string;
12642
  width = ctx.measureText(tick.label).width;
12845
  };
12846
  }
12847
  determineDataLimits() {
12848
+ const options = this.options;
12849
+ const adapter = this._adapter;
 
12850
  const unit = options.time.unit || 'day';
12851
+ let {min, max, minDefined, maxDefined} = this.getUserBounds();
12852
  function _applyBounds(bounds) {
12853
  if (!minDefined && !isNaN(bounds.min)) {
12854
  min = Math.min(min, bounds.min);
12858
  }
12859
  }
12860
  if (!minDefined || !maxDefined) {
12861
+ _applyBounds(this._getLabelBounds());
12862
  if (options.bounds !== 'ticks' || options.ticks.source !== 'labels') {
12863
+ _applyBounds(this.getMinMax(false));
12864
  }
12865
  }
12866
  min = isNumberFinite(min) && !isNaN(min) ? min : +adapter.startOf(Date.now(), unit);
12867
  max = isNumberFinite(max) && !isNaN(max) ? max : +adapter.endOf(Date.now(), unit) + 1;
12868
+ this.min = Math.min(min, max - 1);
12869
+ this.max = Math.max(min + 1, max);
12870
  }
12871
  _getLabelBounds() {
12872
  const arr = this.getLabelTimestamps();
12879
  return {min, max};
12880
  }
12881
  buildTicks() {
12882
+ const options = this.options;
 
12883
  const timeOpts = options.time;
12884
  const tickOpts = options.ticks;
12885
+ const timestamps = tickOpts.source === 'labels' ? this.getLabelTimestamps() : this._generate();
12886
  if (options.bounds === 'ticks' && timestamps.length) {
12887
+ this.min = this._userMin || timestamps[0];
12888
+ this.max = this._userMax || timestamps[timestamps.length - 1];
12889
  }
12890
+ const min = this.min;
12891
+ const max = this.max;
12892
  const ticks = _filterBetween(timestamps, min, max);
12893
+ this._unit = timeOpts.unit || (tickOpts.autoSkip
12894
+ ? determineUnitForAutoTicks(timeOpts.minUnit, this.min, this.max, this._getLabelCapacity(min))
12895
+ : determineUnitForFormatting(this, ticks.length, timeOpts.minUnit, this.min, this.max));
12896
+ this._majorUnit = !tickOpts.major.enabled || this._unit === 'year' ? undefined
12897
+ : determineMajorUnit(this._unit);
12898
+ this.initOffsets(timestamps);
12899
  if (options.reverse) {
12900
  ticks.reverse();
12901
  }
12902
+ return ticksFromTimestamps(this, ticks, this._majorUnit);
12903
  }
12904
  initOffsets(timestamps) {
 
12905
  let start = 0;
12906
  let end = 0;
12907
  let first, last;
12908
+ if (this.options.offset && timestamps.length) {
12909
+ first = this.getDecimalForValue(timestamps[0]);
12910
  if (timestamps.length === 1) {
12911
  start = 1 - first;
12912
  } else {
12913
+ start = (this.getDecimalForValue(timestamps[1]) - first) / 2;
12914
  }
12915
+ last = this.getDecimalForValue(timestamps[timestamps.length - 1]);
12916
  if (timestamps.length === 1) {
12917
  end = last;
12918
  } else {
12919
+ end = (last - this.getDecimalForValue(timestamps[timestamps.length - 2])) / 2;
12920
  }
12921
  }
12922
  const limit = timestamps.length < 3 ? 0.5 : 0.25;
12923
  start = _limitValue(start, 0, limit);
12924
  end = _limitValue(end, 0, limit);
12925
+ this._offsets = {start, end, factor: 1 / (start + 1 + end)};
12926
  }
12927
  _generate() {
12928
+ const adapter = this._adapter;
12929
+ const min = this.min;
12930
+ const max = this.max;
12931
+ const options = this.options;
 
12932
  const timeOpts = options.time;
12933
+ const minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, this._getLabelCapacity(min));
12934
  const stepSize = valueOrDefault(timeOpts.stepSize, 1);
12935
  const weekday = minor === 'week' ? timeOpts.isoWeekday : false;
12936
  const hasWeekday = isNumber(weekday) || weekday === true;
12944
  if (adapter.diff(max, min, minor) > 100000 * stepSize) {
12945
  throw new Error(min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor);
12946
  }
12947
+ const timestamps = options.ticks.source === 'data' && this.getDataTimestamps();
12948
  for (time = first, count = 0; time < max; time = +adapter.add(time, stepSize, minor), count++) {
12949
  addTick(ticks, time, timestamps);
12950
  }
12954
  return Object.keys(ticks).sort((a, b) => a - b).map(x => +x);
12955
  }
12956
  getLabelForValue(value) {
12957
+ const adapter = this._adapter;
12958
+ const timeOpts = this.options.time;
 
12959
  if (timeOpts.tooltipFormat) {
12960
  return adapter.format(value, timeOpts.tooltipFormat);
12961
  }
12962
  return adapter.format(value, timeOpts.displayFormats.datetime);
12963
  }
12964
  _tickFormatFunction(time, index, ticks, format) {
12965
+ const options = this.options;
 
12966
  const formats = options.time.displayFormats;
12967
+ const unit = this._unit;
12968
+ const majorUnit = this._majorUnit;
12969
  const minorFormat = unit && formats[unit];
12970
  const majorFormat = majorUnit && formats[majorUnit];
12971
  const tick = ticks[index];
12972
  const major = majorUnit && majorFormat && tick && tick.major;
12973
+ const label = this._adapter.format(time, format || (major ? majorFormat : minorFormat));
12974
  const formatter = options.ticks.callback;
12975
+ return formatter ? callback(formatter, [label, index, ticks], this) : label;
12976
  }
12977
  generateTickLabels(ticks) {
12978
  let i, ilen, tick;
12982
  }
12983
  }
12984
  getDecimalForValue(value) {
12985
+ return value === null ? NaN : (value - this.min) / (this.max - this.min);
 
12986
  }
12987
  getPixelForValue(value) {
12988
+ const offsets = this._offsets;
12989
+ const pos = this.getDecimalForValue(value);
12990
+ return this.getPixelForDecimal((offsets.start + pos) * offsets.factor);
 
12991
  }
12992
  getValueForPixel(pixel) {
12993
+ const offsets = this._offsets;
12994
+ const pos = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
12995
+ return this.min + pos * (this.max - this.min);
 
12996
  }
12997
  _getLabelSize(label) {
12998
+ const ticksOpts = this.options.ticks;
12999
+ const tickLabelWidth = this.ctx.measureText(label).width;
13000
+ const angle = toRadians(this.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
 
13001
  const cosRotation = Math.cos(angle);
13002
  const sinRotation = Math.sin(angle);
13003
+ const tickFontSize = this._resolveTickFontOptions(0).size;
13004
  return {
13005
  w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation),
13006
  h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation)
13007
  };
13008
  }
13009
  _getLabelCapacity(exampleTime) {
13010
+ const timeOpts = this.options.time;
 
13011
  const displayFormats = timeOpts.displayFormats;
13012
  const format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
13013
+ const exampleLabel = this._tickFormatFunction(exampleTime, 0, ticksFromTimestamps(this, [exampleTime], this._majorUnit), format);
13014
+ const size = this._getLabelSize(exampleLabel);
13015
+ const capacity = Math.floor(this.isHorizontal() ? this.width / size.w : this.height / size.h) - 1;
13016
  return capacity > 0 ? capacity : 1;
13017
  }
13018
  getDataTimestamps() {
13019
+ let timestamps = this._cache.data || [];
 
13020
  let i, ilen;
13021
  if (timestamps.length) {
13022
  return timestamps;
13023
  }
13024
+ const metas = this.getMatchingVisibleMetas();
13025
+ if (this._normalized && metas.length) {
13026
+ return (this._cache.data = metas[0].controller.getAllParsedValues(this));
13027
  }
13028
  for (i = 0, ilen = metas.length; i < ilen; ++i) {
13029
+ timestamps = timestamps.concat(metas[i].controller.getAllParsedValues(this));
13030
  }
13031
+ return (this._cache.data = this.normalize(timestamps));
13032
  }
13033
  getLabelTimestamps() {
13034
+ const timestamps = this._cache.labels || [];
 
13035
  let i, ilen;
13036
  if (timestamps.length) {
13037
  return timestamps;
13038
  }
13039
+ const labels = this.getLabels();
13040
  for (i = 0, ilen = labels.length; i < ilen; ++i) {
13041
+ timestamps.push(parse(this, labels[i]));
13042
  }
13043
+ return (this._cache.labels = this._normalized ? timestamps : this.normalize(timestamps));
13044
  }
13045
  normalize(values) {
13046
  return _arrayUnique(values.sort(sorter));
13094
  this._tableRange = undefined;
13095
  }
13096
  initOffsets() {
13097
+ const timestamps = this._getTimestampsForTable();
13098
+ const table = this._table = this.buildLookupTable(timestamps);
13099
+ this._minPos = interpolate(table, this.min);
13100
+ this._tableRange = interpolate(table, this.max) - this._minPos;
 
13101
  super.initOffsets(timestamps);
13102
  }
13103
  buildLookupTable(timestamps) {
13128
  return table;
13129
  }
13130
  _getTimestampsForTable() {
13131
+ let timestamps = this._cache.all || [];
 
13132
  if (timestamps.length) {
13133
  return timestamps;
13134
  }
13135
+ const data = this.getDataTimestamps();
13136
+ const label = this.getLabelTimestamps();
13137
  if (data.length && label.length) {
13138
+ timestamps = this.normalize(data.concat(label));
13139
  } else {
13140
  timestamps = data.length ? data : label;
13141
  }
13142
+ timestamps = this._cache.all = timestamps;
13143
  return timestamps;
13144
  }
13145
  getDecimalForValue(value) {
13146
  return (interpolate(this._table, value) - this._minPos) / this._tableRange;
13147
  }
13148
  getValueForPixel(pixel) {
13149
+ const offsets = this._offsets;
13150
+ const decimal = this.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
13151
+ return interpolate(this._table, decimal * this._tableRange + this._minPos, true);
 
13152
  }
13153
  }
13154
  TimeSeriesScale.id = 'timeseries';
admin/js/dataTables.bootstrap5.min.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ DataTables Bootstrap 5 integration
3
+ 2020 SpryMedia Ltd - datatables.net/license
4
+ */
5
+ var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var e=a.length,d=0;d<e;d++){var f=a[d];if(b.call(c,f,d,a))return{i:d,v:f}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.ISOLATE_POLYFILLS=!1;
6
+ $jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(a==Array.prototype||a==Object.prototype)return a;a[b]=c.value;return a};$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
7
+ $jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";var $jscomp$lookupPolyfilledValue=function(a,b){var c=$jscomp.propertyToPolyfillSymbol[b];if(null==c)return a[b];c=a[c];return void 0!==c?c:a[b]};
8
+ $jscomp.polyfill=function(a,b,c,e){b&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(a,b,c,e):$jscomp.polyfillUnisolated(a,b,c,e))};$jscomp.polyfillUnisolated=function(a,b,c,e){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var d=a[e];if(!(d in c))return;c=c[d]}a=a[a.length-1];e=c[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})};
9
+ $jscomp.polyfillIsolated=function(a,b,c,e){var d=a.split(".");a=1===d.length;e=d[0];e=!a&&e in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var f=0;f<d.length-1;f++){var l=d[f];if(!(l in e))return;e=e[l]}d=d[d.length-1];c=$jscomp.IS_SYMBOL_NATIVE&&"es6"===c?e[d]:null;b=b(c);null!=b&&(a?$jscomp.defineProperty($jscomp.polyfills,d,{configurable:!0,writable:!0,value:b}):b!==c&&($jscomp.propertyToPolyfillSymbol[d]=$jscomp.IS_SYMBOL_NATIVE?$jscomp.global.Symbol(d):$jscomp.POLYFILL_PREFIX+d,d=
10
+ $jscomp.propertyToPolyfillSymbol[d],$jscomp.defineProperty(e,d,{configurable:!0,writable:!0,value:b})))};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(b,c){return $jscomp.findInternal(this,b,c).v}},"es6","es3");
11
+ (function(a){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(b){return a(b,window,document)}):"object"===typeof exports?module.exports=function(b,c){b||(b=window);c&&c.fn.dataTable||(c=require("datatables.net")(b,c).$);return a(c,b,b.document)}:a(jQuery,window,document)})(function(a,b,c,e){var d=a.fn.dataTable;a.extend(!0,d.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
12
+ renderer:"bootstrap"});a.extend(d.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap5",sFilterInput:"form-control form-control-sm",sLengthSelect:"form-select form-select-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"});d.ext.renderer.pageButton.bootstrap=function(f,l,A,B,m,t){var u=new d.Api(f),C=f.oClasses,n=f.oLanguage.oPaginate,D=f.oLanguage.oAria.paginate||{},h,k,v=0,y=function(q,w){var x,E=function(p){p.preventDefault();a(p.currentTarget).hasClass("disabled")||
13
+ u.page()==p.data.action||u.page(p.data.action).draw("page")};var r=0;for(x=w.length;r<x;r++){var g=w[r];if(Array.isArray(g))y(q,g);else{k=h="";switch(g){case "ellipsis":h="&#x2026;";k="disabled";break;case "first":h=n.sFirst;k=g+(0<m?"":" disabled");break;case "previous":h=n.sPrevious;k=g+(0<m?"":" disabled");break;case "next":h=n.sNext;k=g+(m<t-1?"":" disabled");break;case "last":h=n.sLast;k=g+(m<t-1?"":" disabled");break;default:h=g+1,k=m===g?"active":""}if(h){var F=a("<li>",{"class":C.sPageButton+
14
+ " "+k,id:0===A&&"string"===typeof g?f.sTableId+"_"+g:null}).append(a("<a>",{href:"#","aria-controls":f.sTableId,"aria-label":D[g],"data-dt-idx":v,tabindex:f.iTabIndex,"class":"page-link"}).html(h)).appendTo(q);f.oApi._fnBindAction(F,{action:g},E);v++}}}};try{var z=a(l).find(c.activeElement).data("dt-idx")}catch(q){}y(a(l).empty().html('<ul class="pagination"/>').children("ul"),B);z!==e&&a(l).find("[data-dt-idx="+z+"]").trigger("focus")};return d});
admin/js/popper.min.js DELETED
@@ -1,5 +0,0 @@
1
- /*
2
- Copyright (C) Federico Zivolo 2017
3
- Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
4
- */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=getComputedStyle(e,null);return t?o[t]:o}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll)/.test(r+s+p)?e:n(o(e))}function r(e){var o=e&&e.offsetParent,i=o&&o.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TD','TABLE'].indexOf(o.nodeName)&&'static'===t(o,'position')?r(o):o:e?e.ownerDocument.documentElement:document.documentElement}function p(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||r(e.firstElementChild)===e)}function s(e){return null===e.parentNode?e:s(e.parentNode)}function d(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,i=o?e:t,n=o?t:e,a=document.createRange();a.setStart(i,0),a.setEnd(n,0);var l=a.commonAncestorContainer;if(e!==l&&t!==l||i.contains(n))return p(l)?l:r(l);var f=s(e);return f.host?d(f.host,t):d(e,s(t).host)}function a(e){var t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:'top',o='top'===t?'scrollTop':'scrollLeft',i=e.nodeName;if('BODY'===i||'HTML'===i){var n=e.ownerDocument.documentElement,r=e.ownerDocument.scrollingElement||n;return r[o]}return e[o]}function l(e,t){var o=2<arguments.length&&void 0!==arguments[2]&&arguments[2],i=a(t,'top'),n=a(t,'left'),r=o?-1:1;return e.top+=i*r,e.bottom+=i*r,e.left+=n*r,e.right+=n*r,e}function f(e,t){var o='x'===t?'Left':'Top',i='Left'==o?'Right':'Bottom';return parseFloat(e['border'+o+'Width'],10)+parseFloat(e['border'+i+'Width'],10)}function m(e,t,o,i){return J(t['offset'+e],t['scroll'+e],o['client'+e],o['offset'+e],o['scroll'+e],ie()?o['offset'+e]+i['margin'+('Height'===e?'Top':'Left')]+i['margin'+('Height'===e?'Bottom':'Right')]:0)}function h(){var e=document.body,t=document.documentElement,o=ie()&&getComputedStyle(t);return{height:m('Height',e,t,o),width:m('Width',e,t,o)}}function c(e){return se({},e,{right:e.left+e.width,bottom:e.top+e.height})}function g(e){var o={};if(ie())try{o=e.getBoundingClientRect();var i=a(e,'top'),n=a(e,'left');o.top+=i,o.left+=n,o.bottom+=i,o.right+=n}catch(e){}else o=e.getBoundingClientRect();var r={left:o.left,top:o.top,width:o.right-o.left,height:o.bottom-o.top},p='HTML'===e.nodeName?h():{},s=p.width||e.clientWidth||r.right-r.left,d=p.height||e.clientHeight||r.bottom-r.top,l=e.offsetWidth-s,m=e.offsetHeight-d;if(l||m){var g=t(e);l-=f(g,'x'),m-=f(g,'y'),r.width-=l,r.height-=m}return c(r)}function u(e,o){var i=ie(),r='HTML'===o.nodeName,p=g(e),s=g(o),d=n(e),a=t(o),f=parseFloat(a.borderTopWidth,10),m=parseFloat(a.borderLeftWidth,10),h=c({top:p.top-s.top-f,left:p.left-s.left-m,width:p.width,height:p.height});if(h.marginTop=0,h.marginLeft=0,!i&&r){var u=parseFloat(a.marginTop,10),b=parseFloat(a.marginLeft,10);h.top-=f-u,h.bottom-=f-u,h.left-=m-b,h.right-=m-b,h.marginTop=u,h.marginLeft=b}return(i?o.contains(d):o===d&&'BODY'!==d.nodeName)&&(h=l(h,o)),h}function b(e){var t=e.ownerDocument.documentElement,o=u(e,t),i=J(t.clientWidth,window.innerWidth||0),n=J(t.clientHeight,window.innerHeight||0),r=a(t),p=a(t,'left'),s={top:r-o.top+o.marginTop,left:p-o.left+o.marginLeft,width:i,height:n};return c(s)}function w(e){var i=e.nodeName;return'BODY'===i||'HTML'===i?!1:'fixed'===t(e,'position')||w(o(e))}function y(e,t,i,r){var p={top:0,left:0},s=d(e,t);if('viewport'===r)p=b(s);else{var a;'scrollParent'===r?(a=n(o(t)),'BODY'===a.nodeName&&(a=e.ownerDocument.documentElement)):'window'===r?a=e.ownerDocument.documentElement:a=r;var l=u(a,s);if('HTML'===a.nodeName&&!w(s)){var f=h(),m=f.height,c=f.width;p.top+=l.top-l.marginTop,p.bottom=m+l.top,p.left+=l.left-l.marginLeft,p.right=c+l.left}else p=l}return p.left+=i,p.top+=i,p.right-=i,p.bottom-=i,p}function E(e){var t=e.width,o=e.height;return t*o}function v(e,t,o,i,n){var r=5<arguments.length&&void 0!==arguments[5]?arguments[5]:0;if(-1===e.indexOf('auto'))return e;var p=y(o,i,r,n),s={top:{width:p.width,height:t.top-p.top},right:{width:p.right-t.right,height:p.height},bottom:{width:p.width,height:p.bottom-t.bottom},left:{width:t.left-p.left,height:p.height}},d=Object.keys(s).map(function(e){return se({key:e},s[e],{area:E(s[e])})}).sort(function(e,t){return t.area-e.area}),a=d.filter(function(e){var t=e.width,i=e.height;return t>=o.clientWidth&&i>=o.clientHeight}),l=0<a.length?a[0].key:d[0].key,f=e.split('-')[1];return l+(f?'-'+f:'')}function O(e,t,o){var i=d(t,o);return u(o,i)}function L(e){var t=getComputedStyle(e),o=parseFloat(t.marginTop)+parseFloat(t.marginBottom),i=parseFloat(t.marginLeft)+parseFloat(t.marginRight),n={width:e.offsetWidth+i,height:e.offsetHeight+o};return n}function x(e){var t={left:'right',right:'left',bottom:'top',top:'bottom'};return e.replace(/left|right|bottom|top/g,function(e){return t[e]})}function S(e,t,o){o=o.split('-')[0];var i=L(e),n={width:i.width,height:i.height},r=-1!==['right','left'].indexOf(o),p=r?'top':'left',s=r?'left':'top',d=r?'height':'width',a=r?'width':'height';return n[p]=t[p]+t[d]/2-i[d]/2,n[s]=o===s?t[s]-i[a]:t[x(s)],n}function T(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}function D(e,t,o){if(Array.prototype.findIndex)return e.findIndex(function(e){return e[t]===o});var i=T(e,function(e){return e[t]===o});return e.indexOf(i)}function C(t,o,i){var n=void 0===i?t:t.slice(0,D(t,'name',i));return n.forEach(function(t){t['function']&&console.warn('`modifier.function` is deprecated, use `modifier.fn`!');var i=t['function']||t.fn;t.enabled&&e(i)&&(o.offsets.popper=c(o.offsets.popper),o.offsets.reference=c(o.offsets.reference),o=i(o,t))}),o}function N(){if(!this.state.isDestroyed){var e={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};e.offsets.reference=O(this.state,this.popper,this.reference),e.placement=v(this.options.placement,e.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),e.originalPlacement=e.placement,e.offsets.popper=S(this.popper,e.offsets.reference,e.placement),e.offsets.popper.position='absolute',e=C(this.modifiers,e),this.state.isCreated?this.options.onUpdate(e):(this.state.isCreated=!0,this.options.onCreate(e))}}function k(e,t){return e.some(function(e){var o=e.name,i=e.enabled;return i&&o===t})}function W(e){for(var t=[!1,'ms','Webkit','Moz','O'],o=e.charAt(0).toUpperCase()+e.slice(1),n=0;n<t.length-1;n++){var i=t[n],r=i?''+i+o:e;if('undefined'!=typeof document.body.style[r])return r}return null}function P(){return this.state.isDestroyed=!0,k(this.modifiers,'applyStyle')&&(this.popper.removeAttribute('x-placement'),this.popper.style.left='',this.popper.style.position='',this.popper.style.top='',this.popper.style[W('transform')]=''),this.disableEventListeners(),this.options.removeOnDestroy&&this.popper.parentNode.removeChild(this.popper),this}function B(e){var t=e.ownerDocument;return t?t.defaultView:window}function H(e,t,o,i){var r='BODY'===e.nodeName,p=r?e.ownerDocument.defaultView:e;p.addEventListener(t,o,{passive:!0}),r||H(n(p.parentNode),t,o,i),i.push(p)}function A(e,t,o,i){o.updateBound=i,B(e).addEventListener('resize',o.updateBound,{passive:!0});var r=n(e);return H(r,'scroll',o.updateBound,o.scrollParents),o.scrollElement=r,o.eventsEnabled=!0,o}function I(){this.state.eventsEnabled||(this.state=A(this.reference,this.options,this.state,this.scheduleUpdate))}function M(e,t){return B(e).removeEventListener('resize',t.updateBound),t.scrollParents.forEach(function(e){e.removeEventListener('scroll',t.updateBound)}),t.updateBound=null,t.scrollParents=[],t.scrollElement=null,t.eventsEnabled=!1,t}function R(){this.state.eventsEnabled&&(cancelAnimationFrame(this.scheduleUpdate),this.state=M(this.reference,this.state))}function U(e){return''!==e&&!isNaN(parseFloat(e))&&isFinite(e)}function Y(e,t){Object.keys(t).forEach(function(o){var i='';-1!==['width','height','top','right','bottom','left'].indexOf(o)&&U(t[o])&&(i='px'),e.style[o]=t[o]+i})}function j(e,t){Object.keys(t).forEach(function(o){var i=t[o];!1===i?e.removeAttribute(o):e.setAttribute(o,t[o])})}function F(e,t,o){var i=T(e,function(e){var o=e.name;return o===t}),n=!!i&&e.some(function(e){return e.name===o&&e.enabled&&e.order<i.order});if(!n){var r='`'+t+'`';console.warn('`'+o+'`'+' modifier is required by '+r+' modifier in order to work, be sure to include it before '+r+'!')}return n}function K(e){return'end'===e?'start':'start'===e?'end':e}function q(e){var t=1<arguments.length&&void 0!==arguments[1]&&arguments[1],o=ae.indexOf(e),i=ae.slice(o+1).concat(ae.slice(0,o));return t?i.reverse():i}function V(e,t,o,i){var n=e.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+n[1],p=n[2];if(!r)return e;if(0===p.indexOf('%')){var s;switch(p){case'%p':s=o;break;case'%':case'%r':default:s=i;}var d=c(s);return d[t]/100*r}if('vh'===p||'vw'===p){var a;return a='vh'===p?J(document.documentElement.clientHeight,window.innerHeight||0):J(document.documentElement.clientWidth,window.innerWidth||0),a/100*r}return r}function z(e,t,o,i){var n=[0,0],r=-1!==['right','left'].indexOf(i),p=e.split(/(\+|\-)/).map(function(e){return e.trim()}),s=p.indexOf(T(p,function(e){return-1!==e.search(/,|\s/)}));p[s]&&-1===p[s].indexOf(',')&&console.warn('Offsets separated by white space(s) are deprecated, use a comma (,) instead.');var d=/\s*,\s*|\s+/,a=-1===s?[p]:[p.slice(0,s).concat([p[s].split(d)[0]]),[p[s].split(d)[1]].concat(p.slice(s+1))];return a=a.map(function(e,i){var n=(1===i?!r:r)?'height':'width',p=!1;return e.reduce(function(e,t){return''===e[e.length-1]&&-1!==['+','-'].indexOf(t)?(e[e.length-1]=t,p=!0,e):p?(e[e.length-1]+=t,p=!1,e):e.concat(t)},[]).map(function(e){return V(e,n,t,o)})}),a.forEach(function(e,t){e.forEach(function(o,i){U(o)&&(n[t]+=o*('-'===e[i-1]?-1:1))})}),n}function G(e,t){var o,i=t.offset,n=e.placement,r=e.offsets,p=r.popper,s=r.reference,d=n.split('-')[0];return o=U(+i)?[+i,0]:z(i,p,s,d),'left'===d?(p.top+=o[0],p.left-=o[1]):'right'===d?(p.top+=o[0],p.left+=o[1]):'top'===d?(p.left+=o[0],p.top-=o[1]):'bottom'===d&&(p.left+=o[0],p.top+=o[1]),e.popper=p,e}for(var _=Math.min,X=Math.floor,J=Math.max,Q='undefined'!=typeof window&&'undefined'!=typeof document,Z=['Edge','Trident','Firefox'],$=0,ee=0;ee<Z.length;ee+=1)if(Q&&0<=navigator.userAgent.indexOf(Z[ee])){$=1;break}var i,te=Q&&window.Promise,oe=te?function(e){var t=!1;return function(){t||(t=!0,window.Promise.resolve().then(function(){t=!1,e()}))}}:function(e){var t=!1;return function(){t||(t=!0,setTimeout(function(){t=!1,e()},$))}},ie=function(){return void 0==i&&(i=-1!==navigator.appVersion.indexOf('MSIE 10')),i},ne=function(e,t){if(!(e instanceof t))throw new TypeError('Cannot call a class as a function')},re=function(){function e(e,t){for(var o,n=0;n<t.length;n++)o=t[n],o.enumerable=o.enumerable||!1,o.configurable=!0,'value'in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}return function(t,o,i){return o&&e(t.prototype,o),i&&e(t,i),t}}(),pe=function(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e},se=Object.assign||function(e){for(var t,o=1;o<arguments.length;o++)for(var i in t=arguments[o],t)Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e},de=['auto-start','auto','auto-end','top-start','top','top-end','right-start','right','right-end','bottom-end','bottom','bottom-start','left-end','left','left-start'],ae=de.slice(3),le={FLIP:'flip',CLOCKWISE:'clockwise',COUNTERCLOCKWISE:'counterclockwise'},fe=function(){function t(o,i){var n=this,r=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{};ne(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(n.update)},this.update=oe(this.update.bind(this)),this.options=se({},t.Defaults,r),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=o&&o.jquery?o[0]:o,this.popper=i&&i.jquery?i[0]:i,this.options.modifiers={},Object.keys(se({},t.Defaults.modifiers,r.modifiers)).forEach(function(e){n.options.modifiers[e]=se({},t.Defaults.modifiers[e]||{},r.modifiers?r.modifiers[e]:{})}),this.modifiers=Object.keys(this.options.modifiers).map(function(e){return se({name:e},n.options.modifiers[e])}).sort(function(e,t){return e.order-t.order}),this.modifiers.forEach(function(t){t.enabled&&e(t.onLoad)&&t.onLoad(n.reference,n.popper,n.options,t,n.state)}),this.update();var p=this.options.eventsEnabled;p&&this.enableEventListeners(),this.state.eventsEnabled=p}return re(t,[{key:'update',value:function(){return N.call(this)}},{key:'destroy',value:function(){return P.call(this)}},{key:'enableEventListeners',value:function(){return I.call(this)}},{key:'disableEventListeners',value:function(){return R.call(this)}}]),t}();return fe.Utils=('undefined'==typeof window?global:window).PopperUtils,fe.placements=de,fe.Defaults={placement:'bottom',eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(e){var t=e.placement,o=t.split('-')[0],i=t.split('-')[1];if(i){var n=e.offsets,r=n.reference,p=n.popper,s=-1!==['bottom','top'].indexOf(o),d=s?'left':'top',a=s?'width':'height',l={start:pe({},d,r[d]),end:pe({},d,r[d]+r[a]-p[a])};e.offsets.popper=se({},p,l[i])}return e}},offset:{order:200,enabled:!0,fn:G,offset:0},preventOverflow:{order:300,enabled:!0,fn:function(e,t){var o=t.boundariesElement||r(e.instance.popper);e.instance.reference===o&&(o=r(o));var i=y(e.instance.popper,e.instance.reference,t.padding,o);t.boundaries=i;var n=t.priority,p=e.offsets.popper,s={primary:function(e){var o=p[e];return p[e]<i[e]&&!t.escapeWithReference&&(o=J(p[e],i[e])),pe({},e,o)},secondary:function(e){var o='right'===e?'left':'top',n=p[o];return p[e]>i[e]&&!t.escapeWithReference&&(n=_(p[o],i[e]-('right'===e?p.width:p.height))),pe({},o,n)}};return n.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';p=se({},p,s[t](e))}),e.offsets.popper=p,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,i=t.reference,n=e.placement.split('-')[0],r=X,p=-1!==['top','bottom'].indexOf(n),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]<r(i[d])&&(e.offsets.popper[d]=r(i[d])-o[a]),o[d]>r(i[s])&&(e.offsets.popper[d]=r(i[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var i;if(!F(e.instance.modifiers,'arrow','keepTogether'))return e;var n=o.element;if('string'==typeof n){if(n=e.instance.popper.querySelector(n),!n)return e;}else if(!e.instance.popper.contains(n))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',g=a?'bottom':'right',u=L(n)[l];d[g]-u<s[m]&&(e.offsets.popper[m]-=s[m]-(d[g]-u)),d[m]+u>s[g]&&(e.offsets.popper[m]+=d[m]+u-s[g]),e.offsets.popper=c(e.offsets.popper);var b=d[m]+d[l]/2-u/2,w=t(e.instance.popper),y=parseFloat(w['margin'+f],10),E=parseFloat(w['border'+f+'Width'],10),v=b-e.offsets.popper[m]-y-E;return v=J(_(s[l]-u,v),0),e.arrowElement=n,e.offsets.arrow=(i={},pe(i,m,Math.round(v)),pe(i,h,''),i),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(k(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=y(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement),i=e.placement.split('-')[0],n=x(i),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case le.FLIP:p=[i,n];break;case le.CLOCKWISE:p=q(i);break;case le.COUNTERCLOCKWISE:p=q(i,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(i!==s||p.length===d+1)return e;i=e.placement.split('-')[0],n=x(i);var a=e.offsets.popper,l=e.offsets.reference,f=X,m='left'===i&&f(a.right)>f(l.left)||'right'===i&&f(a.left)<f(l.right)||'top'===i&&f(a.bottom)>f(l.top)||'bottom'===i&&f(a.top)<f(l.bottom),h=f(a.left)<f(o.left),c=f(a.right)>f(o.right),g=f(a.top)<f(o.top),u=f(a.bottom)>f(o.bottom),b='left'===i&&h||'right'===i&&c||'top'===i&&g||'bottom'===i&&u,w=-1!==['top','bottom'].indexOf(i),y=!!t.flipVariations&&(w&&'start'===r&&h||w&&'end'===r&&c||!w&&'start'===r&&g||!w&&'end'===r&&u);(m||b||y)&&(e.flipped=!0,(m||b)&&(i=p[d+1]),y&&(r=K(r)),e.placement=i+(r?'-'+r:''),e.offsets.popper=se({},e.offsets.popper,S(e.instance.popper,e.offsets.reference,e.placement)),e=C(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],i=e.offsets,n=i.popper,r=i.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return n[p?'left':'top']=r[o]-(s?n[p?'width':'height']:0),e.placement=x(t),e.offsets.popper=c(n),e}},hide:{order:800,enabled:!0,fn:function(e){if(!F(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=T(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottom<o.top||t.left>o.right||t.top>o.bottom||t.right<o.left){if(!0===e.hide)return e;e.hide=!0,e.attributes['x-out-of-boundaries']=''}else{if(!1===e.hide)return e;e.hide=!1,e.attributes['x-out-of-boundaries']=!1}return e}},computeStyle:{order:850,enabled:!0,fn:function(e,t){var o=t.x,i=t.y,n=e.offsets.popper,p=T(e.instance.modifiers,function(e){return'applyStyle'===e.name}).gpuAcceleration;void 0!==p&&console.warn('WARNING: `gpuAcceleration` option moved to `computeStyle` modifier and will not be supported in future versions of Popper.js!');var s,d,a=void 0===p?t.gpuAcceleration:p,l=r(e.instance.popper),f=g(l),m={position:n.position},h={left:X(n.left),top:X(n.top),bottom:X(n.bottom),right:X(n.right)},c='bottom'===o?'top':'bottom',u='right'===i?'left':'right',b=W('transform');if(d='bottom'==c?-f.height+h.bottom:h.top,s='right'==u?-f.width+h.right:h.left,a&&b)m[b]='translate3d('+s+'px, '+d+'px, 0)',m[c]=0,m[u]=0,m.willChange='transform';else{var w='bottom'==c?-1:1,y='right'==u?-1:1;m[c]=d*w,m[u]=s*y,m.willChange=c+', '+u}var E={"x-placement":e.placement};return e.attributes=se({},E,e.attributes),e.styles=se({},m,e.styles),e.arrowStyles=se({},e.offsets.arrow,e.arrowStyles),e},gpuAcceleration:!0,x:'bottom',y:'right'},applyStyle:{order:900,enabled:!0,fn:function(e){return Y(e.instance.popper,e.styles),j(e.instance.popper,e.attributes),e.arrowElement&&Object.keys(e.arrowStyles).length&&Y(e.arrowElement,e.arrowStyles),e},onLoad:function(e,t,o,i,n){var r=O(n,t,e),p=v(o.placement,r,t,e,o.modifiers.flip.boundariesElement,o.modifiers.flip.padding);return t.setAttribute('x-placement',p),Y(t,{position:'absolute'}),o},gpuAcceleration:void 0}}},fe});
5
- //# sourceMappingURL=popper.min.js.map
 
 
 
 
 
admin/partials/class-conversios-header.php CHANGED
@@ -46,12 +46,12 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
46
  <div class="row">
47
  <div class="promoleft">
48
  <div class="promobandmsg">
49
- <?php _e("Level up your game by getting detail insights on every products. Make the informed decision for your next campaign.","conversios"); ?>
50
  </div>
51
  </div>
52
  <div class="promoright">
53
  <div class="prmoupgrdbtn">
54
- <a target="_blank" href="<?php echo esc_url($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"><?php _e("Upgrade","conversios"); ?></a>
55
  </div>
56
  </div>
57
  </div>
@@ -60,7 +60,7 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
60
  <!--- Promotion box end -->
61
  <?php
62
  }
63
- echo $this->call_tvc_site_verified_and_domain_claim();
64
  }
65
  /**
66
  * header section
@@ -68,40 +68,9 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
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\">".esc_attr($value["date"])."</span>":"";
84
- $m_title = isset($value["title"])?"<h4 class=\"tvc-msg_title\">".esc_attr($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=".esc_url($value["link"])." class=\"tvc-notification-button is-secondary\">".esc_attr($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","conversios")."</button>
97
- </div>
98
- ".$m_date."
99
- </div>
100
- </div>
101
- </section>
102
- </li>";
103
- }
104
- }
105
  }
106
  ?>
107
  <!-- header start -->
@@ -110,25 +79,19 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
110
  <div class="row align-items-center">
111
  <div class="hdrtpleft">
112
  <div class="brandlogo">
113
- <a target="_blank" href="<?php echo esc_url($this->conversios_site_url); ?>"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/logo.png'); ?>" alt="" /></a>
114
  </div>
115
  <div class="hdrcntcbx">
116
- <?php _e("For any query, contact us at <span><a href=\"tel:+1 (415) 968-6313\">+1 (415) 968-6313</a></span>","conversios"); ?>
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 esc_attr($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
- <?php _e("For any query, contact us at <span><a href=\"tel:+1 (415) 968-6313\">+1 (415) 968-6313</a></span>","conversios"); ?>
132
  </div>
133
  </div>
134
  </div>
@@ -139,7 +102,7 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
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 ;
@@ -149,15 +112,15 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
149
  $conversios_menu_list = array(
150
  'conversios' => array(
151
  'title'=>'Dashboard',
152
- 'icon'=>esc_url(ENHANCAD_PLUGIN_URL."/admin/images/conversios-menu.png"),
153
- 'acitve_icon'=>esc_url(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
  }
@@ -179,23 +142,23 @@ if ( ! class_exists( 'Conversios_Header' ) ) {
179
  $is_active = $this->is_active_menu($key);
180
  $icon = "";
181
  if(!isset($value['icon']) && !isset($value['acitve_icon'])){
182
- $icon = esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/'.$key.'-menu.png');
183
  if($is_active == 'active'){
184
- $icon = esc_url(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
46
  <div class="row">
47
  <div class="promoleft">
48
  <div class="promobandmsg">
49
+ <?php esc_html_e("Level up your game by getting detail insights on every products. Make the informed decision for your next campaign.","conversios"); ?>
50
  </div>
51
  </div>
52
  <div class="promoright">
53
  <div class="prmoupgrdbtn">
54
+ <a target="_blank" href="<?php echo esc_url_raw($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"><?php esc_html_e("Upgrade","conversios"); ?></a>
55
  </div>
56
  </div>
57
  </div>
60
  <!--- Promotion box end -->
61
  <?php
62
  }
63
+ echo esc_attr($this->call_tvc_site_verified_and_domain_claim());
64
  }
65
  /**
66
  * header section
68
  * @since 4.1.4
69
  */
70
  public function conversios_header(){
71
+ $plan_name = esc_html__("Free Plan","conversios");
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
  ?>
76
  <!-- header start -->
79
  <div class="row align-items-center">
80
  <div class="hdrtpleft">
81
  <div class="brandlogo">
82
+ <a target="_blank" href="<?php echo esc_url_raw($this->conversios_site_url); ?>"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/logo.png'); ?>" alt="" /></a>
83
  </div>
84
  <div class="hdrcntcbx">
85
+ <?php printf("%s <span><a href=\"tel:+1 (415) 968-6313\">+1 (415) 968-6313</a></span>",esc_html_e("For any query, contact us at","conversios")); ?>
86
  </div>
87
  </div>
88
  <div class="hdrtpright">
89
  <div class="hustleplanbtn">
90
+ <a href="<?php echo esc_url_raw($this->site_url.'conversios-account'); ?>"><button class="cvrs-btn greenbtn"><?php echo esc_attr($plan_name); ?></button></a>
 
 
 
 
 
91
  </div>
 
92
  </div>
93
  <div class="hdrcntcbx mblhdrcntcbx">
94
+ <?php printf("%s <span><a href=\"tel:+1 (415) 968-6313\">+1 (415) 968-6313</a></span>",esc_html_e("For any query, contact us at","conversios")); ?>
95
  </div>
96
  </div>
97
  </div>
102
 
103
  /* add active tab class */
104
  protected function is_active_menu($page=""){
105
+ if($page!="" && isset($_GET['page']) && sanitize_text_field($_GET['page']) == $page){
106
  return "active";
107
  }
108
  return ;
112
  $conversios_menu_list = array(
113
  'conversios' => array(
114
  'title'=>'Dashboard',
115
+ 'icon'=>esc_url_raw(ENHANCAD_PLUGIN_URL."/admin/images/conversios-menu.png"),
116
+ 'acitve_icon'=>esc_url_raw(ENHANCAD_PLUGIN_URL."/admin/images/active-conversios-menu.png")
117
  ),
118
+ 'conversios-google-analytics'=>array('title'=>esc_html__('Google Analytics','conversios')),
119
+ 'conversios-google-ads'=>array('title'=>esc_html__('Google Ads','conversios')),
120
+ 'conversios-google-shopping-feed'=>array('title'=>esc_html__('Google Shopping','conversios'))
121
  );
122
  if($this->plan_id == 1){
123
+ $conversios_menu_list['conversios-pricings'] = array('title'=>esc_html__('Free Vs Pro','conversios'),'icon'=>'');
124
  }
125
  return apply_filters('conversios_menu_list', $conversios_menu_list, $conversios_menu_list);
126
  }
142
  $is_active = $this->is_active_menu($key);
143
  $icon = "";
144
  if(!isset($value['icon']) && !isset($value['acitve_icon'])){
145
+ $icon = esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/'.esc_attr($key).'-menu.png');
146
  if($is_active == 'active'){
147
+ $icon = esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/'.esc_attr($is_active).'-'.esc_attr($key).'-menu.png');
148
  }
149
  }else{
150
+ $icon = (isset($value['icon']))?$value['icon']:((isset($value['acitve_icon']))?esc_attr($value['acitve_icon']):"");
151
  if($is_active == 'active' && isset($value['acitve_icon'])){
152
  $icon =$value['acitve_icon'];
153
  }
154
  }
155
  ?>
156
+ <li class="<?php echo esc_attr($is_active); ?>">
157
+ <a href="<?php echo esc_url_raw($this->site_url.$key); ?>">
158
  <?php if($icon != ""){?>
159
+ <span class="navinfoicon"><img src="<?php echo esc_url_raw($icon); ?>" /></span>
160
  <?php } ?>
161
+ <span class="navinfonavtext"><?php echo esc_attr($value['title']); ?></span>
162
  </a>
163
  </li>
164
  <?php
admin/partials/general-fields.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- echo "<script>var return_url ='".esc_url($this->url)."';</script>";
3
  $TVC_Admin_Helper = new TVC_Admin_Helper();
4
  $class = "";
5
  $message_p = "";
@@ -7,8 +7,8 @@ if (isset($_GET['connect']) && isset($_GET['subscription_id'])) {
7
  /*
8
  * save subscription_id in "ee_options" and then API call for get subscription details
9
  */
10
- if (isset($_GET['subscription_id']) && $_GET['subscription_id']) {
11
- $_POST['subscription_id'] = $_GET['subscription_id'];
12
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
13
  }
14
  $customApiObj = new CustomApi();
@@ -19,7 +19,7 @@ if (isset($_GET['connect']) && isset($_GET['subscription_id'])) {
19
  $ee_additional_data = $TVC_Admin_Helper->get_ee_additional_data();
20
  if(isset($ee_additional_data['temp_active_licence_key']) && $ee_additional_data['temp_active_licence_key'] != ""){
21
  $licence_key = $ee_additional_data['temp_active_licence_key'];
22
- $TVC_Admin_Helper->active_licence($licence_key, $_GET['subscription_id']);
23
  unset($ee_additional_data['temp_active_licence_key']);
24
  $TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
25
  }
@@ -35,10 +35,10 @@ if (isset($_GET['connect']) && isset($_GET['subscription_id'])) {
35
  }
36
  //'website_url' => $googleDetail->site_url,
37
  $postData = [
38
- 'merchant_id' => $googleDetail->merchant_id,
39
  'website_url' => get_site_url(),
40
- 'subscription_id' => $googleDetail->id,
41
- 'account_id' => $googleDetail->google_merchant_center_id
42
  ];
43
 
44
  if ($googleDetail->is_site_verified == '0') {
@@ -83,27 +83,27 @@ if (isset($_GET['connect']) && isset($_GET['subscription_id'])) {
83
  $googleDetail->is_domain_claim = '1';
84
  }
85
  }
86
- $_POST['subscription_id'] = esc_attr($googleDetail->id);
87
  $_POST['ga_eeT'] = (isset($googleDetail->enhanced_e_commerce_tracking) && $googleDetail->enhanced_e_commerce_tracking == "1") ? "on" : "";
88
 
89
  $_POST['ga_ST'] = (isset($googleDetail->add_gtag_snippet) && $googleDetail->add_gtag_snippet == "1") ? "on" : "";
90
- $_POST['gm_id'] = esc_attr($googleDetail->measurement_id);
91
- $_POST['ga_id'] = esc_attr($googleDetail->property_id);
92
- $_POST['google_ads_id'] = esc_attr($googleDetail->google_ads_id);
93
- $_POST['google_merchant_id'] = esc_attr($googleDetail->google_merchant_center_id);
94
- $_POST['tracking_option'] = esc_attr($googleDetail->tracking_option);
95
- $_POST['ga_gUser'] = 'on';
96
  //$_POST['ga_gCkout'] = 'on';
97
- $_POST['ga_Impr'] = 6;
98
- $_POST['ga_IPA'] = 'on';
99
- $_POST['ga_OPTOUT'] = 'on';
100
- $_POST['ga_PrivacyPolicy'] = 'on';
101
  $_POST['google-analytic'] = '';
102
  //update option in wordpress local database
103
- update_option('google_ads_conversion_tracking', $googleDetail->google_ads_conversion_tracking);
104
- update_option('ads_tracking_id', esc_attr($googleDetail->google_ads_id));
105
- update_option('ads_ert', $googleDetail->remarketing_tags);
106
- update_option('ads_edrt', $googleDetail->dynamic_remarketing_tags);
107
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
108
  /*
109
  * function call for save API data in WP DB
@@ -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(esc_url("admin.php?page=conversios-google-shopping-feed&tab=sync_product_page&welcome_msg=true"));
119
  exit;
120
  }else{
121
- wp_redirect(esc_url("admin.php?page=conversios-google-shopping-feed&tab=gaa_config_page&welcome_msg=true"));
122
  exit;
123
  }
124
  }
@@ -134,50 +134,20 @@ if (isset($_GET['connect']) && isset($_GET['subscription_id'])) {
134
  }
135
  $message = new Enhanced_Ecommerce_Google_Settings();
136
  if (isset($_POST['ee_submit_plugin'])) {
137
- if(!empty($_POST['ga_id'])){
138
- $_POST['tracking_option'] = "UA";
139
  }
140
- if(!empty($_POST['gm_id'])){
141
- $_POST['tracking_option'] = "GA4";
142
  }
143
- if(!empty($_POST['gm_id']) && !empty($_POST['ga_id'])){
144
- $_POST['tracking_option'] = "BOTH";
145
  }
146
- update_option('ads_tracking_id', $_POST['google_ads_id']);
147
 
148
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
149
  $class = 'alert-message tvc-alert-success';
150
- $message_p = __( 'Your settings have been saved.', 'conversios' );
151
- /* API Save */
152
- /*if(isset($_POST['ga_eeT'])){
153
- $_POST['enhanced_e_commerce_tracking']=($_POST['ga_eeT']=="on")?1:0;
154
- unset($_POST['ga_eeT']);
155
- }
156
- if(isset($_POST['ga_ST'])){
157
- $_POST['add_gtag_snippet']=($_POST['ga_ST']=="on")?1:0;
158
- unset($_POST['ga_ST']);
159
- }
160
- if(isset($_POST['subscription_id']) && $_POST['subscription_id'] >0) {
161
- $customApiObj = new CustomApi();
162
- $response = $customApiObj->updateTrackingOption($_POST);
163
- if (isset($response->errors) && !empty($response->errors)) {
164
- $error_code = array_keys($response->errors)[0];
165
- $class = 'notice notice-error';
166
- $r_message = esc_html__('The tracking options is not added successfully.');
167
- printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), esc_html('Error : ' . $r_message));
168
- } else {
169
- $response = $response->data;
170
- if (isset($response['status']) && $response['status'] == 200) {
171
- $class = 'notice notice-success';
172
- $r_message = esc_html__('The tracking options added successfully.');
173
- //printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), esc_html($r_message));
174
- }
175
- }
176
- }else{
177
- $class = 'notice notice-error';
178
- $r_message = esc_html__('Connect Google account to enable more features.');
179
- printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), esc_html('Error : ' . $r_message));
180
- }*/
181
  }
182
  $data = unserialize(get_option('ee_options'));
183
  $subscription_id = $TVC_Admin_Helper->get_subscriptionId();
@@ -203,13 +173,13 @@ if(isset($google_detail['setting'])){
203
  <?php if($plan_id == 1){?>
204
  <div class="licence tvc-licence" >
205
  <div class="tvc_licence_key_wapper <?php if($plan_id != 1){?>tvc-hide<?php }?>">
206
- <p><?php _e("You are using our free plugin, no licence needed ! Happy analyzing..!! :)","conversios"); ?></p>
207
- <p class="font-weight-bold"><?php _e("To unlock more features of google products, consider our","conversios"); ?> <a href="<?php echo $TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=Google+Analytics+Screen+pro+version&utm_campaign=Upsell+at+Conversios'; ?>" target="_blank"><?php _e("pro version.","conversios"); ?></a></p>
208
  <form method="post" name="google-analytic" id="tvc-licence-active">
209
  <div class="input-group">
210
- <input type="text" id="licence_key" name="licence_key" class="form-control" placeholder="<?php _e("Already purchased? Enter licence key","conversios"); ?>" required="">
211
  <div class="input-group-append">
212
- <button type="submit" class="btn btn-primary" name="verify-licence-key"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/right-arrow.svg'); ?>" alt="active licence key"></button>
213
  </div>
214
  </div>
215
  </form>
@@ -220,72 +190,72 @@ if(isset($google_detail['setting'])){
220
  <div class="google-account-analytics">
221
  <div class="row mb-3">
222
  <div class="col-6 col-md-6 col-lg-6">
223
- <h2 class="ga-title"><?php _e("Connected Google Analytics Account:","conversios"); ?></h2>
224
  </div>
225
  <div class="col-6 col-md-6 col-lg-6 text-right">
226
  <div class="acc-num">
227
  <p class="ga-text">
228
- <?php echo (isset($data['ga_id']) && $data['ga_id'] != '') ? $data['ga_id'] : '<span>'.__("Get started","conversios").'</span>'; ?>
229
  </p>
230
  <?php
231
  if (isset($data['ga_id']) && $data['ga_id'] != '') {
232
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'.esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
233
  } else {
234
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
235
  }?>
236
  </div>
237
  </div>
238
  </div>
239
  <div class="row mb-3">
240
  <div class="col-6 col-md-6 col-lg-6">
241
- <h2 class="ga-title"><?php _e("Connected Google Analytics 4 Account:","conversios"); ?></h2>
242
  </div>
243
  <div class="col-6 col-md-6 col-lg-6 text-right">
244
  <div class="acc-num">
245
  <p class="ga-text">
246
- <?php echo (isset($data['gm_id']) && $data['gm_id'] != '') ? $data['gm_id'] : '<span>'.__("Get started","conversios").'</span>'; ?>
247
  </p>
248
  <?php
249
  if (isset($data['gm_id']) && esc_attr($data['gm_id']) != '') {
250
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
251
  } else {
252
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
253
  }?>
254
  </div>
255
  </div>
256
  </div>
257
  <div class="row mb-3">
258
  <div class="col-6 col-md-6 col-lg-6">
259
- <h2 class="ga-title"><?php _e("Linked Google Ads Account:","conversios"); ?></h2>
260
  </div>
261
  <div class="col-6 col-md-6 col-lg-6 text-right">
262
  <div class="acc-num">
263
  <p class="ga-text">
264
- <?php echo (isset($data['google_ads_id']) && $data['google_ads_id'] != '') ? $data['google_ads_id'] : '<span>'.__("Get started","conversios").'</span>'; ?>
265
  </p>
266
  <?php
267
  if (isset($data['google_ads_id']) && esc_attr($data['google_ads_id']) != '') {
268
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
269
  } else {
270
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
271
  }?>
272
  </div>
273
  </div>
274
  </div>
275
  <div class="row mb-3">
276
  <div class="col-6 col-md-6 col-lg-6">
277
- <h2 class="ga-title"><?php _e("Linked Google Merchant Center Account:","conversios"); ?></h2>
278
  </div>
279
  <div class="col-6 col-md-6 col-lg-6 text-right">
280
  <div class="acc-num">
281
  <p class="ga-text">
282
- <?php echo (isset($data['google_merchant_id']) && $data['google_merchant_id'] != '') ? $data['google_merchant_id'] : '<span>'.__("Get started","conversios").'</span>'; ?>
283
  </p>
284
  <?php
285
  if (isset($data['google_merchant_id']) && esc_attr($data['google_merchant_id']) != '') {
286
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
287
  } else {
288
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
289
  }?>
290
  </div>
291
  </div>
@@ -297,83 +267,83 @@ if(isset($google_detail['setting'])){
297
  <tbody>
298
  <tr>
299
  <th>
300
- <label class="align-middle" for="tracking_code"><?php _e("Tracking Code","conversios"); ?></label>
301
  </th>
302
  <td>
303
  <label class = "align-middle">
304
  <?php $ga_ST = !empty($data['ga_ST']) ? 'checked' : ''; ?>
305
  <input type="checkbox" name="ga_ST" id="ga_ST" <?php echo esc_attr($ga_ST); ?> >
306
- <label class="custom-control-label" for="ga_ST"><?php _e("Add Global Site Tracking Code 'gtag.js'","conversios"); ?></label>
307
 
308
- <i style="cursor: help;" class="fas fa-question-circle" title="This feature adds new gtag.js tracking code to your store. You don't need to enable this if gtag.js is implemented via any third party analytics plugin."></i>
309
  </label><br/>
310
  <label class = "align-middle">
311
  <?php $ga_eeT = !empty($data['ga_eeT']) ? 'checked' : ''; ?>
312
  <input type="checkbox" name="ga_eeT" id="ga_eeT" <?php echo esc_attr($ga_eeT); ?> >
313
- <label class="custom-control-label" for="ga_eeT"><?php _e("Add Enhanced Ecommerce Tracking Code","conversios"); ?></label>
314
 
315
- <i style="cursor: help;" class="fas fa-question-circle" title="This feature adds Enhanced Ecommerce Tracking Code to your Store"></i>
316
  </label><br/>
317
  <label class = "align-middle">
318
  <?php $ga_gUser = !empty($data['ga_gUser']) ? 'checked' : ''; ?>
319
  <input type="checkbox" name="ga_gUser" id="ga_gUser" <?php echo esc_attr($ga_gUser); ?> >
320
- <label class="custom-control-label" for="ga_gUser"><?php _e("Add Code to Track the Login Step of Guest Users (Optional)","conversios"); ?></label>
321
 
322
- <i style="cursor: help;" class="fas fa-question-circle" title="If you have Guest Check out enable, we recommend you to add this code"></i>
323
  </label>
324
  </td>
325
  </tr>
326
  <tr>
327
  <th>
328
- <label for="ga_Impr"><?php _e("Impression Thresold","conversios"); ?></label>
329
  </th>
330
  <td>
331
  <?php $ga_Impr = !empty($data['ga_Impr']) ? $data['ga_Impr'] : 6; ?>
332
  <input type="number" min="1" id="ga_Impr" name = "ga_Impr" value = "<?php echo esc_attr($ga_Impr); ?>">
333
  <label for="ga_Impr"></label>
334
- <i style="cursor: help;" class="fas fa-question-circle" title="This feature sets Impression threshold for category page. It sends hit after these many numbers of products impressions."></i>
335
- <p class="description"><b><?php _e("Note : To avoid processing load on server we recommend upto 6 Impression Thresold.","conversios"); ?></b></p>
336
  </td>
337
  </tr>
338
  <tr>
339
  <th>
340
- <label class = "align-middle" for="ga_IPA"><?php _e("I.P. Anoymization","conversios"); ?></label>
341
  </th>
342
  <td>
343
  <label class = "align-middle">
344
  <?php $ga_IPA = !empty($data['ga_IPA']) ? 'checked' : ''; ?>
345
  <input class="" type="checkbox" name="ga_IPA" id="ga_IPA" <?php echo esc_attr($ga_IPA); ?>>
346
- <label class="custom-control-label" for="ga_IPA"><?php _e("Enable I.P. Anonymization","conversios"); ?></label>
347
 
348
- <i style="cursor: help;" class="fas fa-question-circle" title="Use this feature to anonymize (or stop collecting) the I.P Address of your users in Google Analytics. Be in legal compliance by using I.P Anonymization which is important for EU countries As per the GDPR compliance"></i>
349
  </label>
350
  </td>
351
  </tr>
352
  <tr>
353
  <th>
354
- <label class = "align-middle" for="ga_OPTOUT"><?php _e("Google Analytics Opt Out","conversios"); ?></label>
355
  </th>
356
  <td>
357
  <label class = "align-middle">
358
  <?php $ga_OPTOUT = !empty($data['ga_OPTOUT']) ? 'checked' : ''; ?>
359
  <input class="" type="checkbox" name="ga_OPTOUT" id="ga_OPTOUT" <?php echo esc_attr($ga_OPTOUT); ?>>
360
- <label class="custom-control-label" for="ga_OPTOUT"><?php _e("Enable Google Analytics Opt Out (Optional)","conversios"); ?></label>
361
 
362
- <i style="cursor: help;" class="fas fa-question-circle" title="Use this feature to provide website visitors the ability to prevent their data from being used by Google Analytics As per the GDPR compliance.Go through the documentation to check the setup"></i>
363
  </label>
364
  </td>
365
  </tr>
366
  <tr>
367
  <th>
368
- <label class = "align-middle" for="ga_PrivacyPolicy"><?php _e("Privacy Policy","conversios"); ?></label>
369
  </th>
370
  <td>
371
  <label class = "align-middle">
372
  <?php $ga_PrivacyPolicy = !empty($data['ga_PrivacyPolicy']) ? 'checked' : ''; ?>
373
  <input type="checkbox" name="ga_PrivacyPolicy" id="ga_PrivacyPolicy" required="required" <?php echo esc_attr($ga_PrivacyPolicy); ?>>
374
- <label class="custom-control-label" for="ga_PrivacyPolicy"><?php _e("Accept Privacy Policy of Plugin","conversios"); ?></label>
375
 
376
- <p class="description"><?php _e("By using Tatvic Plugin, you agree to Tatvic plugin's","conversios"); ?> <a href= "https://www.tatvic.com/privacy-policy/?ref=plugin_policy&utm_source=plugin_backend&utm_medium=woo_premium_plugin&utm_campaign=GDPR_complaince_ecomm_plugins" target="_blank"><?php _e("Privacy Policy","conversios"); ?></a></p>
377
  </label>
378
  </td>
379
  </tr>
@@ -385,7 +355,7 @@ if(isset($google_detail['setting'])){
385
  <input type="hidden" id="google_ads_id" name = "google_ads_id" value="<?= esc_attr((!empty($data['google_ads_id']))?$data['google_ads_id']:""); ?>"/>
386
  <input type="hidden" id="google_merchant_id" name = "google_merchant_id" value="<?= esc_attr((!empty($data['google_merchant_id']))?$data['google_merchant_id']:""); ?>"/>
387
  <input type="hidden" name="subscription_id" value="<?php echo esc_attr((!empty($data['subscription_id']))?$data['subscription_id']:""); ?>">
388
- <button type="submit" class="btn btn-primary" id="ee_submit_plugin" name="ee_submit_plugin"><?php _e("Save","conversios"); ?></button>
389
  </p>
390
  </form>
391
  </div>
1
  <?php
2
+ echo "<script>var return_url ='".esc_url_raw($this->url)."';</script>";
3
  $TVC_Admin_Helper = new TVC_Admin_Helper();
4
  $class = "";
5
  $message_p = "";
7
  /*
8
  * save subscription_id in "ee_options" and then API call for get subscription details
9
  */
10
+ if (isset($_GET['subscription_id']) && sanitize_text_field($_GET['subscription_id'])) {
11
+ $_POST['subscription_id'] = sanitize_text_field($_GET['subscription_id']);
12
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
13
  }
14
  $customApiObj = new CustomApi();
19
  $ee_additional_data = $TVC_Admin_Helper->get_ee_additional_data();
20
  if(isset($ee_additional_data['temp_active_licence_key']) && $ee_additional_data['temp_active_licence_key'] != ""){
21
  $licence_key = $ee_additional_data['temp_active_licence_key'];
22
+ $TVC_Admin_Helper->active_licence($licence_key, sanitize_text_field($_GET['subscription_id']));
23
  unset($ee_additional_data['temp_active_licence_key']);
24
  $TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
25
  }
35
  }
36
  //'website_url' => $googleDetail->site_url,
37
  $postData = [
38
+ 'merchant_id' => esc_attr($googleDetail->merchant_id),
39
  'website_url' => get_site_url(),
40
+ 'subscription_id' => esc_attr($googleDetail->id),
41
+ 'account_id' => esc_attr($googleDetail->google_merchant_center_id)
42
  ];
43
 
44
  if ($googleDetail->is_site_verified == '0') {
83
  $googleDetail->is_domain_claim = '1';
84
  }
85
  }
86
+ $_POST['subscription_id'] = esc_attr(sanitize_text_field($googleDetail->id));
87
  $_POST['ga_eeT'] = (isset($googleDetail->enhanced_e_commerce_tracking) && $googleDetail->enhanced_e_commerce_tracking == "1") ? "on" : "";
88
 
89
  $_POST['ga_ST'] = (isset($googleDetail->add_gtag_snippet) && $googleDetail->add_gtag_snippet == "1") ? "on" : "";
90
+ $_POST['gm_id'] = esc_attr(sanitize_text_field($googleDetail->measurement_id));
91
+ $_POST['ga_id'] = esc_attr(sanitize_text_field($googleDetail->property_id));
92
+ $_POST['google_ads_id'] = esc_attr(sanitize_text_field($googleDetail->google_ads_id));
93
+ $_POST['google_merchant_id'] = esc_attr(sanitize_text_field($googleDetail->google_merchant_center_id));
94
+ $_POST['tracking_option'] = esc_attr(sanitize_text_field($googleDetail->tracking_option));
95
+ $_POST['ga_gUser'] = esc_attr('on');
96
  //$_POST['ga_gCkout'] = 'on';
97
+ $_POST['ga_Impr'] = esc_attr(6);
98
+ $_POST['ga_IPA'] = esc_attr('on');
99
+ $_POST['ga_OPTOUT'] = esc_attr('on');
100
+ $_POST['ga_PrivacyPolicy'] = esc_attr('on');
101
  $_POST['google-analytic'] = '';
102
  //update option in wordpress local database
103
+ update_option('google_ads_conversion_tracking', sanitize_option('google_ads_conversion_tracking',$googleDetail->google_ads_conversion_tracking) );
104
+ update_option('ads_tracking_id', sanitize_option('ads_tracking_id', $googleDetail->google_ads_id));
105
+ update_option('ads_ert', sanitize_option( 'ads_ert', $googleDetail->remarketing_tags));
106
+ update_option('ads_edrt', sanitize_option('ads_edrt', $googleDetail->dynamic_remarketing_tags));
107
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
108
  /*
109
  * function call for save API data in WP DB
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(esc_url_raw("admin.php?page=conversios-google-shopping-feed&tab=sync_product_page&welcome_msg=true"));
119
  exit;
120
  }else{
121
+ wp_redirect(esc_url_raw("admin.php?page=conversios-google-shopping-feed&tab=gaa_config_page&welcome_msg=true"));
122
  exit;
123
  }
124
  }
134
  }
135
  $message = new Enhanced_Ecommerce_Google_Settings();
136
  if (isset($_POST['ee_submit_plugin'])) {
137
+ if(!empty(sanitize_text_field($_POST['ga_id']))){
138
+ $_POST['tracking_option'] = esc_attr("UA");
139
  }
140
+ if(!empty(sanitize_text_field($_POST['gm_id']))){
141
+ $_POST['tracking_option'] = esc_attr("GA4");
142
  }
143
+ if(!empty(sanitize_text_field($_POST['gm_id'])) && !empty(sanitize_text_field($_POST['ga_id']))){
144
+ $_POST['tracking_option'] = esc_attr("BOTH");
145
  }
146
+ update_option('ads_tracking_id', sanitize_option('ads_tracking_id', $_POST['google_ads_id']));
147
 
148
  Enhanced_Ecommerce_Google_Settings::add_update_settings('ee_options');
149
  $class = 'alert-message tvc-alert-success';
150
+ $message_p = esc_html__( 'Your settings have been saved.', 'conversios' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  }
152
  $data = unserialize(get_option('ee_options'));
153
  $subscription_id = $TVC_Admin_Helper->get_subscriptionId();
173
  <?php if($plan_id == 1){?>
174
  <div class="licence tvc-licence" >
175
  <div class="tvc_licence_key_wapper <?php if($plan_id != 1){?>tvc-hide<?php }?>">
176
+ <p><?php esc_html_e("You are using our free plugin, no licence needed ! Happy analyzing..!! :)","conversios"); ?></p>
177
+ <p class="font-weight-bold"><?php esc_html_e("To unlock more features of google products, consider our","conversios"); ?> <a href="<?php echo esc_url_raw($TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=Google+Analytics+Screen+pro+version&utm_campaign=Upsell+at+Conversios'); ?>" target="_blank"><?php esc_html_e("pro version.","conversios"); ?></a></p>
178
  <form method="post" name="google-analytic" id="tvc-licence-active">
179
  <div class="input-group">
180
+ <input type="text" id="licence_key" name="licence_key" class="form-control" placeholder="<?php esc_html_e("Already purchased? Enter licence key","conversios"); ?>" required="">
181
  <div class="input-group-append">
182
+ <button type="submit" class="btn btn-primary" name="verify-licence-key"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/right-arrow.svg'); ?>" alt="active licence key"></button>
183
  </div>
184
  </div>
185
  </form>
190
  <div class="google-account-analytics">
191
  <div class="row mb-3">
192
  <div class="col-6 col-md-6 col-lg-6">
193
+ <h2 class="ga-title"><?php esc_html_e("Connected Google Analytics Account:","conversios"); ?></h2>
194
  </div>
195
  <div class="col-6 col-md-6 col-lg-6 text-right">
196
  <div class="acc-num">
197
  <p class="ga-text">
198
+ <?php echo (isset($data['ga_id']) && $data['ga_id'] != '') ? $data['ga_id'] : '<span>'.esc_html__("Get started","conversios").'</span>'; ?>
199
  </p>
200
  <?php
201
  if (isset($data['ga_id']) && $data['ga_id'] != '') {
202
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'.esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
203
  } else {
204
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
205
  }?>
206
  </div>
207
  </div>
208
  </div>
209
  <div class="row mb-3">
210
  <div class="col-6 col-md-6 col-lg-6">
211
+ <h2 class="ga-title"><?php esc_html_e("Connected Google Analytics 4 Account:","conversios"); ?></h2>
212
  </div>
213
  <div class="col-6 col-md-6 col-lg-6 text-right">
214
  <div class="acc-num">
215
  <p class="ga-text">
216
+ <?php echo (isset($data['gm_id']) && $data['gm_id'] != '') ? $data['gm_id'] : '<span>'.esc_html__("Get started","conversios").'</span>'; ?>
217
  </p>
218
  <?php
219
  if (isset($data['gm_id']) && esc_attr($data['gm_id']) != '') {
220
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
221
  } else {
222
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
223
  }?>
224
  </div>
225
  </div>
226
  </div>
227
  <div class="row mb-3">
228
  <div class="col-6 col-md-6 col-lg-6">
229
+ <h2 class="ga-title"><?php esc_html_e("Linked Google Ads Account:","conversios"); ?></h2>
230
  </div>
231
  <div class="col-6 col-md-6 col-lg-6 text-right">
232
  <div class="acc-num">
233
  <p class="ga-text">
234
+ <?php echo (isset($data['google_ads_id']) && $data['google_ads_id'] != '') ? $data['google_ads_id'] : '<span>'.esc_html__("Get started","conversios").'</span>'; ?>
235
  </p>
236
  <?php
237
  if (isset($data['google_ads_id']) && esc_attr($data['google_ads_id']) != '') {
238
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
239
  } else {
240
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
241
  }?>
242
  </div>
243
  </div>
244
  </div>
245
  <div class="row mb-3">
246
  <div class="col-6 col-md-6 col-lg-6">
247
+ <h2 class="ga-title"><?php esc_html_e("Linked Google Merchant Center Account:","conversios"); ?></h2>
248
  </div>
249
  <div class="col-6 col-md-6 col-lg-6 text-right">
250
  <div class="acc-num">
251
  <p class="ga-text">
252
+ <?php echo (isset($data['google_merchant_id']) && $data['google_merchant_id'] != '') ? $data['google_merchant_id'] : '<span>'.esc_html__("Get started","conversios").'</span>'; ?>
253
  </p>
254
  <?php
255
  if (isset($data['google_merchant_id']) && esc_attr($data['google_merchant_id']) != '') {
256
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
257
  } else {
258
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
259
  }?>
260
  </div>
261
  </div>
267
  <tbody>
268
  <tr>
269
  <th>
270
+ <label class="align-middle" for="tracking_code"><?php esc_html_e("Tracking Code","conversios"); ?></label>
271
  </th>
272
  <td>
273
  <label class = "align-middle">
274
  <?php $ga_ST = !empty($data['ga_ST']) ? 'checked' : ''; ?>
275
  <input type="checkbox" name="ga_ST" id="ga_ST" <?php echo esc_attr($ga_ST); ?> >
276
+ <label class="custom-control-label" for="ga_ST"><?php esc_html_e("Add Global Site Tracking Code 'gtag.js'","conversios"); ?></label>
277
 
278
+ <i style="cursor: help;" class="fas fa-question-circle" title="<?php esc_html_e("This feature adds new gtag.js tracking code to your store. You don't need to enable this if gtag.js is implemented via any third party analytics plugin.","conversios"); ?>"></i>
279
  </label><br/>
280
  <label class = "align-middle">
281
  <?php $ga_eeT = !empty($data['ga_eeT']) ? 'checked' : ''; ?>
282
  <input type="checkbox" name="ga_eeT" id="ga_eeT" <?php echo esc_attr($ga_eeT); ?> >
283
+ <label class="custom-control-label" for="ga_eeT"><?php esc_html_e("Add Enhanced Ecommerce Tracking Code","conversios"); ?></label>
284
 
285
+ <i style="cursor: help;" class="fas fa-question-circle" title="<?php esc_html_e("This feature adds Enhanced Ecommerce Tracking Code to your Store","conversios"); ?>"></i>
286
  </label><br/>
287
  <label class = "align-middle">
288
  <?php $ga_gUser = !empty($data['ga_gUser']) ? 'checked' : ''; ?>
289
  <input type="checkbox" name="ga_gUser" id="ga_gUser" <?php echo esc_attr($ga_gUser); ?> >
290
+ <label class="custom-control-label" for="ga_gUser"><?php esc_html_e("Add Code to Track the Login Step of Guest Users (Optional)","conversios"); ?></label>
291
 
292
+ <i style="cursor: help;" class="fas fa-question-circle" title="<?php esc_html_e("If you have Guest Check out enable, we recommend you to add this code","conversios"); ?>"></i>
293
  </label>
294
  </td>
295
  </tr>
296
  <tr>
297
  <th>
298
+ <label for="ga_Impr"><?php esc_html_e("Impression Thresold","conversios"); ?></label>
299
  </th>
300
  <td>
301
  <?php $ga_Impr = !empty($data['ga_Impr']) ? $data['ga_Impr'] : 6; ?>
302
  <input type="number" min="1" id="ga_Impr" name = "ga_Impr" value = "<?php echo esc_attr($ga_Impr); ?>">
303
  <label for="ga_Impr"></label>
304
+ <i style="cursor: help;" class="fas fa-question-circle" title="<?php esc_html_e("This feature sets Impression threshold for category page. It sends hit after these many numbers of products impressions.","conversios"); ?>"></i>
305
+ <p class="description"><b><?php esc_html_e("Note : To avoid processing load on server we recommend upto 6 Impression Thresold.","conversios"); ?></b></p>
306
  </td>
307
  </tr>
308
  <tr>
309
  <th>
310
+ <label class = "align-middle" for="ga_IPA"><?php esc_html_e("I.P. Anoymization","conversios"); ?></label>
311
  </th>
312
  <td>
313
  <label class = "align-middle">
314
  <?php $ga_IPA = !empty($data['ga_IPA']) ? 'checked' : ''; ?>
315
  <input class="" type="checkbox" name="ga_IPA" id="ga_IPA" <?php echo esc_attr($ga_IPA); ?>>
316
+ <label class="custom-control-label" for="ga_IPA"><?php esc_html_e("Enable I.P. Anonymization","conversios"); ?></label>
317
 
318
+ <i style="cursor: help;" class="fas fa-question-circle" title="<?php esc_html_e("Use this feature to anonymize (or stop collecting) the I.P Address of your users in Google Analytics. Be in legal compliance by using I.P Anonymization which is important for EU countries As per the GDPR compliance","conversios"); ?>"></i>
319
  </label>
320
  </td>
321
  </tr>
322
  <tr>
323
  <th>
324
+ <label class = "align-middle" for="ga_OPTOUT"><?php esc_html_e("Google Analytics Opt Out","conversios"); ?></label>
325
  </th>
326
  <td>
327
  <label class = "align-middle">
328
  <?php $ga_OPTOUT = !empty($data['ga_OPTOUT']) ? 'checked' : ''; ?>
329
  <input class="" type="checkbox" name="ga_OPTOUT" id="ga_OPTOUT" <?php echo esc_attr($ga_OPTOUT); ?>>
330
+ <label class="custom-control-label" for="ga_OPTOUT"><?php esc_html_e("Enable Google Analytics Opt Out (Optional)","conversios"); ?></label>
331
 
332
+ <i style="cursor: help;" class="fas fa-question-circle" title="<?php esc_html_e("Use this feature to provide website visitors the ability to prevent their data from being used by Google Analytics As per the GDPR compliance.Go through the documentation to check the setup","conversios"); ?>"></i>
333
  </label>
334
  </td>
335
  </tr>
336
  <tr>
337
  <th>
338
+ <label class = "align-middle" for="ga_PrivacyPolicy"><?php esc_html_e("Privacy Policy","conversios"); ?></label>
339
  </th>
340
  <td>
341
  <label class = "align-middle">
342
  <?php $ga_PrivacyPolicy = !empty($data['ga_PrivacyPolicy']) ? 'checked' : ''; ?>
343
  <input type="checkbox" name="ga_PrivacyPolicy" id="ga_PrivacyPolicy" required="required" <?php echo esc_attr($ga_PrivacyPolicy); ?>>
344
+ <label class="custom-control-label" for="ga_PrivacyPolicy"><?php esc_html_e("Accept Privacy Policy of Plugin","conversios"); ?></label>
345
 
346
+ <p class="description"><?php esc_html_e("By using Tatvic Plugin, you agree to Tatvic plugin's","conversios"); ?> <a href= "<?php echo esc_url_raw("https://www.tatvic.com/privacy-policy/?ref=plugin_policy&utm_source=plugin_backend&utm_medium=woo_premium_plugin&utm_campaign=GDPR_complaince_ecomm_plugins"); ?>" target="_blank"><?php esc_html_e("Privacy Policy","conversios"); ?></a></p>
347
  </label>
348
  </td>
349
  </tr>
355
  <input type="hidden" id="google_ads_id" name = "google_ads_id" value="<?= esc_attr((!empty($data['google_ads_id']))?$data['google_ads_id']:""); ?>"/>
356
  <input type="hidden" id="google_merchant_id" name = "google_merchant_id" value="<?= esc_attr((!empty($data['google_merchant_id']))?$data['google_merchant_id']:""); ?>"/>
357
  <input type="hidden" name="subscription_id" value="<?php echo esc_attr((!empty($data['subscription_id']))?$data['subscription_id']:""); ?>">
358
+ <button type="submit" class="btn btn-primary" id="ee_submit_plugin" name="ee_submit_plugin"><?php esc_html_e("Save","conversios"); ?></button>
359
  </p>
360
  </form>
361
  </div>
admin/partials/pricings.php CHANGED
@@ -17,8 +17,7 @@ class TVC_Pricings {
17
  $this->create_form();
18
  }
19
 
20
- public function create_form() {
21
- $message = ""; $class="";
22
  $googleDetail = [];
23
  $plan_id = 1;
24
  if(isset($this->google_detail['setting'])){
@@ -30,328 +29,325 @@ class TVC_Pricings {
30
  }
31
  }
32
 
33
- $close_icon = esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close.png');
34
- $check_icon = esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/check.png');
35
  ?>
36
  <div class="tab-content">
37
- <?php if($message){
38
- printf('<div class="%1$s"><div class="alert">%2$s</div></div>', esc_attr($class), esc_html($message));
39
- }?>
40
  <div class="tab-pane show active" id="tvc-account-page">
41
  <div class="tab-card" >
42
  <div class="tvc-price-table-features columns-5">
43
  <div class="tvc-container">
44
  <div class="clearfix">
45
  <div class="row-heading clearfix">
46
- <div class="column tvc-blank-col"><span><?php _e("Features","conversios"); ?></span></div>
47
  <div class="column discounted tvc-free-plan">
48
- <div class="name-wrap"><div class="name"><?php _e("STARTER","conversios"); ?></div></div>
49
  <div class="tvc-list-price">
50
- <div class="price-current"><span class="inner"><?php _e("FREE","conversios"); ?></span></div>
51
- <div class="tvc_month_free"><?php _e("FOREVER FREE","conversios"); ?></div>
52
  </div>
53
  <span>
54
- <a href="javascript:void(0)" class="btn tvc-btn"><?php _e("Currently Active","conversios"); ?></a>
55
  </span>
56
  </div>
57
  <div class="column discounted ">
58
- <div class="name-wrap"><div class="name"><?php _e("HUSTLE","conversios"); ?></div></div>
59
  <div class="tvc-list-price-month">
60
  <div class="tvc-list-price">
61
  <div class="price-normal">
62
- <span><?php _e("$39.00","conversios"); ?></span>
63
- <div class="tvc-plan-off"><?php _e("50% OFF","conversios"); ?></div>
64
  </div>
65
- <div class="price-current"><span class="inner"><?php _e("$19<span>/month</span>","conversios"); ?></span></div>
66
- <div class="tvc_month_free"><?php _e("Limited Offer","conversios"); ?></div>
67
  </div>
68
- <a target="_blank" href="<?php echo esc_url("https://conversios.io/checkout/?pid=plan_1_m&utm_source=EE+Plugin+User+Interface&utm_medium=HUSTLE&utm_campaign=Upsell+at+Conversios"); ?>" class="btn tvc-btn"><?php _e("Get Started","conversios"); ?></a>
69
  </div>
70
  </div>
71
  <div class="column discounted popular">
72
  <div class="tvc_popular">
73
- <div class="tvc_popular_inner"><?php _e("POPULAR","conversios"); ?></div>
74
  </div>
75
  <div class="name-wrap">
76
- <div class="name"><?php _e("GROWTH","conversios"); ?></div>
77
  </div>
78
  <div class="tvc-list-price-month">
79
  <div class="tvc-list-price">
80
- <div class="price-normal"><span><?php _e("$59.00","conversios"); ?></span>
81
- <div class="tvc-plan-off"><?php _e("50% OFF","conversios"); ?></div>
82
  </div>
83
- <div class="price-current"><span class="inner"><?php _e("$29<span>/month</span>","conversios"); ?></span></div>
84
- <div class="tvc_month_free"><?php _e("Limited Offer","conversios"); ?></div>
85
  </div>
86
- <a target="_blank" href="<?php echo esc_url("https://conversios.io/checkout/?pid=plan_2_m&utm_source=EE+Plugin+User+Interface&utm_medium=GROWTH&utm_campaign=Upsell+at+Conversios"); ?>" class="btn tvc-btn"><?php _e("Get Started","conversios"); ?></a>
87
  </div>
88
  </div>
89
  <div class="column discounted ">
90
  <div class="name-wrap">
91
- <div class="name"><?php _e("LEAP","conversios"); ?></div>
92
  </div>
93
  <div class="tvc-list-price-month">
94
  <div class="tvc-list-price">
95
- <div class="price-normal"><span><?php _e("$99.00","conversios"); ?></span>
96
- <div class="tvc-plan-off"><?php _e("50% OFF","conversios"); ?></div>
97
  </div>
98
- <div class="price-current"><span class="inner"><?php _e("$49<span>/month</span>","conversios"); ?></span></div>
99
- <div class="tvc_month_free"><?php _e("Limited Offer","conversios"); ?></div>
100
  </div>
101
- <a target="_blank" href="<?php echo esc_url("https://conversios.io/checkout/?pid=plan_3_m&utm_source=EE+Plugin+User+Interface&utm_medium=LEAP&utm_campaign=Upsell+at+Conversios"); ?>" class="btn tvc-btn"><?php _e("Get Started","conversios"); ?></a>
102
  </div>
103
  </div>
104
  </div>
105
- <div class="row-subheading clearfix"><?php _e("Google Analytics","conversios"); ?></div>
106
  <div class="row-feature clearfix">
107
- <div class="column"><?php _e("Universal Analytics Tracking","conversios"); ?></div>
108
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
109
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
110
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
111
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
112
  </div>
113
  <div class="row-feature clearfix">
114
- <div class="column"><?php _e("Google Analytics 4 Tracking","conversios"); ?></div>
115
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
116
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
117
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
118
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
119
  </div>
120
  <div class="row-feature clearfix">
121
- <div class="column"><?php _e("Dual Set up (UA + GA4)","conversios"); ?></div>
122
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
123
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
124
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
125
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
126
  </div>
127
  <div class="row-feature clearfix">
128
- <div class="column"><?php _e("eCommerce tracking","conversios"); ?></div>
129
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Limited)","conversios"); ?></div>
130
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
131
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
132
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
133
  </div><div class="row-feature clearfix">
134
- <div class="column"><?php _e("Shopping Behavior Analysis","conversios"); ?></div>
135
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Limited)","conversios"); ?></div>
136
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
137
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
138
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
139
  </div><div class="row-feature clearfix">
140
- <div class="column"><?php _e("Checkout Behavior Tracking","conversios"); ?></div>
141
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Limited)","conversios"); ?></div>
142
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
143
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
144
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
145
  </div><div class="row-feature clearfix">
146
- <div class="column"><?php _e("Channel Performance Analysis","conversios"); ?></div>
147
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Limited)","conversios"); ?></div>
148
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
149
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
150
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
151
  </div><div class="row-feature clearfix">
152
- <div class="column"><?php _e("All Pages tracking","conversios"); ?></div>
153
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
154
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
155
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
156
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
157
  </div><div class="row-feature clearfix">
158
- <div class="column"><?php _e("Google Analytics and Google Ads linking","conversios"); ?></div>
159
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
160
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
161
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
162
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
163
  </div><div class="row-feature clearfix">
164
- <div class="column"><?php _e("Custom dimensions tracking","conversios"); ?></div>
165
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
166
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
167
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
168
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
169
  </div><div class="row-feature clearfix">
170
- <div class="column"><?php _e("Custom metrics tracking","conversios"); ?></div>
171
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
172
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
173
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
174
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
175
  </div><div class="row-feature clearfix">
176
- <div class="column"><?php _e("User id tracking","conversios"); ?></div>
177
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
178
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
179
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
180
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
181
  </div><div class="row-feature clearfix">
182
- <div class="column"><?php _e("Client id tracking","conversios"); ?></div>
183
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
184
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
185
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
186
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
187
  </div><div class="row-feature clearfix">
188
- <div class="column"><?php _e("Scroll tracking","conversios"); ?></div>
189
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
190
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
191
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
192
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
193
  </div>
194
  <div class="row-feature clearfix">
195
- <div class="column"><?php _e("Affiliate performance tracking","conversios"); ?></div>
196
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
197
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
198
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
199
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
200
  </div>
201
  <div class="row-feature clearfix">
202
- <div class="column"><?php _e("Coupon Performance Tracking","conversios"); ?></div>
203
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
204
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
205
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
206
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
207
  </div>
208
  <div class="row-feature clearfix">
209
- <div class="column"><?php _e("Actionable Dashboard","conversios"); ?></div>
210
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Limited)","conversios"); ?></div>
211
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
212
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
213
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
214
  </div>
215
  <div class="row-feature clearfix">
216
- <div class="column"><?php _e("Menu tracking","conversios"); ?></div>
217
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
218
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
219
- <div class="column popular"><?php _e("(Upcoming)","conversios"); ?></div>
220
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
221
  </div>
222
  <div class="row-feature clearfix">
223
- <div class="column"><?php _e("Search Query Tracking","conversios"); ?></div>
224
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
225
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
226
- <div class="column popular"><?php _e("(Upcoming)","conversios"); ?></div>
227
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
228
  </div>
229
- <div class="row-subheading clearfix"><?php _e("Google Shopping","conversios"); ?></div>
230
  <div class="row-feature clearfix">
231
- <div class="column"><?php _e("Google Merchant Center account management","conversios"); ?></div>
232
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
233
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
234
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
235
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
236
  </div><div class="row-feature clearfix">
237
- <div class="column"><?php _e("Site verification","conversios"); ?></div>
238
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
239
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
240
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
241
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
242
  </div><div class="row-feature clearfix">
243
- <div class="column"><?php _e("Domain claim","conversios"); ?></div>
244
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
245
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
246
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
247
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
248
  </div><div class="row-feature clearfix">
249
- <div class="column"><?php _e("Products Sync via Content API","conversios"); ?></div>
250
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(upto 100)</div>
251
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(upto 1000)</div>
252
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br>(upto 5000)</div>
253
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(Unlimited)</div>
254
  </div><div class="row-feature clearfix">
255
- <div class="column"><?php _e("Automatic Products Update","conversios"); ?></div>
256
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(upto 100)</div>
257
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(upto 1000)</div>
258
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br>(upto 5000)</div>
259
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br>(Unlimited)</div>
260
  </div><div class="row-feature clearfix">
261
- <div class="column"><?php _e("Schedule Product Sync","conversios"); ?></div>
262
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
263
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
264
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
265
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
266
  </div><div class="row-feature clearfix">
267
- <div class="column"><?php _e("Smart Shopping Campaign management","conversios"); ?></div>
268
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
269
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
270
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
271
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
272
  </div><div class="row-feature clearfix">
273
- <div class="column"><?php _e("Smart Shopping reports","conversios"); ?></div>
274
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
275
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
276
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
277
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
278
  </div><div class="row-feature clearfix">
279
- <div class="column"><?php _e("Google Ads and Google Merchant Center account linking","conversios"); ?></div>
280
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
281
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
282
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
283
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
284
  </div><div class="row-feature clearfix">
285
- <div class="column"><?php _e("Remarketing tags","conversios"); ?></div>
286
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
287
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
288
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
289
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
290
  </div><div class="row-feature clearfix">
291
- <div class="column"><?php _e("Dynamic Remarketing Tags for eCommerce events","conversios"); ?></div>
292
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Limited)","conversios"); ?></div>
293
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
294
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
295
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"><br><?php _e("(Complete)","conversios"); ?></div>
296
  </div><div class="row-feature clearfix">
297
- <div class="column"><?php _e("Google Ads Conversion tracking","conversios"); ?></div>
298
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
299
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
300
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
301
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
302
  </div><div class="row-feature clearfix">
303
- <div class="column"><?php _e("Product filters for selected products sync","conversios"); ?></div>
304
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
305
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
306
- <div class="column popular"><?php _e("(Upcoming)","conversios"); ?></div>
307
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
308
  </div><div class="row-feature clearfix">
309
- <div class="column"><?php _e("Product filter for Smart Shopping Campaign","conversios"); ?></div>
310
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
311
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
312
- <div class="column popular"><?php _e("(Upcoming)","conversios"); ?></div>
313
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
314
  </div>
315
  <div class="row-feature clearfix">
316
- <div class="column"><?php _e("Rule Based Shopping Campaigns","conversios"); ?></div>
317
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
318
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
319
- <div class="column popular"><?php _e("(Upcoming)","conversios"); ?></div>
320
- <div class="column"><?php _e("(Upcoming)","conversios"); ?></div>
321
  </div>
322
- <div class="row-subheading clearfix"><?php _e("Support","conversios"); ?></div>
323
  <div class="row-feature clearfix">
324
- <div class="column"><?php _e("Free Google Analytics Audit","conversios"); ?></div>
325
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
326
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
327
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
328
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
329
  </div><div class="row-feature clearfix">
330
- <div class="column"><?php _e("Free Consultation with Shopping Expert","conversios"); ?></div>
331
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
332
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
333
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
334
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
335
  </div><div class="row-feature clearfix">
336
- <div class="column"><?php _e("Dedicated Customer Success Manager","conversios"); ?></div>
337
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
338
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
339
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
340
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
341
  </div><div class="row-footer clearfix">
342
- <div class="column"><?php _e("Premium Support (24*7)","conversios"); ?></div>
343
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
344
- <div class="column"><img src="<?php echo $close_icon; ?>" alt="no"></div>
345
- <div class="column popular "><img src="<?php echo $check_icon; ?>" alt="yes"></div>
346
- <div class="column"><img src="<?php echo $check_icon; ?>" alt="yes"></div>
347
  </div>
348
  </div>
349
  </div>
350
  </div>
351
  <div class="tvc-guarantee">
352
  <div class="guarantee">
353
- <div class="title"><?php _e("<span>15 Days</span>100% No-Risk Money Back Guarantee!","conversios"); ?></div>
354
- <div class="description"><?php _e("You are fully protected by our 100% No-Risk-Double-Guarantee. If you don’t like over the next 15 days, then we will happily refund 100% of your money. No questions asked.","conversios"); ?></div>
355
  </div>
356
  </div>
357
  </div>
17
  $this->create_form();
18
  }
19
 
20
+ public function create_form() {
 
21
  $googleDetail = [];
22
  $plan_id = 1;
23
  if(isset($this->google_detail['setting'])){
29
  }
30
  }
31
 
32
+ $close_icon = esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close.png');
33
+ $check_icon = esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/check.png');
34
  ?>
35
  <div class="tab-content">
 
 
 
36
  <div class="tab-pane show active" id="tvc-account-page">
37
  <div class="tab-card" >
38
  <div class="tvc-price-table-features columns-5">
39
  <div class="tvc-container">
40
  <div class="clearfix">
41
  <div class="row-heading clearfix">
42
+ <div class="column tvc-blank-col"><span><?php esc_html_e("Features","conversios"); ?></span></div>
43
  <div class="column discounted tvc-free-plan">
44
+ <div class="name-wrap"><div class="name"><?php esc_html_e("STARTER","conversios"); ?></div></div>
45
  <div class="tvc-list-price">
46
+ <div class="price-current"><span class="inner"><?php esc_html_e("FREE","conversios"); ?></span></div>
47
+ <div class="tvc_month_free"><?php esc_html_e("FOREVER FREE","conversios"); ?></div>
48
  </div>
49
  <span>
50
+ <a href="javascript:void(0)" class="btn tvc-btn"><?php esc_html_e("Currently Active","conversios"); ?></a>
51
  </span>
52
  </div>
53
  <div class="column discounted ">
54
+ <div class="name-wrap"><div class="name"><?php esc_html_e("HUSTLE","conversios"); ?></div></div>
55
  <div class="tvc-list-price-month">
56
  <div class="tvc-list-price">
57
  <div class="price-normal">
58
+ <span><?php esc_html_e("$39.00","conversios"); ?></span>
59
+ <div class="tvc-plan-off"><?php esc_html_e("50% OFF","conversios"); ?></div>
60
  </div>
61
+ <div class="price-current"><span class="inner"><?php printf("%s <span>%s</span>",esc_html_e("$19"),esc_html_e("/month")); ?></span></div>
62
+ <div class="tvc_month_free"><?php esc_html_e("Limited Offer","conversios"); ?></div>
63
  </div>
64
+ <a target="_blank" href="<?php echo esc_url_raw("https://conversios.io/checkout/?pid=plan_1_m&utm_source=EE+Plugin+User+Interface&utm_medium=HUSTLE&utm_campaign=Upsell+at+Conversios"); ?>" class="btn tvc-btn"><?php esc_html_e("Get Started","conversios"); ?></a>
65
  </div>
66
  </div>
67
  <div class="column discounted popular">
68
  <div class="tvc_popular">
69
+ <div class="tvc_popular_inner"><?php esc_html_e("POPULAR","conversios"); ?></div>
70
  </div>
71
  <div class="name-wrap">
72
+ <div class="name"><?php esc_html_e("GROWTH","conversios"); ?></div>
73
  </div>
74
  <div class="tvc-list-price-month">
75
  <div class="tvc-list-price">
76
+ <div class="price-normal"><span><?php esc_html_e("$59.00","conversios"); ?></span>
77
+ <div class="tvc-plan-off"><?php esc_html_e("50% OFF","conversios"); ?></div>
78
  </div>
79
+ <div class="price-current"><span class="inner"><?php printf("%s <span>%s</span>",esc_html_e("$29"),esc_html_e("/month")); ?></span></div>
80
+ <div class="tvc_month_free"><?php esc_html_e("Limited Offer","conversios"); ?></div>
81
  </div>
82
+ <a target="_blank" href="<?php echo esc_url_raw("https://conversios.io/checkout/?pid=plan_2_m&utm_source=EE+Plugin+User+Interface&utm_medium=GROWTH&utm_campaign=Upsell+at+Conversios"); ?>" class="btn tvc-btn"><?php esc_html_e("Get Started","conversios"); ?></a>
83
  </div>
84
  </div>
85
  <div class="column discounted ">
86
  <div class="name-wrap">
87
+ <div class="name"><?php esc_html_e("LEAP","conversios"); ?></div>
88
  </div>
89
  <div class="tvc-list-price-month">
90
  <div class="tvc-list-price">
91
+ <div class="price-normal"><span><?php esc_html_e("$99.00","conversios"); ?></span>
92
+ <div class="tvc-plan-off"><?php esc_html_e("50% OFF","conversios"); ?></div>
93
  </div>
94
+ <div class="price-current"><span class="inner"><?php printf("%s <span>%s</span>",esc_html_e("$49"),esc_html_e("/month")); ?></span></div>
95
+ <div class="tvc_month_free"><?php esc_html_e("Limited Offer","conversios"); ?></div>
96
  </div>
97
+ <a target="_blank" href="<?php echo esc_url_raw("https://conversios.io/checkout/?pid=plan_3_m&utm_source=EE+Plugin+User+Interface&utm_medium=LEAP&utm_campaign=Upsell+at+Conversios"); ?>" class="btn tvc-btn"><?php esc_html_e("Get Started","conversios"); ?></a>
98
  </div>
99
  </div>
100
  </div>
101
+ <div class="row-subheading clearfix"><?php esc_html_e("Google Analytics","conversios"); ?></div>
102
  <div class="row-feature clearfix">
103
+ <div class="column"><?php esc_html_e("Universal Analytics Tracking","conversios"); ?></div>
104
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
105
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
106
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
107
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
108
  </div>
109
  <div class="row-feature clearfix">
110
+ <div class="column"><?php esc_html_e("Google Analytics 4 Tracking","conversios"); ?></div>
111
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
112
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
113
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
114
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
115
  </div>
116
  <div class="row-feature clearfix">
117
+ <div class="column"><?php esc_html_e("Dual Set up (UA + GA4)","conversios"); ?></div>
118
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
119
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
120
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
121
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
122
  </div>
123
  <div class="row-feature clearfix">
124
+ <div class="column"><?php esc_html_e("eCommerce tracking","conversios"); ?></div>
125
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Limited)","conversios"); ?></div>
126
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
127
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
128
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
129
  </div><div class="row-feature clearfix">
130
+ <div class="column"><?php esc_html_e("Shopping Behavior Analysis","conversios"); ?></div>
131
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Limited)","conversios"); ?></div>
132
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
133
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
134
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
135
  </div><div class="row-feature clearfix">
136
+ <div class="column"><?php esc_html_e("Checkout Behavior Tracking","conversios"); ?></div>
137
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Limited)","conversios"); ?></div>
138
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
139
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
140
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
141
  </div><div class="row-feature clearfix">
142
+ <div class="column"><?php esc_html_e("Channel Performance Analysis","conversios"); ?></div>
143
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Limited)","conversios"); ?></div>
144
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
145
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
146
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
147
  </div><div class="row-feature clearfix">
148
+ <div class="column"><?php esc_html_e("All Pages tracking","conversios"); ?></div>
149
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
150
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
151
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
152
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
153
  </div><div class="row-feature clearfix">
154
+ <div class="column"><?php esc_html_e("Google Analytics and Google Ads linking","conversios"); ?></div>
155
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
156
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
157
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
158
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
159
  </div><div class="row-feature clearfix">
160
+ <div class="column"><?php esc_html_e("Custom dimensions tracking","conversios"); ?></div>
161
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
162
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
163
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
164
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
165
  </div><div class="row-feature clearfix">
166
+ <div class="column"><?php esc_html_e("Custom metrics tracking","conversios"); ?></div>
167
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
168
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
169
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
170
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
171
  </div><div class="row-feature clearfix">
172
+ <div class="column"><?php esc_html_e("User id tracking","conversios"); ?></div>
173
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
174
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
175
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
176
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
177
  </div><div class="row-feature clearfix">
178
+ <div class="column"><?php esc_html_e("Client id tracking","conversios"); ?></div>
179
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
180
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
181
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
182
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
183
  </div><div class="row-feature clearfix">
184
+ <div class="column"><?php esc_html_e("Scroll tracking","conversios"); ?></div>
185
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
186
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
187
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
188
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
189
  </div>
190
  <div class="row-feature clearfix">
191
+ <div class="column"><?php esc_html_e("Affiliate performance tracking","conversios"); ?></div>
192
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
193
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
194
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
195
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
196
  </div>
197
  <div class="row-feature clearfix">
198
+ <div class="column"><?php esc_html_e("Coupon Performance Tracking","conversios"); ?></div>
199
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
200
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
201
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
202
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
203
  </div>
204
  <div class="row-feature clearfix">
205
+ <div class="column"><?php esc_html_e("Actionable Dashboard","conversios"); ?></div>
206
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Limited)","conversios"); ?></div>
207
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
208
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
209
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
210
  </div>
211
  <div class="row-feature clearfix">
212
+ <div class="column"><?php esc_html_e("Menu tracking","conversios"); ?></div>
213
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
214
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
215
+ <div class="column popular"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
216
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
217
  </div>
218
  <div class="row-feature clearfix">
219
+ <div class="column"><?php esc_html_e("Search Query Tracking","conversios"); ?></div>
220
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
221
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
222
+ <div class="column popular"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
223
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
224
  </div>
225
+ <div class="row-subheading clearfix"><?php esc_html_e("Google Shopping","conversios"); ?></div>
226
  <div class="row-feature clearfix">
227
+ <div class="column"><?php esc_html_e("Google Merchant Center account management","conversios"); ?></div>
228
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
229
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
230
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
231
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
232
  </div><div class="row-feature clearfix">
233
+ <div class="column"><?php esc_html_e("Site verification","conversios"); ?></div>
234
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
235
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
236
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
237
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
238
  </div><div class="row-feature clearfix">
239
+ <div class="column"><?php esc_html_e("Domain claim","conversios"); ?></div>
240
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
241
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
242
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
243
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
244
  </div><div class="row-feature clearfix">
245
+ <div class="column"><?php esc_html_e("Products Sync via Content API","conversios"); ?></div>
246
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(upto 100)","conversios"); ?></div>
247
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(upto 1000)","conversios"); ?></div>
248
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(upto 5000)","conversios"); ?></div>
249
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Unlimited)","conversios"); ?></div>
250
  </div><div class="row-feature clearfix">
251
+ <div class="column"><?php esc_html_e("Automatic Products Update","conversios"); ?></div>
252
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(upto 100)","conversios"); ?></div>
253
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(upto 1000","conversios"); ?></div>
254
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(upto 5000)","conversios"); ?></div>
255
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Unlimited)","conversios"); ?></div>
256
  </div><div class="row-feature clearfix">
257
+ <div class="column"><?php esc_html_e("Schedule Product Sync","conversios"); ?></div>
258
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
259
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
260
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
261
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
262
  </div><div class="row-feature clearfix">
263
+ <div class="column"><?php esc_html_e("Smart Shopping Campaign management","conversios"); ?></div>
264
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
265
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
266
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
267
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
268
  </div><div class="row-feature clearfix">
269
+ <div class="column"><?php esc_html_e("Smart Shopping reports","conversios"); ?></div>
270
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
271
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
272
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
273
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
274
  </div><div class="row-feature clearfix">
275
+ <div class="column"><?php esc_html_e("Google Ads and Google Merchant Center account linking","conversios"); ?></div>
276
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
277
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
278
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
279
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
280
  </div><div class="row-feature clearfix">
281
+ <div class="column"><?php esc_html_e("Remarketing tags","conversios"); ?></div>
282
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
283
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
284
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
285
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
286
  </div><div class="row-feature clearfix">
287
+ <div class="column"><?php esc_html_e("Dynamic Remarketing Tags for eCommerce events","conversios"); ?></div>
288
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Limited)","conversios"); ?></div>
289
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
290
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
291
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"><br><?php esc_html_e("(Complete)","conversios"); ?></div>
292
  </div><div class="row-feature clearfix">
293
+ <div class="column"><?php esc_html_e("Google Ads Conversion tracking","conversios"); ?></div>
294
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
295
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
296
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
297
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
298
  </div><div class="row-feature clearfix">
299
+ <div class="column"><?php esc_html_e("Product filters for selected products sync","conversios"); ?></div>
300
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
301
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
302
+ <div class="column popular"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
303
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
304
  </div><div class="row-feature clearfix">
305
+ <div class="column"><?php esc_html_e("Product filter for Smart Shopping Campaign","conversios"); ?></div>
306
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
307
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
308
+ <div class="column popular"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
309
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
310
  </div>
311
  <div class="row-feature clearfix">
312
+ <div class="column"><?php esc_html_e("Rule Based Shopping Campaigns","conversios"); ?></div>
313
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
314
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
315
+ <div class="column popular"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
316
+ <div class="column"><?php esc_html_e("(Upcoming)","conversios"); ?></div>
317
  </div>
318
+ <div class="row-subheading clearfix"><?php esc_html_e("Support","conversios"); ?></div>
319
  <div class="row-feature clearfix">
320
+ <div class="column"><?php esc_html_e("Free Google Analytics Audit","conversios"); ?></div>
321
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
322
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
323
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
324
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
325
  </div><div class="row-feature clearfix">
326
+ <div class="column"><?php esc_html_e("Free Consultation with Shopping Expert","conversios"); ?></div>
327
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
328
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
329
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
330
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
331
  </div><div class="row-feature clearfix">
332
+ <div class="column"><?php esc_html_e("Dedicated Customer Success Manager","conversios"); ?></div>
333
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
334
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
335
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
336
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
337
  </div><div class="row-footer clearfix">
338
+ <div class="column"><?php esc_html_e("Premium Support (24*7)","conversios"); ?></div>
339
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
340
+ <div class="column"><img src="<?php echo esc_url_raw($close_icon); ?>" alt="no"></div>
341
+ <div class="column popular "><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
342
+ <div class="column"><img src="<?php echo esc_url_raw($check_icon); ?>" alt="yes"></div>
343
  </div>
344
  </div>
345
  </div>
346
  </div>
347
  <div class="tvc-guarantee">
348
  <div class="guarantee">
349
+ <div class="title"><?php printf("<span>%s</span>%s", esc_html_e("15 Days","conversios"), esc_html_e("100% No-Risk Money Back Guarantee!","conversios")); ?></div>
350
+ <div class="description"><?php esc_html_e("You are fully protected by our 100% No-Risk-Double-Guarantee. If you don’t like over the next 15 days, then we will happily refund 100% of your money. No questions asked.","conversios"); ?></div>
351
  </div>
352
  </div>
353
  </div>
enhanced-ecommerce-google-analytics.php CHANGED
@@ -15,7 +15,7 @@
15
  * Plugin Name: Conversios.io - Google Analytics and Google Shopping plugin for WooCommerce
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.5.1
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.5.1' );
41
  $fullName = plugin_basename( __FILE__ );
42
  $dir = str_replace('/enhanced-ecommerce-google-analytics.php','',$fullName);
43
  if ( ! defined( 'ENHANCAD_PLUGIN_NAME' ) ) {
15
  * Plugin Name: Conversios.io - Google Analytics and Google Shopping plugin for WooCommerce
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.5.2
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.5.2' );
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-activator.php CHANGED
@@ -32,7 +32,7 @@ class Enhanced_Ecommerce_Google_Analytics_Activator {
32
  */
33
  public static function activate() {
34
  if (!is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
35
- wp_die(__("Hey, It seems WooCommerce plugin is not active on your wp-admin. Conversios.io - Google Analytics and Google Shopping plugin can only be activated if you have active WooCommerce plugin in your wp-admin.","conversios").' <br><a href="' . esc_url(admin_url( 'plugins.php' )) . '">&laquo; Return to Plugins</a>');
36
  }
37
  }
38
  }
32
  */
33
  public static function activate() {
34
  if (!is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
35
+ wp_die(wp_sprintf("%s <br><a href='" . esc_url(admin_url( 'plugins.php' )) . "'>&laquo; %s</a>",esc_html__("Hey, It seems WooCommerce plugin is not active on your wp-admin. Conversios.io - Google Analytics and Google Shopping plugin can only be activated if you have active WooCommerce plugin in your wp-admin.","conversios"), esc_html__("Return to Plugins","conversios")));
36
  }
37
  }
38
  }
includes/class-enhanced-ecommerce-google-analytics.php CHANGED
@@ -179,13 +179,11 @@ class Enhanced_Ecommerce_Google_Analytics {
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' );
186
  $this->loader->add_action( 'admin_notices', $plugin_admin, 'tvc_admin_notice' );
187
  if ( is_admin() ) {
188
- new TVC_Survey( "Enhanced ecommerce google analytics plugin for woocommerce", ENHANCAD_PLUGIN_NAME );
189
  }
190
 
191
  }
@@ -218,7 +216,7 @@ class Enhanced_Ecommerce_Google_Analytics {
218
  $this->loader->run();
219
  }else if( is_plugin_active( 'enhanced-e-commerce-for-woocommerce-store/enhanced-ecommerce-google-analytics.php' ) ){
220
  if(!isset($_POST['action'])){
221
- printf('<div class="notice tvc-notice notice-error"><p>'.__("Hey, It seems WooCommerce plugin is not active on your wp-admin. Conversios.io - Google Analytics and Google Shopping plugin can only be activated if you have active WooCommerce plugin in your wp-admin.","conversios").'</p></div>');
222
  }
223
  }
224
  }
@@ -260,12 +258,12 @@ class Enhanced_Ecommerce_Google_Analytics {
260
  public function tvc_plugin_action_links($links) {
261
  $deactivate_link = $links['deactivate'];
262
  unset($links['deactivate']);
263
- $setting_url = 'admin.php?page=conversios-google-analytics';
264
- $links[] = '<a href="' . get_admin_url(null, $setting_url) . '">'.__("Settings","conversios").'</a>';
265
 
266
- $links[] = '<a href="'.esc_url("https://wordpress.org/plugins/enhanced-e-commerce-for-woocommerce-store/#faq").'" target="_blank">'.__("FAQ","conversios").'</a>';
267
- $links[] = '<a href="'.esc_url("https://conversios.io/help-center/Installation-Manual.pdf").'" target="_blank">'.__("Documentation","conversios").'</a>';
268
- $links[] = '<a href="'.esc_url("https://conversios.io/pricings/?utm_source=EE+Plugin+User+Interface&utm_medium=Plugins+Listing+Page+Upgrade+to+Premium&utm_campaign=Upsell+at+Conversios").'" target="_blank"><b>'.__("Upgrade to Premium","conversios").'</b></a>';
269
  $links['deactivate'] = $deactivate_link;
270
  return $links;
271
  }
@@ -277,7 +275,7 @@ class Enhanced_Ecommerce_Google_Analytics {
277
 
278
  public function check_dependency(){
279
  if ( function_exists('run_actionable_google_analytics')) {
280
- _e('<div class="error"><p><strong>'. wp_sprintf( 'Note: ' ) .'</strong>'. wp_sprintf(__( 'It seems <strong>Actionable Google Analytics Plugin</strong> is active on your store. Kindly deactivate it in order to avoid data duplication in GA.' )) .'</p></div>');
281
  die();
282
  }
283
  }
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_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
183
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
184
  $this->loader->add_action( 'admin_notices', $plugin_admin, 'tvc_admin_notice' );
185
  if ( is_admin() ) {
186
+ new TVC_Survey( esc_html__("Enhanced ecommerce google analytics plugin for woocommerce"), ENHANCAD_PLUGIN_NAME );
187
  }
188
 
189
  }
216
  $this->loader->run();
217
  }else if( is_plugin_active( 'enhanced-e-commerce-for-woocommerce-store/enhanced-ecommerce-google-analytics.php' ) ){
218
  if(!isset($_POST['action'])){
219
+ printf('<div class="notice tvc-notice notice-error"><p>%s</p></div>',esc_html__("Hey, It seems WooCommerce plugin is not active on your wp-admin. Conversios.io - Google Analytics and Google Shopping plugin can only be activated if you have active WooCommerce plugin in your wp-admin.","conversios"));
220
  }
221
  }
222
  }
258
  public function tvc_plugin_action_links($links) {
259
  $deactivate_link = $links['deactivate'];
260
  unset($links['deactivate']);
261
+ $setting_url = esc_url_raw('admin.php?page=conversios-google-analytics');
262
+ $links[] = '<a href="' . get_admin_url(null, $setting_url) . '">'.esc_html__("Settings","conversios").'</a>';
263
 
264
+ $links[] = '<a href="'.esc_url_raw("https://wordpress.org/plugins/enhanced-e-commerce-for-woocommerce-store/#faq").'" target="_blank">'.esc_html__("FAQ","conversios").'</a>';
265
+ $links[] = '<a href="'.esc_url_raw("https://conversios.io/help-center/Installation-Manual.pdf").'" target="_blank">'.esc_html__("Documentation","conversios").'</a>';
266
+ $links[] = '<a href="'.esc_url_raw("https://conversios.io/pricings/?utm_source=EE+Plugin+User+Interface&utm_medium=Plugins+Listing+Page+Upgrade+to+Premium&utm_campaign=Upsell+at+Conversios").'" target="_blank"><b>'.esc_html__("Upgrade to Premium","conversios").'</b></a>';
267
  $links['deactivate'] = $deactivate_link;
268
  return $links;
269
  }
275
 
276
  public function check_dependency(){
277
  if ( function_exists('run_actionable_google_analytics')) {
278
+ printf('<div class="error"><p><strong>%s</strong>%s</p></div>', esc_html__("Note: ","conversios"),esc_html__("It seems Actionable Google Analytics Plugin is active on your store. Kindly deactivate it in order to avoid data duplication in GA.","conversios"));
279
  die();
280
  }
281
  }
includes/class-tvc-register-scripts.php CHANGED
@@ -14,7 +14,7 @@ if ( ! class_exists( 'TVC_Register_Scripts' ) ) :
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
  }
@@ -26,7 +26,7 @@ if ( ! class_exists( 'TVC_Register_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
  }
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(sanitize_text_field($_GET['page']), 'conversios') !== false) {
18
  add_action( 'admin_enqueue_scripts', array( $this, 'tvc_register_required_options_page_scripts' ) );
19
  }
20
  }
26
  // enqueue notice handling script
27
  ?>
28
  <script>
29
+ var tvc_ajax_url = '<?php echo esc_js(admin_url( 'admin-ajax.php' )); ?>';
30
  </script>
31
  <?php
32
  }
includes/data/class-tvc-ajax-file.php CHANGED
@@ -45,7 +45,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
45
  // barch size for inser data in DB
46
  $product_db_batch_size = 100;
47
  // barch size for inser product in GMC
48
- $product_batch_size = 50;
49
  if(!class_exists('CustomApi')){
50
  include(ENHANCAD_PLUGIN_DIR . 'includes/setup/CustomApi.php');
51
  }
@@ -63,29 +63,28 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
63
  $sync_progressive_data = isset($_POST['sync_progressive_data'])?$_POST['sync_progressive_data']:"";
64
  $sync_progressive_data = $sync_progressive_data;
65
 
66
- $sync_produt = isset($sync_progressive_data['sync_produt'])?$sync_progressive_data['sync_produt']:"";
67
  $sync_produt = sanitize_text_field($sync_produt);
68
 
69
- $sync_step = isset($sync_progressive_data['sync_step'])?$sync_progressive_data['sync_step']:"1";
70
  $sync_step = sanitize_text_field($sync_step);
71
 
72
- $total_product =isset($sync_progressive_data['total_product'])?$sync_progressive_data['total_product']:"0";
73
  $total_product = sanitize_text_field($total_product);
74
 
75
- $last_sync_product_id =isset($sync_progressive_data['last_sync_product_id'])?$sync_progressive_data['last_sync_product_id']:"";
76
  $last_sync_product_id = sanitize_text_field($last_sync_product_id);
77
 
78
- $skip_products =isset($sync_progressive_data['skip_products'])?$sync_progressive_data['skip_products']:"0";
79
  $skip_products = sanitize_text_field($skip_products);
80
 
81
  //print_r($_POST);
82
- $merchant_id = esc_attr(isset($_POST['tvc_data'])?$_POST['tvc_data']:"");
83
- $account_id = esc_attr(isset($_POST['account_id'])?$_POST['account_id']:"");
84
- $customer_id = esc_attr(isset($_POST['customer_id'])?$_POST['customer_id']:"");
85
- $subscription_id = esc_attr(isset($_POST['subscription_id'])?$_POST['subscription_id']:"");
86
  $data = isset($_POST['tvc_data'])?$_POST['tvc_data']:"";
87
 
88
-
89
  if( $sync_progressive_data == "" && $TVC_Admin_DB_Helper->tvc_row_count("ee_prouct_pre_sync_data") > 0 ){
90
  $TVC_Admin_DB_Helper->tvc_safe_truncate_table($prouct_pre_sync_table);
91
  }
@@ -123,14 +122,14 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
123
  }
124
  }
125
  //add/update data in defoult profile
126
- $profile_data = array("profile_title"=>"Default","g_attribute_mapping"=>json_encode($mappedAttrs),"update_date"=>date('Y-m-d'));
127
  if($TVC_Admin_DB_Helper->tvc_row_count("ee_product_sync_profile") ==0){
128
  $TVC_Admin_DB_Helper->tvc_add_row("ee_product_sync_profile",$profile_data);
129
  }else{
130
  $TVC_Admin_DB_Helper->tvc_update_row("ee_product_sync_profile",$profile_data,array("id"=>1));
131
  }
132
- update_option("ee_prod_mapped_cats", serialize($mappedCatsDB));
133
- update_option("ee_prod_mapped_attrs", serialize($mappedAttrs));
134
 
135
  /*
136
  * start product add in DB
@@ -167,11 +166,11 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
167
  'g_cat_id'=>$mappedCat,
168
  'product_sync_profile_id'=> 1,
169
  'update_date'=>date('Y-m-d')
170
- );
171
- array_push( $values, $postvalue->ID, $mc_key, $mappedCat, 1, date('Y-m-d') );
172
  $place_holders[] = "('%d', '%d', '%d','%d', '%s')";
173
  if($batch_count >= $product_db_batch_size){
174
- $query = "INSERT INTO $prouct_pre_sync_table (w_product_id, w_cat_id, g_cat_id, product_sync_profile_id, update_date) VALUES ";
175
  $query .= implode( ', ', $place_holders );
176
  $wpdb->query($wpdb->prepare( "$query", $values ));
177
  $batch_count = 0;
@@ -185,7 +184,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
185
  * add last batch data in DB
186
  */
187
  if($batch_count > 0){
188
- $query = "INSERT INTO $prouct_pre_sync_table (w_product_id, w_cat_id, g_cat_id, product_sync_profile_id, update_date) VALUES ";
189
  $query .= implode( ', ', $place_holders );
190
  $wpdb->query($wpdb->prepare( "$query", $values ));
191
  }
@@ -195,7 +194,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
195
  $sync_produt = $total_product;
196
  $sync_produt_p = ($sync_produt*100)/$total_product;
197
  $is_synced_up = ($total_product <= $sync_produt)?true:false;
198
- $sync_message = __("Initiated, products are being synced to Merchant Center.Do not refresh..","conversios");
199
  //step one end
200
  }else if($sync_step == 2){
201
  $rs = $TVCProductSyncHelper->call_batch_wise_sync_product($last_sync_product_id, $product_batch_size);
@@ -209,13 +208,13 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
209
  $last_sync_product_id = (isset($rs['last_sync_product_id']))?$rs['last_sync_product_id']:0;
210
  $sync_produt_p = ($sync_produt*100)/$total_product;
211
  $is_synced_up = ($total_product <= $sync_produt)?true:false;
212
- $sync_message = __("Initiated, products are being synced to Merchant Center.Do not refresh..","conversios");
213
  if($total_product <= $sync_produt){
214
  $customObj->setGmcCategoryMapping($catMapRequest);
215
  $customObj->setGmcAttributeMapping($attrMapRequest);
216
  $TVC_Admin_Auto_Product_sync_Helper = new TVC_Admin_Auto_Product_sync_Helper();
217
  $TVC_Admin_Auto_Product_sync_Helper->update_last_sync_in_db();
218
- $sync_message = __("Initiated, products are being synced to Merchant Center.Do not refresh..","conversios");
219
  $TVC_Admin_DB_Helper->tvc_safe_truncate_table($prouct_pre_sync_table);
220
  }
221
  }
@@ -239,8 +238,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
239
  //active licence key
240
  public function tvc_call_active_licence(){
241
  if ( is_admin() ) {
242
- $licence_key = isset($_POST['licence_key'])?$_POST['licence_key']:"";
243
- $licence_key = sanitize_text_field($licence_key);
244
  $TVC_Admin_Helper = new TVC_Admin_Helper();
245
  $subscription_id = $TVC_Admin_Helper->get_subscriptionId();
246
  if($subscription_id!="" && $licence_key != ""){
@@ -248,9 +246,9 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
248
 
249
  if($response->error== false){
250
  //$key, $html, $title = null, $link = null, $link_title = null, $overwrite= false
251
- $TVC_Admin_Helper->add_ee_msg_nofification("active_licence_key", __("Your plan is now successfully activated.","conversios"), __("Congratulations!!","conversios"), "", "", true);
252
  $TVC_Admin_Helper->update_subscription_details_api_to_db();
253
- echo json_encode(array('error' => false, "is_connect"=>true, 'message' => __("The licence key has been activated.","conversios") ));
254
  }else{
255
  echo json_encode(array('error' => true, "is_connect"=>true, 'message' => $response->message));
256
  }
@@ -260,7 +258,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
260
  $TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
261
  echo json_encode(array('error' => true, "is_connect"=>false, 'message' => ""));
262
  }else{
263
- echo json_encode(array('error' => true, "is_connect"=>false, 'message' => __("Licence key is required.","conversios")));
264
  }
265
  }
266
  exit;
@@ -268,7 +266,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
268
 
269
  public function tvc_call_notification_dismiss(){
270
  if($this->safe_ajax_call(filter_input(INPUT_POST, 'TVCNonce'), 'tvc_call_notification_dismiss-nonce')){
271
- $ee_dismiss_id = isset($_POST['data']['ee_dismiss_id'])?$_POST['data']['ee_dismiss_id']:"";
272
  $ee_dismiss_id = sanitize_text_field($ee_dismiss_id);
273
  if($ee_dismiss_id != ""){
274
  $TVC_Admin_Helper = new TVC_Admin_Helper();
@@ -286,7 +284,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
286
  }
287
  public function tvc_call_notice_dismiss(){
288
  if($this->safe_ajax_call(filter_input(INPUT_POST, 'apiNoticDismissNonce'), 'tvc_call_notice_dismiss-nonce')){
289
- $ee_notice_dismiss_id = isset($_POST['data']['ee_notice_dismiss_id'])?$_POST['data']['ee_notice_dismiss_id']:"";
290
  $ee_notice_dismiss_id = sanitize_text_field($ee_notice_dismiss_id);
291
  if($ee_notice_dismiss_id != ""){
292
  $TVC_Admin_Helper = new TVC_Admin_Helper();
@@ -302,10 +300,10 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
302
  if($this->safe_ajax_call(filter_input(INPUT_POST, 'apiSyncupNonce'), 'tvc_call_api_sync-nonce')){
303
  $TVC_Admin_Helper = new TVC_Admin_Helper();
304
  $api_rs = $TVC_Admin_Helper->set_update_api_to_db();
305
- if(isset($api_rs['error']) && isset($api_rs['message']) && $api_rs['message']){
306
  echo json_encode($api_rs);
307
  }else{
308
- echo json_encode(array('error' => true, 'message' => __("Please try after some time.","conversios")));
309
  }
310
  exit;
311
  }
@@ -317,9 +315,9 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
317
  $tvc_rs =[];
318
  $tvc_rs = $TVC_Admin_Helper->call_site_verified();
319
  if(isset($tvc_rs['error']) && $tvc_rs['error'] == 1){
320
- echo json_encode(array('status' => 'error', 'message' => $tvc_rs['msg']));
321
  }else{
322
- echo json_encode(array('status' => 'success', 'message' => $tvc_rs['msg']));
323
  }
324
  exit;
325
  }
@@ -330,9 +328,9 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
330
  $TVC_Admin_Helper = new TVC_Admin_Helper();
331
  $tvc_rs = $TVC_Admin_Helper->call_domain_claim();
332
  if(isset($tvc_rs['error']) && $tvc_rs['error'] == 1){
333
- echo json_encode(array('status' => 'error', 'message' => $tvc_rs['msg']));
334
  }else{
335
- echo json_encode(array('status' => 'success', 'message' => $tvc_rs['msg']));
336
  }
337
  exit;
338
  }
@@ -341,13 +339,10 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
341
  public function get_tvc_access_token(){
342
  if(!empty($this->access_token)){
343
  return $this->access_token;
344
- }else if(isset($_SESSION['access_token']) && $_SESSION['access_token']){
345
- $this->access_token = $_SESSION['access_token'];
346
- return $this->access_token;
347
  }else{
348
  $TVC_Admin_Helper = new TVC_Admin_Helper();
349
  $google_detail = $TVC_Admin_Helper->get_ee_options_data();
350
- $this->access_token = $google_detail['setting']->access_token;
351
  return $this->access_token;
352
  }
353
  }
@@ -355,13 +350,10 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
355
  public function get_tvc_refresh_token(){
356
  if(!empty($this->refresh_token)){
357
  return $this->refresh_token;
358
- }else if(isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']){
359
- $this->refresh_token = $_SESSION['refresh_token'];
360
- return $this->refresh_token;
361
  }else{
362
  $TVC_Admin_Helper = new TVC_Admin_Helper();
363
  $google_detail = $TVC_Admin_Helper->get_ee_options_data();
364
- $this->refresh_token = $google_detail['setting']->refresh_token;
365
  return $this->refresh_token;
366
  }
367
  }
@@ -464,7 +456,7 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
464
  $message = $response->message;
465
  echo json_encode(['status' => 'success', 'message' => $message]);
466
  }else{
467
- $message = is_array($response->errors) ? $response->errors[0] : __("Face some unprocessable entity","conversios");
468
  echo json_encode(['status' => 'error', 'message' => $message]);
469
  }
470
  }
@@ -579,168 +571,6 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
579
  exit;
580
  }
581
 
582
- /**
583
- * create product batch for product sync up
584
- */
585
- /*==public function tvcajax_custom_metrics_dimension(){
586
- // make sure this call is legal
587
- if($this->safe_ajax_call(filter_input(INPUT_POST, 'customMetricsDimensionNonce'), 'tvcajax-custom-metrics-dimension-nonce')){
588
-
589
- if(!class_exists('CustomApi')){
590
- include(ENHANCAD_PLUGIN_DIR . 'includes/setup/CustomApi.php');
591
- }
592
- $customObj = new CustomApi();
593
-
594
- $accountId = sanitize_text_field(filter_input(INPUT_POST, 'accountId'));
595
- // $accountId = '184918792';
596
- $webPropertyId = sanitize_text_field(filter_input(INPUT_POST, 'webPropertyId'));
597
- // $webPropertyId = 'UA-184918792-5';
598
- $subscriptionId = sanitize_text_field(filter_input(INPUT_POST, 'subscriptionId'));
599
- $data = esc_attr(filter_input(INPUT_POST, 'data'));
600
- parse_str($data, $formArray);
601
- // Only for print array
602
-
603
- $customDimension = [];
604
- $customMetrics = [];
605
- $dimensions = [];
606
- $metrics = [];
607
-
608
- for($i = 1; $i <= 12; $i++){
609
- $dimension['id'] = "";
610
- $dimension['index'] = $formArray['did-' . $i];
611
- $dimension['active'] = true;
612
- $dimension['kind'] = "";
613
- $dimension['name'] = $formArray['dname-' . $i];
614
- $dimension['scope'] = $formArray['dscope-' . $i];
615
- $dimension['created'] = "";
616
- $dimension['updated'] = "";
617
- $dimension['self_link'] = "";
618
- $dimension['parent_link']['href'] = "";
619
- $dimension['parent_link']['parent_link_type'] = "";
620
- $dimensions[] = $dimension;
621
- }
622
-
623
- for($i = 1; $i <= 7; $i++){
624
- $metric['id'] = "";
625
- $metric['index'] = $formArray['mid-' . $i];
626
- $metric['active'] = true;
627
- $metric['kind'] = "";
628
- $metric['name'] = $formArray['mname-' . $i];
629
- $metric['scope'] = $formArray['mscope-' . $i];
630
- $metric['created'] = "";
631
- $metric['updated'] = "";
632
- $metric['self_link'] = "";
633
- $metric['max_value'] = "";
634
- $metric['min_value'] = "";
635
- $metric['type'] = "INTEGER";
636
- $metric['parent_link']['href'] = "";
637
- $metric['parent_link']['parent_link_type'] = "";
638
- $metrics[] = $metric;
639
- }
640
-
641
- if(!empty($dimensions)){
642
- $dimenRequest = [];
643
- $dimenRequest['account_id'] = $accountId;
644
- $dimenRequest['web_property_id'] = $webPropertyId;
645
- $dimenRequest['subscription_id'] = $subscriptionId;
646
- $dimenRequest['data'] = $dimensions;
647
- $dimenResponse = $customObj->createCustomDimensions($dimenRequest);
648
- }
649
- if(!empty($metrics)){
650
- $metrRequest = [];
651
- $metrRequest['account_id'] = $accountId;
652
- $metrRequest['web_property_id'] = $webPropertyId;
653
- $metrRequest['subscription_id'] = $subscriptionId;
654
- $metrRequest['data'] = $metrics;
655
- $metrResponse = $customObj->createCustomMetrics($metrRequest);
656
- }
657
-
658
-
659
- // Retrieve information
660
- /* $response_code = wp_remote_retrieve_response_code($request);
661
- $response_message = wp_remote_retrieve_response_message($request);
662
- $response_body = json_decode(wp_remote_retrieve_body($request)); */
663
-
664
-
665
- /*==
666
-
667
- if((isset($dimenResponse->error) && $dimenResponse->error == '' && isset($metrResponse->error) && $metrResponse->error == '')){
668
- echo json_encode(['status' => 'success']);
669
- // return new WP_REST_Response(
670
- // array(
671
- // 'status' => $response_code,
672
- // 'message' => $response_message,
673
- // 'data' => $response_body->data
674
- // )
675
- // );
676
- }else{
677
- $metrError = '';
678
- $dimenError = '';
679
- $message = NULL;
680
- if($dimenResponse->errors){
681
-
682
- $dimenError = $dimenResponse->errors[0];
683
- $message = str_replace('this entity', 'dimensions ', $dimenError);
684
- }
685
- if($metrResponse->errors){
686
- $metrError = str_replace('this entity', 'metrics ', $metrResponse->errors[0]);
687
- $message = is_null($message) ? $metrError : $message . ' ' . $metrError;
688
- }
689
- echo json_encode(['status' => 'error', 'message' => $message]);
690
- }
691
- }
692
-
693
- // IMPORTANT: don't forget to exit
694
- exit;
695
- }==*/
696
-
697
- /*public function tvcajax_store_time_taken(){
698
- // make sure this call is legal
699
- if($this->safe_ajax_call(filter_input(INPUT_POST, 'campaignCategoryListsNonce'), 'tvcajax-store-time-taken-nonce')){
700
- $ee_options_data = unserialize(get_option('ee_options'));
701
- if(isset($ee_options_data['subscription_id'])) {
702
- $ee_subscription_id = $ee_options_data['subscription_id'];
703
- } else {
704
- $ee_subscription_id = null;
705
- }
706
- $url = $this->apiDomain.'/customer-subscriptions/update-setup-time';
707
- $data = [
708
- 'subscription_id' => $ee_subscription_id,
709
- 'setup_start_time' => date('Y-m-d H:i:s'),
710
- ];
711
- $args = array(
712
- 'headers' => array(
713
- 'Authorization' => "Bearer MTIzNA==",
714
- 'Content-Type' => 'application/json'
715
- ),
716
- 'body' => wp_json_encode($data)
717
- );
718
- // Send remote request
719
- $request = wp_remote_post($url, $args);
720
- // Retrieve information
721
- $response_code = wp_remote_retrieve_response_code($request);
722
- $response_message = wp_remote_retrieve_response_message($request);
723
- $response_body = json_decode(wp_remote_retrieve_body($request));
724
- if((isset($response_body->error) && $response_body->error == '')){
725
- echo json_encode($response_body->data);
726
- // return new WP_REST_Response(
727
- // array(
728
- // 'status' => $response_code,
729
- // 'message' => $response_message,
730
- // 'data' => $response_body->data
731
- // )
732
- // );
733
- }else{
734
- echo json_encode([]);
735
- // return new WP_Error($response_code, $response_message, $response_body);
736
- }
737
-
738
- // echo json_encode( $categories );
739
- }
740
-
741
- // IMPORTANT: don't forget to exit
742
- exit;
743
- }*/
744
  public function generateAccessToken($access_token, $refresh_token) {
745
  $request = "https://www.googleapis.com/oauth2/v1/tokeninfo?"
746
  . "access_token=" . $access_token;
@@ -758,8 +588,8 @@ class TVC_Ajax_File extends TVC_Ajax_Calls {
758
  $credentials = $str ? json_decode($str, true) : [];
759
  $url = 'https://www.googleapis.com/oauth2/v4/token';
760
  $header = array("content-type: application/json");
761
- $clientId = $credentials['web']['client_id'];
762
- $clientSecret = $credentials['web']['client_secret'];
763
  $refreshToken = $refresh_token;
764
  $data = [
765
  "grant_type" => 'refresh_token',
45
  // barch size for inser data in DB
46
  $product_db_batch_size = 100;
47
  // barch size for inser product in GMC
48
+ $product_batch_size = 25;
49
  if(!class_exists('CustomApi')){
50
  include(ENHANCAD_PLUGIN_DIR . 'includes/setup/CustomApi.php');
51
  }
63
  $sync_progressive_data = isset($_POST['sync_progressive_data'])?$_POST['sync_progressive_data']:"";
64
  $sync_progressive_data = $sync_progressive_data;
65
 
66
+ $sync_produt = isset($sync_progressive_data['sync_produt'])?sanitize_text_field($sync_progressive_data['sync_produt']):"";
67
  $sync_produt = sanitize_text_field($sync_produt);
68
 
69
+ $sync_step = isset($sync_progressive_data['sync_step'])?sanitize_text_field($sync_progressive_data['sync_step']):"1";
70
  $sync_step = sanitize_text_field($sync_step);
71
 
72
+ $total_product =isset($sync_progressive_data['total_product'])?sanitize_text_field($sync_progressive_data['total_product']):"0";
73
  $total_product = sanitize_text_field($total_product);
74
 
75
+ $last_sync_product_id =isset($sync_progressive_data['last_sync_product_id'])?sanitize_text_field($sync_progressive_data['last_sync_product_id']):"";
76
  $last_sync_product_id = sanitize_text_field($last_sync_product_id);
77
 
78
+ $skip_products =isset($sync_progressive_data['skip_products'])?sanitize_text_field($sync_progressive_data['skip_products']):"0";
79
  $skip_products = sanitize_text_field($skip_products);
80
 
81
  //print_r($_POST);
82
+ //$merchant_id = esc_attr(isset($_POST['tvc_data'])?$_POST['tvc_data']:"");
83
+ $account_id = esc_attr(isset($_POST['account_id'])?sanitize_text_field($_POST['account_id']):"");
84
+ $customer_id = esc_attr(isset($_POST['customer_id'])?sanitize_text_field($_POST['customer_id']):"");
85
+ $subscription_id = esc_attr(isset($_POST['subscription_id'])?sanitize_text_field($_POST['subscription_id']):"");
86
  $data = isset($_POST['tvc_data'])?$_POST['tvc_data']:"";
87
 
 
88
  if( $sync_progressive_data == "" && $TVC_Admin_DB_Helper->tvc_row_count("ee_prouct_pre_sync_data") > 0 ){
89
  $TVC_Admin_DB_Helper->tvc_safe_truncate_table($prouct_pre_sync_table);
90
  }
122
  }
123
  }
124
  //add/update data in defoult profile
125
+ $profile_data = array("profile_title"=>esc_sql("Default"),"g_attribute_mapping"=>json_encode($mappedAttrs),"update_date"=>date('Y-m-d'));
126
  if($TVC_Admin_DB_Helper->tvc_row_count("ee_product_sync_profile") ==0){
127
  $TVC_Admin_DB_Helper->tvc_add_row("ee_product_sync_profile",$profile_data);
128
  }else{
129
  $TVC_Admin_DB_Helper->tvc_update_row("ee_product_sync_profile",$profile_data,array("id"=>1));
130
  }
131
+ update_option("ee_prod_mapped_cats", serialize(sanitize_option("ee_prod_mapped_cats",$mappedCatsDB)));
132
+ update_option("ee_prod_mapped_attrs", serialize(sanitize_option("ee_prod_mapped_attrs",$mappedAttrs)));
133
 
134
  /*
135
  * start product add in DB
166
  'g_cat_id'=>$mappedCat,
167
  'product_sync_profile_id'=> 1,
168
  'update_date'=>date('Y-m-d')
169
+ );
170
+ array_push( $values, esc_sql($postvalue->ID), esc_sql($mc_key), esc_sql($mappedCat), 1, date('Y-m-d') );
171
  $place_holders[] = "('%d', '%d', '%d','%d', '%s')";
172
  if($batch_count >= $product_db_batch_size){
173
+ $query = "INSERT INTO ".esc_sql($prouct_pre_sync_table)." (w_product_id, w_cat_id, g_cat_id, product_sync_profile_id, update_date) VALUES ";
174
  $query .= implode( ', ', $place_holders );
175
  $wpdb->query($wpdb->prepare( "$query", $values ));
176
  $batch_count = 0;
184
  * add last batch data in DB
185
  */
186
  if($batch_count > 0){
187
+ $query = "INSERT INTO ".esc_sql($prouct_pre_sync_table)." (w_product_id, w_cat_id, g_cat_id, product_sync_profile_id, update_date) VALUES ";
188
  $query .= implode( ', ', $place_holders );
189
  $wpdb->query($wpdb->prepare( "$query", $values ));
190
  }
194
  $sync_produt = $total_product;
195
  $sync_produt_p = ($sync_produt*100)/$total_product;
196
  $is_synced_up = ($total_product <= $sync_produt)?true:false;
197
+ $sync_message = esc_html__("Initiated, products are being synced to Merchant Center.Do not refresh..","conversios");
198
  //step one end
199
  }else if($sync_step == 2){
200
  $rs = $TVCProductSyncHelper->call_batch_wise_sync_product($last_sync_product_id, $product_batch_size);
208
  $last_sync_product_id = (isset($rs['last_sync_product_id']))?$rs['last_sync_product_id']:0;
209
  $sync_produt_p = ($sync_produt*100)/$total_product;
210
  $is_synced_up = ($total_product <= $sync_produt)?true:false;
211
+ $sync_message = esc_html__("Initiated, products are being synced to Merchant Center.Do not refresh..","conversios");
212
  if($total_product <= $sync_produt){
213
  $customObj->setGmcCategoryMapping($catMapRequest);
214
  $customObj->setGmcAttributeMapping($attrMapRequest);
215
  $TVC_Admin_Auto_Product_sync_Helper = new TVC_Admin_Auto_Product_sync_Helper();
216
  $TVC_Admin_Auto_Product_sync_Helper->update_last_sync_in_db();
217
+ $sync_message = esc_html__("Initiated, products are being synced to Merchant Center.Do not refresh..","conversios");
218
  $TVC_Admin_DB_Helper->tvc_safe_truncate_table($prouct_pre_sync_table);
219
  }
220
  }
238
  //active licence key
239
  public function tvc_call_active_licence(){
240
  if ( is_admin() ) {
241
+ $licence_key = isset($_POST['licence_key'])?sanitize_text_field($_POST['licence_key']):"";
 
242
  $TVC_Admin_Helper = new TVC_Admin_Helper();
243
  $subscription_id = $TVC_Admin_Helper->get_subscriptionId();
244
  if($subscription_id!="" && $licence_key != ""){
246
 
247
  if($response->error== false){
248
  //$key, $html, $title = null, $link = null, $link_title = null, $overwrite= false
249
+ $TVC_Admin_Helper->add_ee_msg_nofification("active_licence_key", esc_html__("Your plan is now successfully activated.","conversios"), esc_html__("Congratulations!!","conversios"), "", "", true);
250
  $TVC_Admin_Helper->update_subscription_details_api_to_db();
251
+ echo json_encode(array('error' => false, "is_connect"=>true, 'message' => esc_html__("The licence key has been activated.","conversios") ));
252
  }else{
253
  echo json_encode(array('error' => true, "is_connect"=>true, 'message' => $response->message));
254
  }
258
  $TVC_Admin_Helper->set_ee_additional_data($ee_additional_data);
259
  echo json_encode(array('error' => true, "is_connect"=>false, 'message' => ""));
260
  }else{
261
+ echo json_encode(array('error' => true, "is_connect"=>false, 'message' => esc_html__("Licence key is required.","conversios")));
262
  }
263
  }
264
  exit;
266
 
267
  public function tvc_call_notification_dismiss(){
268
  if($this->safe_ajax_call(filter_input(INPUT_POST, 'TVCNonce'), 'tvc_call_notification_dismiss-nonce')){
269
+ $ee_dismiss_id = isset($_POST['data']['ee_dismiss_id'])?sanitize_text_field($_POST['data']['ee_dismiss_id']):"";
270
  $ee_dismiss_id = sanitize_text_field($ee_dismiss_id);
271
  if($ee_dismiss_id != ""){
272
  $TVC_Admin_Helper = new TVC_Admin_Helper();
284
  }
285
  public function tvc_call_notice_dismiss(){
286
  if($this->safe_ajax_call(filter_input(INPUT_POST, 'apiNoticDismissNonce'), 'tvc_call_notice_dismiss-nonce')){
287
+ $ee_notice_dismiss_id = isset($_POST['data']['ee_notice_dismiss_id'])?sanitize_text_field($_POST['data']['ee_notice_dismiss_id']):"";
288
  $ee_notice_dismiss_id = sanitize_text_field($ee_notice_dismiss_id);
289
  if($ee_notice_dismiss_id != ""){
290
  $TVC_Admin_Helper = new TVC_Admin_Helper();
300
  if($this->safe_ajax_call(filter_input(INPUT_POST, 'apiSyncupNonce'), 'tvc_call_api_sync-nonce')){
301
  $TVC_Admin_Helper = new TVC_Admin_Helper();
302
  $api_rs = $TVC_Admin_Helper->set_update_api_to_db();
303
+ if(isset($api_rs['error']) && isset($api_rs['message']) && sanitize_text_field($api_rs['message'])){
304
  echo json_encode($api_rs);
305
  }else{
306
+ echo json_encode(array('error' => true, 'message' => esc_html__("Please try after some time.","conversios")));
307
  }
308
  exit;
309
  }
315
  $tvc_rs =[];
316
  $tvc_rs = $TVC_Admin_Helper->call_site_verified();
317
  if(isset($tvc_rs['error']) && $tvc_rs['error'] == 1){
318
+ echo json_encode(array('status' => 'error', 'message' => sanitize_text_field($tvc_rs['msg'])));
319
  }else{
320
+ echo json_encode(array('status' => 'success', 'message' => sanitize_text_field($tvc_rs['msg'])));
321
  }
322
  exit;
323
  }
328
  $TVC_Admin_Helper = new TVC_Admin_Helper();
329
  $tvc_rs = $TVC_Admin_Helper->call_domain_claim();
330
  if(isset($tvc_rs['error']) && $tvc_rs['error'] == 1){
331
+ echo json_encode(array('status' => 'error', 'message' => sanitize_text_field($tvc_rs['msg'])));
332
  }else{
333
+ echo json_encode(array('status' => 'success', 'message' => sanitize_text_field($tvc_rs['msg'])));
334
  }
335
  exit;
336
  }
339
  public function get_tvc_access_token(){
340
  if(!empty($this->access_token)){
341
  return $this->access_token;
 
 
 
342
  }else{
343
  $TVC_Admin_Helper = new TVC_Admin_Helper();
344
  $google_detail = $TVC_Admin_Helper->get_ee_options_data();
345
+ $this->access_token = base64_decode(sanitize_text_field($google_detail['setting']->access_token));
346
  return $this->access_token;
347
  }
348
  }
350
  public function get_tvc_refresh_token(){
351
  if(!empty($this->refresh_token)){
352
  return $this->refresh_token;
 
 
 
353
  }else{
354
  $TVC_Admin_Helper = new TVC_Admin_Helper();
355
  $google_detail = $TVC_Admin_Helper->get_ee_options_data();
356
+ $this->refresh_token = base64_decode(sanitize_text_field($google_detail['setting']->refresh_token));
357
  return $this->refresh_token;
358
  }
359
  }
456
  $message = $response->message;
457
  echo json_encode(['status' => 'success', 'message' => $message]);
458
  }else{
459
+ $message = is_array($response->errors) ? $response->errors[0] : esc_html__("Face some unprocessable entity","conversios");
460
  echo json_encode(['status' => 'error', 'message' => $message]);
461
  }
462
  }
571
  exit;
572
  }
573
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  public function generateAccessToken($access_token, $refresh_token) {
575
  $request = "https://www.googleapis.com/oauth2/v1/tokeninfo?"
576
  . "access_token=" . $access_token;
588
  $credentials = $str ? json_decode($str, true) : [];
589
  $url = 'https://www.googleapis.com/oauth2/v4/token';
590
  $header = array("content-type: application/json");
591
+ $clientId = sanitize_text_field($credentials['web']['client_id']);
592
+ $clientSecret = sanitize_text_field($credentials['web']['client_secret']);
593
  $refreshToken = $refresh_token;
594
  $data = [
595
  "grant_type" => 'refresh_token',
includes/setup/CustomApi.php CHANGED
@@ -11,13 +11,12 @@ class CustomApi{
11
  public function get_tvc_access_token(){
12
  if(!empty($this->access_token)){
13
  return $this->access_token;
14
- }else if(isset($_SESSION['access_token']) && $_SESSION['access_token']){
15
- $this->access_token = $_SESSION['access_token'];
16
- return $this->access_token;
17
  }else{
18
  $TVC_Admin_Helper = new TVC_Admin_Helper();
19
- $google_detail = $TVC_Admin_Helper->get_ee_options_data();
20
- $this->access_token = (isset($google_detail['setting']->access_token))?$google_detail['setting']->access_token:"";
 
 
21
  return $this->access_token;
22
  }
23
  }
@@ -25,13 +24,12 @@ class CustomApi{
25
  public function get_tvc_refresh_token(){
26
  if(!empty($this->refresh_token)){
27
  return $this->refresh_token;
28
- }else if(isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']){
29
- $this->refresh_token = $_SESSION['refresh_token'];
30
- return $this->refresh_token;
31
  }else{
32
  $TVC_Admin_Helper = new TVC_Admin_Helper();
33
- $google_detail = $TVC_Admin_Helper->get_ee_options_data();
34
- $this->refresh_token = (isset($google_detail['setting']->refresh_token))?$google_detail['setting']->refresh_token:"";
 
 
35
  return $this->refresh_token;
36
  }
37
  }
@@ -43,60 +41,6 @@ class CustomApi{
43
  return false;
44
  }
45
  }
46
-
47
- public function getFeatureList() {
48
- try {
49
-
50
- // $this->header = array("Authorization: Bearer MTIzNA==", "content-type: application/json");
51
- // $this->curl_url = "http://127.0.0.1:8000/api/plans/feature-list";
52
- // $postData = json_encode(["plan_id" => 1]);
53
- // $ch = curl_init();
54
- // curl_setopt_array($ch, array(
55
- // CURLOPT_URL => esc_url($this->curl_url),
56
- // CURLOPT_RETURNTRANSFER => true,
57
- // CURLOPT_TIMEOUT => 20,
58
- // CURLOPT_HTTPHEADER => $this->header,
59
- // CURLOPT_POSTFIELDS => $postData
60
- // ));
61
- // $this->response = curl_exec($ch);
62
- // $this->response = json_decode($this->response);
63
- // die;
64
-
65
- $url = $this->apiDomain . '/plans/feature-list';
66
-
67
- $data = [
68
- 'plan_id' => 1,
69
- ];
70
- $args = array(
71
- 'headers' => array(
72
- 'Authorization' => "Bearer $this->token",
73
- 'Content-Type' => 'application/json'
74
- ),
75
- 'body' => wp_json_encode($data)
76
- );
77
- // Send remote request
78
- $request = wp_remote_post($url, $args);
79
- // Retrieve information
80
- $response_code = wp_remote_retrieve_response_code($request);
81
- $response_message = wp_remote_retrieve_response_message($request);
82
- $response_body = json_decode(wp_remote_retrieve_body($request));
83
-
84
- if ((isset($response_body->error) && $response_body->error == '')) {
85
-
86
- return new WP_REST_Response(
87
- array(
88
- 'status' => $response_code,
89
- 'message' => $response_message,
90
- 'data' => $response_body->data
91
- )
92
- );
93
- } else {
94
- return new WP_Error($response_code, $response_message, $response_body);
95
- }
96
- } catch (Exception $e) {
97
- return $e->getMessage();
98
- }
99
- }
100
 
101
  public function getGoogleAnalyticDetail($subscription_id = null) {
102
  try {
@@ -107,68 +51,46 @@ class CustomApi{
107
  );
108
  $ee_options_data = unserialize(get_option('ee_options'));
109
  if($subscription_id == null && isset($ee_options_data['subscription_id'])) {
110
- $subscription_id = $ee_options_data['subscription_id'];
111
- }
112
- $actual_link = get_site_url();
113
  $data = [
114
- 'subscription_id' => $subscription_id,
115
- 'domain' => $actual_link
116
  ];
117
  $postData = json_encode($data);
118
  $ch = curl_init();
119
  curl_setopt_array($ch, array(
120
- CURLOPT_URL => $url,
121
  CURLOPT_RETURNTRANSFER => true,
122
  CURLOPT_TIMEOUT => 1000,
123
  CURLOPT_HTTPHEADER => $header,
124
  CURLOPT_POSTFIELDS => $postData
125
  ));
126
  $response = curl_exec($ch);
127
- $response_body = json_decode($response);
128
- if((isset($response_body->error) && $response_body->error == '')) {
129
- if ($response_body->data) {
130
- $store_raw_country = get_option('woocommerce_default_country');
131
- // Split the country/state
132
- $split_country = explode(":", $store_raw_country);
133
-
134
- $GLOBALS['tatvicData']['tvc_customer'] = esc_attr($response_body->data->google_ads_id);
135
- $GLOBALS['tatvicData']['tvc_merchant'] = esc_attr($response_body->data->google_merchant_center_id);
136
- $GLOBALS['tatvicData']['tvc_account'] = esc_attr($response_body->data->ua_analytic_account_id);
137
- $GLOBALS['tatvicData']['tvc_subscription'] = esc_attr($response_body->data->id);
138
- $GLOBALS['tatvicData']['tvc_country'] = $split_country[0];
139
- $GLOBALS['tatvicData']['tvc_gmc_id'] = esc_attr($response_body->data->google_merchant_center_id);
140
- $GLOBALS['tatvicData']['tvc_main_merchant_id'] = $response_body->data->merchant_id;
141
- $GLOBALS['tatvicData']['tvc_site_url'] = $response_body->data->site_url;
142
- $GLOBALS['tatvicData']['tvc_track_opt'] = $response_body->data->tracking_option;
143
- $GLOBALS['tatvicData']['access_token'] = $response_body->data->access_token;
144
- $GLOBALS['tatvicData']['refresh_token'] = $response_body->data->refresh_token;
145
- $_SESSION['access_token'] = $response_body->data->access_token;
146
- $_SESSION['refresh_token'] = $response_body->data->refresh_token;
147
- }
148
- $return = new \stdClass();
149
- if (isset($response_body->error) && $response_body->error == '') {
150
  $return->error = false;
151
  $return->data = $response_body->data;
152
  $return->message = $response_body->message;
153
- return $return;
154
  } else {
155
- if (isset($response_body->data)) {
156
- $return->error = false;
157
- $return->data = $response_body->data;
158
- $return->message = $response_body->message;
159
- } else {
160
- $return->error = true;
161
- $return->data = [];
162
- if(isset($response_body->errors->key[0])){
163
- $return->message = $response_body->errors->key[0];
164
- }else{
165
- $return->message = __("Please try after some time.","conversios");
166
- }
167
  }
168
- return $return;
169
  }
170
- //return (object) array( 'status' => $response_code, 'message' => $response_message, 'data' => $response_body->data );
171
- }
 
172
  } catch (Exception $e) {
173
  return $e->getMessage();
174
  }
@@ -188,7 +110,7 @@ class CustomApi{
188
  );
189
 
190
  // Send remote request
191
- $request = wp_remote_post($url, $args);
192
 
193
  // Retrieve information
194
  $response_code = wp_remote_retrieve_response_code($request);
@@ -221,7 +143,7 @@ class CustomApi{
221
  $postData = json_encode($data);
222
  $ch = curl_init();
223
  curl_setopt_array($ch, array(
224
- CURLOPT_URL => esc_url($curl_url),
225
  CURLOPT_RETURNTRANSFER => true,
226
  CURLOPT_TIMEOUT => 1000,
227
  CURLOPT_HTTPHEADER => $header,
@@ -251,14 +173,14 @@ class CustomApi{
251
  );
252
  $curl_url = $this->apiDomain . "/licence/activation";
253
  $postData = [
254
- 'key' => $licence_key,
255
  'domain' => get_site_url(),
256
  'subscription_id'=>esc_attr($subscription_id)
257
  ];
258
  $postData = json_encode($postData);
259
  $ch = curl_init();
260
  curl_setopt_array($ch, array(
261
- CURLOPT_URL => esc_url($curl_url),
262
  CURLOPT_RETURNTRANSFER => true,
263
  CURLOPT_TIMEOUT => 1000,
264
  CURLOPT_HTTPHEADER => $header,
@@ -283,7 +205,7 @@ class CustomApi{
283
  if(isset($response->errors->key[0])){
284
  $return->message = $response->errors->key[0];
285
  }else{
286
- $return->message = __("Check your entered licese key.","conversios");
287
  }
288
 
289
  }
@@ -306,7 +228,7 @@ class CustomApi{
306
  $postData = json_encode($postData);
307
  $ch = curl_init();
308
  curl_setopt_array($ch, array(
309
- CURLOPT_URL => esc_url($curl_url),
310
  CURLOPT_RETURNTRANSFER => true,
311
  CURLOPT_TIMEOUT => 1000,
312
  CURLOPT_HTTPHEADER => $header,
@@ -354,7 +276,7 @@ class CustomApi{
354
  $postData = json_encode($postData);
355
  $ch = curl_init();
356
  curl_setopt_array($ch, array(
357
- CURLOPT_URL => esc_url($curl_url),
358
  CURLOPT_RETURNTRANSFER => true,
359
  CURLOPT_TIMEOUT => 1000,
360
  CURLOPT_HTTPHEADER => $header,
@@ -382,7 +304,7 @@ class CustomApi{
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,
@@ -413,7 +335,7 @@ class CustomApi{
413
  $postData = json_encode($postData);
414
  $ch = curl_init();
415
  curl_setopt_array($ch, array(
416
- CURLOPT_URL => esc_url($curl_url),
417
  CURLOPT_RETURNTRANSFER => true,
418
  CURLOPT_TIMEOUT => 2000,
419
  CURLOPT_HTTPHEADER => $header,
@@ -448,7 +370,7 @@ class CustomApi{
448
  'body' => wp_json_encode($data)
449
  );
450
  // Send remote request
451
- $request = wp_remote_post($url, $args);
452
  // Retrieve information
453
  $response_code = wp_remote_retrieve_response_code($request);
454
  $response_message = wp_remote_retrieve_response_message($request);
@@ -477,7 +399,7 @@ class CustomApi{
477
  );
478
 
479
  // Send remote request
480
- $request = wp_remote_post($url, $args);
481
 
482
  // Retrieve information
483
  $response_code = wp_remote_retrieve_response_code($request);
@@ -500,7 +422,6 @@ class CustomApi{
500
  return $e->getMessage();
501
  }
502
  }
503
-
504
  public function setGmcAttributeMapping($postData) {
505
  try {
506
  $url = $this->apiDomain . '/gmc/gmc-attribute-mapping';
@@ -549,7 +470,7 @@ class CustomApi{
549
  $postData = json_encode($data);
550
  $ch = curl_init();
551
  curl_setopt_array($ch, array(
552
- CURLOPT_URL => esc_url($curl_url),
553
  CURLOPT_RETURNTRANSFER => true,
554
  CURLOPT_TIMEOUT => 10000,
555
  CURLOPT_HTTPHEADER => $header,
@@ -586,7 +507,7 @@ class CustomApi{
586
  $postData = json_encode($postData);
587
  $ch = curl_init();
588
  curl_setopt_array($ch, array(
589
- CURLOPT_URL => esc_url($curl_url),
590
  CURLOPT_RETURNTRANSFER => true,
591
  CURLOPT_TIMEOUT => 1000,
592
  CURLOPT_HTTPHEADER => $header,
@@ -614,82 +535,7 @@ class CustomApi{
614
  return $e->getMessage();
615
  }
616
  }
617
-
618
- public function createCustomDimensions($postData) {
619
- try {
620
-
621
- $url = $this->apiDomain . '/google-analytics/dimensions/insert';
622
-
623
- $args = array(
624
- 'headers' => array(
625
- 'Authorization' => "Bearer $this->token",
626
- 'Content-Type' => 'application/json',
627
- 'AccessToken' => $this->generateAccessToken($this->get_tvc_access_token(), $this->get_tvc_refresh_token())
628
- ),
629
- 'body' => wp_json_encode($postData)
630
- );
631
- // Send remote request
632
- $request = wp_remote_post($url, $args);
633
-
634
- // Retrieve information
635
- $response_code = wp_remote_retrieve_response_code($request);
636
- $response_message = wp_remote_retrieve_response_message($request);
637
- $response_body = json_decode(wp_remote_retrieve_body($request));
638
-
639
- if ((isset($response_body->error) && $response_body->error == '')) {
640
- return new WP_REST_Response(
641
- array(
642
- 'status' => $response_code,
643
- 'message' => $response_message,
644
- 'data' => $response_body->data
645
- )
646
- );
647
- } else {
648
- //return new WP_Error($response_code, $response_message, $response_body);
649
- return $response_body;
650
- }
651
- } catch (Exception $e) {
652
- return $e->getMessage();
653
- }
654
- }
655
-
656
- public function createCustomMetrics($postData) {
657
- try {
658
- $url = $this->apiDomain . '/google-analytics/metrics/insert';
659
-
660
- $args = array(
661
- 'headers' => array(
662
- 'Authorization' => "Bearer $this->token",
663
- 'Content-Type' => 'application/json',
664
- 'AccessToken' => $this->generateAccessToken($this->get_tvc_access_token(), $this->get_tvc_refresh_token())
665
- ),
666
- 'body' => wp_json_encode($postData)
667
- );
668
-
669
- // Send remote request
670
-
671
- $request = wp_remote_post($url, $args);
672
- // Retrieve information
673
- $response_code = wp_remote_retrieve_response_code($request);
674
- $response_message = wp_remote_retrieve_response_message($request);
675
- $response_body = json_decode(wp_remote_retrieve_body($request));
676
-
677
- if ((isset($response_body->error) && $response_body->error == '')) {
678
- return new WP_REST_Response(
679
- array(
680
- 'status' => $response_code,
681
- 'message' => $response_message,
682
- 'data' => $response_body->data
683
- )
684
- );
685
- } else {
686
- //return new WP_Error($response_code, $response_message, $response_body);
687
- return $response_body;
688
- }
689
- } catch (Exception $e) {
690
- return $e->getMessage();
691
- }
692
- }
693
 
694
  public function getCampaignCurrencySymbol($postData) {
695
  try {
@@ -698,14 +544,13 @@ class CustomApi{
698
  $args = array(
699
  'headers' => array(
700
  'Authorization' => "Bearer $this->token",
701
- 'Content-Type' => 'application/json',
702
- //'AccessToken' => $this->generateAccessToken($_SESSION['access_token'], $_SESSION['access_token'])
703
  ),
704
  'body' => wp_json_encode($postData)
705
  );
706
 
707
  // Send remote request
708
- $request = wp_remote_post($url, $args);
709
 
710
  // Retrieve information
711
  $response_code = wp_remote_retrieve_response_code($request);
@@ -748,14 +593,14 @@ class CustomApi{
748
  $refreshToken = $refresh_token;
749
  $data = [
750
  "grant_type" => 'refresh_token',
751
- "client_id" => $clientId,
752
  'client_secret' => $clientSecret,
753
  'refresh_token' => $refreshToken,
754
  ];
755
  $postData = json_encode($data);
756
  $ch = curl_init();
757
  curl_setopt_array($ch, array(
758
- CURLOPT_URL => $url, //esc_url($this->curl_url),
759
  CURLOPT_RETURNTRANSFER => true,
760
  CURLOPT_TIMEOUT => 0,
761
  CURLOPT_HTTPHEADER => $header,
@@ -773,45 +618,6 @@ class CustomApi{
773
  }
774
  }
775
 
776
- public function updateShowSetupTimeFlag($postData) {
777
- try {
778
- $url = $this->apiDomain . '/customer-subscriptions/update-setup-time';
779
-
780
- $data = [
781
- 'subscription_id' => $postData['subscription_id'],
782
- 'show_setup_time' => 0,
783
- ];
784
- $args = array(
785
- 'headers' => array(
786
- 'Authorization' => "Bearer $this->token",
787
- 'Content-Type' => 'application/json',
788
- ),
789
- 'body' => wp_json_encode($data)
790
- );
791
- // Send remote request
792
- $request = wp_remote_post($url, $args);
793
-
794
- // Retrieve information
795
- $response_code = wp_remote_retrieve_response_code($request);
796
- $response_message = wp_remote_retrieve_response_message($request);
797
- $response_body = json_decode(wp_remote_retrieve_body($request));
798
- if ((isset($response_body->error) && $response_body->error == '')) {
799
-
800
- return new WP_REST_Response(
801
- array(
802
- 'status' => $response_code,
803
- 'message' => $response_message,
804
- 'data' => $response_body->data
805
- )
806
- );
807
- } else {
808
- return new WP_Error($response_code, $response_message, $response_body);
809
- }
810
- } catch (Exception $e) {
811
- return $e->getMessage();
812
- }
813
- }
814
-
815
  public function siteVerificationToken($postData) {
816
  try {
817
  $url = $this->apiDomain . '/gmc/site-verification-token';
@@ -822,7 +628,7 @@ class CustomApi{
822
 
823
  $data = [
824
  'merchant_id' => esc_attr($postData['merchant_id']),
825
- 'website' => $postData['website_url'],
826
  'account_id' => esc_attr($postData['account_id']),
827
  'method' => esc_attr($postData['method'])
828
  ];
@@ -832,7 +638,7 @@ class CustomApi{
832
  $data = json_encode($data);
833
  $ch = curl_init();
834
  curl_setopt_array($ch, array(
835
- CURLOPT_URL => $this->curl_url, //esc_url($this->curl_url),
836
  CURLOPT_RETURNTRANSFER => true,
837
  CURLOPT_TIMEOUT => 0,
838
  CURLOPT_HTTPHEADER => $this->header,
@@ -856,10 +662,10 @@ class CustomApi{
856
 
857
  $data = [
858
  'merchant_id' => esc_attr($postData['merchant_id']),
859
- 'website' => $postData['website_url'],
860
  'subscription_id' => esc_attr($postData['subscription_id']),
861
  'account_id' => esc_attr($postData['account_id']),
862
- 'method' => $postData['method']
863
  ];
864
 
865
  $this->curl_url = $url;
@@ -867,7 +673,7 @@ class CustomApi{
867
  $data = json_encode($data);
868
  $ch = curl_init();
869
  curl_setopt_array($ch, array(
870
- CURLOPT_URL => $this->curl_url, //esc_url($this->curl_url),
871
  CURLOPT_RETURNTRANSFER => true,
872
  CURLOPT_TIMEOUT => 0,
873
  CURLOPT_HTTPHEADER => $this->header,
@@ -901,7 +707,7 @@ class CustomApi{
901
  $data = json_encode($data);
902
  $ch = curl_init();
903
  curl_setopt_array($ch, array(
904
- CURLOPT_URL => $this->curl_url, //esc_url($this->curl_url),
905
  CURLOPT_RETURNTRANSFER => true,
906
  CURLOPT_TIMEOUT => 0,
907
  CURLOPT_HTTPHEADER => $this->header,
11
  public function get_tvc_access_token(){
12
  if(!empty($this->access_token)){
13
  return $this->access_token;
 
 
 
14
  }else{
15
  $TVC_Admin_Helper = new TVC_Admin_Helper();
16
+ $google_detail = $TVC_Admin_Helper->get_ee_options_data();
17
+ if((isset($google_detail['setting']->access_token))){
18
+ $this->access_token = base64_decode(sanitize_text_field($google_detail['setting']->access_token));
19
+ }
20
  return $this->access_token;
21
  }
22
  }
24
  public function get_tvc_refresh_token(){
25
  if(!empty($this->refresh_token)){
26
  return $this->refresh_token;
 
 
 
27
  }else{
28
  $TVC_Admin_Helper = new TVC_Admin_Helper();
29
+ $google_detail = $TVC_Admin_Helper->get_ee_options_data();
30
+ if(isset($google_detail['setting']->refresh_token)){
31
+ $this->refresh_token = base64_decode(sanitize_text_field($google_detail['setting']->refresh_token));
32
+ }
33
  return $this->refresh_token;
34
  }
35
  }
41
  return false;
42
  }
43
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  public function getGoogleAnalyticDetail($subscription_id = null) {
46
  try {
51
  );
52
  $ee_options_data = unserialize(get_option('ee_options'));
53
  if($subscription_id == null && isset($ee_options_data['subscription_id'])) {
54
+ $subscription_id = esc_attr($ee_options_data['subscription_id']);
55
+ }
 
56
  $data = [
57
+ 'subscription_id' => sanitize_text_field($subscription_id),
58
+ 'domain' => get_site_url()
59
  ];
60
  $postData = json_encode($data);
61
  $ch = curl_init();
62
  curl_setopt_array($ch, array(
63
+ CURLOPT_URL => esc_url_raw($url),
64
  CURLOPT_RETURNTRANSFER => true,
65
  CURLOPT_TIMEOUT => 1000,
66
  CURLOPT_HTTPHEADER => $header,
67
  CURLOPT_POSTFIELDS => $postData
68
  ));
69
  $response = curl_exec($ch);
70
+ $response_body = json_decode($response);
71
+ $return = new \stdClass();
72
+ if (isset($response_body->error) && $response_body->error == '') {
73
+ $return->error = false;
74
+ $return->data = $response_body->data;
75
+ $return->message = $response_body->message;
76
+ return $return;
77
+ } else {
78
+ if (isset($response_body->data)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  $return->error = false;
80
  $return->data = $response_body->data;
81
  $return->message = $response_body->message;
 
82
  } else {
83
+ $return->error = true;
84
+ $return->data = [];
85
+ if(isset($response_body->errors->key[0])){
86
+ $return->message = $response_body->errors->key[0];
87
+ }else{
88
+ $return->message = esc_html__("Please try after some time.","conversios");
 
 
 
 
 
 
89
  }
 
90
  }
91
+ return $return;
92
+ }
93
+
94
  } catch (Exception $e) {
95
  return $e->getMessage();
96
  }
110
  );
111
 
112
  // Send remote request
113
+ $request = wp_remote_post(esc_url_raw($url), $args);
114
 
115
  // Retrieve information
116
  $response_code = wp_remote_retrieve_response_code($request);
143
  $postData = json_encode($data);
144
  $ch = curl_init();
145
  curl_setopt_array($ch, array(
146
+ CURLOPT_URL => esc_url_raw($curl_url),
147
  CURLOPT_RETURNTRANSFER => true,
148
  CURLOPT_TIMEOUT => 1000,
149
  CURLOPT_HTTPHEADER => $header,
173
  );
174
  $curl_url = $this->apiDomain . "/licence/activation";
175
  $postData = [
176
+ 'key' => esc_attr($licence_key),
177
  'domain' => get_site_url(),
178
  'subscription_id'=>esc_attr($subscription_id)
179
  ];
180
  $postData = json_encode($postData);
181
  $ch = curl_init();
182
  curl_setopt_array($ch, array(
183
+ CURLOPT_URL => esc_url_raw($curl_url),
184
  CURLOPT_RETURNTRANSFER => true,
185
  CURLOPT_TIMEOUT => 1000,
186
  CURLOPT_HTTPHEADER => $header,
205
  if(isset($response->errors->key[0])){
206
  $return->message = $response->errors->key[0];
207
  }else{
208
+ $return->message = esc_html__("Check your entered licese key.","conversios");
209
  }
210
 
211
  }
228
  $postData = json_encode($postData);
229
  $ch = curl_init();
230
  curl_setopt_array($ch, array(
231
+ CURLOPT_URL => esc_url_raw($curl_url),
232
  CURLOPT_RETURNTRANSFER => true,
233
  CURLOPT_TIMEOUT => 1000,
234
  CURLOPT_HTTPHEADER => $header,
276
  $postData = json_encode($postData);
277
  $ch = curl_init();
278
  curl_setopt_array($ch, array(
279
+ CURLOPT_URL => esc_url_raw($curl_url),
280
  CURLOPT_RETURNTRANSFER => true,
281
  CURLOPT_TIMEOUT => 1000,
282
  CURLOPT_HTTPHEADER => $header,
304
  $postData = json_encode($postData);
305
  $ch = curl_init();
306
  curl_setopt_array($ch, array(
307
+ CURLOPT_URL => esc_url_raw($curl_url),
308
  CURLOPT_RETURNTRANSFER => true,
309
  CURLOPT_TIMEOUT => 1000,
310
  CURLOPT_HTTPHEADER => $header,
335
  $postData = json_encode($postData);
336
  $ch = curl_init();
337
  curl_setopt_array($ch, array(
338
+ CURLOPT_URL => esc_url_raw($curl_url),
339
  CURLOPT_RETURNTRANSFER => true,
340
  CURLOPT_TIMEOUT => 2000,
341
  CURLOPT_HTTPHEADER => $header,
370
  'body' => wp_json_encode($data)
371
  );
372
  // Send remote request
373
+ $request = wp_remote_post(esc_url_raw($url), $args);
374
  // Retrieve information
375
  $response_code = wp_remote_retrieve_response_code($request);
376
  $response_message = wp_remote_retrieve_response_message($request);
399
  );
400
 
401
  // Send remote request
402
+ $request = wp_remote_post(esc_url_raw($url), $args);
403
 
404
  // Retrieve information
405
  $response_code = wp_remote_retrieve_response_code($request);
422
  return $e->getMessage();
423
  }
424
  }
 
425
  public function setGmcAttributeMapping($postData) {
426
  try {
427
  $url = $this->apiDomain . '/gmc/gmc-attribute-mapping';
470
  $postData = json_encode($data);
471
  $ch = curl_init();
472
  curl_setopt_array($ch, array(
473
+ CURLOPT_URL => esc_url_raw($curl_url),
474
  CURLOPT_RETURNTRANSFER => true,
475
  CURLOPT_TIMEOUT => 10000,
476
  CURLOPT_HTTPHEADER => $header,
507
  $postData = json_encode($postData);
508
  $ch = curl_init();
509
  curl_setopt_array($ch, array(
510
+ CURLOPT_URL => esc_url_raw($curl_url),
511
  CURLOPT_RETURNTRANSFER => true,
512
  CURLOPT_TIMEOUT => 1000,
513
  CURLOPT_HTTPHEADER => $header,
535
  return $e->getMessage();
536
  }
537
  }
538
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
  public function getCampaignCurrencySymbol($postData) {
541
  try {
544
  $args = array(
545
  'headers' => array(
546
  'Authorization' => "Bearer $this->token",
547
+ 'Content-Type' => 'application/json'
 
548
  ),
549
  'body' => wp_json_encode($postData)
550
  );
551
 
552
  // Send remote request
553
+ $request = wp_remote_post(esc_url_raw($url), $args);
554
 
555
  // Retrieve information
556
  $response_code = wp_remote_retrieve_response_code($request);
593
  $refreshToken = $refresh_token;
594
  $data = [
595
  "grant_type" => 'refresh_token',
596
+ "client_id" => esc_attr($clientId),
597
  'client_secret' => $clientSecret,
598
  'refresh_token' => $refreshToken,
599
  ];
600
  $postData = json_encode($data);
601
  $ch = curl_init();
602
  curl_setopt_array($ch, array(
603
+ CURLOPT_URL => esc_url_raw($url), //esc_url($this->curl_url),
604
  CURLOPT_RETURNTRANSFER => true,
605
  CURLOPT_TIMEOUT => 0,
606
  CURLOPT_HTTPHEADER => $header,
618
  }
619
  }
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  public function siteVerificationToken($postData) {
622
  try {
623
  $url = $this->apiDomain . '/gmc/site-verification-token';
628
 
629
  $data = [
630
  'merchant_id' => esc_attr($postData['merchant_id']),
631
+ 'website' => esc_url_raw($postData['website_url']),
632
  'account_id' => esc_attr($postData['account_id']),
633
  'method' => esc_attr($postData['method'])
634
  ];
638
  $data = json_encode($data);
639
  $ch = curl_init();
640
  curl_setopt_array($ch, array(
641
+ CURLOPT_URL => esc_url_raw($this->curl_url), //esc_url($this->curl_url),
642
  CURLOPT_RETURNTRANSFER => true,
643
  CURLOPT_TIMEOUT => 0,
644
  CURLOPT_HTTPHEADER => $this->header,
662
 
663
  $data = [
664
  'merchant_id' => esc_attr($postData['merchant_id']),
665
+ 'website' => esc_url_raw($postData['website_url']),
666
  'subscription_id' => esc_attr($postData['subscription_id']),
667
  'account_id' => esc_attr($postData['account_id']),
668
+ 'method' => esc_attr($postData['method'])
669
  ];
670
 
671
  $this->curl_url = $url;
673
  $data = json_encode($data);
674
  $ch = curl_init();
675
  curl_setopt_array($ch, array(
676
+ CURLOPT_URL => esc_url_raw($this->curl_url), //esc_url($this->curl_url),
677
  CURLOPT_RETURNTRANSFER => true,
678
  CURLOPT_TIMEOUT => 0,
679
  CURLOPT_HTTPHEADER => $this->header,
707
  $data = json_encode($data);
708
  $ch = curl_init();
709
  curl_setopt_array($ch, array(
710
+ CURLOPT_URL => esc_url_raw($this->curl_url), //esc_url($this->curl_url),
711
  CURLOPT_RETURNTRANSFER => true,
712
  CURLOPT_TIMEOUT => 0,
713
  CURLOPT_HTTPHEADER => $this->header,
includes/setup/ShoppingApi.php CHANGED
@@ -23,8 +23,8 @@ class ShoppingApi {
23
  $url = $this->apiDomain . '/campaigns/list';
24
 
25
  $data = [
26
- 'merchant_id' => $this->merchantId,
27
- 'customer_id' => $this->customerId
28
  ];
29
  $args = array(
30
  'headers' => array(
@@ -35,7 +35,7 @@ class ShoppingApi {
35
  );
36
 
37
  // Send remote request
38
- $request = wp_remote_post($url, $args);
39
  // Retrieve information
40
  $response_code = wp_remote_retrieve_response_code($request);
41
  $response_message = wp_remote_retrieve_response_message($request);
@@ -63,7 +63,7 @@ class ShoppingApi {
63
  $url = $this->apiDomain . '/products/categories';
64
 
65
  $data = [
66
- 'customer_id' => $this->customerId,
67
  'country_code' => sanitize_text_field($country_code)
68
  ];
69
 
@@ -76,7 +76,7 @@ class ShoppingApi {
76
  );
77
 
78
  // Send remote request
79
- $request = wp_remote_post($url, $args);
80
 
81
  // Retrieve information
82
  $response_code = wp_remote_retrieve_response_code($request);
@@ -110,8 +110,8 @@ class ShoppingApi {
110
 
111
  $url = $this->apiDomain . '/reports/account-performance';
112
  $data = [
113
- 'customer_id' => $this->customerId,
114
- 'graph_type' => ($date_range_type == 2 && $days_diff > 31) ? 'month' : 'day',
115
  'date_range_type' => sanitize_text_field($date_range_type),
116
  'days' => sanitize_text_field($days),
117
  'from_date' => sanitize_text_field($from_date),
@@ -128,7 +128,7 @@ class ShoppingApi {
128
 
129
 
130
  // Send remote request
131
- $request = wp_remote_post($url, $args);
132
 
133
  // Retrieve information
134
  $response_code = wp_remote_retrieve_response_code($request);
@@ -159,8 +159,8 @@ class ShoppingApi {
159
  }
160
  $curl_url = $this->apiDomain . '/reports/account-performance';
161
  $data = [
162
- 'customer_id' => $this->customerId,
163
- 'graph_type' => ($date_range_type == 2 && $days_diff > 61) ? 'month' : 'day',
164
  'date_range_type' => sanitize_text_field($date_range_type),
165
  'days' => sanitize_text_field($days),
166
  'from_date' => sanitize_text_field($from_date),
@@ -174,7 +174,7 @@ class ShoppingApi {
174
  $postData = json_encode($data);
175
  $ch = curl_init();
176
  curl_setopt_array($ch, array(
177
- CURLOPT_URL => esc_url($curl_url),
178
  CURLOPT_RETURNTRANSFER => true,
179
  CURLOPT_TIMEOUT => 2000,
180
  CURLOPT_HTTPHEADER => $header,
@@ -197,8 +197,8 @@ class ShoppingApi {
197
  $days_diff = abs(round($days_diff / 86400));
198
  }
199
  $data = [
200
- 'customer_id' => $this->customerId,
201
- 'graph_type' => ($date_range_type == 2 && $days_diff > 61) ? 'month' : 'day',
202
  'date_range_type' => sanitize_text_field($date_range_type),
203
  'days' => sanitize_text_field($days),
204
  'from_date' => sanitize_text_field($from_date),
@@ -211,7 +211,7 @@ class ShoppingApi {
211
  $postData = json_encode($data);
212
  $ch = curl_init();
213
  curl_setopt_array($ch, array(
214
- CURLOPT_URL => esc_url($curl_url),
215
  CURLOPT_RETURNTRANSFER => true,
216
  CURLOPT_TIMEOUT => 2000,
217
  CURLOPT_HTTPHEADER => $header,
@@ -235,8 +235,8 @@ class ShoppingApi {
235
  $days_diff = abs(round($days_diff / 86400));
236
  }
237
  $data = [
238
- 'customer_id' => $this->customerId,
239
- 'graph_type' => ($date_range_type == 2 && $days_diff > 31) ? 'month' : 'day',
240
  'date_range_type' => sanitize_text_field($date_range_type),
241
  'days' => sanitize_text_field($days),
242
  'from_date' => sanitize_text_field($from_date),
@@ -252,7 +252,7 @@ class ShoppingApi {
252
  );
253
 
254
  // Send remote request
255
- $request = wp_remote_post($url, $args);
256
 
257
  // Retrieve information
258
  $response_code = wp_remote_retrieve_response_code($request);
@@ -280,8 +280,8 @@ class ShoppingApi {
280
  $url = $this->apiDomain . '/reports/product-performance';
281
 
282
  $data = [
283
- 'merchant_id' => $this->merchantId,
284
- 'customer_id' => $this->customerId,
285
  'campaign_id' => sanitize_text_field($campaign_id),
286
  'date_range_type' => sanitize_text_field($date_range_type),
287
  'days' => sanitize_text_field($days),
@@ -298,7 +298,7 @@ class ShoppingApi {
298
  );
299
 
300
  // Send remote request
301
- $request = wp_remote_post($url, $args);
302
 
303
  // Retrieve information
304
  $response_code = wp_remote_retrieve_response_code($request);
@@ -326,8 +326,8 @@ class ShoppingApi {
326
  $url = $this->apiDomain . '/reports/product-partition-performance';
327
 
328
  $data = [
329
- 'merchant_id' => $this->merchantId,
330
- 'customer_id' => $this->customerId,
331
  'campaign_id' => sanitize_text_field($campaign_id),
332
  'date_range_type' => sanitize_text_field($date_range_type),
333
  'days' => sanitize_text_field($days),
@@ -344,7 +344,7 @@ class ShoppingApi {
344
  );
345
 
346
  // Send remote request
347
- $request = wp_remote_post($url, $args);
348
 
349
  // Retrieve information
350
  $response_code = wp_remote_retrieve_response_code($request);
@@ -372,8 +372,8 @@ class ShoppingApi {
372
  $url = $this->apiDomain . '/campaigns/detail';
373
 
374
  $data = [
375
- 'merchant_id' => $this->merchantId,
376
- 'customer_id' => $this->customerId,
377
  'campaign_id' => sanitize_text_field($campaign_id)
378
  ];
379
 
@@ -387,7 +387,7 @@ class ShoppingApi {
387
 
388
 
389
  // Send remote request
390
- $request = wp_remote_post($url, $args);
391
 
392
 
393
  // Retrieve information
@@ -419,19 +419,19 @@ class ShoppingApi {
419
  );
420
  $curl_url = $this->apiDomain . "/campaigns/create";
421
  $data = [
422
- 'merchant_id' => $this->merchantId,
423
- 'customer_id' => $this->customerId,
424
  'campaign_name' => sanitize_text_field($campaign_name),
425
  'budget' => sanitize_text_field($budget),
426
  'target_country' => sanitize_text_field($target_country),
427
- 'all_products' => $all_products,
428
  'filter_by' => 'category',
429
- 'filter_data' => ["id" => $category_id, "level" => $category_level]
430
  ];
431
  $postData = json_encode($data);
432
  $ch = curl_init();
433
  curl_setopt_array($ch, array(
434
- CURLOPT_URL => esc_url($curl_url),
435
  CURLOPT_RETURNTRANSFER => true,
436
  CURLOPT_TIMEOUT => 1000,
437
  CURLOPT_HTTPHEADER => $header,
@@ -463,24 +463,24 @@ class ShoppingApi {
463
  );
464
  $curl_url = $this->apiDomain . '/campaigns/update';
465
  $data = [
466
- 'merchant_id' => $this->merchantId,
467
- 'customer_id' => $this->customerId,
468
  'campaign_id' => sanitize_text_field($campaign_id),
469
  'account_budget_id' => sanitize_text_field($budget_id),
470
  'campaign_name' => sanitize_text_field($campaign_name),
471
  'target_country' => sanitize_text_field($target_country),
472
  'budget' => sanitize_text_field($budget),
473
  'status' => 2, // ENABLE => 2, PAUSED => 3, REMOVED => 4
474
- 'all_products' => $all_products,
475
  'ad_group_id' => sanitize_text_field($ad_group_id),
476
  'ad_group_resource_name' => sanitize_text_field($ad_group_resource_name),
477
  'filter_by' => 'category',
478
- 'filter_data' => ["id" => $category_id, "level" => $category_level]
479
  ];
480
  $postData = json_encode($data);
481
  $ch = curl_init();
482
  curl_setopt_array($ch, array(
483
- CURLOPT_URL => esc_url($curl_url),
484
  CURLOPT_CUSTOMREQUEST => 'PATCH',
485
  CURLOPT_RETURNTRANSFER => true,
486
  CURLOPT_TIMEOUT => 1000,
23
  $url = $this->apiDomain . '/campaigns/list';
24
 
25
  $data = [
26
+ 'merchant_id' => sanitize_text_field($this->merchantId),
27
+ 'customer_id' => sanitize_text_field($this->customerId)
28
  ];
29
  $args = array(
30
  'headers' => array(
35
  );
36
 
37
  // Send remote request
38
+ $request = wp_remote_post(esc_url_raw($url), $args);
39
  // Retrieve information
40
  $response_code = wp_remote_retrieve_response_code($request);
41
  $response_message = wp_remote_retrieve_response_message($request);
63
  $url = $this->apiDomain . '/products/categories';
64
 
65
  $data = [
66
+ 'customer_id' => sanitize_text_field($this->customerId),
67
  'country_code' => sanitize_text_field($country_code)
68
  ];
69
 
76
  );
77
 
78
  // Send remote request
79
+ $request = wp_remote_post(esc_url_raw($url), $args);
80
 
81
  // Retrieve information
82
  $response_code = wp_remote_retrieve_response_code($request);
110
 
111
  $url = $this->apiDomain . '/reports/account-performance';
112
  $data = [
113
+ 'customer_id' => sanitize_text_field($this->customerId),
114
+ 'graph_type' => sanitize_text_field(($date_range_type == 2 && $days_diff > 31) ? 'month' : 'day'),
115
  'date_range_type' => sanitize_text_field($date_range_type),
116
  'days' => sanitize_text_field($days),
117
  'from_date' => sanitize_text_field($from_date),
128
 
129
 
130
  // Send remote request
131
+ $request = wp_remote_post(esc_url_raw($url), $args);
132
 
133
  // Retrieve information
134
  $response_code = wp_remote_retrieve_response_code($request);
159
  }
160
  $curl_url = $this->apiDomain . '/reports/account-performance';
161
  $data = [
162
+ 'customer_id' => sanitize_text_field($this->customerId),
163
+ 'graph_type' => sanitize_text_field(($date_range_type == 2 && $days_diff > 61) ? 'month' : 'day'),
164
  'date_range_type' => sanitize_text_field($date_range_type),
165
  'days' => sanitize_text_field($days),
166
  'from_date' => sanitize_text_field($from_date),
174
  $postData = json_encode($data);
175
  $ch = curl_init();
176
  curl_setopt_array($ch, array(
177
+ CURLOPT_URL => esc_url_raw($curl_url),
178
  CURLOPT_RETURNTRANSFER => true,
179
  CURLOPT_TIMEOUT => 2000,
180
  CURLOPT_HTTPHEADER => $header,
197
  $days_diff = abs(round($days_diff / 86400));
198
  }
199
  $data = [
200
+ 'customer_id' => sanitize_text_field($this->customerId),
201
+ 'graph_type' => sanitize_text_field(($date_range_type == 2 && $days_diff > 61) ? 'month' : 'day'),
202
  'date_range_type' => sanitize_text_field($date_range_type),
203
  'days' => sanitize_text_field($days),
204
  'from_date' => sanitize_text_field($from_date),
211
  $postData = json_encode($data);
212
  $ch = curl_init();
213
  curl_setopt_array($ch, array(
214
+ CURLOPT_URL => esc_url_raw($curl_url),
215
  CURLOPT_RETURNTRANSFER => true,
216
  CURLOPT_TIMEOUT => 2000,
217
  CURLOPT_HTTPHEADER => $header,
235
  $days_diff = abs(round($days_diff / 86400));
236
  }
237
  $data = [
238
+ 'customer_id' => sanitize_text_field($this->customerId),
239
+ 'graph_type' => sanitize_text_field(($date_range_type == 2 && $days_diff > 31) ? 'month' : 'day'),
240
  'date_range_type' => sanitize_text_field($date_range_type),
241
  'days' => sanitize_text_field($days),
242
  'from_date' => sanitize_text_field($from_date),
252
  );
253
 
254
  // Send remote request
255
+ $request = wp_remote_post(esc_url_raw($url), $args);
256
 
257
  // Retrieve information
258
  $response_code = wp_remote_retrieve_response_code($request);
280
  $url = $this->apiDomain . '/reports/product-performance';
281
 
282
  $data = [
283
+ 'merchant_id' => sanitize_text_field($this->merchantId),
284
+ 'customer_id' => sanitize_text_field($this->customerId),
285
  'campaign_id' => sanitize_text_field($campaign_id),
286
  'date_range_type' => sanitize_text_field($date_range_type),
287
  'days' => sanitize_text_field($days),
298
  );
299
 
300
  // Send remote request
301
+ $request = wp_remote_post(esc_url_raw($url), $args);
302
 
303
  // Retrieve information
304
  $response_code = wp_remote_retrieve_response_code($request);
326
  $url = $this->apiDomain . '/reports/product-partition-performance';
327
 
328
  $data = [
329
+ 'merchant_id' => sanitize_text_field($this->merchantId),
330
+ 'customer_id' => sanitize_text_field($this->customerId),
331
  'campaign_id' => sanitize_text_field($campaign_id),
332
  'date_range_type' => sanitize_text_field($date_range_type),
333
  'days' => sanitize_text_field($days),
344
  );
345
 
346
  // Send remote request
347
+ $request = wp_remote_post(esc_url_raw($url), $args);
348
 
349
  // Retrieve information
350
  $response_code = wp_remote_retrieve_response_code($request);
372
  $url = $this->apiDomain . '/campaigns/detail';
373
 
374
  $data = [
375
+ 'merchant_id' => sanitize_text_field($this->merchantId),
376
+ 'customer_id' => sanitize_text_field($this->customerId),
377
  'campaign_id' => sanitize_text_field($campaign_id)
378
  ];
379
 
387
 
388
 
389
  // Send remote request
390
+ $request = wp_remote_post(esc_url_raw($url), $args);
391
 
392
 
393
  // Retrieve information
419
  );
420
  $curl_url = $this->apiDomain . "/campaigns/create";
421
  $data = [
422
+ 'merchant_id' => sanitize_text_field($this->merchantId),
423
+ 'customer_id' => sanitize_text_field($this->customerId),
424
  'campaign_name' => sanitize_text_field($campaign_name),
425
  'budget' => sanitize_text_field($budget),
426
  'target_country' => sanitize_text_field($target_country),
427
+ 'all_products' => sanitize_text_field($all_products),
428
  'filter_by' => 'category',
429
+ 'filter_data' => ["id" => sanitize_text_field($category_id), "level" => sanitize_text_field($category_level)]
430
  ];
431
  $postData = json_encode($data);
432
  $ch = curl_init();
433
  curl_setopt_array($ch, array(
434
+ CURLOPT_URL => esc_url_raw($curl_url),
435
  CURLOPT_RETURNTRANSFER => true,
436
  CURLOPT_TIMEOUT => 1000,
437
  CURLOPT_HTTPHEADER => $header,
463
  );
464
  $curl_url = $this->apiDomain . '/campaigns/update';
465
  $data = [
466
+ 'merchant_id' => sanitize_text_field($this->merchantId),
467
+ 'customer_id' => sanitize_text_field($this->customerId),
468
  'campaign_id' => sanitize_text_field($campaign_id),
469
  'account_budget_id' => sanitize_text_field($budget_id),
470
  'campaign_name' => sanitize_text_field($campaign_name),
471
  'target_country' => sanitize_text_field($target_country),
472
  'budget' => sanitize_text_field($budget),
473
  'status' => 2, // ENABLE => 2, PAUSED => 3, REMOVED => 4
474
+ 'all_products' => sanitize_text_field($all_products),
475
  'ad_group_id' => sanitize_text_field($ad_group_id),
476
  'ad_group_resource_name' => sanitize_text_field($ad_group_resource_name),
477
  'filter_by' => 'category',
478
+ 'filter_data' => ["id" => sanitize_text_field($category_id), "level" => sanitize_text_field($category_level)]
479
  ];
480
  $postData = json_encode($data);
481
  $ch = curl_init();
482
  curl_setopt_array($ch, array(
483
+ CURLOPT_URL => esc_url_raw($curl_url),
484
  CURLOPT_CUSTOMREQUEST => 'PATCH',
485
  CURLOPT_RETURNTRANSFER => true,
486
  CURLOPT_TIMEOUT => 1000,
includes/setup/account.php CHANGED
@@ -18,8 +18,8 @@ class TVC_Account {
18
  $message = ""; $class="";
19
  $googleDetail = [];
20
  $plan_id = 1;
21
- $plan_name = __("Free Plan","conversios");
22
- $plan_price = __("Free","conversios");
23
  $api_licence_key="";
24
  $paypal_subscr_id = "";
25
  $product_sync_max_limit ="100";
@@ -35,13 +35,6 @@ class TVC_Account {
35
  if(isset($googleDetail->licence_key) && !in_array($googleDetail->plan_id, array("1"))){
36
  $api_licence_key = esc_attr($googleDetail->licence_key);
37
  }
38
- /*if(isset($googleDetail->subscription_type) && !in_array($googleDetail->plan_id, array("1"))){
39
- if($googleDetail->subscription_type == 1){
40
- // $subscription_type = " ( Monthly )";
41
- }else if($googleDetail->subscription_type == 2){
42
- //$subscription_type = " ( Yearly )";
43
- }
44
- }*/
45
  if(isset($googleDetail->plan_name) && !in_array($googleDetail->plan_id, array("1"))){
46
  $plan_name = esc_attr($googleDetail->plan_name);
47
  }
@@ -54,7 +47,7 @@ class TVC_Account {
54
  if(isset($googleDetail->max_limit)){
55
  $product_sync_max_limit = esc_attr($googleDetail->max_limit);
56
  if(in_array($plan_id, array("7","8"))){
57
- $product_sync_max_limit = __("Unlimited","conversios");
58
  }
59
  }
60
  if(isset($googleDetail->subscription_activation_date) && !in_array($googleDetail->plan_id, array("1"))){
@@ -78,39 +71,39 @@ class TVC_Account {
78
  <div class="licence tvc-licence" >
79
  <div class="tvc_licence_key_wapper <?php if($plan_id != 1){?>tvc-hide<?php }?>">
80
  <?php if($plan_id == 1){?>
81
- <p><?php _e("You are using our free plugin, no licence needed ! Happy analyzing..!! :)","conversios"); ?></p>
82
- <p class="font-weight-bold"><?php _e("To unlock more features of google products, consider our","conversios"); ?> <a href="<?php echo $this->TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=Account+Summary+pro+version&utm_campaign=Upsell+at+Conversios'; ?>" target="_blank"><?php _e("pro version.","conversios"); ?></a></p>
83
  <?php }?>
84
  <form method="post" name="google-analytic" id="tvc-licence-active">
85
  <div class="input-group">
86
- <input type="text" id="licence_key" name="licence_key" class="form-control" placeholder="Already purchased? Enter licence key" required="">
87
  <div class="input-group-append">
88
- <button type="submit" class="btn btn-primary" name="verify-licence-key"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/right-arrow.svg'); ?>" alt="active licence key"></button>
89
  </div>
90
  </div>
91
  </form>
92
  </div>
93
  <div class="google-account-analytics tvc_licence_key_change_wapper <?php if($plan_id == 1){?>tvc-hide<?php }?>">
94
  <div class="acc-num">
95
- <label class="ga-title tvc_licence_key_title"><?php _e("Licence key:","conversios"); ?></label>
96
- <p class="ga-text tvc_licence_key"><?php echo $api_licence_key; ?></p>
97
- <p class="ga-text text-right tvc_licence_key_change"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg'); ?>" alt="active licence key"></p>
98
  </div>
99
  </div>
100
  </div>
101
 
102
  <div class="tvc-table">
103
- <strong><?php _e("Account Summary","conversios"); ?></strong>
104
  <table>
105
  <tbody>
106
- <tr><th><?php _e("Plan name","conversios"); ?></th><td><?php echo $plan_name; ?></td></tr>
107
- <tr><th><?php _e("Plan price","conversios"); ?></th><td><?php echo $plan_price; ?></td></tr>
108
- <tr><th><?php _e("Product sync limit","conversios"); ?></th><td><?php echo $product_sync_max_limit; ?></td></tr>
109
  <?php if($plan_id != 1){?>
110
- <tr><th><?php _e("Active licence key","conversios"); ?></th><td><?php echo $api_licence_key; ?></td></tr>
111
- <tr><th><?php _e("PayPal subscription id","conversios"); ?></th><td><?php echo $paypal_subscr_id; ?></td></tr>
112
- <tr><th><?php _e("Last bill date","conversios"); ?></th><td><?php echo $activation_date; ?></td></tr>
113
- <tr><th><?php _e("Expected bill date","conversios"); ?></th><td><?php echo $next_payment_date; ?></td></tr>
114
  <?php } ?>
115
  </tbody>
116
  </table>
18
  $message = ""; $class="";
19
  $googleDetail = [];
20
  $plan_id = 1;
21
+ $plan_name = esc_html__("Free Plan","conversios");
22
+ $plan_price = esc_html__("Free","conversios");
23
  $api_licence_key="";
24
  $paypal_subscr_id = "";
25
  $product_sync_max_limit ="100";
35
  if(isset($googleDetail->licence_key) && !in_array($googleDetail->plan_id, array("1"))){
36
  $api_licence_key = esc_attr($googleDetail->licence_key);
37
  }
 
 
 
 
 
 
 
38
  if(isset($googleDetail->plan_name) && !in_array($googleDetail->plan_id, array("1"))){
39
  $plan_name = esc_attr($googleDetail->plan_name);
40
  }
47
  if(isset($googleDetail->max_limit)){
48
  $product_sync_max_limit = esc_attr($googleDetail->max_limit);
49
  if(in_array($plan_id, array("7","8"))){
50
+ $product_sync_max_limit = esc_html__("Unlimited","conversios");
51
  }
52
  }
53
  if(isset($googleDetail->subscription_activation_date) && !in_array($googleDetail->plan_id, array("1"))){
71
  <div class="licence tvc-licence" >
72
  <div class="tvc_licence_key_wapper <?php if($plan_id != 1){?>tvc-hide<?php }?>">
73
  <?php if($plan_id == 1){?>
74
+ <p><?php esc_html_e("You are using our free plugin, no licence needed ! Happy analyzing..!! :)","conversios"); ?></p>
75
+ <p class="font-weight-bold"><?php esc_html_e("To unlock more features of google products, consider our","conversios"); ?> <a href="<?php echo esc_url_raw($this->TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=Account+Summary+pro+version&utm_campaign=Upsell+at+Conversios'); ?>" target="_blank"><?php esc_html_e("pro version.","conversios"); ?></a></p>
76
  <?php }?>
77
  <form method="post" name="google-analytic" id="tvc-licence-active">
78
  <div class="input-group">
79
+ <input type="text" id="licence_key" name="licence_key" class="form-control" placeholder="<?php esc_html_e("Already purchased? Enter licence key","conversios"); ?>" required="">
80
  <div class="input-group-append">
81
+ <button type="submit" class="btn btn-primary" name="verify-licence-key"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/right-arrow.svg'); ?>" alt="active licence key"></button>
82
  </div>
83
  </div>
84
  </form>
85
  </div>
86
  <div class="google-account-analytics tvc_licence_key_change_wapper <?php if($plan_id == 1){?>tvc-hide<?php }?>">
87
  <div class="acc-num">
88
+ <label class="ga-title tvc_licence_key_title"><?php esc_html_e("Licence key:","conversios"); ?></label>
89
+ <p class="ga-text tvc_licence_key"><?php echo esc_attr($api_licence_key); ?></p>
90
+ <p class="ga-text text-right tvc_licence_key_change"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg'); ?>" alt="active licence key"></p>
91
  </div>
92
  </div>
93
  </div>
94
 
95
  <div class="tvc-table">
96
+ <strong><?php esc_html_e("Account Summary","conversios"); ?></strong>
97
  <table>
98
  <tbody>
99
+ <tr><th><?php esc_html_e("Plan name","conversios"); ?></th><td><?php echo esc_attr($plan_name); ?></td></tr>
100
+ <tr><th><?php esc_html_e("Plan price","conversios"); ?></th><td><?php echo esc_attr($plan_price); ?></td></tr>
101
+ <tr><th><?php esc_html_e("Product sync limit","conversios"); ?></th><td><?php echo esc_attr($product_sync_max_limit); ?></td></tr>
102
  <?php if($plan_id != 1){?>
103
+ <tr><th><?php esc_html_e("Active licence key","conversios"); ?></th><td><?php echo esc_attr($api_licence_key); ?></td></tr>
104
+ <tr><th><?php esc_html_e("PayPal subscription id","conversios"); ?></th><td><?php echo esc_attr($paypal_subscr_id); ?></td></tr>
105
+ <tr><th><?php esc_html_e("Last bill date","conversios"); ?></th><td><?php echo esc_attr($activation_date); ?></td></tr>
106
+ <tr><th><?php esc_html_e("Expected bill date","conversios"); ?></th><td><?php echo esc_attr($next_payment_date); ?></td></tr>
107
  <?php } ?>
108
  </tbody>
109
  </table>
includes/setup/add-campaign.php CHANGED
@@ -32,10 +32,10 @@ class AddCampaign {
32
  $contData = json_decode($getCountris);
33
  $wooCountry = $this->TVC_Admin_Helper->get_woo_country();
34
  $is_disabled = ($is_disabled) ? "style='max-width:30rem;height:35px;pointer-events:none;background:#f2f2f2;'" : "style='max-width: 30rem;height: 35px;'";
35
- $data = "<select id='sales_country' name='sales_country' class='form-group col-md-6' readonly='true' $is_disabled onchange='selectCountry()'>";
36
  foreach ($contData as $key => $value) {
37
  $selected = ($value->code == $wooCountry) ? "selected='selected'" : "";
38
- $data .= "<option value=" . $value->code . " " . $selected . " >" . $value->name . "</option>";
39
  }
40
  $data .= "</select>";
41
  return $data;
@@ -61,73 +61,74 @@ class AddCampaign {
61
  }
62
 
63
  $defaultCountry = $this->TVC_Admin_Helper->get_woo_country();
64
- $category_list = $api_obj->getCategories($defaultCountry);
65
  if(isset($category_list->errors) && !empty($category_list->errors)){
66
  }else{
67
  $category_list = isset($category_list->data) ? $category_list->data : [];
68
  if (isset($category_list['status']) && $category_list['status'] == 200) {
69
  $categories = $category_list['data'];
70
  }
71
- }
72
 
73
 
74
  if (isset($_POST['create_campaign'])) {
75
- $campaign_name = esc_attr(isset($_POST['campaign_name'])?$_POST['campaign_name']:"");
76
- $campaign_budget = esc_attr(isset($_POST['campaign_budget'])?$_POST['campaign_budget']:"");
77
- $sales_country = esc_attr(isset($_POST['sales_country'])?$_POST['sales_country']:"");
78
- $all_products = esc_attr(isset($_POST['all_products'])?$_POST['all_products']:"");
79
- $category_id = esc_attr(isset($_POST['dimension'])?$_POST['dimension']:"");
80
- $category_level = esc_attr(isset($_POST['category_level'])?$_POST['category_level']:"");
81
 
82
  $campaign = $api_obj->createCampaign($campaign_name, $campaign_budget, $sales_country, $all_products, $category_id, $category_level);
83
  if(isset($campaign->errors) && !empty($campaign->errors)){
84
  $class = 'alert-message tvc-alert-error';
85
- $message = esc_html__((isset($campaign->errors) && isset($campaign->errors[0])) ? $campaign->errors[0] : 'Error', 'sample-text-domain');
86
  }else{
87
  $class = 'alert-message tvc-alert-success';
88
  $campaign_neme = isset($campaign->data)?'with Resource name '.$campaign->data:"";
89
- $message = esc_html__('Smart Shopping Campaign Created Successfully '.$campaign_neme, 'sample-text-domain');
90
  }
91
  }else if (isset($_POST['update_campaign'])) {
92
- $campaign_name = esc_attr(isset($_POST['campaign_name'])?$_POST['campaign_name']:"");
93
- $campaign_budget = esc_attr(isset($_POST['campaign_budget'])?$_POST['campaign_budget']:"");
94
- $campaign_id = esc_attr(isset($_POST['campaign_id'])?$_POST['campaign_id']:"");
95
- $budget_id = esc_attr(isset($_POST['budget_id'])?$_POST['budget_id']:"");
96
- $sales_country = esc_attr(isset($_POST['sales_country'])?$_POST['sales_country']:"");
97
- $all_products = esc_attr(isset($_POST['all_products'])?$_POST['all_products']:"");
98
- $ad_group_id = esc_attr(isset($_POST['ad_group_id'])?$_POST['ad_group_id']:"");
99
- $ad_group_resource_name = esc_attr(isset($_POST['ad_group_resource_name'])?$_POST['ad_group_resource_name']:"");
100
- $category_id = esc_attr(isset($_POST['dimension']) ? $_POST['dimension']:"");
101
- $category_level = esc_attr(isset($_POST['category_level'])?$_POST['category_level']:"");
102
 
103
  $campaign = $api_obj->updateCampaign($campaign_name, $campaign_budget, $campaign_id, $budget_id, $sales_country, $all_products, $category_id, $category_level, $ad_group_id, $ad_group_resource_name);
104
  if (isset($campaign->errors) && !empty($campaign->errors)) {
105
  $class = 'alert-message tvc-alert-error';
106
- $message = esc_html__(isset($campaign->errors) ? $campaign->errors[0] : 'Error', 'sample-text-domain');
107
  } else if(isset($campaign->data)){
108
  $campaign_neme = isset($campaign->data)?'with Resource name '.$campaign->data:"";
109
  $class = 'alert-message tvc-alert-success';
110
- $message = esc_html__('Smart Shopping Campaign Updated Successfully ' . $campaign_neme, 'sample-text-domain');
111
- // $url = admin_url('admin.php?page=tvc-configuration-page');
112
- //wp_redirect($url);
113
-
114
  }
115
- echo "<script>jQuery('#feed-spinner').css('display', 'none');</script>";
 
 
116
  }
117
  $currency = $this->TVC_Admin_Helper->get_user_currency_symbol();
118
- if (isset($_GET['edit']) && $_GET['edit'] != '') {
119
- $campaign_details_res = $api_obj->getCampaignDetails($_GET['edit']);
120
  if (isset($campaign_details_res->errors) && !empty($campaign_details_res->errors)) {
121
  $error_code = array_keys($campaign_details_res->errors)[0];
122
  if($error_code == 404){
123
- $error_msg = __("Campaign details not found","conversios");
124
  }else{
125
  if (isset($campaign_details_res->error_data) && !empty($campaign_details_res->error_data)) {
126
  // $error_msg = array_values($campaign_details_res->error_data)[0]->errors[0];
127
  }
128
  }
129
  $class = 'alert-message tvc-alert-error';
130
- $message = esc_html__(isset($error_msg) ? $error_msg : 'There was some error fetching campaign details.', 'sample-text-domain');
131
  } else {
132
  $campaign_details = $campaign_details_res->data;
133
  if ($campaign_details['status'] == 200) {
@@ -147,154 +148,123 @@ class AddCampaign {
147
  <div class="row">
148
  <div class="col-md-6 col-lg-8 edit-section">
149
  <div class="edit-header-section">
150
- <script>
151
- var back_img = '<img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/left-angle-arrow.svg'); ?>" alt="back"/>';
152
- document.write('<a href="<?php echo $this->site_url."shopping_campaigns_page"; ?>" class="back-btn">'+back_img+'<span>Campaigns</span></a>');
153
  </script>
154
  </div>
155
  <?php
156
  if (!isset($_GET['edit'])) { ?>
157
  <form method="post" id="create-form">
158
  <div class="form-group">
159
- <h2 class="lead"><?php _e("Create a Smart Shopping Campaign to promote your products","conversios"); ?></h2>
160
- <p style="text-align:left; font-size: 14px;"><?php _e("A Smart Shopping campaign shows your products to potential customers across Google, Google Search Partners, the Google Display Network, YouTube, and Gmail.","conversios"); ?></p>
161
  <p style="text-align:left">
162
- <a href="<?php echo esc_url("https://support.google.com/google-ads/answer/7674739?hl=en"); ?>" target="_blank"><?php _e("Learn more about Smart Shopping Campaigns.","conversios"); ?></a>
163
  </p>
164
  </div>
165
  <div class="form-row" style="margin-bottom: 0">
166
  <div class="col-md-12 row">
167
- <label for="campaign-name" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php _e("Campaign name: ","conversios"); ?></label>
168
  <input type="text" class="form-group col-md-6" name="campaign_name" id="campaign-name" required>
169
  </div>
170
  <div class="col-md-12 row">
171
- <label for="campaign-budget" class="form-group col-md-4 mt-2 mb-0 text-left"><span class="font-weight-bold"><?php _e("Daily Campaign Budget","conversios"); ?> (<?php echo $currency ;?>):</span> <p style="text-align:left;font-size: 11px;"><?php _e("Only pay if someone clicks your ad. Recommended minimum budget of $5 per day.","conversios"); ?></p></label>
172
  <input type="number" class="form-group col-md-6" name="campaign_budget" id="campaign-budget" style="height: 35px;" required>
173
  </div>
174
  <div class="col-md-12 row">
175
- <label for="sales-country" class="form-group col-md-4 mt-2 text-left"><span class="font-weight-bold"><?php _e("Target Country:","conversios"); ?></span> <p style="text-align:left;font-size: 11px;"><?php _e("If you want to target multiple countries, then create multiple campaigns.","conversios"); ?></p></label><?php echo $this->country_dropdown(); ?>
176
  </div>
177
  <div class="col-md-12 row">
178
- <label for="campaign-products" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php _e("Products in campaign:","conversios"); ?> </label>
179
- <label class="mt-2"><input type="radio" id="campaign-products" name="all_products" value="1" checked /><?php _e("Include all Merchant Center products","conversios"); ?></label>
180
- </div>
181
- <div class="col-md-12 row" style="display:none;">
182
- <div class="col-md-4"></div>
183
- <label class=""><input type="radio" id="campaign-product-partition" value="0" name="all_products" /><?php _e("Select products using product partition filters","conversios"); ?></label>
184
  </div>
185
  <div class="col-md-12 row" style="display:none;">
186
  <div class="col-md-4"></div>
187
- <label><?php _e("Product Partition filter dimension:","conversios"); ?></label><b> Category</b>
188
- </div>
189
- <div class="col-md-12 row" style="display:none;">
190
- <div class="col-md-4"></div>
191
- <label><?php _e("Dimension Value:","conversios"); ?></label>
192
- <input type="hidden" name="category_level" id="category_level" value="" />
193
- <select class="col-md-3 ml-2" name="dimension" id="dimension" onchange="changeCategory()">
194
- <?php
195
- for ($i = 0; $i < count($categories); $i++) {
196
- echo '<option value="' . $categories[$i]->id . '" level="' . $categories[$i]->level . '">' . $categories[$i]->name . '</option>';
197
- } ?>
198
- </select>
199
  </div>
 
200
  <div class="col-md-12 row">
201
- <label class="form-group col-md-12 mt-2 mb-0 text-left font-weight-bold"><?php _e("Campaign duration:","conversios"); ?> </label>
202
- <p class="ml-3" style="text-align:left; font-size: 14px;"><?php _e("Your campaign will run until you pause it. You can pause your campaign at any time, however it can take up to 30 days for google to optimize your products and ads.","conversios"); ?></p>
203
  </div>
204
  </div>
205
- <div class="col-12 row">
206
- <button onclick="showLoader()" type="submit" class="btn btn-primary" id="create_campaign" name="create_campaign"><?php _e("Create Smart Shopping Campaign","conversios"); ?></button>
207
  </div>
208
  </form>
209
  <hr>
210
  <form method="post">
211
  <div class="text-left">
212
- <p style="font-size: 14px;"><?php _e("Please note that campaigns will be created with accounts configured in previous steps.","conversios"); ?></p>
213
- <p style="font-size: 14px;"><span><?php _e("Google Merchant Center :","conversios"); ?> <?php echo $this->merchantId; ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span><?php _e("Google Ads Account Id :","conversios"); ?> <?php echo $this->currentCustomerId; ?></span></p>
214
  </div>
215
  </form>
216
  <?php
217
- } else if (isset($_GET['edit']) && $_GET['edit'] != '') { ?>
218
  <form method="post">
219
  <div class="form-group">
220
- <h2 class="lead"><?php _e("Create/Update a Smart Shopping Campaign to promote your products","conversios"); ?></h2>
221
- <p style="text-align:left; font-size: 14px;"><?php _e("A Smart Shopping campaign shows your products to potential customers across Google, Google Search Partners, the Google Display Network, YouTube, and Gmail.","conversios"); ?></p>
222
  <p style="text-align:left">
223
- <a href="https://support.google.com/google-ads/answer/7674739?hl=en" target="_blank"><?php _e("Learn more about Smart Shopping Campaigns.","conversios"); ?></a>
224
  </p>
225
  </div>
226
  <div class="form-row" style="margin-bottom: 0">
227
  <div class="col-md-12 row">
228
- <label for="campaign-name" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php _e("Campaign name:","conversios"); ?> </label>
229
- <input type="text" class="form-group col-md-6" name="campaign_name" value="<?php echo (isset($campaign_details) && $campaign_details != '')?$campaign_details->compaignName:""; ?>" id="campaign-name" required>
230
  </div>
231
  <div class="col-md-12 row">
232
- <label for="campaign-budget" class="form-group col-md-4 mt-2 mb-0 text-left"><span class="font-weight-bold"><?php _e("Daily Campaign Budget","conversios"); ?> (<?php echo $currency; ?>):</span> <p style="text-align:left;font-size: 11px;"><?php _e("Only pay if someone clicks your ad. Recommended minimum budget of","conversios"); ?> <?php echo $currency; ?>5 per day.</p></label>
233
- <input type="number" class="form-group col-md-6" name="campaign_budget" id="campaign-budget" value="<?php echo (isset($campaign_details) && $campaign_details != '')?$campaign_details->dailyBudget:""; ?>" style="height: 35px;" required>
234
  </div>
235
- <input type="hidden" name="campaign_id" value="<?php echo (isset($campaign_details) && $campaign_details != '')?$campaign_details->compaignId:""; ?>" />
236
- <input type="hidden" name="budget_id" value="<?php echo (isset($campaign_details) && $campaign_details != '')?$campaign_details->budgetId:""; ?>" />
237
- <input type="hidden" name="ad_group_id" value="<?php echo (isset($campaign_details) && $campaign_details != '')?$campaign_details->adGroupId:""; ?>" />
238
- <input type="hidden" name="ad_group_resource_name" value="<?php echo(isset($campaign_details) && $campaign_details != '')? $campaign_details->adGroupResourceName:""; ?>" />
239
  <div class="col-md-12 row">
240
- <label for="sales-country" class="form-group col-md-4 mt-2 text-left"><span class="font-weight-bold"><?php _e("Target Country:","conversios"); ?></span> <p style="text-align:left;font-size: 11px;"><?php _e("If you want to target multiple countries, then create multiple campaigns.","conversios"); ?></p></label>
241
  <?php echo $this->country_dropdown($defaultCountry, true); ?>
242
  </div>
243
  <div class="col-md-12 row">
244
- <label for="campaign-products" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php _e("Products in campaign:","conversios"); ?> </label>
245
  <label class="mt-2">
246
  <?php
247
- if(isset($campaign_details) && $campaign_details->category_level > 0 && $campaign_details->category_id > 0){
248
- echo '<input type="radio" id="campaign-products" name="all_products" value="1" />';
249
- }else{
250
- echo '<input type="radio" id="campaign-products" name="all_products" value="1" checked />';
251
- } ?> <?php _e("Include all Merchant Center products","conversios"); ?></label>
252
- </div>
253
- <div class="col-md-12 row" style="display: none;">
254
- <div class="col-md-4"></div>
255
- <label class="">
256
  <?php
257
- if (isset($campaign_details) && $campaign_details->category_level > 0 && $campaign_details->category_id > 0) {
258
- echo '<input type="radio" id="campaign-product-partition" value="0" name="all_products" checked />';
259
- } else {
260
- echo '<input type="radio" id="campaign-product-partition" value="0" name="all_products" />';
261
- } ?> <?php _e("Select products using product partition filters","conversios"); ?></label>
262
- </div>
263
- <div class="col-md-12 row" style="display: none;">
264
- <div class="col-md-4"></div>
265
- <label><?php _e("Product Partition filter dimension:","conversios"); ?></label><b> Category</b>
266
  </div>
267
  <div class="col-md-12 row" style="display: none;">
268
  <div class="col-md-4"></div>
269
- <label><?php _e("Dimension Value:","conversios"); ?></label>
270
- <input type="hidden" name="category_level" id="category_level" value=" <?php echo (isset($campaign_details) && $campaign_details != '')?$campaign_details->category_level:""; ?>" />
271
- <select class="col-md-3 ml-2" name="dimension" id="dimension" onchange="changeCategory()">
 
 
 
 
272
  <?php
273
- if (isset($campaign_details) && $campaign_details != '') {
274
- for ($i = 0; $i < count($categories); $i++) {
275
- if ($campaign_details->category_id == $categories[$i]->id) {
276
- echo '<option value="' . $categories[$i]->id . '" level="' . $categories[$i]->level . '" selected="selected">' . $categories[$i]->name . '</option>';
277
- } else {
278
- echo '<option value="' . $categories[$i]->id . '" level="' . $categories[$i]->level . '">' . $categories[$i]->name . '</option>';
279
- }
280
- }
281
- }?>
282
- </select>
283
  </div>
284
  <div class="col-md-12 row">
285
- <label class="form-group col-md-12 mt-2 mb-0 text-left font-weight-bold"><?php _e("Campaign duration:","conversios"); ?> </label>
286
- <p class="ml-3" style="text-align:left; font-size: 14px;"><?php _e("Your campaign will run until you pause it. You can pause your campaign at any time, however it can take up to 30 days for google to optimize your products and ads.","conversios"); ?></p>
287
  </div>
288
  </div>
289
- <div class="col-12 row">
290
- <button onclick="showLoader()" type="submit" class="btn btn-primary btn-success" id="update_campaign" name="update_campaign"><?php _e("Update Smart Shopping Campaign","conversios"); ?></button>
291
  </div>
292
  </form>
293
  <hr>
294
  <form method="post">
295
  <div class="text-left">
296
- <p style="font-size: 14px;"><?php _e("Please note that campaigns will be created with accounts configured in previous steps.","conversios"); ?></p>
297
- <p style="font-size: 14px;"><span><?php _e("Google Merchant Center :","conversios"); ?> <?php echo $this->merchantId; ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span><?php _e("Google Ads Account Id :","conversios"); ?> <?php echo $this->currentCustomerId; ?></span></p>
298
  </div>
299
  </form>
300
  <?php } ?>
@@ -320,7 +290,7 @@ class AddCampaign {
320
  let level = jQuery('#dimension').find('option[value='+dimension+']'). attr('level');
321
  jQuery('#category_level').val(level);
322
  }
323
- function selectCountry() {
324
  var sales_country = document.getElementById('sales_country').value;
325
  var customer_id = "<?php echo $this->currentCustomerId; ?>";
326
  jQuery('#feed-spinner').css('display', 'block');
@@ -343,7 +313,7 @@ class AddCampaign {
343
  jQuery('#feed-spinner').css('display', 'none');
344
  }
345
  );
346
- }
347
  function showLoader() {
348
  if($('#campaign-name').val()!=''&&$('#campaign-budget').val()!=''){
349
  jQuery('#feed-spinner').css('display', 'block');
32
  $contData = json_decode($getCountris);
33
  $wooCountry = $this->TVC_Admin_Helper->get_woo_country();
34
  $is_disabled = ($is_disabled) ? "style='max-width:30rem;height:35px;pointer-events:none;background:#f2f2f2;'" : "style='max-width: 30rem;height: 35px;'";
35
+ $data = "<select id='sales_country' name='sales_country' class='form-group col-md-6' readonly='true' $is_disabled'>";
36
  foreach ($contData as $key => $value) {
37
  $selected = ($value->code == $wooCountry) ? "selected='selected'" : "";
38
+ $data .= "<option value=" . esc_attr($value->code) . " " . esc_attr($selected) . " >" . esc_attr($value->name) . "</option>";
39
  }
40
  $data .= "</select>";
41
  return $data;
61
  }
62
 
63
  $defaultCountry = $this->TVC_Admin_Helper->get_woo_country();
64
+ /*$category_list = $api_obj->getCategories($defaultCountry);
65
  if(isset($category_list->errors) && !empty($category_list->errors)){
66
  }else{
67
  $category_list = isset($category_list->data) ? $category_list->data : [];
68
  if (isset($category_list['status']) && $category_list['status'] == 200) {
69
  $categories = $category_list['data'];
70
  }
71
+ }*/
72
 
73
 
74
  if (isset($_POST['create_campaign'])) {
75
+ $campaign_name = esc_attr(isset($_POST['campaign_name'])?sanitize_text_field($_POST['campaign_name']):"");
76
+ $campaign_budget = esc_attr(isset($_POST['campaign_budget'])?sanitize_text_field($_POST['campaign_budget']):"");
77
+ $sales_country = esc_attr(isset($_POST['sales_country'])?sanitize_text_field($_POST['sales_country']):"");
78
+ $all_products = esc_attr(isset($_POST['all_products'])?sanitize_text_field($_POST['all_products']):"");
79
+ $category_id = esc_attr(isset($_POST['dimension'])?sanitize_text_field($_POST['dimension']):"");
80
+ $category_level = esc_attr(isset($_POST['category_level'])?sanitize_text_field($_POST['category_level']):"");
81
 
82
  $campaign = $api_obj->createCampaign($campaign_name, $campaign_budget, $sales_country, $all_products, $category_id, $category_level);
83
  if(isset($campaign->errors) && !empty($campaign->errors)){
84
  $class = 'alert-message tvc-alert-error';
85
+ $message = esc_html__((isset($campaign->errors) && isset($campaign->errors[0])) ? $campaign->errors[0] : 'Error', 'conversios');
86
  }else{
87
  $class = 'alert-message tvc-alert-success';
88
  $campaign_neme = isset($campaign->data)?'with Resource name '.$campaign->data:"";
89
+ $message = esc_html__('Smart Shopping Campaign Created Successfully '.$campaign_neme, 'conversios');
90
  }
91
  }else if (isset($_POST['update_campaign'])) {
92
+ $campaign_name = esc_attr(isset($_POST['campaign_name'])?sanitize_text_field($_POST['campaign_name']):"");
93
+ $campaign_budget = esc_attr(isset($_POST['campaign_budget'])?sanitize_text_field($_POST['campaign_budget']):"");
94
+ $campaign_id = esc_attr(isset($_POST['campaign_id'])?sanitize_text_field($_POST['campaign_id']):"");
95
+ $budget_id = esc_attr(isset($_POST['budget_id'])?sanitize_text_field($_POST['budget_id']):"");
96
+ $sales_country = esc_attr(isset($_POST['sales_country'])?sanitize_text_field($_POST['sales_country']):"");
97
+ $all_products = esc_attr(isset($_POST['all_products'])?sanitize_text_field($_POST['all_products']):"");
98
+ $ad_group_id = esc_attr(isset($_POST['ad_group_id'])?sanitize_text_field($_POST['ad_group_id']):"");
99
+ $ad_group_resource_name = esc_attr(isset($_POST['ad_group_resource_name'])?sanitize_text_field($_POST['ad_group_resource_name']):"");
100
+ $category_id = esc_attr(isset($_POST['dimension']) ? sanitize_text_field($_POST['dimension']):"");
101
+ $category_level = esc_attr(isset($_POST['category_level'])?sanitize_text_field($_POST['category_level']):"");
102
 
103
  $campaign = $api_obj->updateCampaign($campaign_name, $campaign_budget, $campaign_id, $budget_id, $sales_country, $all_products, $category_id, $category_level, $ad_group_id, $ad_group_resource_name);
104
  if (isset($campaign->errors) && !empty($campaign->errors)) {
105
  $class = 'alert-message tvc-alert-error';
106
+ $message = esc_html__(isset($campaign->errors) ? $campaign->errors[0] : 'Error', 'conversios');
107
  } else if(isset($campaign->data)){
108
  $campaign_neme = isset($campaign->data)?'with Resource name '.$campaign->data:"";
109
  $class = 'alert-message tvc-alert-success';
110
+ $message = esc_html__('Smart Shopping Campaign Updated Successfully ' . $campaign_neme, 'conversios');
111
+ // $url = admin_url('admin.php?page=tvc-configuration-page');
112
+ //wp_redirect($url);
 
113
  }
114
+ ?>
115
+ <script>jQuery('#feed-spinner').css('display', 'none');</script>
116
+ <?php
117
  }
118
  $currency = $this->TVC_Admin_Helper->get_user_currency_symbol();
119
+ if (isset($_GET['edit']) && sanitize_text_field($_GET['edit']) != '') {
120
+ $campaign_details_res = $api_obj->getCampaignDetails(sanitize_text_field($_GET['edit']));
121
  if (isset($campaign_details_res->errors) && !empty($campaign_details_res->errors)) {
122
  $error_code = array_keys($campaign_details_res->errors)[0];
123
  if($error_code == 404){
124
+ $error_msg = esc_html__("Campaign details not found","conversios");
125
  }else{
126
  if (isset($campaign_details_res->error_data) && !empty($campaign_details_res->error_data)) {
127
  // $error_msg = array_values($campaign_details_res->error_data)[0]->errors[0];
128
  }
129
  }
130
  $class = 'alert-message tvc-alert-error';
131
+ $message = esc_html__(isset($error_msg) ? $error_msg : 'There was some error fetching campaign details.', 'conversios');
132
  } else {
133
  $campaign_details = $campaign_details_res->data;
134
  if ($campaign_details['status'] == 200) {
148
  <div class="row">
149
  <div class="col-md-6 col-lg-8 edit-section">
150
  <div class="edit-header-section">
151
+ <script>
152
+ document.write('<a href="<?php echo esc_url_raw($this->site_url."shopping_campaigns_page"); ?>" class="back-btn"><img src="'+"<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL."/admin/images/icon/left-angle-arrow.svg"); ?>"+'" alt="back"/><span>'+"<?php esc_html_e("Campaigns","conversios");?>"+'</span></a>');
 
153
  </script>
154
  </div>
155
  <?php
156
  if (!isset($_GET['edit'])) { ?>
157
  <form method="post" id="create-form">
158
  <div class="form-group">
159
+ <h2 class="lead"><?php esc_html_e("Create a Smart Shopping Campaign to promote your products","conversios"); ?></h2>
160
+ <p style="text-align:left; font-size: 14px;"><?php esc_html_e("A Smart Shopping campaign shows your products to potential customers across Google, Google Search Partners, the Google Display Network, YouTube, and Gmail.","conversios"); ?></p>
161
  <p style="text-align:left">
162
+ <a href="<?php echo esc_url_raw("https://support.google.com/google-ads/answer/7674739?hl=en"); ?>" target="_blank"><?php esc_html_e("Learn more about Smart Shopping Campaigns.","conversios"); ?></a>
163
  </p>
164
  </div>
165
  <div class="form-row" style="margin-bottom: 0">
166
  <div class="col-md-12 row">
167
+ <label for="campaign-name" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php esc_html_e("Campaign name: ","conversios"); ?></label>
168
  <input type="text" class="form-group col-md-6" name="campaign_name" id="campaign-name" required>
169
  </div>
170
  <div class="col-md-12 row">
171
+ <label for="campaign-budget" class="form-group col-md-4 mt-2 mb-0 text-left"><span class="font-weight-bold"><?php esc_html_e("Daily Campaign Budget","conversios"); ?> (<?php echo esc_attr($currency) ;?>):</span> <p style="text-align:left;font-size: 11px;"><?php esc_html_e("Only pay if someone clicks your ad. Recommended minimum budget of $5 per day.","conversios"); ?></p></label>
172
  <input type="number" class="form-group col-md-6" name="campaign_budget" id="campaign-budget" style="height: 35px;" required>
173
  </div>
174
  <div class="col-md-12 row">
175
+ <label for="sales-country" class="form-group col-md-4 mt-2 text-left"><span class="font-weight-bold"><?php esc_html_e("Target Country:","conversios"); ?></span> <p style="text-align:left;font-size: 11px;"><?php esc_html_e("If you want to target multiple countries, then create multiple campaigns.","conversios"); ?></p></label><?php echo $this->country_dropdown(); ?>
176
  </div>
177
  <div class="col-md-12 row">
178
+ <label for="campaign-products" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php esc_html_e("Products in campaign:","conversios"); ?> </label>
179
+ <label class="mt-2"><input type="radio" id="campaign-products" name="all_products" value="1" checked /><?php esc_html_e("Include all Merchant Center products","conversios"); ?></label>
 
 
 
 
180
  </div>
181
  <div class="col-md-12 row" style="display:none;">
182
  <div class="col-md-4"></div>
183
+ <label class=""><input type="radio" id="campaign-product-partition" value="0" name="all_products" /><?php esc_html_e("Select products using product partition filters","conversios"); ?></label>
 
 
 
 
 
 
 
 
 
 
 
184
  </div>
185
+
186
  <div class="col-md-12 row">
187
+ <label class="form-group col-md-12 mt-2 mb-0 text-left font-weight-bold"><?php esc_html_e("Campaign duration:","conversios"); ?> </label>
188
+ <p class="ml-3" style="text-align:left; font-size: 14px;"><?php esc_html_e("Your campaign will run until you pause it. You can pause your campaign at any time, however it can take up to 30 days for google to optimize your products and ads.","conversios"); ?></p>
189
  </div>
190
  </div>
191
+ <div class="col-12">
192
+ <button onclick="showLoader()" type="submit" class="btn btn-primary" id="create_campaign" name="create_campaign"><?php esc_html_e("Create Smart Shopping Campaign","conversios"); ?></button>
193
  </div>
194
  </form>
195
  <hr>
196
  <form method="post">
197
  <div class="text-left">
198
+ <p style="font-size: 14px;"><?php esc_html_e("Please note that campaigns will be created with accounts configured in previous steps.","conversios"); ?></p>
199
+ <p style="font-size: 14px;"><span><?php esc_html_e("Google Merchant Center :","conversios"); ?> <?php echo esc_attr($this->merchantId); ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span><?php esc_html_e("Google Ads Account Id :","conversios"); ?> <?php echo esc_attr($this->currentCustomerId); ?></span></p>
200
  </div>
201
  </form>
202
  <?php
203
+ } else if (isset($_GET['edit']) && sanitize_text_field($_GET['edit']) != '') { ?>
204
  <form method="post">
205
  <div class="form-group">
206
+ <h2 class="lead"><?php esc_html_e("Create/Update a Smart Shopping Campaign to promote your products","conversios"); ?></h2>
207
+ <p style="text-align:left; font-size: 14px;"><?php esc_html_e("A Smart Shopping campaign shows your products to potential customers across Google, Google Search Partners, the Google Display Network, YouTube, and Gmail.","conversios"); ?></p>
208
  <p style="text-align:left">
209
+ <a href="<?php echo esc_url_raw("https://support.google.com/google-ads/answer/7674739?hl=en"); ?>" target="_blank"><?php esc_html_e("Learn more about Smart Shopping Campaigns.","conversios"); ?></a>
210
  </p>
211
  </div>
212
  <div class="form-row" style="margin-bottom: 0">
213
  <div class="col-md-12 row">
214
+ <label for="campaign-name" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php esc_html_e("Campaign name:","conversios"); ?> </label>
215
+ <input type="text" class="form-group col-md-6" name="campaign_name" value="<?php echo (isset($campaign_details) && $campaign_details != '')?esc_attr($campaign_details->compaignName):""; ?>" id="campaign-name" required>
216
  </div>
217
  <div class="col-md-12 row">
218
+ <label for="campaign-budget" class="form-group col-md-4 mt-2 mb-0 text-left"><span class="font-weight-bold"><?php esc_html_e("Daily Campaign Budget","conversios"); ?> (<?php echo $currency; ?>):</span> <p style="text-align:left;font-size: 11px;"><?php esc_html_e("Only pay if someone clicks your ad. Recommended minimum budget of","conversios"); ?> <?php echo esc_attr($currency); ?>5 per day.</p></label>
219
+ <input type="number" class="form-group col-md-6" name="campaign_budget" id="campaign-budget" value="<?php echo (isset($campaign_details) && $campaign_details != '')?esc_attr($campaign_details->dailyBudget):""; ?>" style="height: 35px;" required>
220
  </div>
221
+ <input type="hidden" name="campaign_id" value="<?php echo (isset($campaign_details) && $campaign_details != '')?esc_attr($campaign_details->compaignId):""; ?>" />
222
+ <input type="hidden" name="budget_id" value="<?php echo (isset($campaign_details) && $campaign_details != '')?esc_attr($campaign_details->budgetId):""; ?>" />
223
+ <input type="hidden" name="ad_group_id" value="<?php echo (isset($campaign_details) && $campaign_details != '')?esc_attr($campaign_details->adGroupId):""; ?>" />
224
+ <input type="hidden" name="ad_group_resource_name" value="<?php echo(isset($campaign_details) && $campaign_details != '')? esc_attr($campaign_details->adGroupResourceName):""; ?>" />
225
  <div class="col-md-12 row">
226
+ <label for="sales-country" class="form-group col-md-4 mt-2 text-left"><span class="font-weight-bold"><?php esc_html_e("Target Country:","conversios"); ?></span> <p style="text-align:left;font-size: 11px;"><?php esc_html_e("If you want to target multiple countries, then create multiple campaigns.","conversios"); ?></p></label>
227
  <?php echo $this->country_dropdown($defaultCountry, true); ?>
228
  </div>
229
  <div class="col-md-12 row">
230
+ <label for="campaign-products" class="form-group col-md-4 mt-2 text-left font-weight-bold"><?php esc_html_e("Products in campaign:","conversios"); ?> </label>
231
  <label class="mt-2">
232
  <?php
233
+ if(isset($campaign_details) && $campaign_details->category_level > 0 && $campaign_details->category_id > 0){?>
234
+ <input type="radio" id="campaign-products" name="all_products" value="1" />
 
 
 
 
 
 
 
235
  <?php
236
+ }else{ ?>
237
+ <input type="radio" id="campaign-products" name="all_products" value="1" checked />
238
+ <?php
239
+ } ?> <?php esc_html_e("Include all Merchant Center products","conversios"); ?></label>
 
 
 
 
 
240
  </div>
241
  <div class="col-md-12 row" style="display: none;">
242
  <div class="col-md-4"></div>
243
+ <label>
244
+ <?php
245
+ if (isset($campaign_details) && $campaign_details->category_level > 0 && $campaign_details->category_id > 0) { ?>
246
+ <input type="radio" id="campaign-product-partition" value="0" name="all_products" checked />
247
+ <?php
248
+ } else { ?>
249
+ <input type="radio" id="campaign-product-partition" value="0" name="all_products" />
250
  <?php
251
+ } ?>
252
+ <?php esc_html_e("Select products using product partition filters","conversios"); ?></label>
 
 
 
 
 
 
 
 
253
  </div>
254
  <div class="col-md-12 row">
255
+ <label class="form-group col-md-12 mt-2 mb-0 text-left font-weight-bold"><?php esc_html_e("Campaign duration:","conversios"); ?> </label>
256
+ <p class="ml-3" style="text-align:left; font-size: 14px;"><?php esc_html_e("Your campaign will run until you pause it. You can pause your campaign at any time, however it can take up to 30 days for google to optimize your products and ads.","conversios"); ?></p>
257
  </div>
258
  </div>
259
+ <div class="col-12">
260
+ <button onclick="showLoader()" type="submit" class="btn btn-primary btn-success" id="update_campaign" name="update_campaign"><?php esc_html_e("Update Smart Shopping Campaign","conversios"); ?></button>
261
  </div>
262
  </form>
263
  <hr>
264
  <form method="post">
265
  <div class="text-left">
266
+ <p style="font-size: 14px;"><?php esc_html_e("Please note that campaigns will be created with accounts configured in previous steps.","conversios"); ?></p>
267
+ <p style="font-size: 14px;"><span><?php esc_html_e("Google Merchant Center :","conversios"); ?> <?php echo esc_attr($this->merchantId); ?></span>&nbsp;&nbsp;&nbsp;&nbsp;<span><?php esc_html_e("Google Ads Account Id :","conversios"); ?> <?php echo esc_attr($this->currentCustomerId); ?></span></p>
268
  </div>
269
  </form>
270
  <?php } ?>
290
  let level = jQuery('#dimension').find('option[value='+dimension+']'). attr('level');
291
  jQuery('#category_level').val(level);
292
  }
293
+ /*function selectCountry() {
294
  var sales_country = document.getElementById('sales_country').value;
295
  var customer_id = "<?php echo $this->currentCustomerId; ?>";
296
  jQuery('#feed-spinner').css('display', 'block');
313
  jQuery('#feed-spinner').css('display', 'none');
314
  }
315
  );
316
+ } */
317
  function showLoader() {
318
  if($('#campaign-name').val()!=''&&$('#campaign-budget').val()!=''){
319
  jQuery('#feed-spinner').css('display', 'block');
includes/setup/class-conversios-dashboard.php CHANGED
@@ -32,20 +32,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
32
 
33
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
34
  $this->CustomApi = new CustomApi();
35
- $this->connect_url = $this->TVC_Admin_Helper->get_custom_connect_url(admin_url().'admin.php?page=conversios');
36
  $this->subscription_id = $this->TVC_Admin_Helper->get_subscriptionId();
37
  // update API data to DB while expired token
38
 
39
- if ( isset($_GET['subscription_id']) && $_GET['subscription_id'] == $this->subscription_id){
40
- if ( isset($_GET['g_mail']) && $_GET['g_mail']){
41
  $this->TVC_Admin_Helper->update_subscription_details_api_to_db();
42
  }
43
- } else if(isset($_GET['subscription_id']) && $_GET['subscription_id']){
44
- $this->notice = __("You tried signing in with different email. Please try again by signing it with the email id that you used to set up the plugin earlier. <a href=\'".$this->TVC_Admin_Helper->get_conversios_site_url()."\' target=\'_blank\'>Reach out to us</a> if you have any difficulty.","conversios");
45
  }
46
  $this->is_refresh_token_expire = $this->TVC_Admin_Helper->is_refresh_token_expire();
47
  $this->subscription_data = $this->TVC_Admin_Helper->get_user_subscription_data();
48
- $this->pro_plan_site = esc_url($this->TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=dashboard&utm_campaign=Upsell+at+Conversios');
49
  if(isset($this->subscription_data->plan_id) && !in_array($this->subscription_data->plan_id, array("1"))){
50
  $this->plan_id = $this->subscription_data->plan_id;
51
  }
@@ -109,10 +109,10 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
109
  }
110
  }
111
  public function load_html(){
112
- do_action('conversios_start_html_'.$_GET['page']);
113
  $this->current_html();
114
  $this->current_js();
115
- do_action('conversios_end_html_'.$_GET['page']);
116
  }
117
 
118
  /**
@@ -127,13 +127,13 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
127
  /**
128
  * daterage script
129
  **/
130
- var notice='<?php echo $this->notice; ?>';
131
  if(notice != ""){
132
  tvc_helper.tvc_alert("error","Email error",notice);
133
  }
134
  var plan_id = '<?php echo esc_attr($this->plan_id); ?>';
135
  var g_mail = '<?php echo esc_attr($this->g_mail); ?>';
136
- var is_refresh_token_expire = '<?php echo $this->is_refresh_token_expire; ?>';
137
  is_refresh_token_expire = (is_refresh_token_expire == "")?false:true;
138
  console.log(is_refresh_token_expire);
139
  //$(function() {
@@ -251,7 +251,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
251
  const systemZoom = width / window.screen.availWidth;
252
  const left = (width - w) / 2 / systemZoom + dualScreenLeft;
253
  const top = (height - h) / 2 / systemZoom + dualScreenTop;
254
- var url ='<?php echo $this->connect_url; ?>';
255
  url = url.replace(/&amp;/g, '&');
256
  const newWindow = window.open(url, "newwindow", config= `scrollbars=yes,
257
  width=${w / systemZoom},
@@ -287,8 +287,8 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
287
  <div class="dashbrdpage-wrap">
288
  <div class="dflex align-items-center mt24 dshbrdtoparea">
289
  <div class="dashtp-left">
290
- <button class="dashtpleft-btn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/download-icon.png'); ?>" alt="" /><?php _e("Download PDF","conversios"); ?></button>
291
- <button class="dashtpleft-btn <?php echo $this->add_upgrdsbrs_btn_calss('schedule_email'); ?>"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/clock-icon.png'); ?>" alt="" /><?php _e("Schedule Email","conversios"); ?></button>
292
  </div>
293
  <div class="dashtp-right">
294
 
@@ -303,123 +303,123 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
303
  <?php if($this->plan_id != 1){?>
304
  <div id="reportrange" class="dshtpdaterange" >
305
  <div class="dateclndicn">
306
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/claendar-icon.png'); ?>" alt="" />
307
  </div>
308
  <span class="daterangearea report_range_val"></span>
309
- <div class="careticn"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/caret-down.png'); ?>" alt="" /></div>
310
  </div>
311
  <?php } else{ ?>
312
  <div class="dshtpdaterange <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>">
313
  <div class="dateclndicn">
314
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/claendar-icon.png'); ?>" alt="" />
315
  </div>
316
  <span class="daterangearea report_range_val"></span>
317
- <div class="careticn"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/caret-down.png'); ?>" alt="" /></div>
318
  </div>
319
  <?php }?>
320
 
321
  </div>
322
  </div>
323
  <?php if($this->ga_traking_type == "GA4"){?>
324
- <div class="temp_note"><p><?php _e("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.","conversios"); ?></p></div>
325
  <?php } ?>
326
  <!--- dashboard summary section start -->
327
  <div class="wht-rnd-shdwbx mt24 dashsmry-wrap">
328
  <div class="dashsmry-item">
329
  <div class="dashsmrybx" id="s1_transactionsPerSession">
330
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Conversion Rate","conversios"); ?></div>
331
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
332
  <div class="updownsmry dash-smry-compare-val">
333
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/green-up.png'); ?>" alt="" />
334
  %
335
  </div>
336
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
337
  </div>
338
  <div class="dashsmrybx" id="s1_transactionRevenue">
339
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Revenue","conversios"); ?></div>
340
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
341
  <div class="updownsmry dash-smry-compare-val">
342
  %
343
  </div>
344
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
345
  </div>
346
  <div class="dashsmrybx mblsmry3bx" id="s1_transactions">
347
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Total Transactions","conversios"); ?></div>
348
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
349
  <div class="updownsmry dash-smry-compare-val">
350
  %
351
  </div>
352
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
353
  </div>
354
  <div class="dashsmrybx mblsmry3bx" id="s1_revenuePerTransaction">
355
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Avg. Order Value","conversios"); ?></div>
356
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
357
  <div class="updownsmry dash-smry-compare-val">
358
  %
359
  </div>
360
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
361
  </div>
362
  <div class="dashsmrybx mblsmry3bx flwdthmblbx" id="s1_productAddsToCart">
363
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Added to Cart","conversios"); ?></div>
364
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
365
  <div class="updownsmry dash-smry-compare-val">
366
  %
367
  </div>
368
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
369
  </div>
370
  </div>
371
  <div class="dashsmry-item">
372
  <div class="dashsmrybx" id="s1_productRemovesFromCart">
373
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Removed from Cart","conversios"); ?></div>
374
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
375
  <div class="updownsmry dash-smry-compare-val">
376
  %
377
  </div>
378
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
379
  </div>
380
  <div class="dashsmrybx" id="s1_sessions">
381
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Sessions","conversios"); ?></div>
382
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
383
  <div class="updownsmry dash-smry-compare-val">
384
  %
385
  </div>
386
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
387
  </div>
388
  <div class="dashsmrybx" id="s1_users">
389
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Total Users","conversios"); ?></div>
390
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
391
  <div class="updownsmry dash-smry-compare-val">
392
  %
393
  </div>
394
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
395
  </div>
396
  <div class="dashsmrybx" id="s1_newUsers">
397
- <div class="dshsmrycattxt dash-smry-title"><?php _e("New Users","conversios"); ?></div>
398
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
399
  <div class="updownsmry dash-smry-compare-val">
400
  %
401
  </div>
402
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
403
  </div>
404
  <div class="dashsmrybx" id="s1_productDetailViews">
405
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Product Views","conversios"); ?></div>
406
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
407
  <div class="updownsmry dash-smry-compare-val">
408
  %
409
  </div>
410
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
411
  </div>
412
 
413
  </div>
414
  <?php /*
415
  <div class="dashsmry-item">
416
  <div class="dashsmrybx" id="s1_transactionShipping">
417
- <div class="dshsmrycattxt dash-smry-title"><?php _e("Shipping","conversios"); ?></div>
418
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
419
  <div class="updownsmry dash-smry-compare-val">
420
  %
421
  </div>
422
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
423
  </div>
424
  <div class="dashsmrybx" id="s1_transactionTax">
425
  <div class="dshsmrycattxt dash-smry-title">TAX</div>
@@ -427,7 +427,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
427
  <div class="updownsmry dash-smry-compare-val">
428
  %
429
  </div>
430
- <div class="dshsmryprdtxt"><?php _e("From Previous Period","conversios"); ?></div>
431
  </div>
432
  </div> */?>
433
  </div>
@@ -440,7 +440,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
440
  <div class="col50">
441
  <div class="chartbx ecomfunnchrtbx ecom-funn-chrt-bx">
442
  <div class="chartcntnbx">
443
- <h5><?php _e("Ecommerce Conversion Funnel","conversios"); ?></h5>
444
  <div class="chartarea">
445
  <canvas id="ecomfunchart" width="400" height="300"></canvas>
446
  </div>
@@ -448,23 +448,23 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
448
  <div class="ecomchartinfo">
449
  <div class="ecomchrtinfoflex">
450
  <div class="ecomchartinfoitem">
451
- <div class="ecomchartinfolabel"><?php _e("Sessions","conversios"); ?></div>
452
  <div class="chartpercarrow conversion_s1"></div>
453
  </div>
454
  <div class="ecomchartinfoitem">
455
- <div class="ecomchartinfolabel"><?php _e("Product View","conversios"); ?></div>
456
  <div class="chartpercarrow conversion_s2"></div>
457
  </div>
458
  <div class="ecomchartinfoitem">
459
- <div class="ecomchartinfolabel"><?php _e("Add to Cart","conversios"); ?></div>
460
  <div class="chartpercarrow conversion_s3"></div>
461
  </div>
462
  <div class="ecomchartinfoitem">
463
- <div class="ecomchartinfolabel"><?php _e("Checkouts","conversios"); ?></div>
464
  <div class="chartpercarrow conversion_s4"></div>
465
  </div>
466
  <div class="ecomchartinfoitem">
467
- <div class="ecomchartinfolabel"><?php _e("Order Confirmation","conversios"); ?></div>
468
  </div>
469
  </div>
470
  </div>
@@ -475,20 +475,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
475
  <div class="col50">
476
  <div class="chartbx ecomfunnchrtbx">
477
  <div class="chartcntnbx prochrtftr">
478
- <h5><?php _e("Ecommerce Conversion Funnel","conversios"); ?></h5>
479
  <div class="chartarea">
480
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/ecom-chart.jpg'); ?>" alt="" />
481
  </div>
482
  </div>
483
  <div class="prochrtovrbox">
484
  <div class="prochrtcntn">
485
  <div class="prochrttop">
486
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
487
  Locked
488
  </div>
489
- <h5><?php _e("Conversion Funnel","conversios"); ?></h5>
490
- <p><?php _e("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.","conversios"); ?></p>
491
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
492
  </div>
493
  </div>
494
  </div>
@@ -499,7 +499,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
499
  <div class="col50">
500
  <div class="chartbx ecomfunnchrtbx ecom-checkout-funn-chrt-bx">
501
  <div class="chartcntnbx">
502
- <h5><?php _e("Ecommerce Checkout Funnel","conversios"); ?></h5>
503
  <div class="chartarea">
504
  <canvas id="ecomcheckoutfunchart" width="400" height="300"></canvas>
505
  </div>
@@ -507,19 +507,19 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
507
  <div class="ecomchartinfo ecomcheckoutfunchartinfo">
508
  <div class="ecomchrtinfoflex">
509
  <div class="ecomchartinfoitem">
510
- <div class="ecomchartinfolabel"><?php _e("Checkout Step 1","conversios"); ?></div>
511
  <div class="chartpercarrow checkoutfunn_s1"></div>
512
  </div>
513
  <div class="ecomchartinfoitem">
514
- <div class="ecomchartinfolabel"><?php _e("Checkout Step 2","conversios"); ?></div>
515
  <div class="chartpercarrow checkoutfunn_s2"></div>
516
  </div>
517
  <div class="ecomchartinfoitem">
518
- <div class="ecomchartinfolabel"><?php _e("Checkout Step 3","conversios"); ?></div>
519
  <div class="chartpercarrow checkoutfunn_s3"></div>
520
  </div>
521
  <div class="ecomchartinfoitem">
522
- <div class="ecomchartinfolabel"><?php _e("Purchase","conversios"); ?></div>
523
  </div>
524
 
525
  </div>
@@ -532,20 +532,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
532
  <div class="col50">
533
  <div class="chartbx ecomfunnchrtbx">
534
  <div class="chartcntnbx prochrtftr">
535
- <h5><?php _e("Checkout Funnel","conversios"); ?></h5>
536
  <div class="chartarea">
537
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/ecom-chart.jpg'); ?>" alt="" />
538
  </div>
539
  </div>
540
  <div class="prochrtovrbox">
541
  <div class="prochrtcntn">
542
  <div class="prochrttop">
543
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
544
  Locked
545
  </div>
546
- <h5><?php _e("Checkout Funnel","conversios"); ?></h5>
547
- <p><?php _e("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.","conversios"); ?></p>
548
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
549
  </div>
550
  </div>
551
  </div>
@@ -561,24 +561,24 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
561
  <div class="mt24 whiteroundedbx dshreport-sec">
562
  <div class="row dsh-reprttop">
563
  <div class="dshrprttp-left">
564
- <h4><?php _e("Product Performance Report","conversios"); ?></h4>
565
- <a href="#" class="viewallbtn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>">View all <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'); ?>" alt="" /></a>
566
  </div>
567
  </div>
568
  <div class="dashtablewrp product_performance_report" id="product_performance_report">
569
  <table class="dshreporttble mbl-table" >
570
  <thead>
571
  <tr>
572
- <th class="prdnm-cell"><?php _e("Product Name","conversios"); ?></th>
573
- <th><?php _e("Views","conversios"); ?></th>
574
- <th><?php _e("Added to Cart","conversios"); ?></th>
575
- <th><?php _e("Orders","conversios"); ?></th>
576
- <th><?php _e("Qty","conversios"); ?></th>
577
- <th><?php _e("Revenue","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
578
- <th><?php _e("Avg Price","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
579
- <th><?php _e("Refund Amount","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
580
- <th><?php _e("Cart to details (%)","conversios"); ?></th>
581
- <th><?php _e("Buy to details (%)","conversios"); ?></th>
582
  </tr>
583
  </thead>
584
  <tbody>
@@ -592,20 +592,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
592
  <div class="col">
593
  <div class="chartbx ecomfunnchrtbx">
594
  <div class="chartcntnbx prochrtftr">
595
- <h5><?php _e("Product Performance Report","conversios"); ?></h5>
596
  <div class="chartarea">
597
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
598
  </div>
599
  </div>
600
  <div class="prochrtovrbox">
601
  <div class="prochrtcntn">
602
  <div class="prochrttop">
603
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
604
- <?php _e("Locked","conversios"); ?>
605
  </div>
606
- <h5><?php _e("Product Performance Report","conversios"); ?></h5>
607
- <p><?php _e("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.","conversios"); ?></p>
608
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
609
  </div>
610
  </div>
611
  </div>
@@ -620,24 +620,24 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
620
  <div class="mt24 whiteroundedbx dshreport-sec">
621
  <div class="row dsh-reprttop">
622
  <div class="dshrprttp-left">
623
- <h4><?php _e("Source/Medium Performance Report","conversios"); ?></h4>
624
- <a href="" class="viewallbtn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>"><?php _e("View all","conversios"); ?> <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'); ?>" alt="" /></a>
625
  </div>
626
  </div>
627
  <div class="dashtablewrp medium_performance_report" id="medium_performance_report">
628
  <table class="dshreporttble mbl-table" >
629
  <thead>
630
  <tr>
631
- <th class="prdnm-cell"><?php _e("Source/Medium","conversios"); ?></th>
632
- <th><?php _e("Conversion (%)","conversios"); ?></th>
633
- <th><?php _e("Revenue","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
634
- <th><?php _e("Total transactions","conversios"); ?></th>
635
- <th><?php _e("Avg Order value","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
636
- <th><?php _e("Added to carts","conversios"); ?></th>
637
- <th><?php _e("removed from cart","conversios"); ?></th>
638
- <th><?php _e("Product views","conversios"); ?></th>
639
- <th><?php _e("Users","conversios"); ?></th>
640
- <th><?php _e("Sessions","conversios"); ?></th>
641
  </tr>
642
  </thead>
643
  <tbody>
@@ -652,20 +652,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
652
  <div class="col">
653
  <div class="chartbx ecomfunnchrtbx">
654
  <div class="chartcntnbx prochrtftr">
655
- <h5><?php _e("Source/Medium Performance Report","conversios"); ?></h5>
656
  <div class="chartarea">
657
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
658
  </div>
659
  </div>
660
  <div class="prochrtovrbox">
661
  <div class="prochrtcntn">
662
  <div class="prochrttop">
663
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
664
- <?php _e("Locked","conversios"); ?>
665
  </div>
666
- <h5><?php _e("Source/Medium Performance Report","conversios"); ?></h5>
667
- <p><?php _e("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.","conversios"); ?></p>
668
- <a class="blueupgrdbtn" href="<?php echo esc_url($this->pro_plan_site); ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
669
  </div>
670
  </div>
671
  </div>
@@ -679,12 +679,12 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
679
  <!--- Shopping and Google Ads Performance section start -->
680
  <?php /* if($this->plan_id != 1){?>
681
  <div class="mt24 whiteroundedbx ggladsperfom-sec">
682
- <h4><?php _e("Shopping and Google Ads Performance","conversios"); ?></h4>
683
  <div class="row">
684
  <div class="col50">
685
  <div class="chartbx ggladschrtbx daily-clicks-bx">
686
  <div class="chartcntnbx">
687
- <h5><?php _e("Clicks","conversios"); ?></h5>
688
  <div class="chartarea">
689
  <canvas id="dailyClicks" width="400" height="300" class="chartcntainer"></canvas>
690
  </div>
@@ -694,7 +694,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
694
  <div class="col50">
695
  <div class="chartbx ggladschrtbx daily-cost-bx">
696
  <div class="chartcntnbx">
697
- <h5><?php _e("Cost","conversios"); ?></h5>
698
  <div class="chartarea">
699
  <canvas id="dailyCost" width="400" height="300" class="chartcntainer"></canvas>
700
  </div>
@@ -705,7 +705,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
705
  <div class="col50">
706
  <div class="chartbx ggladschrtbx daily-conversions-bx">
707
  <div class="chartcntnbx">
708
- <h5><?php _e("Conversions","conversios"); ?></h5>
709
  <div class="chartarea">
710
  <canvas id="dailyConversions" width="400" height="300" class="chartcntainer"></canvas>
711
  </div>
@@ -715,7 +715,7 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
715
  <div class="col50">
716
  <div class="chartbx ggladschrtbx daily-sales-bx">
717
  <div class="chartcntnbx">
718
- <h5><?php _e("Sales","conversios"); ?></h5>
719
  <div class="chartarea">
720
  <canvas id="dailySales" width="400" height="300" class="chartcntainer"></canvas>
721
  </div>
@@ -726,25 +726,25 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
726
  </div>
727
  <?php }else{ ?>
728
  <div class="mt24 whiteroundedbx ggladsperfom-sec">
729
- <h4><?php _e("Shopping and Google Ads Performance","conversios"); ?></h4>
730
  <div class="row">
731
  <div class="col50">
732
  <div class="chartbx ecomfunnchrtbx">
733
  <div class="chartcntnbx prochrtftr">
734
- <h5><?php _e("Google Ads Clicks Performance","conversios"); ?></h5>
735
  <div class="chartarea">
736
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'; ?>" alt="" />
737
  </div>
738
  </div>
739
  <div class="prochrtovrbox">
740
  <div class="prochrtcntn">
741
  <div class="prochrttop">
742
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
743
- <?php _e("Locked","conversios"); ?>
744
  </div>
745
- <h5><?php _e("Google Ads Clicks Performance","conversios"); ?></h5>
746
- <p><?php _e("This report will help you .","conversios"); ?></p>
747
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
748
  </div>
749
  </div>
750
  </div>
@@ -752,20 +752,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
752
  <div class="col50">
753
  <div class="chartbx ecomfunnchrtbx">
754
  <div class="chartcntnbx prochrtftr">
755
- <h5><?php _e("Google Ads Cost Performance","conversios"); ?></h5>
756
  <div class="chartarea">
757
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'; ?>" alt="" />
758
  </div>
759
  </div>
760
  <div class="prochrtovrbox">
761
  <div class="prochrtcntn">
762
  <div class="prochrttop">
763
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
764
- <?php _e("Locked","conversios"); ?>
765
  </div>
766
- <h5><?php _e("Google Ads Cost Performance","conversios"); ?></h5>
767
- <p><?php _e("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.","conversios"); ?></p>
768
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
769
  </div>
770
  </div>
771
  </div>
@@ -774,20 +774,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
774
  <div class="col50">
775
  <div class="chartbx ecomfunnchrtbx">
776
  <div class="chartcntnbx prochrtftr">
777
- <h5><?php _e("Google Ads Conversions Performance","conversios"); ?></h5>
778
  <div class="chartarea">
779
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'; ?>" alt="" />
780
  </div>
781
  </div>
782
  <div class="prochrtovrbox">
783
  <div class="prochrtcntn">
784
  <div class="prochrttop">
785
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
786
- <?php _e("Locked","conversios"); ?>
787
  </div>
788
- <h5><?php _e("Google Ads Conversions Performance","conversios"); ?></h5>
789
- <p><?php _e("This report will help you","conversios"); ?> </p>
790
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
791
  </div>
792
  </div>
793
  </div>
@@ -795,20 +795,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
795
  <div class="col50">
796
  <div class="chartbx ecomfunnchrtbx">
797
  <div class="chartcntnbx prochrtftr">
798
- <h5><?php _e("Google Ads Sales Performance","conversios"); ?></h5>
799
  <div class="chartarea">
800
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'; ?>" alt="" />
801
  </div>
802
  </div>
803
  <div class="prochrtovrbox">
804
  <div class="prochrtcntn">
805
  <div class="prochrttop">
806
- <img src="<?php echo ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'; ?>" alt="" />
807
  Locked
808
  </div>
809
- <h5><?php _e("Google Ads Sales Performance","conversios"); ?></h5>
810
- <p><?php _e("This report will help you.","conversios"); ?></p>
811
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
812
  </div>
813
  </div>
814
  </div>
@@ -824,26 +824,26 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
824
  <div class="mt24 whiteroundedbx dshreport-sec">
825
  <div class="row dsh-reprttop">
826
  <div class="dshrprttp-left">
827
- <h4><?php _e("Campaign Performance","conversios"); ?></h4>
828
- <a href="" class="viewallbtn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>">View all <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'); ?>" alt="" /></a>
829
  </div>
830
  </div>
831
  <div class="dashtablewrp campaign_performance_report" id="campaign_performance_report">
832
  <?php if($this->google_ads_id == ""){
833
  ?>
834
- <p><?php _e("Set up your google ads account from","conversios"); ?> <a href="<?php echo $this->TVC_Admin_Helper->get_onboarding_page_url(); ?>">here</a> <?php _e("in order to access Campaign performance data.","conversios"); ?></p>
835
  <?php
836
  } ?>
837
  <table class="dshreporttble mbl-table" >
838
  <thead>
839
  <tr>
840
- <th class="prdnm-cell"><?php _e("Campaign Name","conversios"); ?></th>
841
- <th><?php _e("Daily Budget","conversios"); ?></th>
842
- <th><?php _e("Status","conversios"); ?></th>
843
- <th><?php _e("Clicks","conversios"); ?></th>
844
- <th><?php _e("Cost","conversios"); ?></th>
845
- <th><?php _e("Conversions","conversios"); ?></th>
846
- <th><?php _e("Sales","conversios"); ?></th>
847
  </tr>
848
  </thead>
849
  <tbody>
@@ -857,20 +857,20 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
857
  <div class="col">
858
  <div class="chartbx ecomfunnchrtbx">
859
  <div class="chartcntnbx prochrtftr">
860
- <h5><?php _e("Campaign Performance","conversios"); ?></h5>
861
  <div class="chartarea">
862
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
863
  </div>
864
  </div>
865
  <div class="prochrtovrbox">
866
  <div class="prochrtcntn">
867
  <div class="prochrttop">
868
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
869
- Locked
870
  </div>
871
- <h5><?php _e("Campaign Performance","conversios"); ?></h5>
872
- <p><?php _e("Access your campaign performance data to know how are they performing and take actionable decisions to increase your marketing ROI.","conversios"); ?></p>
873
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
874
  </div>
875
  </div>
876
  </div>
@@ -885,15 +885,15 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
885
  <div class="sycnprdct-ppcnt">
886
  <div class="ppwhitebg pp-content upgradsbscrptnpp-cntr">
887
  <div class="ppclsbtn absltpsclsbtn clsbtntrgr">
888
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-white.png'); ?>" alt="">
889
  </div>
890
  <div class="upgradsbscrptnpp-hdr">
891
- <h5><?php _e("Upgrade to Pro..!!","conversios"); ?></h5>
892
  </div>
893
  <div class="ppmodal-body">
894
- <p><?php _e("This feature is only available in the paid plan. Please upgrade to get the full range of reports and more.","conversios"); ?></p>
895
  <div class="ppupgrdbtnwrap">
896
- <a class="blueupgrdbtn" href="<?php echo $this->pro_plan_site; ?>" target="_blank"><?php _e("Upgrade Now","conversios"); ?></a>
897
  </div>
898
  </div>
899
  </div>
@@ -904,14 +904,14 @@ if ( ! class_exists( 'Conversios_Dashboard' ) ) {
904
  <div class="sycnprdct-ppcnt">
905
  <div class="ppwhitebg pp-content upgradsbscrptnpp-cntr">
906
  <div class="ppclsbtn absltpsclsbtn clsbtntrgr">
907
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/close-white.png'); ?>" alt="">
908
  </div>
909
  <div class="upgradsbscrptnpp-hdr">
910
- <h5><?php _e("Upcoming..!!","conversios"); ?></h5>
911
  </div>
912
  <div class="ppmodal-body">
913
- <p><?php _e("We are currently working on this feature and we will reach out to you once this is live.","conversios"); ?></p>
914
- <p><?php _e("We aim to give you a capability to schedule the reports directly in your inbox whenever you want.","conversios"); ?></p>
915
  </div>
916
  </div>
917
  </div>
32
 
33
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
34
  $this->CustomApi = new CustomApi();
35
+ $this->connect_url = esc_url_raw($this->TVC_Admin_Helper->get_custom_connect_url(admin_url().'admin.php?page=conversios'));
36
  $this->subscription_id = $this->TVC_Admin_Helper->get_subscriptionId();
37
  // update API data to DB while expired token
38
 
39
+ if ( isset($_GET['subscription_id']) && sanitize_text_field($_GET['subscription_id']) == $this->subscription_id){
40
+ if ( isset($_GET['g_mail']) && sanitize_email($_GET['g_mail'])){
41
  $this->TVC_Admin_Helper->update_subscription_details_api_to_db();
42
  }
43
+ } else if(isset($_GET['subscription_id']) && sanitize_text_field($_GET['subscription_id'])){
44
+ $this->notice = esc_html__("You tried signing in with different email. Please try again by signing it with the email id that you used to set up the plugin earlier.","conversios");
45
  }
46
  $this->is_refresh_token_expire = $this->TVC_Admin_Helper->is_refresh_token_expire();
47
  $this->subscription_data = $this->TVC_Admin_Helper->get_user_subscription_data();
48
+ $this->pro_plan_site = esc_url_raw($this->TVC_Admin_Helper->get_pro_plan_site().'?utm_source=EE+Plugin+User+Interface&utm_medium=dashboard&utm_campaign=Upsell+at+Conversios');
49
  if(isset($this->subscription_data->plan_id) && !in_array($this->subscription_data->plan_id, array("1"))){
50
  $this->plan_id = $this->subscription_data->plan_id;
51
  }
109
  }
110
  }
111
  public function load_html(){
112
+ do_action('conversios_start_html_'.sanitize_text_field($_GET['page']));
113
  $this->current_html();
114
  $this->current_js();
115
+ do_action('conversios_end_html_'.sanitize_text_field($_GET['page']));
116
  }
117
 
118
  /**
127
  /**
128
  * daterage script
129
  **/
130
+ var notice='<?php echo esc_html__($this->notice); ?>';
131
  if(notice != ""){
132
  tvc_helper.tvc_alert("error","Email error",notice);
133
  }
134
  var plan_id = '<?php echo esc_attr($this->plan_id); ?>';
135
  var g_mail = '<?php echo esc_attr($this->g_mail); ?>';
136
+ var is_refresh_token_expire = '<?php echo esc_attr($this->is_refresh_token_expire); ?>';
137
  is_refresh_token_expire = (is_refresh_token_expire == "")?false:true;
138
  console.log(is_refresh_token_expire);
139
  //$(function() {
251
  const systemZoom = width / window.screen.availWidth;
252
  const left = (width - w) / 2 / systemZoom + dualScreenLeft;
253
  const top = (height - h) / 2 / systemZoom + dualScreenTop;
254
+ var url ='<?php echo esc_attr($this->connect_url); ?>';
255
  url = url.replace(/&amp;/g, '&');
256
  const newWindow = window.open(url, "newwindow", config= `scrollbars=yes,
257
  width=${w / systemZoom},
287
  <div class="dashbrdpage-wrap">
288
  <div class="dflex align-items-center mt24 dshbrdtoparea">
289
  <div class="dashtp-left">
290
+ <button class="dashtpleft-btn <?php echo esc_attr($this->add_upgrdsbrs_btn_calss('download_pdf')); ?>"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/download-icon.png'); ?>" alt="" /><?php esc_html_e("Download PDF","conversios"); ?></button>
291
+ <button class="dashtpleft-btn <?php echo esc_attr($this->add_upgrdsbrs_btn_calss('schedule_email')); ?>"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/clock-icon.png'); ?>" alt="" /><?php esc_html_e("Schedule Email","conversios"); ?></button>
292
  </div>
293
  <div class="dashtp-right">
294
 
303
  <?php if($this->plan_id != 1){?>
304
  <div id="reportrange" class="dshtpdaterange" >
305
  <div class="dateclndicn">
306
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/claendar-icon.png'); ?>" alt="" />
307
  </div>
308
  <span class="daterangearea report_range_val"></span>
309
+ <div class="careticn"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/caret-down.png'); ?>" alt="" /></div>
310
  </div>
311
  <?php } else{ ?>
312
  <div class="dshtpdaterange <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>">
313
  <div class="dateclndicn">
314
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/claendar-icon.png'); ?>" alt="" />
315
  </div>
316
  <span class="daterangearea report_range_val"></span>
317
+ <div class="careticn"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/caret-down.png'); ?>" alt="" /></div>
318
  </div>
319
  <?php }?>
320
 
321
  </div>
322
  </div>
323
  <?php if($this->ga_traking_type == "GA4"){?>
324
+ <div class="temp_note"><p><?php esc_html_e("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.","conversios"); ?></p></div>
325
  <?php } ?>
326
  <!--- dashboard summary section start -->
327
  <div class="wht-rnd-shdwbx mt24 dashsmry-wrap">
328
  <div class="dashsmry-item">
329
  <div class="dashsmrybx" id="s1_transactionsPerSession">
330
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Conversion Rate","conversios"); ?></div>
331
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
332
  <div class="updownsmry dash-smry-compare-val">
333
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/green-up.png'); ?>" alt="" />
334
  %
335
  </div>
336
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
337
  </div>
338
  <div class="dashsmrybx" id="s1_transactionRevenue">
339
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Revenue","conversios"); ?></div>
340
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
341
  <div class="updownsmry dash-smry-compare-val">
342
  %
343
  </div>
344
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
345
  </div>
346
  <div class="dashsmrybx mblsmry3bx" id="s1_transactions">
347
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Total Transactions","conversios"); ?></div>
348
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
349
  <div class="updownsmry dash-smry-compare-val">
350
  %
351
  </div>
352
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
353
  </div>
354
  <div class="dashsmrybx mblsmry3bx" id="s1_revenuePerTransaction">
355
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Avg. Order Value","conversios"); ?></div>
356
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
357
  <div class="updownsmry dash-smry-compare-val">
358
  %
359
  </div>
360
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
361
  </div>
362
  <div class="dashsmrybx mblsmry3bx flwdthmblbx" id="s1_productAddsToCart">
363
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Added to Cart","conversios"); ?></div>
364
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
365
  <div class="updownsmry dash-smry-compare-val">
366
  %
367
  </div>
368
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
369
  </div>
370
  </div>
371
  <div class="dashsmry-item">
372
  <div class="dashsmrybx" id="s1_productRemovesFromCart">
373
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Removed from Cart","conversios"); ?></div>
374
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
375
  <div class="updownsmry dash-smry-compare-val">
376
  %
377
  </div>
378
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
379
  </div>
380
  <div class="dashsmrybx" id="s1_sessions">
381
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Sessions","conversios"); ?></div>
382
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
383
  <div class="updownsmry dash-smry-compare-val">
384
  %
385
  </div>
386
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
387
  </div>
388
  <div class="dashsmrybx" id="s1_users">
389
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Total Users","conversios"); ?></div>
390
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
391
  <div class="updownsmry dash-smry-compare-val">
392
  %
393
  </div>
394
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
395
  </div>
396
  <div class="dashsmrybx" id="s1_newUsers">
397
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("New Users","conversios"); ?></div>
398
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
399
  <div class="updownsmry dash-smry-compare-val">
400
  %
401
  </div>
402
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
403
  </div>
404
  <div class="dashsmrybx" id="s1_productDetailViews">
405
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Product Views","conversios"); ?></div>
406
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
407
  <div class="updownsmry dash-smry-compare-val">
408
  %
409
  </div>
410
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
411
  </div>
412
 
413
  </div>
414
  <?php /*
415
  <div class="dashsmry-item">
416
  <div class="dashsmrybx" id="s1_transactionShipping">
417
+ <div class="dshsmrycattxt dash-smry-title"><?php esc_html_e("Shipping","conversios"); ?></div>
418
  <div class="dshsmrylrgtxt dash-smry-value">-</div>
419
  <div class="updownsmry dash-smry-compare-val">
420
  %
421
  </div>
422
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
423
  </div>
424
  <div class="dashsmrybx" id="s1_transactionTax">
425
  <div class="dshsmrycattxt dash-smry-title">TAX</div>
427
  <div class="updownsmry dash-smry-compare-val">
428
  %
429
  </div>
430
+ <div class="dshsmryprdtxt"><?php esc_html_e("From Previous Period","conversios"); ?></div>
431
  </div>
432
  </div> */?>
433
  </div>
440
  <div class="col50">
441
  <div class="chartbx ecomfunnchrtbx ecom-funn-chrt-bx">
442
  <div class="chartcntnbx">
443
+ <h5><?php esc_html_e("Ecommerce Conversion Funnel","conversios"); ?></h5>
444
  <div class="chartarea">
445
  <canvas id="ecomfunchart" width="400" height="300"></canvas>
446
  </div>
448
  <div class="ecomchartinfo">
449
  <div class="ecomchrtinfoflex">
450
  <div class="ecomchartinfoitem">
451
+ <div class="ecomchartinfolabel"><?php esc_html_e("Sessions","conversios"); ?></div>
452
  <div class="chartpercarrow conversion_s1"></div>
453
  </div>
454
  <div class="ecomchartinfoitem">
455
+ <div class="ecomchartinfolabel"><?php esc_html_e("Product View","conversios"); ?></div>
456
  <div class="chartpercarrow conversion_s2"></div>
457
  </div>
458
  <div class="ecomchartinfoitem">
459
+ <div class="ecomchartinfolabel"><?php esc_html_e("Add to Cart","conversios"); ?></div>
460
  <div class="chartpercarrow conversion_s3"></div>
461
  </div>
462
  <div class="ecomchartinfoitem">
463
+ <div class="ecomchartinfolabel"><?php esc_html_e("Checkouts","conversios"); ?></div>
464
  <div class="chartpercarrow conversion_s4"></div>
465
  </div>
466
  <div class="ecomchartinfoitem">
467
+ <div class="ecomchartinfolabel"><?php esc_html_e("Order Confirmation","conversios"); ?></div>
468
  </div>
469
  </div>
470
  </div>
475
  <div class="col50">
476
  <div class="chartbx ecomfunnchrtbx">
477
  <div class="chartcntnbx prochrtftr">
478
+ <h5><?php esc_html_e("Ecommerce Conversion Funnel","conversios"); ?></h5>
479
  <div class="chartarea">
480
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/ecom-chart.jpg'); ?>" alt="" />
481
  </div>
482
  </div>
483
  <div class="prochrtovrbox">
484
  <div class="prochrtcntn">
485
  <div class="prochrttop">
486
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
487
  Locked
488
  </div>
489
+ <h5><?php esc_html_e("Conversion Funnel","conversios"); ?></h5>
490
+ <p><?php esc_html_e("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.","conversios"); ?></p>
491
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
492
  </div>
493
  </div>
494
  </div>
499
  <div class="col50">
500
  <div class="chartbx ecomfunnchrtbx ecom-checkout-funn-chrt-bx">
501
  <div class="chartcntnbx">
502
+ <h5><?php esc_html_e("Ecommerce Checkout Funnel","conversios"); ?></h5>
503
  <div class="chartarea">
504
  <canvas id="ecomcheckoutfunchart" width="400" height="300"></canvas>
505
  </div>
507
  <div class="ecomchartinfo ecomcheckoutfunchartinfo">
508
  <div class="ecomchrtinfoflex">
509
  <div class="ecomchartinfoitem">
510
+ <div class="ecomchartinfolabel"><?php esc_html_e("Checkout Step 1","conversios"); ?></div>
511
  <div class="chartpercarrow checkoutfunn_s1"></div>
512
  </div>
513
  <div class="ecomchartinfoitem">
514
+ <div class="ecomchartinfolabel"><?php esc_html_e("Checkout Step 2","conversios"); ?></div>
515
  <div class="chartpercarrow checkoutfunn_s2"></div>
516
  </div>
517
  <div class="ecomchartinfoitem">
518
+ <div class="ecomchartinfolabel"><?php esc_html_e("Checkout Step 3","conversios"); ?></div>
519
  <div class="chartpercarrow checkoutfunn_s3"></div>
520
  </div>
521
  <div class="ecomchartinfoitem">
522
+ <div class="ecomchartinfolabel"><?php esc_html_e("Purchase","conversios"); ?></div>
523
  </div>
524
 
525
  </div>
532
  <div class="col50">
533
  <div class="chartbx ecomfunnchrtbx">
534
  <div class="chartcntnbx prochrtftr">
535
+ <h5><?php esc_html_e("Checkout Funnel","conversios"); ?></h5>
536
  <div class="chartarea">
537
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/ecom-chart.jpg'); ?>" alt="" />
538
  </div>
539
  </div>
540
  <div class="prochrtovrbox">
541
  <div class="prochrtcntn">
542
  <div class="prochrttop">
543
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
544
  Locked
545
  </div>
546
+ <h5><?php esc_html_e("Checkout Funnel","conversios"); ?></h5>
547
+ <p><?php esc_html_e("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.","conversios"); ?></p>
548
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
549
  </div>
550
  </div>
551
  </div>
561
  <div class="mt24 whiteroundedbx dshreport-sec">
562
  <div class="row dsh-reprttop">
563
  <div class="dshrprttp-left">
564
+ <h4><?php esc_html_e("Product Performance Report","conversios"); ?></h4>
565
+ <a href="#" class="viewallbtn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>"><?php esc_html_e("View all","conversios"); ?> <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'); ?>" alt="" /></a>
566
  </div>
567
  </div>
568
  <div class="dashtablewrp product_performance_report" id="product_performance_report">
569
  <table class="dshreporttble mbl-table" >
570
  <thead>
571
  <tr>
572
+ <th class="prdnm-cell"><?php esc_html_e("Product Name","conversios"); ?></th>
573
+ <th><?php esc_html_e("Views","conversios"); ?></th>
574
+ <th><?php esc_html_e("Added to Cart","conversios"); ?></th>
575
+ <th><?php esc_html_e("Orders","conversios"); ?></th>
576
+ <th><?php esc_html_e("Qty","conversios"); ?></th>
577
+ <th><?php esc_html_e("Revenue","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
578
+ <th><?php esc_html_e("Avg Price","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
579
+ <th><?php esc_html_e("Refund Amount","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
580
+ <th><?php esc_html_e("Cart to details (%)","conversios"); ?></th>
581
+ <th><?php esc_html_e("Buy to details (%)","conversios"); ?></th>
582
  </tr>
583
  </thead>
584
  <tbody>
592
  <div class="col">
593
  <div class="chartbx ecomfunnchrtbx">
594
  <div class="chartcntnbx prochrtftr">
595
+ <h5><?php esc_html_e("Product Performance Report","conversios"); ?></h5>
596
  <div class="chartarea">
597
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
598
  </div>
599
  </div>
600
  <div class="prochrtovrbox">
601
  <div class="prochrtcntn">
602
  <div class="prochrttop">
603
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
604
+ <?php esc_html_e("Locked","conversios"); ?>
605
  </div>
606
+ <h5><?php esc_html_e("Product Performance Report","conversios"); ?></h5>
607
+ <p><?php esc_html_e("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.","conversios"); ?></p>
608
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
609
  </div>
610
  </div>
611
  </div>
620
  <div class="mt24 whiteroundedbx dshreport-sec">
621
  <div class="row dsh-reprttop">
622
  <div class="dshrprttp-left">
623
+ <h4><?php esc_html_e("Source/Medium Performance Report","conversios"); ?></h4>
624
+ <a href="" class="viewallbtn <?php echo $this->add_upgrdsbrs_btn_calss('download_pdf'); ?>"><?php esc_html_e("View all","conversios"); ?> <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'); ?>" alt="" /></a>
625
  </div>
626
  </div>
627
  <div class="dashtablewrp medium_performance_report" id="medium_performance_report">
628
  <table class="dshreporttble mbl-table" >
629
  <thead>
630
  <tr>
631
+ <th class="prdnm-cell"><?php esc_html_e("Source/Medium","conversios"); ?></th>
632
+ <th><?php esc_html_e("Conversion (%)","conversios"); ?></th>
633
+ <th><?php esc_html_e("Revenue","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
634
+ <th><?php esc_html_e("Total transactions","conversios"); ?></th>
635
+ <th><?php esc_html_e("Avg Order value","conversios"); ?> (<?php echo $this->ga_currency_symbols; ?>)</th>
636
+ <th><?php esc_html_e("Added to carts","conversios"); ?></th>
637
+ <th><?php esc_html_e("removed from cart","conversios"); ?></th>
638
+ <th><?php esc_html_e("Product views","conversios"); ?></th>
639
+ <th><?php esc_html_e("Users","conversios"); ?></th>
640
+ <th><?php esc_html_e("Sessions","conversios"); ?></th>
641
  </tr>
642
  </thead>
643
  <tbody>
652
  <div class="col">
653
  <div class="chartbx ecomfunnchrtbx">
654
  <div class="chartcntnbx prochrtftr">
655
+ <h5><?php esc_html_e("Source/Medium Performance Report","conversios"); ?></h5>
656
  <div class="chartarea">
657
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
658
  </div>
659
  </div>
660
  <div class="prochrtovrbox">
661
  <div class="prochrtcntn">
662
  <div class="prochrttop">
663
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
664
+ <?php esc_html_e("Locked","conversios"); ?>
665
  </div>
666
+ <h5><?php esc_html_e("Source/Medium Performance Report","conversios"); ?></h5>
667
+ <p><?php esc_html_e("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.","conversios"); ?></p>
668
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
669
  </div>
670
  </div>
671
  </div>
679
  <!--- Shopping and Google Ads Performance section start -->
680
  <?php /* if($this->plan_id != 1){?>
681
  <div class="mt24 whiteroundedbx ggladsperfom-sec">
682
+ <h4><?php esc_html_e("Shopping and Google Ads Performance","conversios"); ?></h4>
683
  <div class="row">
684
  <div class="col50">
685
  <div class="chartbx ggladschrtbx daily-clicks-bx">
686
  <div class="chartcntnbx">
687
+ <h5><?php esc_html_e("Clicks","conversios"); ?></h5>
688
  <div class="chartarea">
689
  <canvas id="dailyClicks" width="400" height="300" class="chartcntainer"></canvas>
690
  </div>
694
  <div class="col50">
695
  <div class="chartbx ggladschrtbx daily-cost-bx">
696
  <div class="chartcntnbx">
697
+ <h5><?php esc_html_e("Cost","conversios"); ?></h5>
698
  <div class="chartarea">
699
  <canvas id="dailyCost" width="400" height="300" class="chartcntainer"></canvas>
700
  </div>
705
  <div class="col50">
706
  <div class="chartbx ggladschrtbx daily-conversions-bx">
707
  <div class="chartcntnbx">
708
+ <h5><?php esc_html_e("Conversions","conversios"); ?></h5>
709
  <div class="chartarea">
710
  <canvas id="dailyConversions" width="400" height="300" class="chartcntainer"></canvas>
711
  </div>
715
  <div class="col50">
716
  <div class="chartbx ggladschrtbx daily-sales-bx">
717
  <div class="chartcntnbx">
718
+ <h5><?php esc_html_e("Sales","conversios"); ?></h5>
719
  <div class="chartarea">
720
  <canvas id="dailySales" width="400" height="300" class="chartcntainer"></canvas>
721
  </div>
726
  </div>
727
  <?php }else{ ?>
728
  <div class="mt24 whiteroundedbx ggladsperfom-sec">
729
+ <h4><?php esc_html_e("Shopping and Google Ads Performance","conversios"); ?></h4>
730
  <div class="row">
731
  <div class="col50">
732
  <div class="chartbx ecomfunnchrtbx">
733
  <div class="chartcntnbx prochrtftr">
734
+ <h5><?php esc_html_e("Google Ads Clicks Performance","conversios"); ?></h5>
735
  <div class="chartarea">
736
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
737
  </div>
738
  </div>
739
  <div class="prochrtovrbox">
740
  <div class="prochrtcntn">
741
  <div class="prochrttop">
742
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
743
+ <?php esc_html_e("Locked","conversios"); ?>
744
  </div>
745
+ <h5><?php esc_html_e("Google Ads Clicks Performance","conversios"); ?></h5>
746
+ <p><?php esc_html_e("This report will help you .","conversios"); ?></p>
747
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
748
  </div>
749
  </div>
750
  </div>
752
  <div class="col50">
753
  <div class="chartbx ecomfunnchrtbx">
754
  <div class="chartcntnbx prochrtftr">
755
+ <h5><?php esc_html_e("Google Ads Cost Performance","conversios"); ?></h5>
756
  <div class="chartarea">
757
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
758
  </div>
759
  </div>
760
  <div class="prochrtovrbox">
761
  <div class="prochrtcntn">
762
  <div class="prochrttop">
763
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
764
+ <?php esc_html_e("Locked","conversios"); ?>
765
  </div>
766
+ <h5><?php esc_html_e("Google Ads Cost Performance","conversios"); ?></h5>
767
+ <p><?php esc_html_e("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.","conversios"); ?></p>
768
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
769
  </div>
770
  </div>
771
  </div>
774
  <div class="col50">
775
  <div class="chartbx ecomfunnchrtbx">
776
  <div class="chartcntnbx prochrtftr">
777
+ <h5><?php esc_html_e("Google Ads Conversions Performance","conversios"); ?></h5>
778
  <div class="chartarea">
779
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
780
  </div>
781
  </div>
782
  <div class="prochrtovrbox">
783
  <div class="prochrtcntn">
784
  <div class="prochrttop">
785
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
786
+ <?php esc_html_e("Locked","conversios"); ?>
787
  </div>
788
+ <h5><?php esc_html_e("Google Ads Conversions Performance","conversios"); ?></h5>
789
+ <p><?php esc_html_e("This report will help you","conversios"); ?> </p>
790
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
791
  </div>
792
  </div>
793
  </div>
795
  <div class="col50">
796
  <div class="chartbx ecomfunnchrtbx">
797
  <div class="chartcntnbx prochrtftr">
798
+ <h5><?php esc_html_e("Google Ads Sales Performance","conversios"); ?></h5>
799
  <div class="chartarea">
800
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
801
  </div>
802
  </div>
803
  <div class="prochrtovrbox">
804
  <div class="prochrtcntn">
805
  <div class="prochrttop">
806
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
807
  Locked
808
  </div>
809
+ <h5><?php esc_html_e("Google Ads Sales Performance","conversios"); ?></h5>
810
+ <p><?php esc_html_e("This report will help you.","conversios"); ?></p>
811
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
812
  </div>
813
  </div>
814
  </div>
824
  <div class="mt24 whiteroundedbx dshreport-sec">
825
  <div class="row dsh-reprttop">
826
  <div class="dshrprttp-left">
827
+ <h4><?php esc_html_e("Campaign Performance","conversios"); ?></h4>
828
+ <a href="" class="viewallbtn <?php echo esc_attr($this->add_upgrdsbrs_btn_calss('download_pdf')); ?>"><?php esc_html_e("View all","conversios"); ?> <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/blue-right-arrow.png'); ?>" alt="" /></a>
829
  </div>
830
  </div>
831
  <div class="dashtablewrp campaign_performance_report" id="campaign_performance_report">
832
  <?php if($this->google_ads_id == ""){
833
  ?>
834
+ <p><?php esc_html_e("Set up your google ads account from","conversios"); ?> <a href="<?php echo esc_url_raw($this->TVC_Admin_Helper->get_onboarding_page_url()); ?>"><?php esc_html_e("here","conversios"); ?></a> <?php esc_html_e("in order to access Campaign performance data.","conversios"); ?></p>
835
  <?php
836
  } ?>
837
  <table class="dshreporttble mbl-table" >
838
  <thead>
839
  <tr>
840
+ <th class="prdnm-cell"><?php esc_html_e("Campaign Name","conversios"); ?></th>
841
+ <th><?php esc_html_e("Daily Budget","conversios"); ?></th>
842
+ <th><?php esc_html_e("Status","conversios"); ?></th>
843
+ <th><?php esc_html_e("Clicks","conversios"); ?></th>
844
+ <th><?php esc_html_e("Cost","conversios"); ?></th>
845
+ <th><?php esc_html_e("Conversions","conversios"); ?></th>
846
+ <th><?php esc_html_e("Sales","conversios"); ?></th>
847
  </tr>
848
  </thead>
849
  <tbody>
857
  <div class="col">
858
  <div class="chartbx ecomfunnchrtbx">
859
  <div class="chartcntnbx prochrtftr">
860
+ <h5><?php esc_html_e("Campaign Performance","conversios"); ?></h5>
861
  <div class="chartarea">
862
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/table-data.jpg'); ?>" alt="" />
863
  </div>
864
  </div>
865
  <div class="prochrtovrbox">
866
  <div class="prochrtcntn">
867
  <div class="prochrttop">
868
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/lock-orange.png'); ?>" alt="" />
869
+ <?php esc_html_e("Locked","conversios"); ?>
870
  </div>
871
+ <h5><?php esc_html_e("Campaign Performance","conversios"); ?></h5>
872
+ <p><?php esc_html_e("Access your campaign performance data to know how are they performing and take actionable decisions to increase your marketing ROI.","conversios"); ?></p>
873
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
874
  </div>
875
  </div>
876
  </div>
885
  <div class="sycnprdct-ppcnt">
886
  <div class="ppwhitebg pp-content upgradsbscrptnpp-cntr">
887
  <div class="ppclsbtn absltpsclsbtn clsbtntrgr">
888
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-white.png'); ?>" alt="">
889
  </div>
890
  <div class="upgradsbscrptnpp-hdr">
891
+ <h5><?php esc_html_e("Upgrade to Pro..!!","conversios"); ?></h5>
892
  </div>
893
  <div class="ppmodal-body">
894
+ <p><?php esc_html_e("This feature is only available in the paid plan. Please upgrade to get the full range of reports and more.","conversios"); ?></p>
895
  <div class="ppupgrdbtnwrap">
896
+ <a class="blueupgrdbtn" href="<?php echo esc_url_raw($this->pro_plan_site); ?>" target="_blank"><?php esc_html_e("Upgrade Now","conversios"); ?></a>
897
  </div>
898
  </div>
899
  </div>
904
  <div class="sycnprdct-ppcnt">
905
  <div class="ppwhitebg pp-content upgradsbscrptnpp-cntr">
906
  <div class="ppclsbtn absltpsclsbtn clsbtntrgr">
907
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/close-white.png'); ?>" alt="">
908
  </div>
909
  <div class="upgradsbscrptnpp-hdr">
910
+ <h5><?php esc_html_e("Upcoming..!!","conversios"); ?></h5>
911
  </div>
912
  <div class="ppmodal-body">
913
+ <p><?php esc_html_e("We are currently working on this feature and we will reach out to you once this is live.","conversios"); ?></p>
914
+ <p><?php esc_html_e("We aim to give you a capability to schedule the reports directly in your inbox whenever you want.","conversios"); ?></p>
915
  </div>
916
  </div>
917
  </div>
includes/setup/class-tatvic-category-selector-element.php CHANGED
@@ -57,25 +57,25 @@ if ( ! class_exists( 'Tatvic_Category_Selector_Element' ) ) :
57
  $cat_name = esc_attr($ee_prod_mapped_cats[$id]['name']);
58
  $html_code = '<div id="category-selector-' . esc_attr($ident) . '" style="display:' . esc_attr($display) . '">
59
  <div id="selected-categories">
60
- <input type="hidden" name="category-'.$id.'" id="category-'.$id.'" value="'.$cat_id.'">
61
- <input type="hidden" name="category-name-'.$id.'" id="category-name-'.$id.'" value="'.$cat_name.'">
62
  </div>
63
- <label id="label-'.esc_attr($ident).'_0">'.$cat_name.'</label><span class="change_prodct_feed_cat" data-cat-id="'.$id.'" data-id="'.esc_attr($ident).'_0">Edit</span>
64
- <select class="form-control" style="display:none;" id="' . esc_attr($ident) . '_0" catId="'.$id.'" onchange="selectSubCategory(this)"></select>';
65
 
66
  for ( $i = 1; $i < $category_levels; $i ++ ) {
67
- $html_code .= '<select class="" id="' . esc_attr($ident) . '_' . $i . '" value="0" catId="'.$id.'" style="display:none;" onchange="selectSubCategory(this)"></select>';
68
  }
69
  }else{
70
  $html_code = '<div id="category-selector-' . esc_attr($ident) . '" style="display:' . esc_attr($display) . '">
71
  <div id="selected-categories">
72
- <input type="hidden" name="category-'.$id.'" id="category-'.$id.'" value="">
73
- <input type="hidden" name="category-name-'.$id.'" id="category-name-'.$id.'" value="">
74
  </div>
75
- <select class="form-control" id="' . esc_attr($ident) . '_0" catId="'.$id.'" onchange="selectSubCategory(this)"></select>';
76
 
77
  for ( $i = 1; $i < $category_levels; $i ++ ) {
78
- $html_code .= '<select class="" id="' . esc_attr($ident) . '_' . $i . '" value="0" catId="'.$id.'" style="display:none;" onchange="selectSubCategory(this)"></select>';
79
  }
80
 
81
  }
57
  $cat_name = esc_attr($ee_prod_mapped_cats[$id]['name']);
58
  $html_code = '<div id="category-selector-' . esc_attr($ident) . '" style="display:' . esc_attr($display) . '">
59
  <div id="selected-categories">
60
+ <input type="hidden" name="category-'.esc_attr($id).'" id="category-'.esc_attr($id).'" value="'.esc_attr($cat_id).'">
61
+ <input type="hidden" name="category-name-'.esc_attr($id).'" id="category-name-'.esc_attr($id).'" value="'.esc_attr($cat_name).'">
62
  </div>
63
+ <label id="label-'.esc_attr($ident).'_0">'.esc_attr($cat_name).'</label><span class="change_prodct_feed_cat" data-cat-id="'.esc_attr($id).'" data-id="'.esc_attr($ident).'_0">Edit</span>
64
+ <select class="form-control" style="display:none;" id="' . esc_attr($ident) . '_0" catId="'.esc_attr($id).'" onchange="selectSubCategory(this)"></select>';
65
 
66
  for ( $i = 1; $i < $category_levels; $i ++ ) {
67
+ $html_code .= '<select class="" id="' . esc_attr($ident) . '_' . esc_attr($i) . '" value="0" catId="'.esc_attr($id).'" style="display:none;" onchange="selectSubCategory(this)"></select>';
68
  }
69
  }else{
70
  $html_code = '<div id="category-selector-' . esc_attr($ident) . '" style="display:' . esc_attr($display) . '">
71
  <div id="selected-categories">
72
+ <input type="hidden" name="category-'.esc_attr($id).'" id="category-'.esc_attr($id).'" value="">
73
+ <input type="hidden" name="category-name-'.esc_attr($id).'" id="category-name-'.esc_attr($id).'" value="">
74
  </div>
75
+ <select class="form-control" id="' . esc_attr($ident) . '_0" catId="'.esc_attr($id).'" onchange="selectSubCategory(this)"></select>';
76
 
77
  for ( $i = 1; $i < $category_levels; $i ++ ) {
78
+ $html_code .= '<select class="" id="' . esc_attr($ident) . '_' . esc_attr($i) . '" value="0" catId="'.esc_attr($id).'" style="display:none;" onchange="selectSubCategory(this)"></select>';
79
  }
80
 
81
  }
includes/setup/class-tvc-product-sync-helper.php CHANGED
@@ -91,10 +91,10 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
91
  }
92
  }
93
  if(empty($sync_profile_data)){
94
- return array("error"=>true,"message"=>__("No product sync profiles find.","conversios"));
95
  }
96
  if(empty($products)){
97
- return array("error"=>true,"message"=>__("Products not found.","conversios"));
98
  }
99
  foreach ($products as $postkey => $postvalue) {
100
  $product_ids[] = $postvalue->w_product_id;
@@ -119,7 +119,7 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
119
  }
120
 
121
  if(empty($formArray)){
122
- return array("error"=>true,"message"=>__("Product sync profile not found.","conversios"));
123
  }
124
  //$formArray = json_decode($postvalue->g_attribute_mapping, true);
125
  foreach ($fixed_att_select_list as $fixed_key) {
@@ -142,39 +142,45 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
142
 
143
  if($prd->get_type() == "variable"){
144
  /*$variation_attributes = $prd->get_variation_attributes();*/
145
- $p_variations = $prd->get_available_variations();
 
146
  if(!empty($p_variations)){
147
- foreach ($p_variations as $v_key => $v_value) {
 
 
 
 
 
148
  unset($product['customAttributes']);
149
- $postmeta_var = (object)$this->TVC_Admin_Helper->tvc_get_post_meta($v_value['variation_id']);
150
  $formArray_val = $formArray['title'];
151
  $product['title'] = (isset($postObj->$formArray_val))?$postObj->$formArray_val:get_the_title($postvalue->w_product_id);
152
  $tvc_temp_desc_key = $formArray['description'];
153
- $product['description'] = (isset($v_value['variation_description']) && $v_value['variation_description'] != "")?$v_value['variation_description']:$postObj->$tvc_temp_desc_key;
154
- $product['offer_id'] = $v_value['variation_id'];
155
- $product['id'] = $v_value['variation_id'];
156
  $product['item_group_id'] = $postvalue->w_product_id;
157
  $productTypes = $this->get_product_category($postvalue->w_product_id);
158
  if(!empty($productTypes)){
159
  $product['productTypes'] = $productTypes;
160
  }
161
- $image_id = $v_value['image_id'];
162
  $product['image_link'] = wp_get_attachment_image_url($image_id, 'full');
163
- //if($is_color_size == true){
164
- if(isset($v_value['attributes']) && !empty($v_value['attributes']) ){
165
- foreach($v_value['attributes'] as $va_key => $va_value ){
166
- $va_key = str_replace("_", " ", $va_key);
167
- if (strpos($va_key, 'color') !== false) {
168
- $product['color'] = $va_value;
169
- }else if (strpos($va_key, 'size') !== false) {
170
- $product['sizes'] = $va_value;
171
- }else{
172
- $va_key = str_replace("attribute", "", $va_key);
173
- $product['customAttributes'][] = array("name"=>$va_key, "value"=>$va_value);
174
- }
175
  }
176
  }
177
- //}
178
 
179
  foreach($formArray as $key => $value){
180
  if($key == 'price'){
@@ -315,7 +321,7 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
315
  $CustomApi = new CustomApi();
316
  $product_count = $this->TVC_Admin_DB_Helper->tvc_row_count('ee_prouct_pre_sync_data');
317
  //$count = 0;
318
- $pre_last_sync_product_id = $last_sync_product_id;
319
  if( $product_count > 0 ){
320
  $tvc_currency = esc_attr($this->TVC_Admin_Helper->get_woo_currency());
321
  $merchantId = esc_attr($this->merchantId);
@@ -366,44 +372,44 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
366
  }
367
 
368
  public function tvc_product_sync_popup_html(){
369
- $category_wrapper = $this->category_wrapper_obj->category_table_content('mapping');
370
  ob_start();
371
  ?>
372
  <div class="modal fade popup-modal create-campa overlay" id="syncProduct" data-backdrop="false">
373
  <div class="modal-dialog modal-dialog-centered">
374
  <div class="modal-content">
375
  <div class="modal-body">
376
- <button type="button" class="close tvc-popup-close" data-dismiss="modal"> &times; </button>
377
- <h5><?php _e("Map your product attributes","conversios"); ?></h5>
378
- <p><?php _e("Google Merchant Center uses attributes to format your product information for Shopping Ads. Map your product attributes to the Merchant Center product attributes below. You can also edit each product’s individual attributes after you sync your products. Not all fields below are marked required, however based on your shop's categories and your country you might map a few optional attributes as well. See the full guide","conversios"); ?> <a target="_blank" href="<?php esc_url("https://support.google.com/merchants/answer/7052112"); ?>"><?php _e("here","conversios"); ?></a>.
379
  </p>
380
  <div class="wizard-section campaign-wizard">
381
  <div class="wizard-content">
382
  <input type="hidden" name="merchant_id" id="merchant_id" value="<?php echo esc_attr($this->merchantId); ?>">
383
  <form class="tab-wizard wizard- wizard" id="productSync" method="POST">
384
- <h5><span class="wiz-title"><?php _e("Category Mapping","conversios"); ?></span></h5>
385
  <section>
386
  <div class="card-wrapper">
387
  <div class="row">
388
  <div class="col-6">
389
- <h6 class="heading-tbl"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/woocommerce.svg'); ?>" alt="WooCommerce"/><?php _e("Commerce Category","conversios"); ?></h6>
390
  </div>
391
  <div class="col-6">
392
- <h6 class="heading-tbl gmc-image-heading"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/google-shopping.svg'); ?>" alt="google-shopping"/><?php _e("Google Merchant Center Category","conversios"); ?></h6>
393
  </div>
394
- </div><?php echo $category_wrapper; ?>
395
  </div>
396
  </section>
397
  <!-- Step 2 -->
398
- <h5><span class="wiz-title"><?php _e("Product Attribution Mapping","conversios"); ?></span></h5>
399
  <section>
400
  <div class="card-wrapper">
401
  <div class="row">
402
  <div class="col-6">
403
- <h6 class="heading-tbl gmc-image-heading"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/google-shopping.svg'); ?>" alt="google-shopping"/><?php _e("Google Merchant center product attributes","conversios"); ?></h6>
404
  </div>
405
  <div class="col-6">
406
- <h6 class="heading-tbl"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/woocommerce.svg'); ?>" alt="WooCommerce"/><?php _e("Commerce product attributes","conversios"); ?></h6>
407
  </div>
408
  </div>
409
  <?php
@@ -414,10 +420,10 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
414
  echo '<div class="row">
415
  <div class="col-6 align-self-center">
416
  <div class="form-group">
417
- <span class="td-head">' . $attribute["field"] . " " . (isset($attribute["required"]) && $attribute["required"] == 1 ? '<span style="color: red;"> *</span>' : "") . '
418
  <div class="tvc-tooltip">
419
- <span class="tvc-tooltiptext tvc-tooltip-right">'.(isset($attribute["desc"])? $attribute["desc"]:"") .'</span>
420
- <img src="'. esc_url(ENHANCAD_PLUGIN_URL."/admin/images/icon/informationI.svg").'" alt=""/>
421
  </div>
422
  </span>
423
  </div>
@@ -429,33 +435,33 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
429
  $require = (isset($attribute['required']) && $attribute['required'])?true:false;
430
  $sel_val_def = (isset($attribute['wAttribute']))?$attribute['wAttribute']:"";
431
  if($attribute["field"]=='link'){
432
- echo "product link";
433
  }else if($attribute["field"]=='shipping'){
434
  //$name, $class_id, string $label=null, $sel_val = null, bool $require = false
435
  $sel_val = (isset($ee_mapped_attrs[$attribute["field"]]))?$ee_mapped_attrs[$attribute["field"]]:$sel_val_def;
436
- echo $this->TVC_Admin_Helper->tvc_text($attribute["field"], 'number', '', __('Add shipping flat rate','conversios'), $sel_val, $require);
437
  }else if($attribute["field"]=='tax'){
438
  //$name, $class_id, string $label=null, $sel_val = null, bool $require = false
439
- $sel_val = (isset($ee_mapped_attrs[$attribute["field"]]))?$ee_mapped_attrs[$attribute["field"]]:$sel_val_def;
440
  echo $this->TVC_Admin_Helper->tvc_text($attribute["field"], 'number', '', 'Add TAX flat (%)', $sel_val, $require);
441
  }else if($attribute["field"]=='content_language'){
442
- echo $this->TVC_Admin_Helper->tvc_language_select($attribute["field"], 'content_language', __('Please Select Attribute','conversios'), 'en',$require);
443
  }else if($attribute["field"]=='target_country'){
444
  //$name, $class_id, bool $require = false
445
- echo $this->TVC_Admin_Helper->tvc_countries_select($attribute["field"], 'target_country', __('Please Select Attribute','conversios'), $require);
446
  }else{
447
  if(isset($attribute['fixed_options']) && $attribute['fixed_options'] !=""){
448
  $tvc_select_option_t = explode(",", $attribute['fixed_options']);
449
  $tvc_select_option=[];
450
  foreach( $tvc_select_option_t as $o_val ){
451
- $tvc_select_option[]['field'] = $o_val;
452
  }
453
  $sel_val = $sel_val_def;
454
- $this->TVC_Admin_Helper->tvc_select($attribute["field"],$attribute["field"],__('Please Select Attribute','conversios'), $sel_val, $require, $tvc_select_option);
455
  }else{
456
  $sel_val = (isset($ee_mapped_attrs[$attribute["field"]]))?$ee_mapped_attrs[$attribute["field"]]:$sel_val_def;
457
  //$name, $class_id, $label="Please Select", $sel_val, $require, $option_list
458
- $this->TVC_Admin_Helper->tvc_select($attribute["field"],$attribute["field"],__('Please Select Attribute','conversios'), $sel_val, $require, $tvc_select_option);
459
  }
460
  }
461
  echo '</div>
@@ -473,7 +479,7 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
473
  </div>
474
  </div>
475
  <div class="progress-bar-wapper">
476
- <span class="tvc-sync-message"><?php _e("Initializing...","conversios"); ?></span>
477
  <div class="progress tvc-sync-progress-db">
478
  <div class="progress-bar progress-bar-striped progress-bar-animated tvc-sync-progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
479
  </div>
@@ -626,14 +632,14 @@ if ( ! class_exists( 'TVCProductSyncHelper' ) ) {
626
  }else{
627
  tvc_helper.tvc_alert("error","",response.message);
628
  setTimeout(function(){
629
- window.location.replace("<?php echo $this->site_url.'sync_product_page'; ?>");
630
  }, 2000);
631
  }
632
 
633
  }
634
  });
635
  }
636
-
637
  $(document).on("show.bs.modal", "#syncProduct", function (e) {
638
  jQuery("#feed-spinner").css("display", "block");
639
  selectCategory();
91
  }
92
  }
93
  if(empty($sync_profile_data)){
94
+ return array("error"=>true,"message"=>esc_html__("No product sync profiles find.","conversios"));
95
  }
96
  if(empty($products)){
97
+ return array("error"=>true,"message"=>esc_html__("Products not found.","conversios"));
98
  }
99
  foreach ($products as $postkey => $postvalue) {
100
  $product_ids[] = $postvalue->w_product_id;
119
  }
120
 
121
  if(empty($formArray)){
122
+ return array("error"=>true,"message"=>esc_html__("Product sync profile not found.","conversios"));
123
  }
124
  //$formArray = json_decode($postvalue->g_attribute_mapping, true);
125
  foreach ($fixed_att_select_list as $fixed_key) {
142
 
143
  if($prd->get_type() == "variable"){
144
  /*$variation_attributes = $prd->get_variation_attributes();*/
145
+ //$p_variations = $prd->get_available_variations();
146
+ $p_variations = $prd->get_children();
147
  if(!empty($p_variations)){
148
+ foreach ($p_variations as $v_key => $variation_id) {
149
+ $variation = wc_get_product( $variation_id );
150
+ if(empty($variation)){
151
+ continue;
152
+ }
153
+ $variation_description = wc_format_content( $variation->get_description() );
154
  unset($product['customAttributes']);
155
+ $postmeta_var = (object)$this->TVC_Admin_Helper->tvc_get_post_meta($variation_id);
156
  $formArray_val = $formArray['title'];
157
  $product['title'] = (isset($postObj->$formArray_val))?$postObj->$formArray_val:get_the_title($postvalue->w_product_id);
158
  $tvc_temp_desc_key = $formArray['description'];
159
+ $product['description'] = ( $variation_description != "")?sanitize_text_field($variation_description):$postObj->$tvc_temp_desc_key;
160
+ $product['offer_id'] = $variation_id;
161
+ $product['id'] = $variation_id;
162
  $product['item_group_id'] = $postvalue->w_product_id;
163
  $productTypes = $this->get_product_category($postvalue->w_product_id);
164
  if(!empty($productTypes)){
165
  $product['productTypes'] = $productTypes;
166
  }
167
+ $image_id = $variation->get_image_id();
168
  $product['image_link'] = wp_get_attachment_image_url($image_id, 'full');
169
+ $variation_attributes = $variation->get_variation_attributes();
170
+
171
+ if(!empty($variation_attributes) ){
172
+ foreach($variation_attributes as $va_key => $va_value ){
173
+ $va_key = str_replace("_", " ", $va_key);
174
+ if (strpos($va_key, 'color') !== false) {
175
+ $product['color'] = $va_value;
176
+ }else if (strpos($va_key, 'size') !== false) {
177
+ $product['sizes'] = $va_value;
178
+ }else{
179
+ $va_key = str_replace("attribute", "", $va_key);
180
+ $product['customAttributes'][] = array("name"=>$va_key, "value"=>$va_value);
181
  }
182
  }
183
+ }
184
 
185
  foreach($formArray as $key => $value){
186
  if($key == 'price'){
321
  $CustomApi = new CustomApi();
322
  $product_count = $this->TVC_Admin_DB_Helper->tvc_row_count('ee_prouct_pre_sync_data');
323
  //$count = 0;
324
+ $pre_last_sync_product_id = sanitize_text_field($last_sync_product_id);
325
  if( $product_count > 0 ){
326
  $tvc_currency = esc_attr($this->TVC_Admin_Helper->get_woo_currency());
327
  $merchantId = esc_attr($this->merchantId);
372
  }
373
 
374
  public function tvc_product_sync_popup_html(){
375
+
376
  ob_start();
377
  ?>
378
  <div class="modal fade popup-modal create-campa overlay" id="syncProduct" data-backdrop="false">
379
  <div class="modal-dialog modal-dialog-centered">
380
  <div class="modal-content">
381
  <div class="modal-body">
382
+ <button type="button" class="btn-close tvc-popup-close" data-bs-dismiss="modal" aria-label="Close"> &times; </button>
383
+ <h5><?php esc_html_e("Map your product attributes","conversios"); ?></h5>
384
+ <p><?php esc_html_e("Google Merchant Center uses attributes to format your product information for Shopping Ads. Map your product attributes to the Merchant Center product attributes below. You can also edit each product’s individual attributes after you sync your products. Not all fields below are marked required, however based on your shop's categories and your country you might map a few optional attributes as well. See the full guide","conversios"); ?> <a target="_blank" href="<?php esc_url_raw("https://support.google.com/merchants/answer/7052112"); ?>"><?php esc_html_e("here","conversios"); ?></a>.
385
  </p>
386
  <div class="wizard-section campaign-wizard">
387
  <div class="wizard-content">
388
  <input type="hidden" name="merchant_id" id="merchant_id" value="<?php echo esc_attr($this->merchantId); ?>">
389
  <form class="tab-wizard wizard- wizard" id="productSync" method="POST">
390
+ <h5><span class="wiz-title"><?php esc_html_e("Category Mapping","conversios"); ?></span></h5>
391
  <section>
392
  <div class="card-wrapper">
393
  <div class="row">
394
  <div class="col-6">
395
+ <h6 class="heading-tbl"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/woocommerce.svg'); ?>" alt="WooCommerce"/><?php esc_html_e("Commerce Category","conversios"); ?></h6>
396
  </div>
397
  <div class="col-6">
398
+ <h6 class="heading-tbl gmc-image-heading"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/google-shopping.svg'); ?>" alt="google-shopping"/><?php esc_html_e("Google Merchant Center Category","conversios"); ?></h6>
399
  </div>
400
+ </div><?php echo $this->category_wrapper_obj->category_table_content('mapping'); ?>
401
  </div>
402
  </section>
403
  <!-- Step 2 -->
404
+ <h5><span class="wiz-title"><?php esc_html_e("Product Attribution Mapping","conversios"); ?></span></h5>
405
  <section>
406
  <div class="card-wrapper">
407
  <div class="row">
408
  <div class="col-6">
409
+ <h6 class="heading-tbl gmc-image-heading"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/google-shopping.svg'); ?>" alt="google-shopping"/><?php esc_html_e("Google Merchant center product attributes","conversios"); ?></h6>
410
  </div>
411
  <div class="col-6">
412
+ <h6 class="heading-tbl"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/woocommerce.svg'); ?>" alt="WooCommerce"/><?php esc_html_e("Commerce product attributes","conversios"); ?></h6>
413
  </div>
414
  </div>
415
  <?php
420
  echo '<div class="row">
421
  <div class="col-6 align-self-center">
422
  <div class="form-group">
423
+ <span class="td-head">' . esc_attr($attribute["field"]) . " " . (isset($attribute["required"]) && esc_attr($attribute["required"]) == 1 ? '<span style="color: red;"> *</span>' : "") . '
424
  <div class="tvc-tooltip">
425
+ <span class="tvc-tooltiptext tvc-tooltip-right">'.(isset($attribute["desc"])? esc_attr($attribute["desc"]):"") .'</span>
426
+ <img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL."/admin/images/icon/informationI.svg").'" alt=""/>
427
  </div>
428
  </span>
429
  </div>
435
  $require = (isset($attribute['required']) && $attribute['required'])?true:false;
436
  $sel_val_def = (isset($attribute['wAttribute']))?$attribute['wAttribute']:"";
437
  if($attribute["field"]=='link'){
438
+ echo esc_attr("product link");
439
  }else if($attribute["field"]=='shipping'){
440
  //$name, $class_id, string $label=null, $sel_val = null, bool $require = false
441
  $sel_val = (isset($ee_mapped_attrs[$attribute["field"]]))?$ee_mapped_attrs[$attribute["field"]]:$sel_val_def;
442
+ echo $this->TVC_Admin_Helper->tvc_text($attribute["field"], 'number', '', esc_html__('Add shipping flat rate','conversios'), $sel_val, $require);
443
  }else if($attribute["field"]=='tax'){
444
  //$name, $class_id, string $label=null, $sel_val = null, bool $require = false
445
+ $sel_val = (isset($ee_mapped_attrs[$attribute["field"]]))?esc_attr($ee_mapped_attrs[$attribute["field"]]):esc_attr($sel_val_def);
446
  echo $this->TVC_Admin_Helper->tvc_text($attribute["field"], 'number', '', 'Add TAX flat (%)', $sel_val, $require);
447
  }else if($attribute["field"]=='content_language'){
448
+ echo $this->TVC_Admin_Helper->tvc_language_select($attribute["field"], 'content_language', esc_html__('Please Select Attribute','conversios'), 'en',$require);
449
  }else if($attribute["field"]=='target_country'){
450
  //$name, $class_id, bool $require = false
451
+ echo $this->TVC_Admin_Helper->tvc_countries_select($attribute["field"], 'target_country', esc_html__('Please Select Attribute','conversios'), $require);
452
  }else{
453
  if(isset($attribute['fixed_options']) && $attribute['fixed_options'] !=""){
454
  $tvc_select_option_t = explode(",", $attribute['fixed_options']);
455
  $tvc_select_option=[];
456
  foreach( $tvc_select_option_t as $o_val ){
457
+ $tvc_select_option[]['field'] = esc_attr($o_val);
458
  }
459
  $sel_val = $sel_val_def;
460
+ $this->TVC_Admin_Helper->tvc_select($attribute["field"],$attribute["field"],esc_html__('Please Select Attribute','conversios'), $sel_val, $require, $tvc_select_option);
461
  }else{
462
  $sel_val = (isset($ee_mapped_attrs[$attribute["field"]]))?$ee_mapped_attrs[$attribute["field"]]:$sel_val_def;
463
  //$name, $class_id, $label="Please Select", $sel_val, $require, $option_list
464
+ $this->TVC_Admin_Helper->tvc_select($attribute["field"],$attribute["field"],esc_html__('Please Select Attribute','conversios'), $sel_val, $require, $tvc_select_option);
465
  }
466
  }
467
  echo '</div>
479
  </div>
480
  </div>
481
  <div class="progress-bar-wapper">
482
+ <span class="tvc-sync-message"><?php esc_html_e("Initializing...","conversios"); ?></span>
483
  <div class="progress tvc-sync-progress-db">
484
  <div class="progress-bar progress-bar-striped progress-bar-animated tvc-sync-progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
485
  </div>
632
  }else{
633
  tvc_helper.tvc_alert("error","",response.message);
634
  setTimeout(function(){
635
+ window.location.replace("<?php echo esc_url_raw($this->site_url.'sync_product_page'); ?>");
636
  }, 2000);
637
  }
638
 
639
  }
640
  });
641
  }
642
+
643
  $(document).on("show.bs.modal", "#syncProduct", function (e) {
644
  jQuery("#feed-spinner").css("display", "block");
645
  selectCategory();
includes/setup/google-ads.php CHANGED
@@ -51,7 +51,7 @@ class GoogleAds {
51
  }else{
52
  $googleDetail_setting->link_google_analytics_with_google_ads = 0;
53
  }
54
- $googleDetail['setting'] =$googleDetail_setting;
55
  $this->TVC_Admin_Helper->set_ee_options_data($googleDetail);
56
  $class = 'alert-message tvc-alert-success';
57
  $message = esc_html__("Your tracking options have been saved.","conversios");
@@ -73,22 +73,22 @@ class GoogleAds {
73
  <div class="row">
74
  <div class="col-md-6 col-lg-8 border-right">
75
  <form method="post" name="google-analytic" class="tvc_ee_plugin_form">
76
- <input type="hidden" name="subscription_id" value="<?php echo (($this->subscriptionId)?$this->subscriptionId:"");?>">
77
  <div class="google-account-analytics">
78
  <div class="row mb-3">
79
  <div class="col-6 col-md-6 col-lg-6">
80
- <h2 class="ga-title"><?php _e("Connected Google Ads account:","conversios"); ?></h2>
81
  </div>
82
  <div class="col-6 col-md-6 col-lg-6 text-right">
83
  <div class="acc-num">
84
  <p class="ga-text">
85
- <?php echo (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') ? $googleDetail->google_ads_id : __('<span>Get started</span>','conversios'); ?>
86
  </p>
87
  <?php
88
  if (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') {
89
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
90
  } else {
91
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
92
  }?>
93
  </div>
94
  </div>
@@ -96,35 +96,35 @@ class GoogleAds {
96
  </div>
97
  <div class="row mb-3">
98
  <div class="col-6 col-md-6 col-lg-6">
99
- <h2 class="ga-title"><?php _e("Linked Google Analytics Account:","conversios"); ?></h2>
100
  </div>
101
  <div class="col-6 col-md-6 col-lg-6 text-right">
102
  <div class="acc-num">
103
  <p class="ga-text">
104
- <?php echo isset($googleDetail->property_id) && $googleDetail->property_id != '' ? $googleDetail->property_id : __('<span>Get started</span>','conversios');?>
105
  </p>
106
  <?php
107
  if(isset($googleDetail->property_id) && $googleDetail->property_id != ''){
108
- echo '<p class="ga-text text-right"><a href="' . esc_url($this->url) . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
109
  }else{
110
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
111
  } ?>
112
  </div>
113
  </div>
114
  </div>
115
  <div class="row mb-3">
116
  <div class="col-6 col-md-6 col-lg-6">
117
- <h2 class="ga-title"><?php _e("Linked Google Merchant Center Account:","conversios"); ?></h2>
118
  </div>
119
  <div class="col-6 col-md-6 col-lg-6 text-right">
120
  <div class="acc-num">
121
- <p class="ga-text"><?php echo isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != '' ? $googleDetail->google_merchant_center_id : __('<span>Get started</span>','conversios'); ?>
122
  </p>
123
  <?php
124
  if (isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != '') {
125
- echo '<p class="ga-text text-right"><a target="_blank" href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
126
  } else {
127
- echo '<p class="ga-text text-right"><a href="#" class="text-underline" data-toggle="modal" data-target="#tvc_google_connect"><img src="'.esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
128
  } ?>
129
  </div>
130
  </div>
@@ -136,7 +136,7 @@ class GoogleAds {
136
  <div class="form-group">
137
  <div class="tvc-custom-control tvc-custom-checkbox">
138
  <input type="checkbox" class="tvc-custom-control-input" id="customCheck1" name="remarketing_tags" value="1" <?php echo (esc_attr($googleDetail->remarketing_tags) == 1) ? 'checked="checked"' : ''; ?> >
139
- <label class="custom-control-label" for="customCheck1"><?php _e("Enable remarketing tags","conversios"); ?></label>
140
  </div>
141
  </div>
142
  </div>
@@ -144,7 +144,7 @@ class GoogleAds {
144
  <div class="form-group">
145
  <div class="tvc-custom-control tvc-custom-checkbox">
146
  <input type="checkbox" class="tvc-custom-control-input" id="customCheck2" name="dynamic_remarketing_tags" value="1" <?php echo (esc_attr($googleDetail->dynamic_remarketing_tags) == 1) ? 'checked="checked"' : ''; ?>>
147
- <label class="custom-control-label" for="customCheck2"><?php _e("Enable dynamic remarketing tags","conversios"); ?></label>
148
  </div>
149
  </div>
150
  </div>
@@ -153,7 +153,7 @@ class GoogleAds {
153
  <div class="form-group">
154
  <div class="tvc-custom-control tvc-custom-checkbox">
155
  <input type="checkbox" class="tvc-custom-control-input" id="google_ads_conversion_tracking" name="google_ads_conversion_tracking" value="1" <?php echo (esc_attr($googleDetail->google_ads_conversion_tracking) == 1) ? 'checked="checked"' : ''; ?>>
156
- <label class="custom-control-label" for="google_ads_conversion_tracking"><?php _e("Enable Google Ads conversion tracking","conversios"); ?></label>
157
  </div>
158
  </div>
159
  </div>
@@ -162,21 +162,19 @@ class GoogleAds {
162
  <div class="form-group">
163
  <div class="tvc-custom-control tvc-custom-checkbox">
164
  <input type="checkbox" class="tvc-custom-control-input" id="customCheck3" name="link_google_analytics_with_google_ads" value="1" <?php echo (esc_attr($googleDetail->link_google_analytics_with_google_ads) == 1) ? 'checked="checked"' : ''; ?> >
165
- <label class="custom-control-label" for="customCheck3"><?php _e("Link Google analytics with google ads","conversios"); ?></label>
166
  </div>
167
  </div>
168
  </div>
169
  </div>
170
  <?php
171
- }else{
172
- $icon_img ='<img src="'.esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/config-success.svg').'" alt="configuration success" class="config-success">';
173
- ?>
174
- <h2 class="ga-title"><?php _e("Connect Google Ads account to enable below features.","conversios"); ?></h2>
175
  <br>
176
  <ul>
177
- <li><?php echo $icon_img;?><?php _e("Enable remarketing tags","conversios"); ?></li>
178
- <li><?php echo $icon_img;?><?php _e("Enable dynamic remarketing tags","conversios"); ?></li>
179
- <li><?php echo $icon_img;?><?php _e("Link Google analytics with google ads","conversios"); ?></li>
180
  </ul>
181
  <?php
182
  } ?>
@@ -184,7 +182,7 @@ class GoogleAds {
184
  <?php
185
  if (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') { ?>
186
  <div class="text-left">
187
- <button type="submit" id="google-add" class="btn btn-primary btn-success" name="google-add"><?php _e("Save","conversios"); ?></button>
188
  </div>
189
  <?php } ?>
190
  </form>
51
  }else{
52
  $googleDetail_setting->link_google_analytics_with_google_ads = 0;
53
  }
54
+ $googleDetail['setting'] = $googleDetail_setting;
55
  $this->TVC_Admin_Helper->set_ee_options_data($googleDetail);
56
  $class = 'alert-message tvc-alert-success';
57
  $message = esc_html__("Your tracking options have been saved.","conversios");
73
  <div class="row">
74
  <div class="col-md-6 col-lg-8 border-right">
75
  <form method="post" name="google-analytic" class="tvc_ee_plugin_form">
76
+ <input type="hidden" name="subscription_id" value="<?php echo (($this->subscriptionId)?esc_attr($this->subscriptionId):"");?>">
77
  <div class="google-account-analytics">
78
  <div class="row mb-3">
79
  <div class="col-6 col-md-6 col-lg-6">
80
+ <h2 class="ga-title"><?php esc_html_e("Connected Google Ads account:","conversios"); ?></h2>
81
  </div>
82
  <div class="col-6 col-md-6 col-lg-6 text-right">
83
  <div class="acc-num">
84
  <p class="ga-text">
85
+ <?php echo (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') ? esc_attr($googleDetail->google_ads_id) :'<span>'. esc_html__('Get started','conversios').'</span>'; ?>
86
  </p>
87
  <?php
88
  if (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') {
89
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
90
  } else {
91
+ echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
92
  }?>
93
  </div>
94
  </div>
96
  </div>
97
  <div class="row mb-3">
98
  <div class="col-6 col-md-6 col-lg-6">
99
+ <h2 class="ga-title"><?php esc_html_e("Linked Google Analytics Account:","conversios"); ?></h2>
100
  </div>
101
  <div class="col-6 col-md-6 col-lg-6 text-right">
102
  <div class="acc-num">
103
  <p class="ga-text">
104
+ <?php echo isset($googleDetail->property_id) && $googleDetail->property_id != '' ? esc_attr($googleDetail->property_id) : '<span>'. esc_html__('Get started','conversios').'</span>';?>
105
  </p>
106
  <?php
107
  if(isset($googleDetail->property_id) && $googleDetail->property_id != ''){
108
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
109
  }else{
110
+ echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
111
  } ?>
112
  </div>
113
  </div>
114
  </div>
115
  <div class="row mb-3">
116
  <div class="col-6 col-md-6 col-lg-6">
117
+ <h2 class="ga-title"><?php esc_html_e("Linked Google Merchant Center Account:","conversios"); ?></h2>
118
  </div>
119
  <div class="col-6 col-md-6 col-lg-6 text-right">
120
  <div class="acc-num">
121
+ <p class="ga-text"><?php echo isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != '' ? esc_attr($googleDetail->google_merchant_center_id) :'<span>'. esc_html__('Get started','conversios').'</span>'; ?>
122
  </p>
123
  <?php
124
  if (isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != '') {
125
+ echo '<p class="ga-text text-right"><a target="_blank" href="' . $this->url . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
126
  } else {
127
+ echo '<p class="ga-text text-right"><a href="#" class="text-underline" data-toggle="modal" data-target="#tvc_google_connect"><img src="'.esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
128
  } ?>
129
  </div>
130
  </div>
136
  <div class="form-group">
137
  <div class="tvc-custom-control tvc-custom-checkbox">
138
  <input type="checkbox" class="tvc-custom-control-input" id="customCheck1" name="remarketing_tags" value="1" <?php echo (esc_attr($googleDetail->remarketing_tags) == 1) ? 'checked="checked"' : ''; ?> >
139
+ <label class="custom-control-label" for="customCheck1"><?php esc_html_e("Enable remarketing tags","conversios"); ?></label>
140
  </div>
141
  </div>
142
  </div>
144
  <div class="form-group">
145
  <div class="tvc-custom-control tvc-custom-checkbox">
146
  <input type="checkbox" class="tvc-custom-control-input" id="customCheck2" name="dynamic_remarketing_tags" value="1" <?php echo (esc_attr($googleDetail->dynamic_remarketing_tags) == 1) ? 'checked="checked"' : ''; ?>>
147
+ <label class="custom-control-label" for="customCheck2"><?php esc_html_e("Enable dynamic remarketing tags","conversios"); ?></label>
148
  </div>
149
  </div>
150
  </div>
153
  <div class="form-group">
154
  <div class="tvc-custom-control tvc-custom-checkbox">
155
  <input type="checkbox" class="tvc-custom-control-input" id="google_ads_conversion_tracking" name="google_ads_conversion_tracking" value="1" <?php echo (esc_attr($googleDetail->google_ads_conversion_tracking) == 1) ? 'checked="checked"' : ''; ?>>
156
+ <label class="custom-control-label" for="google_ads_conversion_tracking"><?php esc_html_e("Enable Google Ads conversion tracking","conversios"); ?></label>
157
  </div>
158
  </div>
159
  </div>
162
  <div class="form-group">
163
  <div class="tvc-custom-control tvc-custom-checkbox">
164
  <input type="checkbox" class="tvc-custom-control-input" id="customCheck3" name="link_google_analytics_with_google_ads" value="1" <?php echo (esc_attr($googleDetail->link_google_analytics_with_google_ads) == 1) ? 'checked="checked"' : ''; ?> >
165
+ <label class="custom-control-label" for="customCheck3"><?php esc_html_e("Link Google analytics with google ads","conversios"); ?></label>
166
  </div>
167
  </div>
168
  </div>
169
  </div>
170
  <?php
171
+ }else{ ?>
172
+ <h2 class="ga-title"><?php esc_html_e("Connect Google Ads account to enable below features.","conversios"); ?></h2>
 
 
173
  <br>
174
  <ul>
175
+ <li><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/config-success.svg'); ?>" alt="configuration success" class="config-success"><?php esc_html_e("Enable remarketing tags","conversios"); ?></li>
176
+ <li><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/config-success.svg'); ?>" alt="configuration success" class="config-success"><?php esc_html_e("Enable dynamic remarketing tags","conversios"); ?></li>
177
+ <li><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/config-success.svg'); ?>" alt="configuration success" class="config-success"><?php esc_html_e("Link Google analytics with google ads","conversios"); ?></li>
178
  </ul>
179
  <?php
180
  } ?>
182
  <?php
183
  if (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') { ?>
184
  <div class="text-left">
185
+ <button type="submit" id="google-add" class="btn btn-primary btn-success" name="google-add"><?php esc_html_e("Save","conversios"); ?></button>
186
  </div>
187
  <?php } ?>
188
  </form>
includes/setup/google-shopping-feed-gaa-config.php CHANGED
@@ -4,8 +4,6 @@ class GAAConfiguration {
4
  protected $subscriptionId;
5
  protected $TVCProductSyncHelper;
6
  public function __construct() {
7
- ini_set('max_execution_time', '0');
8
- ini_set('memory_limit','-1');
9
  $this->includes();
10
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
11
  $this->TVCProductSyncHelper = new TVCProductSyncHelper();
@@ -26,14 +24,14 @@ class GAAConfiguration {
26
  }
27
 
28
  public function create_form() {
29
- if(isset($_GET['welcome_msg']) && $_GET['welcome_msg'] == true){
30
  $class = 'notice notice-success';
31
- $message = esc_html__('Get your WooCommerce products in front of the millions of shoppers across Google by setting up your Google Merchant Center account from below.');
32
  printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), esc_html($message));
33
  ?>
34
  <script>
35
  $(document).ready(function() {
36
- var msg="<?php echo $message;?>"
37
  tvc_helper.tvc_alert("success","Hey!",msg,true);
38
  });
39
  </script>
@@ -53,12 +51,6 @@ class GAAConfiguration {
53
  <div class="tab-card">
54
  <div class="row">
55
  <div class="col-md-6 col-lg-8 edit-section">
56
- <div class="edit-header-section">
57
- <script>
58
- var back_img = '<img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/left-angle-arrow.svg'); ?>" alt="back"/>';
59
- document.write('<a href="' + document.referrer + '" class="back-btn">'+back_img+'<span>Back</span></a>');
60
- </script>
61
- </div>
62
  <div class="configuration-section" id="config-pt1">
63
  <?php echo get_google_shopping_tabs_html($this->site_url,(isset($googleDetail->google_merchant_center_id))?$googleDetail->google_merchant_center_id:""); ?>
64
  </div>
@@ -66,69 +58,69 @@ class GAAConfiguration {
66
  <div class="google-account-analytics" id="gaa-config">
67
  <div class="row mb-3">
68
  <div class="col-6 col-md-6 col-lg-6">
69
- <h2 class="ga-title"><?php _e("Connected Google Merchant center account:","conversios"); ?></h2>
70
  </div>
71
  <div class="col-6 col-md-6 col-lg-6 text-right">
72
  <div class="acc-num">
73
- <p class="ga-text"><?php echo ((isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != '') ? $googleDetail->google_merchant_center_id : __('<span>Get started</span>','conversios')); ?></p>
74
  <?php
75
- if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != ''){
76
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'.esc_url( ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
77
  }else{
78
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
79
  }?>
80
  </div>
81
  </div>
82
  </div>
83
  <div class="row mb-3">
84
  <div class="col-6 col-md-6 col-lg-6">
85
- <h2 class="ga-title"><?php _e("Linked Google Ads Account:","conversios"); ?></h2>
86
  </div>
87
  <div class="col-6 col-md-6 col-lg-6 text-right">
88
  <div class="acc-num">
89
- <p class="ga-text"><?php echo (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '' ? $googleDetail->google_ads_id : __('<span>Get started</span>','conversios'));?></p>
90
  <?php
91
- if (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != '') {
92
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
93
  } else {
94
- echo '<p class="ga-text text-right"><a href="' . $this->url . '" class="text-underline"><img src="'. esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
95
  } ?>
96
  </div>
97
  </div>
98
  </div>
99
  <?php
100
- if (isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != '') {?>
101
  <div class="row mb-3">
102
  <div class="col-6 col-md-4">
103
- <h2 class="ga-title"><?php _e("Sync Products:","conversios"); ?></h2>
104
  </div>
105
  <div class="col-6 col-md-4">
106
- <button id="tvc_btn_product_sync" type="button" class="btn btn-primary btn-success" data-toggle="modal" data-target="#syncProduct"><?php _e("Sync New Products","conversios"); ?></button>
107
  </div>
108
  </div>
109
  <div class="row mb-3">
110
  <div class="col-6 col-md-4">
111
- <h2 class="ga-title"><?php _e("Smart Shopping Campaigns:","conversios"); ?></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"><?php _e("Create Smart Shopping Campaign","conversios"); ?></a>
115
  </div>
116
  </div>
117
  <?php }else{ ?>
118
  <div class="row mb-3">
119
  <div class="col-6 col-md-4">
120
- <h2 class="ga-title"><?php _e("Sync Products:","conversios"); ?></h2>
121
  </div>
122
  <div class="col-6 col-md-4">
123
- <a href="<?php echo $this->url; ?>" class="btn btn-primary btn-success"><?php _e("Sync New Products","conversios"); ?></a>
124
  </div>
125
  </div>
126
  <div class="row mb-3">
127
  <div class="col-6 col-md-4">
128
- <h2 class="ga-title"><?php _e("Smart Shopping Campaigns:","conversios"); ?></h2>
129
  </div>
130
  <div class="col-6 col-md-6">
131
- <a href="<?php echo $this->url; ?>" class="btn btn-primary btn-success"><?php _e("Create Smart Shopping Campaign","conversios"); ?></a>
132
  </div>
133
  </div>
134
  <?php } ?>
@@ -148,7 +140,7 @@ echo $this->TVCProductSyncHelper->tvc_product_sync_popup_html();
148
  ?>
149
  <?php
150
  $is_need_to_domain_claim = false;
151
- if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id && $this->subscriptionId != "" && isset($googleDetail->is_domain_claim) && $googleDetail->is_domain_claim == '0'){
152
  $is_need_to_domain_claim = true;
153
  }?>
154
  <script type="text/javascript">
4
  protected $subscriptionId;
5
  protected $TVCProductSyncHelper;
6
  public function __construct() {
 
 
7
  $this->includes();
8
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
9
  $this->TVCProductSyncHelper = new TVCProductSyncHelper();
24
  }
25
 
26
  public function create_form() {
27
+ if(isset($_GET['welcome_msg']) && sanitize_textarea_field($_GET['welcome_msg']) == true){
28
  $class = 'notice notice-success';
29
+ $message = esc_html__("Get your WooCommerce products in front of the millions of shoppers across Google by setting up your Google Merchant Center account from below.","conversios");
30
  printf('<div class="%1$s"><p>%2$s</p></div>', esc_attr($class), esc_html($message));
31
  ?>
32
  <script>
33
  $(document).ready(function() {
34
+ var msg="<?php echo esc_html($message);?>"
35
  tvc_helper.tvc_alert("success","Hey!",msg,true);
36
  });
37
  </script>
51
  <div class="tab-card">
52
  <div class="row">
53
  <div class="col-md-6 col-lg-8 edit-section">
 
 
 
 
 
 
54
  <div class="configuration-section" id="config-pt1">
55
  <?php echo get_google_shopping_tabs_html($this->site_url,(isset($googleDetail->google_merchant_center_id))?$googleDetail->google_merchant_center_id:""); ?>
56
  </div>
58
  <div class="google-account-analytics" id="gaa-config">
59
  <div class="row mb-3">
60
  <div class="col-6 col-md-6 col-lg-6">
61
+ <h2 class="ga-title"><?php esc_html_e("Connected Google Merchant center account:","conversios"); ?></h2>
62
  </div>
63
  <div class="col-6 col-md-6 col-lg-6 text-right">
64
  <div class="acc-num">
65
+ <p class="ga-text"><?php echo ((isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) != '') ? esc_attr($googleDetail->google_merchant_center_id) : '<span>'.esc_html__('Get started','conversios').'</span>'); ?></p>
66
  <?php
67
+ if(isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) != ''){
68
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'.esc_url_raw( ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
69
  }else{
70
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
71
  }?>
72
  </div>
73
  </div>
74
  </div>
75
  <div class="row mb-3">
76
  <div class="col-6 col-md-6 col-lg-6">
77
+ <h2 class="ga-title"><?php esc_html_e("Linked Google Ads Account:","conversios"); ?></h2>
78
  </div>
79
  <div class="col-6 col-md-6 col-lg-6 text-right">
80
  <div class="acc-num">
81
+ <p class="ga-text"><?php echo (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != '' ? esc_attr($googleDetail->google_ads_id) : '<span>'.esc_html__('Get started','conversios').'</span>');?></p>
82
  <?php
83
+ if (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != '') {
84
+ echo '<p class="ga-text text-right"><a href="' . esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/refresh.svg').'" alt="refresh"/></a></p>';
85
  } else {
86
+ echo '<p class="ga-text text-right"><a href="' .esc_url_raw($this->url) . '" class="text-underline"><img src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/add.svg').'" alt="connect account"/></a></p>';
87
  } ?>
88
  </div>
89
  </div>
90
  </div>
91
  <?php
92
+ if (isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) != '') {?>
93
  <div class="row mb-3">
94
  <div class="col-6 col-md-4">
95
+ <h2 class="ga-title"><?php esc_html_e("Sync Products:","conversios"); ?></h2>
96
  </div>
97
  <div class="col-6 col-md-4">
98
+ <button id="tvc_btn_product_sync" type="button" class="btn btn-primary btn-success" data-bs-toggle="modal" data-bs-target="#syncProduct"><?php esc_html_e("Sync New Products","conversios"); ?></button>
99
  </div>
100
  </div>
101
  <div class="row mb-3">
102
  <div class="col-6 col-md-4">
103
+ <h2 class="ga-title"><?php esc_html_e("Smart Shopping Campaigns:","conversios"); ?></h2>
104
  </div>
105
  <div class="col-6 col-md-6">
106
+ <a href="admin.php?page=conversios-google-shopping-feed&tab=add_campaign_page" class="btn btn-primary btn-success"><?php esc_html_e("Create Smart Shopping Campaign","conversios"); ?></a>
107
  </div>
108
  </div>
109
  <?php }else{ ?>
110
  <div class="row mb-3">
111
  <div class="col-6 col-md-4">
112
+ <h2 class="ga-title"><?php esc_html_e("Sync Products:","conversios"); ?></h2>
113
  </div>
114
  <div class="col-6 col-md-4">
115
+ <a href="<?php echo esc_url_raw($this->url); ?>" class="btn btn-primary btn-success"><?php esc_html_e("Sync New Products","conversios"); ?></a>
116
  </div>
117
  </div>
118
  <div class="row mb-3">
119
  <div class="col-6 col-md-4">
120
+ <h2 class="ga-title"><?php esc_html_e("Smart Shopping Campaigns:","conversios"); ?></h2>
121
  </div>
122
  <div class="col-6 col-md-6">
123
+ <a href="<?php echo esc_url_raw($this->url); ?>" class="btn btn-primary btn-success"><?php esc_html_e("Create Smart Shopping Campaign","conversios"); ?></a>
124
  </div>
125
  </div>
126
  <?php } ?>
140
  ?>
141
  <?php
142
  $is_need_to_domain_claim = false;
143
+ if(isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) && esc_attr($this->subscriptionId) != "" && isset($googleDetail->is_domain_claim) && esc_attr($googleDetail->is_domain_claim) == '0'){
144
  $is_need_to_domain_claim = true;
145
  }?>
146
  <script type="text/javascript">
includes/setup/google-shopping-feed-shopping-campaigns.php CHANGED
@@ -11,8 +11,6 @@ class CampaignsConfiguration
11
  public function __construct(){
12
  $this->includes();
13
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
14
- $this->returnUrl = $_SERVER['REQUEST_URI'];
15
-
16
  $this->customApiObj = new CustomApi();
17
 
18
  $this->merchantId = $this->TVC_Admin_Helper->get_merchantId();
@@ -20,10 +18,10 @@ class CampaignsConfiguration
20
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();;
21
  $this->new_campaign = true;
22
 
23
- $this->date_range_type = (isset($_POST['customRadio'])) ? $_POST['customRadio'] : 1;
24
- $this->days = (isset($_POST['days']) && $_POST['days'] != '') ? $_POST['days'] : 7;
25
- $this->from_date = (isset($_POST['from_date']) && $_POST['from_date'] != '') ? $_POST['from_date'] : "";
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=";
@@ -56,10 +54,10 @@ class CampaignsConfiguration
56
  }
57
  }
58
 
59
- $date_range_type = sanitize_text_field((isset($_POST['customRadio'])) ? $_POST['customRadio'] : 1);
60
- $days = sanitize_text_field((isset($_POST['days']) && $_POST['days'] != '') ? $_POST['days'] : 7);
61
- $from_date = (isset($_POST['from_date']) && $_POST['from_date'] != '') ? $_POST['from_date'] : "";
62
- $to_date = (isset($_POST['to_date']) && $_POST['to_date'] != '') ? $_POST['to_date'] : "";
63
  $from_date = sanitize_text_field($from_date);
64
  $to_date = sanitize_text_field($to_date);
65
  $campaigns_list = [];
@@ -74,8 +72,8 @@ class CampaignsConfiguration
74
 
75
  if (isset($campaigns_list_res->errors) && !empty($campaigns_list_res->errors)) {
76
  $class = 'error';
77
- $message = esc_html__('Not any campaigns found.');
78
- printf('<div class="alert-message"><div class="%1$s" role="alert"><img src="'. plugins_url('img/missing-warning.svg', __FILE__) . '" alt="" class="mr-2"/>%2$s</div></div>', esc_attr($class), esc_html($message));
79
  } else if(isset($campaigns_list_res->data)){
80
  $campaigns_list_res = $campaigns_list_res->data;
81
  if ($campaigns_list_res['status'] == 200) {
@@ -128,9 +126,9 @@ class CampaignsConfiguration
128
  $campaign_performance = $campaign_performance_res['data'];
129
  }
130
  }
131
- }else if(isset($_GET['id']) && $_GET['id'] != '') {
132
  //Product Performance
133
- $product_performance_res = $api_old_obj->productPerformance($_GET['id'], $this->date_range_type, $this->days, $this->from_date, $this->to_date);
134
 
135
  if (isset($product_performance_res->errors) && !empty($product_performance_res->errors)) {
136
 
@@ -142,7 +140,7 @@ class CampaignsConfiguration
142
  }
143
 
144
  //Product Partition Performance
145
- $product_partition_performance_res = $api_old_obj->productPartitionPerformance($_GET['id'], $this->date_range_type, $this->days, $this->from_date, $this->to_date);
146
 
147
  if (isset($product_partition_performance_res->errors) && !empty($product_partition_performance_res->errors)) {
148
 
@@ -164,12 +162,6 @@ class CampaignsConfiguration
164
  <div class="row">
165
  <div class="col-md-12">
166
  <div class="edit-section">
167
- <div class="edit-header-section">
168
- <script>
169
- var back_img = '<img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/left-angle-arrow.svg'); ?>" alt="back"/>';
170
- document.write('<a href="' + document.referrer + '" class="back-btn">'+back_img+'<span>Back</span></a>');
171
- </script>
172
- </div>
173
  <div class="row">
174
  <div class="configuration-section col-md-6 col-lg-8">
175
  <?php echo get_google_shopping_tabs_html($this->site_url, (isset($googleDetail->google_merchant_center_id))?$googleDetail->google_merchant_center_id:""); ?>
@@ -182,18 +174,18 @@ class CampaignsConfiguration
182
  <input type="hidden" id="customRange2" name="customRadio" value="2" class="custom-control-input" checked="checked">
183
  <div class="campaigns">
184
  <div class="d-flex justify-content-between align-items-center">
185
- <h3 class="title mb-0"><?php _e("Smart Shopping Campaigns","conversios"); ?></h3>
186
  <div class="campaing-date">
187
  <div class="input-group input-daterange">
188
- <input type="text" class="form-control" id="from_date" name="from_date" value="<?php echo $this->from_date; ?>">
189
  <div class="input-group-addon text px-3">to</div>
190
- <input type="text" class="form-control" id="to_date" name="to_date" value="<?php echo $this->to_date; ?>">
191
- <label class="mt-2 mb-2 error-msg float-left hidden" id="errorMessage"><?php _e("Please select both from and to date","conversios"); ?></label>
192
  </div>
193
  </div>
194
  <div class="create-campaign-btn">
195
- <button type="button" class="btn btn-outline-primary" onclick="validateAll()" id="select_range" name="select_range"><?php _e("Submit","conversios"); ?></button>
196
- <a href="<?php echo $this->site_url.'add_campaign_page';?>" class="btn btn-outline-primary"><?php _e("Create a New Campaign","conversios"); ?></a>
197
  </div>
198
  </div>
199
  </div>
@@ -201,29 +193,29 @@ class CampaignsConfiguration
201
  <div class="account-performance">
202
  <div class="row">
203
  <div class="col-md-12">
204
- <h3 class="title"><?php _e("Account Performance","conversios"); ?></h3>
205
  </div>
206
  <div class="col-md-6 col-lg-3">
207
  <div class="chart">
208
- <h4 class="sub-title"><?php _e("Daily Clicks","conversios"); ?></h4>
209
  <canvas id="dailyClick" width="240" height="180"></canvas>
210
  </div>
211
  </div>
212
  <div class="col-md-6 col-lg-3">
213
  <div class="chart">
214
- <h4 class="sub-title"><?php _e("Daily Cost","conversios"); ?></h4>
215
  <canvas id="dailyCost" width="240" height="180"></canvas>
216
  </div>
217
  </div>
218
  <div class="col-md-6 col-lg-3">
219
  <div class="chart">
220
- <h4 class="sub-title"><?php _e("Daily Conversions","conversios"); ?></h4>
221
  <canvas id="dailyConversions" width="240" height="180"></canvas>
222
  </div>
223
  </div>
224
  <div class="col-md-6 col-lg-3">
225
  <div class="chart">
226
- <h4 class="sub-title"><?php _e("Daily Sales","conversios"); ?></h4>
227
  <canvas id="dailySales" width="240" height="180"></canvas>
228
  </div>
229
  </div>
@@ -233,42 +225,42 @@ class CampaignsConfiguration
233
  <div class="account-performance">
234
  <div class="row">
235
  <div class="col-lg-8">
236
- <h3 class="title"><?php _e("Campaign Performance","conversios"); ?></h3>
237
  <div class="table-section">
238
  <table id="campaingPerformance" class="table dt-responsive nowrap" style="width:100%">
239
  <thead>
240
  <tr>
241
- <th>Campaign</th>
242
- <th width="100"><?php _e("Daily Budget","conversios"); ?></th>
243
- <th class="text-center"><?php _e("Active","conversios"); ?></th>
244
- <th class="text-center"><?php _e("Clicks","conversios"); ?></th>
245
- <th class="text-center"><?php _e("Cost","conversios"); ?></th>
246
- <th class="text-center"><?php _e("Conversions","conversios"); ?></th>
247
- <th class="text-center"><?php _e("Sales","conversios"); ?></th>
248
- <th class="text-center"><?php _e("Action","conversios"); ?></th>
249
  </tr>
250
  </thead>
251
  <tbody>
252
  <?php
253
  $total_campaigns = count($campaign_performance);
254
  for ($i = 0; $i < $total_campaigns; $i++) {
255
- $checked = $campaign_performance[$i]->active == 0 ? '' : 'checked';
256
- if ($campaign_performance[$i]->active != 2) {?>
257
  <tr>
258
- <td><a href="<?php echo $this->site_url.'shopping_campaigns_page&id='.$campaign_performance[$i]->compaignId; ?>" class="text-underline"><?php echo esc_attr($campaign_performance[$i]->compaignName); ?></a></td>
259
  <td><?php echo esc_attr($this->currency_symbol.$campaign_performance[$i]->dailyBudget); ?></td>
260
  <td class="text-center">
261
- <div class="custom-control custom-switch">
262
- <input type="checkbox" class="custom-control-input" id="customSwitch<?php echo $i; ?>" <?php echo $checked; ?> onchange="updateCampaignStatus('<?php echo esc_attr($this->merchantId); ?>','<?php echo esc_attr($this->currentCustomerId); ?>','<?php echo esc_attr($campaign_performance[$i]->compaignId); ?>','<?php echo esc_attr($campaign_performance[$i]->dailyBudget); ?>','<?php echo esc_attr($campaign_performance[$i]->budgetId); ?>','<?php echo $i; ?>')">
263
- <label class="custom-control-label" for="customSwitch<?php echo $i; ?>"></label>
264
  </div>
265
  </td>
266
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->clicks); ?></td>
267
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->cost); ?></td>
268
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->conversions); ?></td>
269
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->sales); ?></td>
270
- <input type="hidden" value="<?php echo esc_attr($campaign_performance[$i]->compaignName); ?>" id="campaign_name_<?php echo $i; ?>" />
271
- <td><a href="<?php echo esc_url($this->site_url.'add_campaign_page&edit='.$campaign_performance[$i]->compaignId); ?>"><?php _e("Edit","conversios"); ?></a> | <a href="#" onclick="deleteCampaign('<?php echo $this->merchantId; ?>','<?php echo esc_attr($this->currentCustomerId); ?>','<?php echo esc_attr($campaign_performance[$i]->compaignId); ?>','<?php echo $i; ?>')"><?php _e("Delete","conversios"); ?></a></td>
272
  </tr>
273
  <?php
274
  }
@@ -283,15 +275,15 @@ class CampaignsConfiguration
283
  <div class="card">
284
  <div class="card-body">
285
  <div class="account-img">
286
- <img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/undraw_update_uxn2.svg'); ?>" alt=""/>
287
  </div>
288
  <div class="pro-content">
289
- <h3 class="userName">Hello <?php echo get_bloginfo(); ?>!</h3>
290
- <p>Explore <a target="_blank" href="<?php echo esc_url($this->TVC_Admin_Helper->get_pro_plan_site()); ?>" class="font-weight-bold"><?php _e("Pro account","conversios"); ?></a> <?php _e("with Premium features.","conversios"); ?></p>
291
  </div>
292
  </div>
293
  <div class="card-footer">
294
- <a target="_blank" href="<?php echo esc_url($this->TVC_Admin_Helper->get_pro_plan_site().'pricings'); ?>"><button class="btn btn-primary"><?php _e("Learn more","conversios"); ?></button></a>
295
  </div>
296
  </div>
297
  </div>
@@ -300,27 +292,27 @@ class CampaignsConfiguration
300
  </div>
301
  </div><!-- account-performance-->
302
  <?php
303
- if(isset($_GET['id']) && $_GET['id'] != '') { ?>
304
  <div class="account-performance">
305
  <div class="row">
306
  <div class="col-lg-6">
307
- <h3 class="title"><?php _e("Product Performance :","conversios"); ?></h3>
308
  <div class="table-section">
309
  <table id="productPerformance" class="table dt-responsive nowrap" style="width:100%">
310
  <thead>
311
  <tr>
312
  <th></th>
313
- <th><?php _e("Product","conversios"); ?></th>
314
- <th class="text-center"><?php _e("Clicks","conversios"); ?></th>
315
- <th class="text-center"><?php _e("Cost","conversios"); ?></th>
316
- <th class="text-center"><?php _e("Conversions","conversios"); ?></th>
317
- <th class="text-center"><?php _e("Sales","conversios"); ?></th>
318
  </tr>
319
  </thead>
320
  <tbody>
321
  <?php for ($i = 0; $i < count($product_performance); $i++) { ?>
322
  <tr>
323
- <td class="product-image"><img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/sneaker.jpg'); ?>" alt=""/></td>
324
  <td><?php echo esc_attr($product_performance[$i]->product); ?></td>
325
  <td class="text-center"><?php echo esc_attr($product_performance[$i]->clicks); ?></td>
326
  <td class="text-center"><?php echo esc_attr($product_performance[$i]->cost); ?></td>
@@ -337,18 +329,18 @@ class CampaignsConfiguration
337
  <div class="account-performance">
338
  <div class="row">
339
  <div class="col-lg-6">
340
- <h3 class="title"><?php _e("Product Partition Performance","conversios"); ?></h3>
341
  <div class="table-section">
342
  <table id="productPerformance" class="table dt-responsive nowrap" style="width:100%">
343
  <thead>
344
  <tr>
345
- <th><?php _e("Campaign","conversios"); ?></th>
346
- <th class="text-center"><?php _e("Product Dimension","conversios"); ?></th>
347
- <th class="text-center"><?php _e("Product Dimension Value","conversios"); ?></th>
348
- <th class="text-center"><?php _e("Clicks","conversios"); ?></th>
349
- <th class="text-center"><?php _e("Cost","conversios"); ?></th>
350
- <th class="text-center"><?php _e("Conversions","conversios"); ?></th>
351
- <th class="text-center"><?php _e("Sales","conversios"); ?></th>
352
  </tr>
353
  </thead>
354
  <tbody>
@@ -394,6 +386,9 @@ class CampaignsConfiguration
394
  values.push(clickData.clicks);
395
  })
396
  }
 
 
 
397
  var dailyClick = new Chart(ctx, {
398
  type: 'line',
399
  data: {
@@ -401,34 +396,63 @@ class CampaignsConfiguration
401
  datasets: [{
402
  label: 'Clicks',
403
  data: values,
404
- backgroundColor: [
405
- 'rgba(255, 99, 132, 0.2)',
406
- 'rgba(54, 162, 235, 0.2)',
407
- 'rgba(255, 206, 86, 0.2)',
408
- 'rgba(75, 192, 192, 0.2)',
409
- 'rgba(153, 102, 255, 0.2)',
410
- 'rgba(255, 159, 64, 0.2)'
411
- ],
412
- borderColor: [
413
- 'rgba(255, 99, 132, 1)',
414
- 'rgba(54, 162, 235, 1)',
415
- 'rgba(255, 206, 86, 1)',
416
- 'rgba(75, 192, 192, 1)',
417
- 'rgba(153, 102, 255, 1)',
418
- 'rgba(255, 159, 64, 1)'
419
- ],
420
  borderWidth: 1
421
  }]
422
  },
423
  options: {
424
- scales: {
425
- yAxes: [{
426
- ticks: {
427
- beginAtZero: true
428
- }
429
- }]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  }
431
- }
432
  });
433
 
434
  var ctx = document.getElementById('dailyCost').getContext('2d');
@@ -441,6 +465,9 @@ class CampaignsConfiguration
441
  values.push(costData.costs);
442
  })
443
  }
 
 
 
444
  var dailyClick = new Chart(ctx, {
445
  type: 'line',
446
  data: {
@@ -448,34 +475,63 @@ class CampaignsConfiguration
448
  datasets: [{
449
  label: 'Cost',
450
  data: values,
451
- backgroundColor: [
452
- 'rgba(255, 99, 132, 0.2)',
453
- 'rgba(54, 162, 235, 0.2)',
454
- 'rgba(255, 206, 86, 0.2)',
455
- 'rgba(75, 192, 192, 0.2)',
456
- 'rgba(153, 102, 255, 0.2)',
457
- 'rgba(255, 159, 64, 0.2)'
458
- ],
459
- borderColor: [
460
- 'rgba(255, 99, 132, 0.2)',
461
- 'rgba(54, 162, 235, 0.2)',
462
- 'rgba(255, 206, 86, 0.2)',
463
- 'rgba(75, 192, 192, 0.2)',
464
- 'rgba(153, 102, 255, 0.2)',
465
- 'rgba(255, 159, 64, 0.2)'
466
- ],
467
- borderWidth: 1
468
  }]
469
  },
470
  options: {
471
- scales: {
472
- yAxes: [{
473
- ticks: {
474
- beginAtZero: true
475
- }
476
- }]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  }
478
- }
479
  });
480
 
481
  var ctx = document.getElementById('dailyConversions').getContext('2d');
@@ -489,7 +545,9 @@ class CampaignsConfiguration
489
  values.push(conversionsData.conversions);
490
  })
491
  }
492
-
 
 
493
  var dailyClick = new Chart(ctx, {
494
  type: 'line',
495
  data: {
@@ -497,34 +555,63 @@ class CampaignsConfiguration
497
  datasets: [{
498
  label: 'Conversions',
499
  data: values,
500
- backgroundColor: [
501
- 'rgba(255, 99, 132, 0.2)',
502
- 'rgba(54, 162, 235, 0.2)',
503
- 'rgba(255, 206, 86, 0.2)',
504
- 'rgba(75, 192, 192, 0.2)',
505
- 'rgba(153, 102, 255, 0.2)',
506
- 'rgba(255, 159, 64, 0.2)'
507
- ],
508
- borderColor: [
509
- 'rgba(255, 99, 132, 0.2)',
510
- 'rgba(54, 162, 235, 0.2)',
511
- 'rgba(255, 206, 86, 0.2)',
512
- 'rgba(75, 192, 192, 0.2)',
513
- 'rgba(153, 102, 255, 0.2)',
514
- 'rgba(255, 159, 64, 0.2)'
515
- ],
516
  borderWidth: 1
517
  }]
518
  },
519
  options: {
520
- scales: {
521
- yAxes: [{
522
- ticks: {
523
- beginAtZero: true
524
- }
525
- }]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526
  }
527
- }
528
  });
529
 
530
  var ctx = document.getElementById('dailySales').getContext('2d');
@@ -538,41 +625,72 @@ class CampaignsConfiguration
538
  values.push(salesData.sales);
539
  })
540
  }
 
 
 
541
  var dailyClick = new Chart(ctx, {
542
  type: 'line',
543
  data: {
544
  labels: labels,
545
  datasets: [{
546
- label: 'Sales',
547
- data: values,
548
- backgroundColor: [
549
- 'rgba(255, 99, 132, 0.2)',
550
- 'rgba(54, 162, 235, 0.2)',
551
- 'rgba(255, 206, 86, 0.2)',
552
- 'rgba(75, 192, 192, 0.2)',
553
- 'rgba(153, 102, 255, 0.2)',
554
- 'rgba(255, 159, 64, 0.2)'
555
- ],
556
- borderColor: [
557
- 'rgba(255, 99, 132, 0.2)',
558
- 'rgba(54, 162, 235, 0.2)',
559
- 'rgba(255, 206, 86, 0.2)',
560
- 'rgba(75, 192, 192, 0.2)',
561
- 'rgba(153, 102, 255, 0.2)',
562
- 'rgba(255, 159, 64, 0.2)'
563
- ],
564
  borderWidth: 1
565
  }]
566
  },
567
  options: {
568
- scales: {
569
- yAxes: [{
570
- ticks: {
571
- beginAtZero: true
572
- }
573
- }]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  }
575
- }
576
  });
577
 
578
  function validateAll() {
@@ -649,7 +767,7 @@ class CampaignsConfiguration
649
  if (rsp.status == "success") {
650
  var message = campaign_name + " is deleted successfully";
651
  alert(message);
652
- window.location.href = "<?php echo $this->site_url.'shopping_campaigns_page';?>";
653
  } else {
654
  var message = rsp.message;
655
  alert(message);
@@ -682,7 +800,6 @@ class CampaignsConfiguration
682
  if(rsp.status == "success"){
683
  var message = campaign_name + " status updated successfully";
684
  alert(message);
685
- //window.location.href = "<?php echo $this->site_url.'shopping_campaigns_page'; ?>";
686
  }else{
687
  var message = rsp.message;
688
  alert(message);
11
  public function __construct(){
12
  $this->includes();
13
  $this->TVC_Admin_Helper = new TVC_Admin_Helper();
 
 
14
  $this->customApiObj = new CustomApi();
15
 
16
  $this->merchantId = $this->TVC_Admin_Helper->get_merchantId();
18
  $this->subscriptionId = $this->TVC_Admin_Helper->get_subscriptionId();;
19
  $this->new_campaign = true;
20
 
21
+ $this->date_range_type = (isset($_POST['customRadio'])) ? sanitize_text_field($_POST['customRadio']) : 1;
22
+ $this->days = (isset($_POST['days']) && sanitize_text_field($_POST['days']) != '') ? sanitize_text_field($_POST['days']) : 7;
23
+ $this->from_date = (isset($_POST['from_date']) && sanitize_text_field($_POST['from_date']) != '') ? sanitize_text_field($_POST['from_date']) : "";
24
+ $this->to_date = (isset($_POST['to_date']) && sanitize_text_field($_POST['to_date'] )!= '') ? sanitize_text_field($_POST['to_date']) : "";
25
  $this->country = $this->TVC_Admin_Helper->get_woo_country();
26
  $this->currency_symbol = $this->TVC_Admin_Helper->get_user_currency_symbol();
27
  $this->site_url = "admin.php?page=conversios-google-shopping-feed&tab=";
54
  }
55
  }
56
 
57
+ $date_range_type = ((isset($_POST['customRadio'])) ? sanitize_text_field($_POST['customRadio']) : 1);
58
+ $days = ((isset($_POST['days']) && sanitize_text_field($_POST['days']) != '') ? sanitize_text_field($_POST['days']) : 7);
59
+ $from_date = ((isset($_POST['from_date']) && sanitize_text_field($_POST['from_date']) != '') ? sanitize_text_field($_POST['from_date']) : "");
60
+ $to_date = ((isset($_POST['to_date']) && sanitize_text_field($_POST['to_date']) != '') ? sanitize_text_field($_POST['to_date']) : "");
61
  $from_date = sanitize_text_field($from_date);
62
  $to_date = sanitize_text_field($to_date);
63
  $campaigns_list = [];
72
 
73
  if (isset($campaigns_list_res->errors) && !empty($campaigns_list_res->errors)) {
74
  $class = 'error';
75
+ $message = esc_html__("Not any campaigns found.","conversios");
76
+ printf('<div class="alert-message"><div class="%1$s" role="alert">%2$s</div></div>', esc_attr($class), esc_html($message));
77
  } else if(isset($campaigns_list_res->data)){
78
  $campaigns_list_res = $campaigns_list_res->data;
79
  if ($campaigns_list_res['status'] == 200) {
126
  $campaign_performance = $campaign_performance_res['data'];
127
  }
128
  }
129
+ }else if(isset($_GET['id']) && sanitize_text_field($_GET['id']) != '') {
130
  //Product Performance
131
+ $product_performance_res = $api_old_obj->productPerformance(sanitize_text_field($_GET['id']), $this->date_range_type, $this->days, $this->from_date, $this->to_date);
132
 
133
  if (isset($product_performance_res->errors) && !empty($product_performance_res->errors)) {
134
 
140
  }
141
 
142
  //Product Partition Performance
143
+ $product_partition_performance_res = $api_old_obj->productPartitionPerformance(sanitize_text_field($_GET['id']), $this->date_range_type, $this->days, $this->from_date, $this->to_date);
144
 
145
  if (isset($product_partition_performance_res->errors) && !empty($product_partition_performance_res->errors)) {
146
 
162
  <div class="row">
163
  <div class="col-md-12">
164
  <div class="edit-section">
 
 
 
 
 
 
165
  <div class="row">
166
  <div class="configuration-section col-md-6 col-lg-8">
167
  <?php echo get_google_shopping_tabs_html($this->site_url, (isset($googleDetail->google_merchant_center_id))?$googleDetail->google_merchant_center_id:""); ?>
174
  <input type="hidden" id="customRange2" name="customRadio" value="2" class="custom-control-input" checked="checked">
175
  <div class="campaigns">
176
  <div class="d-flex justify-content-between align-items-center">
177
+ <h3 class="title mb-0"><?php esc_html_e("Smart Shopping Campaigns","conversios"); ?></h3>
178
  <div class="campaing-date">
179
  <div class="input-group input-daterange">
180
+ <input type="text" class="form-control" id="from_date" name="from_date" value="<?php echo esc_attr($this->from_date); ?>">
181
  <div class="input-group-addon text px-3">to</div>
182
+ <input type="text" class="form-control" id="to_date" name="to_date" value="<?php echo esc_attr($this->to_date); ?>">
183
+ <label class="mt-2 mb-2 error-msg float-left hidden" id="errorMessage"><?php esc_html_e("Please select both from and to date","conversios"); ?></label>
184
  </div>
185
  </div>
186
  <div class="create-campaign-btn">
187
+ <button type="button" class="btn btn-outline-primary" onclick="validateAll()" id="select_range" name="select_range"><?php esc_html_e("Submit","conversios"); ?></button>
188
+ <a href="<?php echo esc_url_raw($this->site_url.'add_campaign_page');?>" class="btn btn-outline-primary"><?php esc_html_e("Create a New Campaign","conversios"); ?></a>
189
  </div>
190
  </div>
191
  </div>
193
  <div class="account-performance">
194
  <div class="row">
195
  <div class="col-md-12">
196
+ <h3 class="title"><?php esc_html_e("Account Performance","conversios"); ?></h3>
197
  </div>
198
  <div class="col-md-6 col-lg-3">
199
  <div class="chart">
200
+ <h4 class="sub-title"><?php esc_html_e("Daily Clicks","conversios"); ?></h4>
201
  <canvas id="dailyClick" width="240" height="180"></canvas>
202
  </div>
203
  </div>
204
  <div class="col-md-6 col-lg-3">
205
  <div class="chart">
206
+ <h4 class="sub-title"><?php esc_html_e("Daily Cost","conversios"); ?></h4>
207
  <canvas id="dailyCost" width="240" height="180"></canvas>
208
  </div>
209
  </div>
210
  <div class="col-md-6 col-lg-3">
211
  <div class="chart">
212
+ <h4 class="sub-title"><?php esc_html_e("Daily Conversions","conversios"); ?></h4>
213
  <canvas id="dailyConversions" width="240" height="180"></canvas>
214
  </div>
215
  </div>
216
  <div class="col-md-6 col-lg-3">
217
  <div class="chart">
218
+ <h4 class="sub-title"><?php esc_html_e("Daily Sales","conversios"); ?></h4>
219
  <canvas id="dailySales" width="240" height="180"></canvas>
220
  </div>
221
  </div>
225
  <div class="account-performance">
226
  <div class="row">
227
  <div class="col-lg-8">
228
+ <h3 class="title"><?php esc_html_e("Campaign Performance","conversios"); ?></h3>
229
  <div class="table-section">
230
  <table id="campaingPerformance" class="table dt-responsive nowrap" style="width:100%">
231
  <thead>
232
  <tr>
233
+ <th><?php esc_html_e("Campaign","conversios"); ?></th>
234
+ <th width="100"><?php esc_html_e("Daily Budget","conversios"); ?></th>
235
+ <th class="text-center"><?php esc_html_e("Active","conversios"); ?></th>
236
+ <th class="text-center"><?php esc_html_e("Clicks","conversios"); ?></th>
237
+ <th class="text-center"><?php esc_html_e("Cost","conversios"); ?></th>
238
+ <th class="text-center"><?php esc_html_e("Conversions","conversios"); ?></th>
239
+ <th class="text-center"><?php esc_html_e("Sales","conversios"); ?></th>
240
+ <th class="text-center"><?php esc_html_e("Action","conversios"); ?></th>
241
  </tr>
242
  </thead>
243
  <tbody>
244
  <?php
245
  $total_campaigns = count($campaign_performance);
246
  for ($i = 0; $i < $total_campaigns; $i++) {
247
+ $checked = esc_attr($campaign_performance[$i]->active) == 0 ? '' : 'checked';
248
+ if (esc_attr($campaign_performance[$i]->active) != 2) {?>
249
  <tr>
250
+ <td><a href="<?php echo esc_url_raw($this->site_url.'shopping_campaigns_page&id='.$campaign_performance[$i]->compaignId); ?>" class="text-underline"><?php echo esc_attr($campaign_performance[$i]->compaignName); ?></a></td>
251
  <td><?php echo esc_attr($this->currency_symbol.$campaign_performance[$i]->dailyBudget); ?></td>
252
  <td class="text-center">
253
+ <div class="form-check form-switch">
254
+ <input type="checkbox" class="form-check-input" id="customSwitch<?php echo esc_attr($i); ?>" <?php echo esc_attr($checked); ?> onchange="updateCampaignStatus('<?php echo esc_attr($this->merchantId); ?>','<?php echo esc_attr($this->currentCustomerId); ?>','<?php echo esc_attr($campaign_performance[$i]->compaignId); ?>','<?php echo esc_attr($campaign_performance[$i]->dailyBudget); ?>','<?php echo esc_attr($campaign_performance[$i]->budgetId); ?>','<?php echo esc_attr($i); ?>')">
255
+ <label class="form-check-label" for="customSwitch<?php echo esc_attr($i); ?>"></label>
256
  </div>
257
  </td>
258
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->clicks); ?></td>
259
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->cost); ?></td>
260
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->conversions); ?></td>
261
  <td class="text-center"><?php echo esc_attr($campaign_performance[$i]->sales); ?></td>
262
+ <input type="hidden" value="<?php echo esc_attr($campaign_performance[$i]->compaignName); ?>" id="campaign_name_<?php echo esc_attr($i); ?>" />
263
+ <td><a href="<?php echo esc_url_raw($this->site_url.'add_campaign_page&edit='.$campaign_performance[$i]->compaignId); ?>"><?php esc_html_e("Edit","conversios"); ?></a> | <a href="#" onclick="deleteCampaign('<?php echo $this->merchantId; ?>','<?php echo esc_attr($this->currentCustomerId); ?>','<?php echo esc_attr($campaign_performance[$i]->compaignId); ?>','<?php echo $i; ?>')"><?php esc_html_e("Delete","conversios"); ?></a></td>
264
  </tr>
265
  <?php
266
  }
275
  <div class="card">
276
  <div class="card-body">
277
  <div class="account-img">
278
+ <img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/undraw_update_uxn2.svg'); ?>" alt=""/>
279
  </div>
280
  <div class="pro-content">
281
+ <h3 class="userName"><?php esc_html_e("Hello","conversios"); ?> <?php echo esc_attr(get_bloginfo()); ?>!</h3>
282
+ <p>Explore <a target="_blank" href="<?php echo esc_url_raw($this->TVC_Admin_Helper->get_pro_plan_site()); ?>" class="font-weight-bold"><?php esc_html_e("Pro account","conversios"); ?></a> <?php esc_html_e("with Premium features.","conversios"); ?></p>
283
  </div>
284
  </div>
285
  <div class="card-footer">
286
+ <a target="_blank" href="<?php echo esc_url_raw($this->TVC_Admin_Helper->get_pro_plan_site().'pricings'); ?>"><button class="btn btn-primary"><?php esc_html_e("Learn more","conversios"); ?></button></a>
287
  </div>
288
  </div>
289
  </div>
292
  </div>
293
  </div><!-- account-performance-->
294
  <?php
295
+ if(isset($_GET['id']) && sanitize_text_field($_GET['id']) != '') { ?>
296
  <div class="account-performance">
297
  <div class="row">
298
  <div class="col-lg-6">
299
+ <h3 class="title"><?php esc_html_e("Product Performance :","conversios"); ?></h3>
300
  <div class="table-section">
301
  <table id="productPerformance" class="table dt-responsive nowrap" style="width:100%">
302
  <thead>
303
  <tr>
304
  <th></th>
305
+ <th><?php esc_html_e("Product","conversios"); ?></th>
306
+ <th class="text-center"><?php esc_html_e("Clicks","conversios"); ?></th>
307
+ <th class="text-center"><?php esc_html_e("Cost","conversios"); ?></th>
308
+ <th class="text-center"><?php esc_html_e("Conversions","conversios"); ?></th>
309
+ <th class="text-center"><?php esc_html_e("Sales","conversios"); ?></th>
310
  </tr>
311
  </thead>
312
  <tbody>
313
  <?php for ($i = 0; $i < count($product_performance); $i++) { ?>
314
  <tr>
315
+ <td class="product-image"><img src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/icon/sneaker.jpg'); ?>" alt=""/></td>
316
  <td><?php echo esc_attr($product_performance[$i]->product); ?></td>
317
  <td class="text-center"><?php echo esc_attr($product_performance[$i]->clicks); ?></td>
318
  <td class="text-center"><?php echo esc_attr($product_performance[$i]->cost); ?></td>
329
  <div class="account-performance">
330
  <div class="row">
331
  <div class="col-lg-6">
332
+ <h3 class="title"><?php esc_html_e("Product Partition Performance","conversios"); ?></h3>
333
  <div class="table-section">
334
  <table id="productPerformance" class="table dt-responsive nowrap" style="width:100%">
335
  <thead>
336
  <tr>
337
+ <th><?php esc_html_e("Campaign","conversios"); ?></th>
338
+ <th class="text-center"><?php esc_html_e("Product Dimension","conversios"); ?></th>
339
+ <th class="text-center"><?php esc_html_e("Product Dimension Value","conversios"); ?></th>
340
+ <th class="text-center"><?php esc_html_e("Clicks","conversios"); ?></th>
341
+ <th class="text-center"><?php esc_html_e("Cost","conversios"); ?></th>
342
+ <th class="text-center"><?php esc_html_e("Conversions","conversios"); ?></th>
343
+ <th class="text-center"><?php esc_html_e("Sales","conversios"); ?></th>
344
  </tr>
345
  </thead>
346
  <tbody>
386
  values.push(clickData.clicks);
387
  })
388
  }
389
+ var gradientFill = ctx.createLinearGradient(0, 0, 0, 500);
390
+ gradientFill.addColorStop(0.4, 'rgba(107, 232, 255, 0.9)');
391
+ gradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.75)');
392
  var dailyClick = new Chart(ctx, {
393
  type: 'line',
394
  data: {
396
  datasets: [{
397
  label: 'Clicks',
398
  data: values,
399
+ borderColor: '#002BFC',
400
+ pointBorderColor: '#002BFC',
401
+ pointBackgroundColor: '#fff',
402
+ pointBorderWidth: 1,
403
+ pointRadius: 2,
404
+ fill: true,
405
+ backgroundColor: gradientFill,
 
 
 
 
 
 
 
 
 
406
  borderWidth: 1
407
  }]
408
  },
409
  options: {
410
+ animation: {
411
+ easing: "easeInOutBack"
412
+ },
413
+ plugins:{
414
+ legend:false
415
+ },
416
+ responsive: true,
417
+ scales: {
418
+ y:{
419
+ fontColor: "#ffffff",
420
+ fontStyle: "normal",
421
+ beginAtZero: true,
422
+ maxTicksLimit: 5,
423
+ padding: 30,
424
+ grid:{
425
+ borderWidth:0,
426
+ },
427
+ ticks: {
428
+ stepSize: 1000,
429
+ callback: function(value) {
430
+ var ranges = [
431
+ { divider: 1e6, suffix: 'M' },
432
+ { divider: 1e3, suffix: 'k' }
433
+ ];
434
+ function formatNumber(n) {
435
+ for (var i = 0; i < ranges.length; i++) {
436
+ if (n >= ranges[i].divider) {
437
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
438
+ }
439
+ }
440
+ return n;
441
+ }
442
+ return '' + formatNumber(value);
443
+ }
444
+ }
445
+ },
446
+ x:{
447
+ padding: 10,
448
+ fontColor: "#ffffff",
449
+ fontStyle: "normal",
450
+ grid: {
451
+ display:false
452
+ }
453
+ }
454
+ }
455
  }
 
456
  });
457
 
458
  var ctx = document.getElementById('dailyCost').getContext('2d');
465
  values.push(costData.costs);
466
  })
467
  }
468
+ var gradientFill = ctx.createLinearGradient(0, 0, 0, 500);
469
+ gradientFill.addColorStop(0.4, 'rgba(255, 229, 139, 0.9)');
470
+ gradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.7)');
471
  var dailyClick = new Chart(ctx, {
472
  type: 'line',
473
  data: {
475
  datasets: [{
476
  label: 'Cost',
477
  data: values,
478
+ borderColor: '#002BFC',
479
+ pointBorderColor: '#002BFC',
480
+ pointBackgroundColor: '#fff',
481
+ pointBorderWidth: 1,
482
+ pointRadius: 2,
483
+ fill: true,
484
+ backgroundColor: gradientFill,
485
+ borderWidth: 1
 
 
 
 
 
 
 
 
 
486
  }]
487
  },
488
  options: {
489
+ animation: {
490
+ easing: "easeInOutBack"
491
+ },
492
+ plugins:{
493
+ legend:false
494
+ },
495
+ responsive: true,
496
+ scales: {
497
+ y:{
498
+ fontColor: "#ffffff",
499
+ fontStyle: "normal",
500
+ beginAtZero: true,
501
+ maxTicksLimit: 5,
502
+ padding: 30,
503
+ grid:{
504
+ borderWidth:0,
505
+ },
506
+ ticks: {
507
+ stepSize: 1000,
508
+ callback: function(value) {
509
+ var ranges = [
510
+ { divider: 1e6, suffix: 'M' },
511
+ { divider: 1e3, suffix: 'k' }
512
+ ];
513
+ function formatNumber(n) {
514
+ for (var i = 0; i < ranges.length; i++) {
515
+ if (n >= ranges[i].divider) {
516
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
517
+ }
518
+ }
519
+ return n;
520
+ }
521
+ return '' + formatNumber(value);
522
+ }
523
+ }
524
+ },
525
+ x:{
526
+ padding: 10,
527
+ fontColor: "#ffffff",
528
+ fontStyle: "normal",
529
+ grid: {
530
+ display:false
531
+ }
532
+ }
533
+ }
534
  }
 
535
  });
536
 
537
  var ctx = document.getElementById('dailyConversions').getContext('2d');
545
  values.push(conversionsData.conversions);
546
  })
547
  }
548
+ var gradientFill = ctx.createLinearGradient(0, 0, 0, 500);
549
+ gradientFill.addColorStop(0.4, 'rgba(110, 245, 197, 0.9)');
550
+ gradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.7)');
551
  var dailyClick = new Chart(ctx, {
552
  type: 'line',
553
  data: {
555
  datasets: [{
556
  label: 'Conversions',
557
  data: values,
558
+ borderColor: '#002BFC',
559
+ pointBorderColor: '#002BFC',
560
+ pointBackgroundColor: '#fff',
561
+ pointBorderWidth: 1,
562
+ pointRadius: 2,
563
+ fill: true,
564
+ backgroundColor: gradientFill,
 
 
 
 
 
 
 
 
 
565
  borderWidth: 1
566
  }]
567
  },
568
  options: {
569
+ animation: {
570
+ easing: "easeInOutBack"
571
+ },
572
+ plugins:{
573
+ legend:false
574
+ },
575
+ responsive: true,
576
+ scales: {
577
+ y:{
578
+ fontColor: "#ffffff",
579
+ fontStyle: "normal",
580
+ beginAtZero: true,
581
+ maxTicksLimit: 5,
582
+ padding: 30,
583
+ grid:{
584
+ borderWidth:0,
585
+ },
586
+ ticks: {
587
+ stepSize: 1000,
588
+ callback: function(value) {
589
+ var ranges = [
590
+ { divider: 1e6, suffix: 'M' },
591
+ { divider: 1e3, suffix: 'k' }
592
+ ];
593
+ function formatNumber(n) {
594
+ for (var i = 0; i < ranges.length; i++) {
595
+ if (n >= ranges[i].divider) {
596
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
597
+ }
598
+ }
599
+ return n;
600
+ }
601
+ return '' + formatNumber(value);
602
+ }
603
+ }
604
+ },
605
+ x:{
606
+ padding: 10,
607
+ fontColor: "#ffffff",
608
+ fontStyle: "normal",
609
+ grid: {
610
+ display:false
611
+ }
612
+ }
613
+ }
614
  }
 
615
  });
616
 
617
  var ctx = document.getElementById('dailySales').getContext('2d');
625
  values.push(salesData.sales);
626
  })
627
  }
628
+ var gradientFill = ctx.createLinearGradient(0, 0, 0, 500);
629
+ gradientFill.addColorStop(0.4, 'rgba(153, 170, 255, 0.9)');
630
+ gradientFill.addColorStop(0.85, 'rgba(255, 255, 255, 0.7)');
631
  var dailyClick = new Chart(ctx, {
632
  type: 'line',
633
  data: {
634
  labels: labels,
635
  datasets: [{
636
+ data: values,
637
+ borderColor: '#002BFC',
638
+ pointBorderColor: '#002BFC',
639
+ pointBackgroundColor: '#fff',
640
+ pointBorderWidth: 1,
641
+ pointRadius: 2,
642
+ fill: true,
643
+ backgroundColor: gradientFill,
 
 
 
 
 
 
 
 
 
 
644
  borderWidth: 1
645
  }]
646
  },
647
  options: {
648
+ animation: {
649
+ easing: "easeInOutBack"
650
+ },
651
+ plugins:{
652
+ legend:false
653
+ },
654
+ responsive: true,
655
+ scales: {
656
+ y:{
657
+ fontColor: "#ffffff",
658
+ fontStyle: "normal",
659
+ beginAtZero: true,
660
+ maxTicksLimit: 5,
661
+ padding: 30,
662
+ grid:{
663
+ borderWidth:0,
664
+ },
665
+ ticks: {
666
+ stepSize: 1000,
667
+ callback: function(value) {
668
+ var ranges = [
669
+ { divider: 1e6, suffix: 'M' },
670
+ { divider: 1e3, suffix: 'k' }
671
+ ];
672
+ function formatNumber(n) {
673
+ for (var i = 0; i < ranges.length; i++) {
674
+ if (n >= ranges[i].divider) {
675
+ return (n / ranges[i].divider).toString() + ranges[i].suffix;
676
+ }
677
+ }
678
+ return n;
679
+ }
680
+ return '' + formatNumber(value);
681
+ }
682
+ }
683
+ },
684
+ x:{
685
+ padding: 10,
686
+ fontColor: "#ffffff",
687
+ fontStyle: "normal",
688
+ grid: {
689
+ display:false
690
+ }
691
+ }
692
+ }
693
  }
 
694
  });
695
 
696
  function validateAll() {
767
  if (rsp.status == "success") {
768
  var message = campaign_name + " is deleted successfully";
769
  alert(message);
770
+ window.location.href = "<?php echo esc_url_raw($this->site_url.'shopping_campaigns_page');?>";
771
  } else {
772
  var message = rsp.message;
773
  alert(message);
800
  if(rsp.status == "success"){
801
  var message = campaign_name + " status updated successfully";
802
  alert(message);
 
803
  }else{
804
  var message = rsp.message;
805
  alert(message);
includes/setup/google-shopping-feed-sync-product.php CHANGED
@@ -27,7 +27,7 @@ public function html_run(){
27
  }
28
 
29
  public function create_form(){
30
- if(isset($_GET['welcome_msg']) && $_GET['welcome_msg'] == true){
31
  $this->TVC_Admin_Helper->call_domain_claim();
32
  $class = 'notice notice-success';
33
  $message = esc_html__("Everthing is now set up. One more step - Sync your WooCommerce products into your Merchant Center and reach out to millions of shopper across Google.","conversios");
@@ -35,8 +35,8 @@ public function create_form(){
35
  ?>
36
  <script>
37
  $(document).ready(function() {
38
- var msg="<?php echo $message;?>"
39
- tvc_helper.tvc_alert("success", "<?php _e("Congratulation..!","conversios"); ?>", msg, true);
40
  });
41
  </script>
42
  <?php
@@ -77,32 +77,26 @@ public function create_form(){
77
  <div class="tab-card">
78
  <div class="row">
79
  <div class="col-md-6 col-lg-8 edit-section">
80
- <div class="edit-header-section">
81
- <script>
82
- var back_img = '<img src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/icon/left-angle-arrow.svg'); ?>" alt="back"/>';
83
- document.write('<a href="' + document.referrer + '" class="back-btn">'+back_img+'<span>Back</span></a>');
84
- </script>
85
- </div>
86
  <div class="configuration-section" id="config-pt1">
87
  <?php if($this->subscriptionId != ""){?>
88
  <div class="tvc-api-sunc">
89
  <span>
90
  <?php if($last_api_sync_up){
91
- echo __("Details last synced at","conversios").$last_api_sync_up;
92
  }else{
93
- echo "Refresh sync up";
94
- }?></span><img id="refresh_api" onclick="call_tvc_api_sync_up();" src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/refresh.png'); ?>">
95
  </div>
96
  <?php } ?>
97
- <?php echo get_google_shopping_tabs_html($this->site_url,(isset($googleDetail->google_merchant_center_id))?$googleDetail->google_merchant_center_id:""); ?>
98
  </div>
99
  <div class="mt-3" id="config-pt2">
100
  <div class="sync-new-product" id="sync-product">
101
  <div class="row">
102
  <div class="col-12">
103
  <div class="d-flex justify-content-between ">
104
- <p class="mb-0 align-self-center product-title"><?php _e("Products in your Merchant Center account","conversios"); ?></p>
105
- <button id="tvc_btn_product_sync" class="btn btn-outline-primary align-self-center" data-toggle="modal" data-target="#syncProduct"><?php _e("Sync New Products","conversios"); ?></button>
106
  </div>
107
  </div>
108
  </div>
@@ -116,35 +110,35 @@ public function create_form(){
116
  <div class="col">
117
  <div class="card">
118
  <h3 class="pro-count"><?php
119
- echo esc_attr(($woo_product) ? $woo_product : "0"); ?></h3>
120
- <p class="pro-title"><?php _e("Total Products","conversios"); ?></p>
121
  </div>
122
  </div>
123
  <div class="col">
124
  <div class="card">
125
  <h3 class="pro-count"><?php
126
- echo $sync_product_total ; ?></h3>
127
- <p class="pro-title"><?php _e("Sync Products","conversios"); ?></p>
128
  </div>
129
  </div>
130
  <div class="col">
131
  <div class="card pending">
132
  <h3 class="pro-count">
133
- <?php echo $sync_product_pending;?></h3>
134
- <p class="pro-title"><?php _e("Pending Review","conversios"); ?></p>
135
  </div>
136
  </div>
137
  <div class="col">
138
  <div class="card approved">
139
- <h3 class="pro-count"><?php echo $sync_product_approved;?></h3>
140
- <p class="pro-title"><?php _e("Approved","conversios"); ?></p>
141
  </div>
142
  </div>
143
  <div class="col">
144
  <div class="card disapproved">
145
  <h3 class="pro-count"><?php
146
- echo $sync_product_disapproved; ?></h3>
147
- <p class="pro-title"><?php _e("Disapproved","conversios"); ?></p>
148
  </div>
149
  </div>
150
  </div>
@@ -157,37 +151,40 @@ public function create_form(){
157
  <thead>
158
  <tr>
159
  <th></th>
160
- <th style="vertical-align: top;"><?php _e("Product","conversios"); ?></th>
161
- <th style="vertical-align: top;"><?php _e("Google status","conversios"); ?></th>
162
- <th style="vertical-align: top;"><?php _e("Issues","conversios"); ?></th>
163
  </tr>
164
  </thead>
165
  <tbody>
166
  <?php
167
  if (isset($syncProductList) && count($syncProductList) > 0) {
168
- foreach ($syncProductList as $skey => $sValue) {
169
- echo '<tr><td class="product-image">
170
- <img src="'.esc_url($sValue->image_link).'" alt=""/></td>
171
- <td>'.esc_attr($sValue->name).'</td>
172
- <td>'.esc_attr($sValue->google_status).'</td>
173
- <td>';
 
174
  $p_issues = json_decode($sValue->issues);
175
  if (count($p_issues) > 0) {
176
  $str = '';
177
  foreach ($p_issues as $key => $issue) {
178
  if ($key <= 2) {
179
- ($key <= 1) ? $str .= $issue.", <br>" : "";
180
  }
181
- ($key == 3) ? $str .= "..." : "";
182
  }
183
  echo sanitize_text_field($str);
184
  } else {
185
- echo "---";
186
- }
187
- echo '</td></tr>';
 
188
  }
189
- }else{
190
- echo '<tr><td colspan="4">'.__("Record not found","conversios").'</td></tr>';
 
191
  } ?>
192
  </tbody>
193
  </table>
@@ -210,7 +207,7 @@ public function create_form(){
210
  // add product sync popup
211
  echo $this->TVCProductSyncHelper->tvc_product_sync_popup_html();
212
  $is_need_to_domain_claim = false;
213
- if(isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id && $this->subscriptionId != "" && isset($googleDetail->is_domain_claim) && $googleDetail->is_domain_claim == '0'){
214
  $is_need_to_domain_claim = true;
215
  }?>
216
  <script type="text/javascript">
@@ -222,22 +219,10 @@ $(document).ready(function() {
222
  "lengthMenu": [ 10, 20, 50, 100, 200 ]
223
  });
224
  //auto syncup call
225
- var is_need_to_update = "<?php echo $is_need_to_update; ?>";
226
  if(is_need_to_update == 1 || is_need_to_update == true){
227
  call_tvc_api_sync_up();
228
- }
229
- //custom call for domain clain while product sync call
230
- $(document).on("click", "#tvc_btn_product_sync", function(event){
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
- });
239
- }
240
- });
241
  });
242
  //Update syncup detail by ajax call
243
  function call_tvc_api_sync_up(){
@@ -245,7 +230,7 @@ function call_tvc_api_sync_up(){
245
  $("#tvc_msg").remove();
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","<?php _e("Attention !","conversios"); ?>", "<?php _e("Sync up is in the process do not refresh the page. it may take few minutes, if GMC product sync count is large.","conversios"); ?>");
249
  jQuery.post(tvc_ajax_url,{
250
  action: "tvc_call_api_sync"
251
  },function( response ){
27
  }
28
 
29
  public function create_form(){
30
+ if(isset($_GET['welcome_msg']) && sanitize_text_field($_GET['welcome_msg']) == true){
31
  $this->TVC_Admin_Helper->call_domain_claim();
32
  $class = 'notice notice-success';
33
  $message = esc_html__("Everthing is now set up. One more step - Sync your WooCommerce products into your Merchant Center and reach out to millions of shopper across Google.","conversios");
35
  ?>
36
  <script>
37
  $(document).ready(function() {
38
+ var msg="<?php echo esc_attr($message);?>"
39
+ tvc_helper.tvc_alert("success", "<?php esc_html_e("Congratulation..!","conversios"); ?>", msg, true);
40
  });
41
  </script>
42
  <?php
77
  <div class="tab-card">
78
  <div class="row">
79
  <div class="col-md-6 col-lg-8 edit-section">
 
 
 
 
 
 
80
  <div class="configuration-section" id="config-pt1">
81
  <?php if($this->subscriptionId != ""){?>
82
  <div class="tvc-api-sunc">
83
  <span>
84
  <?php if($last_api_sync_up){
85
+ echo esc_html__("Details last synced at","conversios").$last_api_sync_up;
86
  }else{
87
+ echo esc_html__("Refresh sync up","conversios");
88
+ }?></span><img id="refresh_api" onclick="call_tvc_api_sync_up();" src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/refresh.png'); ?>">
89
  </div>
90
  <?php } ?>
91
+ <?php echo get_google_shopping_tabs_html(esc_attr($this->site_url),(isset($googleDetail->google_merchant_center_id))?esc_attr($googleDetail->google_merchant_center_id):""); ?>
92
  </div>
93
  <div class="mt-3" id="config-pt2">
94
  <div class="sync-new-product" id="sync-product">
95
  <div class="row">
96
  <div class="col-12">
97
  <div class="d-flex justify-content-between ">
98
+ <p class="mb-0 align-self-center product-title"><?php esc_html_e("Products in your Merchant Center account","conversios"); ?></p>
99
+ <button id="tvc_btn_product_sync" class="btn btn-outline-primary align-self-center" data-bs-toggle="modal" data-bs-target="#syncProduct"><?php esc_html_e("Sync New Products","conversios"); ?></button>
100
  </div>
101
  </div>
102
  </div>
110
  <div class="col">
111
  <div class="card">
112
  <h3 class="pro-count"><?php
113
+ echo esc_attr(($woo_product) ? esc_attr($woo_product) : "0"); ?></h3>
114
+ <p class="pro-title"><?php esc_html_e("Total Products","conversios"); ?></p>
115
  </div>
116
  </div>
117
  <div class="col">
118
  <div class="card">
119
  <h3 class="pro-count"><?php
120
+ echo esc_attr($sync_product_total) ; ?></h3>
121
+ <p class="pro-title"><?php esc_html_e("Sync Products","conversios"); ?></p>
122
  </div>
123
  </div>
124
  <div class="col">
125
  <div class="card pending">
126
  <h3 class="pro-count">
127
+ <?php echo esc_attr($sync_product_pending);?></h3>
128
+ <p class="pro-title"><?php esc_html_e("Pending Review","conversios"); ?></p>
129
  </div>
130
  </div>
131
  <div class="col">
132
  <div class="card approved">
133
+ <h3 class="pro-count"><?php echo esc_attr($sync_product_approved);?></h3>
134
+ <p class="pro-title"><?php esc_html_e("Approved","conversios"); ?></p>
135
  </div>
136
  </div>
137
  <div class="col">
138
  <div class="card disapproved">
139
  <h3 class="pro-count"><?php
140
+ echo esc_attr($sync_product_disapproved); ?></h3>
141
+ <p class="pro-title"><?php esc_html_e("Disapproved","conversios"); ?></p>
142
  </div>
143
  </div>
144
  </div>
151
  <thead>
152
  <tr>
153
  <th></th>
154
+ <th style="vertical-align: top;"><?php esc_html_e("Product","conversios"); ?></th>
155
+ <th style="vertical-align: top;"><?php esc_html_e("Google status","conversios"); ?></th>
156
+ <th style="vertical-align: top;"><?php esc_html_e("Issues","conversios"); ?></th>
157
  </tr>
158
  </thead>
159
  <tbody>
160
  <?php
161
  if (isset($syncProductList) && count($syncProductList) > 0) {
162
+ foreach ($syncProductList as $skey => $sValue) { ?>
163
+ <tr><td class="product-image">
164
+ <img src="<?php echo esc_url_raw($sValue->image_link); ?>" alt=""/></td>
165
+ <td><?php echo esc_attr($sValue->name); ?></td>
166
+ <td><?php echo esc_attr($sValue->google_status); ?></td>
167
+ <td>
168
+ <?php
169
  $p_issues = json_decode($sValue->issues);
170
  if (count($p_issues) > 0) {
171
  $str = '';
172
  foreach ($p_issues as $key => $issue) {
173
  if ($key <= 2) {
174
+ ($key <= 1) ? $str .= esc_attr($issue).", <br>" : "";
175
  }
176
+ ($key == 3) ? $str .= esc_attr("...") : "";
177
  }
178
  echo sanitize_text_field($str);
179
  } else {
180
+ echo esc_attr("---");
181
+ }?>
182
+ </td></tr>
183
+ <?php
184
  }
185
+ }else{ ?>
186
+ <tr><td colspan="4"><?php echo esc_html__("Record not found","conversios"); ?></td></tr>
187
+ <?php
188
  } ?>
189
  </tbody>
190
  </table>
207
  // add product sync popup
208
  echo $this->TVCProductSyncHelper->tvc_product_sync_popup_html();
209
  $is_need_to_domain_claim = false;
210
+ if(isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) && esc_attr($this->subscriptionId) != "" && isset($googleDetail->is_domain_claim) && esc_attr($googleDetail->is_domain_claim) == '0'){
211
  $is_need_to_domain_claim = true;
212
  }?>
213
  <script type="text/javascript">
219
  "lengthMenu": [ 10, 20, 50, 100, 200 ]
220
  });
221
  //auto syncup call
222
+ var is_need_to_update = "<?php echo esc_attr($is_need_to_update); ?>";
223
  if(is_need_to_update == 1 || is_need_to_update == true){
224
  call_tvc_api_sync_up();
225
+ }
 
 
 
 
 
 
 
 
 
 
 
 
226
  });
227
  //Update syncup detail by ajax call
228
  function call_tvc_api_sync_up(){
230
  $("#tvc_msg").remove();
231
  $("#refresh_api").css("visibility","hidden");
232
  $(tvs_this).after('<div class="tvc-nb-spinner" id="tvc-nb-spinner"></div>');
233
+ tvc_helper.tvc_alert("error","<?php esc_html_e("Attention !","conversios"); ?>", "<?php esc_html_e("Sync up is in the process do not refresh the page. it may take few minutes, if GMC product sync count is large.","conversios"); ?>");
234
  jQuery.post(tvc_ajax_url,{
235
  action: "tvc_call_api_sync"
236
  },function( response ){
includes/setup/google-shopping-feed.php CHANGED
@@ -14,7 +14,7 @@ class GoogleShoppingFeed {
14
  return '<li>
15
  <div class="row">
16
  <div class="col-7 col-md-8 align-self-center pr-0">
17
- <span class="text">'.$title.'</span>
18
  </div>
19
  <div class="col-5 col-md-4 align-self-center text-right">
20
  <span class="text"><strong>'. esc_attr($val).'</strong></span>
@@ -23,14 +23,14 @@ class GoogleShoppingFeed {
23
  </li>';
24
  }
25
  public function configuration_list_html($title, $val){
26
- $imge = (isset($val) && $val != "" && $val != 0) ? '<img src="' . ENHANCAD_PLUGIN_URL.'/admin/images/config-success.svg" alt="config-success"/>' : '<img src="' . ENHANCAD_PLUGIN_URL.'/admin/images/exclaimation.png" alt="no-config-success"/>';
27
  return '<li>
28
  <div class="row">
29
  <div class="col-7 col-md-7 col-lg-9 align-self-center pr-0">
30
- <span class="text">'.$title.'</span>
31
  </div>
32
- <div class="col-5 col-md-5 col-lg-3 align-self-center text-right">
33
- <div class="list-image">'.$imge.'</div>
34
  </div>
35
  </div>
36
  </li>';
@@ -40,10 +40,10 @@ class GoogleShoppingFeed {
40
  return '<li>
41
  <div class="row">
42
  <div class="col-7 col-md-7 col-lg-7 align-self-center pr-0">
43
- <span class="text">'.$title.'</span>
44
  </div>
45
- <div class="col-5 col-md-5 col-lg-5 align-self-center text-right">
46
- <div class="list-image"><img id="refresh_'.$call_domain_claim.'" onclick="'.$call_domain_claim.'();" src="'. ENHANCAD_PLUGIN_URL.'/admin/images/refresh.png"><img src="' . ENHANCAD_PLUGIN_URL.'/admin/images/exclaimation.png" alt="no-config-success"/></div>
47
  </div>
48
  </div>
49
  </li>';
@@ -51,10 +51,10 @@ class GoogleShoppingFeed {
51
  return '<li>
52
  <div class="row">
53
  <div class="col-7 col-md-7 col-lg-7 align-self-center pr-0">
54
- <span class="text">'.$title.'</span>
55
  </div>
56
  <div class="col-5 col-md-5 col-lg-5 align-self-center text-right">
57
- <div class="list-image"><img src="' . ENHANCAD_PLUGIN_URL.'/admin/images/exclaimation.png" alt="no-config-success"/></div>
58
  </div>
59
  </div>
60
  </li>';
@@ -80,10 +80,10 @@ class GoogleShoppingFeed {
80
  }
81
  $sync_product_total = 0; $sync_product_approved = 0; $sync_product_disapproved = 0; $sync_product_pending = 0;
82
  if($syncProductStat){
83
- $sync_product_total = (property_exists($syncProductStat,"total")) ? $syncProductStat->total : "0";
84
- $sync_product_approved = (property_exists($syncProductStat,"approved")) ? $syncProductStat->approved : "0";
85
- $sync_product_disapproved = (property_exists($syncProductStat,"disapproved")) ? $syncProductStat->disapproved : "0";
86
- $sync_product_pending = (property_exists($syncProductStat,"pending")) ? $syncProductStat->pending : "0";
87
  }
88
  $totalCampaigns = 0;$campaignActive = 0; $campaignClicks = 0;
89
  $campaignCost = 0; $campaignConversions = 0; $campaignSales = 0;
@@ -97,68 +97,66 @@ class GoogleShoppingFeed {
97
  if ($campaign->active == 1) {
98
  $campaignActive = $campaignActive + $campaign->active;
99
  }
100
- $campaignClicks = $campaignClicks + $campaign->clicks;
101
- $row_campaign_cost = ($campaign->cost);
102
- $campaignCost = $campaignCost + $row_campaign_cost;
103
- $campaignConversions = $campaignConversions + $campaign->conversions;
104
- $campaignSales = $campaignSales + $campaign->sales;
105
  }
106
  if (count($campaigns_list) > 0) {
107
  $campaignConversions = $campaignConversions / count($campaigns_list);
108
  }
109
  }
110
- $campaignActive = (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != "" ? $campaignActive : '0');
111
- $campaignCost = (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != "" ? $currency . $campaignCost : '0');
112
- $campaignClicks = (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != "" ? $campaignClicks : '0');
113
- $campaignConversions = (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != "" ? $campaignConversions . "%" : '0');
114
- $campaignSales = (isset($googleDetail->google_ads_id) && $googleDetail->google_ads_id != "" ? $currency . $campaignSales : '0');
115
  }
116
  $last_api_sync_up = "";
117
  $date_formate=get_option('date_format')." ".get_option('time_format');
118
  if($date_formate ==""){
119
  $date_formate = 'M-d-Y H:i';
120
  }
121
- if(isset($google_detail['sync_time']) && $google_detail['sync_time']){
122
- $last_api_sync_up = date( $date_formate, $google_detail['sync_time']);
123
  }
124
  $is_need_to_update = $this->TVC_Admin_Helper->is_need_to_update_api_to_db();
125
  ?>
126
-
127
-
128
  <div class="tab-content">
129
  <div class="tab-pane show active" id="googleShoppingFeed">
130
  <div class="tab-card">
131
  <div class="row">
132
  <div class="col-md-6 col-lg-8 border-right">
133
- <?php if($this->subscriptionId != ""){?>
134
  <div class="tvc-api-sunc">
135
  <span>
136
- <?php if($last_api_sync_up){
137
- echo __("Details last synced at ","conversios").$last_api_sync_up;
138
  }else{
139
- echo __("Refresh sync up","conversios");
140
- }?></span><img id="refresh_api" onclick="call_tvc_api_sync_up();" src="<?php echo esc_url(ENHANCAD_PLUGIN_URL.'/admin/images/refresh.png'); ?>">
141
  </div>
142
  <?php } ?>
143
  <?php
144
  $last_auto_sync = $this->TVC_Admin_Helper->get_last_auto_sync_product_info();
145
- if(!empty($last_auto_sync) && isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id && $this->subscriptionId != ""){
146
- $status = isset($last_auto_sync['status'])?$last_auto_sync['status']:0;
147
  $status_text = array("0"=>"Failed","1"=>"Completed");
148
- $create_sync = (isset($last_auto_sync['create_sync']))?$last_auto_sync['create_sync']:"";
149
  $create_sync = date($date_formate,strtotime($create_sync));
150
- $next_sync = (isset($last_auto_sync['next_sync']))?$last_auto_sync['next_sync']:"";
151
  $next_sync = date($date_formate,strtotime($next_sync));
152
  ?>
153
  <div class="product-auto-sync-details">
154
- <strong><?php _e("Last auto product sync details","conversios"); ?></strong>
155
  <table>
156
- <tr><th><?php _e("Last sync","conversios"); ?></th><th><?php _e("Sync product","conversios"); ?></th><th><?php _e("Status","conversios"); ?></th><th><?php _e("Upcoming sync","conversios"); ?></th></tr>
157
  <tr>
158
- <td><?php echo $create_sync;?></td>
159
- <td><?php echo (isset($last_auto_sync['total_sync_product']))?$last_auto_sync['total_sync_product']:"";?></td>
160
- <td><?php echo $status_text[$status]; ?></td>
161
- <td><?php echo $next_sync;?></td>
162
  </tr>
163
  </table>
164
  </div>
@@ -170,52 +168,52 @@ class GoogleShoppingFeed {
170
  <div class="col-md-12 col-lg-4 mb-3 mb-lg-0">
171
  <div class="card configure-card">
172
  <div class="card-header">
173
- <h4 class="confg-title"><?php _e("Configuration","conversios"); ?></h4>
174
  </div>
175
  <div class="card-body">
176
  <ul class="list-unstyled"><?php
177
- $is_domain_claim = esc_attr((isset($googleDetail->is_domain_claim))?$googleDetail->is_domain_claim:"");
178
- $is_site_verified = esc_attr((isset($googleDetail->is_site_verified))?$googleDetail->is_site_verified:"");
179
- echo $this->configuration_list_html("Google merchant center",(isset($googleDetail->google_merchant_center_id))?$googleDetail->google_merchant_center_id:"");
180
  if($is_site_verified ==1){
181
- echo $this->configuration_list_html("Site Verified",$is_site_verified);
182
  }else{
183
- echo $this->configuration_error_list_html("Site Verified",$is_site_verified,"call_site_verified", $googleDetail);
184
  }
185
  if($is_domain_claim ==1){
186
- echo $this->configuration_list_html("Domain claim",$is_domain_claim);
187
  }else{
188
- echo $this->configuration_error_list_html("Domain claim",$is_domain_claim, 'call_domain_claim', $googleDetail);
189
  }
190
- echo $this->configuration_list_html("Google Ads linking",((isset($googleDetail->google_ads_id)))?$googleDetail->google_ads_id:"");
191
  ?>
192
  </ul>
193
  </div>
194
  <div class="card-footer">
195
- <a href="<?php echo $this->site_url.'gaa_config_page'; ?>" class="btn btn-primary" id="configuration"><?php _e("Edit","conversios"); ?></a>
196
  </div>
197
  </div>
198
  </div>
199
  <div class="col-md-12 col-lg-4 mb-3 mb-lg-0">
200
  <div class="card">
201
  <div class="card-header">
202
- <h4 class="confg-title"><?php _e("Product Sync","conversios"); ?></h4>
203
  </div>
204
  <div class="card-body">
205
  <ul class="list-unstyled">
206
  <?php
207
- echo $this->add_list_html(__("Active products in WooCommerce","conversios"),$totalActiveWooProduct)
208
- .$this->add_list_html(__("Total synced products in Merchant center","conversios"), $sync_product_total)
209
- .$this->add_list_html(__("Approved","conversios"), $sync_product_approved)
210
- .$this->add_list_html(__("Disapproved","conversios"), $sync_product_disapproved)
211
- .$this->add_list_html(__("Pending","conversios"), $sync_product_pending);
212
  ?>
213
  </ul>
214
  </div>
215
  <?php
216
- if (isset($googleDetail->google_merchant_center_id) && $googleDetail->google_merchant_center_id != "") {?>
217
  <div class="card-footer">
218
- <a href="<?php echo esc_url($this->site_url.'sync_product_page'); ?>" class="btn btn-primary" id="product-sync"><?php _e("Edit","conversios"); ?></a>
219
  </div>
220
  <?php } ?>
221
  </div>
@@ -223,22 +221,22 @@ class GoogleShoppingFeed {
223
  <div class="col-md-12 col-lg-4 mb-3 mb-lg-0">
224
  <div class="card">
225
  <div class="card-header">
226
- <h4 class="confg-title"><?php _e("Smart Shopping Campaigns","conversios"); ?></h4>
227
  </div>
228
  <div class="card-body">
229
  <ul class="list-unstyled">
230
  <?php
231
- echo $this->add_list_html(__("Total campaign","conversios"),$totalCampaigns)
232
- .$this->add_list_html(__("Active campaigns","conversios"),$campaignActive)
233
- .$this->add_list_html(__("Cost","conversios"),$campaignCost)
234
- .$this->add_list_html(__("Click","conversios"),$campaignClicks)
235
- .$this->add_list_html(__("Conversion%","conversios"),$campaignConversions)
236
- .$this->add_list_html(__("Sales","conversios"),$campaignSales); ?>
237
  </ul>
238
  </div>
239
-
14
  return '<li>
15
  <div class="row">
16
  <div class="col-7 col-md-8 align-self-center pr-0">
17
+ <span class="text">'.esc_attr($title).'</span>
18
  </div>
19
  <div class="col-5 col-md-4 align-self-center text-right">
20
  <span class="text"><strong>'. esc_attr($val).'</strong></span>
23
  </li>';
24
  }
25
  public function configuration_list_html($title, $val){
26
+ $imge = (isset($val) && $val != "" && $val != 0) ? esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/config-success.svg') : esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/exclaimation.png');
27
  return '<li>
28
  <div class="row">
29
  <div class="col-7 col-md-7 col-lg-9 align-self-center pr-0">
30
+ <span class="text">'.esc_attr($title).'</span>
31
  </div>
32
+ <div class="col-5 col-md-5 col-lg-3 text-end">
33
+ <div class="list-image"><img src="'.esc_url_raw($imge).'"></div>
34
  </div>
35
  </div>
36
  </li>';
40
  return '<li>
41
  <div class="row">
42
  <div class="col-7 col-md-7 col-lg-7 align-self-center pr-0">
43
+ <span class="text">'.esc_attr($title).'</span>
44
  </div>
45
+ <div class="col-5 col-md-5 col-lg-5 text-end">
46
+ <div class="list-image"><img id="refresh_'.esc_attr($call_domain_claim).'" onclick="'.esc_attr($call_domain_claim).'();" src="'. esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/refresh.png').'"><img src="' .esc_url_raw( ENHANCAD_PLUGIN_URL.'/admin/images/exclaimation.png').'" alt="no-config-success"/></div>
47
  </div>
48
  </div>
49
  </li>';
51
  return '<li>
52
  <div class="row">
53
  <div class="col-7 col-md-7 col-lg-7 align-self-center pr-0">
54
+ <span class="text">'.esc_attr($title).'</span>
55
  </div>
56
  <div class="col-5 col-md-5 col-lg-5 align-self-center text-right">
57
+ <div class="list-image"><img src="' . esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/exclaimation.png').'" alt="no-config-success"/></div>
58
  </div>
59
  </div>
60
  </li>';
80
  }
81
  $sync_product_total = 0; $sync_product_approved = 0; $sync_product_disapproved = 0; $sync_product_pending = 0;
82
  if($syncProductStat){
83
+ $sync_product_total = (property_exists($syncProductStat,"total")) ? esc_attr($syncProductStat->total) : "0";
84
+ $sync_product_approved = (property_exists($syncProductStat,"approved")) ? esc_attr($syncProductStat->approved) : "0";
85
+ $sync_product_disapproved = (property_exists($syncProductStat,"disapproved")) ? esc_attr($syncProductStat->disapproved) : "0";
86
+ $sync_product_pending = (property_exists($syncProductStat,"pending")) ? esc_attr($syncProductStat->pending) : "0";
87
  }
88
  $totalCampaigns = 0;$campaignActive = 0; $campaignClicks = 0;
89
  $campaignCost = 0; $campaignConversions = 0; $campaignSales = 0;
97
  if ($campaign->active == 1) {
98
  $campaignActive = $campaignActive + $campaign->active;
99
  }
100
+ $campaignClicks = $campaignClicks + esc_attr($campaign->clicks);
101
+ $row_campaign_cost = esc_attr($campaign->cost);
102
+ $campaignCost = $campaignCost + esc_attr($row_campaign_cost);
103
+ $campaignConversions = $campaignConversions + esc_attr($campaign->conversions);
104
+ $campaignSales = $campaignSales + esc_attr($campaign->sales);
105
  }
106
  if (count($campaigns_list) > 0) {
107
  $campaignConversions = $campaignConversions / count($campaigns_list);
108
  }
109
  }
110
+ $campaignActive = (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != "" ? esc_attr($campaignActive) : '0');
111
+ $campaignCost = (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != "" ? esc_attr($currency) . esc_attr($campaignCost) : '0');
112
+ $campaignClicks = (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != "" ? $campaignClicks : '0');
113
+ $campaignConversions = (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != "" ? esc_attr($campaignConversions) . "%" : '0');
114
+ $campaignSales = (isset($googleDetail->google_ads_id) && esc_attr($googleDetail->google_ads_id) != "" ? esc_attr($currency) . esc_attr($campaignSales) : '0');
115
  }
116
  $last_api_sync_up = "";
117
  $date_formate=get_option('date_format')." ".get_option('time_format');
118
  if($date_formate ==""){
119
  $date_formate = 'M-d-Y H:i';
120
  }
121
+ if(isset($google_detail['sync_time']) && esc_attr($google_detail['sync_time'])){
122
+ $last_api_sync_up = date( $date_formate, esc_attr($google_detail['sync_time']));
123
  }
124
  $is_need_to_update = $this->TVC_Admin_Helper->is_need_to_update_api_to_db();
125
  ?>
 
 
126
  <div class="tab-content">
127
  <div class="tab-pane show active" id="googleShoppingFeed">
128
  <div class="tab-card">
129
  <div class="row">
130
  <div class="col-md-6 col-lg-8 border-right">
131
+ <?php if(esc_attr($this->subscriptionId) != ""){?>
132
  <div class="tvc-api-sunc">
133
  <span>
134
+ <?php if(esc_attr($last_api_sync_up)){
135
+ echo esc_html__("Details last synced at ","conversios").esc_attr($last_api_sync_up);
136
  }else{
137
+ echo esc_html__("Refresh sync up","conversios");
138
+ }?></span><img id="refresh_api" onclick="call_tvc_api_sync_up();" src="<?php echo esc_url_raw(ENHANCAD_PLUGIN_URL.'/admin/images/refresh.png'); ?>">
139
  </div>
140
  <?php } ?>
141
  <?php
142
  $last_auto_sync = $this->TVC_Admin_Helper->get_last_auto_sync_product_info();
143
+ if(!empty($last_auto_sync) && isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) && esc_attr($this->subscriptionId) != ""){
144
+ $status = isset($last_auto_sync['status'])?esc_attr($last_auto_sync['status']):0;
145
  $status_text = array("0"=>"Failed","1"=>"Completed");
146
+ $create_sync = (isset($last_auto_sync['create_sync']))?esc_attr($last_auto_sync['create_sync']):"";
147
  $create_sync = date($date_formate,strtotime($create_sync));
148
+ $next_sync = (isset($last_auto_sync['next_sync']))?esc_attr($last_auto_sync['next_sync']):"";
149
  $next_sync = date($date_formate,strtotime($next_sync));
150
  ?>
151
  <div class="product-auto-sync-details">
152
+ <strong><?php esc_html_e("Last auto product sync details","conversios"); ?></strong>
153
  <table>
154
+ <tr><th><?php esc_html_e("Last sync","conversios"); ?></th><th><?php esc_html_e("Sync product","conversios"); ?></th><th><?php esc_html_e("Status","conversios"); ?></th><th><?php esc_html_e("Upcoming sync","conversios"); ?></th></tr>
155
  <tr>
156
+ <td><?php echo esc_attr($create_sync);?></td>
157
+ <td><?php echo (isset($last_auto_sync['total_sync_product']))?esc_attr($last_auto_sync['total_sync_product']):"";?></td>
158
+ <td><?php echo esc_attr($status_text[$status]); ?></td>
159
+ <td><?php echo esc_attr($next_sync);?></td>
160
  </tr>
161
  </table>
162
  </div>
168
  <div class="col-md-12 col-lg-4 mb-3 mb-lg-0">
169
  <div class="card configure-card">
170
  <div class="card-header">
171
+ <h4 class="confg-title"><?php esc_html_e("Configuration","conversios"); ?></h4>
172
  </div>
173
  <div class="card-body">
174
  <ul class="list-unstyled"><?php
175
+ $is_domain_claim = esc_attr((isset($googleDetail->is_domain_claim))?esc_attr($googleDetail->is_domain_claim):"");
176
+ $is_site_verified = esc_attr((isset($googleDetail->is_site_verified))?esc_attr($googleDetail->is_site_verified):"");
177
+ echo $this->configuration_list_html("Google merchant center",(isset($googleDetail->google_merchant_center_id))?esc_attr($googleDetail->google_merchant_center_id):"");
178
  if($is_site_verified ==1){
179
+ echo $this->configuration_list_html(esc_html__("Site Verified","conversios"), esc_attr($is_site_verified));
180
  }else{
181
+ echo $this->configuration_error_list_html(esc_html__("Site Verified","conversios"),esc_attr($is_site_verified),"call_site_verified", $googleDetail);
182
  }
183
  if($is_domain_claim ==1){
184
+ echo $this->configuration_list_html(esc_html__("Domain claim","conversios"),esc_attr($is_domain_claim));
185
  }else{
186
+ echo $this->configuration_error_list_html(esc_html__("Domain claim","conversios"),esc_attr($is_domain_claim), 'call_domain_claim', $googleDetail);
187
  }
188
+ echo $this->configuration_list_html(esc_html__("Google Ads linking","conversios"),((isset($googleDetail->google_ads_id)))?esc_attr($googleDetail->google_ads_id):"");
189
  ?>
190
  </ul>
191
  </div>
192
  <div class="card-footer">
193
+ <a href="<?php echo $this->site_url.'gaa_config_page'; ?>" class="btn btn-primary" id="configuration"><?php esc_html_e("Edit","conversios"); ?></a>
194
  </div>
195
  </div>
196
  </div>
197
  <div class="col-md-12 col-lg-4 mb-3 mb-lg-0">
198
  <div class="card">
199
  <div class="card-header">
200
+ <h4 class="confg-title"><?php esc_html_e("Product Sync","conversios"); ?></h4>
201
  </div>
202
  <div class="card-body">
203
  <ul class="list-unstyled">
204
  <?php
205
+ echo $this->add_list_html(esc_html__("Active products in WooCommerce","conversios"),esc_attr($totalActiveWooProduct))
206
+ .$this->add_list_html(esc_html__("Total synced products in Merchant center","conversios"), esc_attr($sync_product_total))
207
+ .$this->add_list_html(esc_html__("Approved","conversios"), esc_attr($sync_product_approved))
208
+ .$this->add_list_html(esc_html__("Disapproved","conversios"), esc_attr($sync_product_disapproved))
209
+ .$this->add_list_html(esc_html__("Pending","conversios"), esc_attr($sync_product_pending));
210
  ?>
211
  </ul>
212
  </div>
213
  <?php
214
+ if (isset($googleDetail->google_merchant_center_id) && esc_attr($googleDetail->google_merchant_center_id) != "") {?>
215
  <div class="card-footer">
216
+ <a href="<?php echo esc_url_raw($this->site_url.'sync_product_page'); ?>" class="btn btn-primary" id="product-sync"><?php esc_html_e("Edit","conversios"); ?></a>
217
  </div>
218
  <?php } ?>
219
  </div>
221
  <div class="col-md-12 col-lg-4 mb-3 mb-lg-0">
222
  <div class="card">
223
  <div class="card-header">
224
+ <h4 class="confg-title"><?php esc_html_e("Smart Shopping Campaigns","conversios"); ?></h4>
225
  </div>
226
  <div class="card-body">
227
  <ul class="list-unstyled">
228
  <?php
229
+ echo $this->add_list_html(esc_html__("Total campaign","conversios"),esc_attr($totalCampaigns))
230
+ .$this->add_list_html(esc_html__("Active campaigns","conversios"),esc_attr($campaignActive))
231
+ .$this->add_list_html(esc_html__("Cost","conversios"),esc_attr($campaignCost))
232
+ .$this->add_list_html(esc_html__("Click","conversios"),esc_attr($campaignClicks))
233
+ .$this->add_list_html(esc_html__("Conversion%","conversios"),esc_attr($campaignConversions))
234
+ .$this->add_list_html(esc_html__("Sales","conversios"),esc_attr($campaignSales)); ?>
235
  </ul>
236
  </div>