W3 Total Cache - Version 0.10.0

Version Description

  • Improved Statistics component for pro users
  • Improved support for CloudFront distributions with multiple origins
  • Improved redirects by using safter wp_safe redirect
  • Improved .htaccess usage when pagecache does not require it
  • Improved protection of unexpected values in global variables
  • Added more Amazon S3 regions
  • Added support for memcached binary protocol when available
  • Added caching for webp MIME type
  • Updated S3 bucket creation by settings CORS policy
  • Updated blogmap to allow urls with custom ports
  • Fixed usage of base url with minify
  • Fixed mixing content of sync & async scripts with minify

  • Fixed S3 + CloudFront urls when CNAMEs not used

Download this release

Release Info

Developer fredericktownes
Plugin Icon 128x128 W3 Total Cache
Version 0.10.0
Comparing to
See all releases

Code changes from version 0.9.7.5 to 0.10.0

Files changed (99) hide show
  1. BrowserCache_Environment.php +1 -0
  2. Cache_Memcached.php +4 -1
  3. CdnEngine_CloudFront.php +19 -5
  4. CdnEngine_Mirror_CloudFront.php +6 -3
  5. CdnEngine_S3.php +49 -13
  6. Cdn_AdminActions.php +0 -28
  7. Cdn_Plugin.php +0 -9
  8. ConfigCompiler.php +15 -1
  9. ConfigKeys.php +28 -0
  10. DbCache_Plugin.php +44 -3
  11. DbCache_Plugin_Admin.php +11 -21
  12. DbCache_WpdbInjection_QueryCaching.php +49 -53
  13. Dispatcher.php +20 -14
  14. Extension_FragmentCache_Plugin_Admin.php +7 -2
  15. Extension_FragmentCache_WpObjectCache.php +0 -44
  16. Extension_Genesis_Plugin_Admin.php +1 -1
  17. Extension_Wpml_Plugin_Admin.php +3 -3
  18. Generic_AdminActions_Default.php +12 -7
  19. Generic_AdminNotes.php +19 -0
  20. Generic_Plugin.php +15 -7
  21. Generic_Plugin_Admin.php +1 -1
  22. Licensing_AdminActions.php +6 -3
  23. Licensing_Core.php +8 -0
  24. Licensing_Plugin_Admin.php +2 -2
  25. Minify_AutoJs.php +2 -1
  26. Minify_Core.php +3 -3
  27. Minify_Plugin.php +0 -9
  28. ObjectCache_Plugin.php +38 -2
  29. ObjectCache_Plugin_Admin.php +16 -27
  30. ObjectCache_WpObjectCache_Regular.php +130 -144
  31. PageSpeed_Plugin_Widget.php +4 -0
  32. PgCache_ContentGrabber.php +88 -27
  33. PgCache_Environment.php +1 -1
  34. PgCache_Plugin.php +38 -7
  35. PgCache_Plugin_Admin.php +77 -55
  36. Root_AdminActions.php +2 -1
  37. Root_AdminMenu.php +5 -0
  38. UsageStatistics_AdminActions.php +23 -0
  39. UsageStatistics_Core.php +5 -2
  40. UsageStatistics_GeneralPage.php +32 -0
  41. UsageStatistics_GeneralPage_View.php +120 -0
  42. UsageStatistics_Page.php +187 -0
  43. UsageStatistics_Page_DbRequests_View.php +43 -0
  44. UsageStatistics_Page_ObjectCacheLog_View.php +47 -0
  45. UsageStatistics_Page_PageCacheRequests_View.php +41 -0
  46. UsageStatistics_Page_View.css +103 -0
  47. UsageStatistics_Page_View.js +807 -0
  48. UsageStatistics_Page_View.php +486 -0
  49. UsageStatistics_Page_View_Ad.php +11 -0
  50. UsageStatistics_Page_View_Disabled.php +20 -0
  51. UsageStatistics_Page_View_Free.php +21 -0
  52. UsageStatistics_Page_View_NoDebugMode.php +19 -0
  53. UsageStatistics_Plugin.php +17 -0
  54. UsageStatistics_Plugin_Admin.php +100 -20
  55. UsageStatistics_Source_AccessLog.php +382 -0
  56. UsageStatistics_Source_DbQueriesLog.php +153 -0
  57. UsageStatistics_Source_ObjectCacheLog.php +164 -0
  58. UsageStatistics_Source_PageCacheLog.php +154 -0
  59. UsageStatistics_Source_Wpdb.php +61 -0
  60. UsageStatistics_Sources.php +130 -0
  61. UsageStatistics_Sources_Apc.php +63 -0
  62. UsageStatistics_Sources_Memcached.php +104 -0
  63. UsageStatistics_Sources_Redis.php +139 -0
  64. UsageStatistics_StorageReader.php +8 -131
  65. UsageStatistics_StorageWriter.php +50 -22
  66. UsageStatistics_View_General.php +0 -32
  67. UsageStatistics_Widget.php +18 -38
  68. UsageStatistics_Widget_View.css +0 -45
  69. UsageStatistics_Widget_View.js +186 -50
  70. UsageStatistics_Widget_View.php +40 -111
  71. UsageStatistics_Widget_View_Disabled.php +29 -2
  72. Util_Content.php +3 -15
  73. Util_Environment.php +17 -6
  74. Util_Ui.php +6 -3
  75. Util_UsageStatistics.php +77 -4
  76. Util_Widget.php +6 -2
  77. Util_WpmuBlogmap.php +1 -1
  78. inc/lightbox/cdn_s3_bucket_location.php +0 -17
  79. inc/lightbox/upgrade.php +20 -38
  80. inc/options/cdn/cf.php +9 -17
  81. inc/options/cdn/s3.php +26 -34
  82. inc/options/edd/buy.php +6 -5
  83. inc/options/general.php +1 -3
  84. inc/options/minify/css.php +1 -1
  85. inc/options/pgcache.php +37 -37
  86. pub/css/lightbox.css +4 -3
  87. pub/fonts/source-a.svg +1 -1
  88. pub/fonts/w3tc.eot +0 -0
  89. pub/fonts/w3tc.svg +11 -1
  90. pub/fonts/w3tc.ttf +0 -0
  91. pub/fonts/w3tc.woff +0 -0
  92. pub/img/overlay/w3-meteor.png +0 -0
  93. pub/img/stats-bg.png +0 -0
  94. pub/img/usage-statistics-widget.png +0 -0
  95. pub/js/chartjs.min.js +7 -0
  96. pub/js/lightbox.js +12 -31
  97. readme.txt +17 -1
  98. w3-total-cache-api.php +1 -1
  99. w3-total-cache.php +1 -1
BrowserCache_Environment.php CHANGED
@@ -123,6 +123,7 @@ class BrowserCache_Environment {
123
  unset( $other_compression['png'] );
124
  unset( $other_compression['ra|ram'] );
125
  unset( $other_compression['tar'] );
 
126
  unset( $other_compression['wma'] );
127
  unset( $other_compression['zip'] );
128
 
123
  unset( $other_compression['png'] );
124
  unset( $other_compression['ra|ram'] );
125
  unset( $other_compression['tar'] );
126
+ unset( $other_compression['webp'] );
127
  unset( $other_compression['wma'] );
128
  unset( $other_compression['zip'] );
129
 
Cache_Memcached.php CHANGED
@@ -59,6 +59,9 @@ class Cache_Memcached extends Cache_Base {
59
  if ( defined( '\Memcached::OPT_REMOVE_FAILED_SERVERS' ) ) {
60
  $this->_memcache->setOption( \Memcached::OPT_REMOVE_FAILED_SERVERS, true );
61
  }
 
 
 
62
 
63
  if ( isset( $config['aws_autodiscovery'] ) &&
64
  $config['aws_autodiscovery'] &&
@@ -370,7 +373,7 @@ class Cache_Memcached extends Cache_Base {
370
  return true;
371
 
372
  $storage_key = $this->get_item_key( $key );
373
- $r = @$this->_memcache->increment( $storage_key, $value );
374
  if ( !$r ) // it doesnt initialize counter by itself
375
  $this->counter_set( $key, 0 );
376
 
59
  if ( defined( '\Memcached::OPT_REMOVE_FAILED_SERVERS' ) ) {
60
  $this->_memcache->setOption( \Memcached::OPT_REMOVE_FAILED_SERVERS, true );
61
  }
62
+ if ( defined( '\Memcached::OPT_BINARY_PROTOCOL' ) ) {
63
+ $this->_memcache->setOption( \Memcached::OPT_BINARY_PROTOCOL, true );
64
+ }
65
 
66
  if ( isset( $config['aws_autodiscovery'] ) &&
67
  $config['aws_autodiscovery'] &&
373
  return true;
374
 
375
  $storage_key = $this->get_item_key( $key );
376
+ $r = $this->_memcache->increment( $storage_key, $value, 0, 3600 );
377
  if ( !$r ) // it doesnt initialize counter by itself
378
  $this->counter_set( $key, 0 );
379
 
CdnEngine_CloudFront.php CHANGED
@@ -48,8 +48,19 @@ class CdnEngine_CloudFront extends CdnEngine_Base {
48
  * Formats URL
49
  */
50
  function _format_url( $path ) {
51
- // the same limitation as S3 has
52
- return $this->s3->_format_url( $path );
 
 
 
 
 
 
 
 
 
 
 
53
  }
54
 
55
  /**
@@ -334,9 +345,12 @@ class CdnEngine_CloudFront extends CdnEngine_Base {
334
 
335
  $items = $dists['DistributionList']['Items'];
336
  foreach ( $items as $dist ) {
337
- if ( isset( $dist['Origins']['Items'][0]['DomainName'] ) &&
338
- $dist['Origins']['Items'][0]['DomainName'] == $origin ) {
339
- return $dist;
 
 
 
340
  }
341
  }
342
 
48
  * Formats URL
49
  */
50
  function _format_url( $path ) {
51
+ $domain = $this->get_domain( $path );
52
+
53
+ if ( $domain ) {
54
+ $scheme = $this->_get_scheme();
55
+
56
+ // it does not support '+', requires '%2B'
57
+ $path = str_replace( '+', '%2B', $path );
58
+ $url = sprintf( '%s://%s/%s', $scheme, $domain, $path );
59
+
60
+ return $url;
61
+ }
62
+
63
+ return false;
64
  }
65
 
66
  /**
345
 
346
  $items = $dists['DistributionList']['Items'];
347
  foreach ( $items as $dist ) {
348
+ if ( isset( $dist['Origins']['Items'] ) ) {
349
+ foreach ( $dist['Origins']['Items'] as $o ) {
350
+ if ( isset( $o['DomainName'] ) && $o['DomainName'] == $origin ) {
351
+ return $dist;
352
+ }
353
+ }
354
  }
355
  }
356
 
CdnEngine_Mirror_CloudFront.php CHANGED
@@ -313,9 +313,12 @@ class CdnEngine_Mirror_CloudFront extends CdnEngine_Mirror {
313
 
314
  $items = $dists['DistributionList']['Items'];
315
  foreach ( $items as $dist ) {
316
- if ( isset( $dist['Origins']['Items'][0]['DomainName'] ) &&
317
- $dist['Origins']['Items'][0]['DomainName'] == $origin ) {
318
- return $dist;
 
 
 
319
  }
320
  }
321
 
313
 
314
  $items = $dists['DistributionList']['Items'];
315
  foreach ( $items as $dist ) {
316
+ if ( isset( $dist['Origins']['Items'] ) ) {
317
+ foreach ( $dist['Origins']['Items'] as $o ) {
318
+ if ( isset( $o['DomainName'] ) && $o['DomainName'] == $origin ) {
319
+ return $dist;
320
+ }
321
+ }
322
  }
323
  }
324
 
CdnEngine_S3.php CHANGED
@@ -11,6 +11,31 @@ if ( !defined( 'W3TC_SKIPLIB_AWS' ) ) {
11
  class CdnEngine_S3 extends CdnEngine_Base {
12
  private $api;
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  public function __construct( $config = array() ) {
15
  $config = array_merge( array(
16
  'key' => '',
@@ -161,8 +186,7 @@ class CdnEngine_S3 extends CdnEngine_Base {
161
  $result = $this->_put_object( array(
162
  'Key' => $remote_path,
163
  'SourceFile' => $local_path,
164
- 'Metadata' => $headers
165
- )
166
  );
167
 
168
  return $this->_get_result( $local_path, $remote_path,
@@ -232,9 +256,8 @@ class CdnEngine_S3 extends CdnEngine_Base {
232
 
233
  $result = $this->_put_object( array(
234
  'Key' => $remote_path,
235
- 'Body' => $data,
236
- 'Metadata' => $headers
237
- )
238
  );
239
 
240
  return $this->_get_result( $local_path, $remote_path,
@@ -250,18 +273,18 @@ class CdnEngine_S3 extends CdnEngine_Base {
250
  /**
251
  * Wrapper to set headers well
252
  */
253
- private function _put_object( $data ) {
254
  $data['ACL'] = 'public-read';
255
  $data['Bucket'] = $this->_config['bucket'];
256
 
257
- if ( isset( $data['Metadata']['Content-Type'] ) ) {
258
- $data['ContentType'] = $data['Metadata']['Content-Type'];
259
  }
260
- if ( isset( $data['Metadata']['Content-Encoding'] ) ) {
261
- $data['ContentEncoding'] = $data['Metadata']['Content-Encoding'];
262
  }
263
- if ( isset( $data['Metadata']['Cache-Control'] ) ) {
264
- $data['CacheControl'] = $data['Metadata']['Cache-Control'];
265
  }
266
 
267
  return $this->api->putObject( $data );
@@ -429,9 +452,22 @@ class CdnEngine_S3 extends CdnEngine_Base {
429
  }
430
 
431
  try {
432
- $result = $this->api->createBucket( array(
433
  'Bucket' => $this->_config['bucket'],
434
  ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  } catch ( \Exception $e) {
436
  throw new \Exception( 'Failed to create bucket: ' . $ex->getMessage() );
437
  }
11
  class CdnEngine_S3 extends CdnEngine_Base {
12
  private $api;
13
 
14
+ static public function regions_list() {
15
+ return array(
16
+ 'us-east-1' => __( 'US East (N. Virginia)', 'w3-total-cache' ),
17
+ 'us-east-2' => __( 'US East (Ohio)', 'w3-total-cache' ),
18
+ 'us-west-1' => __( 'US-West (N. California)', 'w3-total-cache' ),
19
+ 'us-west-2' => __( 'US-West (Oregon)', 'w3-total-cache' ),
20
+ 'ap-east-1' => __( 'Asia Pacific (Hong Kong)', 'w3-total-cache' ),
21
+ 'ap-northeast-1'=> __( 'Asia Pacific (Tokyo)', 'w3-total-cache' ),
22
+ 'ap-northeast-2' => __( 'Asia Pacific (Seoul)', 'w3-total-cache' ),
23
+ 'ap-northeast-3' => __( 'Asia Pacific (Osaka-Local)', 'w3-total-cache' ),
24
+ 'ap-south-1' => __( 'Asia Pacific (Mumbai)', 'w3-total-cache' ),
25
+ 'ap-southeast-1' => __( 'Asia Pacific (Singapore)', 'w3-total-cache' ),
26
+ 'ap-southeast-2' => __( 'Asia Pacific (Sydney)', 'w3-total-cache' ),
27
+ 'ca-central-1' => __( 'Canada (Central)', 'w3-total-cache' ),
28
+ 'cn-northwest-1.cn' => __( 'China (Ningxia)', 'w3-total-cache' ),
29
+ 'eu-central-1' => __( 'EU (Frankfurt)', 'w3-total-cache' ),
30
+ 'eu-north-1' => __( 'EU (Stockholm)', 'w3-total-cache' ),
31
+ 'eu-west-1' => __( 'EU (Ireland)', 'w3-total-cache' ),
32
+ 'eu-west-2' => __( 'EU (London)', 'w3-total-cache' ),
33
+ 'eu-west-3' => __( 'EU (Paris)', 'w3-total-cache' ),
34
+ 'me-south-1' => __( 'Middle East (Bahrain)', 'w3-total-cache' ),
35
+ 'sa-east-1' => __( 'South America (São Paulo)', 'w3-total-cache' ),
36
+ );
37
+ }
38
+
39
  public function __construct( $config = array() ) {
40
  $config = array_merge( array(
41
  'key' => '',
186
  $result = $this->_put_object( array(
187
  'Key' => $remote_path,
188
  'SourceFile' => $local_path,
189
+ ), $headers
 
190
  );
191
 
192
  return $this->_get_result( $local_path, $remote_path,
256
 
257
  $result = $this->_put_object( array(
258
  'Key' => $remote_path,
259
+ 'Body' => $data
260
+ ), $headers
 
261
  );
262
 
263
  return $this->_get_result( $local_path, $remote_path,
273
  /**
274
  * Wrapper to set headers well
275
  */
276
+ private function _put_object( $data, $headers ) {
277
  $data['ACL'] = 'public-read';
278
  $data['Bucket'] = $this->_config['bucket'];
279
 
280
+ if ( isset( $headers['Content-Type'] ) ) {
281
+ $data['ContentType'] = $headers['Content-Type'];
282
  }
283
+ if ( isset( $headers['Content-Encoding'] ) ) {
284
+ $data['ContentEncoding'] = $headers['Content-Encoding'];
285
  }
286
+ if ( isset( $headers['Cache-Control'] ) ) {
287
+ $data['CacheControl'] = $headers['Cache-Control'];
288
  }
289
 
290
  return $this->api->putObject( $data );
452
  }
453
 
454
  try {
455
+ $this->api->createBucket( array(
456
  'Bucket' => $this->_config['bucket'],
457
  ) );
458
+
459
+ $this->api->putBucketCors( array(
460
+ 'Bucket' => $this->_config['bucket'],
461
+ 'CORSConfiguration' => array(
462
+ 'CORSRules' => array(
463
+ array(
464
+ 'AllowedHeaders' => array( '*' ),
465
+ 'AllowedMethods' => array( 'GET' ),
466
+ 'AllowedOrigins' => array( '*' )
467
+ )
468
+ )
469
+ )
470
+ ) );
471
  } catch ( \Exception $e) {
472
  throw new \Exception( 'Failed to create bucket: ' . $ex->getMessage() );
473
  }
Cdn_AdminActions.php CHANGED
@@ -495,34 +495,6 @@ class Cdn_AdminActions {
495
  echo json_encode( $response );
496
  }
497
 
498
- /**
499
- * S3 bucket location lightbox
500
- *
501
- * @return void
502
- */
503
- function w3tc_cdn_s3_bucket_location() {
504
- $type = Util_Request::get_string( 'type', 's3' );
505
-
506
- $locations = array(
507
- 'us-east-1' => __( 'US East (N. Virginia)', 'w3-total-cache' ),
508
- 'us-east-2' => __( 'US East (Ohio)', 'w3-total-cache' ),
509
- 'us-west-1' => __( 'US-West (N. California)', 'w3-total-cache' ),
510
- 'us-west-2' => __( 'US-West (Oregon)', 'w3-total-cache' ),
511
- 'ca-central-1' => __( 'Canada (Montreal)', 'w3-total-cache' ),
512
- 'ap-south-1' => __( 'Asia Pacific (Mumbai)', 'w3-total-cache' ),
513
- 'ap-northeast-2'=> __( 'Asia Pacific (Seoul)', 'w3-total-cache' ),
514
- 'ap-southeast-1'=> __( 'Asia Pacific (Singapore)', 'w3-total-cache' ),
515
- 'ap-southeast-2'=> __( 'Asia Pacific (Sydney)', 'w3-total-cache' ),
516
- 'ap-northeast-1'=> __( 'Asia Pacific (Tokyo)', 'w3-total-cache' ),
517
- 'eu-central-1' => __( 'EU (Frankfurt)', 'w3-total-cache' ),
518
- 'eu-west-1' => __( 'EU (Ireland)', 'w3-total-cache' ),
519
- 'eu-west-2' => __( 'EU (London)', 'w3-total-cache' ),
520
- 'sa-east-1' => __( 'South America (São Paulo)', 'w3-total-cache' ),
521
- );
522
-
523
- include W3TC_INC_DIR . '/lightbox/cdn_s3_bucket_location.php';
524
- }
525
-
526
 
527
 
528
  private function test_cdn_url( $url ) {
495
  echo json_encode( $response );
496
  }
497
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
 
499
 
500
  private function test_cdn_url( $url ) {
Cdn_Plugin.php CHANGED
@@ -570,15 +570,6 @@ class Cdn_Plugin {
570
  * @return string
571
  */
572
  function can_cdn2( $buffer ) {
573
- /**
574
- * Check for database error
575
- */
576
- if ( Util_Content::is_database_error( $buffer ) ) {
577
- $this->cdn_reject_reason = 'Database Error occurred';
578
-
579
- return false;
580
- }
581
-
582
  /**
583
  * Check for DONOTCDN constant
584
  */
570
  * @return string
571
  */
572
  function can_cdn2( $buffer ) {
 
 
 
 
 
 
 
 
 
573
  /**
574
  * Check for DONOTCDN constant
575
  */
ConfigCompiler.php CHANGED
@@ -103,6 +103,8 @@ class ConfigCompiler {
103
  $this->_data[$key] = $value['default'];
104
 
105
  $this->_data['version'] = W3TC_VERSION;
 
 
106
  }
107
 
108
 
@@ -209,12 +211,24 @@ class ConfigCompiler {
209
 
210
 
211
 
 
 
 
 
 
 
 
 
 
 
 
212
  /**
213
  * Apply new default values when version changes
214
  */
215
  private function upgrade( $file_data ) {
216
- if ( !isset( $file_data['version'] ) )
217
  $file_data['version'] = '0.0.0';
 
218
 
219
  if ( !function_exists( 'bb2_start' ) ) {
220
  $file_data['pgcache.bad_behavior_path'] = '';
103
  $this->_data[$key] = $value['default'];
104
 
105
  $this->_data['version'] = W3TC_VERSION;
106
+
107
+ $this->set_dynamic_defaults();
108
  }
109
 
110
 
211
 
212
 
213
 
214
+ private function set_dynamic_defaults() {
215
+ if ( empty( $this->_data['stats.access_log.webserver'] ) ) {
216
+ if ( Util_Environment::is_nginx() ) {
217
+ $this->_data['stats.access_log.webserver'] = 'nginx';
218
+ $this->_data['stats.access_log.format'] = '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
219
+ } else {
220
+ $this->_data['stats.access_log.webserver'] = 'apache';
221
+ }
222
+ }
223
+
224
+ }
225
  /**
226
  * Apply new default values when version changes
227
  */
228
  private function upgrade( $file_data ) {
229
+ if ( !isset( $file_data['version'] ) ) {
230
  $file_data['version'] = '0.0.0';
231
+ }
232
 
233
  if ( !function_exists( 'bb2_start' ) ) {
234
  $file_data['pgcache.bad_behavior_path'] = '';
ConfigKeys.php CHANGED
@@ -595,6 +595,34 @@ $keys = array(
595
  'type' => 'boolean',
596
  'default' => false
597
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
 
599
  'minify.configuration_overloaded' => array(
600
  'type' => 'boolean',
595
  'type' => 'boolean',
596
  'default' => false
597
  ),
598
+ 'stats.slot_seconds' => array(
599
+ 'type' => 'integer',
600
+ 'default' => 60,
601
+ ),
602
+ 'stats.slots_count' => array(
603
+ 'type' => 'integer',
604
+ 'default' => 60,
605
+ ),
606
+ 'stats.cpu.enabled' => array(
607
+ 'type' => 'boolean',
608
+ 'default' => false
609
+ ),
610
+ 'stats.access_log.enabled' => array(
611
+ 'type' => 'boolean',
612
+ 'default' => false
613
+ ),
614
+ 'stats.access_log.filename' => array(
615
+ 'type' => 'string',
616
+ 'default' => ''
617
+ ),
618
+ 'stats.access_log.format' => array(
619
+ 'type' => 'string',
620
+ 'default' => '%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"'
621
+ ),
622
+ 'stats.access_log.webserver' => array(
623
+ 'type' => 'string',
624
+ 'default' => ''
625
+ ),
626
 
627
  'minify.configuration_overloaded' => array(
628
  'type' => 'boolean',
DbCache_Plugin.php CHANGED
@@ -108,6 +108,8 @@ class DbCache_Plugin {
108
  // usage statistics handling
109
  add_filter( 'w3tc_usage_statistics_metrics', array(
110
  $this, 'w3tc_usage_statistics_metrics' ) );
 
 
111
  }
112
 
113
  /**
@@ -162,10 +164,13 @@ class DbCache_Plugin {
162
  static $flushed = false;
163
 
164
  if ( !$flushed ) {
165
- if ( is_null( $post ) )
166
  $post = $post_id;
 
167
 
168
- if ( $post_id>0 && !Util_Environment::is_flushable_post( $post, 'dbcache', $this->_config ) ) {
 
 
169
  return;
170
  }
171
 
@@ -219,13 +224,49 @@ class DbCache_Plugin {
219
  return $menu_items;
220
  }
221
 
 
 
222
  public function w3tc_usage_statistics_of_request( $storage ) {
223
  $o = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
224
  $o->w3tc_usage_statistics_of_request( $storage );
225
  }
226
 
 
 
227
  public function w3tc_usage_statistics_metrics( $metrics ) {
228
  return array_merge( $metrics, array(
229
- 'dbcache_calls_total', 'dbcache_calls_hits' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  }
231
  }
108
  // usage statistics handling
109
  add_filter( 'w3tc_usage_statistics_metrics', array(
110
  $this, 'w3tc_usage_statistics_metrics' ) );
111
+ add_filter( 'w3tc_usage_statistics_sources', array(
112
+ $this, 'w3tc_usage_statistics_sources' ) );
113
  }
114
 
115
  /**
164
  static $flushed = false;
165
 
166
  if ( !$flushed ) {
167
+ if ( is_null( $post ) ) {
168
  $post = $post_id;
169
+ }
170
 
171
+ if ( $post_id > 0 &&
172
+ !Util_Environment::is_flushable_post(
173
+ $post, 'dbcache', $this->_config ) ) {
174
  return;
175
  }
176
 
224
  return $menu_items;
225
  }
226
 
227
+
228
+
229
  public function w3tc_usage_statistics_of_request( $storage ) {
230
  $o = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
231
  $o->w3tc_usage_statistics_of_request( $storage );
232
  }
233
 
234
+
235
+
236
  public function w3tc_usage_statistics_metrics( $metrics ) {
237
  return array_merge( $metrics, array(
238
+ 'dbcache_calls_total',
239
+ 'dbcache_calls_hits',
240
+ 'dbcache_flushes',
241
+ 'dbcache_time_ms'
242
+ ) );
243
+ }
244
+
245
+
246
+
247
+ public function w3tc_usage_statistics_sources( $sources ) {
248
+ $c = Dispatcher::config();
249
+ if ( $c->get_string( 'dbcache.engine' ) == 'apc' ) {
250
+ $sources['apc_servers']['dbcache'] = array(
251
+ 'name' => __( 'Database Cache', 'w3-total-cache' )
252
+ );
253
+ } elseif ( $c->get_string( 'dbcache.engine' ) == 'memcached' ) {
254
+ $sources['memcached_servers']['dbcache'] = array(
255
+ 'servers' => $c->get_array( 'dbcache.memcached.servers' ),
256
+ 'username' => $c->get_string( 'dbcache.memcached.username' ),
257
+ 'password' => $c->get_string( 'dbcache.memcached.password' ),
258
+ 'name' => __( 'Database Cache', 'w3-total-cache' )
259
+ );
260
+ } elseif ( $c->get_string( 'dbcache.engine' ) == 'redis' ) {
261
+ $sources['redis_servers']['dbcache'] = array(
262
+ 'servers' => $c->get_array( 'dbcache.redis.servers' ),
263
+ 'username' => $c->get_boolean( 'dbcache.redis.username' ),
264
+ 'dbid' => $c->get_integer( 'dbcache.redis.dbid' ),
265
+ 'password' => $c->get_string( 'dbcache.redis.password' ),
266
+ 'name' => __( 'Database Cache', 'w3-total-cache' )
267
+ );
268
+ }
269
+
270
+ return $sources;
271
  }
272
  }
DbCache_Plugin_Admin.php CHANGED
@@ -17,39 +17,29 @@ class DbCache_Plugin_Admin {
17
 
18
 
19
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
20
- // memcached servers
21
- $c = Dispatcher::config();
22
- if ( $c->get_string( 'dbcache.engine' ) == 'memcached' ) {
23
- $summary['memcached_servers']['dbcache'] = array(
24
- 'servers' => $c->get_array( 'dbcache.memcached.servers' ),
25
- 'username' => $c->get_string( 'dbcache.memcached.username' ),
26
- 'password' => $c->get_string( 'dbcache.memcached.password' ),
27
- 'name' => __( 'Database Cache', 'w3-total-cache' )
28
- );
29
- } elseif ( $c->get_string( 'dbcache.engine' ) == 'redis' ) {
30
- $summary['redis_servers']['dbcache'] = array(
31
- 'servers' => $c->get_array( 'dbcache.redis.servers' ),
32
- 'username' => $c->get_boolean( 'dbcache.redis.username' ),
33
- 'dbid' => $c->get_integer( 'dbcache.redis.dbid' ),
34
- 'password' => $c->get_string( 'dbcache.redis.password' ),
35
- 'name' => __( 'Database Cache', 'w3-total-cache' )
36
- );
37
- }
38
-
39
-
40
  // counters
41
  $dbcache_calls_total = Util_UsageStatistics::sum( $history,
42
  'dbcache_calls_total' );
43
  $dbcache_calls_hits = Util_UsageStatistics::sum( $history,
44
  'dbcache_calls_hits' );
 
 
 
 
 
 
 
45
 
46
  $summary['dbcache'] = array(
47
  'calls_total' => Util_UsageStatistics::integer(
48
  $dbcache_calls_total ),
49
  'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
50
  $dbcache_calls_total, $summary ),
 
 
51
  'hit_rate' => Util_UsageStatistics::percent(
52
- $dbcache_calls_total, $dbcache_calls_total )
 
53
  );
54
 
55
  return $summary;
17
 
18
 
19
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  // counters
21
  $dbcache_calls_total = Util_UsageStatistics::sum( $history,
22
  'dbcache_calls_total' );
23
  $dbcache_calls_hits = Util_UsageStatistics::sum( $history,
24
  'dbcache_calls_hits' );
25
+ $dbcache_flushes = Util_UsageStatistics::sum( $history,
26
+ 'dbcache_flushes' );
27
+ $dbcache_time_ms = Util_UsageStatistics::sum( $history,
28
+ 'dbcache_time_ms' );
29
+
30
+ $c = Dispatcher::config();
31
+ $e = $c->get_string( 'dbcache.engine' );
32
 
33
  $summary['dbcache'] = array(
34
  'calls_total' => Util_UsageStatistics::integer(
35
  $dbcache_calls_total ),
36
  'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
37
  $dbcache_calls_total, $summary ),
38
+ 'flushes' => Util_UsageStatistics::integer( $dbcache_flushes ),
39
+ 'time_ms' => Util_UsageStatistics::integer( $dbcache_time_ms ),
40
  'hit_rate' => Util_UsageStatistics::percent(
41
+ $dbcache_calls_hits, $dbcache_calls_total ),
42
+ 'engine_name' => Cache::engine_name( $e )
43
  );
44
 
45
  return $summary;
DbCache_WpdbInjection_QueryCaching.php CHANGED
@@ -5,13 +5,6 @@ namespace W3TC;
5
  * class DbCache_WpdbInjection_QueryCaching
6
  */
7
  class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
8
- /**
9
- * Array of queries
10
- *
11
- * @var array
12
- */
13
- var $query_stats = array();
14
-
15
  /**
16
  * Queries total
17
  *
@@ -32,9 +25,13 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
32
  * @var integer
33
  */
34
  var $query_misses = 0;
 
 
 
 
35
 
36
  /**
37
- * Time total
38
  *
39
  * @var integer
40
  */
@@ -71,6 +68,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
71
  private $reject_logged = false;
72
  private $reject_constants;
73
  private $use_filters;
 
74
 
75
  /**
76
  * Result of check if caching is possible at the level of current http request
@@ -105,8 +103,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
105
  return $this->next_injection->query( $query );
106
  }
107
 
108
- $reason = '';
109
- $cached = false;
110
  $data = false;
111
  $time_total = 0;
112
  $group = '';
@@ -114,25 +112,25 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
114
 
115
  $this->query_total++;
116
 
117
- $caching = $this->_can_cache( $query, $reason );
118
  if ( preg_match( '~^\s*start transaction\b~is', $query ) ) {
119
  $this->cache_reject_reason = 'transaction';
120
- $reason = $this->cache_reject_reason;
121
  $caching = false;
122
  }
123
 
124
  if ( preg_match( '~^\s*insert\b|^\s*delete\b|^\s*update\b|^\s*replace\b|^\s*commit\b|^\s*truncate\b|^\s*drop\b|^\s*create\b~is', $query ) ) {
125
  $this->cache_reject_reason = 'modification query';
126
- $reason = $this->cache_reject_reason;
127
  $caching = false;
128
  $flush_after_query = true;
129
  }
130
 
131
  if ( $this->use_filters && function_exists( 'apply_filters' ) ) {
132
- $reason = apply_filters( 'w3tc_dbcache_can_cache_sql',
133
- ( $caching ? '' : $reason ), $query );
134
 
135
- $caching = empty( $reason );
136
  }
137
 
138
  if ( $caching ) {
@@ -146,7 +144,7 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
146
  }
147
 
148
  if ( is_array( $data ) ) {
149
- $cached = true;
150
  $this->query_hits++;
151
 
152
  $this->wpdb_mixin->last_error = $data['last_error'];
@@ -202,15 +200,16 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
202
  }
203
 
204
  if ( $this->debug ) {
205
- $this->query_stats[] = array(
206
- 'query' => $query,
207
- 'group' => $group,
208
- 'caching' => $caching,
209
- 'reason' => $reason,
210
- 'cached' => $cached,
211
- 'data_size' => ( $data ? strlen( serialize( $data ) ) : 0 ),
212
- 'time_total' => $time_total
213
- );
 
214
  }
215
 
216
  $this->time_total += $time_total;
@@ -295,6 +294,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
295
  }
296
 
297
  private function _flush_cache_for_sql_group( $group, $extras = array() ) {
 
 
298
  if ( $this->debug ) {
299
  $filename = Util_Debug::log( 'dbcache',
300
  'flushing based on sqlquery group ' . $group .
@@ -305,6 +306,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
305
  $flush_groups = $this->_get_flush_groups( $group, $extras );
306
  $v = true;
307
 
 
 
308
  foreach ( $flush_groups as $f_group => $nothing ) {
309
  if ( $this->debug ) {
310
  $filename = Util_Debug::log( 'dbcache', 'flush group ' . $f_group );
@@ -312,6 +315,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
312
  $v &= $cache->flush( $f_group );
313
  }
314
 
 
 
315
  return $v;
316
  }
317
 
@@ -710,8 +715,8 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
710
  }
711
 
712
  public function w3tc_footer_comment( $strings ) {
713
- $reason = $this->get_reject_reason();
714
- $append = ( $reason ? sprintf( ' (%s)', $reason ) : '' );
715
 
716
  if ( $this->query_hits ) {
717
  $strings[] = sprintf(
@@ -732,38 +737,29 @@ class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
732
  $strings[] = sprintf( "%s%d", str_pad( 'Total queries: ', 20 ), $this->query_total );
733
  $strings[] = sprintf( "%s%d", str_pad( 'Cached queries: ', 20 ), $this->query_hits );
734
  $strings[] = sprintf( "%s%.4f", str_pad( 'Total query time: ', 20 ), $this->time_total );
735
-
736
- if ( count( $this->query_stats ) ) {
737
- $strings[] = "SQL info:";
738
- $strings[] = sprintf( "%s | %s | %s | % s | %s | %s | %s",
739
- str_pad( '#', 5, ' ', STR_PAD_LEFT ), str_pad( 'Time (s)', 8, ' ', STR_PAD_LEFT ),
740
- str_pad( 'Caching (Reject reason)', 30, ' ', STR_PAD_BOTH ),
741
- str_pad( 'Status', 10, ' ', STR_PAD_BOTH ),
742
- str_pad( 'Data size (b)', 13, ' ', STR_PAD_LEFT ),
743
- str_pad( 'Group', 10, ' ', STR_PAD_BOTH ),
744
- 'Query' );
745
-
746
- foreach ( $this->query_stats as $index => $query ) {
747
- $strings[] = sprintf( "%s | %s | %s | %s | %s | %s | %s",
748
- str_pad( $index + 1, 5, ' ', STR_PAD_LEFT ),
749
- str_pad( round( $query['time_total'], 4 ), 8, ' ', STR_PAD_LEFT ),
750
- str_pad( ( $query['caching'] ? 'enabled'
751
- : sprintf( 'disabled (%s)', $query['reason'] ) ), 30, ' ', STR_PAD_BOTH ),
752
- str_pad( ( $query['cached'] ? 'cached' : 'not cached' ), 10, ' ', STR_PAD_BOTH ),
753
- str_pad( $query['data_size'], 13, ' ', STR_PAD_LEFT ),
754
- str_pad( $query['group'], 10, ' ', STR_PAD_LEFT ),
755
- trim( $query['query'] ) );
756
- }
757
- }
758
-
759
- $strings[] = '';
760
  }
761
 
 
 
 
 
762
  return $strings;
763
  }
764
 
765
  public function w3tc_usage_statistics_of_request( $storage ) {
766
  $storage->counter_add( 'dbcache_calls_total', $this->query_total );
767
  $storage->counter_add( 'dbcache_calls_hits', $this->query_hits );
 
 
 
 
 
 
 
 
 
 
 
 
768
  }
769
  }
5
  * class DbCache_WpdbInjection_QueryCaching
6
  */
7
  class DbCache_WpdbInjection_QueryCaching extends DbCache_WpdbInjection {
 
 
 
 
 
 
 
8
  /**
9
  * Queries total
10
  *
25
  * @var integer
26
  */
27
  var $query_misses = 0;
28
+ /**
29
+ * Number of cache flushes during http request processing
30
+ */
31
+ private $cache_flushes = 0;
32
 
33
  /**
34
+ * Time total taken by queries, in microsecs
35
  *
36
  * @var integer
37
  */
68
  private $reject_logged = false;
69
  private $reject_constants;
70
  private $use_filters;
71
+ private $log_filehandle = false;
72
 
73
  /**
74
  * Result of check if caching is possible at the level of current http request
103
  return $this->next_injection->query( $query );
104
  }
105
 
106
+ $reject_reason = '';
107
+ $is_cache_hit = false;
108
  $data = false;
109
  $time_total = 0;
110
  $group = '';
112
 
113
  $this->query_total++;
114
 
115
+ $caching = $this->_can_cache( $query, $reject_reason );
116
  if ( preg_match( '~^\s*start transaction\b~is', $query ) ) {
117
  $this->cache_reject_reason = 'transaction';
118
+ $reject_reason = $this->cache_reject_reason;
119
  $caching = false;
120
  }
121
 
122
  if ( preg_match( '~^\s*insert\b|^\s*delete\b|^\s*update\b|^\s*replace\b|^\s*commit\b|^\s*truncate\b|^\s*drop\b|^\s*create\b~is', $query ) ) {
123
  $this->cache_reject_reason = 'modification query';
124
+ $reject_reason = $this->cache_reject_reason;
125
  $caching = false;
126
  $flush_after_query = true;
127
  }
128
 
129
  if ( $this->use_filters && function_exists( 'apply_filters' ) ) {
130
+ $reject_reason = apply_filters( 'w3tc_dbcache_can_cache_sql',
131
+ ( $caching ? '' : $reject_reason ), $query );
132
 
133
+ $caching = empty( $reject_reason );
134
  }
135
 
136
  if ( $caching ) {
144
  }
145
 
146
  if ( is_array( $data ) ) {
147
+ $is_cache_hit = true;
148
  $this->query_hits++;
149
 
150
  $this->wpdb_mixin->last_error = $data['last_error'];
200
  }
201
 
202
  if ( $this->debug ) {
203
+ $this->log_query( array(
204
+ date( 'r' ),
205
+ strtr( $_SERVER['REQUEST_URI'], "<>\r\n", '.. ' ),
206
+ strtr( $query, "<>\r\n", '.. ' ), // 'query'
207
+ (int)($time_total * 1000000), // 'time_total' (microsecs)
208
+ $reject_reason, // 'reason'
209
+ $is_cache_hit, // 'cached'
210
+ ( $data ? strlen( serialize( $data ) ) : 0 ), // 'data_size'
211
+ strtr( $group, "<>\r\n", '.. ' ) // 'group'
212
+ ) );
213
  }
214
 
215
  $this->time_total += $time_total;
294
  }
295
 
296
  private function _flush_cache_for_sql_group( $group, $extras = array() ) {
297
+ $this->wpdb_mixin->timer_start();
298
+
299
  if ( $this->debug ) {
300
  $filename = Util_Debug::log( 'dbcache',
301
  'flushing based on sqlquery group ' . $group .
306
  $flush_groups = $this->_get_flush_groups( $group, $extras );
307
  $v = true;
308
 
309
+ $this->cache_flushes++;
310
+
311
  foreach ( $flush_groups as $f_group => $nothing ) {
312
  if ( $this->debug ) {
313
  $filename = Util_Debug::log( 'dbcache', 'flush group ' . $f_group );
315
  $v &= $cache->flush( $f_group );
316
  }
317
 
318
+ $this->time_total += $this->wpdb_mixin->timer_stop();
319
+
320
  return $v;
321
  }
322
 
715
  }
716
 
717
  public function w3tc_footer_comment( $strings ) {
718
+ $reject_reason = $this->get_reject_reason();
719
+ $append = ( $reject_reason ? sprintf( ' (%s)', $reject_reason ) : '' );
720
 
721
  if ( $this->query_hits ) {
722
  $strings[] = sprintf(
737
  $strings[] = sprintf( "%s%d", str_pad( 'Total queries: ', 20 ), $this->query_total );
738
  $strings[] = sprintf( "%s%d", str_pad( 'Cached queries: ', 20 ), $this->query_hits );
739
  $strings[] = sprintf( "%s%.4f", str_pad( 'Total query time: ', 20 ), $this->time_total );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740
  }
741
 
742
+ if ( $this->log_filehandle ) {
743
+ fclose( $this->log_filehandle );
744
+ $this->log_filehandle = false;
745
+ }
746
  return $strings;
747
  }
748
 
749
  public function w3tc_usage_statistics_of_request( $storage ) {
750
  $storage->counter_add( 'dbcache_calls_total', $this->query_total );
751
  $storage->counter_add( 'dbcache_calls_hits', $this->query_hits );
752
+ $storage->counter_add( 'dbcache_flushes', $this->cache_flushes );
753
+ $time_ms = (int)( $this->time_total * 1000 );
754
+ $storage->counter_add( 'dbcache_time_ms', $time_ms );
755
+ }
756
+
757
+ private function log_query( $line ) {
758
+ if ( !$this->log_filehandle ) {
759
+ $filename = Util_Debug::log_filename( 'dbcache-queries' );
760
+ $this->log_filehandle = fopen( $filename, 'a' );
761
+ }
762
+
763
+ fputcsv ( $this->log_filehandle, $line, "\t" );
764
  }
765
  }
Dispatcher.php CHANGED
@@ -250,20 +250,25 @@ class Dispatcher {
250
  static $cache = null;
251
  if ( is_null( $cache ) ) {
252
  $c = Dispatcher::config();
253
- if ( $c->get_boolean( 'objectcache.enabled' ) )
 
254
  $provider = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
255
- else if ( $c->get_boolean( 'dbcache.enabled' ) )
256
- $provider = Dispatcher::component( 'DbCache_Core' );
257
- else if ( $c->get_boolean( 'pgcache.enabled' ) )
258
- $provider = Dispatcher::component( 'PgCache_ContentGrabber' );
259
- else if ( $c->get_boolean( 'minify.enabled' ) )
260
- $provider = Dispatcher::component( 'Minify_Core' );
261
- else
262
- return null;
263
-
264
- $engineConfig = $provider->get_usage_statistics_cache_config();
265
- $engineConfig['module'] = 'stats';
266
- $engineConfig['blog_id'] = 0; // count wpmu-wide stats
 
 
 
 
267
 
268
  if ( $engineConfig['engine'] == 'file' ) {
269
  $engineConfig['cache_dir'] = Util_Environment::cache_dir( 'stats' );
@@ -285,8 +290,9 @@ class Dispatcher {
285
  static public function usage_statistics_apply_before_init_and_exit(
286
  $metrics_function ) {
287
  $c = Dispatcher::config();
288
- if ( !$c->get_boolean( 'stats.enabled' ) )
289
  exit();
 
290
 
291
  $core = Dispatcher::component( 'UsageStatistics_Core' );
292
  $core->apply_metrics_before_init_and_exit( $metrics_function );
250
  static $cache = null;
251
  if ( is_null( $cache ) ) {
252
  $c = Dispatcher::config();
253
+ $engineConfig = null;
254
+ if ( $c->get_boolean( 'objectcache.enabled' ) ) {
255
  $provider = Dispatcher::component( 'ObjectCache_WpObjectCache_Regular' );
256
+ } else if ( $c->get_boolean( 'dbcache.enabled' ) ) {
257
+ $provider = Dispatcher::component( 'DbCache_Core' );
258
+ } else if ( $c->get_boolean( 'pgcache.enabled' ) ) {
259
+ $provider = Dispatcher::component( 'PgCache_ContentGrabber' );
260
+ } else if ( $c->get_boolean( 'minify.enabled' ) ) {
261
+ $provider = Dispatcher::component( 'Minify_Core' );
262
+ } else {
263
+ $engineConfig = array( 'engine' => 'file' );
264
+ }
265
+
266
+ if ( is_null( $engineConfig ) ) {
267
+ $engineConfig = $provider->get_usage_statistics_cache_config();
268
+ }
269
+
270
+ $engineConfig['module'] = 'stats';
271
+ $engineConfig['blog_id'] = 0; // count wpmu-wide stats
272
 
273
  if ( $engineConfig['engine'] == 'file' ) {
274
  $engineConfig['cache_dir'] = Util_Environment::cache_dir( 'stats' );
290
  static public function usage_statistics_apply_before_init_and_exit(
291
  $metrics_function ) {
292
  $c = Dispatcher::config();
293
+ if ( !$c->get_boolean( 'stats.enabled' ) ) {
294
  exit();
295
+ }
296
 
297
  $core = Dispatcher::component( 'UsageStatistics_Core' );
298
  $core->apply_metrics_before_init_and_exit( $metrics_function );
Extension_FragmentCache_Plugin_Admin.php CHANGED
@@ -13,7 +13,7 @@ class Extension_FragmentCache_Plugin_Admin {
13
  static public function w3tc_extensions( $extensions, $config ) {
14
  $requirements = array();
15
  if ( !Util_Environment::is_w3tc_pro( $config ) )
16
- $requirements[] = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>';
17
 
18
  $extensions['fragmentcache'] = array (
19
  'name' => 'Fragment Cache',
@@ -71,8 +71,9 @@ class Extension_FragmentCache_Plugin_Admin {
71
 
72
 
73
  public function w3tc_objectcache_addin_required( $addin_required ) {
74
- if ( $this->_config->is_extension_active_frontend( 'fragmentcache' ) )
75
  return true;
 
76
 
77
  return $addin_required;
78
  }
@@ -142,6 +143,10 @@ class Extension_FragmentCache_Plugin_Admin {
142
 
143
 
144
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
 
 
 
 
145
  // memcached servers
146
  $c = Dispatcher::config();
147
  if ( $c->get_string( array( 'fragmentcache', 'engine' ) ) == 'memcached' ) {
13
  static public function w3tc_extensions( $extensions, $config ) {
14
  $requirements = array();
15
  if ( !Util_Environment::is_w3tc_pro( $config ) )
16
+ $requirements[] = 'Available after <a href="#" class="button-buy-plugin" data-src="fc_requirements">upgrade</a>';
17
 
18
  $extensions['fragmentcache'] = array (
19
  'name' => 'Fragment Cache',
71
 
72
 
73
  public function w3tc_objectcache_addin_required( $addin_required ) {
74
+ if ( $this->_config->is_extension_active_frontend( 'fragmentcache' ) ) {
75
  return true;
76
+ }
77
 
78
  return $addin_required;
79
  }
143
 
144
 
145
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
146
+ if ( !$this->_config->is_extension_active_frontend( 'fragmentcache' ) ) {
147
+ return $summary;
148
+ }
149
+
150
  // memcached servers
151
  $c = Dispatcher::config();
152
  if ( $c->get_string( array( 'fragmentcache', 'engine' ) ) == 'memcached' ) {
Extension_FragmentCache_WpObjectCache.php CHANGED
@@ -427,50 +427,6 @@ class Extension_FragmentCache_WpObjectCache {
427
  return $value;
428
  }
429
 
430
- /**
431
- * Print Fragment Cache stats
432
- *
433
- * @return void
434
- */
435
- function stats() {
436
- echo '<h2>Summary</h2>';
437
- echo '<p>';
438
- echo '<strong>Engine</strong>: ' . Cache::engine_name( $this->_config->get_string( array( 'fragmentcache', 'engine' ) ) ) . '<br />';
439
- echo '<strong>Caching</strong>: ' . ( $this->_caching ? 'enabled' : 'disabled' ) . '<br />';
440
-
441
- if ( !$this->_caching ) {
442
- echo '<strong>Reject reason</strong>: ' . $this->cache_reject_reason . '<br />';
443
- }
444
-
445
- echo '<strong>Total calls</strong>: ' . $this->cache_total . '<br />';
446
- echo '<strong>Cache hits</strong>: ' . $this->cache_hits . '<br />';
447
- echo '<strong>Cache misses</strong>: ' . $this->cache_misses . '<br />';
448
- echo '<strong>Total time</strong>: '. round( $this->time_total, 4 ) . 's';
449
- echo '</p>';
450
-
451
- echo '<h2>Cache info</h2>';
452
-
453
- if ( $this->_debug ) {
454
- echo '<table cellpadding="0" cellspacing="3" border="1">';
455
- echo '<tr><td>#</td><td>Status</td><td>Source</td><td>Data size (b)</td><td>Query time (s)</td><td>ID:Group</td></tr>';
456
-
457
- foreach ( $this->debug_info as $index => $debug ) {
458
- echo '<tr>';
459
- echo '<td>' . ( $index + 1 ) . '</td>';
460
- echo '<td>' . ( $debug['cached'] ? 'cached' : 'not cached' ) . '</td>';
461
- echo '<td>' . ( $debug['internal'] ? 'internal' : 'persistent' ) . '</td>';
462
- echo '<td>' . $debug['data_size'] . '</td>';
463
- echo '<td>' . round( $debug['time'], 4 ) . '</td>';
464
- echo '<td>' . sprintf( '%s:%s', $debug['id'], $debug['group'] ) . '</td>';
465
- echo '</tr>';
466
- }
467
-
468
- echo '</table>';
469
- } else {
470
- echo '<p>Enable debug mode.</p>';
471
- }
472
- }
473
-
474
  /**
475
  * Switches context to another blog
476
  *
427
  return $value;
428
  }
429
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  /**
431
  * Switches context to another blog
432
  *
Extension_Genesis_Plugin_Admin.php CHANGED
@@ -30,7 +30,7 @@ class Extension_Genesis_Plugin_Admin {
30
  'Optimizes "Genesis Framework" version >= 1.9.0, which is not active';
31
 
32
  if ( empty( $requirements ) && !Util_Environment::is_w3tc_pro( $config ) )
33
- $requirements[] = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>';
34
 
35
  if ( !$config->is_extension_active( 'fragmentcache' ) )
36
  $requirements[] = 'Activate "Fragment Cache" extension first';
30
  'Optimizes "Genesis Framework" version >= 1.9.0, which is not active';
31
 
32
  if ( empty( $requirements ) && !Util_Environment::is_w3tc_pro( $config ) )
33
+ $requirements[] = 'Available after <a href="#" class="button-buy-plugin" data-src="genesis_requirements">upgrade</a>';
34
 
35
  if ( !$config->is_extension_active( 'fragmentcache' ) )
36
  $requirements[] = 'Activate "Fragment Cache" extension first';
Extension_Wpml_Plugin_Admin.php CHANGED
@@ -42,8 +42,8 @@ class Extension_Wpml_Plugin_Admin {
42
  $requirements[] = 'Ensure "WPML" plugin compatibility, which is not currently active.';
43
  if ( empty( $requirements ) && !Util_Environment::is_w3tc_pro( $config ) ) {
44
  $enabled = false;
45
- $requirements[] = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>';
46
- $disabled_message = '<a href="#" class="button-buy-plugin">upgrade</a>';
47
  }
48
 
49
  $extensions['wpml'] = array(
@@ -108,7 +108,7 @@ class Extension_Wpml_Plugin_Admin {
108
 
109
  $config = Dispatcher::config();
110
  if ( !Util_Environment::is_w3tc_pro( $config ) )
111
- $activate_text = 'Available after <a href="#" class="button-buy-plugin">upgrade</a>. ';
112
  else {
113
  $activate_text = sprintf( '<a class="button" href="%s">Click here</a> to try it. ',
114
  Util_Ui::url( array( 'w3tc_extensions_activate' => $extension_id ) ) );
42
  $requirements[] = 'Ensure "WPML" plugin compatibility, which is not currently active.';
43
  if ( empty( $requirements ) && !Util_Environment::is_w3tc_pro( $config ) ) {
44
  $enabled = false;
45
+ $requirements[] = 'Available after <a href="#" class="button-buy-plugin" data-src="wpml_requirements">upgrade</a>';
46
+ $disabled_message = '<a href="#" class="button-buy-plugin" data-src="wpml_requirements2">upgrade</a>';
47
  }
48
 
49
  $extensions['wpml'] = array(
108
 
109
  $config = Dispatcher::config();
110
  if ( !Util_Environment::is_w3tc_pro( $config ) )
111
+ $activate_text = 'Available after <a href="#" class="button-buy-plugin" data-src="wpml_requirements3">upgrade</a>. ';
112
  else {
113
  $activate_text = sprintf( '<a class="button" href="%s">Click here</a> to try it. ',
114
  Util_Ui::url( array( 'w3tc_extensions_activate' => $extension_id ) ) );
Generic_AdminActions_Default.php CHANGED
@@ -268,7 +268,7 @@ class Generic_AdminActions_Default {
268
  */
269
  if ( $this->_page == 'w3tc_minify' ) {
270
  if ( ( $this->_config->get_boolean( 'minify.js.http2push' ) && ! $config->get_boolean( 'minify.js.http2push' ) ) ||
271
- ( $this->_config->get_boolean( 'minify.css.http2push' ) && ! $config->get_boolean( 'minify.css.http2push' ) ) ) {
272
  if ( $config->get_string( 'pgcache.engine' ) == 'file_generic' ) {
273
  $cache_dir = Util_Environment::cache_blog_dir( 'page_enhanced' );
274
  $this->_deleteAllHtaccessFiles( $cache_dir );
@@ -807,13 +807,18 @@ class Generic_AdminActions_Default {
807
  $config->set( $key, $request_value );
808
  } elseif ( array_key_exists( $key, $keys ) ) {
809
  $descriptor = $keys[$key];
810
- if ( isset( $descriptor['type'] ) &&
811
- $descriptor['type'] == 'array' ) {
812
- if ( is_array( $request_value ) ) {
813
- $request_value = implode( "\n", $request_value );
 
 
 
 
 
 
 
814
  }
815
- $request_value = explode( "\n",
816
- str_replace( "\r\n", "\n", $request_value ) );
817
  }
818
 
819
  $config->set( $key, $request_value );
268
  */
269
  if ( $this->_page == 'w3tc_minify' ) {
270
  if ( ( $this->_config->get_boolean( 'minify.js.http2push' ) && ! $config->get_boolean( 'minify.js.http2push' ) ) ||
271
+ ( $this->_config->get_boolean( 'minify.css.http2push' ) && ! $config->get_boolean( 'minify.css.http2push' ) ) ) {
272
  if ( $config->get_string( 'pgcache.engine' ) == 'file_generic' ) {
273
  $cache_dir = Util_Environment::cache_blog_dir( 'page_enhanced' );
274
  $this->_deleteAllHtaccessFiles( $cache_dir );
807
  $config->set( $key, $request_value );
808
  } elseif ( array_key_exists( $key, $keys ) ) {
809
  $descriptor = $keys[$key];
810
+ if ( isset( $descriptor['type'] ) ) {
811
+ if ( $descriptor['type'] == 'array' ) {
812
+ if ( is_array( $request_value ) ) {
813
+ $request_value = implode( "\n", $request_value );
814
+ }
815
+ $request_value = explode( "\n",
816
+ str_replace( "\r\n", "\n", $request_value ) );
817
+ } elseif ( $descriptor['type'] == 'boolean' ) {
818
+ $request_value = ( $request_value == '1' );
819
+ } elseif ( $descriptor['type'] == 'integer' ) {
820
+ $request_value = (int)$request_value;
821
  }
 
 
822
  }
823
 
824
  $config->set( $key, $request_value );
Generic_AdminNotes.php CHANGED
@@ -168,6 +168,25 @@ class Generic_AdminNotes {
168
  }
169
  }
170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  return $notes;
172
  }
173
 
168
  }
169
  }
170
 
171
+ $is_debug = $c->get_boolean( 'cluster.messagebus.debug' ) ||
172
+ $c->get_boolean( 'dbcache.debug' ) ||
173
+ $c->get_boolean( 'objectcache.debug' ) ||
174
+ $c->get_boolean( 'pgcache.debug' ) ||
175
+ $c->get_boolean( 'minify.debug' ) ||
176
+ $c->get_boolean( 'cdn.debug' ) ||
177
+ $c->get_boolean( 'cdnfsd.debug' ) ||
178
+ $c->get_boolean( 'varnish.debug' );
179
+
180
+ if ( $is_debug && !$state_master->get_boolean( 'common.hide_note_debug_enabled' ) ) {
181
+ $notes['debug_enabled'] = sprintf(
182
+ __( 'You’re running debug mode, it’s using Resources and not recommend to run continuously. %s',
183
+ 'w3-total-cache' ),
184
+ Util_Ui::button_hide_note2( array(
185
+ 'w3tc_default_config_state_master' => 'y',
186
+ 'key' => 'common.hide_note_debug_enabled',
187
+ 'value' => 'true' ) ) );
188
+ }
189
+
190
  return $notes;
191
  }
192
 
Generic_Plugin.php CHANGED
@@ -5,11 +5,8 @@ namespace W3TC;
5
  * W3 Total Cache plugin
6
  */
7
  class Generic_Plugin {
8
-
9
  private $_translations = array();
10
- /**
11
- * Config
12
- */
13
  private $_config = null;
14
 
15
  function __construct() {
@@ -86,6 +83,9 @@ class Generic_Plugin {
86
  }
87
 
88
  if ( $this->can_ob() ) {
 
 
 
89
  ob_start( array(
90
  $this,
91
  'ob_callback'
@@ -93,6 +93,14 @@ class Generic_Plugin {
93
  }
94
  }
95
 
 
 
 
 
 
 
 
 
96
  /**
97
  * Cron schedules filter
98
  *
@@ -204,10 +212,10 @@ class Generic_Plugin {
204
  // so need to redirect to something a bit different
205
  if ( $do_redirect ) {
206
  if ( strpos( $_SERVER['REQUEST_URI'], '?' ) === false )
207
- Util_Environment::redirect_temp( $_SERVER['REQUEST_URI'] . '?repeat=w3tc' );
208
  else {
209
  if ( strpos( $_SERVER['REQUEST_URI'], 'repeat=w3tc' ) === false )
210
- Util_Environment::redirect_temp( $_SERVER['REQUEST_URI'] . '&repeat=w3tc' );
211
  }
212
  }
213
  }
@@ -509,7 +517,7 @@ class Generic_Plugin {
509
  return $buffer;
510
  }
511
 
512
- if ( Util_Content::is_database_error( $buffer ) ) {
513
  status_header( 503 );
514
  } else {
515
  $buffer = apply_filters( 'w3tc_process_content', $buffer );
5
  * W3 Total Cache plugin
6
  */
7
  class Generic_Plugin {
8
+ private $is_wp_die = false;
9
  private $_translations = array();
 
 
 
10
  private $_config = null;
11
 
12
  function __construct() {
83
  }
84
 
85
  if ( $this->can_ob() ) {
86
+ add_filter( 'wp_die_xml_handler', array( $this, 'wp_die_handler' ) );
87
+ add_filter( 'wp_die_handler', array( $this, 'wp_die_handler' ) );
88
+
89
  ob_start( array(
90
  $this,
91
  'ob_callback'
93
  }
94
  }
95
 
96
+ /**
97
+ * Marks wp_die was called so response is system message
98
+ **/
99
+ public function wp_die_handler( $v ) {
100
+ $this->is_wp_die = true;
101
+ return $v;
102
+ }
103
+
104
  /**
105
  * Cron schedules filter
106
  *
212
  // so need to redirect to something a bit different
213
  if ( $do_redirect ) {
214
  if ( strpos( $_SERVER['REQUEST_URI'], '?' ) === false )
215
+ Util_Environment::safe_redirect_temp( $_SERVER['REQUEST_URI'] . '?repeat=w3tc' );
216
  else {
217
  if ( strpos( $_SERVER['REQUEST_URI'], 'repeat=w3tc' ) === false )
218
+ Util_Environment::safe_redirect_temp( $_SERVER['REQUEST_URI'] . '&repeat=w3tc' );
219
  }
220
  }
221
  }
517
  return $buffer;
518
  }
519
 
520
+ if ( $this->is_wp_die ) {
521
  status_header( 503 );
522
  } else {
523
  $buffer = apply_filters( 'w3tc_process_content', $buffer );
Generic_Plugin_Admin.php CHANGED
@@ -296,7 +296,7 @@ class Generic_Plugin_Admin {
296
 
297
  ?>
298
  <style type="text/css" media="screen">
299
- .toplevel_page_w3tc_dashboard .wp-menu-image:before{
300
  content:'\0041';
301
  top: 2px;
302
  font-family: 'w3tc';
296
 
297
  ?>
298
  <style type="text/css" media="screen">
299
+ li.toplevel_page_w3tc_dashboard .wp-menu-image:before{
300
  content:'\0041';
301
  top: 2px;
302
  font-family: 'w3tc';
Licensing_AdminActions.php CHANGED
@@ -17,9 +17,7 @@ class Licensing_AdminActions {
17
  * test action
18
  */
19
  function w3tc_licensing_buy_plugin() {
20
- $state = Dispatcher::config_state_master();
21
- $iframe_url = W3TC_PURCHASE_URL .
22
- '?install_date=' . esc_attr( $state->get_integer( 'common.install' ) );
23
 
24
  include W3TC_INC_DIR . '/lightbox/purchase.php';
25
  }
@@ -30,6 +28,11 @@ class Licensing_AdminActions {
30
  * Self test action
31
  */
32
  function w3tc_licensing_upgrade() {
 
 
 
 
 
33
  include W3TC_INC_DIR . '/lightbox/upgrade.php';
34
  }
35
 
17
  * test action
18
  */
19
  function w3tc_licensing_buy_plugin() {
20
+ $iframe_url = Licensing_Core::purchase_url();
 
 
21
 
22
  include W3TC_INC_DIR . '/lightbox/purchase.php';
23
  }
28
  * Self test action
29
  */
30
  function w3tc_licensing_upgrade() {
31
+ $data_src = '';
32
+ if ( isset( $_REQUEST['data_src'] ) ) {
33
+ $data_src = preg_replace( '/[^0-9a-z_]/', '', $_REQUEST['data_src'] );
34
+ }
35
+
36
  include W3TC_INC_DIR . '/lightbox/upgrade.php';
37
  }
38
 
Licensing_Core.php CHANGED
@@ -119,4 +119,12 @@ class Licensing_Core {
119
  // not called in this mode
120
  }
121
  }
 
 
 
 
 
 
 
 
122
  }
119
  // not called in this mode
120
  }
121
  }
122
+
123
+
124
+
125
+ static public function purchase_url() {
126
+ $state = Dispatcher::config_state_master();
127
+ return W3TC_PURCHASE_URL .
128
+ '?install_date=' . esc_attr( $state->get_integer( 'common.install' ) );
129
+ }
130
  }
Licensing_Plugin_Admin.php CHANGED
@@ -66,7 +66,7 @@ class Licensing_Plugin_Admin {
66
  ?>
67
  <script type="text/javascript">
68
  jQuery(function() {
69
- w3tc_lightbox_upgrade(w3tc_nonce);
70
  jQuery('#w3tc-license-instruction').show();
71
  });
72
  </script>
@@ -161,7 +161,7 @@ class Licensing_Plugin_Admin {
161
  } elseif ( $status == 'no_key' ) {
162
  } elseif ( $this->_status_is( $status, 'inactive.expired' ) ) {
163
  $message = sprintf( __( 'The W3 Total Cache license key has expired. Please renew it: %s', 'w3-total-cache' ),
164
- '<input type="button" class="button-primary button-buy-plugin {nonce: \''. wp_create_nonce( 'w3tc' ).'\'}" value="'.__( 'Renew', 'w3-total-cache' ) . '" />' );
165
  } elseif ( $this->_status_is( $status, 'invalid' ) ) {
166
  $message = __( 'The W3 Total Cache license key you entered is not valid.', 'w3-total-cache' ) .
167
  '<a href="' . ( is_network_admin() ? network_admin_url( 'admin.php?page=w3tc_general#licensing' ):
66
  ?>
67
  <script type="text/javascript">
68
  jQuery(function() {
69
+ w3tc_lightbox_upgrade(w3tc_nonce, 'test');
70
  jQuery('#w3tc-license-instruction').show();
71
  });
72
  </script>
161
  } elseif ( $status == 'no_key' ) {
162
  } elseif ( $this->_status_is( $status, 'inactive.expired' ) ) {
163
  $message = sprintf( __( 'The W3 Total Cache license key has expired. Please renew it: %s', 'w3-total-cache' ),
164
+ '<input type="button" class="button-primary button-buy-plugin {nonce: \''. wp_create_nonce( 'w3tc' ).'\'}" data-src="licensing_expired" value="'.__( 'Renew', 'w3-total-cache' ) . '" />' );
165
  } elseif ( $this->_status_is( $status, 'invalid' ) ) {
166
  $message = __( 'The W3 Total Cache license key you entered is not valid.', 'w3-total-cache' ) .
167
  '<a href="' . ( is_network_admin() ? network_admin_url( 'admin.php?page=w3tc_general#licensing' ):
Minify_AutoJs.php CHANGED
@@ -210,8 +210,9 @@ class Minify_AutoJs {
210
 
211
  $step1 = !empty( $step1_result );
212
  $step2 = !in_array( $file, $this->ignore_js_files );
 
213
 
214
- $do_tag_minification = $step1 && $step2;
215
  $do_tag_minification = apply_filters( 'w3tc_minify_js_do_tag_minification',
216
  $do_tag_minification, $script_tag, $file );
217
 
210
 
211
  $step1 = !empty( $step1_result );
212
  $step2 = !in_array( $file, $this->ignore_js_files );
213
+ $step3 = !preg_match( '~\s+(async|defer)[> ]~is', $script_tag );
214
 
215
+ $do_tag_minification = $step1 && $step2 && $step3;
216
  $do_tag_minification = apply_filters( 'w3tc_minify_js_do_tag_minification',
217
  $do_tag_minification, $script_tag, $file );
218
 
Minify_Core.php CHANGED
@@ -70,11 +70,11 @@ class Minify_Core {
70
  continue;
71
 
72
  if ( !$external_regexp &&
73
- preg_match( '~^' . Util_Environment::get_url_regexp( $ext ) . '~', $file ) &&
74
  !$verified ) {
75
  $verified = true;
76
  }
77
- if ( $external_regexp &&
78
  preg_match( '~' . $ext . '~', $file ) && !$verified ) {
79
  $verified = true;
80
  }
@@ -115,7 +115,7 @@ class Minify_Core {
115
  return Util_Environment::filename_to_url( $filename );
116
  }
117
 
118
- return network_site_url( '?w3tc_minify=' . $minify_filename );
119
  }
120
 
121
 
70
  continue;
71
 
72
  if ( !$external_regexp &&
73
+ preg_match( '~^' . Util_Environment::get_url_regexp( $ext ) . '~', $file ) &&
74
  !$verified ) {
75
  $verified = true;
76
  }
77
+ if ( $external_regexp &&
78
  preg_match( '~' . $ext . '~', $file ) && !$verified ) {
79
  $verified = true;
80
  }
115
  return Util_Environment::filename_to_url( $filename );
116
  }
117
 
118
+ return home_url( '?w3tc_minify=' . $minify_filename );
119
  }
120
 
121
 
Minify_Plugin.php CHANGED
@@ -894,15 +894,6 @@ class Minify_Plugin {
894
  * @return string
895
  */
896
  function can_minify2( $buffer ) {
897
- /**
898
- * Check for database error
899
- */
900
- if ( Util_Content::is_database_error( $buffer ) ) {
901
- $this->minify_reject_reason = 'Database Error occurred';
902
-
903
- return false;
904
- }
905
-
906
  /**
907
  * Check for DONOTMINIFY constant
908
  */
894
  * @return string
895
  */
896
  function can_minify2( $buffer ) {
 
 
 
 
 
 
 
 
 
897
  /**
898
  * Check for DONOTMINIFY constant
899
  */
ObjectCache_Plugin.php CHANGED
@@ -108,6 +108,9 @@ class ObjectCache_Plugin {
108
  $this, 'w3tc_usage_statistics_of_request' ), 10, 1 );
109
  add_filter( 'w3tc_usage_statistics_metrics', array(
110
  $this, 'w3tc_usage_statistics_metrics' ) );
 
 
 
111
 
112
  if ( Util_Environment::is_wpmu() ) {
113
  add_action( 'delete_blog', array(
@@ -289,8 +292,41 @@ class ObjectCache_Plugin {
289
  }
290
 
291
  public function w3tc_usage_statistics_metrics( $metrics ) {
292
- return array_merge( $metrics, array(
293
- 'objectcache_calls_total', 'objectcache_calls_hits' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  }
295
 
296
  /**
108
  $this, 'w3tc_usage_statistics_of_request' ), 10, 1 );
109
  add_filter( 'w3tc_usage_statistics_metrics', array(
110
  $this, 'w3tc_usage_statistics_metrics' ) );
111
+ add_filter( 'w3tc_usage_statistics_sources', array(
112
+ $this, 'w3tc_usage_statistics_sources' ) );
113
+
114
 
115
  if ( Util_Environment::is_wpmu() ) {
116
  add_action( 'delete_blog', array(
292
  }
293
 
294
  public function w3tc_usage_statistics_metrics( $metrics ) {
295
+ $metrics = array_merge( $metrics, array(
296
+ 'objectcache_get_total',
297
+ 'objectcache_get_hits',
298
+ 'objectcache_sets',
299
+ 'objectcache_flushes',
300
+ 'objectcache_time_ms'
301
+ ) );
302
+
303
+ return $metrics;
304
+ }
305
+
306
+ public function w3tc_usage_statistics_sources($sources) {
307
+ $c = Dispatcher::config();
308
+ if ( $c->get_string( 'objectcache.engine' ) == 'apc' ) {
309
+ $sources['apc_servers']['objectcache'] = array(
310
+ 'name' => __( 'Object Cache', 'w3-total-cache' )
311
+ );
312
+ } elseif ( $c->get_string( 'objectcache.engine' ) == 'memcached' ) {
313
+ $sources['memcached_servers']['objectcache'] = array(
314
+ 'servers' => $c->get_array( 'objectcache.memcached.servers' ),
315
+ 'username' => $c->get_string( 'objectcache.memcached.username' ),
316
+ 'password' => $c->get_string( 'objectcache.memcached.password' ),
317
+ 'name' => __( 'Object Cache', 'w3-total-cache' )
318
+ );
319
+ } elseif ( $c->get_string( 'objectcache.engine' ) == 'redis' ) {
320
+ $sources['redis_servers']['objectcache'] = array(
321
+ 'servers' => $c->get_array( 'objectcache.redis.servers' ),
322
+ 'username' => $c->get_boolean( 'objectcache.redis.username' ),
323
+ 'dbid' => $c->get_integer( 'objectcache.redis.dbid' ),
324
+ 'password' => $c->get_string( 'objectcache.redis.password' ),
325
+ 'name' => __( 'Object Cache', 'w3-total-cache' )
326
+ );
327
+ }
328
+
329
+ return $sources;
330
  }
331
 
332
  /**
ObjectCache_Plugin_Admin.php CHANGED
@@ -64,38 +64,27 @@ class ObjectCache_Plugin_Admin {
64
 
65
 
66
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
67
- // memcached servers
68
- $c = Dispatcher::config();
69
- if ( $c->get_string( 'objectcache.engine' ) == 'memcached' ) {
70
- $summary['memcached_servers']['objectcache'] = array(
71
- 'servers' => $c->get_array( 'objectcache.memcached.servers' ),
72
- 'username' => $c->get_string( 'objectcache.memcached.username' ),
73
- 'password' => $c->get_string( 'objectcache.memcached.password' ),
74
- 'name' => __( 'Object Cache', 'w3-total-cache' )
75
- );
76
- } elseif ( $c->get_string( 'objectcache.engine' ) == 'redis' ) {
77
- $summary['redis_servers']['objectcache'] = array(
78
- 'servers' => $c->get_array( 'objectcache.redis.servers' ),
79
- 'username' => $c->get_boolean( 'objectcache.redis.username' ),
80
- 'dbid' => $c->get_integer( 'objectcache.redis.dbid' ),
81
- 'password' => $c->get_string( 'objectcache.redis.password' ),
82
- 'name' => __( 'Object Cache', 'w3-total-cache' )
83
- );
84
- }
85
-
86
  // counters
87
- $objectcache_calls_total = Util_UsageStatistics::sum( $history,
88
- 'objectcache_calls_total' );
89
- $objectcache_calls_hits = Util_UsageStatistics::sum( $history,
90
- 'objectcache_calls_hits' );
 
 
91
 
92
  $summary['objectcache'] = array(
93
- 'calls_total' => Util_UsageStatistics::integer(
94
- $objectcache_calls_total ),
 
 
 
 
 
95
  'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
96
- $objectcache_calls_total, $summary ),
97
  'hit_rate' => Util_UsageStatistics::percent(
98
- $objectcache_calls_total, $objectcache_calls_total )
 
99
  );
100
 
101
  return $summary;
64
 
65
 
66
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  // counters
68
+ $get_total = Util_UsageStatistics::sum( $history, 'objectcache_get_total' );
69
+ $get_hits = Util_UsageStatistics::sum( $history, 'objectcache_get_hits' );
70
+ $sets = Util_UsageStatistics::sum( $history, 'objectcache_sets' );
71
+
72
+ $c = Dispatcher::config();
73
+ $e = $c->get_string( 'objectcache.engine' );
74
 
75
  $summary['objectcache'] = array(
76
+ 'get_total' => Util_UsageStatistics::integer( $get_total ),
77
+ 'get_hits' => Util_UsageStatistics::integer( $get_hits ),
78
+ 'sets' => Util_UsageStatistics::integer( $sets ),
79
+ 'flushes' => Util_UsageStatistics::integer(
80
+ Util_UsageStatistics::sum( $history, 'objectcache_flushes' ) ),
81
+ 'time_ms' => Util_UsageStatistics::integer(
82
+ Util_UsageStatistics::sum( $history, 'objectcache_time_ms' ) ),
83
  'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
84
+ $get_total + $sets, $summary ),
85
  'hit_rate' => Util_UsageStatistics::percent(
86
+ $get_hits, $get_total ),
87
+ 'engine_name' => Cache::engine_name( $e )
88
  );
89
 
90
  return $summary;
ObjectCache_WpObjectCache_Regular.php CHANGED
@@ -28,38 +28,27 @@ class ObjectCache_WpObjectCache_Regular {
28
 
29
  /**
30
  * Total count of calls
31
- *
32
- * @var integer
33
  */
34
  var $cache_total = 0;
35
 
36
  /**
37
  * Cache hits count
38
- *
39
- * @var integer
40
  */
41
  var $cache_hits = 0;
42
-
43
  /**
44
- * Cache misses count
45
- *
46
- * @var integer
47
  */
48
- var $cache_misses = 0;
 
49
 
50
  /**
51
- * Total time
52
  *
53
  * @var integer
54
  */
55
  var $time_total = 0;
56
 
57
- /**
58
- * Store debug information of w3tc using
59
- *
60
- * @var array
61
- */
62
- var $debug_info = array();
63
 
64
  /**
65
  * Blog id of cache
@@ -113,6 +102,7 @@ class ObjectCache_WpObjectCache_Regular {
113
  * @var boolean
114
  */
115
  var $_debug = false;
 
116
 
117
  /**
118
  * PHP5 style constructor
@@ -125,6 +115,7 @@ class ObjectCache_WpObjectCache_Regular {
125
  $this->global_groups = $this->_config->get_array( 'objectcache.groups.global' );
126
  $this->nonpersistent_groups = $this->_config->get_array(
127
  'objectcache.groups.nonpersistent' );
 
128
 
129
  $this->_blog_id = Util_Environment::blog_id();
130
  }
@@ -137,14 +128,21 @@ class ObjectCache_WpObjectCache_Regular {
137
  * @return mixed
138
  */
139
  function get( $id, $group = 'default', $force = false, &$found = null ) {
140
- if ( $this->_debug ) {
141
  $time_start = Util_Debug::microtime();
142
  }
143
 
 
 
 
 
144
  $key = $this->_get_cache_key( $id, $group );
145
  $in_incall_cache = isset( $this->cache[$key] );
146
  $fallback_used = false;
147
 
 
 
 
148
  if ( $in_incall_cache && !$force ) {
149
  $found = true;
150
  $value = $this->cache[$key];
@@ -162,16 +160,15 @@ class ObjectCache_WpObjectCache_Regular {
162
  json_encode($a);
163
  */
164
 
165
- $this->cache_total++;
166
 
167
  if ( is_array( $v ) && isset( $v['content'] ) ) {
168
  $found = true;
169
  $value = $v['content'];
170
- $this->cache_hits++;
171
  } else {
172
  $found = false;
173
  $value = false;
174
- $this->cache_misses++;
175
  }
176
  } else {
177
  $found = false;
@@ -203,39 +200,40 @@ class ObjectCache_WpObjectCache_Regular {
203
  /**
204
  * Add debug info
205
  */
206
- if ( $this->_debug ) {
207
  $time = Util_Debug::microtime() - $time_start;
208
  $this->time_total += $time;
 
 
209
 
210
- if ( !$group ) {
211
- $group = 'default';
212
- }
213
-
214
- if ( $fallback_used ) {
215
- if ( !$found )
216
- $returned = 'not in db';
217
- else
218
- $returned = 'from db fallback';
219
- } else {
220
- if ( !$found )
221
- $returned = 'not in cache';
222
- else {
223
- if ( $in_incall_cache )
224
- $returned = 'from in-call cache';
225
- else
226
  $returned = 'from persistent cache';
 
227
  }
228
- }
229
 
230
- if ( !$in_incall_cache ) {
231
- $this->debug_info[] = array(
232
- 'id' => $id,
233
- 'group' => $group,
234
- 'operation' => 'get',
235
- 'returned' => $returned,
236
- 'data_size' => ( $value ? strlen( serialize( $value ) ) : '' ),
237
- 'time' => $time
238
- );
239
  }
240
  }
241
 
@@ -252,6 +250,14 @@ class ObjectCache_WpObjectCache_Regular {
252
  * @return boolean
253
  */
254
  function set( $id, $data, $group = 'default', $expire = 0 ) {
 
 
 
 
 
 
 
 
255
  $key = $this->_get_cache_key( $id, $group );
256
 
257
  if ( is_object( $data ) ) {
@@ -260,7 +266,8 @@ class ObjectCache_WpObjectCache_Regular {
260
 
261
  $this->cache[$key] = $data;
262
  $return = true;
263
- $ext_return = false;
 
264
 
265
  if ( $this->_caching &&
266
  !in_array( $group, $this->nonpersistent_groups ) &&
@@ -278,6 +285,7 @@ class ObjectCache_WpObjectCache_Regular {
278
  }
279
 
280
  $v = array( 'content' => $data );
 
281
  $ext_return = $cache->set( $key, $v,
282
  ( $expire ? $expire : $this->_lifetime ) );
283
  $return = $ext_return;
@@ -288,15 +296,31 @@ class ObjectCache_WpObjectCache_Regular {
288
  $this->_transient_fallback_set( $id, $data, $group, $expire );
289
  }
290
 
291
- if ( $this->_debug ) {
292
- $this->debug_info[] = array(
293
- 'id' => $id,
294
- 'group' => $group,
295
- 'operation' => 'set',
296
- 'returned' => ( $ext_return ? 'put in cache' : 'discarded' ),
297
- 'data_size' => ( $data ? strlen( serialize( $data ) ) : '' ),
298
- 'time' => 0
299
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  }
301
 
302
  return $return;
@@ -330,14 +354,15 @@ class ObjectCache_WpObjectCache_Regular {
330
  }
331
 
332
  if ( $this->_debug ) {
333
- $this->debug_info[] = array(
334
- 'id' => $id,
335
- 'group' => $group,
336
- 'operation' => 'delete',
337
- 'returned' => ( $return ? 'deleted' : 'discarded' ),
338
- 'data_size' => 0,
339
- 'time' => 0
340
- );
 
341
  }
342
 
343
  return $return;
@@ -394,6 +419,10 @@ class ObjectCache_WpObjectCache_Regular {
394
  * @return boolean
395
  */
396
  function flush( $reason = '' ) {
 
 
 
 
397
  $this->cache = array();
398
 
399
  global $w3_multisite_blogs;
@@ -410,15 +439,23 @@ class ObjectCache_WpObjectCache_Regular {
410
  $cache->flush();
411
  }
412
 
413
- if ( $this->_debug ) {
414
- $this->debug_info[] = array(
415
- 'id' => '',
416
- 'group' => '',
417
- 'operation' => 'flush',
418
- 'returned' => $reason,
419
- 'data_size' => 0,
420
- 'time' => 0
421
- );
 
 
 
 
 
 
 
 
422
  }
423
 
424
  return true;
@@ -606,53 +643,6 @@ class ObjectCache_WpObjectCache_Regular {
606
  }
607
  }
608
 
609
- /**
610
- * Print Object Cache stats
611
- *
612
- * @return void
613
- */
614
- function stats() {
615
- echo '<h2>Summary</h2>';
616
- echo '<p>';
617
- echo '<strong>Engine</strong>: ' . Cache::engine_name(
618
- $this->_config->get_string( 'objectcache.engine' ) ) . '<br />';
619
- echo '<strong>Caching</strong>: ' .
620
- ( $this->_caching ? 'enabled' : 'disabled' ) . '<br />';
621
-
622
- if ( !$this->_caching ) {
623
- echo '<strong>Reject reason</strong>: ' .
624
- $this->get_reject_reason() . '<br />';
625
- }
626
-
627
- echo '<strong>Total calls</strong>: ' . $this->cache_total . '<br />';
628
- echo '<strong>Cache hits</strong>: ' . $this->cache_hits . '<br />';
629
- echo '<strong>Cache misses</strong>: ' . $this->cache_misses . '<br />';
630
- echo '<strong>Total time</strong>: '. round( $this->time_total, 4 ) . 's';
631
- echo '</p>';
632
-
633
- echo '<h2>Cache info</h2>';
634
-
635
- if ( $this->_debug ) {
636
- echo '<table cellpadding="0" cellspacing="3" border="1">';
637
- echo '<tr><td>#</td><td>Operation</td><td>Returned</td><td>Data size (b)</td><td>Query time (s)</td><td>ID:Group</td></tr>';
638
-
639
- foreach ( $this->debug_info as $index => $debug ) {
640
- echo '<tr>';
641
- echo '<td>' . ( $index + 1 ) . '</td>';
642
- echo '<td>' . $debug['operation'] . '</td>';
643
- echo '<td>' . $debug['returned'] . '</td>';
644
- echo '<td>' . $debug['data_size'] . '</td>';
645
- echo '<td>' . round( $debug['time'], 4 ) . '</td>';
646
- echo '<td>' . sprintf( '%s:%s', $debug['id'], $debug['group'] ) . '</td>';
647
- echo '</tr>';
648
- }
649
-
650
- echo '</table>';
651
- } else {
652
- echo '<p>Enable debug mode.</p>';
653
- }
654
- }
655
-
656
  /**
657
  * Switches context to another blog
658
  *
@@ -863,38 +853,23 @@ class ObjectCache_WpObjectCache_Regular {
863
 
864
  $strings[] = sprintf( "%s%d", str_pad( 'Total calls: ', 20 ), $this->cache_total );
865
  $strings[] = sprintf( "%s%d", str_pad( 'Cache hits: ', 20 ), $this->cache_hits );
866
- $strings[] = sprintf( "%s%d", str_pad( 'Cache misses: ', 20 ), $this->cache_misses );
867
  $strings[] = sprintf( "%s%.4f", str_pad( 'Total time: ', 20 ), $this->time_total );
868
 
869
- $strings[] = "W3TC Object Cache info:";
870
- $strings[] = sprintf( "%s | %s | %s | %s | %s | %s | %s",
871
- str_pad( '#', 5, ' ', STR_PAD_LEFT ),
872
- str_pad( 'Op', 5, ' ', STR_PAD_BOTH ),
873
- str_pad( 'Returned', 25, ' ', STR_PAD_BOTH ),
874
- str_pad( 'Data size (b)', 13, ' ', STR_PAD_LEFT ),
875
- str_pad( 'Query time (s)', 14, ' ', STR_PAD_LEFT ),
876
- str_pad( 'Group', 15, ' ', STR_PAD_LEFT ),
877
- 'ID' );
878
-
879
- foreach ( $this->debug_info as $index => $debug ) {
880
- $strings[] = sprintf( "%s | %s | %s | %s | %s | %s | %s",
881
- str_pad( $index + 1, 5, ' ', STR_PAD_LEFT ),
882
- str_pad( $debug['operation'], 5, ' ', STR_PAD_BOTH ),
883
- str_pad( $debug['returned'], 25, ' ', STR_PAD_BOTH ),
884
- str_pad( $debug['data_size'], 13, ' ', STR_PAD_LEFT ),
885
- str_pad( round( $debug['time'], 4 ), 14, ' ', STR_PAD_LEFT ),
886
- str_pad( $debug['group'], 15, ' ', STR_PAD_LEFT ),
887
- $debug['id'] );
888
  }
889
- $strings[] = '';
890
  }
891
 
892
  return $strings;
893
  }
894
 
895
  public function w3tc_usage_statistics_of_request( $storage ) {
896
- $storage->counter_add( 'objectcache_calls_total', $this->cache_total );
897
- $storage->counter_add( 'objectcache_calls_hits', $this->cache_hits );
 
 
 
898
  }
899
 
900
  public function get_reject_reason() {
@@ -922,4 +897,15 @@ class ObjectCache_WpObjectCache_Regular {
922
  return '';
923
  }
924
  }
 
 
 
 
 
 
 
 
 
 
 
925
  }
28
 
29
  /**
30
  * Total count of calls
 
 
31
  */
32
  var $cache_total = 0;
33
 
34
  /**
35
  * Cache hits count
 
 
36
  */
37
  var $cache_hits = 0;
 
38
  /**
39
+ * Number of flushes
 
 
40
  */
41
+ private $cache_flushes = 0;
42
+ private $cache_sets = 0;
43
 
44
  /**
45
+ * Total time (microsecs)
46
  *
47
  * @var integer
48
  */
49
  var $time_total = 0;
50
 
51
+ private $log_filehandle = false;
 
 
 
 
 
52
 
53
  /**
54
  * Blog id of cache
102
  * @var boolean
103
  */
104
  var $_debug = false;
105
+ private $stats_enabled = false;
106
 
107
  /**
108
  * PHP5 style constructor
115
  $this->global_groups = $this->_config->get_array( 'objectcache.groups.global' );
116
  $this->nonpersistent_groups = $this->_config->get_array(
117
  'objectcache.groups.nonpersistent' );
118
+ $this->stats_enabled = $this->_config->get_boolean( 'stats.enabled' );
119
 
120
  $this->_blog_id = Util_Environment::blog_id();
121
  }
128
  * @return mixed
129
  */
130
  function get( $id, $group = 'default', $force = false, &$found = null ) {
131
+ if ( $this->_debug || $this->stats_enabled ) {
132
  $time_start = Util_Debug::microtime();
133
  }
134
 
135
+ if ( empty( $group ) ) {
136
+ $group = 'default';
137
+ }
138
+
139
  $key = $this->_get_cache_key( $id, $group );
140
  $in_incall_cache = isset( $this->cache[$key] );
141
  $fallback_used = false;
142
 
143
+ $cache_total_inc = 0;
144
+ $cache_hits_inc = 0;
145
+
146
  if ( $in_incall_cache && !$force ) {
147
  $found = true;
148
  $value = $this->cache[$key];
160
  json_encode($a);
161
  */
162
 
163
+ $cache_total_inc = 1;
164
 
165
  if ( is_array( $v ) && isset( $v['content'] ) ) {
166
  $found = true;
167
  $value = $v['content'];
168
+ $cache_hits_inc = 1;
169
  } else {
170
  $found = false;
171
  $value = false;
 
172
  }
173
  } else {
174
  $found = false;
200
  /**
201
  * Add debug info
202
  */
203
+ if ( !$in_incall_cache && ( $this->_debug || $this->stats_enabled ) ) {
204
  $time = Util_Debug::microtime() - $time_start;
205
  $this->time_total += $time;
206
+ $this->cache_total += $cache_total_inc;
207
+ $this->cache_hits += $cache_hits_inc;
208
 
209
+ if ( $this->_debug ) {
210
+ if ( $fallback_used ) {
211
+ if ( !$found ) {
212
+ $returned = 'not in db';
213
+ } else {
214
+ $returned = 'from db fallback';
215
+ }
216
+ } else {
217
+ if ( !$found ) {
218
+ if ( $cache_total_inc <= 0 ) {
219
+ $returned = 'not tried cache';
220
+ } else {
221
+ $returned = 'not in cache';
222
+ }
223
+ } else {
 
224
  $returned = 'from persistent cache';
225
+ }
226
  }
 
227
 
228
+ $this->log_call( array(
229
+ date( 'r' ),
230
+ 'get',
231
+ $group,
232
+ $id,
233
+ $returned,
234
+ ( $value ? strlen( serialize( $value ) ) : 0 ),
235
+ (int)($time * 1000000)
236
+ ) );
237
  }
238
  }
239
 
250
  * @return boolean
251
  */
252
  function set( $id, $data, $group = 'default', $expire = 0 ) {
253
+ if ( $this->_debug || $this->stats_enabled ) {
254
+ $time_start = Util_Debug::microtime();
255
+ }
256
+
257
+ if ( empty( $group ) ) {
258
+ $group = 'default';
259
+ }
260
+
261
  $key = $this->_get_cache_key( $id, $group );
262
 
263
  if ( is_object( $data ) ) {
266
 
267
  $this->cache[$key] = $data;
268
  $return = true;
269
+ $ext_return = NULL;
270
+ $cache_sets_inc = 0;
271
 
272
  if ( $this->_caching &&
273
  !in_array( $group, $this->nonpersistent_groups ) &&
285
  }
286
 
287
  $v = array( 'content' => $data );
288
+ $cache_sets_inc = 1;
289
  $ext_return = $cache->set( $key, $v,
290
  ( $expire ? $expire : $this->_lifetime ) );
291
  $return = $ext_return;
296
  $this->_transient_fallback_set( $id, $data, $group, $expire );
297
  }
298
 
299
+ if ( $this->_debug || $this->stats_enabled ) {
300
+ $time = Util_Debug::microtime() - $time_start;
301
+
302
+ $this->cache_sets += $cache_sets_inc;
303
+ $this->time_total += $time;
304
+
305
+ if ( $this->_debug ) {
306
+ if ( is_null( $ext_return ) ) {
307
+ $reason = 'not set ' . $this->cache_reject_reason;
308
+ } else if ( $ext_return ) {
309
+ $reason = 'put in cache';
310
+ } else {
311
+ $reason = 'failed';
312
+ }
313
+
314
+ $this->log_call( array(
315
+ date( 'r' ),
316
+ 'set',
317
+ $group,
318
+ $id,
319
+ $reason,
320
+ ( $data ? strlen( serialize( $data ) ) : 0 ),
321
+ (int)($time * 1000000)
322
+ ) );
323
+ }
324
  }
325
 
326
  return $return;
354
  }
355
 
356
  if ( $this->_debug ) {
357
+ $this->log_call( array(
358
+ date( 'r' ),
359
+ 'delete',
360
+ $group,
361
+ $id,
362
+ ( $return ? 'deleted' : 'discarded' ),
363
+ 0,
364
+ 0
365
+ ) );
366
  }
367
 
368
  return $return;
419
  * @return boolean
420
  */
421
  function flush( $reason = '' ) {
422
+ if ( $this->_debug || $this->stats_enabled ) {
423
+ $time_start = Util_Debug::microtime();
424
+ }
425
+
426
  $this->cache = array();
427
 
428
  global $w3_multisite_blogs;
439
  $cache->flush();
440
  }
441
 
442
+ if ( $this->_debug || $this->stats_enabled ) {
443
+ $time = Util_Debug::microtime() - $time_start;
444
+
445
+ $this->cache_flushes++;
446
+ $this->time_total += $time;
447
+
448
+ if ( $this->_debug ) {
449
+ $this->log_call( array(
450
+ date( 'r' ),
451
+ 'flush',
452
+ '',
453
+ '',
454
+ $reason,
455
+ 0,
456
+ (int)($time * 1000000)
457
+ ) );
458
+ }
459
  }
460
 
461
  return true;
643
  }
644
  }
645
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  /**
647
  * Switches context to another blog
648
  *
853
 
854
  $strings[] = sprintf( "%s%d", str_pad( 'Total calls: ', 20 ), $this->cache_total );
855
  $strings[] = sprintf( "%s%d", str_pad( 'Cache hits: ', 20 ), $this->cache_hits );
 
856
  $strings[] = sprintf( "%s%.4f", str_pad( 'Total time: ', 20 ), $this->time_total );
857
 
858
+ if ( $this->log_filehandle ) {
859
+ fclose( $this->log_filehandle );
860
+ $this->log_filehandle = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  }
 
862
  }
863
 
864
  return $strings;
865
  }
866
 
867
  public function w3tc_usage_statistics_of_request( $storage ) {
868
+ $storage->counter_add( 'objectcache_get_total', $this->cache_total );
869
+ $storage->counter_add( 'objectcache_get_hits', $this->cache_hits );
870
+ $storage->counter_add( 'objectcache_sets', $this->cache_sets );
871
+ $storage->counter_add( 'objectcache_flushes', $this->cache_flushes );
872
+ $storage->counter_add( 'objectcache_time_ms', (int)($this->time_total * 1000) );
873
  }
874
 
875
  public function get_reject_reason() {
897
  return '';
898
  }
899
  }
900
+
901
+
902
+
903
+ private function log_call( $line ) {
904
+ if ( !$this->log_filehandle ) {
905
+ $filename = Util_Debug::log_filename( 'objectcache-calls' );
906
+ $this->log_filehandle = fopen( $filename, 'a' );
907
+ }
908
+
909
+ fputcsv ( $this->log_filehandle, $line, "\t" );
910
+ }
911
  }
PageSpeed_Plugin_Widget.php CHANGED
@@ -129,6 +129,10 @@ class PageSpeed_Plugin_Widget {
129
 
130
 
131
  public function w3tc_monitoring_score( $score ) {
 
 
 
 
132
  $url = $_SERVER['HTTP_REFERER'];
133
 
134
  $config = Dispatcher::config();
129
 
130
 
131
  public function w3tc_monitoring_score( $score ) {
132
+ if ( empty( $_SERVER['HTTP_REFERER'] ) ) {
133
+ return 'n/a';
134
+ }
135
+
136
  $url = $_SERVER['HTTP_REFERER'];
137
 
138
  $config = Dispatcher::config();
PgCache_ContentGrabber.php CHANGED
@@ -103,6 +103,9 @@ class PgCache_ContentGrabber {
103
  */
104
  var $cache_reject_reason = '';
105
 
 
 
 
106
  /**
107
  *
108
  *
@@ -151,16 +154,34 @@ class PgCache_ContentGrabber {
151
  */
152
  switch ( true ) {
153
  case defined( 'DONOTCACHEPAGE' ):
 
 
154
  if ( $this->_debug ) {
155
  self::log( 'skip processing because of DONOTCACHEPAGE constant' );
156
  }
157
  return;
158
  case defined( 'DOING_AJAX' ):
159
- case defined( 'DOING_CRON' ):
 
 
 
 
 
 
160
  case defined( 'APP_REQUEST' ):
161
  case defined( 'XMLRPC_REQUEST' ):
 
 
 
 
 
 
 
 
162
  case defined( 'WP_ADMIN' ):
163
  case ( defined( 'SHORTINIT' ) && SHORTINIT ):
 
 
164
  if ( $this->_debug ) {
165
  self::log( 'skip processing because of generic constant' );
166
  }
@@ -174,12 +195,14 @@ class PgCache_ContentGrabber {
174
  $this->_time_start = Util_Debug::microtime();
175
  }
176
 
 
177
  $this->_caching = $this->_can_cache();
178
  global $w3_late_init;
179
 
180
  if ( $this->_debug ) {
181
  self::log( 'start, can_cache: ' .
182
- ( $this->_caching ? 'true' : 'false' ) );
 
183
  }
184
 
185
  $this->_page_key_extension = $this->_get_key_extension();
@@ -197,6 +220,7 @@ class PgCache_ContentGrabber {
197
  $w3_late_init = true;
198
  return;
199
  } else {
 
200
  $this->process_cached_page_and_exit( $this->_cached_data );
201
  // if is passes here - exit is not possible now and
202
  // will happen on init
@@ -357,6 +381,7 @@ class PgCache_ContentGrabber {
357
  }
358
 
359
  echo $content;
 
360
  Dispatcher::usage_statistics_apply_before_init_and_exit( array( $this,
361
  'w3tc_usage_statistics_of_request' ) );
362
  }
@@ -368,6 +393,8 @@ class PgCache_ContentGrabber {
368
  * @return string
369
  */
370
  function ob_callback( $buffer ) {
 
 
371
  if ( !$this->_is_cacheable_content_type() ) {
372
  if ( $this->_debug )
373
  self::log( 'storing cached page - not a cached content' );
@@ -377,10 +404,13 @@ class PgCache_ContentGrabber {
377
 
378
  $compression = false;
379
  $has_dynamic = $this->_has_dynamic( $buffer );
 
 
380
  $original_can_cache = $this->_can_cache2( $buffer );
381
  $can_cache = apply_filters( 'w3tc_can_cache', $original_can_cache, $this, $buffer );
382
- if ( $can_cache != $original_can_cache )
383
  $this->cache_reject_reason = 'Third-party plugin has modified caching activity';
 
384
 
385
  if ( $this->_debug ) {
386
  self::log( 'storing cached page: ' .
@@ -485,6 +515,7 @@ class PgCache_ContentGrabber {
485
 
486
  if ( !$this->_config->get_boolean('pgcache.cache.ssl') && Util_Environment::is_https() ) {
487
  $this->cache_reject_reason = 'SSL caching disabled';
 
488
 
489
  return false;
490
  }
@@ -524,6 +555,7 @@ class PgCache_ContentGrabber {
524
 
525
  if ( $should_skip_qs && strstr( $this->_request_uri, '?' ) !== false ) {
526
  $this->cache_reject_reason = 'Requested URI contains query';
 
527
 
528
  return false;
529
  }
@@ -534,6 +566,7 @@ class PgCache_ContentGrabber {
534
  */
535
  if ( !$this->_passed_accept_files() && !$this->_passed_reject_uri() ) {
536
  $this->cache_reject_reason = 'Requested URI is rejected';
 
537
 
538
  return false;
539
  }
@@ -543,6 +576,12 @@ class PgCache_ContentGrabber {
543
  */
544
  if ( !$this->_check_ua() ) {
545
  $this->cache_reject_reason = 'User agent is rejected';
 
 
 
 
 
 
546
 
547
  return false;
548
  }
@@ -552,6 +591,7 @@ class PgCache_ContentGrabber {
552
  */
553
  if ( !$this->_check_cookies() ) {
554
  $this->cache_reject_reason = 'Cookie is rejected';
 
555
 
556
  return false;
557
  }
@@ -562,11 +602,14 @@ class PgCache_ContentGrabber {
562
  if ( $this->_config->get_boolean( 'pgcache.reject.logged' ) ) {
563
  if ( !$this->_check_logged_in() ) {
564
  $this->cache_reject_reason = 'User is logged in';
 
 
565
  return false;
566
  }
567
  } else {
568
  if ( !$this->_check_logged_in_role_allowed() ) {
569
  $this->cache_reject_reason = 'Rejected user role is logged in';
 
570
  return false;
571
  }
572
  }
@@ -588,26 +631,20 @@ class PgCache_ContentGrabber {
588
  return false;
589
  }
590
 
591
- /**
592
- * Check for database error
593
- */
594
- if ( Util_Content::is_database_error( $buffer ) ) {
595
- $this->cache_reject_reason = 'Database error occurred';
596
-
597
- return false;
598
- }
599
-
600
  /**
601
  * Check for DONOTCACHEPAGE constant
602
  */
603
  if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) {
604
  $this->cache_reject_reason = 'DONOTCACHEPAGE constant is defined';
 
605
  return false;
606
  }
607
 
608
  if ( $this->_config->get_string( 'pgcache.rest' ) != 'cache' ) {
609
  if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
610
  $this->cache_reject_reason = 'REST request';
 
 
611
  return false;
612
  }
613
  }
@@ -617,6 +654,7 @@ class PgCache_ContentGrabber {
617
  */
618
  if ( !$this->_config->get_boolean( 'pgcache.cache.404' ) && function_exists( 'is_404' ) && is_404() ) {
619
  $this->cache_reject_reason = 'Page is 404';
 
620
 
621
  return false;
622
  }
@@ -626,6 +664,7 @@ class PgCache_ContentGrabber {
626
  */
627
  if ( !$this->_config->get_boolean( 'pgcache.cache.home' ) && function_exists( 'is_home' ) && is_home() ) {
628
  $this->cache_reject_reason = is_front_page() && is_home() ? 'Page is front page' : 'Page is posts page';
 
629
 
630
  return false;
631
  }
@@ -635,6 +674,7 @@ class PgCache_ContentGrabber {
635
  */
636
  if ( $this->_config->get_boolean( 'pgcache.reject.front_page' ) && function_exists( 'is_front_page' ) && is_front_page() && !is_home() ) {
637
  $this->cache_reject_reason = 'Page is front page';
 
638
 
639
  return false;
640
  }
@@ -644,6 +684,7 @@ class PgCache_ContentGrabber {
644
  */
645
  if ( !$this->_config->get_boolean( 'pgcache.cache.feed' ) && function_exists( 'is_feed' ) && is_feed() ) {
646
  $this->cache_reject_reason = 'Page is feed';
 
647
 
648
  return false;
649
  }
@@ -653,6 +694,7 @@ class PgCache_ContentGrabber {
653
  */
654
  if ( $this->_enhanced_mode && $this->_has_dynamic( $buffer ) ) {
655
  $this->cache_reject_reason = 'Page contains dynamic tags (mfunc or mclude) can not be cached in enhanced mode';
 
656
 
657
  return false;
658
  }
@@ -664,6 +706,7 @@ class PgCache_ContentGrabber {
664
  */
665
  if ( $this->_passed_reject_categories() ) {
666
  $this->cache_reject_reason = 'Page associated with a rejected category';
 
667
  return false;
668
  }
669
  /**
@@ -671,6 +714,7 @@ class PgCache_ContentGrabber {
671
  */
672
  if ( $this->_passed_reject_tags() ) {
673
  $this->cache_reject_reason = 'Page using a rejected tag';
 
674
  return false;
675
  }
676
  }
@@ -679,6 +723,7 @@ class PgCache_ContentGrabber {
679
  */
680
  if ( $this->_passed_reject_authors() ) {
681
  $this->cache_reject_reason = 'Page written by a rejected author';
 
682
  return false;
683
  }
684
  /**
@@ -686,6 +731,7 @@ class PgCache_ContentGrabber {
686
  */
687
  if ( $this->_passed_reject_custom_fields() ) {
688
  $this->cache_reject_reason = 'Page using a rejected custom field';
 
689
  return false;
690
  }
691
  }
@@ -1880,6 +1926,7 @@ class PgCache_ContentGrabber {
1880
  global $w3_late_caching_succeeded;
1881
  $w3_late_caching_succeeded = true;
1882
 
 
1883
  $this->process_cached_page_and_exit( $this->_cached_data );
1884
  // if is passes here - exit is not possible now and
1885
  // will happen on init
@@ -1888,6 +1935,7 @@ class PgCache_ContentGrabber {
1888
  }
1889
 
1890
  if ( $this->_late_init && $this->_caching ) {
 
1891
  $this->process_cached_page_and_exit( $this->_cached_data );
1892
  // if is passes here - exit is not possible now and
1893
  // will happen on init
@@ -1895,13 +1943,6 @@ class PgCache_ContentGrabber {
1895
  }
1896
  }
1897
 
1898
- /**
1899
- *
1900
- *
1901
- * @param unknown $buffer
1902
- * @param unknown $has_dynamic
1903
- * @return array
1904
- */
1905
  private function _maybe_save_cached_result( $buffer, $has_dynamic ) {
1906
  $mobile_group = $this->_page_key_extension['useragent'];
1907
  $referrer_group = $this->_page_key_extension['referrer'];
@@ -1959,8 +2000,9 @@ class PgCache_ContentGrabber {
1959
  '_check_rules_present'
1960
  ) );
1961
 
1962
- if ( isset( $response_headers['kv']['Content-Type'] ) )
1963
  $content_type = $response_headers['kv']['Content-Type'];
 
1964
  }
1965
 
1966
  $time = time();
@@ -1970,6 +2012,7 @@ class PgCache_ContentGrabber {
1970
  * Store different versions of cache
1971
  */
1972
  $buffers = array();
 
1973
 
1974
  foreach ( $compressions_to_store as $_compression ) {
1975
  $this->_set_extract_page_key(
@@ -1999,8 +2042,16 @@ class PgCache_ContentGrabber {
1999
  $_data = apply_filters( 'w3tc_pagecache_set', $_data, $this->_page_key,
2000
  $this->_page_group );
2001
 
2002
- if ( !empty( $_data ) )
2003
  $cache->set( $this->_page_key, $_data, $this->_lifetime, $this->_page_group );
 
 
 
 
 
 
 
 
2004
  }
2005
 
2006
  // Change buffer if using compression
@@ -2021,14 +2072,24 @@ class PgCache_ContentGrabber {
2021
  }
2022
 
2023
  public function w3tc_usage_statistics_of_request( $storage ) {
2024
- $storage->counter_add( 'pagecache_requests_total', 1 );
2025
- if ( $this->_cached_data )
2026
- $storage->counter_add( 'pagecache_requests_hits', 1 );
2027
-
2028
  global $w3tc_start_microtime;
 
2029
  if ( !empty( $w3tc_start_microtime ) ) {
2030
- $ms10 = (int)( ( microtime( true ) - $w3tc_start_microtime ) * 100 );
2031
- $storage->counter_add( 'pagecache_requests_time_10ms', $ms10 );
 
 
 
 
 
 
 
 
 
 
 
 
 
2032
  }
2033
  }
2034
 
103
  */
104
  var $cache_reject_reason = '';
105
 
106
+ private $process_status = '';
107
+ private $output_size = 0;
108
+
109
  /**
110
  *
111
  *
154
  */
155
  switch ( true ) {
156
  case defined( 'DONOTCACHEPAGE' ):
157
+ $this->process_status = 'miss_third_party';
158
+ $this->cache_reject_reason = 'DONOTCACHEPAGE defined';
159
  if ( $this->_debug ) {
160
  self::log( 'skip processing because of DONOTCACHEPAGE constant' );
161
  }
162
  return;
163
  case defined( 'DOING_AJAX' ):
164
+ $this->process_status = 'miss_ajax';
165
+ $this->cache_reject_reason = 'AJAX request';
166
+ if ( $this->_debug ) {
167
+ self::log( 'skip processing because of AJAX constant' );
168
+ }
169
+ return;
170
+
171
  case defined( 'APP_REQUEST' ):
172
  case defined( 'XMLRPC_REQUEST' ):
173
+ $this->cache_reject_reason = 'API call constant defined';
174
+ $this->process_status = 'miss_api_call';
175
+ if ( $this->_debug ) {
176
+ self::log( 'skip processing because of API call constant' );
177
+ }
178
+ return;
179
+
180
+ case defined( 'DOING_CRON' ):
181
  case defined( 'WP_ADMIN' ):
182
  case ( defined( 'SHORTINIT' ) && SHORTINIT ):
183
+ $this->cache_reject_reason = 'WP_ADMIN defined';
184
+ $this->process_status = 'miss_wp_admin';
185
  if ( $this->_debug ) {
186
  self::log( 'skip processing because of generic constant' );
187
  }
195
  $this->_time_start = Util_Debug::microtime();
196
  }
197
 
198
+ // TODO: call modifies object state, rename method at least
199
  $this->_caching = $this->_can_cache();
200
  global $w3_late_init;
201
 
202
  if ( $this->_debug ) {
203
  self::log( 'start, can_cache: ' .
204
+ ( $this->_caching ? 'true' : 'false' ) .
205
+ ', reject reason: ' . $this->cache_reject_reason );
206
  }
207
 
208
  $this->_page_key_extension = $this->_get_key_extension();
220
  $w3_late_init = true;
221
  return;
222
  } else {
223
+ $this->process_status = 'hit';
224
  $this->process_cached_page_and_exit( $this->_cached_data );
225
  // if is passes here - exit is not possible now and
226
  // will happen on init
381
  }
382
 
383
  echo $content;
384
+ Util_Debug::log('pagecache', 'r3');
385
  Dispatcher::usage_statistics_apply_before_init_and_exit( array( $this,
386
  'w3tc_usage_statistics_of_request' ) );
387
  }
393
  * @return string
394
  */
395
  function ob_callback( $buffer ) {
396
+ $this->output_size = strlen( $buffer );
397
+
398
  if ( !$this->_is_cacheable_content_type() ) {
399
  if ( $this->_debug )
400
  self::log( 'storing cached page - not a cached content' );
404
 
405
  $compression = false;
406
  $has_dynamic = $this->_has_dynamic( $buffer );
407
+
408
+ // TODO: call modifies object state, rename method at least
409
  $original_can_cache = $this->_can_cache2( $buffer );
410
  $can_cache = apply_filters( 'w3tc_can_cache', $original_can_cache, $this, $buffer );
411
+ if ( $can_cache != $original_can_cache ) {
412
  $this->cache_reject_reason = 'Third-party plugin has modified caching activity';
413
+ }
414
 
415
  if ( $this->_debug ) {
416
  self::log( 'storing cached page: ' .
515
 
516
  if ( !$this->_config->get_boolean('pgcache.cache.ssl') && Util_Environment::is_https() ) {
517
  $this->cache_reject_reason = 'SSL caching disabled';
518
+ $this->process_status = 'miss_configuration';
519
 
520
  return false;
521
  }
555
 
556
  if ( $should_skip_qs && strstr( $this->_request_uri, '?' ) !== false ) {
557
  $this->cache_reject_reason = 'Requested URI contains query';
558
+ $this->process_status = 'miss_query_string';
559
 
560
  return false;
561
  }
566
  */
567
  if ( !$this->_passed_accept_files() && !$this->_passed_reject_uri() ) {
568
  $this->cache_reject_reason = 'Requested URI is rejected';
569
+ $this->process_status = 'miss_configuration';
570
 
571
  return false;
572
  }
576
  */
577
  if ( !$this->_check_ua() ) {
578
  $this->cache_reject_reason = 'User agent is rejected';
579
+ if ( isset( $_REQUEST['w3tc_rewrite_test'] ) ) {
580
+ // special common case - w3tc_rewrite_test check request
581
+ $this->process_status = 'miss_wp_admin';
582
+ } else {
583
+ $this->process_status = 'miss_configuration';
584
+ }
585
 
586
  return false;
587
  }
591
  */
592
  if ( !$this->_check_cookies() ) {
593
  $this->cache_reject_reason = 'Cookie is rejected';
594
+ $this->process_status = 'miss_configuration';
595
 
596
  return false;
597
  }
602
  if ( $this->_config->get_boolean( 'pgcache.reject.logged' ) ) {
603
  if ( !$this->_check_logged_in() ) {
604
  $this->cache_reject_reason = 'User is logged in';
605
+ $this->process_status = 'miss_logged_in';
606
+
607
  return false;
608
  }
609
  } else {
610
  if ( !$this->_check_logged_in_role_allowed() ) {
611
  $this->cache_reject_reason = 'Rejected user role is logged in';
612
+ $this->process_status = 'miss_logged_in';
613
  return false;
614
  }
615
  }
631
  return false;
632
  }
633
 
 
 
 
 
 
 
 
 
 
634
  /**
635
  * Check for DONOTCACHEPAGE constant
636
  */
637
  if ( defined( 'DONOTCACHEPAGE' ) && DONOTCACHEPAGE ) {
638
  $this->cache_reject_reason = 'DONOTCACHEPAGE constant is defined';
639
+ $this->process_status = 'miss_third_party';
640
  return false;
641
  }
642
 
643
  if ( $this->_config->get_string( 'pgcache.rest' ) != 'cache' ) {
644
  if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
645
  $this->cache_reject_reason = 'REST request';
646
+ $this->process_status = 'miss_api_call';
647
+
648
  return false;
649
  }
650
  }
654
  */
655
  if ( !$this->_config->get_boolean( 'pgcache.cache.404' ) && function_exists( 'is_404' ) && is_404() ) {
656
  $this->cache_reject_reason = 'Page is 404';
657
+ $this->process_status = 'miss_404';
658
 
659
  return false;
660
  }
664
  */
665
  if ( !$this->_config->get_boolean( 'pgcache.cache.home' ) && function_exists( 'is_home' ) && is_home() ) {
666
  $this->cache_reject_reason = is_front_page() && is_home() ? 'Page is front page' : 'Page is posts page';
667
+ $this->process_status = 'miss_configuration';
668
 
669
  return false;
670
  }
674
  */
675
  if ( $this->_config->get_boolean( 'pgcache.reject.front_page' ) && function_exists( 'is_front_page' ) && is_front_page() && !is_home() ) {
676
  $this->cache_reject_reason = 'Page is front page';
677
+ $this->process_status = 'miss_configuration';
678
 
679
  return false;
680
  }
684
  */
685
  if ( !$this->_config->get_boolean( 'pgcache.cache.feed' ) && function_exists( 'is_feed' ) && is_feed() ) {
686
  $this->cache_reject_reason = 'Page is feed';
687
+ $this->process_status = 'miss_configuration';
688
 
689
  return false;
690
  }
694
  */
695
  if ( $this->_enhanced_mode && $this->_has_dynamic( $buffer ) ) {
696
  $this->cache_reject_reason = 'Page contains dynamic tags (mfunc or mclude) can not be cached in enhanced mode';
697
+ $this->process_status = 'miss_mfunc';
698
 
699
  return false;
700
  }
706
  */
707
  if ( $this->_passed_reject_categories() ) {
708
  $this->cache_reject_reason = 'Page associated with a rejected category';
709
+ $this->process_status = 'miss_configuration';
710
  return false;
711
  }
712
  /**
714
  */
715
  if ( $this->_passed_reject_tags() ) {
716
  $this->cache_reject_reason = 'Page using a rejected tag';
717
+ $this->process_status = 'miss_configuration';
718
  return false;
719
  }
720
  }
723
  */
724
  if ( $this->_passed_reject_authors() ) {
725
  $this->cache_reject_reason = 'Page written by a rejected author';
726
+ $this->process_status = 'miss_configuration';
727
  return false;
728
  }
729
  /**
731
  */
732
  if ( $this->_passed_reject_custom_fields() ) {
733
  $this->cache_reject_reason = 'Page using a rejected custom field';
734
+ $this->process_status = 'miss_configuration';
735
  return false;
736
  }
737
  }
1926
  global $w3_late_caching_succeeded;
1927
  $w3_late_caching_succeeded = true;
1928
 
1929
+ $this->process_status = 'hit';
1930
  $this->process_cached_page_and_exit( $this->_cached_data );
1931
  // if is passes here - exit is not possible now and
1932
  // will happen on init
1935
  }
1936
 
1937
  if ( $this->_late_init && $this->_caching ) {
1938
+ $this->process_status = 'hit';
1939
  $this->process_cached_page_and_exit( $this->_cached_data );
1940
  // if is passes here - exit is not possible now and
1941
  // will happen on init
1943
  }
1944
  }
1945
 
 
 
 
 
 
 
 
1946
  private function _maybe_save_cached_result( $buffer, $has_dynamic ) {
1947
  $mobile_group = $this->_page_key_extension['useragent'];
1948
  $referrer_group = $this->_page_key_extension['referrer'];
2000
  '_check_rules_present'
2001
  ) );
2002
 
2003
+ if ( isset( $response_headers['kv']['Content-Type'] ) ) {
2004
  $content_type = $response_headers['kv']['Content-Type'];
2005
+ }
2006
  }
2007
 
2008
  $time = time();
2012
  * Store different versions of cache
2013
  */
2014
  $buffers = array();
2015
+ $something_was_set = false;
2016
 
2017
  foreach ( $compressions_to_store as $_compression ) {
2018
  $this->_set_extract_page_key(
2042
  $_data = apply_filters( 'w3tc_pagecache_set', $_data, $this->_page_key,
2043
  $this->_page_group );
2044
 
2045
+ if ( !empty( $_data ) ) {
2046
  $cache->set( $this->_page_key, $_data, $this->_lifetime, $this->_page_group );
2047
+ $something_was_set = true;
2048
+ }
2049
+ }
2050
+
2051
+ if ( $something_was_set ) {
2052
+ $this->process_status = 'miss_fill';
2053
+ } else {
2054
+ $this->process_status = 'miss_third_party';
2055
  }
2056
 
2057
  // Change buffer if using compression
2072
  }
2073
 
2074
  public function w3tc_usage_statistics_of_request( $storage ) {
 
 
 
 
2075
  global $w3tc_start_microtime;
2076
+ $time_ms = 0;
2077
  if ( !empty( $w3tc_start_microtime ) ) {
2078
+ $time_ms = (int)( ( microtime( true ) - $w3tc_start_microtime ) * 1000 );
2079
+ $storage->counter_add( 'pagecache_requests_time_10ms', (int)( $time_ms / 10 ) );
2080
+ }
2081
+
2082
+ if ( !empty( $this->process_status ) ) {
2083
+ // see registered keys in PgCache_Plugin.w3tc_usage_statistics_metrics
2084
+ $storage->counter_add( 'php_requests_pagecache_' . $this->process_status, 1 );
2085
+
2086
+ if ( $this->_debug ) {
2087
+ self::log( 'finished in ' . $time_ms .
2088
+ ' size ' . $this->output_size .
2089
+ ' with process status ' .
2090
+ $this->process_status .
2091
+ ' reason ' . $this->cache_reject_reason);
2092
+ }
2093
  }
2094
  }
2095
 
PgCache_Environment.php CHANGED
@@ -125,7 +125,7 @@ class PgCache_Environment {
125
  }
126
 
127
  private function are_rules_present( $c ) {
128
- $e = $c->get_boolean( 'pgcache.enabled' );
129
 
130
  return ( $e == 'file_generic' || $e == 'nginx_memcached' );
131
  }
125
  }
126
 
127
  private function are_rules_present( $c ) {
128
+ $e = $c->get_string( 'pgcache.engine' );
129
 
130
  return ( $e == 'file_generic' || $e == 'nginx_memcached' );
131
  }
PgCache_Plugin.php CHANGED
@@ -52,6 +52,8 @@ class PgCache_Plugin {
52
  10, 1 );
53
  add_filter( 'w3tc_usage_statistics_metrics',
54
  array( $this, 'w3tc_usage_statistics_metrics' ) );
 
 
55
 
56
 
57
  if ( $this->_config->get_string( 'pgcache.engine' ) == 'file' ||
@@ -211,12 +213,6 @@ class PgCache_Plugin {
211
  }
212
  }
213
 
214
- /**
215
- *
216
- *
217
- * @param integer $lifetime
218
- * @return integer
219
- */
220
  function comment_cookie_lifetime( $lifetime ) {
221
  $l = $this->_config->get_integer( 'pgcache.comment_cookie_ttl' );
222
  if ( $l != -1 )
@@ -253,10 +249,45 @@ class PgCache_Plugin {
253
 
254
  public function w3tc_usage_statistics_metrics( $metrics ) {
255
  return array_merge( $metrics, array(
256
- 'pagecache_requests_total', 'pagecache_requests_hits',
 
 
 
 
 
 
 
 
 
 
257
  'pagecache_requests_time_10ms' ) );
258
  }
259
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  public function w3tc_admin_bar_menu( $menu_items ) {
261
  $menu_items['20110.pagecache'] = array(
262
  'id' => 'w3tc_flush_pgcache',
52
  10, 1 );
53
  add_filter( 'w3tc_usage_statistics_metrics',
54
  array( $this, 'w3tc_usage_statistics_metrics' ) );
55
+ add_filter( 'w3tc_usage_statistics_sources', array(
56
+ $this, 'w3tc_usage_statistics_sources' ) );
57
 
58
 
59
  if ( $this->_config->get_string( 'pgcache.engine' ) == 'file' ||
213
  }
214
  }
215
 
 
 
 
 
 
 
216
  function comment_cookie_lifetime( $lifetime ) {
217
  $l = $this->_config->get_integer( 'pgcache.comment_cookie_ttl' );
218
  if ( $l != -1 )
249
 
250
  public function w3tc_usage_statistics_metrics( $metrics ) {
251
  return array_merge( $metrics, array(
252
+ 'php_requests_pagecache_hit',
253
+ 'php_requests_pagecache_miss_404',
254
+ 'php_requests_pagecache_miss_ajax',
255
+ 'php_requests_pagecache_miss_api_call',
256
+ 'php_requests_pagecache_miss_configuration',
257
+ 'php_requests_pagecache_miss_fill',
258
+ 'php_requests_pagecache_miss_logged_in',
259
+ 'php_requests_pagecache_miss_mfunc',
260
+ 'php_requests_pagecache_miss_query_string',
261
+ 'php_requests_pagecache_miss_third_party',
262
+ 'php_requests_pagecache_miss_wp_admin',
263
  'pagecache_requests_time_10ms' ) );
264
  }
265
 
266
+ public function w3tc_usage_statistics_sources( $sources ) {
267
+ $c = Dispatcher::config();
268
+ if ( $c->get_string( 'pgcache.engine' ) == 'apc' ) {
269
+ $sources['apc_servers']['pgcache'] = array(
270
+ 'name' => __( 'Page Cache', 'w3-total-cache' )
271
+ );
272
+ } elseif ( $c->get_string( 'pgcache.engine' ) == 'memcached' ) {
273
+ $sources['memcached_servers']['pgcache'] = array(
274
+ 'servers' => $c->get_array( 'pgcache.memcached.servers' ),
275
+ 'username' => $c->get_string( 'pgcache.memcached.username' ),
276
+ 'password' => $c->get_string( 'pgcache.memcached.password' ),
277
+ 'name' => __( 'Page Cache', 'w3-total-cache' )
278
+ );
279
+ } elseif ( $c->get_string( 'pgcache.engine' ) == 'redis' ) {
280
+ $sources['redis_servers']['pgcache'] = array(
281
+ 'servers' => $c->get_array( 'pgcache.redis.servers' ),
282
+ 'dbid' => $c->get_integer( 'pgcache.redis.dbid' ),
283
+ 'password' => $c->get_string( 'pgcache.redis.password' ),
284
+ 'name' => __( 'Page Cache', 'w3-total-cache' )
285
+ );
286
+ }
287
+
288
+ return $sources;
289
+ }
290
+
291
  public function w3tc_admin_bar_menu( $menu_items ) {
292
  $menu_items['20110.pagecache'] = array(
293
  'id' => 'w3tc_flush_pgcache',
PgCache_Plugin_Admin.php CHANGED
@@ -314,76 +314,98 @@ class PgCache_Plugin_Admin {
314
  }
315
 
316
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
317
- // memcached servers
318
- if ( $this->_config->get_string( 'pgcache.engine' ) == 'memcached' ) {
319
- $summary['memcached_servers']['pgcache'] = array(
320
- 'servers' => $this->_config->get_array( 'pgcache.memcached.servers' ),
321
- 'username' => $this->_config->get_string( 'pgcache.memcached.username' ),
322
- 'password' => $this->_config->get_string( 'pgcache.memcached.password' ),
323
- 'name' => __( 'Page Cache', 'w3-total-cache' )
324
- );
325
- } elseif ( $this->_config->get_string( 'pgcache.engine' ) == 'redis' ) {
326
- $summary['redis_servers']['pgcache'] = array(
327
- 'servers' => $this->_config->get_array( 'pgcache.redis.servers' ),
328
- 'dbid' => $this->_config->get_integer( 'pgcache.redis.dbid' ),
329
- 'password' => $this->_config->get_string( 'pgcache.redis.password' ),
330
- 'name' => __( 'Page Cache', 'w3-total-cache' )
331
- );
332
- }
333
-
334
  // total size
335
  $g = Dispatcher::component( 'PgCache_ContentGrabber' );
336
  $pagecache = array();
337
 
338
  $e = $this->_config->get_string( 'pgcache.engine' );
339
- $pagecache['size_visible'] = ( $e == 'file_generic' );
340
- $pagecache['requests_visible'] = ( $e != 'file_generic' );
341
-
342
-
343
- if ( isset( $summary['period']['timestamp_end'] ) ) {
344
- // need to return cache size
345
- if ( $pagecache['size_visible'] ) {
346
- list( $v, $should_count ) =
347
- Util_UsageStatistics::get_or_init_size_transient(
348
- 'w3tc_ustats_pagecache_size', $summary );
349
- if ( $should_count ) {
350
- $size = $g->get_cache_stats_size( $summary['timeout_time'] );
351
- $v['size_used'] = Util_UsageStatistics::bytes_to_size2(
352
- $size, 'bytes' );
353
- $v['items'] = Util_UsageStatistics::integer2(
354
- $size, 'items' );
355
-
356
- set_transient( 'w3tc_ustats_pagecache_size', $v, 120 );
357
- }
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
- if ( isset( $v['size_used'] ) ) {
360
- $pagecache['size_used'] = $v['size_used'];
361
- $pagecache['items'] = $v['items'];
 
 
362
  }
363
  }
 
 
 
 
 
 
364
 
365
- // counters
366
- $requests_total = Util_UsageStatistics::sum( $history,
367
- 'pagecache_requests_total' );
368
  $requests_time_ms = Util_UsageStatistics::sum( $history,
369
  'pagecache_requests_time_10ms' ) * 10;
370
- $requests_hits = Util_UsageStatistics::sum( $history,
371
- 'pagecache_requests_hits' );
372
-
373
- $pagecache['requests_total'] = Util_UsageStatistics::integer(
374
- $requests_total );
375
- $pagecache['request_time_ms'] =
376
- Util_UsageStatistics::value_per_period_seconds(
377
- $requests_time_ms, $summary );
378
- $pagecache['requests_per_second'] =
379
- Util_UsageStatistics::value_per_period_seconds(
380
- $requests_total, $summary );
381
- $pagecache['hit_rate'] = Util_UsageStatistics::percent(
382
- $requests_hits, $requests_total );
383
 
 
 
 
 
384
  }
385
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  $summary['pagecache'] = $pagecache;
 
387
  return $summary;
388
  }
389
  }
314
  }
315
 
316
  public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  // total size
318
  $g = Dispatcher::component( 'PgCache_ContentGrabber' );
319
  $pagecache = array();
320
 
321
  $e = $this->_config->get_string( 'pgcache.engine' );
322
+ $pagecache['engine_name'] = Cache::engine_name( $e );
323
+ $file_generic = ( $e == 'file_generic' );
324
+
325
+
326
+ // build metrics in php block
327
+ if ( !isset( $summary['php'] ) ) {
328
+ $summary['php'] = array();
329
+ }
330
+
331
+ Util_UsageStatistics::sum_by_prefix_positive( $summary['php'],
332
+ $history, 'php_requests_pagecache' );
333
+
334
+ // need to return cache size
335
+ if ( $file_generic ) {
336
+ list( $v, $should_count ) =
337
+ Util_UsageStatistics::get_or_init_size_transient(
338
+ 'w3tc_ustats_pagecache_size', $summary );
339
+ if ( $should_count ) {
340
+ $size = $g->get_cache_stats_size( $summary['timeout_time'] );
341
+ $v['size_used'] = Util_UsageStatistics::bytes_to_size2(
342
+ $size, 'bytes' );
343
+ $v['items'] = Util_UsageStatistics::integer2(
344
+ $size, 'items' );
345
+
346
+ set_transient( 'w3tc_ustats_pagecache_size', $v, 55 );
347
+ }
348
+
349
+ if ( isset( $v['size_used'] ) ) {
350
+ $pagecache['size_used'] = $v['size_used'];
351
+ $pagecache['items'] = $v['items'];
352
+ }
353
 
354
+ if ( isset( $summary['access_log'] ) ) {
355
+ $pagecache['requests'] = $summary['access_log']['dynamic_requests_total_v'];
356
+ $pagecache['requests_hit'] = $pagecache['requests'] - $summary['php']['php_requests_v'];
357
+ if ($pagecache['requests_hit'] < 0) {
358
+ $pagecache['requests_hit'] = 0;
359
  }
360
  }
361
+ } else {
362
+ // all request counts data available
363
+ $pagecache['requests'] = $summary['php']['php_requests_v'];
364
+ $pagecache['requests_hit'] =
365
+ isset( $summary['php']['php_requests_pagecache_hit'] ) ?
366
+ $summary['php']['php_requests_pagecache_hit'] : 0;
367
 
 
 
 
368
  $requests_time_ms = Util_UsageStatistics::sum( $history,
369
  'pagecache_requests_time_10ms' ) * 10;
370
+ $php_requests = Util_UsageStatistics::sum( $history,
371
+ 'php_requests' );
 
 
 
 
 
 
 
 
 
 
 
372
 
373
+ if ( $php_requests > 0 ) {
374
+ $pagecache['request_time_ms'] = Util_UsageStatistics::integer(
375
+ $requests_time_ms / $php_requests );
376
+ }
377
  }
378
 
379
+ if ( $e == 'memcached' ) {
380
+ $pagecache['size_percent'] = $summary['memcached']['size_percent'];
381
+ }
382
+ if ( isset($pagecache['requests_hit'] ) ) {
383
+ $pagecache['requests_hit_rate'] = Util_UsageStatistics::percent(
384
+ $pagecache['requests_hit'], $pagecache['requests'] );
385
+ }
386
+
387
+ if ( !isset( $summary['php']['php_requests_pagecache_hit'] ) ) {
388
+ $summary['php']['php_requests_pagecache_hit'] = 0;
389
+ }
390
+
391
+ if ( isset( $summary['php']['php_requests_v'] ) ) {
392
+ $v = $summary['php']['php_requests_v'] -
393
+ $summary['php']['php_requests_pagecache_hit'];
394
+ if ( $v < 0 ) {
395
+ $v = 0;
396
+ }
397
+
398
+ $summary['php']['php_requests_pagecache_miss'] = $v;
399
+ }
400
+
401
+ if ( isset( $pagecache['requests'] ) ) {
402
+ $pagecache['requests_per_second'] =
403
+ Util_UsageStatistics::value_per_period_seconds( $pagecache['requests'], $summary );
404
+ }
405
+
406
+
407
  $summary['pagecache'] = $pagecache;
408
+
409
  return $summary;
410
  }
411
  }
Root_AdminActions.php CHANGED
@@ -47,7 +47,8 @@ class Root_AdminActions {
47
  'licensing' => 'Licensing_AdminActions',
48
  'extensions' => 'Extensions_AdminActions',
49
  'default' => 'Generic_AdminActions_Default',
50
- 'support' => 'Support_AdminActions'
 
51
  );
52
  $handlers = apply_filters( 'w3tc_admin_actions', $handlers );
53
  }
47
  'licensing' => 'Licensing_AdminActions',
48
  'extensions' => 'Extensions_AdminActions',
49
  'default' => 'Generic_AdminActions_Default',
50
+ 'support' => 'Support_AdminActions',
51
+ 'ustats' => 'UsageStatistics_AdminActions'
52
  );
53
  $handlers = apply_filters( 'w3tc_admin_actions', $handlers );
54
  }
Root_AdminMenu.php CHANGED
@@ -228,6 +228,11 @@ class Root_AdminMenu {
228
  $options_cdn->options();
229
  break;
230
 
 
 
 
 
 
231
  case 'w3tc_support':
232
  $options_support = new Support_Page();
233
  $options_support->options();
228
  $options_cdn->options();
229
  break;
230
 
231
+ case 'w3tc_stats':
232
+ $p = new UsageStatistics_Page();
233
+ $p->render();
234
+ break;
235
+
236
  case 'w3tc_support':
237
  $options_support = new Support_Page();
238
  $options_support->options();
UsageStatistics_AdminActions.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class UsageStatistics_AdminActions {
7
+ private $_config = null;
8
+
9
+
10
+
11
+ public function __construct() {
12
+ $this->_config = Dispatcher::config();
13
+ }
14
+
15
+
16
+
17
+ public function w3tc_ustats_note_disable() {
18
+ $this->_config->set( 'stats.enabled', false );
19
+ $this->_config->save();
20
+
21
+ Util_Admin::redirect( array(), true );
22
+ }
23
+ }
UsageStatistics_Core.php CHANGED
@@ -59,16 +59,19 @@ class UsageStatistics_Core {
59
  public function apply_metrics_before_init_and_exit( $metrics_function ) {
60
  // plugin already loaded, metrics will be added normal way
61
  // by shutdown
62
- if ( $this->shutdown_handler_added )
 
63
  return;
 
64
 
65
  $this->hotspot_flushing_state_on_exit_attempt =
66
  $this->storage->begin_flush_hotspot_data();
67
 
68
  // flush wants to happen in that process, need to pass through whole
69
  // wp request processing further
70
- if ( $this->hotspot_flushing_state_on_exit_attempt != 'not_needed' )
71
  return;
 
72
 
73
  call_user_func( $metrics_function, $this->storage );
74
  exit();
59
  public function apply_metrics_before_init_and_exit( $metrics_function ) {
60
  // plugin already loaded, metrics will be added normal way
61
  // by shutdown
62
+
63
+ if ( $this->shutdown_handler_added ) {
64
  return;
65
+ }
66
 
67
  $this->hotspot_flushing_state_on_exit_attempt =
68
  $this->storage->begin_flush_hotspot_data();
69
 
70
  // flush wants to happen in that process, need to pass through whole
71
  // wp request processing further
72
+ if ( $this->hotspot_flushing_state_on_exit_attempt != 'not_needed' ) {
73
  return;
74
+ }
75
 
76
  call_user_func( $metrics_function, $this->storage );
77
  exit();
UsageStatistics_GeneralPage.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class UsageStatistics_GeneralPage {
7
+ /**
8
+ * W3TC General settings page modifications
9
+ */
10
+ static public function admin_init_w3tc_general() {
11
+ $o = new UsageStatistics_GeneralPage();
12
+
13
+ add_filter( 'w3tc_settings_general_anchors',
14
+ array( $o, 'w3tc_settings_general_anchors' ) );
15
+ add_action( 'w3tc_settings_general_boxarea_stats',
16
+ array( $o, 'w3tc_settings_general_boxarea_stats' ) );
17
+ }
18
+
19
+
20
+
21
+
22
+ public function w3tc_settings_general_anchors( $anchors ) {
23
+ $anchors[] = array( 'id' => 'stats', 'text' => 'Statistics' );
24
+ return $anchors;
25
+ }
26
+
27
+
28
+
29
+ public function w3tc_settings_general_boxarea_stats() {
30
+ include W3TC_DIR . '/UsageStatistics_GeneralPage_View.php';
31
+ }
32
+ }
UsageStatistics_GeneralPage_View.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ Util_Ui::postbox_header( 'Statistics', '', 'stats' );
8
+
9
+ $c = Dispatcher::config();
10
+ $is_pro = Util_Environment::is_w3tc_pro( $c );
11
+
12
+ ?>
13
+ <p>Cache usage statistics.</p>
14
+
15
+ <table class="form-table">
16
+ <?php
17
+ Util_Ui::config_item( array(
18
+ 'key' => 'stats.enabled',
19
+ 'control' => 'checkbox',
20
+ 'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
21
+ 'disabled' => ( $is_pro ? null : true ),
22
+ 'description' => __( 'Enable statistics collection. Note that this consumes additional resources and is not recommended to be run continuously.',
23
+ 'w3-total-cache' ) .
24
+ ( $is_pro ? '' : __( ' <strong>Available after <a href="#" class="button-buy-plugin" data-src="stats_requirements">upgrade</a>.</strong>', 'w3-total-cache' ) )
25
+ ) );
26
+ Util_Ui::config_item( array(
27
+ 'key' => 'stats.slot_seconds',
28
+ 'label' => __( 'Slot time (seconds):', 'w3-total-cache' ),
29
+ 'control' => 'textbox',
30
+ 'textbox_type' => 'number',
31
+ 'description' =>
32
+ 'The duration of time in seconds to collect statistics per interval.'
33
+ ) );
34
+ Util_Ui::config_item( array(
35
+ 'key' => 'stats.slots_count',
36
+ 'label' => __( 'Slots collected:', 'w3-total-cache' ),
37
+ 'control' => 'textbox',
38
+ 'textbox_type' => 'number',
39
+ 'description' =>
40
+ 'The number of intervals that are represented in the graph.'
41
+ ) );
42
+
43
+ Util_Ui::config_item( array(
44
+ 'key' => 'stats.cpu.enabled',
45
+ 'control' => 'checkbox',
46
+ 'checkbox_label' => __( 'Use the system reported averages of CPU resource usage.', 'w3-total-cache' ),
47
+ 'description' => __( 'Collect CPU usage', 'w3-total-cache' )
48
+ ) );
49
+ Util_Ui::config_item( array(
50
+ 'key' => 'stats.access_log.enabled',
51
+ 'control' => 'checkbox',
52
+ 'checkbox_label' => __( 'Parse server access log', 'w3-total-cache' ),
53
+ 'disabled' => ( $is_pro ? null : true ),
54
+ 'description' => __( 'Enable collecting statistics from an Access Log. This provides much more precise statistics.', 'w3-total-cache' )
55
+ ) );
56
+ Util_Ui::config_item( array(
57
+ 'key' => 'stats.access_log.webserver',
58
+ 'label' => __( 'Webserver:', 'w3-total-cache' ),
59
+ 'control' => 'selectbox',
60
+ 'selectbox_values' => array(
61
+ 'apache' => 'Apache',
62
+ 'nginx' => 'Nginx'
63
+ ),
64
+ 'description' => 'Webserver type generating access logs.'
65
+ ) );
66
+ Util_Ui::config_item( array(
67
+ 'key' => 'stats.access_log.filename',
68
+ 'label' => __( 'Access Log Filename:', 'w3-total-cache' ),
69
+ 'control' => 'textbox',
70
+ 'textbox_size' => 60,
71
+ 'description' => 'Where your access log is located.',
72
+ 'control_after' =>
73
+ '<input type="button" class="button" id="ustats_access_log_test" value="Test" /><span id="ustats_access_log_test_result" style="padding-left: 20px"></span>'
74
+ ) );
75
+ Util_Ui::config_item( array(
76
+ 'key' => 'stats.access_log.format',
77
+ 'label' => __( 'Access Log Format:', 'w3-total-cache' ),
78
+ 'control' => 'textbox',
79
+ 'textbox_size' => 60,
80
+ 'description' =>
81
+ 'Format of your access log from webserver configuration.',
82
+ 'control_after' =>
83
+ '<input type="button" class="button" id="ustats_access_log_format_reset" value="Reset to Default" />'
84
+ ) );
85
+ ?>
86
+ </table>
87
+
88
+ <?php
89
+ Util_Ui::button_config_save( 'stats' );
90
+ ?>
91
+ <?php Util_Ui::postbox_footer(); ?>
92
+
93
+ <script>
94
+ jQuery('#ustats_access_log_format_reset').click(function() {
95
+ var webserver = jQuery('#stats__access_log__webserver').val();
96
+
97
+ var v;
98
+ if (webserver == 'nginx') {
99
+ v = '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
100
+ } else {
101
+ v = '%h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"';
102
+ }
103
+ jQuery('#stats__access_log__format').val(v);
104
+ });
105
+
106
+ jQuery('#ustats_access_log_test').click(function() {
107
+ var params = {
108
+ action: 'w3tc_ajax',
109
+ _wpnonce: w3tc_nonce,
110
+ w3tc_action: 'ustats_access_log_test',
111
+ filename: jQuery('#stats__access_log__filename').val()
112
+ };
113
+
114
+ jQuery.post(ajaxurl, params, function(data) {
115
+ jQuery('#ustats_access_log_test_result').text(data);
116
+ }).fail(function() {
117
+ jQuery('#ustats_access_log_test_result').text('Check failed');
118
+ });
119
+ });
120
+ </script>
UsageStatistics_Page.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ class UsageStatistics_Page {
5
+ static public function admin_print_scripts_w3tc_stats() {
6
+ wp_enqueue_style( 'w3tc-widget-usage-statistics',
7
+ plugins_url( 'UsageStatistics_Page_View.css', W3TC_FILE ),
8
+ array(), W3TC_VERSION );
9
+
10
+ wp_enqueue_script( 'w3tc-canvasjs',
11
+ plugins_url( 'pub/js/chartjs.min.js', W3TC_FILE ),
12
+ array(), W3TC_VERSION );
13
+ wp_enqueue_script( 'w3tc-widget-usage-statistics',
14
+ plugins_url( 'UsageStatistics_Page_View.js', W3TC_FILE ),
15
+ array( 'w3tc-canvasjs' ), W3TC_VERSION );
16
+
17
+ }
18
+
19
+
20
+
21
+ public function render() {
22
+ $c = Dispatcher::config();
23
+ $enabled = ( $c->get_boolean( 'stats.enabled' ) &&
24
+ Util_Environment::is_w3tc_pro( $c ) );
25
+ if ( !$enabled ) {
26
+ if ( !Util_Environment::is_w3tc_pro( $c ) ) {
27
+ include W3TC_DIR . '/UsageStatistics_Page_View_Free.php';
28
+ } else {
29
+ include W3TC_DIR . '/UsageStatistics_Page_View_Disabled.php';
30
+ }
31
+ return;
32
+ }
33
+
34
+ if ( isset( $_REQUEST['view'] ) && $_REQUEST['view'] == 'db_requests' ) {
35
+ $storage = new UsageStatistics_StorageReader();
36
+ $summary = $storage->get_history_summary();
37
+ $timestamp_start = $summary['period']['timestamp_start'];
38
+
39
+ $sort_column = isset( $_REQUEST['sort'] ) ? $_REQUEST['sort'] : '';
40
+ if ( !in_array( $sort_column, array(
41
+ 'query', 'count_total', 'count_hit', 'avg_size',
42
+ 'avg_time_ms', 'sum_time_ms' ) ) ) {
43
+ $sort_column = 'sum_time_ms';
44
+ }
45
+
46
+ if ( !$c->get_boolean( 'dbcache.debug' ) ) {
47
+ include W3TC_DIR . '/UsageStatistics_Page_View_NoDebugMode.php';
48
+ return;
49
+ }
50
+
51
+ $reader = new UsageStatistics_Source_DbQueriesLog( $timestamp_start,
52
+ $sort_column );
53
+ $items = $reader->list();
54
+
55
+ $result = array(
56
+ 'date_min' =>
57
+ Util_UsageStatistics::time_mins( $timestamp_start ),
58
+ 'date_max' => Util_UsageStatistics::time_mins( time() ),
59
+ 'sort_column' => $sort_column,
60
+ 'items' => $items
61
+ );
62
+
63
+ include W3TC_DIR . '/UsageStatistics_Page_DbRequests_View.php';
64
+ } elseif ( isset( $_REQUEST['view'] ) && $_REQUEST['view'] == 'oc_requests' ) {
65
+ $storage = new UsageStatistics_StorageReader();
66
+ $summary = $storage->get_history_summary();
67
+ $timestamp_start = $summary['period']['timestamp_start'];
68
+
69
+ $sort_column = isset( $_REQUEST['sort'] ) ? $_REQUEST['sort'] : '';
70
+ if ( !in_array( $sort_column, array(
71
+ 'group', 'count_total', 'count_get_total', 'count_get_hit',
72
+ 'count_set', 'avg_size', 'sum_size', 'sum_time_ms' ) ) ) {
73
+ $sort_column = 'sum_time_ms';
74
+ }
75
+
76
+ if ( !$c->get_boolean( 'objectcache.debug' ) ) {
77
+ include W3TC_DIR . '/UsageStatistics_Page_View_NoDebugMode.php';
78
+ return;
79
+ }
80
+
81
+ $reader = new UsageStatistics_Source_ObjectCacheLog( $timestamp_start,
82
+ $sort_column );
83
+ $items = $reader->list();
84
+
85
+ $result = array(
86
+ 'date_min' =>
87
+ Util_UsageStatistics::time_mins( $timestamp_start ),
88
+ 'date_max' => Util_UsageStatistics::time_mins( time() ),
89
+ 'sort_column' => $sort_column,
90
+ 'items' => $items
91
+ );
92
+
93
+ include W3TC_DIR . '/UsageStatistics_Page_ObjectCacheLog_View.php';
94
+ } elseif ( isset( $_REQUEST['view'] ) && $_REQUEST['view'] == 'pagecache_requests' ) {
95
+ $storage = new UsageStatistics_StorageReader();
96
+ $summary = $storage->get_history_summary();
97
+ $timestamp_start = $summary['period']['timestamp_start'];
98
+
99
+ $sort_column = isset( $_REQUEST['sort'] ) ? $_REQUEST['sort'] : '';
100
+ if ( !in_array( $sort_column, array(
101
+ 'uri', 'count', 'avg_size', 'avg_time_ms',
102
+ 'sum_time_ms' ) ) ) {
103
+ $sort_column = 'sum_time_ms';
104
+ }
105
+
106
+ if ( !$c->get_boolean( 'pgcache.debug' ) ) {
107
+ include W3TC_DIR . '/UsageStatistics_Page_View_NoDebugMode.php';
108
+ return;
109
+ }
110
+
111
+ $reader = new UsageStatistics_Source_PageCacheLog( $timestamp_start,
112
+ $_REQUEST['status'], $sort_column );
113
+ $items = $reader->list();
114
+
115
+ $result = array(
116
+ 'date_min' =>
117
+ Util_UsageStatistics::time_mins( $timestamp_start ),
118
+ 'date_max' => Util_UsageStatistics::time_mins( time() ),
119
+ 'sort_column' => $sort_column,
120
+ 'items' => $items
121
+ );
122
+
123
+ include W3TC_DIR . '/UsageStatistics_Page_PageCacheRequests_View.php';
124
+ } else {
125
+ $c = Dispatcher::config();
126
+
127
+ $php_php_requests_pagecache_hit_name = 'Cache hit';
128
+ if ( $c->get_boolean( 'pgcache.enabled' ) &&
129
+ $c->get_string( 'pgcache.engine' ) == 'file_generic' ) {
130
+ $php_php_requests_pagecache_hit_name = 'Cache fallback hit';
131
+ }
132
+
133
+ include W3TC_DIR . '/UsageStatistics_Page_View.php';
134
+ }
135
+ }
136
+
137
+
138
+
139
+ public function sort_link( $result, $name, $sort_column ) {
140
+ $name_esc = esc_html( $name );
141
+ if ( $result['sort_column'] == $sort_column ) {
142
+ echo "<strong>$name_esc</strong>";
143
+ return;
144
+ }
145
+
146
+ $new_query_string = $_GET;
147
+ $new_query_string['sort'] = $sort_column;
148
+
149
+ $url_esc = esc_url(
150
+ 'admin.php?' . http_build_query( $new_query_string ) );
151
+
152
+ echo "<a href='$url_esc'>$name_esc</a>";
153
+ }
154
+
155
+
156
+
157
+ public function summary_item( $id, $name, $checked = false,
158
+ $extra_class = '', $column_background = '', $link_key = '' ) {
159
+ echo "<div class='ustats_$id $extra_class'>\n";
160
+ echo '<label>';
161
+ echo '<input type="checkbox" name="';
162
+ echo esc_attr( 'w3tcus_chart_check_' . $id ) . '" ';
163
+ echo 'data-name="' . esc_attr( $name ) . '" ';
164
+ echo 'data-column="' . esc_attr( $id ) . '" ';
165
+
166
+ if ( !empty( $column_background ) ) {
167
+ echo 'data-background="' . esc_attr( $column_background ) . '" ';
168
+ }
169
+
170
+ echo 'class="w3tc-ignore-change w3tcus_chart_check" ';
171
+ checked( $checked );
172
+ echo ' />';
173
+ if ( !empty( $link_key ) ) {
174
+ echo "<a href='" .
175
+ esc_url( 'admin.php?page=w3tc_stats&view=pagecache_requests&status=' .
176
+ urlencode( $link_key ) . '&status_name=' . urlencode( $name ) ) .
177
+ "'>$name</a>";
178
+ } else {
179
+ echo $name;
180
+ }
181
+ echo ": <span></span>\n";
182
+
183
+ echo '</label>';
184
+ echo '</div>';
185
+ }
186
+
187
+ }
UsageStatistics_Page_DbRequests_View.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+ ?>
9
+ <div class="metabox-holder">
10
+ <?php Util_Ui::postbox_header( __( 'Usage Statistics', 'w3-total-cache' ) ); ?>
11
+
12
+ <div style="float: right"><a href="admin.php?page=w3tc_stats">&lt; Back To Statistics</a></div>
13
+ <h1>Database Queries</h1>
14
+ <p>
15
+ Period
16
+ <?php echo $result['date_min'] ?>
17
+ -
18
+ <?php echo $result['date_max'] ?>
19
+ </p>
20
+
21
+ <table style="width: 100%">
22
+ <tr>
23
+ <td><?php $this->sort_link( $result, 'Query', 'query' ) ?></td>
24
+ <td><?php $this->sort_link( $result, 'Count', 'count_total' ) ?></td>
25
+ <td><?php $this->sort_link( $result, 'Cache Hits', 'count_hit' ) ?></td>
26
+ <td><?php $this->sort_link( $result, 'Total processed time (ms)', 'sum_time_ms' ) ?></td>
27
+ <td><?php $this->sort_link( $result, 'Avg Processed time (ms)', 'avg_time_ms' ) ?></td>
28
+ <td><?php $this->sort_link( $result, 'Avg Size', 'avg_size' ) ?></td>
29
+ </tr>
30
+ <?php foreach ($result['items'] as $i): ?>
31
+ <tr>
32
+ <td title="Reject reasons: <?php echo esc_attr( implode( ',', $i['reasons'] ) ) ?>"><?php echo esc_html($i['query']) ?></td>
33
+ <td><?php echo esc_html($i['count_total']) ?></td>
34
+ <td><?php echo esc_html($i['count_hit']) ?></td>
35
+ <td><?php echo esc_html($i['sum_time_ms']) ?></td>
36
+ <td><?php echo esc_html($i['avg_time_ms']) ?></td>
37
+ <td><?php echo $i['avg_size'] == 0 ? 'n/a' : esc_html($i['avg_size']) ?></td>
38
+ </tr>
39
+ <?php endforeach ?>
40
+ </table>
41
+
42
+ <?php Util_Ui::postbox_footer(); ?>
43
+ </div>
UsageStatistics_Page_ObjectCacheLog_View.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+ ?>
9
+ <div class="metabox-holder">
10
+ <?php Util_Ui::postbox_header( __( 'Usage Statistics', 'w3-total-cache' ) ); ?>
11
+
12
+ <div style="float: right"><a href="admin.php?page=w3tc_stats">&lt; Back To Statistics</a></div>
13
+ <h1>Object Cache Calls</h1>
14
+ <p>
15
+ Period
16
+ <?php echo $result['date_min'] ?>
17
+ -
18
+ <?php echo $result['date_max'] ?>
19
+ </p>
20
+
21
+ <table style="width: 100%">
22
+ <tr>
23
+ <td><?php $this->sort_link( $result, 'Group', 'group' ) ?></td>
24
+ <td><?php $this->sort_link( $result, 'Calls', 'count_total' ) ?></td>
25
+ <td><?php $this->sort_link( $result, 'Get', 'count_get_total' ) ?></td>
26
+ <td><?php $this->sort_link( $result, 'Cache Hits', 'count_get_hit' ) ?></td>
27
+ <td><?php $this->sort_link( $result, 'Set', 'count_set' ) ?></td>
28
+ <td><?php $this->sort_link( $result, 'Total processed time (ms)', 'sum_time_ms' ) ?></td>
29
+ <td><?php $this->sort_link( $result, 'Total size transfeffed', 'sum_size' ) ?></td>
30
+ <td><?php $this->sort_link( $result, 'Avg Size', 'avg_size' ) ?></td>
31
+ </tr>
32
+ <?php foreach ($result['items'] as $i): ?>
33
+ <tr>
34
+ <td><?php echo esc_html($i['group']) ?></td>
35
+ <td><?php echo esc_html($i['count_total']) ?></td>
36
+ <td><?php echo esc_html($i['count_get_total']) ?></td>
37
+ <td><?php echo esc_html($i['count_get_hit']) ?></td>
38
+ <td><?php echo esc_html($i['count_set']) ?></td>
39
+ <td><?php echo esc_html($i['sum_time_ms']) ?></td>
40
+ <td><?php echo $i['sum_size'] == 0 ? 'n/a' : esc_html($i['sum_size']) ?></td>
41
+ <td><?php echo $i['avg_size'] == 0 ? 'n/a' : esc_html($i['avg_size']) ?></td>
42
+ </tr>
43
+ <?php endforeach ?>
44
+ </table>
45
+
46
+ <?php Util_Ui::postbox_footer(); ?>
47
+ </div>
UsageStatistics_Page_PageCacheRequests_View.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+ ?>
9
+ <div class="metabox-holder">
10
+ <?php Util_Ui::postbox_header( __( 'Usage Statistics', 'w3-total-cache' ) ); ?>
11
+
12
+ <div style="float: right"><a href="admin.php?page=w3tc_stats">&lt; Back To Statistics</a></div>
13
+ <h1>Page Cache Reject Requests for <?php echo esc_html( $_REQUEST['status_name'] ) ?></h1>
14
+ <p>
15
+ Period
16
+ <?php echo $result['date_min'] ?>
17
+ -
18
+ <?php echo $result['date_max'] ?>
19
+ </p>
20
+
21
+ <table style="width: 100%">
22
+ <tr>
23
+ <td><?php $this->sort_link( $result, 'URI', 'uri' ) ?></td>
24
+ <td><?php $this->sort_link( $result, 'Count', 'count' ) ?></td>
25
+ <td><?php $this->sort_link( $result, 'Total processed time (ms)', 'sum_time_ms' ) ?></td>
26
+ <td><?php $this->sort_link( $result, 'Avg Processed time (ms)', 'avg_time_ms' ) ?></td>
27
+ <td><?php $this->sort_link( $result, 'Avg Size', 'avg_size' ) ?></td>
28
+ </tr>
29
+ <?php foreach ($result['items'] as $i): ?>
30
+ <tr>
31
+ <td title="Reject reasons: <?php echo esc_attr( implode( ',', $i['reasons'] ) ) ?>"><?php echo esc_html($i['uri']) ?></td>
32
+ <td><?php echo esc_html($i['count']) ?></td>
33
+ <td><?php echo esc_html($i['sum_time_ms']) ?></td>
34
+ <td><?php echo esc_html($i['avg_time_ms']) ?></td>
35
+ <td><?php echo $i['avg_size'] == 0 ? 'n/a' : esc_html($i['avg_size']) ?></td>
36
+ </tr>
37
+ <?php endforeach ?>
38
+ </table>
39
+
40
+ <?php Util_Ui::postbox_footer(); ?>
41
+ </div>
UsageStatistics_Page_View.css ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .ustats_ad {
2
+ max-width: 600px;
3
+ margin: auto;
4
+ padding-top: 50px;
5
+ }
6
+ .ustats_ad_metabox .inside {
7
+ background: url('./pub/img/stats-bg.png');
8
+ background-repeat: no-repeat;
9
+ background-size: cover;
10
+ min-height: 400px;
11
+ font-weight: bold;
12
+ }
13
+
14
+ .ustats_content {
15
+ text-align: right;
16
+ }
17
+ .ustats_p {
18
+ text-align: center;
19
+ }
20
+ .ustats_block {
21
+ width: 100%;
22
+ display: none;
23
+ margin-bottom: 10px;
24
+
25
+ }
26
+ .ustats_block_data {
27
+ width: 30%;
28
+ padding-bottom: 10px;
29
+ }
30
+ .ustats_block_data div {
31
+ display: none;
32
+ }
33
+ .ustats_block_data .ustats_header {
34
+ font-weight: bold;
35
+ display: block;
36
+ }
37
+ .ustats_block_chart {
38
+ width: 70%;
39
+ height: 200px;
40
+ }
41
+ .ustats_block_chart canvas {
42
+ display: block;
43
+ height: 200px;
44
+ width: 800px;
45
+ }
46
+
47
+ .ustats_table {
48
+ width: 100%;
49
+ padding-left: 20px;
50
+ padding-right: 20px;
51
+ }
52
+
53
+ td.ustats_td {
54
+ padding: 0;
55
+ padding-right: 10px;
56
+ font-weight: bold;
57
+ color: #333;
58
+ }
59
+
60
+ td.ustats_td_header_name {
61
+ padding: 0;
62
+ padding-right: 5px;
63
+ padding-top: 5px;
64
+ text-align: left;
65
+ font-weight: bold;
66
+ color: #333;
67
+ }
68
+
69
+ td.ustats_td_header {
70
+ padding: 0;
71
+ padding-right: 5px;
72
+ padding-top: 5px;
73
+ text-align: right;
74
+ font-weight: bold;
75
+ color: #333;
76
+ }
77
+
78
+ td.ustats_td_value {
79
+ padding: 0;
80
+ padding-right: 8px;
81
+ text-align: right;
82
+ color: #333;
83
+ }
84
+
85
+ /* specific cache areas */
86
+ .ustats_php_php_requests_pagecache_hit,
87
+ .ustats_php_php_requests_pagecache_miss {
88
+ padding-left: 20px;
89
+ }
90
+ .ustats_block_data div.ustats_php_php_requests_pagecache_miss_level2_wrap {
91
+ display: flex;
92
+ padding-left: 40px;
93
+ flex-wrap: wrap;
94
+ }
95
+ .ustats_php_php_requests_pagecache_miss_level2 {
96
+ width: 50%;
97
+ }
98
+
99
+ @media (max-width: 1500px) {
100
+ .ustats_php_php_requests_pagecache_miss_level2 {
101
+ width: 100%
102
+ }
103
+ }
UsageStatistics_Page_View.js ADDED
@@ -0,0 +1,807 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ var lastData;
3
+
4
+
5
+
6
+ function load() {
7
+ top_object = $('.ustats_top');
8
+ $('.ustats_loading').removeClass('w3tc_hidden');
9
+ $('.ustats_content').addClass('w3tc_hidden');
10
+ $('.ustats_error').addClass('w3tc_none');
11
+ $('.ustats_nodata').addClass('w3tc_none');
12
+
13
+ $.getJSON(ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
14
+ '&w3tc_action=ustats_get',
15
+ function(data) {
16
+ lastData = data;
17
+
18
+ // show sections with data
19
+ for (p in data) {
20
+ var v = data[p];
21
+ jQuery('.ustats_' + p).css('display', 'flex');
22
+ }
23
+
24
+ setValues(data, 'ustats_');
25
+
26
+ if (data.period.seconds)
27
+ $('.ustats_content').removeClass('w3tc_hidden');
28
+ else
29
+ $('.ustats_nodata').removeClass('w3tc_none');
30
+
31
+ $('.ustats_loading').addClass('w3tc_hidden');
32
+
33
+ setCharts(data);
34
+
35
+ setRefresh(
36
+ (data && data.period ? data.period.to_update_secs : 0));
37
+
38
+ showMetaboxes();
39
+ }
40
+ ).fail(function() {
41
+ $('.ustats_error').removeClass('w3tc_none');
42
+ $('.ustats_content').addClass('w3tc_hidden');
43
+ $('.ustats_loading').addClass('w3tc_hidden');
44
+ });
45
+ }
46
+
47
+
48
+
49
+ //
50
+ // chart commons
51
+ //
52
+ var chartOptions = {
53
+ //aspectRatio: 4,
54
+ maintainAspectRatio: false,
55
+ animation: false,
56
+ legend: false,
57
+ scales: {
58
+ yAxes: [{
59
+ ticks: {
60
+ beginAtZero: true
61
+ }
62
+ }]
63
+ }
64
+ };
65
+
66
+
67
+
68
+ var chartDateLabels = [];
69
+ var chartGraphValues = {};
70
+ var charts = {};
71
+
72
+
73
+
74
+ function setCharts(data) {
75
+ // collect functors that prepare data for their own chart
76
+ var processors = [];
77
+ processors.push(setChartsPageCache());
78
+ processors.push(setChartsDb());
79
+ processors.push(setChartsOc());
80
+ processors.push(setChartsPhp());
81
+ processors.push(setChartsCpu());
82
+ processors.push(setChartsWpdb());
83
+ processors.push(setChartsAccessLog());
84
+ processors.push(setChartsMemcached());
85
+ processors.push(setChartsRedis());
86
+ processors.push(setChartsApc());
87
+
88
+ // prepare collections
89
+ var columnsToCollect = [];
90
+
91
+ for (var i = 0; i < processors.length; i++) {
92
+ for (var id in processors[i].chartDatasets) {
93
+ var datasets = [];
94
+ for (var i2 = 0; i2 < processors[i].chartDatasets[id].length; i2++) {
95
+ var datasetTemplate = processors[i].chartDatasets[id][i2];
96
+ var dataColumnString;
97
+ if (Array.isArray(datasetTemplate.dataColumn)) {
98
+ dataColumnString = datasetTemplate.dataColumn.join('.');
99
+ } else {
100
+ dataColumnString = datasetTemplate.dataColumn;
101
+ }
102
+
103
+ chartGraphValues[dataColumnString] = [];
104
+ columnsToCollect.push({
105
+ target: dataColumnString,
106
+ column: datasetTemplate.dataColumn
107
+ });
108
+ datasets.push({
109
+ label: datasetTemplate.label,
110
+ data: chartGraphValues[dataColumnString],
111
+ backgroundColor: datasetTemplate.backgroundColor
112
+ });
113
+ }
114
+
115
+ charts[id].data.datasets = datasets;
116
+ }
117
+ }
118
+
119
+ // collect data for charts
120
+ var history = data.history;
121
+ chartDateLabels.length = 0;
122
+ for (var i = 0; i < history.length; i++) {
123
+ var historyItem = history[i];
124
+ var dateFormatted = '';
125
+ if (history[i].timestamp_start) {
126
+ var d = new Date(parseInt(history[i].timestamp_start) * 1000);
127
+ dateFormatted = dateFormat(d);
128
+ }
129
+
130
+ chartDateLabels.push(dateFormatted);
131
+
132
+ // custom preprocess history row
133
+ for (var i2 = 0; i2 < processors.length; i2++) {
134
+ if (processors[i2].preprocess) {
135
+ processors[i2].preprocess(historyItem);
136
+ }
137
+ }
138
+
139
+ // collect metrics for graphs
140
+ for (var i2 = 0; i2 < columnsToCollect.length; i2++) {
141
+ var c = columnsToCollect[i2];
142
+ var v;
143
+ if (Array.isArray(c.column)) {
144
+ if (v = historyItem[c.column[0]]) {
145
+ v = historyItem[c.column[0]][c.column[1]];
146
+ }
147
+ } else {
148
+ v = historyItem[c.column];
149
+ }
150
+
151
+ chartGraphValues[c.target].push(v);
152
+ }
153
+ }
154
+
155
+ // visualize
156
+ for (var c in charts) {
157
+ charts[c].update();
158
+ }
159
+ }
160
+
161
+
162
+
163
+ $('.w3tcus_chart_check').click(function() {
164
+ setCharts(lastData);
165
+ });
166
+
167
+
168
+
169
+ //
170
+ // PageCache chart
171
+ //
172
+ function setChartsPageCache() {
173
+ if (!charts['pagecache']) {
174
+ var ctx = $('#w3tcus_pagecache_chart');
175
+ charts['pagecache'] = new Chart(ctx, {
176
+ type: 'bar',
177
+ data: {
178
+ labels: chartDateLabels,
179
+ },
180
+ options: chartOptions
181
+ });
182
+ }
183
+
184
+
185
+ return {
186
+ chartDatasets: {
187
+ pagecache: [{
188
+ label: 'Time (ms)',
189
+ dataColumn: 'pagecache_requests_time_ms',
190
+ backgroundColor: '#0073aa'
191
+ }
192
+ ]
193
+ },
194
+ preprocess: function(historyItem) {
195
+ v = 0;
196
+ if (historyItem.pagecache_requests_time_10ms && historyItem.php_requests) {
197
+ v = ((historyItem.pagecache_requests_time_10ms * 10) /
198
+ historyItem.php_requests).toFixed(0);
199
+ }
200
+ historyItem.pagecache_requests_time_ms = v;
201
+ }
202
+ };
203
+ }
204
+
205
+
206
+
207
+ //
208
+ // Database chart
209
+ //
210
+ function setChartsDb() {
211
+ if (!charts['db']) {
212
+ var ctx = $('#w3tcus_dbcache_chart');
213
+ charts['db'] = new Chart(ctx, {
214
+ type: 'bar',
215
+ data: {
216
+ labels: chartDateLabels,
217
+ },
218
+ options: chartOptions
219
+ });
220
+
221
+ var ctx = $('#w3tcus_dbcache_time_chart');
222
+ charts['db_time'] = new Chart(ctx, {
223
+ type: 'bar',
224
+ data: {
225
+ labels: chartDateLabels,
226
+ },
227
+ options: chartOptions
228
+ });
229
+ }
230
+
231
+
232
+ return {
233
+ chartDatasets: {
234
+ db_time: [{
235
+ label: 'Time (ms)',
236
+ dataColumn: 'dbcache_time_ms',
237
+ backgroundColor: '#0073aa'
238
+ }
239
+ ],
240
+ db: [{
241
+ label: 'Calls',
242
+ dataColumn: 'dbcache_calls_total',
243
+ backgroundColor: '#0073aa'
244
+ }, {
245
+ label: 'Hits',
246
+ dataColumn: 'dbcache_calls_hits',
247
+ backgroundColor: 'green'
248
+ }
249
+ ]
250
+ }
251
+ };
252
+ }
253
+
254
+
255
+
256
+ //
257
+ // OC chart
258
+ //
259
+ function setChartsOc(data) {
260
+ if (!charts['oc']) {
261
+ var ctx = $('#w3tcus_objectcache_chart');
262
+ charts['oc'] = new Chart(ctx, {
263
+ type: 'bar',
264
+ data: {
265
+ labels: chartDateLabels
266
+ },
267
+ options: chartOptions
268
+ });
269
+
270
+ var ctx = $('#w3tcus_objectcache_time_chart');
271
+ charts['oc_time'] = new Chart(ctx, {
272
+ type: 'bar',
273
+ data: {
274
+ labels: chartDateLabels
275
+ },
276
+ options: chartOptions
277
+ });
278
+ }
279
+
280
+ return {
281
+ chartDatasets: {
282
+ oc_time: [{
283
+ label: 'Time (ms)',
284
+ dataColumn: 'objectcache_time_ms',
285
+ backgroundColor: '#0073aa'
286
+ }
287
+ ],
288
+ oc: [{
289
+ label: 'Gets',
290
+ dataColumn: 'objectcache_get_total',
291
+ backgroundColor: '#0073aa'
292
+ }, {
293
+ label: 'Hits',
294
+ dataColumn: 'objectcache_get_hits',
295
+ backgroundColor: 'green'
296
+ }, {
297
+ label: 'Sets',
298
+ dataColumn: 'objectcache_sets',
299
+ backgroundColor: 'red'
300
+ }
301
+ ]
302
+ }
303
+ };
304
+ }
305
+
306
+
307
+
308
+ //
309
+ // PHP chart
310
+ //
311
+ function setChartsPhp(data) {
312
+ if (!charts['phpMemory']) {
313
+ var ctx = $('#w3tcus_php_memory_chart');
314
+ charts['phpMemory'] = new Chart(ctx, {
315
+ type: 'bar',
316
+ data: {
317
+ labels: chartDateLabels,
318
+ },
319
+ options: chartOptions
320
+ });
321
+ }
322
+ if (!charts['phpRequests']) {
323
+ var ctx = $('#w3tcus_php_requests_chart');
324
+ charts['phpRequests'] = new Chart(ctx, {
325
+ type: 'bar',
326
+ data: {
327
+ labels: chartDateLabels
328
+ },
329
+ options: chartOptions
330
+ });
331
+ }
332
+
333
+ var phpRequestsDatasets = [];
334
+ $('.w3tcus_chart_check').each(function() {
335
+ if ($(this).is(':checked')) {
336
+ var dataColumn = $(this).attr('data-column');
337
+ var backgroundColor = $(this).attr('data-background');
338
+ if (!backgroundColor) {
339
+ backgroundColor = '#0073aa';
340
+ }
341
+
342
+ if (startsWith(dataColumn, 'php_php_requests')) {
343
+ phpRequestsDatasets.push({
344
+ label: $(this).attr('data-name'),
345
+ dataColumn: dataColumn.substr(4),
346
+ backgroundColor: backgroundColor
347
+ });
348
+ }
349
+ }
350
+ });
351
+
352
+ return {
353
+ chartDatasets: {
354
+ phpMemory: [{
355
+ label: 'MB',
356
+ dataColumn: 'php_memory_mb',
357
+ backgroundColor: '#0073aa'
358
+ }
359
+ ],
360
+ phpRequests: phpRequestsDatasets
361
+ },
362
+ preprocess: function(historyItem) {
363
+ var v = 0;
364
+ if (historyItem.php_requests) {
365
+ v = (historyItem.php_memory_100kb / 100.0 / historyItem.php_requests).toFixed(2)
366
+ }
367
+ historyItem.php_memory_mb = v;
368
+
369
+ historyItem.php_requests_pagecache_miss =
370
+ historyItem.php_requests - historyItem.php_requests_pagecache_hit;
371
+ }
372
+ };
373
+ }
374
+
375
+
376
+
377
+ //
378
+ // CPU chart
379
+ //
380
+ function setChartsCpu(data) {
381
+ if (!charts['cpu']) {
382
+ var ctx = $('#w3tcus_cpu_chart');
383
+ charts['cpu'] = new Chart(ctx, {
384
+ type: 'bar',
385
+ data: {
386
+ labels: chartDateLabels,
387
+ },
388
+ options: chartOptions
389
+ });
390
+ }
391
+
392
+ return {
393
+ chartDatasets: {
394
+ cpu: [{
395
+ label: 'CPU',
396
+ dataColumn: 'cpu',
397
+ backgroundColor: '#0073aa'
398
+ }
399
+ ]
400
+ }
401
+ };
402
+ }
403
+
404
+
405
+
406
+ //
407
+ // WPDB chart
408
+ //
409
+ function setChartsWpdb(data) {
410
+ if (!charts['wpdb']) {
411
+ var ctx = $('#w3tcus_wpdb_chart');
412
+ charts['wpdb'] = new Chart(ctx, {
413
+ type: 'bar',
414
+ data: {
415
+ labels: chartDateLabels,
416
+ },
417
+ options: chartOptions
418
+ });
419
+ }
420
+
421
+ return {
422
+ chartDatasets: {
423
+ wpdb: [{
424
+ label: 'Total',
425
+ dataColumn: 'wpdb_calls_total',
426
+ backgroundColor: '#0073aa'
427
+ }]
428
+ }
429
+ };
430
+ }
431
+
432
+
433
+
434
+ //
435
+ // Access Log chart
436
+ //
437
+ function setChartsAccessLog(data) {
438
+ if (!charts['accessLogRequests']) {
439
+ var ctx = $('#w3tcus_access_log_chart_requests');
440
+ charts['accessLogRequests'] = new Chart(ctx, {
441
+ type: 'bar',
442
+ data: {
443
+ labels: chartDateLabels,
444
+ },
445
+ options: chartOptions
446
+ });
447
+ }
448
+ if (!charts['accessLogTiming']) {
449
+ var ctx = $('#w3tcus_access_log_chart_timing');
450
+ charts['accessLogTiming'] = new Chart(ctx, {
451
+ type: 'bar',
452
+ data: {
453
+ labels: chartDateLabels
454
+ },
455
+ options: chartOptions
456
+ });
457
+ }
458
+
459
+ return {
460
+ chartDatasets: {
461
+ accessLogRequests: [{
462
+ label: 'Dynamic',
463
+ dataColumn: ['access_log', 'dynamic_count'],
464
+ backgroundColor: '#0073aa'
465
+ }, {
466
+ label: 'Static',
467
+ dataColumn: ['access_log', 'static_count'],
468
+ backgroundColor: '#0073aa'
469
+ }
470
+ ],
471
+ accessLogTiming: [{
472
+ label: 'Dynamic',
473
+ dataColumn: ['access_log', 'dynamic_timing'],
474
+ backgroundColor: '#0073aa'
475
+ }, {
476
+ label: 'Static',
477
+ dataColumn: ['access_log', 'static_timing'],
478
+ backgroundColor: '#0073aa'
479
+ }
480
+ ]
481
+ },
482
+ preprocess: function(historyItem) {
483
+ var dc = 0, sc = 0, dt = 0, st = 0;
484
+ if (historyItem.access_log) {
485
+ var a = historyItem.access_log;
486
+ dc = a.dynamic_count;
487
+ if (dc) {
488
+ dt = (a.dynamic_timetaken_ms / dc).toFixed(2);
489
+ }
490
+
491
+ sc = a.static_count;
492
+ if (sc) {
493
+ st = (a.static_timetaken_ms / dc).toFixed(2);
494
+ }
495
+
496
+ historyItem['access_log']['dynamic_timing'] = dt;
497
+ historyItem['access_log']['static_timing'] = st;
498
+ }
499
+ }
500
+ };
501
+ }
502
+
503
+
504
+
505
+ //
506
+ // Memcached chart
507
+ //
508
+ function setChartsMemcached(data) {
509
+ if (!charts['memcachedSize']) {
510
+ var ctx = $('#w3tcus_memcached_size_chart');
511
+ charts['memcachedSize'] = new Chart(ctx, {
512
+ type: 'bar',
513
+ data: {
514
+ labels: chartDateLabels
515
+ },
516
+ options: chartOptions
517
+ });
518
+ }
519
+
520
+ if (!charts['memcachedHit']) {
521
+ var ctx = $('#w3tcus_memcached_hit_chart');
522
+ charts['memcachedHit'] = new Chart(ctx, {
523
+ type: 'bar',
524
+ data: {
525
+ labels: chartDateLabels
526
+ },
527
+ options: chartOptions
528
+ });
529
+ }
530
+
531
+ var prevCalls = -1;
532
+ var prevHits = -1;
533
+
534
+ return {
535
+ chartDatasets: {
536
+ memcachedSize: [{
537
+ label: 'MB',
538
+ dataColumn: 'memcached_size_mb',
539
+ backgroundColor: '#0073aa'
540
+ }],
541
+ memcachedHit: [{
542
+ label: 'Calls',
543
+ dataColumn: 'memcached_requests_total',
544
+ backgroundColor: '#0073aa'
545
+ }, {
546
+ label: 'Hits',
547
+ dataColumn: 'memcached_requests_hits',
548
+ backgroundColor: 'green'
549
+ }
550
+ ]
551
+ },
552
+ preprocess: function(historyItem) {
553
+ var size = 0;
554
+ var calls = 0;
555
+ var hits = 0;
556
+ if (historyItem.memcached && historyItem.memcached.size_used) {
557
+ size = (historyItem.memcached.size_used / 1024.0 / 1024.0).toFixed(2);
558
+ if (prevCalls >= 0 && historyItem.memcached.get_calls >= prevCalls) {
559
+ calls = historyItem.memcached.get_calls - prevCalls;
560
+ hits = historyItem.memcached.get_hits - prevHits;
561
+ }
562
+
563
+ if (calls > 10000) {
564
+ calls = 0;
565
+ hits = 0;
566
+ }
567
+ prevCalls = historyItem.memcached.get_calls;
568
+ prevHits = historyItem.memcached.get_hits;
569
+ }
570
+
571
+ historyItem.memcached_size_mb = size;
572
+ historyItem.memcached_requests_total = calls;
573
+ historyItem.memcached_requests_hits = hits;
574
+ }
575
+ };
576
+ }
577
+
578
+
579
+
580
+ //
581
+ // Redis chart
582
+ //
583
+ function setChartsRedis(data) {
584
+ if (!charts['redisSize']) {
585
+ var ctx = $('#w3tcus_redis_size_chart');
586
+ charts['redisSize'] = new Chart(ctx, {
587
+ type: 'bar',
588
+ data: {
589
+ labels: chartDateLabels
590
+ },
591
+ options: chartOptions
592
+ });
593
+ }
594
+
595
+ if (!charts['redisHit']) {
596
+ var ctx = $('#w3tcus_redis_hit_chart');
597
+ charts['redisHit'] = new Chart(ctx, {
598
+ type: 'bar',
599
+ data: {
600
+ labels: chartDateLabels
601
+ },
602
+ options: chartOptions
603
+ });
604
+ }
605
+
606
+ var prevCalls = -1;
607
+ var prevHits = -1;
608
+
609
+ return {
610
+ chartDatasets: {
611
+ redisSize: [{
612
+ label: 'MB',
613
+ dataColumn: 'redis_size_mb',
614
+ backgroundColor: '#0073aa'
615
+ }],
616
+ redisHit: [{
617
+ label: 'Calls',
618
+ dataColumn: 'redis_requests_total',
619
+ backgroundColor: '#0073aa'
620
+ }, {
621
+ label: 'Hits',
622
+ dataColumn: 'redis_requests_hits',
623
+ backgroundColor: 'green'
624
+ }
625
+ ]
626
+ },
627
+ preprocess: function(historyItem) {
628
+ var size = 0;
629
+ var calls = 0;
630
+ var hits = 0;
631
+ if (historyItem.redis && historyItem.redis.size_used) {
632
+ size = (historyItem.redis.size_used / 1024.0 / 1024.0).toFixed(2);
633
+ if (prevCalls >= 0 && historyItem.redis.get_calls >= prevCalls) {
634
+ calls = historyItem.redis.get_calls - prevCalls;
635
+ hits = historyItem.redis.get_hits - prevHits;
636
+ }
637
+
638
+ if (calls > 10000) {
639
+ calls = 0;
640
+ hits = 0;
641
+ }
642
+ prevCalls = historyItem.redis.get_calls;
643
+ prevHits = historyItem.redis.get_hits;
644
+ }
645
+
646
+ historyItem.redis_size_mb = size;
647
+ historyItem.redis_requests_total = calls;
648
+ historyItem.redis_requests_hits = hits;
649
+ }
650
+ };
651
+ }
652
+
653
+
654
+
655
+ //
656
+ // APC chart
657
+ //
658
+ function setChartsApc(data) {
659
+ if (!charts['apcSize']) {
660
+ var ctx = $('#w3tcus_apc_size_chart');
661
+ charts['apcSize'] = new Chart(ctx, {
662
+ type: 'bar',
663
+ data: {
664
+ labels: chartDateLabels
665
+ },
666
+ options: chartOptions
667
+ });
668
+ }
669
+
670
+ if (!charts['apcHit']) {
671
+ var ctx = $('#w3tcus_apc_hit_chart');
672
+ charts['apcHit'] = new Chart(ctx, {
673
+ type: 'bar',
674
+ data: {
675
+ labels: chartDateLabels
676
+ },
677
+ options: chartOptions
678
+ });
679
+ }
680
+
681
+ var prevCalls = -1;
682
+ var prevHits = -1;
683
+
684
+ return {
685
+ chartDatasets: {
686
+ apcSize: [{
687
+ label: 'MB',
688
+ dataColumn: 'apc_size_mb',
689
+ backgroundColor: '#0073aa'
690
+ }],
691
+ apcHit: [{
692
+ label: 'Calls',
693
+ dataColumn: 'apc_requests_total',
694
+ backgroundColor: '#0073aa'
695
+ }, {
696
+ label: 'Hits',
697
+ dataColumn: 'apc_requests_hits',
698
+ backgroundColor: 'green'
699
+ }
700
+ ]
701
+ },
702
+ preprocess: function(historyItem) {
703
+ var size = 0;
704
+ var calls = 0;
705
+ var hits = 0;
706
+ if (historyItem.apc && historyItem.apc.size_used) {
707
+ size = (historyItem.apc.size_used / 1024.0 / 1024.0).toFixed(2);
708
+ if (prevCalls >= 0 && historyItem.apc.get_total >= prevCalls) {
709
+ calls = historyItem.apc.get_total - prevCalls;
710
+ hits = historyItem.apc.get_hits - prevHits;
711
+ }
712
+
713
+ if (calls > 10000) {
714
+ calls = 0;
715
+ hits = 0;
716
+ }
717
+ prevCalls = historyItem.apc.get_total;
718
+ prevHits = historyItem.apc.get_hits;
719
+ }
720
+
721
+ historyItem.apc_size_mb = size;
722
+ historyItem.apc_requests_total = calls;
723
+ historyItem.apc_requests_hits = hits;
724
+ }
725
+ };
726
+ }
727
+
728
+
729
+
730
+ //
731
+ // Utils
732
+ //
733
+ function startsWith(s, prefix) {
734
+ return s.substr(0, prefix.length) == prefix;
735
+ }
736
+
737
+
738
+
739
+ function dateFormat(d) {
740
+ return ("0" + d.getUTCHours()).slice(-2) + ":" +
741
+ ("0" + d.getUTCMinutes()).slice(-2);
742
+ }
743
+
744
+
745
+
746
+ function setValues(data, css_class_prefix) {
747
+ for (p in data) {
748
+ var v = data[p];
749
+ if (typeof(v) != 'string' && typeof(v) != 'number')
750
+ setValues(v, css_class_prefix + p + '_');
751
+ else {
752
+ jQuery('.' + css_class_prefix + p + ' span').html(v);
753
+ if (jQuery('.' + css_class_prefix + p).hasClass('w3tcus_inline')) {
754
+ jQuery('.' + css_class_prefix + p).css('display', 'inline');
755
+ } else {
756
+ jQuery('.' + css_class_prefix + p).css('display', 'block');
757
+ }
758
+ }
759
+ }
760
+ }
761
+
762
+
763
+
764
+ var seconds_timer_id;
765
+ function setRefresh(new_seconds_till_refresh) {
766
+ clearTimeout(seconds_timer_id);
767
+ var seconds_till_refresh = new_seconds_till_refresh;
768
+
769
+ seconds_timer_id = setInterval(function() {
770
+ seconds_till_refresh--;
771
+ if (seconds_till_refresh <= 0) {
772
+ clearTimeout(seconds_timer_id);
773
+ seconds_timer_id = null;
774
+ load();
775
+ return;
776
+ }
777
+
778
+ jQuery('.ustats_reload').text('Will be recalculated in ' +
779
+ seconds_till_refresh + ' second' +
780
+ (seconds_till_refresh > 1 ? 's' : ''));
781
+ }, 1000);
782
+ }
783
+
784
+
785
+
786
+ function showMetaboxes() {
787
+ jQuery('.metabox-holder').each(function() {
788
+ var visible = false;
789
+ jQuery(this).find('.ustats_block').each(function() {
790
+ visible |= jQuery(this).css('display') != 'none';
791
+ });
792
+
793
+ jQuery(this).css('display', (visible ? '' : 'none'));
794
+ });
795
+ }
796
+
797
+
798
+ //
799
+ // Main entry
800
+ //
801
+ load();
802
+
803
+ $('.ustats_reload').click(function(e) {
804
+ event.preventDefault();
805
+ load();
806
+ })
807
+ });
UsageStatistics_Page_View.php ADDED
@@ -0,0 +1,486 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+
9
+ ?>
10
+ <div class="ustats_loading w3tc_loading">Loading...</div>
11
+ <div class="ustats_error w3tc_none">An error occurred</div>
12
+ <div class="ustats_nodata w3tc_none">
13
+ <p>No data collected yet</p>
14
+ <a href="#" class="ustats_reload">Refresh</a>
15
+ </div>
16
+
17
+ <div class="ustats_content w3tc_hidden">
18
+ <span class="ustats_reload">.</span>
19
+ </div>
20
+
21
+ <div class="metabox-holder" style="display: none">
22
+ <?php Util_Ui::postbox_header( __( 'Web Requests', 'w3-total-cache' ) ); ?>
23
+
24
+ <div class="ustats_block ustats_pagecache">
25
+ <div class="ustats_block_data">
26
+ <div class="ustats_header">
27
+ Page Cache
28
+ <span class="ustats_pagecache_engine_name w3tcus_inline">(<span></span>)</span>
29
+ :
30
+ </div>
31
+ <div class="ustats_pagecache_size_used">
32
+ Cache size: <span></span>
33
+ </div>
34
+ <div class="ustats_pagecache_items">
35
+ Entries: <span></span>
36
+ </div>
37
+
38
+ <div class="ustats_pagecache_requests">
39
+ Requests: <span></span>
40
+ </div>
41
+ <div class="ustats_pagecache_requests_per_second">
42
+ Requests/sec: <span></span>
43
+ </div>
44
+ <div class="ustats_pagecache_requests_hit">
45
+ Cache hits: <span></span>
46
+ </div>
47
+ <div class="ustats_pagecache_requests_hit_rate">
48
+ Cache hit rate: <span></span>
49
+ </div>
50
+
51
+ <div class="ustats_pagecache_request_time_ms">
52
+ Avg processing time: <span></span> ms
53
+ </div>
54
+ <div class="ustats_pagecache_size_percent">
55
+ Size used: <span></span>
56
+ </div>
57
+ </div>
58
+ <div class="ustats_block_chart">
59
+ Request time
60
+ <canvas id="w3tcus_pagecache_chart"></canvas>
61
+ </div>
62
+ </div>
63
+
64
+ <div class="ustats_block ustats_php">
65
+ <div class="ustats_block_data">
66
+ <div class="ustats_header">PHP Requests:</div>
67
+ <div class="ustats_php_php_requests_per_second">
68
+ Requests/sec: <span></span>
69
+ </div>
70
+ <?php
71
+ $this->summary_item(
72
+ 'php_php_requests',
73
+ 'Requests/period',
74
+ true,
75
+ '',
76
+ '#009900'
77
+ );
78
+ $this->summary_item(
79
+ 'php_php_requests_pagecache_hit',
80
+ $php_php_requests_pagecache_hit_name
81
+ );
82
+ $this->summary_item(
83
+ 'php_php_requests_pagecache_miss',
84
+ 'Not cached',
85
+ false,
86
+ '',
87
+ '#990000'
88
+ );
89
+ echo '<div class="ustats_php_php_requests_pagecache_miss_level2_wrap">';
90
+ $this->summary_item(
91
+ 'php_php_requests_pagecache_miss_404',
92
+ '404',
93
+ false,
94
+ 'ustats_php_php_requests_pagecache_miss_level2',
95
+ '',
96
+ 'miss_404'
97
+ );
98
+ $this->summary_item(
99
+ 'php_php_requests_pagecache_miss_ajax',
100
+ 'AJAX',
101
+ false,
102
+ 'ustats_php_php_requests_pagecache_miss_level2',
103
+ '',
104
+ 'miss_ajax'
105
+ );
106
+ $this->summary_item(
107
+ 'php_php_requests_pagecache_miss_api_call',
108
+ 'API call',
109
+ false,
110
+ 'ustats_php_php_requests_pagecache_miss_level2',
111
+ '',
112
+ 'miss_api_call'
113
+ );
114
+ $this->summary_item(
115
+ 'php_php_requests_pagecache_miss_configuration',
116
+ 'W3TC Configuration',
117
+ false,
118
+ 'ustats_php_php_requests_pagecache_miss_level2',
119
+ '',
120
+ 'miss_configuration'
121
+ );
122
+ $this->summary_item(
123
+ 'php_php_requests_pagecache_miss_fill',
124
+ 'Cache Fill',
125
+ false,
126
+ 'ustats_php_php_requests_pagecache_miss_level2',
127
+ '',
128
+ 'miss_fill'
129
+ );
130
+ $this->summary_item(
131
+ 'php_php_requests_pagecache_miss_logged_in',
132
+ 'Logged In',
133
+ false,
134
+ 'ustats_php_php_requests_pagecache_miss_level2',
135
+ '',
136
+ 'miss_logged_in'
137
+ );
138
+ $this->summary_item(
139
+ 'php_php_requests_pagecache_miss_mfunc',
140
+ 'mfunc',
141
+ false,
142
+ 'ustats_php_php_requests_pagecache_miss_level2',
143
+ '',
144
+ 'miss_mfunc'
145
+ );
146
+ $this->summary_item(
147
+ 'php_php_requests_pagecache_miss_query_string',
148
+ 'Query String',
149
+ false,
150
+ 'ustats_php_php_requests_pagecache_miss_level2',
151
+ '',
152
+ 'miss_query_string'
153
+ );
154
+ $this->summary_item(
155
+ 'php_php_requests_pagecache_miss_third_party',
156
+ 'Third Party',
157
+ false,
158
+ 'ustats_php_php_requests_pagecache_miss_level2',
159
+ '',
160
+ 'miss_third_party'
161
+ );
162
+ $this->summary_item(
163
+ 'php_php_requests_pagecache_miss_wp_admin',
164
+ 'wp-admin',
165
+ false,
166
+ 'ustats_php_php_requests_pagecache_miss_level2',
167
+ '',
168
+ 'miss_wp_admin'
169
+ );
170
+ echo '</div>';
171
+ ?>
172
+ </div>
173
+ <div class="ustats_block_chart">
174
+ Requests handled by PHP
175
+ <canvas id="w3tcus_php_requests_chart"></canvas>
176
+ </div>
177
+ </div>
178
+
179
+ <div class="ustats_block ustats_access_log" style="height: 32vw">
180
+ <div class="ustats_block_data">
181
+ <div class="ustats_header">Access Log:</div>
182
+ <div class="ustats_access_log_dynamic_requests_total">
183
+ Dynamic Requests/period: <span></span>
184
+ </div>
185
+ <div class="ustats_access_log_dynamic_requests_per_second">
186
+ Dynamic Requests/second: <span></span>
187
+ </div>
188
+ <div class="ustats_access_log_dynamic_requests_timing">
189
+ Dynamic time to process (ms): <span></span>
190
+ </div>
191
+ <div class="ustats_access_log_static_requests_total">
192
+ Static Requests/period: <span></span>
193
+ </div>
194
+ <div class="ustats_access_log_static_requests_per_second">
195
+ Static Requests/second: <span></span>
196
+ </div>
197
+ <div class="ustats_access_log_static_requests_timing">
198
+ Static time to process (ms): <span></span>
199
+ </div>
200
+ </div>
201
+ <div class="ustats_block_chart">
202
+ Requests
203
+ <canvas id="w3tcus_access_log_chart_requests"></canvas>
204
+ Time per request (ms)
205
+ <canvas id="w3tcus_access_log_chart_timing"></canvas>
206
+ </div>
207
+ </div>
208
+
209
+ <?php Util_Ui::postbox_footer(); ?>
210
+ </div>
211
+
212
+ <div class="metabox-holder" style="display: none">
213
+ <?php Util_Ui::postbox_header( __( 'Minify', 'w3-total-cache' ) ); ?>
214
+
215
+ <div class="ustats_block ustats_minify">
216
+ <div class="ustats_block_data">
217
+ <div class="ustats_header">Minify:</div>
218
+ <div class="ustats_minify_size_used">
219
+ Used: <span></span>
220
+ </div>
221
+ <div class="ustats_minify_size_items">
222
+ Files: <span></span>
223
+ </div>
224
+ <div class="ustats_minify_size_compression_css">
225
+ CSS compression in cache: <span></span>
226
+ </div>
227
+ <div class="ustats_minify_size_compression_js">
228
+ JS compression in cache: <span></span>
229
+ </div>
230
+ <div class="ustats_minify_requests_total">
231
+ Requests/period: <span></span>
232
+ </div>
233
+ <div class="ustats_minify_requests_per_second">
234
+ Requests/sec: <span></span>
235
+ </div>
236
+ <div class="ustats_minify_compression_css">
237
+ Responded CSS compression: <span></span>
238
+ </div>
239
+ <div class="ustats_minify_compression_js">
240
+ Responded JS compression: <span></span>
241
+ </div>
242
+ </div>
243
+ </div>
244
+
245
+ <?php Util_Ui::postbox_footer(); ?>
246
+ </div>
247
+
248
+ <div class="metabox-holder" style="display: none">
249
+ <?php Util_Ui::postbox_header( __( 'Object Cache', 'w3-total-cache' ) ); ?>
250
+
251
+ <div class="ustats_block ustats_objectcache" style="height: 32vw">
252
+ <div class="ustats_block_data">
253
+ <div class="ustats_header">
254
+ Object Cache
255
+ <span class="ustats_objectcache_engine_name w3tcus_inline">(<span></span>)</span>
256
+ </div>
257
+ <div class="ustats_objectcache_get_total">
258
+ Gets/period: <span></span>
259
+ </div>
260
+ <div class="ustats_objectcache_get_hits">
261
+ Hits/period: <span></span>
262
+ </div>
263
+ <div class="ustats_objectcache_hit_rate">
264
+ Hit rate: <span></span>
265
+ </div>
266
+ <div class="ustats_objectcache_sets">
267
+ Sets/period: <span></span>
268
+ </div>
269
+ <div class="ustats_objectcache_flushes">
270
+ Flushes/period: <span></span>
271
+ </div>
272
+ <div class="ustats_objectcache_time_ms">
273
+ Time taken: <span></span> ms
274
+ </div>
275
+
276
+ <div class="ustats_objectcache_calls_per_second">
277
+ Calls/sec: <span></span>
278
+ </div>
279
+
280
+ <a href="?page=w3tc_stats&view=oc_requests">Detailed view (in debug mode only)</a>
281
+ </div>
282
+ <div class="ustats_block_chart">
283
+ Time taken for ObjectCache activity
284
+ <canvas id="w3tcus_objectcache_time_chart"></canvas>
285
+ Calls
286
+ <canvas id="w3tcus_objectcache_chart"></canvas>
287
+ </div>
288
+ </div>
289
+
290
+ <div class="ustats_block ustats_fragmentcache">
291
+ <div class="ustats_block_data">
292
+ <div class="ustats_header">Fragment Cache:</div>
293
+ <div class="ustats_fragmentcache_calls_total">
294
+ Calls/period: <span></span>
295
+ </div>
296
+ <div class="ustats_fragmentcache_calls_per_second">
297
+ Calls/sec: <span></span>
298
+ </div>
299
+ <div class="ustats_fragmentcache_hit_rate">
300
+ Hit rate: <span></span>
301
+ </div>
302
+ </div>
303
+ </div>
304
+
305
+ <?php Util_Ui::postbox_footer(); ?>
306
+ </div>
307
+
308
+
309
+ <div class="metabox-holder" style="display: none">
310
+ <?php Util_Ui::postbox_header( __( 'Database', 'w3-total-cache' ) ); ?>
311
+
312
+ <div class="ustats_block ustats_dbcache" style="height: 32vw">
313
+ <div class="ustats_block_data">
314
+ <div class="ustats_header">
315
+ Database Cache
316
+ <span class="ustats_dbcache_engine_name w3tcus_inline">(<span></span>)</span>
317
+ </div>
318
+
319
+ <div class="ustats_dbcache_calls_total">
320
+ Calls/period: <span></span>
321
+ </div>
322
+ <div class="ustats_dbcache_calls_per_second">
323
+ Calls/sec: <span></span>
324
+ </div>
325
+ <div class="ustats_dbcache_hit_rate">
326
+ Hit rate: <span></span>
327
+ </div>
328
+ <div class="ustats_dbcache_flushes">
329
+ Cache flushes: <span></span>
330
+ </div>
331
+ <div class="ustats_dbcache_time_ms">
332
+ Time taken: <span></span> ms
333
+ </div>
334
+
335
+ <a href="?page=w3tc_stats&view=db_requests">Slowest requests (in debug mode only)</a>
336
+ </div>
337
+ <div class="ustats_block_chart">
338
+ Time taken for database activity
339
+ <canvas id="w3tcus_dbcache_time_chart"></canvas>
340
+ Requests
341
+ <canvas id="w3tcus_dbcache_chart"></canvas>
342
+ </div>
343
+ </div>
344
+
345
+ <div class="ustats_block ustats_wpdb">
346
+ <div class="ustats_block_data">
347
+ <div class="ustats_header">Database:</div>
348
+ <div class="ustats_wpdb_calls_total">
349
+ Calls/period: <span></span>
350
+ </div>
351
+ <div class="ustats_wpdb_calls_per_second">
352
+ Calls/sec: <span></span>
353
+ </div>
354
+ </div>
355
+ <div class="ustats_block_chart">
356
+ Requests
357
+ <canvas id="w3tcus_wpdb_chart"></canvas>
358
+ </div>
359
+ </div>
360
+
361
+ <?php Util_Ui::postbox_footer(); ?>
362
+ </div>
363
+
364
+
365
+ <div class="metabox-holder" style="display: none">
366
+ <?php Util_Ui::postbox_header( __( 'System Info', 'w3-total-cache' ) ); ?>
367
+
368
+ <div class="ustats_block ustats_php">
369
+ <div class="ustats_block_data">
370
+ <div class="ustats_header">PHP Memory:</div>
371
+ <div class="ustats_php_memory">
372
+ Memory used: <span></span>
373
+ </div>
374
+ </div>
375
+ <div class="ustats_block_chart">
376
+ Memory per request (MB)
377
+ <canvas id="w3tcus_php_memory_chart"></canvas>
378
+ </div>
379
+ </div>
380
+
381
+ <div class="ustats_block ustats_cpu">
382
+ <div class="ustats_block_data">
383
+ <div class="ustats_header">CPU load:</div>
384
+ <div class="ustats_cpu_avg">
385
+ CPU load: <span></span>
386
+ </div>
387
+ </div>
388
+ <div class="ustats_block_chart">
389
+ CPU load
390
+ <canvas id="w3tcus_cpu_chart"></canvas>
391
+ </div>
392
+ </div>
393
+
394
+ <?php Util_Ui::postbox_footer(); ?>
395
+ </div>
396
+
397
+
398
+ <div class="metabox-holder" style="display: none">
399
+ <?php Util_Ui::postbox_header( __( 'Cache Storage', 'w3-total-cache' ) ); ?>
400
+
401
+ <div class="ustats_block ustats_memcached" style="height: 32vw">
402
+ <div class="ustats_block_data">
403
+ <div class="ustats_header">Memcached</div>
404
+
405
+ <div class="ustats_memcached_used_by">
406
+ Used by <span></span>
407
+ </div>
408
+ <div class="ustats_memcached_evictions_per_second">
409
+ Evictions/sec: <span></span>
410
+ </div>
411
+ <div class="ustats_memcached_size_used">
412
+ Used: <span></span>
413
+ </div>
414
+ <div class="ustats_memcached_size_percent">
415
+ Used (%): <span></span>
416
+ </div>
417
+ <div class="ustats_memcached_get_hit_rate">
418
+ Hit rate: <span></span>
419
+ </div>
420
+ </div>
421
+ <div class="ustats_block_chart">
422
+ Size used (MB)
423
+ <canvas id="w3tcus_memcached_size_chart"></canvas>
424
+ Hit rate
425
+ <canvas id="w3tcus_memcached_hit_chart"></canvas>
426
+ </div>
427
+ </div>
428
+
429
+ <div class="ustats_block ustats_redis" style="height: 32vw">
430
+ <div class="ustats_block_data">
431
+ <div class="ustats_header">Redis</div>
432
+
433
+ <div class="ustats_redis_used_by">
434
+ Used by <span></span>
435
+ </div>
436
+ <div class="ustats_redis_evictions_per_second">
437
+ Evictions/sec: <span></span>
438
+ </div>
439
+ <div class="ustats_redis_size_used">
440
+ Used: <span></span>
441
+ </div>
442
+ <div class="ustats_redis_get_hit_rate">
443
+ Hit rate: <span></span>
444
+ </div>
445
+ </div>
446
+ <div class="ustats_block_chart">
447
+ Size used (MB)
448
+ <canvas id="w3tcus_redis_size_chart"></canvas>
449
+ Hit rate
450
+ <canvas id="w3tcus_redis_hit_chart"></canvas>
451
+ </div>
452
+ </div>
453
+
454
+ <div class="ustats_block ustats_apc" style="height: 32vw">
455
+ <div class="ustats_block_data">
456
+ <div class="ustats_header">APC</div>
457
+
458
+ <div class="ustats_apc_used_by">
459
+ Used by <span></span>
460
+ </div>
461
+ <div class="ustats_apc_evictions">
462
+ Evictions: <span></span>
463
+ </div>
464
+ <div class="ustats_apc_size_used">
465
+ Used: <span></span>
466
+ </div>
467
+ <div class="ustats_apc_size_percent">
468
+ Used (%): <span></span>
469
+ </div>
470
+ <div class="ustats_apc_get_hit_rate">
471
+ Hit rate: <span></span>
472
+ </div>
473
+ <div class="ustats_apc_items">
474
+ Items: <span></span>
475
+ </div>
476
+ </div>
477
+ <div class="ustats_block_chart">
478
+ Size used (MB)
479
+ <canvas id="w3tcus_apc_size_chart"></canvas>
480
+ Hit rate
481
+ <canvas id="w3tcus_apc_hit_chart"></canvas>
482
+ </div>
483
+ </div>
484
+
485
+ <?php Util_Ui::postbox_footer(); ?>
486
+ </div>
UsageStatistics_Page_View_Ad.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <p>
2
+ Users who upgrade to W3 Total Cache Pro will have access to the new
3
+ Statistics page, which provides an in-depth view of the performance of your
4
+ site.
5
+ </p>
6
+
7
+ <ul style="margin-left: 50px; margin-bottom: 20px">
8
+ <li>View detailed information about your site’s performance</li>
9
+ <li>Visualize your performance over time with graphs</li>
10
+ <li>Examine the overall performance of caching method backends</li>
11
+ </ul>
UsageStatistics_Page_View_Disabled.php ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+ ?>
9
+ <div class="metabox-holder ustats_ad_metabox">
10
+ <?php Util_Ui::postbox_header( __( 'Usage Statistics', 'w3-total-cache' ) ); ?>
11
+
12
+ <div class="ustats_ad">
13
+ <?php include __DIR__ . '/UsageStatistics_Page_View_Ad.php' ?>
14
+
15
+ <a class="button-primary"
16
+ href="admin.php?page=w3tc_general#stats">Enable here</a>
17
+ </div>
18
+
19
+ <?php Util_Ui::postbox_footer(); ?>
20
+ </div>
UsageStatistics_Page_View_Free.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+ ?>
9
+ <div class="metabox-holder ustats_ad_metabox">
10
+ <?php Util_Ui::postbox_header( __( 'Usage Statistics', 'w3-total-cache' ) ); ?>
11
+
12
+ <div class="ustats_ad">
13
+ <?php include __DIR__ . '/UsageStatistics_Page_View_Ad.php' ?>
14
+
15
+ <input type="button" class="button-primary button-buy-plugin"
16
+ data-src="page_stats_bottom"
17
+ value="<?php _e( 'upgrade', 'w3-total-cache' ) ?>" />
18
+ </div>
19
+
20
+ <?php Util_Ui::postbox_footer(); ?>
21
+ </div>
UsageStatistics_Page_View_NoDebugMode.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+ if ( !defined( 'W3TC' ) )
5
+ die();
6
+
7
+ include W3TC_INC_DIR . '/options/common/header.php';
8
+ ?>
9
+ <div class="metabox-holder">
10
+ <?php Util_Ui::postbox_header( __( 'Usage Statistics', 'w3-total-cache' ) ); ?>
11
+
12
+ <p>
13
+ Usage Statistics is collected only when Debug Mode is enabled.
14
+ </p>
15
+
16
+ <a href="admin.php?page=w3tc_general#debug" class="button-primary">Enable it here</a>
17
+
18
+ <?php Util_Ui::postbox_footer(); ?>
19
+ </div>
UsageStatistics_Plugin.php CHANGED
@@ -13,6 +13,14 @@ class UsageStatistics_Plugin {
13
  $this, 'w3tc_usage_statistics_of_request' ), 10, 1 );
14
  add_filter( 'w3tc_usage_statistics_metrics', array(
15
  $this, 'w3tc_usage_statistics_metrics' ) );
 
 
 
 
 
 
 
 
16
  }
17
 
18
 
@@ -22,6 +30,15 @@ class UsageStatistics_Plugin {
22
 
23
  $storage->counter_add( 'php_memory_100kb', $used_100kb );
24
  $storage->counter_add( 'php_requests', 1 );
 
 
 
 
 
 
 
 
 
25
  }
26
 
27
 
13
  $this, 'w3tc_usage_statistics_of_request' ), 10, 1 );
14
  add_filter( 'w3tc_usage_statistics_metrics', array(
15
  $this, 'w3tc_usage_statistics_metrics' ) );
16
+ add_filter( 'w3tc_usage_statistics_metric_values', array(
17
+ '\W3TC\UsageStatistics_Sources',
18
+ 'w3tc_usage_statistics_metric_values' ) );
19
+ add_filter( 'w3tc_usage_statistics_history_set', array(
20
+ '\W3TC\UsageStatistics_Sources',
21
+ 'w3tc_usage_statistics_history_set' ) );
22
+
23
+ UsageStatistics_Source_Wpdb::init();
24
  }
25
 
26
 
30
 
31
  $storage->counter_add( 'php_memory_100kb', $used_100kb );
32
  $storage->counter_add( 'php_requests', 1 );
33
+
34
+ /* keep for mode when pagecache not enabled, otherwise it shows own stats similar to that
35
+ if ( defined( 'WP_ADMIN' ) ) {
36
+ $storage->counter_add( 'php_requests_wp_admin', 1 );
37
+ }
38
+ if ( defined( 'DOING_AJAX' ) ) {
39
+ $storage->counter_add( 'php_requests_ajax', 1 );
40
+ }
41
+ */
42
  }
43
 
44
 
UsageStatistics_Plugin_Admin.php CHANGED
@@ -5,41 +5,121 @@ namespace W3TC;
5
 
6
  class UsageStatistics_Plugin_Admin {
7
  function run() {
8
- add_action( 'w3tc_settings_general_boxarea_miscellaneous_content',
9
- array( $this, 'w3tc_settings_general_boxarea_miscellaneous_content' ) );
 
 
 
 
 
 
 
 
 
10
 
11
  $widget = new UsageStatistics_Widget();
12
  $widget->init();
13
 
14
- add_filter( 'w3tc_usage_statistics_summary_from_history', array(
15
- $this, 'w3tc_usage_statistics_summary_from_history' ), 10, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
 
19
 
20
- public function w3tc_settings_general_boxarea_miscellaneous_content() {
21
- include W3TC_DIR . '/UsageStatistics_View_General.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
 
25
 
26
- public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
27
- $php_memory_100kb = Util_UsageStatistics::sum( $history, 'php_memory_100kb' );
28
- $php_requests = Util_UsageStatistics::sum( $history, 'php_requests' );
 
 
 
 
 
 
 
 
 
29
 
30
- if ( $php_requests > 0 ) {
31
- $summary['php'] = array(
32
- 'memory' => Util_UsageStatistics::bytes_to_size(
33
- $php_memory_100kb / $php_requests * 1024 * 10.24 )
34
- );
35
 
36
- $summary['php']['wp_requests_total'] =
37
- Util_UsageStatistics::integer( $php_requests );
38
- $summary['php']['wp_requests_per_second'] =
39
- Util_UsageStatistics::value_per_period_seconds( $php_requests,
40
- $summary );
41
  }
42
 
43
- return $summary;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
  }
5
 
6
  class UsageStatistics_Plugin_Admin {
7
  function run() {
8
+ $c = Dispatcher::config();
9
+
10
+ add_filter( 'w3tc_admin_menu', array( $this, 'w3tc_admin_menu' ) );
11
+ add_action( 'w3tc_ajax_ustats_get', array( $this, 'w3tc_ajax_ustats_get' ) );
12
+ add_action( 'w3tc_ajax_ustats_access_log_test',
13
+ array( $this, 'w3tc_ajax_ustats_access_log_test' ) );
14
+
15
+ add_filter( 'w3tc_usage_statistics_summary_from_history', array(
16
+ 'W3TC\UsageStatistics_Sources',
17
+ 'w3tc_usage_statistics_summary_from_history'
18
+ ), 5, 2 );
19
 
20
  $widget = new UsageStatistics_Widget();
21
  $widget->init();
22
 
23
+ add_action( 'admin_init_w3tc_dashboard', array(
24
+ '\W3TC\UsageStatistics_Widget',
25
+ 'admin_init_w3tc_dashboard'
26
+ ) );
27
+
28
+ add_action( 'admin_init_w3tc_general', array(
29
+ '\W3TC\UsageStatistics_GeneralPage',
30
+ 'admin_init_w3tc_general'
31
+ ) );
32
+
33
+ add_action( 'admin_print_scripts-performance_page_w3tc_stats', array(
34
+ '\W3TC\UsageStatistics_Page',
35
+ 'admin_print_scripts_w3tc_stats'
36
+ ) );
37
+
38
+ add_action( 'w3tc_config_ui_save', array(
39
+ $this,
40
+ 'w3tc_config_ui_save'
41
+ ), 10, 2 );
42
+
43
+ add_filter( 'w3tc_notes', array( $this, 'w3tc_notes' ) );
44
+ }
45
+
46
+ #
47
+
48
+
49
+ public function w3tc_config_ui_save( $config, $old_config ) {
50
+ if ( $config->get( 'stats.slot_seconds' ) !=
51
+ $old_config->get( 'stats.slot_seconds' ) ) {
52
+ // flush all stats otherwise will be inconsistent
53
+ $storage = new UsageStatistics_StorageWriter();
54
+ $storage->reset();
55
+ }
56
  }
57
 
58
 
59
 
60
+ public function w3tc_notes( $notes ) {
61
+ $c = Dispatcher::config();
62
+ $state_master = Dispatcher::config_state_master();
63
+
64
+ if ( $c->get_boolean( 'stats.enabled' ) &&
65
+ !$state_master->get_boolean( 'common.hide_note_stats_enabled' ) ) {
66
+ $notes['stats_enabled'] = sprintf(
67
+ __( 'W3 Total Cache: Statistics collection is currently enabled. This consumes additional resources, and is not recommended to be run continuously. %s %s',
68
+ 'w3-total-cache' ),
69
+ Util_Ui::button_link(
70
+ __( 'Disable statistics', 'w3-total-cache' ),
71
+ Util_Ui::url( array( 'w3tc_ustats_note_disable' => 'y' ) ),
72
+ false, 'button',
73
+ 'w3tc_note_stats_disable' ),
74
+ Util_Ui::button_hide_note2( array(
75
+ 'w3tc_default_config_state_master' => 'y',
76
+ 'key' => 'common.hide_note_stats_enabled',
77
+ 'value' => 'true' ) ) );
78
+ }
79
+
80
+ return $notes;
81
  }
82
 
83
 
84
 
85
+ public function w3tc_admin_menu( $menu ) {
86
+ $menu['w3tc_stats'] = array(
87
+ 'page_title' => __( 'Statistics', 'w3-total-cache' ),
88
+ 'menu_text' => __( 'Statistics', 'w3-total-cache' ),
89
+ 'visible_always' => false,
90
+ 'order' => 2250
91
+ );
92
+
93
+ return $menu;
94
+ }
95
+
96
+
97
 
98
+ public function w3tc_ajax_ustats_get() {
99
+ $storage = new UsageStatistics_StorageReader();
100
+ $summary = $storage->get_history_summary();
 
 
101
 
102
+ if ( defined( 'W3TC_DEBUG' ) ) {
103
+ echo json_encode( $summary ,JSON_PRETTY_PRINT );
104
+ exit();
 
 
105
  }
106
 
107
+ echo json_encode( $summary );
108
+ exit();
109
+ }
110
+
111
+
112
+
113
+ public function w3tc_ajax_ustats_access_log_test() {
114
+ $filename = $_REQUEST['filename'];
115
+
116
+ $filename = str_replace( '://', '/', $filename );
117
+ $h = @fopen( $filename, 'rb' );
118
+ if ( !$h ) {
119
+ echo 'Failed to open file ' . $filename;
120
+ } else {
121
+ echo 'Success';
122
+ }
123
+ exit();
124
  }
125
  }
UsageStatistics_Source_AccessLog.php ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * Access log reader - provides statistics data from http server access log
8
+ */
9
+ class UsageStatistics_Source_AccessLog {
10
+ // configuration
11
+ private $line_regexp;
12
+
13
+ private $max_line = '';
14
+ private $max_time = 0;
15
+ private $min_time;
16
+ private $min_line = '';
17
+
18
+ // running values
19
+
20
+ // read access log after that timestamp
21
+ private $max_already_counted_timestamp;
22
+
23
+ // what was loaded now in this cycle
24
+ private $max_now_counted_timestamp = null;
25
+
26
+ // if need to read more access log chunks
27
+ private $more_log_needed = true;
28
+
29
+ // where data aggregated
30
+ private $history;
31
+ private $history_current_pos;
32
+ private $history_current_item;
33
+ private $history_current_timestamp_start;
34
+ private $history_current_timestamp_end;
35
+
36
+
37
+
38
+ static public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
39
+ $dynamic_requests_total = Util_UsageStatistics::sum( $history,
40
+ array( 'access_log', 'dynamic_count' ) );
41
+ $dynamic_timetaken_ms_total = Util_UsageStatistics::sum( $history,
42
+ array( 'access_log', 'dynamic_timetaken_ms' ) );
43
+ $static_requests_total = Util_UsageStatistics::sum( $history,
44
+ array( 'access_log', 'static_count' ) );
45
+ $static_timetaken_ms_total = Util_UsageStatistics::sum( $history,
46
+ array( 'access_log', 'static_timetaken_ms' ) );
47
+
48
+
49
+ $summary['access_log'] = array(
50
+ 'dynamic_requests_total_v' => $dynamic_requests_total,
51
+ 'dynamic_requests_total' => Util_UsageStatistics::integer(
52
+ $dynamic_requests_total ),
53
+ 'dynamic_requests_per_second' => Util_UsageStatistics::value_per_period_seconds(
54
+ $dynamic_requests_total, $summary ),
55
+ 'dynamic_requests_timing' => Util_UsageStatistics::integer_divideby(
56
+ $dynamic_timetaken_ms_total, $dynamic_requests_total ),
57
+ 'static_requests_total' => Util_UsageStatistics::integer(
58
+ $static_requests_total ),
59
+ 'static_requests_per_second' => Util_UsageStatistics::value_per_period_seconds(
60
+ $static_requests_total, $summary ),
61
+ 'static_requests_timing' => Util_UsageStatistics::integer_divideby(
62
+ $static_timetaken_ms_total, $static_requests_total ),
63
+ );
64
+
65
+ return $summary;
66
+ }
67
+
68
+
69
+
70
+ /**
71
+ * array( 'webserver', 'format', 'filename' )
72
+ */
73
+ public function __construct( $data ) {
74
+ $format = $data['format'];
75
+ $webserver = $data['webserver'];
76
+ $this->accesslog_filename = str_replace( '://', '/', $data['filename'] );
77
+
78
+ if ( $webserver == 'nginx' ) {
79
+ $line_regexp = $this->logformat_to_regexp_nginx( $format );
80
+ } else {
81
+ $line_regexp = $this->logformat_to_regexp_apache( $format );
82
+ }
83
+
84
+ $this->line_regexp = apply_filters( 'w3tc_ustats_access_log_format_regexp',
85
+ $line_regexp );
86
+ }
87
+
88
+
89
+
90
+ public function w3tc_usage_statistics_history_set( $history ) {
91
+ $this->max_already_counted_timestamp = (int)get_site_option( 'w3tc_stats_history_access_log' );
92
+ if ( isset( $history[0]['timestamp_start'] ) &&
93
+ $history[0]['timestamp_start'] > $this->max_already_counted_timestamp ) {
94
+ $this->max_already_counted_timestamp = $history[0]['timestamp_start'] - 1;
95
+ }
96
+
97
+ $this->history = $history;
98
+ $this->min_time = time();
99
+ $this->setup_history_item( count( $history ) - 1 );
100
+
101
+ $h = @fopen( $this->accesslog_filename, 'rb' );
102
+ if ( !$h ) {
103
+ error_log( 'Failed to open access log for usage statisics collection' );
104
+ return $history;
105
+ }
106
+
107
+ fseek( $h, 0, SEEK_END );
108
+ $pos = ftell( $h );
109
+ $unparsed_head = '';
110
+
111
+ while ( $pos >= 0 && $this->more_log_needed ) {
112
+ $pos -= 8192;
113
+ if ( $pos <= 0 ) {
114
+ $pos = 0;
115
+ }
116
+ fseek( $h, $pos );
117
+
118
+ $s = fread( $h, 8192 );
119
+
120
+ $unparsed_head = $this->parse_string( $s . $unparsed_head, $pos > 0 );
121
+ if ( $pos <= 0 ) {
122
+ $this->more_log_needed = false;
123
+ }
124
+ }
125
+
126
+ if ( defined( 'W3TC_DEBUG' ) && W3TC_DEBUG ) {
127
+ Util_Debug::log( 'time',
128
+ "period " .
129
+ date( DATE_ATOM, $this->max_already_counted_timestamp ) . ' - ' .
130
+ date( DATE_ATOM, $this->max_now_counted_timestamp ) . "\n" .
131
+ "min line: " . $this->min_line . "\n" .
132
+ "max line: " . $this->max_line );
133
+ }
134
+
135
+ if ( !is_null( $this->max_now_counted_timestamp ) ) {
136
+ update_site_option( 'w3tc_stats_history_access_log',
137
+ $this->max_now_counted_timestamp );
138
+ }
139
+
140
+ return $this->history;
141
+ }
142
+
143
+
144
+
145
+ private function setup_history_item( $pos ) {
146
+ $this->history_current_pos = $pos;
147
+
148
+ if ( !isset( $this->history[$pos]['access_log'] ) ) {
149
+ $this->history[$pos]['access_log'] = array(
150
+ 'dynamic_count' => 0,
151
+ 'dynamic_timetaken_ms' => 0,
152
+ 'static_count' => 0,
153
+ 'static_timetaken_ms' => 0,
154
+ );
155
+ }
156
+
157
+ $this->history_current_item = &$this->history[$pos]['access_log'];
158
+ $this->history_current_timestamp_start = $this->history[$pos]['timestamp_start'];
159
+ $this->history_current_timestamp_end = $this->history[$pos]['timestamp_end'];
160
+ }
161
+
162
+
163
+
164
+ private function parse_string( $s, $skip_first_line ) {
165
+ $s_length = strlen( $s );
166
+ $unparsed_head = '';
167
+ $lines = array();
168
+
169
+ $n = 0;
170
+ if ( $skip_first_line ) {
171
+ for ( ; $n < $s_length; $n++ ) {
172
+ $c = substr( $s, $n, 1 );
173
+ if ( $c == "\r" || $c == "\n" ) {
174
+ $unparsed_head = substr( $s, 0, $n + 1 );
175
+ break;
176
+ }
177
+ }
178
+ }
179
+
180
+ $line_start = $n;
181
+ $line_elements = array();
182
+ $line_element_start = $n;
183
+
184
+ for ( ; $n < $s_length; $n++ ) {
185
+ $c = substr( $s, $n, 1 );
186
+ if ( $c == "\r" || $c == "\n" ) {
187
+ if ( $n > $line_start ) {
188
+ $lines[] = substr( $s, $line_start, $n - $line_start );
189
+ }
190
+
191
+ $line_start = $n + 1;
192
+ }
193
+ }
194
+
195
+ // last line comes first, boundary checks logic based on that
196
+ for ( $n = count( $lines ) - 1; $n >= 0; $n-- ) {
197
+ $this->push_line( $lines[$n] );
198
+ }
199
+
200
+ return $unparsed_head;
201
+ }
202
+
203
+
204
+
205
+ private function push_line( $line ) {
206
+ $e = array();
207
+ preg_match( $this->line_regexp, $line, $e );
208
+
209
+ $e = apply_filters( 'w3tc_ustats_access_log_line_elements', $e, $line );
210
+ if ( !isset( $e['request_line'] ) || !isset( $e['date'] ) ) {
211
+ if ( defined( 'W3TC_DEBUG' ) && W3TC_DEBUG ) {
212
+ Util_Debug::log( 'time',
213
+ "line $line cant be parsed using regexp $this->line_regexp, request_line or date elements missing"
214
+ );
215
+ }
216
+ return;
217
+ }
218
+
219
+ $date_string = $e['date'];
220
+ $time = strtotime($date_string);
221
+
222
+ // dont read more if we touched entries before timeperiod of collection
223
+ if ( $time <= $this->max_already_counted_timestamp ) {
224
+ $this->more_log_needed = false;
225
+ return;
226
+ }
227
+ if ( $time > $this->history_current_timestamp_end ) {
228
+ return;
229
+ }
230
+ while ( $time < $this->history_current_timestamp_start ) {
231
+ if ( $this->history_current_pos <= 0 ) {
232
+ $this->more_log_needed = false;
233
+ return;
234
+ }
235
+ $this->setup_history_item( $this->history_current_pos - 1 );
236
+ }
237
+ if ( is_null( $this->max_now_counted_timestamp ) ) {
238
+ $this->max_now_counted_timestamp = $time;
239
+ }
240
+
241
+ if ( defined( 'W3TC_DEBUG' ) && W3TC_DEBUG ) {
242
+ if ($time < $this->min_time) {
243
+ $this->min_line = $line;
244
+ $this->min_time = $time;
245
+ }
246
+ if ($time > $this->max_time) {
247
+ $this->max_line = $line;
248
+ $this->max_time = $time;
249
+ }
250
+ }
251
+
252
+ $http_request_line = $e['request_line'];
253
+ $http_request_line_items = explode( ' ', $http_request_line );
254
+ $uri = $http_request_line_items[1];
255
+
256
+ $time_ms = 0;
257
+ if ( isset( $e['time_taken_microsecs'] ) ) {
258
+ $time_ms = (int)($e['time_taken_microsecs'] / 1000);
259
+ } elseif ( isset( $e['time_taken_ms'] ) ) {
260
+ $time_ms = (int)$e['time_taken_ms'];
261
+ }
262
+
263
+ $m = null;
264
+ preg_match('~\\.([a-zA-Z0-9]+)(\?.+)?$~', $uri, $m );
265
+ if ( $m && $m[1] != 'php') {
266
+ $this->history_current_item['static_count']++;
267
+ $this->history_current_item['static_timetaken_ms'] += $time_ms;
268
+ } else {
269
+ $this->history_current_item['dynamic_count']++;
270
+ $this->history_current_item['dynamic_timetaken_ms'] += $time_ms;
271
+ }
272
+ }
273
+
274
+
275
+
276
+ // default: %h %l %u %t \"%r\" %>s %b
277
+ // common : %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"
278
+ public function logformat_to_regexp_apache( $format ) {
279
+ // remove modifiers like %>s, %!400,501{User-agent}i
280
+ $format = preg_replace('~%[<>!0-9]([a-zA-Z{])~', '%$1', $format);
281
+
282
+ // remove modifiers %{User-agent}^ti, %{User-agent}^to
283
+ $format = preg_replace('~%({[^}]+})(^ti|^to)~', '%$1z', $format);
284
+
285
+ // take all quoted vars
286
+ $format = preg_replace_callback('~\\\"(%[a-zA-Z%]|%{[^}]+}[a-zA-Z])\\\"~',
287
+ array( $this, 'logformat_to_regexp_apache_element_quoted' ),
288
+ $format);
289
+
290
+ // take all remaining vars
291
+ $format = preg_replace_callback('~(%[a-zA-Z%]|%{[^}]+}[a-zA-Z])~',
292
+ array( $this, 'logformat_to_regexp_apache_element_naked' ),
293
+ $format);
294
+
295
+ return '~' . $format . '~';
296
+ }
297
+
298
+
299
+
300
+ public function logformat_to_regexp_apache_element_quoted( $match ) {
301
+ $v = $match[1];
302
+
303
+ if ( $v == '%r' ) {
304
+ return '\"(?<request_line>[^"]+)\"';
305
+ }
306
+
307
+ // default behavior, expected value doesnt contain spaces
308
+ return '\"([^"]+)\"';
309
+ }
310
+
311
+
312
+
313
+ public function logformat_to_regexp_apache_element_naked( $match ) {
314
+ $v = $match[1];
315
+
316
+ if ( $v == '%t' ) {
317
+ return '\[(?<date>[^\]]+)\]';
318
+ } elseif ( $v == '%D' ) {
319
+ return '(?<time_taken_microsecs>[0-9]+)';
320
+ }
321
+
322
+ // default behavior, expected value doesnt contain spaces
323
+ return '([^ ]+)';
324
+ }
325
+
326
+
327
+
328
+ // default: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"
329
+ // w3tc: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time
330
+ public function logformat_to_regexp_nginx( $format ) {
331
+ // escape quotes
332
+ $format = preg_replace_callback('~([\"\[\]])~',
333
+ array( $this, 'logformat_to_regexp_nginx_quote' ),
334
+ $format);
335
+
336
+ // take all quoted vars
337
+ $format = preg_replace_callback('~\\\"(\$[a-zA-Z0-9_]+)\\\"~',
338
+ array( $this, 'logformat_to_regexp_nginx_element_quoted' ),
339
+ $format);
340
+
341
+ // take all remaining vars
342
+ $format = preg_replace_callback('~(\$[a-zA-Z0-9_]+)~',
343
+ array( $this, 'logformat_to_regexp_nginx_element_naked' ),
344
+ $format);
345
+
346
+ return '~' . $format . '~';
347
+ }
348
+
349
+
350
+
351
+ public function logformat_to_regexp_nginx_quote( $match ) {
352
+ return '\\' . $match[1];
353
+ }
354
+
355
+
356
+
357
+ public function logformat_to_regexp_nginx_element_quoted( $match ) {
358
+ $v = $match[1];
359
+
360
+ if ( $v == '$request' ) {
361
+ return '\"(?<request_line>[^"]+)\"';
362
+ }
363
+
364
+ // default behavior, expected value doesnt contain spaces
365
+ return '\"([^"]+)\"';
366
+ }
367
+
368
+
369
+
370
+ public function logformat_to_regexp_nginx_element_naked( $match ) {
371
+ $v = $match[1];
372
+
373
+ if ( $v == '$time_local' ) {
374
+ return '(?<date>[^\]]+)';
375
+ } elseif ( $v == '$request_time' ) {
376
+ return '(?<time_taken_ms>[0-9.]+)';
377
+ }
378
+
379
+ // default behavior, expected value doesnt contain spaces
380
+ return '([^ ]+)';
381
+ }
382
+ }
UsageStatistics_Source_DbQueriesLog.php ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * database queries debug log reader - provides data from this logfile
8
+ */
9
+ class UsageStatistics_Source_DbQueriesLog {
10
+ // running values
11
+ private $timestamp_start;
12
+ private $sort_column;
13
+
14
+ private $by_query = array();
15
+
16
+ /* if need to read more access log chunks */
17
+ private $more_log_needed = true;
18
+
19
+
20
+
21
+ function __construct( $timestamp_start, $sort_column ) {
22
+ $this->timestamp_start = $timestamp_start;
23
+ $this->sort_column = $sort_column;
24
+ }
25
+
26
+ /**
27
+ * Lists entries from log
28
+ **/
29
+ public function list() {
30
+ $log_filename = Util_Debug::log_filename( 'dbcache-queries' );
31
+ $h = @fopen( $log_filename, 'rb' );
32
+ if ( !$h ) {
33
+ throw new \Exception( 'Failed to open log file' . $log_filename );
34
+ }
35
+
36
+ fseek( $h, 0, SEEK_END );
37
+ $pos = ftell( $h );
38
+ $unparsed_head = '';
39
+
40
+ while ( $pos >= 0 && $this->more_log_needed ) {
41
+ $pos -= 8192;
42
+ if ( $pos <= 0 ) {
43
+ $pos = 0;
44
+ }
45
+ fseek( $h, $pos );
46
+
47
+ $s = fread( $h, 8192 );
48
+
49
+ $unparsed_head = $this->parse_string( $s . $unparsed_head, $pos > 0 );
50
+ if ( $pos <= 0 ) {
51
+ $this->more_log_needed = false;
52
+ }
53
+ }
54
+
55
+ $output = array();
56
+ foreach ( $this->by_query as $query => $data ) {
57
+ $output[] = array(
58
+ 'query' => $query,
59
+ 'count_total' => $data['count_total'],
60
+ 'count_hit' => $data['count_hit'],
61
+ 'avg_size' => (int)( $data['sum_size'] / $data['count_total'] ),
62
+ 'avg_time_ms' => (int)( $data['sum_time_ms'] / $data['count_total'] ),
63
+ 'sum_time_ms' => (int)$data['sum_time_ms'],
64
+ 'reasons' => $data['reasons']
65
+ );
66
+ }
67
+
68
+ usort( $output, function($a, $b) {
69
+ return (int)($b[$this->sort_column]) - (int)($a[$this->sort_column]);
70
+ });
71
+
72
+ $output = array_slice( $output, 0, 200 );
73
+
74
+ return $output;
75
+ }
76
+
77
+
78
+
79
+ private function parse_string( $s, $skip_first_line ) {
80
+ $s_length = strlen( $s );
81
+ $unparsed_head = '';
82
+
83
+ $n = 0;
84
+ if ( $skip_first_line ) {
85
+ for ( ; $n < $s_length; $n++ ) {
86
+ $c = substr( $s, $n, 1 );
87
+ if ( $c == "\r" || $c == "\n" ) {
88
+ $unparsed_head = substr( $s, 0, $n + 1 );
89
+ break;
90
+ }
91
+ }
92
+ }
93
+
94
+ $line_start = $n;
95
+ for ( ; $n < $s_length; $n++ ) {
96
+ $c = substr( $s, $n, 1 );
97
+ if ( $c == "\r" || $c == "\n" ) {
98
+ if ( $n > $line_start ) {
99
+ $this->push_line( substr( $s, $line_start, $n - $line_start ) );
100
+ }
101
+
102
+ $line_start = $n + 1;
103
+ }
104
+ }
105
+
106
+ return $unparsed_head;
107
+ }
108
+
109
+
110
+
111
+ private function push_line( $line ) {
112
+ $matches = str_getcsv( $line, "\t" );
113
+
114
+ if ( !$matches ) {
115
+ return;
116
+ }
117
+
118
+ $date_string = $matches[0];
119
+ $query = $matches[2];
120
+ $time_taken_ms = isset( $matches[3] ) ? (float)$matches[3] / 1000 : 0;
121
+ $reason = isset( $matches[4] ) ? $matches[4] : '';
122
+ $hit = isset( $matches[5] ) ? $matches[5] : false;
123
+ $size = isset( $matches[6] ) ? $matches[6] : 0;
124
+
125
+ $time = strtotime($date_string);
126
+
127
+ // dont read more if we touched entries before timeperiod of collection
128
+ if ( $time < $this->timestamp_start ) {
129
+ $this->more_log_needed = false;
130
+ }
131
+
132
+ if ( !isset( $this->by_query[$query] ) ) {
133
+ $this->by_query[$query] = array(
134
+ 'count_total' => 0,
135
+ 'count_hit' => 0,
136
+ 'sum_size' => 0,
137
+ 'sum_time_ms' => 0,
138
+ 'reasons' => array()
139
+ );
140
+ }
141
+
142
+ $this->by_query[$query]['count_total']++;
143
+ if ($hit) {
144
+ $this->by_query[$query]['count_hit']++;
145
+ }
146
+ $this->by_query[$query]['sum_size'] += $size;
147
+ $this->by_query[$query]['sum_time_ms'] += $time_taken_ms;
148
+
149
+ if ( !in_array( $reason, $this->by_query[$query]['reasons']) ) {
150
+ $this->by_query[$query]['reasons'][] = $reason;
151
+ }
152
+ }
153
+ }
UsageStatistics_Source_ObjectCacheLog.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * database queries debug log reader - provides data from this logfile
8
+ */
9
+ class UsageStatistics_Source_ObjectCacheLog {
10
+ // running values
11
+ private $timestamp_start;
12
+ private $sort_column;
13
+
14
+ private $by_group = array();
15
+
16
+ /* if need to read more access log chunks */
17
+ private $more_log_needed = true;
18
+
19
+
20
+
21
+ function __construct( $timestamp_start, $sort_column ) {
22
+ $this->timestamp_start = $timestamp_start;
23
+ $this->sort_column = $sort_column;
24
+ }
25
+
26
+ /**
27
+ * Lists entries from log
28
+ **/
29
+ public function list() {
30
+ $log_filename = Util_Debug::log_filename( 'objectcache-calls' );
31
+ $h = @fopen( $log_filename, 'rb' );
32
+ if ( !$h ) {
33
+ throw new \Exception( 'Failed to open log file' . $log_filename );
34
+ }
35
+
36
+ fseek( $h, 0, SEEK_END );
37
+ $pos = ftell( $h );
38
+ $unparsed_head = '';
39
+
40
+ while ( $pos >= 0 && $this->more_log_needed ) {
41
+ $pos -= 8192;
42
+ if ( $pos <= 0 ) {
43
+ $pos = 0;
44
+ }
45
+ fseek( $h, $pos );
46
+
47
+ $s = fread( $h, 8192 );
48
+
49
+ $unparsed_head = $this->parse_string( $s . $unparsed_head, $pos > 0 );
50
+ if ( $pos <= 0 ) {
51
+ $this->more_log_needed = false;
52
+ }
53
+ }
54
+
55
+ $output = array();
56
+ foreach ( $this->by_group as $group => $data ) {
57
+ $output[] = array(
58
+ 'group' => $group,
59
+ 'count_total' => $data['count_total'],
60
+ 'count_get_total' => $data['count_get_total'],
61
+ 'count_get_hit' => $data['count_get_hit'],
62
+ 'count_set' => $data['count_set'],
63
+ 'sum_size' => $data['sum_size'],
64
+ 'avg_size' => (int)( $data['sum_size'] / $data['count_total'] ),
65
+ 'sum_time_ms' => (int)$data['sum_time_ms']
66
+ );
67
+ }
68
+
69
+ usort( $output, function($a, $b) {
70
+ return (int)($b[$this->sort_column]) - (int)($a[$this->sort_column]);
71
+ });
72
+
73
+ $output = array_slice( $output, 0, 200 );
74
+
75
+ return $output;
76
+ }
77
+
78
+
79
+
80
+ private function parse_string( $s, $skip_first_line ) {
81
+ $s_length = strlen( $s );
82
+ $unparsed_head = '';
83
+
84
+ $n = 0;
85
+ if ( $skip_first_line ) {
86
+ for ( ; $n < $s_length; $n++ ) {
87
+ $c = substr( $s, $n, 1 );
88
+ if ( $c == "\r" || $c == "\n" ) {
89
+ $unparsed_head = substr( $s, 0, $n + 1 );
90
+ break;
91
+ }
92
+ }
93
+ }
94
+
95
+ $line_start = $n;
96
+ for ( ; $n < $s_length; $n++ ) {
97
+ $c = substr( $s, $n, 1 );
98
+ if ( $c == "\r" || $c == "\n" ) {
99
+ if ( $n > $line_start ) {
100
+ $this->push_line( substr( $s, $line_start, $n - $line_start ) );
101
+ }
102
+
103
+ $line_start = $n + 1;
104
+ }
105
+ }
106
+
107
+ return $unparsed_head;
108
+ }
109
+
110
+
111
+
112
+ private function push_line( $line ) {
113
+ $matches = str_getcsv( $line, "\t" );
114
+
115
+ if ( !$matches ) {
116
+ return;
117
+ }
118
+
119
+ $date_string = $matches[0];
120
+ $op = $matches[1];
121
+ $group = $matches[2];
122
+ $id = $matches[3];
123
+ $reason = $matches[4];
124
+ $size = (int)$matches[5];
125
+ $time_taken_ms = (float)$matches[6] / 1000;
126
+
127
+ $time = strtotime($date_string);
128
+
129
+ // dont read more if we touched entries before timeperiod of collection
130
+ if ( $time < $this->timestamp_start ) {
131
+ $this->more_log_needed = false;
132
+ }
133
+
134
+ if ( $reason == 'not tried cache' ||
135
+ substr( $reason, 0, 7 ) == 'not set' ) {
136
+ return; // it's not cache-related activity
137
+ }
138
+
139
+ if ( !isset( $this->by_group[$group] ) ) {
140
+ $this->by_group[$group] = array(
141
+ 'count_total' => 0,
142
+ 'count_get_total' => 0,
143
+ 'count_get_hit' => 0,
144
+ 'count_set' => 0,
145
+ 'sum_size' => 0,
146
+ 'sum_time_ms' => 0
147
+ );
148
+ }
149
+
150
+ if ( $op == 'get' ) {
151
+ $this->by_group[$group]['count_total']++;
152
+ $this->by_group[$group]['count_get_total']++;
153
+ if ($reason == 'from persistent cache') {
154
+ $this->by_group[$group]['count_get_hit']++;
155
+ }
156
+ } elseif ( $op == 'set' ) {
157
+ $this->by_group[$group]['count_total']++;
158
+ $this->by_group[$group]['count_set']++;
159
+ }
160
+
161
+ $this->by_group[$group]['sum_size'] += $size;
162
+ $this->by_group[$group]['sum_time_ms'] += $time_taken_ms;
163
+ }
164
+ }
UsageStatistics_Source_PageCacheLog.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * PageCache debug log reader - provides data from this logfile
8
+ */
9
+ class UsageStatistics_Source_PageCacheLog {
10
+ // running values
11
+ private $timestamp_start;
12
+ private $process_status;
13
+ private $sort_column;
14
+
15
+ private $by_uri = array();
16
+
17
+ /* if need to read more access log chunks */
18
+ private $more_log_needed = true;
19
+
20
+
21
+
22
+ function __construct( $timestamp_start, $process_status, $sort_column ) {
23
+ $this->timestamp_start = $timestamp_start;
24
+ $this->process_status = $process_status;
25
+ $this->sort_column = $sort_column;
26
+ }
27
+
28
+ /**
29
+ * Lists entries from log with specified cache reject reason code
30
+ **/
31
+ public function list() {
32
+ $log_filename = Util_Debug::log_filename( 'pagecache' );
33
+ $h = @fopen( $log_filename, 'rb' );
34
+ if ( !$h ) {
35
+ throw new \Exception( 'Failed to open pagecache log file' . $log_filename );
36
+ }
37
+
38
+ fseek( $h, 0, SEEK_END );
39
+ $pos = ftell( $h );
40
+ $unparsed_head = '';
41
+
42
+ while ( $pos >= 0 && $this->more_log_needed ) {
43
+ $pos -= 8192;
44
+ if ( $pos <= 0 ) {
45
+ $pos = 0;
46
+ }
47
+ fseek( $h, $pos );
48
+
49
+ $s = fread( $h, 8192 );
50
+
51
+ $unparsed_head = $this->parse_string( $s . $unparsed_head, $pos > 0 );
52
+ if ( $pos <= 0 ) {
53
+ $this->more_log_needed = false;
54
+ }
55
+ }
56
+
57
+ $output = array();
58
+ foreach ( $this->by_uri as $uri => $data ) {
59
+ $output[] = array(
60
+ 'uri' => $uri,
61
+ 'count' => $data['count'],
62
+ 'avg_size' => (int)( $data['sum_size'] / $data['count'] ),
63
+ 'avg_time_ms' => (int)( $data['sum_time_ms'] / $data['count'] ),
64
+ 'sum_time_ms' => $data['sum_time_ms'],
65
+ 'reasons' => $data['reasons']
66
+ );
67
+ }
68
+
69
+ usort( $output, function($a, $b) {
70
+ return (int)($b[$this->sort_column]) - (int)($a[$this->sort_column]);
71
+ });
72
+
73
+ return $output;
74
+ }
75
+
76
+
77
+
78
+ private function parse_string( $s, $skip_first_line ) {
79
+ $s_length = strlen( $s );
80
+ $unparsed_head = '';
81
+
82
+ $n = 0;
83
+ if ( $skip_first_line ) {
84
+ for ( ; $n < $s_length; $n++ ) {
85
+ $c = substr( $s, $n, 1 );
86
+ if ( $c == "\r" || $c == "\n" ) {
87
+ $unparsed_head = substr( $s, 0, $n + 1 );
88
+ break;
89
+ }
90
+ }
91
+ }
92
+
93
+ $line_start = $n;
94
+ for ( ; $n < $s_length; $n++ ) {
95
+ $c = substr( $s, $n, 1 );
96
+ if ( $c == "\r" || $c == "\n" ) {
97
+ if ( $n > $line_start ) {
98
+ $this->push_line( substr( $s, $line_start, $n - $line_start ) );
99
+ }
100
+
101
+ $line_start = $n + 1;
102
+ }
103
+ }
104
+
105
+ return $unparsed_head;
106
+ }
107
+
108
+
109
+
110
+ private function push_line( $line ) {
111
+ $matches = null;
112
+ preg_match(
113
+ '/\[([^>\]]+)\] \[([^>\]]+)\] \[([^>\]]+)\] finished in (\d+) size (\d+) with process status ([^ ]+) reason (.*)/',
114
+ $line, $matches);
115
+
116
+ if ( !$matches ) {
117
+ return;
118
+ }
119
+
120
+ $date_string = $matches[1];
121
+ $uri = $matches[2];
122
+ $time_taken_ms = $matches[4];
123
+ $size = $matches[5];
124
+ $status = $matches[6];
125
+ $reason = $matches[7];
126
+ $time = strtotime($date_string);
127
+
128
+ // dont read more if we touched entries before timeperiod of collection
129
+ if ( $time < $this->timestamp_start ) {
130
+ $this->more_log_needed = false;
131
+ }
132
+
133
+ if ( $status != $this->process_status ) {
134
+ return;
135
+ }
136
+
137
+ if ( !isset( $this->by_uri[$uri] ) ) {
138
+ $this->by_uri[$uri] = array(
139
+ 'count' => 0,
140
+ 'sum_size' => 0,
141
+ 'sum_time_ms' => 0,
142
+ 'reasons' => array()
143
+ );
144
+ }
145
+
146
+ $this->by_uri[$uri]['count']++;
147
+ $this->by_uri[$uri]['sum_size'] += $size;
148
+ $this->by_uri[$uri]['sum_time_ms'] += $time_taken_ms;
149
+
150
+ if ( !in_array( $reason, $this->by_uri[$uri]['reasons']) ) {
151
+ $this->by_uri[$uri]['reasons'][] = $reason;
152
+ }
153
+ }
154
+ }
UsageStatistics_Source_Wpdb.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ /**
7
+ * Provides statistics data about requests made to mysql server
8
+ */
9
+ class UsageStatistics_Source_Wpdb {
10
+ private $query_total = 0;
11
+
12
+
13
+
14
+ static public function init() {
15
+ $o = new UsageStatistics_Source_Wpdb();
16
+
17
+ add_filter( 'query', array( $o, 'query' ) );
18
+ add_action( 'w3tc_usage_statistics_of_request', array(
19
+ $o, 'w3tc_usage_statistics_of_request' ), 10, 1 );
20
+ add_filter( 'w3tc_usage_statistics_metrics', array(
21
+ $o, 'w3tc_usage_statistics_metrics' ) );
22
+ add_filter( 'w3tc_usage_statistics_summary_from_history', array(
23
+ $o, 'w3tc_usage_statistics_summary_from_history' ), 10, 2 );
24
+ }
25
+
26
+
27
+
28
+ public function w3tc_usage_statistics_metrics( $metrics ) {
29
+ return array_merge( $metrics, array( 'wpdb_calls_total' ) );
30
+ }
31
+
32
+
33
+
34
+ public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
35
+ // counters
36
+ $wpdb_calls_total = Util_UsageStatistics::sum( $history,
37
+ 'wpdb_calls_total' );
38
+
39
+ $summary['wpdb'] = array(
40
+ 'calls_total' => Util_UsageStatistics::integer(
41
+ $wpdb_calls_total ),
42
+ 'calls_per_second' => Util_UsageStatistics::value_per_period_seconds(
43
+ $wpdb_calls_total, $summary )
44
+ );
45
+
46
+ return $summary;
47
+ }
48
+
49
+
50
+
51
+ public function w3tc_usage_statistics_of_request( $storage ) {
52
+ $storage->counter_add( 'wpdb_calls_total', $this->query_total );
53
+ }
54
+
55
+
56
+
57
+ public function query( $q ) {
58
+ $this->query_total++;
59
+ return $q;
60
+ }
61
+ }
UsageStatistics_Sources.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class UsageStatistics_Sources {
7
+ static public function w3tc_usage_statistics_summary_from_history( $summary, $history ) {
8
+ // php data
9
+ $php_memory_100kb = Util_UsageStatistics::sum( $history, 'php_memory_100kb' );
10
+ $php_requests = Util_UsageStatistics::sum( $history, 'php_requests' );
11
+
12
+ if ( $php_requests > 0 ) {
13
+ if ( !isset( $summary['php'] ) ) {
14
+ $summary['php'] = array();
15
+ }
16
+
17
+ $summary['php']['memory'] = Util_UsageStatistics::bytes_to_size(
18
+ $php_memory_100kb / $php_requests * 1024 * 10.24 );
19
+
20
+ $summary['php']['php_requests_v'] = $php_requests;
21
+ $summary['php']['php_requests'] =
22
+ Util_UsageStatistics::integer( $php_requests );
23
+ $summary['php']['php_requests_per_second'] =
24
+ Util_UsageStatistics::value_per_period_seconds( $php_requests,
25
+ $summary );
26
+ }
27
+
28
+ // apc
29
+ if ( count( $summary['apc_servers'] ) > 0 ) {
30
+ $apc = new UsageStatistics_Sources_Apc(
31
+ $summary['apc_servers'] );
32
+ $summary['apc'] = $apc->get_summary();
33
+ }
34
+
35
+ // memcached
36
+ if ( count( $summary['memcached_servers'] ) > 0 ) {
37
+ $memcached = new UsageStatistics_Sources_Memcached(
38
+ $summary['memcached_servers'] );
39
+ $summary['memcached'] = $memcached->get_summary();
40
+ }
41
+
42
+ // redis
43
+ if ( count( $summary['redis_servers'] ) > 0 ) {
44
+ $redis = new UsageStatistics_Sources_Redis(
45
+ $summary['redis_servers'] );
46
+ $summary['redis'] = $redis->get_summary();
47
+ }
48
+
49
+ // cpu snapshot
50
+ $c = Dispatcher::config();
51
+ if ( $c->get_boolean( 'stats.cpu.enabled' ) ) {
52
+ $summary['cpu'] = array(
53
+ 'avg' => round( Util_UsageStatistics::avg( $history, 'cpu' ), 2 )
54
+ );
55
+ }
56
+
57
+ // access log data
58
+ if ( $c->get_boolean( 'stats.access_log.enabled' ) ) {
59
+ $o = new UsageStatistics_Source_AccessLog( array(
60
+ 'webserver' => $c->get_string( 'stats.access_log.webserver' ),
61
+ 'filename' => $c->get_string( 'stats.access_log.filename' ),
62
+ 'format' => $c->get_string( 'stats.access_log.format' )
63
+ ) );
64
+
65
+ $summary = $o->w3tc_usage_statistics_summary_from_history(
66
+ $summary, $history );
67
+ }
68
+
69
+ return $summary;
70
+ }
71
+
72
+
73
+
74
+ static public function w3tc_usage_statistics_metric_values( $metric_values ) {
75
+ $sources = array(
76
+ 'memcached_servers' => array(),
77
+ 'redis_servers' => array(),
78
+ 'apc_servers' => array()
79
+ );
80
+
81
+ $sources = apply_filters( 'w3tc_usage_statistics_sources',
82
+ $sources );
83
+
84
+ if ( count( $sources['memcached_servers'] ) > 0 ) {
85
+ $memcached = new UsageStatistics_Sources_Memcached(
86
+ $sources['memcached_servers'] );
87
+ $metric_values['memcached'] = $memcached->get_snapshot();
88
+ }
89
+ if ( count( $sources['apc_servers'] ) > 0 ) {
90
+ $apc = new UsageStatistics_Sources_Apc(
91
+ $sources['apc_servers']
92
+ );
93
+ $metric_values['apc'] = $apc->get_snapshot();
94
+ }
95
+ if ( count( $sources['redis_servers'] ) > 0 ) {
96
+ $redis = new UsageStatistics_Sources_Redis(
97
+ $sources['redis_servers'] );
98
+ $metric_values['redis'] = $redis->get_snapshot();
99
+ }
100
+
101
+ $c = Dispatcher::config();
102
+ if ( $c->get_boolean( 'stats.cpu.enabled' ) ) {
103
+ // cpu snapshot
104
+ $cpu = sys_getloadavg();
105
+ if ( isset( $cpu[0] ) ) {
106
+ $metric_values['cpu'] = $cpu[0];
107
+ }
108
+ }
109
+
110
+ return $metric_values;
111
+ }
112
+
113
+
114
+
115
+ static public function w3tc_usage_statistics_history_set( $history ) {
116
+ $c = Dispatcher::config();
117
+ if ( $c->get_boolean( 'stats.access_log.enabled' ) ) {
118
+ // read access log
119
+ $o = new UsageStatistics_Source_AccessLog( array(
120
+ 'webserver' => $c->get_string( 'stats.access_log.webserver' ),
121
+ 'filename' => $c->get_string( 'stats.access_log.filename' ),
122
+ 'format' => $c->get_string( 'stats.access_log.format' )
123
+ ) );
124
+
125
+ $history = $o->w3tc_usage_statistics_history_set( $history );
126
+ }
127
+
128
+ return $history;
129
+ }
130
+ }
UsageStatistics_Sources_Apc.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class UsageStatistics_Sources_Apc {
7
+ private $module_names = array();
8
+
9
+ public function __construct( $server_descriptors ) {
10
+ foreach ( $server_descriptors as $module_key => $i ) {
11
+ $this->module_names[] = $i['name'];
12
+ }
13
+ }
14
+
15
+
16
+
17
+ public function get_snapshot() {
18
+ $cache = apcu_cache_info();
19
+
20
+ return array(
21
+ 'items' => $cache['num_entries'],
22
+ 'size_used' => $cache['mem_size'],
23
+ 'get_hits' => $cache['num_hits'],
24
+ 'get_total' => ( $cache['num_hits'] + $cache['num_misses'] )
25
+ );
26
+ }
27
+
28
+
29
+
30
+ public function get_summary() {
31
+ $cache = apcu_cache_info();
32
+
33
+ $time = time();
34
+ $runtime = $time - $cache['start_time'];
35
+
36
+ $mem = apcu_sma_info();
37
+ $mem_size = $mem['num_seg'] * $mem['seg_size'];
38
+ $mem_avail = $mem['avail_mem'];
39
+ $mem_used = $mem_size - $mem_avail;
40
+
41
+ $sum = array(
42
+ 'used_by' => implode( ',', $this->module_names ),
43
+ 'items' => $cache['num_entries'],
44
+ 'size_used' => Util_UsageStatistics::bytes_to_size( $cache['mem_size'] ),
45
+ 'get_hits' => $cache['num_hits'],
46
+ 'get_total' => ( $cache['num_hits'] + $cache['num_misses'] ),
47
+ 'runtime_secs' => $runtime,
48
+ 'evictions' => $cache['expunges'],
49
+ 'size_percent' => Util_UsageStatistics::percent(
50
+ $mem_used, $mem_avail )
51
+ );
52
+
53
+ if ( $sum['runtime_secs'] != 0 ) {
54
+ $sum['requests_per_second'] = sprintf( '%.2f',
55
+ $sum['get_total'] / $sum['runtime_secs'] );
56
+ }
57
+
58
+ $sum['get_hit_rate'] = Util_UsageStatistics::percent2(
59
+ $sum, 'get_hits', 'get_total' );
60
+
61
+ return $sum;
62
+ }
63
+ }
UsageStatistics_Sources_Memcached.php ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class UsageStatistics_Sources_Memcached {
7
+ private $servers;
8
+
9
+
10
+
11
+ public function __construct( $server_descriptors ) {
12
+ $this->servers = array();
13
+
14
+ foreach ( $server_descriptors as $i ) {
15
+ foreach ( $i['servers'] as $host_port ) {
16
+ if ( !isset( $this->servers[$host_port] ) )
17
+ $this->servers[$host_port] = array(
18
+ 'username' => $i['username'],
19
+ 'password' => $i['password'],
20
+ 'module_names' => array( $i['name'] )
21
+ );
22
+ else
23
+ $this->servers[$host_port]['module_names'][] = $i['name'];
24
+ }
25
+ }
26
+ }
27
+
28
+
29
+
30
+ public function get_snapshot() {
31
+ $size_used = 0;
32
+ $get_calls = 0;
33
+ $get_hits = 0;
34
+
35
+ foreach ( $this->servers as $host_port => $i ) {
36
+ $cache = Cache::instance( 'memcached',
37
+ array(
38
+ 'servers' => array( $host_port ),
39
+ 'username' => $i['username'],
40
+ 'password' => $i['password']
41
+ ) );
42
+
43
+ $stats = $cache->get_statistics();
44
+
45
+ $size_used += Util_UsageStatistics::v( $stats, 'bytes' );
46
+ $get_calls += Util_UsageStatistics::v( $stats, 'cmd_get' );
47
+ $get_hits += Util_UsageStatistics::v( $stats, 'get_hits' );
48
+ }
49
+
50
+ return array(
51
+ 'size_used' => $size_used,
52
+ 'get_calls' => $get_calls,
53
+ 'get_hits' => $get_hits
54
+ );
55
+ }
56
+
57
+
58
+
59
+ public function get_summary() {
60
+ $sum = array(
61
+ 'module_names' => array(),
62
+ 'size_used' => 0,
63
+ 'size_maxbytes' => 0,
64
+ 'get_total' => 0,
65
+ 'get_hits' => 0,
66
+ 'evictions' => 0,
67
+ 'uptime' => 0
68
+ );
69
+
70
+ foreach ( $this->servers as $host_port => $i ) {
71
+ $cache = Cache::instance( 'memcached',
72
+ array(
73
+ 'servers' => array( $host_port ),
74
+ 'username' => $i['username'],
75
+ 'password' => $i['password']
76
+ ) );
77
+
78
+ $stats = $cache->get_statistics();
79
+
80
+ $sum['module_names'] =
81
+ array_merge( $sum['module_names'], $i['module_names'] );
82
+ $sum['size_used'] += Util_UsageStatistics::v3( $stats, 'bytes');
83
+ $sum['size_maxbytes'] += Util_UsageStatistics::v3( $stats, 'limit_maxbytes' );
84
+ $sum['get_total'] += Util_UsageStatistics::v3( $stats, 'cmd_get' );
85
+ $sum['get_hits'] += Util_UsageStatistics::v3( $stats, 'get_hits' );
86
+ $sum['evictions'] += Util_UsageStatistics::v3( $stats, 'evictions' );
87
+ $sum['uptime'] += Util_UsageStatistics::v3( $stats, 'uptime' );
88
+ }
89
+
90
+ $summary = array(
91
+ 'module_names' => implode( ',', $sum['module_names'] ),
92
+ 'size_percent' => Util_UsageStatistics::percent2(
93
+ $sum, 'size_used', 'size_maxbytes' ),
94
+ 'size_used' => Util_UsageStatistics::bytes_to_size2(
95
+ $sum, 'size_used' ),
96
+ 'get_hit_rate' => Util_UsageStatistics::percent2(
97
+ $sum, 'get_hits', 'get_total' ),
98
+ 'evictions_per_second' => Util_UsageStatistics::value_per_second(
99
+ $sum, 'evictions', 'uptime' )
100
+ );
101
+
102
+ return $summary;
103
+ }
104
+ }
UsageStatistics_Sources_Redis.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace W3TC;
3
+
4
+
5
+
6
+ class UsageStatistics_Sources_Redis {
7
+ private $servers;
8
+
9
+
10
+
11
+ public function __construct( $server_descriptors ) {
12
+ $this->servers = array();
13
+
14
+ foreach ( $server_descriptors as $i ) {
15
+ foreach ( $i['servers'] as $host_port ) {
16
+ if ( !isset( $this->servers[$host_port] ) )
17
+ $this->servers[$host_port] = array(
18
+ 'password' => $i['password'],
19
+ 'dbid' => $i['dbid'],
20
+ 'module_names' => array( $i['name'] )
21
+ );
22
+ else
23
+ $this->servers[$host_port]['module_names'][] = $i['name'];
24
+ }
25
+ }
26
+ }
27
+
28
+
29
+
30
+ public function get_snapshot() {
31
+ $size_used = 0;
32
+ $get_calls = 0;
33
+ $get_hits = 0;
34
+
35
+ foreach ( $this->servers as $host_port => $i ) {
36
+ $cache = Cache::instance( 'redis',
37
+ array(
38
+ 'servers' => array( $host_port ),
39
+ 'password' => $i['password'],
40
+ 'dbid' => $i['dbid']
41
+ ) );
42
+
43
+ $stats = $cache->get_statistics();
44
+
45
+ $size_used += Util_UsageStatistics::v( $stats, 'used_memory' );
46
+ $get_calls +=
47
+ Util_UsageStatistics::v3( $stats, 'keyspace_hits' ) +
48
+ Util_UsageStatistics::v3( $stats, 'keyspace_misses' );
49
+ $get_hits += Util_UsageStatistics::v( $stats, 'keyspace_hits' );
50
+ }
51
+
52
+ return array(
53
+ 'size_used' => $size_used,
54
+ 'get_calls' => $get_calls,
55
+ 'get_hits' => $get_hits
56
+ );
57
+ }
58
+
59
+
60
+
61
+ public function get_summary() {
62
+ $sum = array(
63
+ 'module_names' => array(),
64
+ 'size_used' => 0,
65
+ 'size_maxbytes' => 0,
66
+ 'get_total' => 0,
67
+ 'get_hits' => 0,
68
+ 'evictions' => 0,
69
+ 'uptime' => 0
70
+ );
71
+
72
+ foreach ( $this->servers as $host_port => $i ) {
73
+ $cache = Cache::instance( 'redis',
74
+ array(
75
+ 'servers' => array( $host_port ),
76
+ 'password' => $i['password'],
77
+ 'dbid' => $i['dbid']
78
+ ) );
79
+
80
+ $stats = $cache->get_statistics();
81
+
82
+ $sum['module_names'] =
83
+ array_merge( $sum['module_names'], $i['module_names'] );
84
+ $sum['size_used'] += Util_UsageStatistics::v3( $stats, 'used_memory');
85
+ $sum['get_total'] +=
86
+ Util_UsageStatistics::v3( $stats, 'keyspace_hits' ) +
87
+ Util_UsageStatistics::v3( $stats, 'keyspace_misses' );
88
+ $sum['get_hits'] += Util_UsageStatistics::v3( $stats, 'keyspace_hits' );
89
+ $sum['evictions'] += Util_UsageStatistics::v3( $stats, 'evicted_keys' );
90
+ $sum['uptime'] += Util_UsageStatistics::v3( $stats, 'uptime_in_seconds' );
91
+ }
92
+
93
+ $summary = array(
94
+ 'module_names' => implode( ',', $sum['module_names'] ),
95
+ 'size_used' => Util_UsageStatistics::bytes_to_size2(
96
+ $sum, 'size_used' ),
97
+ 'get_hit_rate' => Util_UsageStatistics::percent2(
98
+ $sum, 'get_hits', 'get_total' ),
99
+ 'evictions_per_second' => Util_UsageStatistics::value_per_second(
100
+ $sum, 'evictions', 'uptime' )
101
+ );
102
+
103
+ return $summary;
104
+
105
+
106
+ $summary = array();
107
+
108
+ foreach ( $servers as $host_port => $i ) {
109
+ $cache = Cache::instance( 'redis',
110
+ array(
111
+ 'servers' => array( $host_port ),
112
+ 'password' => $i['password'],
113
+ 'dbid' => $i['dbid']
114
+ ) );
115
+
116
+ $stats = $cache->get_statistics();
117
+
118
+ if ( isset( $stats['keyspace_hits'] ) && $stats['keyspace_misses'] )
119
+ $stats['_keyspace_total'] =
120
+ (int)$stats['keyspace_hits'] + (int)$stats['keyspace_misses'];
121
+
122
+ $id = md5( $host_port );
123
+ $summary[$id] = array(
124
+ 'name' => $host_port,
125
+ 'module_names' => $i['module_names'],
126
+ 'size_used' =>
127
+ Util_UsageStatistics::bytes_to_size2( $stats, 'used_memory' ),
128
+ 'hit_rate' =>
129
+ Util_UsageStatistics::percent2( $stats, 'keyspace_hits', '_keyspace_total' ),
130
+ 'expirations_per_second' => Util_UsageStatistics::value_per_second(
131
+ $stats, 'expired_keys', 'uptime_in_seconds' ),
132
+ 'evictions_per_second' => Util_UsageStatistics::value_per_second(
133
+ $stats, 'evicted_keys', 'uptime_in_seconds' )
134
+ );
135
+ }
136
+
137
+ return $summary;
138
+ }
139
+ }
UsageStatistics_StorageReader.php CHANGED
@@ -7,25 +7,7 @@ namespace W3TC;
7
  *
8
  */
9
  class UsageStatistics_StorageReader {
10
- public function get_history_summary_promise() {
11
- $summary_promise = array(
12
- 'memcached_servers' => array(),
13
- );
14
-
15
- $summary_promise = apply_filters( 'w3tc_usage_statistics_summary_from_history',
16
- $summary_promise, array() );
17
- if ( isset( $summary_promise['memcached_servers'] ) &&
18
- count( $summary_promise['memcached_servers'] ) > 0 )
19
- $summary_promise['memcached'] = $this->get_memcached_summary(
20
- $summary_promise['memcached_servers'], true );
21
- if ( isset( $summary_promise['redis_servers'] ) &&
22
- count( $summary_promise['redis_servers'] ) > 0 )
23
- $summary_promise['redis'] = $this->get_redis_summary(
24
- $summary_promise['redis_servers'], true );
25
-
26
- return $summary_promise;
27
- }
28
-
29
 
30
  public function get_history_summary() {
31
  $w = new UsageStatistics_StorageWriter();
@@ -40,9 +22,12 @@ class UsageStatistics_StorageReader {
40
 
41
  $summary = array(
42
  'memcached_servers' => array(),
43
- 'redis_servers' => array()
 
44
  );
45
 
 
 
46
  if ( count( $history ) <= 0 ) {
47
  $summary = array( 'period' => array() );
48
  } else {
@@ -64,13 +49,6 @@ class UsageStatistics_StorageReader {
64
 
65
  $summary = apply_filters( 'w3tc_usage_statistics_summary_from_history',
66
  $summary, $history );
67
-
68
- if ( count( $summary['memcached_servers'] ) > 0 )
69
- $summary['memcached'] = $this->get_memcached_summary(
70
- $summary['memcached_servers'] );
71
- if ( count( $summary['redis_servers'] ) > 0 )
72
- $summary['redis'] = $this->get_redis_summary(
73
- $summary['redis_servers'] );
74
  }
75
 
76
  $summary['period']['to_update_secs'] = (int)$w->get_hotspot_end() - time() + 1;
@@ -78,112 +56,11 @@ class UsageStatistics_StorageReader {
78
  unset( $summary['memcached_servers'] );
79
  unset( $summary['redis_servers'] );
80
 
81
- return $summary;
82
- }
83
-
84
-
85
-
86
- private function get_memcached_summary( $server_descriptors, $promise_only = false ) {
87
- $servers = array();
88
-
89
- foreach ( $server_descriptors as $i ) {
90
- foreach ( $i['servers'] as $host_port ) {
91
- if ( !isset( $servers[$host_port] ) )
92
- $servers[$host_port] = array(
93
- 'username' => $i['username'],
94
- 'password' => $i['password'],
95
- 'module_names' => array( $i['name'] )
96
- );
97
- else
98
- $servers[$host_port]['module_names'][] = $i['name'];
99
- }
100
- }
101
-
102
- $summary = array();
103
-
104
- foreach ( $servers as $host_port => $i ) {
105
- $cache = Cache::instance( 'memcached',
106
- array(
107
- 'servers' => array( $host_port ),
108
- 'username' => $i['username'],
109
- 'password' => $i['password']
110
- ) );
111
-
112
- if ( $promise_only )
113
- $stats = array();
114
- else
115
- $stats = $cache->get_statistics();
116
-
117
- $id = md5( $host_port );
118
- $summary[$id] = array(
119
- 'name' => $host_port,
120
- 'module_names' => $i['module_names'],
121
- 'size_used' =>
122
- Util_UsageStatistics::bytes_to_size2( $stats, 'bytes' ),
123
- 'size_percent' =>
124
- Util_UsageStatistics::percent2( $stats, 'bytes', 'limit_maxbytes' ),
125
- 'get_hit_rate' =>
126
- Util_UsageStatistics::percent2( $stats, 'get_hits', 'cmd_get' ),
127
- 'evictions_per_second' => Util_UsageStatistics::value_per_second(
128
- $stats, 'evictions', 'uptime' )
129
- );
130
- }
131
-
132
- return $summary;
133
- }
134
-
135
-
136
-
137
- private function get_redis_summary( $server_descriptors, $promise_only = false ) {
138
- $servers = array();
139
-
140
- foreach ( $server_descriptors as $i ) {
141
- foreach ( $i['servers'] as $host_port ) {
142
- if ( !isset( $servers[$host_port] ) )
143
- $servers[$host_port] = array(
144
- 'password' => $i['password'],
145
- 'dbid' => $i['dbid'],
146
- 'module_names' => array( $i['name'] )
147
- );
148
- else
149
- $servers[$host_port]['module_names'][] = $i['name'];
150
- }
151
- }
152
-
153
- $summary = array();
154
-
155
- foreach ( $servers as $host_port => $i ) {
156
- $cache = Cache::instance( 'redis',
157
- array(
158
- 'servers' => array( $host_port ),
159
- 'password' => $i['password'],
160
- 'dbid' => $i['dbid']
161
- ) );
162
-
163
- if ( $promise_only )
164
- $stats = array();
165
- else
166
- $stats = $cache->get_statistics();
167
-
168
- if ( isset( $stats['keyspace_hits'] ) && $stats['keyspace_misses'] )
169
- $stats['_keyspace_total'] =
170
- (int)$stats['keyspace_hits'] + (int)$stats['keyspace_misses'];
171
-
172
- $id = md5( $host_port );
173
- $summary[$id] = array(
174
- 'name' => $host_port,
175
- 'module_names' => $i['module_names'],
176
- 'size_used' =>
177
- Util_UsageStatistics::bytes_to_size2( $stats, 'used_memory' ),
178
- 'hit_rate' =>
179
- Util_UsageStatistics::percent2( $stats, 'keyspace_hits', '_keyspace_total' ),
180
- 'expirations_per_second' => Util_UsageStatistics::value_per_second(
181
- $stats, 'expired_keys', 'uptime_in_seconds' ),
182
- 'evictions_per_second' => Util_UsageStatistics::value_per_second(
183
- $stats, 'evicted_keys', 'uptime_in_seconds' )
184
- );
185
  }
186
 
 
187
  return $summary;
188
  }
189
  }
7
  *
8
  */
9
  class UsageStatistics_StorageReader {
10
+ private $items_to_return = 60;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  public function get_history_summary() {
13
  $w = new UsageStatistics_StorageWriter();
22
 
23
  $summary = array(
24
  'memcached_servers' => array(),
25
+ 'redis_servers' => array(),
26
+ 'apc_servers' => array()
27
  );
28
 
29
+ $summary = apply_filters( 'w3tc_usage_statistics_sources', $summary );
30
+
31
  if ( count( $history ) <= 0 ) {
32
  $summary = array( 'period' => array() );
33
  } else {
49
 
50
  $summary = apply_filters( 'w3tc_usage_statistics_summary_from_history',
51
  $summary, $history );
 
 
 
 
 
 
 
52
  }
53
 
54
  $summary['period']['to_update_secs'] = (int)$w->get_hotspot_end() - time() + 1;
56
  unset( $summary['memcached_servers'] );
57
  unset( $summary['redis_servers'] );
58
 
59
+ while ( count($history) < $this->items_to_return ) {
60
+ array_unshift( $history, array() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
 
63
+ $summary['history'] = $history;
64
  return $summary;
65
  }
66
  }
UsageStatistics_StorageWriter.php CHANGED
@@ -7,8 +7,9 @@ namespace W3TC;
7
  *
8
  */
9
  class UsageStatistics_StorageWriter {
10
- public static $slot_interval_secs = 60;
11
- private $keep_history_interval_secs = 600;
 
12
 
13
  private $cache_storage;
14
 
@@ -35,20 +36,44 @@ class UsageStatistics_StorageWriter {
35
 
36
  public function __construct() {
37
  $this->cache_storage = Dispatcher::get_usage_statistics_cache();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
 
40
 
41
 
42
  public function counter_add( $metric, $value ) {
43
- if ( !is_null( $this->cache_storage ) )
44
  $this->cache_storage->counter_add( $metric, $value );
 
45
  }
46
 
47
 
48
 
49
  public function get_hotspot_end() {
50
- $option_storage = $this->get_option_storage();
51
- return $option_storage->get_hotspot_end();
 
 
 
 
52
  }
53
 
54
 
@@ -78,32 +103,29 @@ class UsageStatistics_StorageWriter {
78
  * at the same time when hotspot time ended.
79
  */
80
  public function begin_flush_hotspot_data() {
81
- if ( is_null( $this->hotspot_endtime ) ) {
 
82
  // if cache not recognized - means nothing is cached at all
83
  // so stats not collected
84
- if ( is_null( $this->cache_storage ) )
85
- return 'not_needed';
86
-
87
- $v = $this->cache_storage->get( 'hotspot_endtime' );
88
- $this->hotspot_endtime = ( isset( $v['content'] ) ? $v['content'] : 0 );
89
  }
90
 
91
- $hotspot_endtime_int = (int)$this->hotspot_endtime;
92
  $this->now = time();
93
 
94
  if ( $hotspot_endtime_int <= 0 ) {
95
  $this->flush_state = 'require_db';
96
- } else if ( $this->now < $hotspot_endtime_int ) {
97
- $this->flush_state = 'not_needed';
98
- } else {
99
  // rand value makes value unique for each process,
100
  // so as a result next replace works as a lock
101
  // passing only single process further
102
- $this->new_hotspot_endtime = $this->now + self::$slot_interval_secs +
103
  ( rand( 1, 9999 ) / 10000.0 );
104
 
105
  $succeeded = $this->cache_storage->set_if_maybe_equals( 'hotspot_endtime',
106
- array( 'content' => $this->hotspot_endtime ),
107
  array( 'content' => $this->new_hotspot_endtime ) );
108
  $this->flush_state =
109
  ( $succeeded ? 'flushing_began_by_cache' : 'not_needed' );
@@ -136,7 +158,7 @@ class UsageStatistics_StorageWriter {
136
  }
137
  if ( $this->new_hotspot_endtime <= 0 )
138
  $this->new_hotspot_endtime = $this->now +
139
- self::$slot_interval_secs +
140
  ( rand( 1, 9999 ) / 10000.0 );
141
 
142
  if ( $hotspot_endtime_int <= 0 ) {
@@ -178,6 +200,9 @@ class UsageStatistics_StorageWriter {
178
  $this->cache_storage->counter_set( $metric, 0 );
179
  }
180
 
 
 
 
181
  $history_encoded = get_site_option( 'w3tc_stats_history' );
182
  $history = null;
183
  if ( !empty( $history_encoded ) )
@@ -185,14 +210,14 @@ class UsageStatistics_StorageWriter {
185
  if ( !is_array( $history ) )
186
  $history = array();
187
 
188
- $time_keep_border = time() - $this->keep_history_interval_secs;
189
 
190
  if ( $hotspot_endtime_int < $time_keep_border )
191
  $history = array(
192
  array(
193
  'timestamp_start' => $time_keep_border,
194
  'timestamp_end' => (int)$this->new_hotspot_endtime -
195
- self::$slot_interval_secs - 1
196
  )
197
  ); // this was started too much time from now
198
  else {
@@ -205,7 +230,7 @@ class UsageStatistics_StorageWriter {
205
  'timestamp_start' => $metric_values['timestamp_end']
206
  );
207
  $metric_values['timestamp_end'] =
208
- $metric_values['timestamp_start'] + self::$slot_interval_secs;
209
  if ( $metric_values['timestamp_end'] < $this->now )
210
  $history[] = $metric_values;
211
  else
@@ -213,7 +238,7 @@ class UsageStatistics_StorageWriter {
213
  }
214
 
215
  // make sure we have at least one value in history
216
- for ( ;count( $history ) > 1; ) {
217
  if ( !isset( $history[0]['timestamp_end'] ) ||
218
  $history[0]['timestamp_end'] < $time_keep_border )
219
  array_shift( $history );
@@ -222,6 +247,9 @@ class UsageStatistics_StorageWriter {
222
  }
223
  }
224
 
 
 
 
225
  update_site_option( 'w3tc_stats_hotspot_start', $this->now );
226
  update_site_option( 'w3tc_stats_history', json_encode( $history ) );
227
  }
7
  *
8
  */
9
  class UsageStatistics_StorageWriter {
10
+ private $slot_interval_seconds;
11
+ private $slots_count;
12
+ private $keep_history_interval_seconds;
13
 
14
  private $cache_storage;
15
 
36
 
37
  public function __construct() {
38
  $this->cache_storage = Dispatcher::get_usage_statistics_cache();
39
+
40
+ $c = Dispatcher::config();
41
+ $this->slot_interval_seconds = $c->get_integer( 'stats.slot_seconds' );
42
+
43
+ $this->keep_history_interval_seconds =
44
+ $c->get_integer( 'stats.slots_count' ) *
45
+ $this->slot_interval_seconds;
46
+ $this->slots_count = $c->get_integer( 'stats.slots_count' );
47
+ }
48
+
49
+
50
+
51
+ public function reset() {
52
+ if ( !is_null( $this->cache_storage ) ) {
53
+ $this->cache_storage->set( 'hotspot_endtime', 0 );
54
+ }
55
+
56
+ update_site_option( 'w3tc_stats_hotspot_start', time() );
57
+ update_site_option( 'w3tc_stats_history', '' );
58
  }
59
 
60
 
61
 
62
  public function counter_add( $metric, $value ) {
63
+ if ( !is_null( $this->cache_storage ) ) {
64
  $this->cache_storage->counter_add( $metric, $value );
65
+ }
66
  }
67
 
68
 
69
 
70
  public function get_hotspot_end() {
71
+ if ( is_null( $this->hotspot_endtime ) ) {
72
+ $v = $this->cache_storage->get( 'hotspot_endtime' );
73
+ $this->hotspot_endtime = ( isset( $v['content'] ) ? $v['content'] : 0 );
74
+ }
75
+
76
+ return $this->hotspot_endtime;
77
  }
78
 
79
 
103
  * at the same time when hotspot time ended.
104
  */
105
  public function begin_flush_hotspot_data() {
106
+ $hotspot_endtime = $this->get_hotspot_end();
107
+ if ( is_null( $hotspot_endtime ) ) {
108
  // if cache not recognized - means nothing is cached at all
109
  // so stats not collected
110
+ return 'not_needed';
 
 
 
 
111
  }
112
 
113
+ $hotspot_endtime_int = (int)$hotspot_endtime;
114
  $this->now = time();
115
 
116
  if ( $hotspot_endtime_int <= 0 ) {
117
  $this->flush_state = 'require_db';
118
+ } elseif ( $this->now < $hotspot_endtime_int ) {
119
+ $this->flush_state = 'not_needed';
120
+ } else {
121
  // rand value makes value unique for each process,
122
  // so as a result next replace works as a lock
123
  // passing only single process further
124
+ $this->new_hotspot_endtime = $this->now + $this->slot_interval_seconds +
125
  ( rand( 1, 9999 ) / 10000.0 );
126
 
127
  $succeeded = $this->cache_storage->set_if_maybe_equals( 'hotspot_endtime',
128
+ array( 'content' => $hotspot_endtime ),
129
  array( 'content' => $this->new_hotspot_endtime ) );
130
  $this->flush_state =
131
  ( $succeeded ? 'flushing_began_by_cache' : 'not_needed' );
158
  }
159
  if ( $this->new_hotspot_endtime <= 0 )
160
  $this->new_hotspot_endtime = $this->now +
161
+ $this->slot_interval_seconds +
162
  ( rand( 1, 9999 ) / 10000.0 );
163
 
164
  if ( $hotspot_endtime_int <= 0 ) {
200
  $this->cache_storage->counter_set( $metric, 0 );
201
  }
202
 
203
+ $metric_values = apply_filters( 'w3tc_usage_statistics_metric_values',
204
+ $metric_values );
205
+
206
  $history_encoded = get_site_option( 'w3tc_stats_history' );
207
  $history = null;
208
  if ( !empty( $history_encoded ) )
210
  if ( !is_array( $history ) )
211
  $history = array();
212
 
213
+ $time_keep_border = time() - $this->keep_history_interval_seconds;
214
 
215
  if ( $hotspot_endtime_int < $time_keep_border )
216
  $history = array(
217
  array(
218
  'timestamp_start' => $time_keep_border,
219
  'timestamp_end' => (int)$this->new_hotspot_endtime -
220
+ $this->slot_interval_seconds - 1
221
  )
222
  ); // this was started too much time from now
223
  else {
230
  'timestamp_start' => $metric_values['timestamp_end']
231
  );
232
  $metric_values['timestamp_end'] =
233
+ $metric_values['timestamp_start'] + $this->slot_interval_seconds;
234
  if ( $metric_values['timestamp_end'] < $this->now )
235
  $history[] = $metric_values;
236
  else
238
  }
239
 
240
  // make sure we have at least one value in history
241
+ for ( ;count( $history ) > $this->slots_count; ) {
242
  if ( !isset( $history[0]['timestamp_end'] ) ||
243
  $history[0]['timestamp_end'] < $time_keep_border )
244
  array_shift( $history );
247
  }
248
  }
249
 
250
+ $history = apply_filters(
251
+ 'w3tc_usage_statistics_history_set', $history );
252
+
253
  update_site_option( 'w3tc_stats_hotspot_start', $this->now );
254
  update_site_option( 'w3tc_stats_history', json_encode( $history ) );
255
  }
UsageStatistics_View_General.php DELETED
@@ -1,32 +0,0 @@
1
- <?php
2
- namespace W3TC;
3
-
4
- if ( !defined( 'W3TC' ) )
5
- die();
6
-
7
- ?>
8
- <tr>
9
- <th colspan="2">
10
- <?php
11
-
12
- $c = Dispatcher::config();
13
- $is_pro = Util_Environment::is_w3tc_pro( $c );
14
-
15
- $key = 'stats.enabled';
16
- $value = $c->get( $key );
17
- if ( !$is_pro )
18
- $value = false;
19
-
20
- $name = Util_Ui::config_key_to_http_name( $key );
21
- Util_Ui::checkbox( $key,
22
- $name,
23
- $value,
24
- $c->is_sealed( 'common.' ) || !$is_pro,
25
- __( 'Enable caching statistics (on dashboard)', 'w3-total-cache' )
26
- );
27
-
28
- if ( !$is_pro )
29
- echo ' (Available after <a href="#" class="button-buy-plugin">upgrade</a>)';
30
- ?>
31
- </th>
32
- </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
UsageStatistics_Widget.php CHANGED
@@ -7,23 +7,17 @@ namespace W3TC;
7
  class UsageStatistics_Widget {
8
  private $enabled = false;
9
 
 
 
10
  public function init() {
11
  $c = Dispatcher::config();
12
  $this->enabled = ( $c->get_boolean( 'stats.enabled' ) &&
13
  Util_Environment::is_w3tc_pro( $c ) );
14
 
15
- add_action( 'admin_print_styles-toplevel_page_w3tc_dashboard',
16
- array( $this, 'admin_print_styles_w3tc_dashboard' ) );
17
-
18
- if ( $this->enabled )
19
- add_action( 'admin_print_scripts-toplevel_page_w3tc_dashboard',
20
- array( $this, 'admin_print_scripts_w3tc_dashboard' ) );
21
-
22
  add_action( 'w3tc_widget_setup', array(
23
  $this,
24
  'w3tc_widget_setup'
25
  ), 1000 );
26
- add_action( 'w3tc_ajax_ustats_get', array( $this, 'w3tc_ajax_ustats_get' ) );
27
  }
28
 
29
 
@@ -35,46 +29,32 @@ class UsageStatistics_Widget {
35
  __( 'Caching Statistics', 'w3-total-cache' ) .
36
  '</div>',
37
  array( $this, 'widget_form' ),
38
- Util_Ui::admin_url( 'admin.php?page=w3tc_general#miscellaneous' ),
39
- 'normal' );
40
- }
41
-
42
-
43
-
44
- public function widget_form() {
45
- $storage = new UsageStatistics_StorageReader();
46
- $summary_promise = $storage->get_history_summary_promise();
47
-
48
- if ( $this->enabled )
49
- include W3TC_DIR . '/UsageStatistics_Widget_View.php';
50
- else
51
- include W3TC_DIR . '/UsageStatistics_Widget_View_Disabled.php';
52
  }
53
 
54
 
55
 
56
- public function admin_print_styles_w3tc_dashboard() {
57
- wp_enqueue_style( 'w3tc-widget' );
58
- wp_enqueue_style( 'w3tc-widget-usage-statistics',
59
- plugins_url( 'UsageStatistics_Widget_View.css', W3TC_FILE ),
60
  array(), W3TC_VERSION );
61
- }
62
-
63
-
64
-
65
- public function admin_print_scripts_w3tc_dashboard() {
66
- wp_enqueue_script( 'w3tc-widget-usage-statistics',
67
  plugins_url( 'UsageStatistics_Widget_View.js', W3TC_FILE ),
68
  array(), W3TC_VERSION );
69
  }
70
 
71
 
72
 
73
- public function w3tc_ajax_ustats_get() {
74
- $storage = new UsageStatistics_StorageReader();
75
- $summary = $storage->get_history_summary();
76
-
77
- echo json_encode( $summary );
78
- exit();
 
 
 
79
  }
80
  }
7
  class UsageStatistics_Widget {
8
  private $enabled = false;
9
 
10
+
11
+
12
  public function init() {
13
  $c = Dispatcher::config();
14
  $this->enabled = ( $c->get_boolean( 'stats.enabled' ) &&
15
  Util_Environment::is_w3tc_pro( $c ) );
16
 
 
 
 
 
 
 
 
17
  add_action( 'w3tc_widget_setup', array(
18
  $this,
19
  'w3tc_widget_setup'
20
  ), 1000 );
 
21
  }
22
 
23
 
29
  __( 'Caching Statistics', 'w3-total-cache' ) .
30
  '</div>',
31
  array( $this, 'widget_form' ),
32
+ Util_Ui::admin_url( 'admin.php?page=w3tc_stats' ),
33
+ 'normal',
34
+ 'Detailed' );
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
 
38
 
39
+ static public function admin_init_w3tc_dashboard() {
40
+ wp_enqueue_script( 'w3tc-canvasjs',
41
+ plugins_url( 'pub/js/chartjs.min.js', W3TC_FILE ),
 
42
  array(), W3TC_VERSION );
43
+ wp_enqueue_script( 'w3tc-widget-usagestatistics',
 
 
 
 
 
44
  plugins_url( 'UsageStatistics_Widget_View.js', W3TC_FILE ),
45
  array(), W3TC_VERSION );
46
  }
47
 
48
 
49
 
50
+ public function widget_form() {
51
+ $c = Dispatcher::config();
52
+ $enabled = ( $c->get_boolean( 'stats.enabled' ) &&
53
+ Util_Environment::is_w3tc_pro( $c ) );
54
+ if ( $enabled ) {
55
+ include W3TC_DIR . '/UsageStatistics_Widget_View.php';
56
+ } else {
57
+ include W3TC_DIR . '/UsageStatistics_Widget_View_Disabled.php';
58
+ }
59
  }
60
  }
UsageStatistics_Widget_View.css DELETED
@@ -1,45 +0,0 @@
1
- .ustats_p {
2
- text-align: center;
3
- }
4
-
5
- .ustats_header {
6
- font-weight: bold;
7
- }
8
-
9
- .ustats_table {
10
- width: 100%;
11
- padding-left: 20px;
12
- padding-right: 20px;
13
- }
14
-
15
- td.ustats_td {
16
- padding: 0;
17
- padding-right: 10px;
18
- font-weight: bold;
19
- color: #333;
20
- }
21
-
22
- td.ustats_td_header_name {
23
- padding: 0;
24
- padding-right: 5px;
25
- padding-top: 5px;
26
- text-align: left;
27
- font-weight: bold;
28
- color: #333;
29
- }
30
-
31
- td.ustats_td_header {
32
- padding: 0;
33
- padding-right: 5px;
34
- padding-top: 5px;
35
- text-align: right;
36
- font-weight: bold;
37
- color: #333;
38
- }
39
-
40
- td.ustats_td_value {
41
- padding: 0;
42
- padding-right: 8px;
43
- text-align: right;
44
- color: #333;
45
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
UsageStatistics_Widget_View.js CHANGED
@@ -1,72 +1,208 @@
1
  jQuery(document).ready(function($) {
2
- function w3tc_ustats_load() {
3
- top_object = $('.ustats_top');
4
- $('.ustats_loading').removeClass('w3tc_hidden');
5
- $('.ustats_content').addClass('w3tc_hidden');
6
- $('.ustats_error').addClass('w3tc_none');
7
- $('.ustats_nodata').addClass('w3tc_none');
8
-
9
- $.getJSON(ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
10
  '&w3tc_action=ustats_get',
11
  function(data) {
12
- w3tc_ustats_set_values(data, 'ustats_');
13
-
14
- if (data.period.seconds)
15
- $('.ustats_content').removeClass('w3tc_hidden');
16
- else
17
- $('.ustats_nodata').removeClass('w3tc_none');
18
 
19
- $('.ustats_loading').addClass('w3tc_hidden');
20
-
21
- w3tc_ustats_set_refresh(
22
- (data && data.period ? data.period.to_update_secs : 0));
23
  }
24
  ).fail(function() {
25
- $('.ustats_error').removeClass('w3tc_none');
26
- $('.ustats_content').addClass('w3tc_hidden');
27
- $('.ustats_loading').addClass('w3tc_hidden');
28
  });
29
  }
30
 
31
 
32
 
33
- function w3tc_ustats_set_values(data, css_class_prefix) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  for (p in data) {
35
  var v = data[p];
36
  if (typeof(v) != 'string' && typeof(v) != 'number')
37
- w3tc_ustats_set_values(v, css_class_prefix + p + '_');
38
  else {
39
- jQuery('.' + css_class_prefix + p).html(v);
 
40
  }
41
  }
42
  }
43
 
44
 
45
 
46
- var seconds_timer_id;
47
- function w3tc_ustats_set_refresh(new_seconds_till_refresh) {
48
- clearTimeout(seconds_timer_id);
49
- var seconds_till_refresh = new_seconds_till_refresh;
50
-
51
- seconds_timer_id = setInterval(function() {
52
- seconds_till_refresh--;
53
- if (seconds_till_refresh <= 0) {
54
- jQuery('.ustats_reload').text('Refresh');
55
- clearTimeout(seconds_timer_id);
56
- seconds_timer_id = null;
57
- return;
58
- }
59
-
60
- jQuery('.ustats_reload').text('Will be recalculated in ' +
61
- seconds_till_refresh + ' second' +
62
- (seconds_till_refresh > 1 ? 's' : ''));
63
- }, 1000);
64
- }
65
-
66
- w3tc_ustats_load();
67
-
68
- $('.ustats_reload').click(function(e) {
69
- event.preventDefault();
70
- w3tc_ustats_load();
71
- })
72
  });
1
  jQuery(document).ready(function($) {
2
+ var lastData;
3
+
4
+
5
+
6
+ function load() {
7
+ $.getJSON(ajaxurl + '?action=w3tc_ajax&_wpnonce=' + w3tc_nonce +
 
 
8
  '&w3tc_action=ustats_get',
9
  function(data) {
10
+ lastData = data;
 
 
 
 
 
11
 
12
+ setValues(data, 'w3tcuw_');
13
+ setChart(data);
 
 
14
  }
15
  ).fail(function() {
16
+ console.log('failed to load widget data');
 
 
17
  });
18
  }
19
 
20
 
21
 
22
+ //
23
+ // chart commons
24
+ //
25
+ var chartOptions = {
26
+ //aspectRatio: 4,
27
+ maintainAspectRatio: false,
28
+ height: '200px',
29
+ legend: false,
30
+ scales: {
31
+ yAxes: [{
32
+ ticks: {
33
+ beginAtZero: true
34
+ }
35
+ }]
36
+ }
37
+ };
38
+
39
+
40
+
41
+ var chartDateLabels = [];
42
+ var chartGraphValues = {};
43
+ var chartObject;
44
+
45
+
46
+
47
+ function setChart(data) {
48
+ var ctx = $('#w3tcuw_chart');
49
+ chartObject = new Chart(ctx, {
50
+ type: 'line',
51
+ data: {
52
+ labels: chartDateLabels,
53
+ },
54
+ options: chartOptions
55
+ });
56
+
57
+ // collect functors that prepare data for their own chart
58
+ var datasetTemplates = [];
59
+ datasetTemplates.push(setChartsDbCache());
60
+ datasetTemplates.push(setChartsObjectCache());
61
+ datasetTemplates.push(setChartsPageCache());
62
+
63
+ // prepare collections
64
+ var datasets = [];
65
+
66
+ for (var i = 0; i < datasetTemplates.length; i++) {
67
+ var datasetTemplate = datasetTemplates[i];
68
+ var datasetName = datasetTemplate.name;
69
+
70
+ chartGraphValues[datasetName] = [];
71
+ datasets.push({
72
+ label: datasetTemplate.label,
73
+ data: chartGraphValues[datasetName],
74
+ borderColor: datasetTemplate.borderColor,
75
+ fill: false
76
+ });
77
+ }
78
+
79
+ chartObject.data.datasets = datasets;
80
+
81
+ // collect data for charts
82
+ var history = data.history;
83
+ chartDateLabels.length = 0;
84
+ var averagesToCollect = Math.floor(history.length / 10);
85
+ if (averagesToCollect <= 1) {
86
+ averagesToCollect = 1;
87
+ }
88
+
89
+ var averages = {};
90
+ var averagesCollected = 0;
91
+
92
+ for (var i = 0; i < history.length; i++) {
93
+ var historyItem = history[i];
94
+
95
+ // collect metrics for graphs
96
+ for (var i2 = 0; i2 < datasetTemplates.length; i2++) {
97
+ var c = datasetTemplates[i2];
98
+ var v = c.valueFunctor(historyItem) * 100;
99
+ averages[i2] = (!averages[i2] ? 0 : averages[i2]) + v;
100
+ }
101
+
102
+ averagesCollected++;
103
+ if (averagesCollected >= averagesToCollect) {
104
+ var dateFormatted = '';
105
+ if (history[i].timestamp_start) {
106
+ var d = new Date(parseInt(history[i].timestamp_start) * 1000);
107
+ dateFormatted = dateFormat(d);
108
+ }
109
+
110
+ chartDateLabels.push(dateFormatted);
111
+
112
+ for (var i2 = 0; i2 < datasetTemplates.length; i2++) {
113
+ var c = datasetTemplates[i2];
114
+ var v = (averages[i2] / averagesCollected).toFixed(2);
115
+ chartGraphValues[c.name].push(v);
116
+ }
117
+
118
+ averages = {};
119
+ averagesCollected = 0;
120
+ }
121
+ }
122
+
123
+ // visualize
124
+ chartObject.update();
125
+ }
126
+
127
+
128
+
129
+ //
130
+ // chart data
131
+ //
132
+ function setChartsDbCache() {
133
+ return {
134
+ label: 'Database cache',
135
+ name: 'dbcache_hit_rate',
136
+ valueFunctor: function(i) {
137
+ return i.dbcache_calls_total == 0 ? 0 :
138
+ i.dbcache_calls_hits / i.dbcache_calls_total;
139
+ },
140
+ borderColor: '#0073aa'
141
+ };
142
+ }
143
+
144
+
145
+
146
+ function setChartsObjectCache() {
147
+ return {
148
+ label: 'Object cache',
149
+ name: 'objectcache_hit_rate',
150
+ valueFunctor: function(i) {
151
+ return i.objectcache_get_total == 0 ? 0 :
152
+ i.objectcache_get_hits / i.objectcache_get_total;
153
+ },
154
+ borderColor: 'green'
155
+ };
156
+ }
157
+
158
+
159
+
160
+ function setChartsPageCache() {
161
+ return {
162
+ label: 'Page cache',
163
+ name: 'pagecache_hit_rate',
164
+ valueFunctor: function(i) {
165
+ return i.php_requests == 0 ? 0 :
166
+ i.php_requests_pagecache_hit / i.php_requests;
167
+ },
168
+ borderColor: 'blue'
169
+ };
170
+ }
171
+
172
+
173
+
174
+ //
175
+ // Utils
176
+ //
177
+ function startsWith(s, prefix) {
178
+ return s.substr(0, prefix.length) == prefix;
179
+ }
180
+
181
+
182
+
183
+ function dateFormat(d) {
184
+ return ("0" + d.getUTCHours()).slice(-2) + ":" +
185
+ ("0" + d.getUTCMinutes()).slice(-2);
186
+ }
187
+
188
+
189
+
190
+ function setValues(data, css_class_prefix) {
191
  for (p in data) {
192
  var v = data[p];
193
  if (typeof(v) != 'string' && typeof(v) != 'number')
194
+ setValues(v, css_class_prefix + p + '_');
195
  else {
196
+ jQuery('.' + css_class_prefix + p + ' .w3tcuw_value').html(v);
197
+ jQuery('.' + css_class_prefix + p).css('display', 'block');
198
  }
199
  }
200
  }
201
 
202
 
203
 
204
+ //
205
+ // Main entry
206
+ //
207
+ load();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  });
UsageStatistics_Widget_View.php CHANGED
@@ -4,116 +4,45 @@ namespace W3TC;
4
  if ( !defined( 'W3TC' ) )
5
  die();
6
  ?>
7
- <div class="ustats_loading w3tc_loading">Loading...</div>
8
- <div class="ustats_error w3tc_none">An error occurred</div>
9
- <div class="ustats_nodata w3tc_none">No data collected</div>
10
- <div class="ustats_content w3tc_hidden">
11
- <p class="ustats_p ustats_top">
12
- Period <span class="ustats_period_timestamp_start_mins"></span>
13
- -
14
- <span class="ustats_period_timestamp_end_mins"></span>
15
- </p>
16
-
17
-
18
-
19
- <?php if ( isset( $summary_promise['pagecache'] ) ): ?>
20
- <div class="ustats_header">Page Cache:</div>
21
- <?php if ( $summary_promise['pagecache']['size_visible'] ): ?>
22
- Cache size: <span class="ustats_pagecache_size_used"></span><br />
23
- Entries: <span class="ustats_pagecache_items"></span><br />
24
- <?php endif; ?>
25
- <?php if ( $summary_promise['pagecache']['requests_visible'] ): ?>
26
- Requests/period: <span class="ustats_pagecache_requests_total"></span><br />
27
- Requests/sec: <span class="ustats_pagecache_requests_per_second"></span><br />
28
- Avg processing time (ms): <span class="ustats_pagecache_request_time_ms"></span><br />
29
- Hit rate: <span class="ustats_pagecache_hit_rate"></span><br />
30
- <?php endif; ?>
31
- <br />
32
- <?php endif; ?>
33
-
34
-
35
-
36
- <?php if ( isset( $summary_promise['minify'] ) ): ?>
37
- <div class="ustats_header">Minify:</div>
38
- <?php if ( $summary_promise['minify']['size_visible'] ): ?>
39
- Used: <span class="ustats_minify_size_used"></span><br />
40
- Files: <span class="ustats_minify_size_items"></span><br />
41
- CSS compression in cache: <span class="ustats_minify_size_compression_css"></span><br />
42
- JS compression in cache: <span class="ustats_minify_size_compression_js"></span><br />
43
- <?php endif ?>
44
- <?php if ( $summary_promise['minify']['requests_visible'] ): ?>
45
- Requests/period: <span class="ustats_minify_requests_total"></span><br />
46
- Requests/sec: <span class="ustats_minify_requests_per_second"></span><br />
47
- Responded CSS compression: <span class="ustats_minify_compression_css"></span><br />
48
- Responded JS compression: <span class="ustats_minify_compression_js"></span><br />
49
- <?php endif; ?>
50
- <br />
51
- <?php endif; ?>
52
-
53
-
54
-
55
- <?php if ( isset( $summary_promise['objectcache'] ) ): ?>
56
- <div class="ustats_header">Object Cache:</div>
57
- Calls/period: <span class="ustats_objectcache_calls_total"></span><br />
58
- Calls/sec: <span class="ustats_objectcache_calls_per_second"></span><br />
59
- Hit rate: <span class="ustats_objectcache_hit_rate"></span><br />
60
- <br />
61
- <?php endif; ?>
62
-
63
-
64
-
65
- <?php if ( isset( $summary_promise['fragmentcache'] ) ): ?>
66
- <div class="ustats_header">Fragment Cache:</div>
67
- Calls/period: <span class="ustats_fragmentcache_calls_total"></span><br />
68
- Calls/sec: <span class="ustats_fragmentcache_calls_per_second"></span><br />
69
- Hit rate: <span class="ustats_fragmentcache_hit_rate"></span><br />
70
- <br />
71
- <?php endif; ?>
72
-
73
-
74
-
75
- <?php if ( isset( $summary_promise['dbcache'] ) ): ?>
76
- <?php $m = $summary_promise['dbcache']; ?>
77
- <div class="ustats_header">Database Cache:</div>
78
- Calls/period: <span class="ustats_dbcache_calls_total"></span><br />
79
- Calls/sec: <span class="ustats_dbcache_calls_per_second"></span><br />
80
- Hit rate: <span class="ustats_dbcache_hit_rate"></span><br />
81
- <br />
82
- <?php endif; ?>
83
-
84
-
85
-
86
- <div class="ustats_header">PHP Memory:</div>
87
- <span class="ustats_php_memory"></span><br />
88
-
89
-
90
-
91
- WordPress requests/period: <span class="ustats_php_wp_requests_total"></span><br />
92
- WordPress requests/sec: <span class="ustats_php_wp_requests_per_second"></span><br />
93
- <br />
94
-
95
-
96
- <?php if ( !empty( $summary_promise['memcached'] ) ): ?>
97
- <?php foreach ( $summary_promise['memcached'] as $id => $m ): ?>
98
- <div class="ustats_header">Memcached <?php echo $m['name'] ?></div>
99
- Used by <?php echo implode( ',', $m['module_names'] ) ?><br />
100
- Evictions/sec: <span class="ustats_memcached_<?php echo $id ?>_evictions_per_second"></span><br />
101
- Used: <span class="ustats_memcached_<?php echo $id ?>_size_used"></span><br />
102
- Used (%): <span class="ustats_memcached_<?php echo $id ?>_size_percent"></span><br />
103
- Hit rate: <span class="ustats_memcached_<?php echo $id ?>_get_hit_rate"></span><br />
104
- <?php endforeach ?>
105
- <?php endif; ?>
106
-
107
 
108
- <?php if ( !empty( $summary_promise['redis'] ) ): ?>
109
- <?php foreach ( $summary_promise['redis'] as $id => $m ): ?>
110
- <div class="ustats_header">Redis <?php echo $m['name'] ?></div>
111
- Used by <?php echo implode( ',', $m['module_names'] ) ?><br />
112
- Evictions/sec: <span class="ustats_redis_<?php echo $id ?>_evictions_per_second"></span><br />
113
- Expirations/sec: <span class="ustats_redis_<?php echo $id ?>_expirations_per_second"></span><br />
114
- Used: <span class="ustats_redis_<?php echo $id ?>_size_used"></span><br />
115
- Hit rate: <span class="ustats_redis_<?php echo $id ?>_hit_rate"></span><br />
116
- <?php endforeach ?>
117
- <?php endif; ?>
 
 
 
118
  </div>
119
- <a href="#" class="ustats_reload">Refresh</a>
4
  if ( !defined( 'W3TC' ) )
5
  die();
6
  ?>
7
+ <style>
8
+ #w3tc_usage_statistics:hover .edit-box {
9
+ opacity: 1;
10
+ }
11
+
12
+ .w3tcuw_sizes {
13
+ display: flex;
14
+ width: 100%;
15
+ padding-bottom: 10px;
16
+ }
17
+
18
+ .w3tcuw_name {
19
+ font-weight: bold;
20
+ }
21
+
22
+ .w3tcuw_size_item {
23
+ flex: 1;
24
+ text-align: center;
25
+ display: none;
26
+ }
27
+ </style>
28
+ <div>
29
+ Hit rate
30
+ <div style="width: 100%; height: 200px">
31
+ <canvas id="w3tcuw_chart"></canvas>
32
+ </div>
33
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
+ <div class="w3tcuw_sizes">
36
+ <div class="w3tcuw_size_item w3tcuw_memcached_size_percent">
37
+ <div class="w3tcuw_name">Memcached Usage</div>
38
+ <div class="w3tcuw_value"></div>
39
+ </div>
40
+ <div class="w3tcuw_size_item w3tcuw_redis_size_percent">
41
+ <div class="w3tcuw_name">Redis Usage</div>
42
+ <div class="w3tcuw_value"></div>
43
+ </div>
44
+ <div class="w3tcuw_size_item w3tcuw_apc_size_percent">
45
+ <div class="w3tcuw_name">APC Usage</div>
46
+ <div class="w3tcuw_value"></div>
47
+ </div>
48
  </div>
 
UsageStatistics_Widget_View_Disabled.php CHANGED
@@ -4,6 +4,33 @@ namespace W3TC;
4
  if ( !defined( 'W3TC' ) )
5
  die();
6
  ?>
7
- <p class="ustats_p ustats_top">
8
- Not active. Activate <a href="?page=w3tc_general#common__track_usage">here</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </p>
4
  if ( !defined( 'W3TC' ) )
5
  die();
6
  ?>
7
+ <style>
8
+
9
+ .w3tcuw_inactive {
10
+ height: 242px;
11
+ background: url("<?php echo esc_url(plugins_url( 'pub/img/usage-statistics-widget.png', W3TC_FILE ) ) ?>");
12
+ background-repeat: no-repeat;
13
+ background-size: cover;
14
+ }
15
+
16
+ .w3tcuw_inactive input, .w3tcuw_inactive a {
17
+ position: absolute;
18
+ top: 50%;
19
+ left: 50%;
20
+ -ms-transform: translate(-50%, -50%);
21
+ transform: translate(-50%, -50%);
22
+ }
23
+
24
+ .w3tcuw_inactive input, .w3tcuw_inactive span {
25
+ background: #fff;
26
+ }
27
+
28
+ </style>
29
+ <p class="w3tcuw_inactive">
30
+ <?php if ( !Util_Environment::is_w3tc_pro( $c ) ): ?>
31
+ <input type="button"
32
+ class="button-primary button-buy-plugin {nonce: '<?php echo wp_create_nonce( 'w3tc' ) ?>'}" data-src="usagestatistics_widget" value="<?php _e( 'Upgrade to Pro', 'w3-total-cache' ) ?>" />
33
+ <?php else: ?>
34
+ <a href="admin.php?page=w3tc_general#stats" class="button-primary">Enable</a>
35
+ <?php endif ?>
36
  </p>
Util_Content.php CHANGED
@@ -10,7 +10,7 @@ class Util_Content {
10
  */
11
  static public function is_html( $content ) {
12
  $content = Util_Content::_is_html_prepare( $content );
13
- return stripos( $content, '<html' ) === 0 ||
14
  stripos( $content, '<!DOCTYPE' ) === 0;
15
  }
16
 
@@ -22,8 +22,8 @@ class Util_Content {
22
  */
23
  static public function is_html_xml( $content ) {
24
  $content = Util_Content::_is_html_prepare( $content );
25
- return stripos( $content, '<?xml' ) === 0 ||
26
- stripos( $content, '<html' ) === 0 ||
27
  stripos( $content, '<!DOCTYPE' ) === 0;
28
  }
29
 
@@ -52,18 +52,6 @@ class Util_Content {
52
  return Util_Content::is_html_xml( $buffer ) && !defined( 'DOING_AJAX' );
53
  }
54
 
55
-
56
-
57
- /**
58
- * Check if there was database error
59
- *
60
- * @param string $content
61
- * @return boolean
62
- */
63
- static public function is_database_error( &$content ) {
64
- return stristr( $content, '<title>Database Error</title>' ) !== false;
65
- }
66
-
67
  /**
68
  * Returns GMT date
69
  *
10
  */
11
  static public function is_html( $content ) {
12
  $content = Util_Content::_is_html_prepare( $content );
13
+ return stripos( $content, '<html' ) === 0 ||
14
  stripos( $content, '<!DOCTYPE' ) === 0;
15
  }
16
 
22
  */
23
  static public function is_html_xml( $content ) {
24
  $content = Util_Content::_is_html_prepare( $content );
25
+ return stripos( $content, '<?xml' ) === 0 ||
26
+ stripos( $content, '<html' ) === 0 ||
27
  stripos( $content, '<!DOCTYPE' ) === 0;
28
  }
29
 
52
  return Util_Content::is_html_xml( $buffer ) && !defined( 'DOING_AJAX' );
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  /**
56
  * Returns GMT date
57
  *
Util_Environment.php CHANGED
@@ -712,8 +712,9 @@ class Util_Environment {
712
  '%BLOG_ID%',
713
  '%HOST%'
714
  ), array(
715
- ( isset( $GLOBALS['blog_id'] ) ? (int) $GLOBALS['blog_id'] : 0 ),
716
- ( isset( $GLOBALS['post_id'] ) ? (int) $GLOBALS['post_id'] : 0 ),
 
717
  Util_Environment::blog_id(),
718
  Util_Environment::host()
719
  ), $path );
@@ -993,12 +994,14 @@ class Util_Environment {
993
  *
994
  * @return string
995
  */
996
- static public function redirect_temp( $url = '', $params = array() ) {
 
997
  $url = Util_Environment::url_format( $url, $params );
998
- if ( function_exists( 'do_action' ) )
999
  do_action( 'w3tc_redirect' );
 
1000
 
1001
- $status_code = 301;
1002
 
1003
  $protocol = $_SERVER["SERVER_PROTOCOL"];
1004
  if ( 'HTTP/1.1' === $protocol ) {
@@ -1010,11 +1013,19 @@ class Util_Environment {
1010
  $status_header = "$protocol $status_code $text";
1011
  @header( $status_header, true, $status_code );
1012
  }
 
 
 
 
1013
  @header( 'Cache-Control: no-cache' );
1014
- @header( 'Location: ' . $url, true, $status_code );
1015
  exit();
1016
  }
1017
 
 
 
 
 
1018
  /**
1019
  * Detects post ID
1020
  *
712
  '%BLOG_ID%',
713
  '%HOST%'
714
  ), array(
715
+ ( isset( $GLOBALS['blog_id'] ) && is_numeric( $GLOBALS['blog_id'] ) ? (int) $GLOBALS['blog_id'] : 0 ),
716
+ ( isset( $GLOBALS['post_id'] ) && is_numeric( $GLOBALS['post_id'] ) ?
717
+ (int) $GLOBALS['post_id'] : 0 ),
718
  Util_Environment::blog_id(),
719
  Util_Environment::host()
720
  ), $path );
994
  *
995
  * @return string
996
  */
997
+ static public function safe_redirect_temp( $url = '', $params = array(),
998
+ $safe_redirect = false ) {
999
  $url = Util_Environment::url_format( $url, $params );
1000
+ if ( function_exists( 'do_action' ) ) {
1001
  do_action( 'w3tc_redirect' );
1002
+ }
1003
 
1004
+ $status_code = 302;
1005
 
1006
  $protocol = $_SERVER["SERVER_PROTOCOL"];
1007
  if ( 'HTTP/1.1' === $protocol ) {
1013
  $status_header = "$protocol $status_code $text";
1014
  @header( $status_header, true, $status_code );
1015
  }
1016
+
1017
+ add_action( 'wp_safe_redirect_fallback', array(
1018
+ '\W3TC\Util_Environment', 'wp_safe_redirect_fallback' ) );
1019
+
1020
  @header( 'Cache-Control: no-cache' );
1021
+ wp_safe_redirect( $url, $status_code );
1022
  exit();
1023
  }
1024
 
1025
+ static public function wp_safe_redirect_fallback( $url ) {
1026
+ return home_url( '?w3tc_repeat=invalid' );
1027
+ }
1028
+
1029
  /**
1030
  * Detects post ID
1031
  *
Util_Ui.php CHANGED
@@ -352,8 +352,9 @@ class Util_Ui {
352
  * @param bool $disabled
353
  * @param int $size
354
  */
355
- static public function textbox( $id, $name, $value, $disabled = false, $size = 40 ) {
356
- echo '<input class="enabled" type="text" id="' . esc_attr( $id ) .
 
357
  '" name="' . esc_attr( $name ) .
358
  '" value="' . esc_attr( $value ) . '" ';
359
  disabled( $disabled );
@@ -597,7 +598,8 @@ class Util_Ui {
597
  elseif ( $key == 'textbox' )
598
  Util_Ui::textbox( $id, $e['name'], $e['value'],
599
  ( isset( $e['disabled'] ) ? $e['disabled'] : false ),
600
- ( !empty( $e['size'] ) ? $e['size'] : 20 ) );
 
601
  elseif ( $key == 'textarea' )
602
  Util_Ui::textarea( $id, $e['name'], $e['value'],
603
  ( isset( $e['disabled'] ) ? $e['disabled'] : false ) );
@@ -691,6 +693,7 @@ class Util_Ui {
691
  'name' => Util_Ui::config_key_to_http_name( $a['key'] ),
692
  'value' => $a['value'],
693
  'disabled' => $disabled,
 
694
  'size' => isset( $a['textbox_size'] ) ? $a['textbox_size'] : null
695
  );
696
  } else if ( $a['control'] == 'textarea' ) {
352
  * @param bool $disabled
353
  * @param int $size
354
  */
355
+ static public function textbox( $id, $name, $value, $disabled = false, $size = 40, $type = 'text' ) {
356
+ echo '<input class="enabled" type="' . esc_attr( $type ) .
357
+ '" id="' . esc_attr( $id ) .
358
  '" name="' . esc_attr( $name ) .
359
  '" value="' . esc_attr( $value ) . '" ';
360
  disabled( $disabled );
598
  elseif ( $key == 'textbox' )
599
  Util_Ui::textbox( $id, $e['name'], $e['value'],
600
  ( isset( $e['disabled'] ) ? $e['disabled'] : false ),
601
+ ( !empty( $e['size'] ) ? $e['size'] : 20 ),
602
+ ( !empty( $e['type'] ) ? $e['type'] : 'text' ) );
603
  elseif ( $key == 'textarea' )
604
  Util_Ui::textarea( $id, $e['name'], $e['value'],
605
  ( isset( $e['disabled'] ) ? $e['disabled'] : false ) );
693
  'name' => Util_Ui::config_key_to_http_name( $a['key'] ),
694
  'value' => $a['value'],
695
  'disabled' => $disabled,
696
+ 'type' => isset( $a['textbox_type'] ) ? $a['textbox_type'] : null,
697
  'size' => isset( $a['textbox_size'] ) ? $a['textbox_size'] : null
698
  );
699
  } else if ( $a['control'] == 'textarea' ) {
Util_UsageStatistics.php CHANGED
@@ -26,10 +26,13 @@ class Util_UsageStatistics {
26
 
27
 
28
  static public function percent( $v1, $v2 ) {
29
- if ( $v2 == 0 )
30
  return '0 %';
31
- else
 
 
32
  return sprintf( '%d', $v1 / $v2 * 100 ) . ' %';
 
33
  }
34
 
35
 
@@ -48,14 +51,53 @@ class Util_UsageStatistics {
48
  static public function sum( $history, $property ) {
49
  $v = 0;
50
  foreach ( $history as $i ) {
51
- if ( !empty( $i[$property] ) )
52
- $v += $i[$property];
 
 
53
  }
54
  return $v;
55
  }
56
 
57
 
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  static public function time_mins( $timestamp ) {
60
  return date( 'm/d/Y H:i', $timestamp );
61
  }
@@ -68,6 +110,16 @@ class Util_UsageStatistics {
68
 
69
 
70
 
 
 
 
 
 
 
 
 
 
 
71
  static public function integer2( $a, $p1, $p2 = null, $p3 = null ) {
72
  $v = self::v( $a, $p1, $p2, $p3 );
73
  if ( is_null( $v ) )
@@ -99,6 +151,27 @@ class Util_UsageStatistics {
99
 
100
 
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  static public function value_per_second( $a, $property1, $property2 ) {
103
  if ( !isset( $a[$property1] ) || !isset( $a[$property2] ) )
104
  return 'n/a';
26
 
27
 
28
  static public function percent( $v1, $v2 ) {
29
+ if ( $v2 == 0 ) {
30
  return '0 %';
31
+ } elseif ($v1 > $v2 ) {
32
+ return '100 %';
33
+ } else {
34
  return sprintf( '%d', $v1 / $v2 * 100 ) . ' %';
35
+ }
36
  }
37
 
38
 
51
  static public function sum( $history, $property ) {
52
  $v = 0;
53
  foreach ( $history as $i ) {
54
+ $item_value = self::v3( $i, $property );
55
+ if ( !empty( $item_value ) ) {
56
+ $v += $item_value;
57
+ }
58
  }
59
  return $v;
60
  }
61
 
62
 
63
 
64
+ static public function avg( $history, $property ) {
65
+ $v = 0;
66
+ $count = 0;
67
+ foreach ( $history as $i ) {
68
+ $item_value = self::v3( $i, $property );
69
+ if ( !empty( $item_value ) ) {
70
+ $v += $item_value;
71
+ $count++;
72
+ }
73
+ }
74
+ return ( $count <= 0 ? 0 : $v / $count );
75
+ }
76
+
77
+
78
+
79
+ /**
80
+ * Sum up all positive metric values which names start with specified prefix
81
+ **/
82
+ static public function sum_by_prefix_positive( &$output, $history, $property_prefix ) {
83
+ $property_prefix_len = strlen( $property_prefix );
84
+
85
+ foreach ( $history as $i ) {
86
+ foreach ( $i as $key => $value ) {
87
+ if ( substr( $key, 0, $property_prefix_len ) == $property_prefix &&
88
+ $value > 0 ) {
89
+ if ( !isset( $output[$key] ) ) {
90
+ $output[$key] = 0;
91
+ }
92
+
93
+ $output[$key] += $value;
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+
100
+
101
  static public function time_mins( $timestamp ) {
102
  return date( 'm/d/Y H:i', $timestamp );
103
  }
110
 
111
 
112
 
113
+ static public function integer_divideby( $v, $divide_by ) {
114
+ if ( $divide_by == 0 ) {
115
+ return 'n/a';
116
+ }
117
+
118
+ return self::integer( $v / $divide_by );
119
+ }
120
+
121
+
122
+
123
  static public function integer2( $a, $p1, $p2 = null, $p3 = null ) {
124
  $v = self::v( $a, $p1, $p2, $p3 );
125
  if ( is_null( $v ) )
151
 
152
 
153
 
154
+ static public function v3( $a, $p ) {
155
+ if ( !is_array( $p ) ) {
156
+ $p = array( $p );
157
+ }
158
+
159
+ $actual = &$a;
160
+ for ( $i = 0; $i < count( $p ); $i++) {
161
+ $property = $p[$i];
162
+
163
+ if ( !isset( $actual[$property] ) ) {
164
+ return null;
165
+ }
166
+
167
+ $actual = &$actual[$property];
168
+ }
169
+
170
+ return $actual;
171
+ }
172
+
173
+
174
+
175
  static public function value_per_second( $a, $property1, $property2 ) {
176
  if ( !isset( $a[$property1] ) || !isset( $a[$property2] ) )
177
  return 'n/a';
Util_Widget.php CHANGED
@@ -52,7 +52,7 @@ class Util_Widget {
52
  * Registers widget
53
  */
54
  static public function add( $widget_id, $widget_name, $callback,
55
- $control_callback = null, $location = 'normal' ) {
56
  $screen = get_current_screen();
57
  global $w3tc_dashboard_control_callbacks;
58
 
@@ -63,9 +63,13 @@ class Util_Widget {
63
  // it's url
64
  if ( $control_callback && current_user_can( 'edit_dashboard' ) &&
65
  is_string( $control_callback ) ) {
 
 
 
 
66
  $widget_name .= ' <div class="w3tc-widget-configure postbox-title-action">' .
67
  '<a href="' . esc_url( $control_callback ) .
68
- '" class="edit-box open-box">' . __( 'Configure' ) .
69
  '</a></span>';
70
  }
71
 
52
  * Registers widget
53
  */
54
  static public function add( $widget_id, $widget_name, $callback,
55
+ $control_callback = null, $location = 'normal', $header_text = null ) {
56
  $screen = get_current_screen();
57
  global $w3tc_dashboard_control_callbacks;
58
 
63
  // it's url
64
  if ( $control_callback && current_user_can( 'edit_dashboard' ) &&
65
  is_string( $control_callback ) ) {
66
+ if ( !$header_text ) {
67
+ $header_text = __( 'Configure' );
68
+ }
69
+
70
  $widget_name .= ' <div class="w3tc-widget-configure postbox-title-action">' .
71
  '<a href="' . esc_url( $control_callback ) .
72
+ '" class="edit-box open-box">' . $header_text .
73
  '</a></span>';
74
  }
75
 
Util_WpmuBlogmap.php CHANGED
@@ -112,7 +112,7 @@ class Util_WpmuBlogmap {
112
  if ( isset( $blog_ids[$blog_home_url] ) )
113
  return false;
114
  $data = $config->get_boolean( 'common.force_master' ) ? 'm' : 'c';
115
- $blog_home_url = preg_replace( '/[^a-zA-Z0-9\+\.%~!()\/\-\_]/', '', $blog_home_url );
116
  $blog_ids[$blog_home_url] = $data . $GLOBALS['current_blog']->blog_id;
117
 
118
  $data = json_encode( $blog_ids );
112
  if ( isset( $blog_ids[$blog_home_url] ) )
113
  return false;
114
  $data = $config->get_boolean( 'common.force_master' ) ? 'm' : 'c';
115
+ $blog_home_url = preg_replace( '/[^a-zA-Z0-9\+\.%~!:()\/\-\_]/', '', $blog_home_url );
116
  $blog_ids[$blog_home_url] = $data . $GLOBALS['current_blog']->blog_id;
117
 
118
  $data = json_encode( $blog_ids );
inc/lightbox/cdn_s3_bucket_location.php DELETED
@@ -1,17 +0,0 @@
1
- <?php if ( !defined( 'W3TC' ) ) die(); ?>
2
- <h3>Select bucket location:</h3>
3
-
4
- <p>
5
- <label>Location:
6
- <select id="cdn_<?php echo $type; ?>_bucket_location">
7
- <?php foreach ( $locations as $location => $name ): ?>
8
- <option value="<?php echo esc_attr( $location ); ?>"><?php echo $name; ?></option>
9
- <?php endforeach; ?>
10
- </select>
11
- </label>
12
- </p>
13
- <p>
14
- <input class="button" type="button" value="<?php _e( 'Close', 'w3-total-cache' ); ?>" style="float: right" />
15
- <input id="cdn_create_container" class="button-primary {type: '<?php echo $type; ?>', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Create bucket', 'w3-total-cache' ); ?>" />
16
- <span id="cdn_create_container_status" class="w3tc-status w3tc-process"></span>
17
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/lightbox/upgrade.php CHANGED
@@ -4,44 +4,26 @@ namespace W3TC;
4
  if ( !defined( 'W3TC' ) )
5
  die();
6
 
 
7
  ?>
8
  <div id="w3tc-upgrade">
9
- <div class="w3tc-overlay-logo"></div>
10
- <div class="w3tc_overlay_upgrade_header">
11
- <div>
12
- <div class="w3tc_overlay_upgrade_left_h">
13
- W3 Total Cache Pro unlocks more performance options for any website!
14
- </div>
15
- <div class="w3tc_overlay_upgrade_right_h">
16
- only $99 <span class="w3tc_overlay_upgrade_right_text">/year</span>
17
- </div>
18
- </div>
19
- <div class="w3tc_overlay_upgrade_description">
20
- <div class="w3tc_overlay_upgrade_content_l">
21
- <img src="<?php echo plugins_url( 'pub/img/overlay/w3-meteor.png', W3TC_FILE ) ?>"
22
- width="238" height="178" />
23
- </div>
24
- <div class="w3tc_overlay_upgrade_content_r">
25
- <ul>
26
- <li>
27
- <strong>Full Site Delivery (FSD)</strong><br>
28
- Provide the best user experience possible by enhancing by hosting HTML pages and RSS feeds with (supported) <acronym title="Content Delivery Network">CDN</acronym>'s high speed global networks.</li>
29
- <li>
30
- <strong>Extensions Support</strong><br>
31
- Add accelerated mobile pages (<acronym title="Accelerated Mobile Pages">AMP</acronym>) support. Improve the performance of your Genesis, WPML-powered site, and much more.</li>
32
- <li>
33
- <strong><acronym title="Representational State Transfer">REST</acronym> <acronym title="Application Programming Interface">API</acronym> Caching</strong><br>
34
- Save resources or add scale and performance to the WordPress <acronym title="Application Programming Interface">API</acronym> with W3TC Pro.</li>
35
- <li><strong>Fragment Caching</strong><br>
36
- Unlocking the fragment caching module delivers enhanced performance for plugins and themes that use the WordPress Transient <acronym title="Application Programming Interface">API</acronym>. StudioPress' Genesis Framework is up to 60% faster with W3TC Pro.</li>
37
- </ul>
38
- </div>
39
- </div>
40
- <div style="clear: both"></div>
41
- </div>
42
- <div class="w3tc_overlay_content"></div>
43
- <div class="w3tc_overlay_footer">
44
- <input id="w3tc-purchase" type="button" class="btn w3tc-size image btn-default palette-turquoise secure" value="<?php _e( 'Subscribe to Go Faster Now', 'w3-total-cache' ) ?> " />
45
- </div>
46
- <div style="clear: both"></div>
47
  </div>
4
  if ( !defined( 'W3TC' ) )
5
  die();
6
 
7
+
8
  ?>
9
  <div id="w3tc-upgrade">
10
+ <div class="w3tc-overlay-logo"></div>
11
+ <div class="w3tc_overlay_upgrade_header">
12
+ <iframe src="https://www.w3-edge.com/checkout-ad/?data_src=<?php echo esc_attr($data_src) ?>" width="100%" height="420px"></iframe>
13
+ </div>
14
+ <div class="w3tc_overlay_content"></div>
15
+ <div class="w3tc_overlay_footer">
16
+ <?php if ( \W3TC\Util_Environment::is_https() ): ?>
17
+ <input id="w3tc-purchase" type="button"
18
+ class="btn w3tc-size image btn-default palette-turquoise secure"
19
+ value="<?php _e( 'Subscribe to Go Faster Now', 'w3-total-cache' ) ?> " />
20
+ <?php else: ?>
21
+ <a id="w3tc-purchase-link" href="<?php echo \W3TC\Licensing_Core::purchase_url() ?>"
22
+ target="_blank"
23
+ class="btn w3tc-size image btn-default palette-turquoise secure">
24
+ <?php _e( 'Subscribe to Go Faster Now', 'w3-total-cache' ) ?>
25
+ </a>
26
+ <?php endif ?>
27
+ </div>
28
+ <div style="clear: both"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  </div>
inc/options/cdn/cf.php CHANGED
@@ -29,23 +29,15 @@ if ( !defined( 'W3TC' ) )
29
  <td>
30
  <input id="cdn_cf_bucket" type="text" name="cdn__cf__bucket"
31
  <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> value="<?php echo esc_attr( strtolower( $this->_config->get_string( 'cdn.cf.bucket' ) ) ); ?>" size="30" />
32
- <select id="cdn_cf_bucket_location" name="cdn__cf__bucket__location" style="margin-top:-4px">
33
- <option value="" style="display:none;">Select Bucket Location</option>
34
- <option value="us-east-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'us-east-1' ); ?>>US East (N. Virginia)</option>
35
- <option value="us-east-2"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'us-east-2' ); ?>>US East (Ohio)</option>
36
- <option value="us-west-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'us-west-1' ); ?>>US-West (N. California)</option>
37
- <option value="us-west-2"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'us-west-2' ); ?>>US-West (Oregon)</option>
38
- <option value="ca-central-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'ca-central-1' ); ?>>Canada (Montreal)</option>
39
- <option value="ap-south-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'ap-south-1' ); ?>>Asia Pacific (Mumbai)</option>
40
- <option value="ap-northeast-2"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'ap-northeast-2' ); ?>>Asia Pacific (Seoul)</option>
41
- <option value="ap-southeast-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'ap-southeast-1' ); ?>>Asia Pacific (Singapore)</option>
42
- <option value="ap-southeast-2"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'ap-southeast-2' ); ?>>Asia Pacific (Sydney)</option>
43
- <option value="ap-northeast-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'ap-northeast-1' ); ?>>Asia Pacific (Tokyo)</option>
44
- <option value="eu-central-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'eu-central-1' ); ?>>EU (Frankfurt)</option>
45
- <option value="eu-west-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'eu-west-1' ); ?>>EU (Ireland)</option>
46
- <option value="eu-west-2"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'eu-west-2' ); ?>>EU (London)</option>
47
- <option value="sa-east-1"<?php selected( $this->_config->get_string( 'cdn.cf.bucket.location' ), 'sa-east-1' ); ?>>South America (S&atilde;o Paulo)</option>
48
- </select>
49
  <b>or</b>
50
  <input id="cdn_create_container" class="button {type: 'cf', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Create as new bucket with distribution', 'w3-total-cache' ); ?>" /> <span id="cdn_create_container_status" class="w3tc-status w3tc-process"></span>
51
  </td>
29
  <td>
30
  <input id="cdn_cf_bucket" type="text" name="cdn__cf__bucket"
31
  <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> value="<?php echo esc_attr( strtolower( $this->_config->get_string( 'cdn.cf.bucket' ) ) ); ?>" size="30" />
32
+ <?php
33
+
34
+ Util_Ui::selectbox(
35
+ 'cdn_cf_bucket_location',
36
+ 'cdn__cf__bucket__location',
37
+ $this->_config->get_string( 'cdn.cf.bucket.location' ),
38
+ CdnEngine_S3::regions_list() )
39
+
40
+ ?>
 
 
 
 
 
 
 
 
41
  <b>or</b>
42
  <input id="cdn_create_container" class="button {type: 'cf', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Create as new bucket with distribution', 'w3-total-cache' ); ?>" /> <span id="cdn_create_container_status" class="w3tc-status w3tc-process"></span>
43
  </td>
inc/options/cdn/s3.php CHANGED
@@ -6,49 +6,41 @@ if ( !defined( 'W3TC' ) )
6
 
7
  ?>
8
  <tr>
9
- <th colspan="2">
10
- <span class="description"><?php _e( 'We recommend that you use <a href="http://docs.amazonwebservices.com/IAM/latest/UserGuide/AccessPolicyLanguage_KeyConcepts.html" target="_blank"><acronym title="AWS Identity and Access Management">IAM</acronym></a> to create a new policy for <acronym title="Amazon Web Services">AWS</acronym> services that have limited permissions. A helpful tool: <a href="http://awspolicygen.s3.amazonaws.com/policygen.html" target="_blank"><acronym title="Amazon Web Services">AWS</acronym> Policy Generator</a>', 'w3-total-cache' ); ?></span>
11
- </th>
12
  </tr>
13
  <tr>
14
  <th style="width: 300px;"><label for="cdn_s3_key"><?php _e( 'Access key ID:', 'w3-total-cache' ); ?></label></th>
15
  <td>
16
  <input id="cdn_s3_key" class="w3tc-ignore-change" type="text"
17
- <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> name="cdn__s3__key" value="<?php echo esc_attr( $this->_config->get_string( 'cdn.s3.key' ) ); ?>" size="30" />
18
  </td>
19
  </tr>
20
  <tr>
21
  <th><label for="cdn_s3_secret"><?php _e( 'Secret key:', 'w3-total-cache' ); ?></label></th>
22
  <td>
23
  <input id="cdn_s3_secret" class="w3tc-ignore-change"
24
- <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> type="password" name="cdn__s3__secret" value="<?php echo esc_attr( $this->_config->get_string( 'cdn.s3.secret' ) ); ?>" size="60" />
25
  </td>
26
  </tr>
27
  <tr>
28
  <th><label for="cdn_s3_bucket"><?php _e( 'Bucket:', 'w3-total-cache' ); ?></label></th>
29
- <td>
30
- <input id="cdn_s3_bucket" type="text" name="cdn__s3__bucket"
31
- <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> value="<?php echo esc_attr( strtolower( $this->_config->get_string( 'cdn.s3.bucket' ) ) ); ?>" size="30" />
32
- <select id="cdn_s3_bucket_location" name="cdn__s3__bucket__location" style="margin-top:-4px">
33
- <option value="" style="display:none;">Select Bucket Location</option>
34
- <option value="us-east-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'us-east-1' ); ?>>US East (N. Virginia)</option>
35
- <option value="us-east-2"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'us-east-2' ); ?>>US East (Ohio)</option>
36
- <option value="us-west-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'us-west-1' ); ?>>US-West (N. California)</option>
37
- <option value="us-west-2"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'us-west-2' ); ?>>US-West (Oregon)</option>
38
- <option value="ca-central-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'ca-central-1' ); ?>>Canada (Montreal)</option>
39
- <option value="ap-south-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'ap-south-1' ); ?>>Asia Pacific (Mumbai)</option>
40
- <option value="ap-northeast-2"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'ap-northeast-2' ); ?>>Asia Pacific (Seoul)</option>
41
- <option value="ap-southeast-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'ap-southeast-1' ); ?>>Asia Pacific (Singapore)</option>
42
- <option value="ap-southeast-2"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'ap-southeast-2' ); ?>>Asia Pacific (Sydney)</option>
43
- <option value="ap-northeast-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'ap-northeast-1' ); ?>>Asia Pacific (Tokyo)</option>
44
- <option value="eu-central-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'eu-central-1' ); ?>>EU (Frankfurt)</option>
45
- <option value="eu-west-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'eu-west-1' ); ?>>EU (Ireland)</option>
46
- <option value="eu-west-2"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'eu-west-2' ); ?>>EU (London)</option>
47
- <option value="sa-east-1"<?php selected( $this->_config->get_string( 'cdn.s3.bucket.location' ), 'sa-east-1' ); ?>>South America (S&atilde;o Paulo)</option>
48
- </select>
49
- <b>or</b>
50
- <input id="cdn_create_container" class="button {type: 's3', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Create as new bucket', 'w3-total-cache' ); ?>" /> <span id="cdn_create_container_status" class="w3tc-status w3tc-process"></span>
51
- </td>
52
  </tr>
53
  <tr>
54
  <th><label for="cdn_s3_ssl"><?php _e( '<acronym title="Secure Sockets Layer">SSL</acronym> support:', 'w3-total-cache' ); ?></label></th>
@@ -58,23 +50,23 @@ if ( !defined( 'W3TC' ) )
58
  <option value="enabled"<?php selected( $this->_config->get_string( 'cdn.s3.ssl' ), 'enabled' ); ?>><?php _e( 'Enabled (always use SSL)', 'w3-total-cache' ); ?></option>
59
  <option value="disabled"<?php selected( $this->_config->get_string( 'cdn.s3.ssl' ), 'disabled' ); ?>><?php _e( 'Disabled (always use HTTP)', 'w3-total-cache' ); ?></option>
60
  </select>
61
- <br /><span class="description"><?php _e( 'Some <acronym title="Content Delivery Network">CDN</acronym> providers may or may not support <acronym title="Secure Sockets Layer">SSL</acronym>, contact your vendor for more information.', 'w3-total-cache' ); ?></span>
62
  </td>
63
  </tr>
64
  <tr>
65
  <th><?php _e( 'Replace site\'s hostname with:', 'w3-total-cache' ); ?></th>
66
  <td>
67
  <?php if ( ( $cdn_s3_bucket = $this->_config->get_string( 'cdn.s3.bucket' ) ) != '' ): ?>
68
- <?php echo htmlspecialchars( $cdn_s3_bucket ); ?>.s3.amazonaws.com
69
  <?php else: ?>
70
- &lt;bucket&gt;.s3.amazonaws.com
71
  <?php endif; ?> <?php _e( 'or CNAME:', 'w3-total-cache' ); ?>
72
  <?php $cnames = $this->_config->get_array( 'cdn.s3.cname' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?>
73
- <br /><span class="description"><?php _e( 'If you have already added a <a href="http://docs.amazonwebservices.com/AmazonS3/latest/DeveloperGuide/VirtualHosting.html#VirtualHostingCustomURLs" target="_blank">CNAME</a> to your <acronym title="Domain Name System">DNS</acronym> Zone, enter it here.', 'w3-total-cache' ); ?></span>
74
  </td>
75
  </tr>
76
  <tr>
77
  <th colspan="2">
78
- <input id="cdn_test" class="button {type: 's3', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Test S3 upload', 'w3-total-cache' ); ?>" /> <span id="cdn_test_status" class="w3tc-status w3tc-process"></span>
79
- </th>
80
  </tr>
6
 
7
  ?>
8
  <tr>
9
+ <th colspan="2">
10
+ <span class="description"><?php _e( 'We recommend that you use <a href="http://docs.amazonwebservices.com/IAM/latest/UserGuide/AccessPolicyLanguage_KeyConcepts.html" target="_blank"><acronym title="AWS Identity and Access Management">IAM</acronym></a> to create a new policy for <acronym title="Amazon Web Services">AWS</acronym> services that have limited permissions. A helpful tool: <a href="http://awspolicygen.s3.amazonaws.com/policygen.html" target="_blank"><acronym title="Amazon Web Services">AWS</acronym> Policy Generator</a>', 'w3-total-cache' ); ?></span>
11
+ </th>
12
  </tr>
13
  <tr>
14
  <th style="width: 300px;"><label for="cdn_s3_key"><?php _e( 'Access key ID:', 'w3-total-cache' ); ?></label></th>
15
  <td>
16
  <input id="cdn_s3_key" class="w3tc-ignore-change" type="text"
17
+ <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> name="cdn__s3__key" value="<?php echo esc_attr( $this->_config->get_string( 'cdn.s3.key' ) ); ?>" size="30" />
18
  </td>
19
  </tr>
20
  <tr>
21
  <th><label for="cdn_s3_secret"><?php _e( 'Secret key:', 'w3-total-cache' ); ?></label></th>
22
  <td>
23
  <input id="cdn_s3_secret" class="w3tc-ignore-change"
24
+ <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> type="password" name="cdn__s3__secret" value="<?php echo esc_attr( $this->_config->get_string( 'cdn.s3.secret' ) ); ?>" size="60" />
25
  </td>
26
  </tr>
27
  <tr>
28
  <th><label for="cdn_s3_bucket"><?php _e( 'Bucket:', 'w3-total-cache' ); ?></label></th>
29
+ <td>
30
+ <input id="cdn_s3_bucket" type="text" name="cdn__s3__bucket"
31
+ <?php Util_Ui::sealing_disabled( 'cdn.' ) ?> value="<?php echo esc_attr( strtolower( $this->_config->get_string( 'cdn.s3.bucket' ) ) ); ?>" size="30" />
32
+ <?php
33
+
34
+ Util_Ui::selectbox(
35
+ 'cdn_s3_bucket_location',
36
+ 'cdn__s3__bucket__location',
37
+ $this->_config->get_string( 'cdn.s3.bucket.location' ),
38
+ CdnEngine_S3::regions_list() )
39
+
40
+ ?>
41
+ <b>or</b>
42
+ <input id="cdn_create_container" class="button {type: 's3', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Create as new bucket', 'w3-total-cache' ); ?>" /> <span id="cdn_create_container_status" class="w3tc-status w3tc-process"></span>
43
+ </td>
 
 
 
 
 
 
 
 
44
  </tr>
45
  <tr>
46
  <th><label for="cdn_s3_ssl"><?php _e( '<acronym title="Secure Sockets Layer">SSL</acronym> support:', 'w3-total-cache' ); ?></label></th>
50
  <option value="enabled"<?php selected( $this->_config->get_string( 'cdn.s3.ssl' ), 'enabled' ); ?>><?php _e( 'Enabled (always use SSL)', 'w3-total-cache' ); ?></option>
51
  <option value="disabled"<?php selected( $this->_config->get_string( 'cdn.s3.ssl' ), 'disabled' ); ?>><?php _e( 'Disabled (always use HTTP)', 'w3-total-cache' ); ?></option>
52
  </select>
53
+ <br /><span class="description"><?php _e( 'Some <acronym title="Content Delivery Network">CDN</acronym> providers may or may not support <acronym title="Secure Sockets Layer">SSL</acronym>, contact your vendor for more information.', 'w3-total-cache' ); ?></span>
54
  </td>
55
  </tr>
56
  <tr>
57
  <th><?php _e( 'Replace site\'s hostname with:', 'w3-total-cache' ); ?></th>
58
  <td>
59
  <?php if ( ( $cdn_s3_bucket = $this->_config->get_string( 'cdn.s3.bucket' ) ) != '' ): ?>
60
+ <?php echo htmlspecialchars( $cdn_s3_bucket ); ?>.s3.amazonaws.com
61
  <?php else: ?>
62
+ &lt;bucket&gt;.s3.amazonaws.com
63
  <?php endif; ?> <?php _e( 'or CNAME:', 'w3-total-cache' ); ?>
64
  <?php $cnames = $this->_config->get_array( 'cdn.s3.cname' ); include W3TC_INC_DIR . '/options/cdn/common/cnames.php'; ?>
65
+ <br /><span class="description"><?php _e( 'If you have already added a <a href="http://docs.amazonwebservices.com/AmazonS3/latest/DeveloperGuide/VirtualHosting.html#VirtualHostingCustomURLs" target="_blank">CNAME</a> to your <acronym title="Domain Name System">DNS</acronym> Zone, enter it here.', 'w3-total-cache' ); ?></span>
66
  </td>
67
  </tr>
68
  <tr>
69
  <th colspan="2">
70
+ <input id="cdn_test" class="button {type: 's3', nonce: '<?php echo wp_create_nonce( 'w3tc' ); ?>'}" type="button" value="<?php _e( 'Test S3 upload', 'w3-total-cache' ); ?>" /> <span id="cdn_test_status" class="w3tc-status w3tc-process"></span>
71
+ </th>
72
  </tr>
inc/options/edd/buy.php CHANGED
@@ -6,11 +6,12 @@ if ( !defined( 'W3TC' ) )
6
 
7
  ?>
8
  <div>
9
- <?php printf( __( 'Unlock more speed, %s now!', 'w3-total-cache' ),
10
- '<input type="button" class="button-primary button-buy-plugin {nonce: \'' . wp_create_nonce( 'w3tc' ) . '\'}" value="' . __( 'upgrade', 'w3-total-cache' ) . '" />' ) ?>
11
- <div id="w3tc-license-instruction" style="display: none;">
12
- <span class="description"><?php printf( __( 'Please enter the license key you received after successful checkout %s.', 'w3-total-cache' ),
 
13
  '<a href="' . network_admin_url( 'admin.php?page=w3tc_general#licensing' ) .'">' . __( 'here', 'w3-total-cache' ) . '</a>' )
14
  ?></span>
15
- </div>
16
  </div>
6
 
7
  ?>
8
  <div>
9
+ <?php printf( __( 'Unlock more speed, %s now!', 'w3-total-cache' ),
10
+ '<input type="button" class="button-primary button-buy-plugin" data-src="' . esc_attr( 'page_' . $page ) .
11
+ '" value="' . __( 'upgrade', 'w3-total-cache' ) . '" />' ) ?>
12
+ <div id="w3tc-license-instruction" style="display: none;">
13
+ <span class="description"><?php printf( __( 'Please enter the license key you received after successful checkout %s.', 'w3-total-cache' ),
14
  '<a href="' . network_admin_url( 'admin.php?page=w3tc_general#licensing' ) .'">' . __( 'here', 'w3-total-cache' ) . '</a>' )
15
  ?></span>
16
+ </div>
17
  </div>
inc/options/general.php CHANGED
@@ -400,7 +400,7 @@ foreach ( $custom_areas as $area )
400
  <input id="plugin_license_key_verify" type="button" class="button" value="<?php _e( 'Verify license key', 'w3-total-cache' ) ?>"/>
401
  <span class="w3tc_license_verification"></span>
402
  <br />
403
- <span class="description"><?php printf( __( 'Please enter the license key provided after %s.', 'w3-total-cache' ), '<a class="button-buy-plugin" href="#">' . __( 'upgrading', 'w3-total-cache' ) . '</a>' )?></span>
404
  </td>
405
  </tr>
406
 
@@ -489,8 +489,6 @@ Util_Ui::config_item( array(
489
  'style' => '2'
490
  ) );
491
  ?>
492
-
493
- <?php do_action( 'w3tc_settings_general_boxarea_miscellaneous_content' ); ?>
494
  </table>
495
 
496
  <?php Util_Ui::button_config_save( 'general_misc' ); ?>
400
  <input id="plugin_license_key_verify" type="button" class="button" value="<?php _e( 'Verify license key', 'w3-total-cache' ) ?>"/>
401
  <span class="w3tc_license_verification"></span>
402
  <br />
403
+ <span class="description"><?php printf( __( 'Please enter the license key provided after %s.', 'w3-total-cache' ), '<a class="button-buy-plugin" data-src="generic_license" href="#">' . __( 'upgrading', 'w3-total-cache' ) . '</a>' )?></span>
404
  </td>
405
  </tr>
406
 
489
  'style' => '2'
490
  ) );
491
  ?>
 
 
492
  </table>
493
 
494
  <?php Util_Ui::button_config_save( 'general_misc' ); ?>
inc/options/minify/css.php CHANGED
@@ -12,6 +12,6 @@ $is_pro = Util_Environment::is_w3tc_pro( $this->_config );
12
  <?php $this->checkbox( 'minify.css.embed', !$is_pro, 'csse_', true, ( $is_pro ? null : false ) ) ?> Eliminate render-blocking <acronym title="Cascading Style Sheet">CSS</acronym> by moving it to <acronym title="Hypertext Markup Language">HTTP</acronym> body</label>
13
  <?php
14
  if ( !$is_pro )
15
- echo ' (Available after <a href="#" class="button-buy-plugin">upgrade</a>)';
16
  ?>
17
  <br />
12
  <?php $this->checkbox( 'minify.css.embed', !$is_pro, 'csse_', true, ( $is_pro ? null : false ) ) ?> Eliminate render-blocking <acronym title="Cascading Style Sheet">CSS</acronym> by moving it to <acronym title="Hypertext Markup Language">HTTP</acronym> body</label>
13
  <?php
14
  if ( !$is_pro )
15
+ echo ' (Available after <a href="#" class="button-buy-plugin" data-src="minify_css_renderblocking">upgrade</a>)';
16
  ?>
17
  <br />
inc/options/pgcache.php CHANGED
@@ -261,7 +261,7 @@ Util_Ui::postbox_header( __( 'Purge Policy: ', 'w3-total-cache' ) . implode( ',
261
  'label' => "Cache",
262
  'disabled' => !Util_Environment::is_w3tc_pro( $this->_config ),
263
  'postfix' => ( Util_Environment::is_w3tc_pro( $this->_config ) ? '' :
264
- '&nbsp;&nbsp;&nbsp;(<a href="#" class="button-buy-plugin">Upgrade</a> now to enable)')
265
  ),
266
  'disable' => 'Disable <acronym title="REpresentational State Transfer">REST</acronym> <acronym title="Application Programming Interface">API</acronym>',
267
  ),
@@ -401,42 +401,42 @@ echo sprintf(
401
  </span>
402
  </td>
403
  </tr>
404
- <tr>
405
- <th><label for="pgcache_reject_categories"><?php Util_Ui::e_config_label( 'pgcache.reject.categories' ) ?></label></th>
406
- <td>
407
- <textarea id="pgcache_reject_categories" name="pgcache__reject__categories"
408
- <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
409
- cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array('pgcache.reject.categories' ) ) ); ?></textarea><br />
410
- <span class="description"><?php _e( 'Always ignore all pages filed under the specified category slugs.', 'w3-total-cache' ); ?></span>
411
- </td>
412
- </tr>
413
- <tr>
414
- <th><label for="pgcache_reject_tags"><?php Util_Ui::e_config_label( 'pgcache.reject.tags' ) ?></label></th>
415
- <td>
416
- <textarea id="pgcache_reject_tags" name="pgcache__reject__tags"
417
- <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
418
- cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array( 'pgcache.reject.tags' ) ) ); ?></textarea><br />
419
- <span class="description"><?php _e( 'Always ignore all pages filed under the specified tag slugs.', 'w3-total-cache' ); ?></span>
420
- </td>
421
- </tr>
422
- <tr>
423
- <th><label for="pgcache_reject_authors"><?php Util_Ui::e_config_label( 'pgcache.reject.authors' ) ?></label></th>
424
- <td>
425
- <textarea id="pgcache_reject_authors" name="pgcache__reject__authors"
426
- <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
427
- cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array( 'pgcache.reject.authors' ) ) ); ?></textarea><br />
428
- <span class="description"><?php _e( 'Always ignore all pages filed under the specified author usernames.', 'w3-total-cache' ); ?></span>
429
- </td>
430
- </tr>
431
- <tr>
432
- <th><label for="pgcache_reject_custom"><?php Util_Ui::e_config_label( 'pgcache.reject.custom' ) ?></label></th>
433
- <td>
434
- <textarea id="pgcache_reject_custom" name="pgcache__reject__custom"
435
- <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
436
- cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array('pgcache.reject.custom' ) ) ); ?></textarea><br />
437
- <span class="description"><?php _e( 'Always ignore all pages filed under the specified custom fields. Separate name-value pairs with an equals sign (i.e., name=value).', 'w3-total-cache' ); ?></span>
438
- </td>
439
- </tr>
440
  <tr>
441
  <th><label for="pgcache_accept_files"><?php Util_Ui::e_config_label( 'pgcache.accept.files' ) ?></label></th>
442
  <td>
261
  'label' => "Cache",
262
  'disabled' => !Util_Environment::is_w3tc_pro( $this->_config ),
263
  'postfix' => ( Util_Environment::is_w3tc_pro( $this->_config ) ? '' :
264
+ '&nbsp;&nbsp;&nbsp;(<a href="#" class="button-buy-plugin" data-src="pagecache_rest">Upgrade</a> now to enable)')
265
  ),
266
  'disable' => 'Disable <acronym title="REpresentational State Transfer">REST</acronym> <acronym title="Application Programming Interface">API</acronym>',
267
  ),
401
  </span>
402
  </td>
403
  </tr>
404
+ <tr>
405
+ <th><label for="pgcache_reject_categories"><?php Util_Ui::e_config_label( 'pgcache.reject.categories' ) ?></label></th>
406
+ <td>
407
+ <textarea id="pgcache_reject_categories" name="pgcache__reject__categories"
408
+ <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
409
+ cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array('pgcache.reject.categories' ) ) ); ?></textarea><br />
410
+ <span class="description"><?php _e( 'Always ignore all pages filed under the specified category slugs.', 'w3-total-cache' ); ?></span>
411
+ </td>
412
+ </tr>
413
+ <tr>
414
+ <th><label for="pgcache_reject_tags"><?php Util_Ui::e_config_label( 'pgcache.reject.tags' ) ?></label></th>
415
+ <td>
416
+ <textarea id="pgcache_reject_tags" name="pgcache__reject__tags"
417
+ <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
418
+ cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array( 'pgcache.reject.tags' ) ) ); ?></textarea><br />
419
+ <span class="description"><?php _e( 'Always ignore all pages filed under the specified tag slugs.', 'w3-total-cache' ); ?></span>
420
+ </td>
421
+ </tr>
422
+ <tr>
423
+ <th><label for="pgcache_reject_authors"><?php Util_Ui::e_config_label( 'pgcache.reject.authors' ) ?></label></th>
424
+ <td>
425
+ <textarea id="pgcache_reject_authors" name="pgcache__reject__authors"
426
+ <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
427
+ cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array( 'pgcache.reject.authors' ) ) ); ?></textarea><br />
428
+ <span class="description"><?php _e( 'Always ignore all pages filed under the specified author usernames.', 'w3-total-cache' ); ?></span>
429
+ </td>
430
+ </tr>
431
+ <tr>
432
+ <th><label for="pgcache_reject_custom"><?php Util_Ui::e_config_label( 'pgcache.reject.custom' ) ?></label></th>
433
+ <td>
434
+ <textarea id="pgcache_reject_custom" name="pgcache__reject__custom"
435
+ <?php Util_Ui::sealing_disabled( 'pgcache' ) ?>
436
+ cols="40" rows="5"><?php echo esc_textarea( implode( "\r\n", $this->_config->get_array('pgcache.reject.custom' ) ) ); ?></textarea><br />
437
+ <span class="description"><?php _e( 'Always ignore all pages filed under the specified custom fields. Separate name-value pairs with an equals sign (i.e., name=value).', 'w3-total-cache' ); ?></span>
438
+ </td>
439
+ </tr>
440
  <tr>
441
  <th><label for="pgcache_accept_files"><?php Util_Ui::e_config_label( 'pgcache.accept.files' ) ?></label></th>
442
  <td>
pub/css/lightbox.css CHANGED
@@ -271,9 +271,10 @@ fieldset[disabled] .btn {
271
  .w3tc-overlay .btn.secure:active,
272
  .w3tc-overlay .btn.secure.active,
273
  .w3tc-overlay .btn.secure {
274
- background-position: 10px 5px; /* equivalent to 'top left' */
275
- padding:5px 10px 5px 37px;
276
- background-image: url(../img/overlay/save-close.png);
 
277
  }
278
 
279
  .w3tc-overlay btn.palette-turquoise {
271
  .w3tc-overlay .btn.secure:active,
272
  .w3tc-overlay .btn.secure.active,
273
  .w3tc-overlay .btn.secure {
274
+ background-position: 10px 5px; /* equivalent to 'top left' */
275
+ padding:5px 10px 5px 37px;
276
+ background-image: url(../img/overlay/save-close.png);
277
+ text-decoration: none;
278
  }
279
 
280
  .w3tc-overlay btn.palette-turquoise {
pub/fonts/source-a.svg CHANGED
@@ -1,3 +1,3 @@
1
  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16" width="16" height="16"><defs><path d="M9.57 6.44C9.97 6.65 10.44 6.65 10.84 6.45C11.53 6.11 12.92 5.42 13.6 5.09C13.89 4.95 13.89 4.55 13.61 4.4C12.3 3.71 9.05 2 7.74 1.31C7.29 1.07 6.75 1.05 6.27 1.24C5.51 1.55 4.03 2.15 3.29 2.45C2.96 2.58 2.94 3.04 3.25 3.2C4.64 3.92 8.15 5.71 9.57 6.44Z" id="a1ibI1mTkr"></path><path d="M8.2 14.33C7.88 14.51 7.51 14.19 7.64 13.85C8.15 12.51 9.44 9.06 9.99 7.61C10.1 7.32 10.31 7.08 10.59 6.93C11.35 6.55 13.07 5.66 13.8 5.29C14.01 5.18 14.24 5.38 14.16 5.6C13.73 6.91 12.63 10.23 12.2 11.54C12.07 11.94 11.8 12.27 11.43 12.48C10.6 12.96 8.96 13.89 8.2 14.33Z" id="a3oZBniNh8"></path><path d="M12.14 15.15C12.78 15.22 13.43 15.03 13.94 14.62C14.47 14.21 15.28 13.57 15.81 13.15C16.07 12.94 15.94 12.51 15.6 12.49C14.86 12.44 13.2 12.34 12.36 12.29C12.06 12.28 11.77 12.34 11.52 12.49C10.75 12.93 8.94 13.96 8.2 14.39C8.03 14.48 8.09 14.74 8.28 14.76C9.26 14.86 11.24 15.06 12.14 15.15Z" id="aeIjeZCLN"></path><path d="M3.13 3.44L9.42 6.66L9.57 6.75L9.68 6.88L9.76 7.03L9.8 7.19L9.8 7.36L9.76 7.54L7.41 13.77L7.32 13.92L7.2 14.04L7.05 14.12L6.88 14.16L6.71 14.14L6.54 14.07L0.82 10.71L0.57 10.53L0.38 10.29L0.25 10.02L0.18 9.73L0.18 9.43L0.25 9.13L2.22 3.8L2.31 3.64L2.44 3.51L2.6 3.42L2.77 3.38L2.95 3.38L3.13 3.44ZM2.93 9.13L3.84 8.08L3.84 9.68L4.24 9.86L5.41 8.66L6.56 9.26L5.97 9.61L6.09 9.79L6.16 9.95L6.19 10.07L6.18 10.17L6.13 10.23L6.02 10.28L5.92 10.26L5.8 10.18L5.66 10.04L5.48 9.86L5.32 9.85L5.19 9.85L5.07 9.85L4.96 9.85L4.88 9.86L5.28 10.4L5.65 10.78L6 11.02L6.32 11.1L6.63 11.02L6.94 10.77L7.06 10.49L7.04 10.22L6.93 9.99L6.79 9.86L6.87 9.78L7 9.69L7.15 9.57L7.35 9.42L7.57 9.26L5.35 7.93L4.4 8.9L4.31 7.4L3.91 7.19L3.08 8.16L3.08 6.64L2.46 6.32L2.46 8.83L2.93 9.13Z" id="b1H2p6I96S"></path></defs><g><g><g><use xlink:href="#a1ibI1mTkr" opacity="0.8" fill="black" fill-opacity="1"></use></g><g><use xlink:href="#a3oZBniNh8" opacity="0.7" fill="black" fill-opacity="1"></use></g><g><use xlink:href="#aeIjeZCLN" opacity="0.1" fill="black" fill-opacity="1"></use></g><g><use xlink:href="#b1H2p6I96S" opacity="1" fill="black" fill-opacity="1"></use></g></g></g></svg>
1
  <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16" width="16" height="16"><defs><path d="M10.39 6.69C10.79 6.9 11.26 6.9 11.67 6.7C12.35 6.36 13.75 5.68 14.43 5.34C14.71 5.2 14.71 4.8 14.43 4.65C13.12 3.96 9.87 2.25 8.56 1.57C8.11 1.33 7.57 1.3 7.09 1.49C6.33 1.8 4.85 2.4 4.11 2.7C3.78 2.84 3.76 3.29 4.07 3.46C5.46 4.17 8.97 5.96 10.39 6.69Z" id="bEaEbzNdf"></path><path d="M9.02 14.58C8.7 14.76 8.33 14.45 8.46 14.11C8.97 12.77 10.26 9.32 10.81 7.87C10.92 7.57 11.13 7.33 11.41 7.19C12.17 6.8 13.89 5.92 14.62 5.54C14.83 5.44 15.06 5.64 14.99 5.86C14.55 7.17 13.45 10.49 13.02 11.79C12.89 12.19 12.62 12.53 12.25 12.73C11.42 13.21 9.78 14.15 9.02 14.58Z" id="aSR8reqKk"></path><path d="M3.95 3.7L10.24 6.91L10.39 7.01L10.5 7.13L10.58 7.28L10.62 7.45L10.62 7.62L10.58 7.79L8.23 14.02L8.14 14.18L8.02 14.3L7.87 14.37L7.7 14.41L7.53 14.39L7.36 14.33L1.64 10.97L1.39 10.78L1.2 10.55L1.07 10.28L1 9.99L1 9.68L1.07 9.38L3.04 4.06L3.13 3.89L3.26 3.76L3.42 3.67L3.59 3.63L3.77 3.64L3.95 3.7ZM3.76 9.39L4.66 8.34L4.66 9.93L5.06 10.11L6.23 8.91L7.38 9.51L6.79 9.86L6.91 10.05L6.98 10.2L7.02 10.33L7.01 10.42L6.95 10.49L6.84 10.53L6.74 10.51L6.62 10.43L6.48 10.29L6.3 10.11L6.15 10.11L6.01 10.1L5.89 10.1L5.79 10.11L5.7 10.11L6.1 10.65L6.47 11.04L6.82 11.27L7.15 11.35L7.45 11.28L7.76 11.03L7.88 10.74L7.86 10.47L7.75 10.24L7.61 10.11L7.7 10.04L7.82 9.94L7.97 9.82L8.17 9.68L8.39 9.51L6.18 8.19L5.22 9.16L5.13 7.66L4.73 7.44L3.9 8.42L3.9 6.9L3.28 6.58L3.28 9.09L3.76 9.39Z" id="b5dPBHXJfL"></path></defs><g><g><g><use xlink:href="#bEaEbzNdf" opacity="1" fill="#000000" fill-opacity="1"></use></g><g><use xlink:href="#aSR8reqKk" opacity="1" fill="#000000" fill-opacity="1"></use></g><g><use xlink:href="#b5dPBHXJfL" opacity="1" fill="#000000" fill-opacity="1"></use></g></g></g></svg>
pub/fonts/w3tc.eot CHANGED
Binary file
pub/fonts/w3tc.svg CHANGED
@@ -1 +1,11 @@
1
- <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" ><svg xmlns="http://www.w3.org/2000/svg"><metadata>Generated by Glyphter</metadata><defs><font id="w3tc" horiz-adv-x="0"><font-face units-per-em="1024" ascent="1024" descent="0" font-family="w3tc" font-weight="normal" /><missing-glyph horiz-adv-x="0" /><glyph unicode="&#x0041;" d="M610.318,622.581C636.296,608.943,666.819,608.943,692.796,621.932C737.607,644.013,827.879,688.823,872.04,710.255C890.874,719.347,890.874,745.324,872.69,755.066C787.614,799.877,576.548,910.93,491.472,955.741C462.247,971.327,427.178,972.626,396.005,960.287C346.648,940.154,250.532,901.188,202.474,881.705C181.043,873.263,179.744,843.389,199.876,832.998C290.147,786.239,518.099,669.99,610.318,622.581C610.318,622.581,610.318,622.581,610.318,622.581M521.346,110.178C500.564,98.488,476.535,119.27,484.978,141.351C518.099,228.375,601.876,452.43,637.595,546.597C644.738,565.431,658.376,581.017,676.561,590.759C725.918,615.437,837.62,673.237,885.029,697.266C898.667,704.41,913.604,691.421,908.408,677.134C880.483,592.058,809.045,376.446,781.119,291.37C772.677,265.393,755.142,243.961,731.113,230.323C677.21,199.15,570.703,138.753,521.346,110.178C521.346,110.178,521.346,110.178,521.346,110.178M777.223,56.924C818.787,52.378,861,64.718,894.121,91.344C928.541,117.971,981.145,159.535,1015.565,186.811C1032.45,200.449,1024.008,228.375,1001.927,229.674C953.869,232.921,846.063,239.415,791.51,242.662C772.027,243.312,753.194,239.415,736.958,229.674C686.952,201.099,569.404,134.207,521.346,106.281C510.306,100.436,514.202,83.551,526.541,82.252C590.186,75.758,718.774,62.769,777.223,56.924C777.223,56.924,777.223,56.924,777.223,56.924M192.083,817.411C192.083,817.411,600.577,608.294,600.577,608.294C600.577,608.294,610.318,602.449,610.318,602.449C610.318,602.449,617.462,594.006,617.462,594.006C617.462,594.006,622.658,584.265,622.658,584.265C622.658,584.265,625.255,573.874,625.255,573.874C625.255,573.874,625.255,562.833,625.255,562.833C625.255,562.833,622.658,551.144,622.658,551.144C622.658,551.144,470.041,146.546,470.041,146.546C470.041,146.546,464.196,136.805,464.196,136.805C464.196,136.805,456.403,129.012,456.403,129.012C456.403,129.012,446.661,123.816,446.661,123.816C446.661,123.816,435.621,121.218,435.621,121.218C435.621,121.218,424.58,122.517,424.58,122.517C424.58,122.517,413.54,127.063,413.54,127.063C413.54,127.063,42.064,345.273,42.064,345.273C42.064,345.273,25.828,356.963,25.828,356.963C25.828,356.963,13.489,372.549,13.489,372.549C13.489,372.549,5.046,390.084,5.046,390.084C5.046,390.084,0.5,408.918,0.5,408.918C0.5,408.918,0.5,428.401,0.5,428.401C0.5,428.401,5.046,447.884,5.046,447.884C5.046,447.884,132.984,794.032,132.984,794.032C132.984,794.032,138.829,804.423,138.829,804.423C138.829,804.423,147.272,812.865,147.272,812.865C147.272,812.865,157.663,818.71,157.663,818.71C157.663,818.71,168.703,821.308,168.703,821.308C168.703,821.308,180.393,821.308,180.393,821.308C180.393,821.308,192.083,817.411,192.083,817.411C192.083,817.411,192.083,817.411,192.083,817.411M179.094,447.884C179.094,447.884,238.193,516.074,238.193,516.074C238.193,516.074,238.193,412.165,238.193,412.165C238.193,412.165,264.17,400.475,264.17,400.475C264.17,400.475,340.154,478.407,340.154,478.407C340.154,478.407,414.839,439.441,414.839,439.441C414.839,439.441,376.522,416.711,376.522,416.711C376.522,416.711,384.315,405.021,384.315,405.021C384.315,405.021,388.861,394.63,388.861,394.63C388.861,394.63,390.81,386.837,390.81,386.837C390.81,386.837,390.16,380.342,390.16,380.342C390.16,380.342,386.913,376.446,386.913,376.446C386.913,376.446,379.769,373.199,379.769,373.199C379.769,373.199,373.275,374.498,373.275,374.498C373.275,374.498,365.482,379.693,365.482,379.693C365.482,379.693,356.39,388.785,356.39,388.785C356.39,388.785,344.7,400.475,344.7,400.475C344.7,400.475,334.309,401.124,334.309,401.124C334.309,401.124,325.866,401.124,325.866,401.124C325.866,401.124,318.073,401.124,318.073,401.124C318.073,401.124,310.929,401.124,310.929,401.124C310.929,401.124,305.734,400.475,305.734,400.475C305.734,400.475,331.711,365.405,331.711,365.405C331.711,365.405,355.74,340.727,355.74,340.727C355.74,340.727,378.47,325.141,378.47,325.141C378.47,325.141,399.252,319.945,399.252,319.945C399.252,319.945,419.385,325.141,419.385,325.141C419.385,325.141,439.517,341.376,439.517,341.376C439.517,341.376,447.31,359.561,447.31,359.561C447.31,359.561,446.012,377.095,446.012,377.095C446.012,377.095,438.868,392.032,438.868,392.032C438.868,392.032,429.776,400.475,429.776,400.475C429.776,400.475,434.971,405.67,434.971,405.67C434.971,405.67,443.414,411.515,443.414,411.515C443.414,411.515,453.155,419.308,453.155,419.308C453.155,419.308,466.144,429.05,466.144,429.05C466.144,429.05,480.432,439.441,480.432,439.441C480.432,439.441,336.257,525.816,336.257,525.816C336.257,525.816,274.561,462.821,274.561,462.821C274.561,462.821,268.716,560.236,268.716,560.236C268.716,560.236,242.739,573.874,242.739,573.874C242.739,573.874,188.836,510.879,188.836,510.879C188.836,510.879,188.836,609.593,188.836,609.593C188.836,609.593,148.571,630.374,148.571,630.374C148.571,630.374,148.571,467.367,148.571,467.367C148.571,467.367,179.094,447.884,179.094,447.884C179.094,447.884,179.094,447.884,179.094,447.884" class="icon-menuicon"/></font></defs></svg>
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
3
+ <svg xmlns="http://www.w3.org/2000/svg">
4
+ <metadata>Generated by IcoMoon</metadata>
5
+ <defs>
6
+ <font id="w3tc" horiz-adv-x="1024">
7
+ <font-face units-per-em="1024" ascent="960" descent="-64" />
8
+ <missing-glyph horiz-adv-x="1024" />
9
+ <glyph unicode="&#x20;" horiz-adv-x="512" d="" />
10
+ <glyph unicode="&#x41;" glyph-name="menu-icon" d="M664.96 531.84c25.6-13.44 55.68-13.44 81.92-0.64 43.52 21.76 133.12 65.28 176.64 87.040 17.92 8.96 17.92 34.56 0 44.16-83.84 44.16-291.84 153.6-375.68 197.12-28.8 15.36-63.36 17.28-94.080 5.12-48.64-19.84-143.36-58.24-190.72-77.44-21.12-8.96-22.4-37.76-2.56-48.64 88.96-45.44 313.6-160 404.48-206.72zM577.28 26.88c-20.48-11.52-44.16 8.32-35.84 30.080 32.64 85.76 115.2 306.56 150.4 399.36 7.040 19.2 20.48 34.56 38.4 43.52 48.64 24.96 158.72 81.28 205.44 105.6 13.44 6.4 28.16-6.4 23.68-20.48-28.16-83.84-98.56-296.32-126.080-379.52-8.32-25.6-25.6-47.36-49.28-60.16-53.12-30.72-158.080-90.88-206.72-118.4zM252.8 723.2l402.56-205.44 9.6-6.4 7.040-7.68 5.12-9.6 2.56-10.88v-10.88l-2.56-10.88-150.4-398.72-5.76-10.24-7.68-7.68-9.6-4.48-10.88-2.56-10.88 1.28-10.88 3.84-366.080 215.040-16 12.16-12.16 14.72-8.32 17.28-4.48 18.56v19.84l4.48 19.2 126.080 340.48 5.76 10.88 8.32 8.32 10.24 5.76 10.88 2.56 11.52-0.64 11.52-3.84zM240.64 359.040l57.6 67.2v-101.76l25.6-11.52 74.88 76.8 73.6-38.4-37.76-22.4 7.68-12.16 4.48-9.6 2.56-8.32-0.64-5.76-3.84-4.48-7.040-2.56-6.4 1.28-7.68 5.12-20.48 20.48h-9.6l-8.96 0.64h-7.68l-6.4-0.64h-5.76l25.6-34.56 23.68-24.96 22.4-14.72 21.12-5.12 19.2 4.48 19.84 16 7.68 18.56-1.28 17.28-7.040 14.72-8.96 8.32 5.76 4.48 7.68 6.4 9.6 7.68 12.8 8.96 14.080 10.88-141.44 84.48-61.44-62.080-5.76 96-25.6 14.080-53.12-62.72v97.28l-39.68 20.48v-160.64l30.72-19.2z" />
11
+ </font></defs></svg>
pub/fonts/w3tc.ttf CHANGED
Binary file
pub/fonts/w3tc.woff CHANGED
Binary file
pub/img/overlay/w3-meteor.png DELETED
Binary file
pub/img/stats-bg.png ADDED
Binary file
pub/img/usage-statistics-widget.png ADDED
Binary file
pub/js/chartjs.min.js ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ /*!
2
+ * Chart.js v2.8.0
3
+ * https://www.chartjs.org
4
+ * (c) 2019 Chart.js Contributors
5
+ * Released under the MIT License
6
+ */
7
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(function(){try{return require("moment")}catch(t){}}()):"function"==typeof define&&define.amd?define(["require"],function(t){return e(function(){try{return t("moment")}catch(t){}}())}):t.Chart=e(t.moment)}(this,function(t){"use strict";t=t&&t.hasOwnProperty("default")?t.default:t;var e={rgb2hsl:i,rgb2hsv:n,rgb2hwb:a,rgb2cmyk:o,rgb2keyword:s,rgb2xyz:l,rgb2lab:d,rgb2lch:function(t){return x(d(t))},hsl2rgb:u,hsl2hsv:function(t){var e=t[0],i=t[1]/100,n=t[2]/100;if(0===n)return[0,0,0];return[e,100*(2*(i*=(n*=2)<=1?n:2-n)/(n+i)),100*((n+i)/2)]},hsl2hwb:function(t){return a(u(t))},hsl2cmyk:function(t){return o(u(t))},hsl2keyword:function(t){return s(u(t))},hsv2rgb:h,hsv2hsl:function(t){var e,i,n=t[0],a=t[1]/100,o=t[2]/100;return e=a*o,[n,100*(e=(e/=(i=(2-a)*o)<=1?i:2-i)||0),100*(i/=2)]},hsv2hwb:function(t){return a(h(t))},hsv2cmyk:function(t){return o(h(t))},hsv2keyword:function(t){return s(h(t))},hwb2rgb:c,hwb2hsl:function(t){return i(c(t))},hwb2hsv:function(t){return n(c(t))},hwb2cmyk:function(t){return o(c(t))},hwb2keyword:function(t){return s(c(t))},cmyk2rgb:f,cmyk2hsl:function(t){return i(f(t))},cmyk2hsv:function(t){return n(f(t))},cmyk2hwb:function(t){return a(f(t))},cmyk2keyword:function(t){return s(f(t))},keyword2rgb:w,keyword2hsl:function(t){return i(w(t))},keyword2hsv:function(t){return n(w(t))},keyword2hwb:function(t){return a(w(t))},keyword2cmyk:function(t){return o(w(t))},keyword2lab:function(t){return d(w(t))},keyword2xyz:function(t){return l(w(t))},xyz2rgb:p,xyz2lab:m,xyz2lch:function(t){return x(m(t))},lab2xyz:v,lab2rgb:y,lab2lch:x,lch2lab:k,lch2xyz:function(t){return v(k(t))},lch2rgb:function(t){return y(k(t))}};function i(t){var e,i,n=t[0]/255,a=t[1]/255,o=t[2]/255,r=Math.min(n,a,o),s=Math.max(n,a,o),l=s-r;return s==r?e=0:n==s?e=(a-o)/l:a==s?e=2+(o-n)/l:o==s&&(e=4+(n-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),i=(r+s)/2,[e,100*(s==r?0:i<=.5?l/(s+r):l/(2-s-r)),100*i]}function n(t){var e,i,n=t[0],a=t[1],o=t[2],r=Math.min(n,a,o),s=Math.max(n,a,o),l=s-r;return i=0==s?0:l/s*1e3/10,s==r?e=0:n==s?e=(a-o)/l:a==s?e=2+(o-n)/l:o==s&&(e=4+(n-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),[e,i,s/255*1e3/10]}function a(t){var e=t[0],n=t[1],a=t[2];return[i(t)[0],100*(1/255*Math.min(e,Math.min(n,a))),100*(a=1-1/255*Math.max(e,Math.max(n,a)))]}function o(t){var e,i=t[0]/255,n=t[1]/255,a=t[2]/255;return[100*((1-i-(e=Math.min(1-i,1-n,1-a)))/(1-e)||0),100*((1-n-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]}function s(t){return _[JSON.stringify(t)]}function l(t){var e=t[0]/255,i=t[1]/255,n=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)+.1805*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)),100*(.2126*e+.7152*i+.0722*n),100*(.0193*e+.1192*i+.9505*n)]}function d(t){var e=l(t),i=e[0],n=e[1],a=e[2];return n/=100,a/=108.883,i=(i/=95.047)>.008856?Math.pow(i,1/3):7.787*i+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(i-n),200*(n-(a=a>.008856?Math.pow(a,1/3):7.787*a+16/116))]}function u(t){var e,i,n,a,o,r=t[0]/360,s=t[1]/100,l=t[2]/100;if(0==s)return[o=255*l,o,o];e=2*l-(i=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var d=0;d<3;d++)(n=r+1/3*-(d-1))<0&&n++,n>1&&n--,o=6*n<1?e+6*(i-e)*n:2*n<1?i:3*n<2?e+(i-e)*(2/3-n)*6:e,a[d]=255*o;return a}function h(t){var e=t[0]/60,i=t[1]/100,n=t[2]/100,a=Math.floor(e)%6,o=e-Math.floor(e),r=255*n*(1-i),s=255*n*(1-i*o),l=255*n*(1-i*(1-o));n*=255;switch(a){case 0:return[n,l,r];case 1:return[s,n,r];case 2:return[r,n,l];case 3:return[r,s,n];case 4:return[l,r,n];case 5:return[n,r,s]}}function c(t){var e,i,n,a,o=t[0]/360,s=t[1]/100,l=t[2]/100,d=s+l;switch(d>1&&(s/=d,l/=d),n=6*o-(e=Math.floor(6*o)),0!=(1&e)&&(n=1-n),a=s+n*((i=1-l)-s),e){default:case 6:case 0:r=i,g=a,b=s;break;case 1:r=a,g=i,b=s;break;case 2:r=s,g=i,b=a;break;case 3:r=s,g=a,b=i;break;case 4:r=a,g=s,b=i;break;case 5:r=i,g=s,b=a}return[255*r,255*g,255*b]}function f(t){var e=t[0]/100,i=t[1]/100,n=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a))]}function p(t){var e,i,n,a=t[0]/100,o=t[1]/100,r=t[2]/100;return i=-.9689*a+1.8758*o+.0415*r,n=.0557*a+-.204*o+1.057*r,e=(e=3.2406*a+-1.5372*o+-.4986*r)>.0031308?1.055*Math.pow(e,1/2.4)-.055:e*=12.92,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:i*=12.92,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:n*=12.92,[255*(e=Math.min(Math.max(0,e),1)),255*(i=Math.min(Math.max(0,i),1)),255*(n=Math.min(Math.max(0,n),1))]}function m(t){var e=t[0],i=t[1],n=t[2];return i/=100,n/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(e-i),200*(i-(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116))]}function v(t){var e,i,n,a,o=t[0],r=t[1],s=t[2];return o<=8?a=(i=100*o/903.3)/100*7.787+16/116:(i=100*Math.pow((o+16)/116,3),a=Math.pow(i/100,1/3)),[e=e/95.047<=.008856?e=95.047*(r/500+a-16/116)/7.787:95.047*Math.pow(r/500+a,3),i,n=n/108.883<=.008859?n=108.883*(a-s/200-16/116)/7.787:108.883*Math.pow(a-s/200,3)]}function x(t){var e,i=t[0],n=t[1],a=t[2];return(e=360*Math.atan2(a,n)/2/Math.PI)<0&&(e+=360),[i,Math.sqrt(n*n+a*a),e]}function y(t){return p(v(t))}function k(t){var e,i=t[0],n=t[1];return e=t[2]/360*2*Math.PI,[i,n*Math.cos(e),n*Math.sin(e)]}function w(t){return M[t]}var M={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},_={};for(var C in M)_[JSON.stringify(M[C])]=C;var S=function(){return new T};for(var P in e){S[P+"Raw"]=function(t){return function(i){return"number"==typeof i&&(i=Array.prototype.slice.call(arguments)),e[t](i)}}(P);var I=/(\w+)2(\w+)/.exec(P),A=I[1],D=I[2];(S[A]=S[A]||{})[D]=S[P]=function(t){return function(i){"number"==typeof i&&(i=Array.prototype.slice.call(arguments));var n=e[t](i);if("string"==typeof n||void 0===n)return n;for(var a=0;a<n.length;a++)n[a]=Math.round(n[a]);return n}}(P)}var T=function(){this.convs={}};T.prototype.routeSpace=function(t,e){var i=e[0];return void 0===i?this.getValues(t):("number"==typeof i&&(i=Array.prototype.slice.call(e)),this.setValues(t,i))},T.prototype.setValues=function(t,e){return this.space=t,this.convs={},this.convs[t]=e,this},T.prototype.getValues=function(t){var e=this.convs[t];if(!e){var i=this.space,n=this.convs[i];e=S[i][t](n),this.convs[t]=e}return e},["rgb","hsl","hsv","cmyk","keyword"].forEach(function(t){T.prototype[t]=function(e){return this.routeSpace(t,arguments)}});var F=S,L={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},R={getRgba:O,getHsla:z,getRgb:function(t){var e=O(t);return e&&e.slice(0,3)},getHsl:function(t){var e=z(t);return e&&e.slice(0,3)},getHwb:B,getAlpha:function(t){var e=O(t);if(e)return e[3];if(e=z(t))return e[3];if(e=B(t))return e[3]},hexString:function(t,e){var e=void 0!==e&&3===t.length?e:t[3];return"#"+H(t[0])+H(t[1])+H(t[2])+(e>=0&&e<1?H(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return N(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:N,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return W(t,e);var i=Math.round(t[0]/255*100),n=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+i+"%, "+n+"%, "+a+"%)"},percentaString:W,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return V(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:V,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return j[t.slice(0,3)]}};function O(t){if(t){var e=[0,0,0],i=1,n=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(n){a=(n=n[1])[3];for(var o=0;o<e.length;o++)e[o]=parseInt(n[o]+n[o],16);a&&(i=Math.round(parseInt(a+a,16)/255*100)/100)}else if(n=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=n[2],n=n[1];for(o=0;o<e.length;o++)e[o]=parseInt(n.slice(2*o,2*o+2),16);a&&(i=Math.round(parseInt(a,16)/255*100)/100)}else if(n=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(o=0;o<e.length;o++)e[o]=parseInt(n[o+1]);i=parseFloat(n[4])}else if(n=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(o=0;o<e.length;o++)e[o]=Math.round(2.55*parseFloat(n[o+1]));i=parseFloat(n[4])}else if(n=t.match(/(\w+)/)){if("transparent"==n[1])return[0,0,0,0];if(!(e=L[n[1]]))return}for(o=0;o<e.length;o++)e[o]=E(e[o],0,255);return i=i||0==i?E(i,0,1):1,e[3]=i,e}}function z(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var i=parseFloat(e[4]);return[E(parseInt(e[1]),0,360),E(parseFloat(e[2]),0,100),E(parseFloat(e[3]),0,100),E(isNaN(i)?1:i,0,1)]}}}function B(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var i=parseFloat(e[4]);return[E(parseInt(e[1]),0,360),E(parseFloat(e[2]),0,100),E(parseFloat(e[3]),0,100),E(isNaN(i)?1:i,0,1)]}}}function N(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function W(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function V(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function E(t,e,i){return Math.min(Math.max(e,t),i)}function H(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var j={};for(var q in L)j[L[q]]=q;var Y=function(t){return t instanceof Y?t:this instanceof Y?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=R.getRgba(t))?this.setValues("rgb",e):(e=R.getHsla(t))?this.setValues("hsl",e):(e=R.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new Y(t);var e};Y.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return R.hexString(this.values.rgb)},rgbString:function(){return R.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return R.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return R.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return R.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return R.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return R.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return R.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],i=0;i<t.length;i++){var n=t[i]/255;e[i]=n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),i=t.luminosity();return e>i?(e+.05)/(i+.05):(i+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,i=(e[0]+t)%360;return e[0]=i<0?360+i:i,this.setValues("hsl",e),this},mix:function(t,e){var i=t,n=void 0===e?.5:e,a=2*n-1,o=this.alpha()-i.alpha(),r=((a*o==-1?a:(a+o)/(1+a*o))+1)/2,s=1-r;return this.rgb(r*this.red()+s*i.red(),r*this.green()+s*i.green(),r*this.blue()+s*i.blue()).alpha(this.alpha()*n+i.alpha()*(1-n))},toJSON:function(){return this.rgb()},clone:function(){var t,e,i=new Y,n=this.values,a=i.values;for(var o in n)n.hasOwnProperty(o)&&(t=n[o],"[object Array]"===(e={}.toString.call(t))?a[o]=t.slice(0):"[object Number]"===e?a[o]=t:console.error("unexpected color value:",t));return i}},Y.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},Y.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},Y.prototype.getValues=function(t){for(var e=this.values,i={},n=0;n<t.length;n++)i[t.charAt(n)]=e[t][n];return 1!==e.alpha&&(i.a=e.alpha),i},Y.prototype.setValues=function(t,e){var i,n,a=this.values,o=this.spaces,r=this.maxes,s=1;if(this.valid=!0,"alpha"===t)s=e;else if(e.length)a[t]=e.slice(0,t.length),s=e[t.length];else if(void 0!==e[t.charAt(0)]){for(i=0;i<t.length;i++)a[t][i]=e[t.charAt(i)];s=e.a}else if(void 0!==e[o[t][0]]){var l=o[t];for(i=0;i<t.length;i++)a[t][i]=e[l[i]];s=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===s?a.alpha:s)),"alpha"===t)return!1;for(i=0;i<t.length;i++)n=Math.max(0,Math.min(r[t][i],a[t][i])),a[t][i]=Math.round(n);for(var d in o)d!==t&&(a[d]=F[t][d](a[t]));return!0},Y.prototype.setSpace=function(t,e){var i=e[0];return void 0===i?this.getValues(t):("number"==typeof i&&(i=Array.prototype.slice.call(e)),this.setValues(t,i),this)},Y.prototype.setChannel=function(t,e,i){var n=this.values[t];return void 0===i?n[e]:i===n[e]?this:(n[e]=i,this.setValues(t,n),this)},"undefined"!=typeof window&&(window.Color=Y);var U,X=Y,K={noop:function(){},uid:(U=0,function(){return U++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,i){return K.valueOrDefault(K.isArray(t)?t[e]:t,i)},callback:function(t,e,i){if(t&&"function"==typeof t.call)return t.apply(i,e)},each:function(t,e,i,n){var a,o,r;if(K.isArray(t))if(o=t.length,n)for(a=o-1;a>=0;a--)e.call(i,t[a],a);else for(a=0;a<o;a++)e.call(i,t[a],a);else if(K.isObject(t))for(o=(r=Object.keys(t)).length,a=0;a<o;a++)e.call(i,t[r[a]],r[a])},arrayEquals:function(t,e){var i,n,a,o;if(!t||!e||t.length!==e.length)return!1;for(i=0,n=t.length;i<n;++i)if(a=t[i],o=e[i],a instanceof Array&&o instanceof Array){if(!K.arrayEquals(a,o))return!1}else if(a!==o)return!1;return!0},clone:function(t){if(K.isArray(t))return t.map(K.clone);if(K.isObject(t)){for(var e={},i=Object.keys(t),n=i.length,a=0;a<n;++a)e[i[a]]=K.clone(t[i[a]]);return e}return t},_merger:function(t,e,i,n){var a=e[t],o=i[t];K.isObject(a)&&K.isObject(o)?K.merge(a,o,n):e[t]=K.clone(o)},_mergerIf:function(t,e,i){var n=e[t],a=i[t];K.isObject(n)&&K.isObject(a)?K.mergeIf(n,a):e.hasOwnProperty(t)||(e[t]=K.clone(a))},merge:function(t,e,i){var n,a,o,r,s,l=K.isArray(e)?e:[e],d=l.length;if(!K.isObject(t))return t;for(n=(i=i||{}).merger||K._merger,a=0;a<d;++a)if(e=l[a],K.isObject(e))for(s=0,r=(o=Object.keys(e)).length;s<r;++s)n(o[s],t,e,i);return t},mergeIf:function(t,e){return K.merge(t,e,{merger:K._mergerIf})},extend:function(t){for(var e=function(e,i){t[i]=e},i=1,n=arguments.length;i<n;++i)K.each(arguments[i],e);return t},inherits:function(t){var e=this,i=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},n=function(){this.constructor=i};return n.prototype=e.prototype,i.prototype=new n,i.extend=K.inherits,t&&K.extend(i.prototype,t),i.__super__=e.prototype,i}},G=K;K.callCallback=K.callback,K.indexOf=function(t,e,i){return Array.prototype.indexOf.call(t,e,i)},K.getValueOrDefault=K.valueOrDefault,K.getValueAtIndexOrDefault=K.valueAtIndexOrDefault;var Z={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:1===t?1:(i||(i=.3),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),-n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i))},easeOutElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:1===t?1:(i||(i=.3),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),n*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/i)+1)},easeInOutElastic:function(t){var e=1.70158,i=0,n=1;return 0===t?0:2==(t/=.5)?1:(i||(i=.45),n<1?(n=1,e=i/4):e=i/(2*Math.PI)*Math.asin(1/n),t<1?n*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*-.5:n*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/i)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-Z.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*Z.easeInBounce(2*t):.5*Z.easeOutBounce(2*t-1)+.5}},$={effects:Z};G.easingEffects=Z;var J=Math.PI,Q=J/180,tt=2*J,et=J/2,it=J/4,nt=2*J/3,at={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,i,n,a,o){if(o){var r=Math.min(o,a/2,n/2),s=e+r,l=i+r,d=e+n-r,u=i+a-r;t.moveTo(e,l),s<d&&l<u?(t.arc(s,l,r,-J,-et),t.arc(d,l,r,-et,0),t.arc(d,u,r,0,et),t.arc(s,u,r,et,J)):s<d?(t.moveTo(s,i),t.arc(d,l,r,-et,et),t.arc(s,l,r,et,J+et)):l<u?(t.arc(s,l,r,-J,0),t.arc(s,u,r,0,J)):t.arc(s,l,r,-J,J),t.closePath(),t.moveTo(e,i)}else t.rect(e,i,n,a)},drawPoint:function(t,e,i,n,a,o){var r,s,l,d,u,h=(o||0)*Q;if(!e||"object"!=typeof e||"[object HTMLImageElement]"!==(r=e.toString())&&"[object HTMLCanvasElement]"!==r){if(!(isNaN(i)||i<=0)){switch(t.beginPath(),e){default:t.arc(n,a,i,0,tt),t.closePath();break;case"triangle":t.moveTo(n+Math.sin(h)*i,a-Math.cos(h)*i),h+=nt,t.lineTo(n+Math.sin(h)*i,a-Math.cos(h)*i),h+=nt,t.lineTo(n+Math.sin(h)*i,a-Math.cos(h)*i),t.closePath();break;case"rectRounded":d=i-(u=.516*i),s=Math.cos(h+it)*d,l=Math.sin(h+it)*d,t.arc(n-s,a-l,u,h-J,h-et),t.arc(n+l,a-s,u,h-et,h),t.arc(n+s,a+l,u,h,h+et),t.arc(n-l,a+s,u,h+et,h+J),t.closePath();break;case"rect":if(!o){d=Math.SQRT1_2*i,t.rect(n-d,a-d,2*d,2*d);break}h+=it;case"rectRot":s=Math.cos(h)*i,l=Math.sin(h)*i,t.moveTo(n-s,a-l),t.lineTo(n+l,a-s),t.lineTo(n+s,a+l),t.lineTo(n-l,a+s),t.closePath();break;case"crossRot":h+=it;case"cross":s=Math.cos(h)*i,l=Math.sin(h)*i,t.moveTo(n-s,a-l),t.lineTo(n+s,a+l),t.moveTo(n+l,a-s),t.lineTo(n-l,a+s);break;case"star":s=Math.cos(h)*i,l=Math.sin(h)*i,t.moveTo(n-s,a-l),t.lineTo(n+s,a+l),t.moveTo(n+l,a-s),t.lineTo(n-l,a+s),h+=it,s=Math.cos(h)*i,l=Math.sin(h)*i,t.moveTo(n-s,a-l),t.lineTo(n+s,a+l),t.moveTo(n+l,a-s),t.lineTo(n-l,a+s);break;case"line":s=Math.cos(h)*i,l=Math.sin(h)*i,t.moveTo(n-s,a-l),t.lineTo(n+s,a+l);break;case"dash":t.moveTo(n,a),t.lineTo(n+Math.cos(h)*i,a+Math.sin(h)*i)}t.fill(),t.stroke()}}else t.drawImage(e,n-e.width/2,a-e.height/2,e.width,e.height)},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,i,n){var a=i.steppedLine;if(a){if("middle"===a){var o=(e.x+i.x)/2;t.lineTo(o,n?i.y:e.y),t.lineTo(o,n?e.y:i.y)}else"after"===a&&!n||"after"!==a&&n?t.lineTo(e.x,i.y):t.lineTo(i.x,e.y);t.lineTo(i.x,i.y)}else i.tension?t.bezierCurveTo(n?e.controlPointPreviousX:e.controlPointNextX,n?e.controlPointPreviousY:e.controlPointNextY,n?i.controlPointNextX:i.controlPointPreviousX,n?i.controlPointNextY:i.controlPointPreviousY,i.x,i.y):t.lineTo(i.x,i.y)}},ot=at;G.clear=at.clear,G.drawRoundedRectangle=function(t){t.beginPath(),at.roundedRect.apply(at,arguments)};var rt={_set:function(t,e){return G.merge(this[t]||(this[t]={}),e)}};rt._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var st=rt,lt=G.valueOrDefault;var dt={toLineHeight:function(t,e){var i=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!i||"normal"===i[1])return 1.2*e;switch(t=+i[2],i[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,i,n,a;return G.isObject(t)?(e=+t.top||0,i=+t.right||0,n=+t.bottom||0,a=+t.left||0):e=i=n=a=+t||0,{top:e,right:i,bottom:n,left:a,height:e+n,width:a+i}},_parseFont:function(t){var e=st.global,i=lt(t.fontSize,e.defaultFontSize),n={family:lt(t.fontFamily,e.defaultFontFamily),lineHeight:G.options.toLineHeight(lt(t.lineHeight,e.defaultLineHeight),i),size:i,style:lt(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return n.string=function(t){return!t||G.isNullOrUndef(t.size)||G.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(n),n},resolve:function(t,e,i){var n,a,o;for(n=0,a=t.length;n<a;++n)if(void 0!==(o=t[n])&&(void 0!==e&&"function"==typeof o&&(o=o(e)),void 0!==i&&G.isArray(o)&&(o=o[i]),void 0!==o))return o}},ut=G,ht=$,ct=ot,ft=dt;ut.easing=ht,ut.canvas=ct,ut.options=ft;var gt=function(t){ut.extend(this,t),this.initialize.apply(this,arguments)};ut.extend(gt.prototype,{initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=ut.clone(t._model)),t._start={},t},transition:function(t){var e=this,i=e._model,n=e._start,a=e._view;return i&&1!==t?(a||(a=e._view={}),n||(n=e._start={}),function(t,e,i,n){var a,o,r,s,l,d,u,h,c,f=Object.keys(i);for(a=0,o=f.length;a<o;++a)if(d=i[r=f[a]],e.hasOwnProperty(r)||(e[r]=d),(s=e[r])!==d&&"_"!==r[0]){if(t.hasOwnProperty(r)||(t[r]=s),(u=typeof d)==typeof(l=t[r]))if("string"===u){if((h=X(l)).valid&&(c=X(d)).valid){e[r]=c.mix(h,n).rgbString();continue}}else if(ut.isFinite(l)&&ut.isFinite(d)){e[r]=l+(d-l)*n;continue}e[r]=d}}(n,a,i,t),e):(e._view=i,e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return ut.isNumber(this._model.x)&&ut.isNumber(this._model.y)}}),gt.extend=ut.inherits;var pt=gt,mt=pt.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),vt=mt;Object.defineProperty(mt.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(mt.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),st._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:ut.noop,onComplete:ut.noop}});var bt={animations:[],request:null,addAnimation:function(t,e,i,n){var a,o,r=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=i,n||(t.animating=!0),a=0,o=r.length;a<o;++a)if(r[a].chart===t)return void(r[a]=e);r.push(e),1===r.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=ut.findIndex(this.animations,function(e){return e.chart===t});-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=ut.requestAnimFrame.call(window,function(){t.request=null,t.startDigest()}))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,i,n,a=this.animations,o=0;o<a.length;)e=(t=a[o]).chart,i=t.numSteps,n=Math.floor((Date.now()-t.startTime)/t.duration*i)+1,t.currentStep=Math.min(n,i),ut.callback(t.render,[e,t],e),ut.callback(t.onAnimationProgress,[t],e),t.currentStep>=i?(ut.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(o,1)):++o}},xt=ut.options.resolve,yt=["push","pop","shift","splice","unshift"];function kt(t,e){var i=t._chartjs;if(i){var n=i.listeners,a=n.indexOf(e);-1!==a&&n.splice(a,1),n.length>0||(yt.forEach(function(e){delete t[e]}),delete t._chartjs)}}var wt=function(t,e){this.initialize(t,e)};ut.extend(wt.prototype,{datasetElementType:null,dataElementType:null,initialize:function(t,e){this.chart=t,this.index=e,this.linkScales(),this.addElements()},updateIndex:function(t){this.index=t},linkScales:function(){var t=this,e=t.getMeta(),i=t.getDataset();null!==e.xAxisID&&e.xAxisID in t.chart.scales||(e.xAxisID=i.xAxisID||t.chart.options.scales.xAxes[0].id),null!==e.yAxisID&&e.yAxisID in t.chart.scales||(e.yAxisID=i.yAxisID||t.chart.options.scales.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this.update(!0)},destroy:function(){this._data&&kt(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,i=this.getMeta(),n=this.getDataset().data||[],a=i.data;for(t=0,e=n.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);i.dataset=i.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,i=this,n=i.getDataset(),a=n.data||(n.data=[]);i._data!==a&&(i._data&&kt(i._data,i),a&&Object.isExtensible(a)&&(e=i,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),yt.forEach(function(e){var i="onData"+e.charAt(0).toUpperCase()+e.slice(1),n=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=n.apply(this,e);return ut.each(t._chartjs.listeners,function(t){"function"==typeof t[i]&&t[i].apply(t,e)}),a}})}))),i._data=a),i.resyncElements()},update:ut.noop,transition:function(t){for(var e=this.getMeta(),i=e.data||[],n=i.length,a=0;a<n;++a)i[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],i=e.length,n=0;for(t.dataset&&t.dataset.draw();n<i;++n)e[n].draw()},removeHoverStyle:function(t){ut.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],i=t._index,n=t.custom||{},a=t._model,o=ut.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=xt([n.hoverBackgroundColor,e.hoverBackgroundColor,o(a.backgroundColor)],void 0,i),a.borderColor=xt([n.hoverBorderColor,e.hoverBorderColor,o(a.borderColor)],void 0,i),a.borderWidth=xt([n.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,i)},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,i=t.data.length,n=e.length;n<i?t.data.splice(n,i-n):n>i&&this.insertElements(i,n-i)},insertElements:function(t,e){for(var i=0;i<e;++i)this.addElementAndReset(t+i)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),wt.extend=ut.inherits;var Mt=wt;st._set("global",{elements:{arc:{backgroundColor:st.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var _t=pt.extend({inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var i=this._view;if(i){for(var n=ut.getAngleFromPoint(i,{x:t,y:e}),a=n.angle,o=n.distance,r=i.startAngle,s=i.endAngle;s<r;)s+=2*Math.PI;for(;a>s;)a-=2*Math.PI;for(;a<r;)a+=2*Math.PI;var l=a>=r&&a<=s,d=o>=i.innerRadius&&o<=i.outerRadius;return l&&d}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,i=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,i=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*i,y:t.y+Math.sin(e)*i}},draw:function(){var t,e=this._chart.ctx,i=this._view,n=i.startAngle,a=i.endAngle,o="inner"===i.borderAlign?.33:0;e.save(),e.beginPath(),e.arc(i.x,i.y,Math.max(i.outerRadius-o,0),n,a),e.arc(i.x,i.y,i.innerRadius,a,n,!0),e.closePath(),e.fillStyle=i.backgroundColor,e.fill(),i.borderWidth&&("inner"===i.borderAlign?(e.beginPath(),t=o/i.outerRadius,e.arc(i.x,i.y,i.outerRadius,n-t,a+t),i.innerRadius>o?(t=o/i.innerRadius,e.arc(i.x,i.y,i.innerRadius-o,a+t,n-t,!0)):e.arc(i.x,i.y,o,a+Math.PI/2,n-Math.PI/2),e.closePath(),e.clip(),e.beginPath(),e.arc(i.x,i.y,i.outerRadius,n,a),e.arc(i.x,i.y,i.innerRadius,a,n,!0),e.closePath(),e.lineWidth=2*i.borderWidth,e.lineJoin="round"):(e.lineWidth=i.borderWidth,e.lineJoin="bevel"),e.strokeStyle=i.borderColor,e.stroke()),e.restore()}}),Ct=ut.valueOrDefault,St=st.global.defaultColor;st._set("global",{elements:{line:{tension:.4,backgroundColor:St,borderWidth:3,borderColor:St,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var Pt=pt.extend({draw:function(){var t,e,i,n,a=this._view,o=this._chart.ctx,r=a.spanGaps,s=this._children.slice(),l=st.global,d=l.elements.line,u=-1;for(this._loop&&s.length&&s.push(s[0]),o.save(),o.lineCap=a.borderCapStyle||d.borderCapStyle,o.setLineDash&&o.setLineDash(a.borderDash||d.borderDash),o.lineDashOffset=Ct(a.borderDashOffset,d.borderDashOffset),o.lineJoin=a.borderJoinStyle||d.borderJoinStyle,o.lineWidth=Ct(a.borderWidth,d.borderWidth),o.strokeStyle=a.borderColor||l.defaultColor,o.beginPath(),u=-1,t=0;t<s.length;++t)e=s[t],i=ut.previousItem(s,t),n=e._view,0===t?n.skip||(o.moveTo(n.x,n.y),u=t):(i=-1===u?i:s[u],n.skip||(u!==t-1&&!r||-1===u?o.moveTo(n.x,n.y):ut.canvas.lineTo(o,i._view,e._view),u=t));o.stroke(),o.restore()}}),It=ut.valueOrDefault,At=st.global.defaultColor;function Dt(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}st._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:At,borderColor:At,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var Tt=pt.extend({inRange:function(t,e){var i=this._view;return!!i&&Math.pow(t-i.x,2)+Math.pow(e-i.y,2)<Math.pow(i.hitRadius+i.radius,2)},inLabelRange:Dt,inXRange:Dt,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,i=this._chart.ctx,n=e.pointStyle,a=e.rotation,o=e.radius,r=e.x,s=e.y,l=st.global,d=l.defaultColor;e.skip||(void 0===t||ut.canvas._isPointInArea(e,t))&&(i.strokeStyle=e.borderColor||d,i.lineWidth=It(e.borderWidth,l.elements.point.borderWidth),i.fillStyle=e.backgroundColor||d,ut.canvas.drawPoint(i,n,o,r,s,a))}}),Ft=st.global.defaultColor;function Lt(t){return t&&void 0!==t.width}function Rt(t){var e,i,n,a,o;return Lt(t)?(o=t.width/2,e=t.x-o,i=t.x+o,n=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(o=t.height/2,e=Math.min(t.x,t.base),i=Math.max(t.x,t.base),n=t.y-o,a=t.y+o),{left:e,top:n,right:i,bottom:a}}function Ot(t,e,i){return t===e?i:t===i?e:t}function zt(t,e,i){var n,a,o,r,s=t.borderWidth,l=function(t){var e=t.borderSkipped,i={};return e?(t.horizontal?t.base>t.x&&(e=Ot(e,"left","right")):t.base<t.y&&(e=Ot(e,"bottom","top")),i[e]=!0,i):i}(t);return ut.isObject(s)?(n=+s.top||0,a=+s.right||0,o=+s.bottom||0,r=+s.left||0):n=a=o=r=+s||0,{t:l.top||n<0?0:n>i?i:n,r:l.right||a<0?0:a>e?e:a,b:l.bottom||o<0?0:o>i?i:o,l:l.left||r<0?0:r>e?e:r}}function Bt(t,e,i){var n=null===e,a=null===i,o=!(!t||n&&a)&&Rt(t);return o&&(n||e>=o.left&&e<=o.right)&&(a||i>=o.top&&i<=o.bottom)}st._set("global",{elements:{rectangle:{backgroundColor:Ft,borderColor:Ft,borderSkipped:"bottom",borderWidth:0}}});var Nt=pt.extend({draw:function(){var t=this._chart.ctx,e=this._view,i=function(t){var e=Rt(t),i=e.right-e.left,n=e.bottom-e.top,a=zt(t,i/2,n/2);return{outer:{x:e.left,y:e.top,w:i,h:n},inner:{x:e.left+a.l,y:e.top+a.t,w:i-a.l-a.r,h:n-a.t-a.b}}}(e),n=i.outer,a=i.inner;t.fillStyle=e.backgroundColor,t.fillRect(n.x,n.y,n.w,n.h),n.w===a.w&&n.h===a.h||(t.save(),t.beginPath(),t.rect(n.x,n.y,n.w,n.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return Bt(this._view,t,e)},inLabelRange:function(t,e){var i=this._view;return Lt(i)?Bt(i,t,null):Bt(i,null,e)},inXRange:function(t){return Bt(this._view,t,null)},inYRange:function(t){return Bt(this._view,null,t)},getCenterPoint:function(){var t,e,i=this._view;return Lt(i)?(t=i.x,e=(i.y+i.base)/2):(t=(i.x+i.base)/2,e=i.y),{x:t,y:e}},getArea:function(){var t=this._view;return Lt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),Wt={},Vt=_t,Et=Pt,Ht=Tt,jt=Nt;Wt.Arc=Vt,Wt.Line=Et,Wt.Point=Ht,Wt.Rectangle=jt;var qt=ut.options.resolve;st._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}});var Yt=Mt.extend({dataElementType:Wt.Rectangle,initialize:function(){var t;Mt.prototype.initialize.apply(this,arguments),(t=this.getMeta()).stack=this.getDataset().stack,t.bar=!0},update:function(t){var e,i,n=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,i=n.length;e<i;++e)this.updateElement(n[e],e,t)},updateElement:function(t,e,i){var n=this,a=n.getMeta(),o=n.getDataset(),r=n._resolveElementOptions(t,e);t._xScale=n.getScaleForId(a.xAxisID),t._yScale=n.getScaleForId(a.yAxisID),t._datasetIndex=n.index,t._index=e,t._model={backgroundColor:r.backgroundColor,borderColor:r.borderColor,borderSkipped:r.borderSkipped,borderWidth:r.borderWidth,datasetLabel:o.label,label:n.chart.data.labels[e]},n._updateElementGeometry(t,e,i),t.pivot()},_updateElementGeometry:function(t,e,i){var n=this,a=t._model,o=n._getValueScale(),r=o.getBasePixel(),s=o.isHorizontal(),l=n._ruler||n.getRuler(),d=n.calculateBarValuePixels(n.index,e),u=n.calculateBarIndexPixels(n.index,e,l);a.horizontal=s,a.base=i?r:d.base,a.x=s?i?r:d.head:u.center,a.y=s?u.center:i?r:d.head,a.height=s?u.size:void 0,a.width=s?void 0:u.size},_getStacks:function(t){var e,i,n=this.chart,a=this._getIndexScale().options.stacked,o=void 0===t?n.data.datasets.length:t+1,r=[];for(e=0;e<o;++e)(i=n.getDatasetMeta(e)).bar&&n.isDatasetVisible(e)&&(!1===a||!0===a&&-1===r.indexOf(i.stack)||void 0===a&&(void 0===i.stack||-1===r.indexOf(i.stack)))&&r.push(i.stack);return r},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var i=this._getStacks(t),n=void 0!==e?i.indexOf(e):-1;return-1===n?i.length-1:n},getRuler:function(){var t,e,i=this._getIndexScale(),n=this.getStackCount(),a=this.index,o=i.isHorizontal(),r=o?i.left:i.top,s=r+(o?i.width:i.height),l=[];for(t=0,e=this.getMeta().data.length;t<e;++t)l.push(i.getPixelForValue(null,t,a));return{min:ut.isNullOrUndef(i.options.barThickness)?function(t,e){var i,n,a,o,r=t.isHorizontal()?t.width:t.height,s=t.getTicks();for(a=1,o=e.length;a<o;++a)r=Math.min(r,Math.abs(e[a]-e[a-1]));for(a=0,o=s.length;a<o;++a)n=t.getPixelForTick(a),r=a>0?Math.min(r,n-i):r,i=n;return r}(i,l):-1,pixels:l,start:r,end:s,stackCount:n,scale:i}},calculateBarValuePixels:function(t,e){var i,n,a,o,r,s,l=this.chart,d=this.getMeta(),u=this._getValueScale(),h=u.isHorizontal(),c=l.data.datasets,f=+u.getRightValue(c[t].data[e]),g=u.options.minBarLength,p=u.options.stacked,m=d.stack,v=0;if(p||void 0===p&&void 0!==m)for(i=0;i<t;++i)(n=l.getDatasetMeta(i)).bar&&n.stack===m&&n.controller._getValueScaleId()===u.id&&l.isDatasetVisible(i)&&(a=+u.getRightValue(c[i].data[e]),(f<0&&a<0||f>=0&&a>0)&&(v+=a));return o=u.getPixelForValue(v),s=(r=u.getPixelForValue(v+f))-o,void 0!==g&&Math.abs(s)<g&&(s=g,r=f>=0&&!h||f<0&&h?o-g:o+g),{size:s,base:o,head:r,center:r+s/2}},calculateBarIndexPixels:function(t,e,i){var n=i.scale.options,a="flex"===n.barThickness?function(t,e,i){var n,a=e.pixels,o=a[t],r=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=i.categoryPercentage;return null===r&&(r=o-(null===s?e.end-e.start:s-o)),null===s&&(s=o+o-r),n=o-(o-Math.min(r,s))/2*l,{chunk:Math.abs(s-r)/2*l/e.stackCount,ratio:i.barPercentage,start:n}}(e,i,n):function(t,e,i){var n,a,o=i.barThickness,r=e.stackCount,s=e.pixels[t];return ut.isNullOrUndef(o)?(n=e.min*i.categoryPercentage,a=i.barPercentage):(n=o*r,a=1),{chunk:n/r,ratio:a,start:s-n/2}}(e,i,n),o=this.getStackIndex(t,this.getMeta().stack),r=a.start+a.chunk*o+a.chunk/2,s=Math.min(ut.valueOrDefault(n.maxBarThickness,1/0),a.chunk*a.ratio);return{base:r-s/2,head:r+s/2,center:r,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),i=this.getMeta().data,n=this.getDataset(),a=i.length,o=0;for(ut.canvas.clipArea(t.ctx,t.chartArea);o<a;++o)isNaN(e.getRightValue(n.data[o]))||i[o].draw();ut.canvas.unclipArea(t.ctx)},_resolveElementOptions:function(t,e){var i,n,a,o=this.chart,r=o.data.datasets[this.index],s=t.custom||{},l=o.options.elements.rectangle,d={},u={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},h=["backgroundColor","borderColor","borderSkipped","borderWidth"];for(i=0,n=h.length;i<n;++i)d[a=h[i]]=qt([s[a],r[a],l[a]],u,e);return d}}),Ut=ut.valueOrDefault,Xt=ut.options.resolve;st._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var i=e.datasets[t.datasetIndex].label||"",n=e.datasets[t.datasetIndex].data[t.index];return i+": ("+t.xLabel+", "+t.yLabel+", "+n.r+")"}}}});var Kt=Mt.extend({dataElementType:Wt.Point,update:function(t){var e=this,i=e.getMeta().data;ut.each(i,function(i,n){e.updateElement(i,n,t)})},updateElement:function(t,e,i){var n=this,a=n.getMeta(),o=t.custom||{},r=n.getScaleForId(a.xAxisID),s=n.getScaleForId(a.yAxisID),l=n._resolveElementOptions(t,e),d=n.getDataset().data[e],u=n.index,h=i?r.getPixelForDecimal(.5):r.getPixelForValue("object"==typeof d?d:NaN,e,u),c=i?s.getBasePixel():s.getPixelForValue(d,e,u);t._xScale=r,t._yScale=s,t._options=l,t._datasetIndex=u,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:i?0:l.radius,skip:o.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,i=t._options,n=ut.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Ut(i.hoverBackgroundColor,n(i.backgroundColor)),e.borderColor=Ut(i.hoverBorderColor,n(i.borderColor)),e.borderWidth=Ut(i.hoverBorderWidth,i.borderWidth),e.radius=i.radius+i.hoverRadius},_resolveElementOptions:function(t,e){var i,n,a,o=this.chart,r=o.data.datasets[this.index],s=t.custom||{},l=o.options.elements.point,d=r.data[e],u={},h={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},c=["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"];for(i=0,n=c.length;i<n;++i)u[a=c[i]]=Xt([s[a],r[a],l[a]],h,e);return u.radius=Xt([s.radius,d?d.r:void 0,r.radius,l.radius],h,e),u}}),Gt=ut.options.resolve,Zt=ut.valueOrDefault;st._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e=[];e.push('<ul class="'+t.id+'-legend">');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o<n[0].data.length;++o)e.push('<li><span style="background-color:'+n[0].backgroundColor[o]+'"></span>'),a[o]&&e.push(a[o]),e.push("</li>");return e.push("</ul>"),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(i,n){var a=t.getDatasetMeta(0),o=e.datasets[0],r=a.data[n],s=r&&r.custom||{},l=t.options.elements.arc;return{text:i,fillStyle:Gt([s.backgroundColor,o.backgroundColor,l.backgroundColor],void 0,n),strokeStyle:Gt([s.borderColor,o.borderColor,l.borderColor],void 0,n),lineWidth:Gt([s.borderWidth,o.borderWidth,l.borderWidth],void 0,n),hidden:isNaN(o.data[n])||a.data[n].hidden,index:n}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i<n;++i)(a=r.getDatasetMeta(i)).data[o]&&(a.data[o].hidden=!a.data[o].hidden);r.update()}},cutoutPercentage:50,rotation:-.5*Math.PI,circumference:2*Math.PI,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var i=e.labels[t.index],n=": "+e.datasets[t.datasetIndex].data[t.index];return ut.isArray(i)?(i=i.slice())[0]+=n:i+=n,i}}}});var $t=Mt.extend({dataElementType:Wt.Arc,linkScales:ut.noop,getRingIndex:function(t){for(var e=0,i=0;i<t;++i)this.chart.isDatasetVisible(i)&&++e;return e},update:function(t){var e,i,n=this,a=n.chart,o=a.chartArea,r=a.options,s=o.right-o.left,l=o.bottom-o.top,d=Math.min(s,l),u={x:0,y:0},h=n.getMeta(),c=h.data,f=r.cutoutPercentage,g=r.circumference,p=n._getRingWeight(n.index);if(g<2*Math.PI){var m=r.rotation%(2*Math.PI),v=(m+=2*Math.PI*(m>=Math.PI?-1:m<-Math.PI?1:0))+g,b={x:Math.cos(m),y:Math.sin(m)},x={x:Math.cos(v),y:Math.sin(v)},y=m<=0&&v>=0||m<=2*Math.PI&&2*Math.PI<=v,k=m<=.5*Math.PI&&.5*Math.PI<=v||m<=2.5*Math.PI&&2.5*Math.PI<=v,w=m<=-Math.PI&&-Math.PI<=v||m<=Math.PI&&Math.PI<=v,M=m<=.5*-Math.PI&&.5*-Math.PI<=v||m<=1.5*Math.PI&&1.5*Math.PI<=v,_=f/100,C={x:w?-1:Math.min(b.x*(b.x<0?1:_),x.x*(x.x<0?1:_)),y:M?-1:Math.min(b.y*(b.y<0?1:_),x.y*(x.y<0?1:_))},S={x:y?1:Math.max(b.x*(b.x>0?1:_),x.x*(x.x>0?1:_)),y:k?1:Math.max(b.y*(b.y>0?1:_),x.y*(x.y>0?1:_))},P={width:.5*(S.x-C.x),height:.5*(S.y-C.y)};d=Math.min(s/P.width,l/P.height),u={x:-.5*(S.x+C.x),y:-.5*(S.y+C.y)}}for(e=0,i=c.length;e<i;++e)c[e]._options=n._resolveElementOptions(c[e],e);for(a.borderWidth=n.getMaxBorderWidth(),a.outerRadius=Math.max((d-a.borderWidth)/2,0),a.innerRadius=Math.max(f?a.outerRadius/100*f:0,0),a.radiusLength=(a.outerRadius-a.innerRadius)/(n._getVisibleDatasetWeightTotal()||1),a.offsetX=u.x*a.outerRadius,a.offsetY=u.y*a.outerRadius,h.total=n.calculateTotal(),n.outerRadius=a.outerRadius-a.radiusLength*n._getRingWeightOffset(n.index),n.innerRadius=Math.max(n.outerRadius-a.radiusLength*p,0),e=0,i=c.length;e<i;++e)n.updateElement(c[e],e,t)},updateElement:function(t,e,i){var n=this,a=n.chart,o=a.chartArea,r=a.options,s=r.animation,l=(o.left+o.right)/2,d=(o.top+o.bottom)/2,u=r.rotation,h=r.rotation,c=n.getDataset(),f=i&&s.animateRotate?0:t.hidden?0:n.calculateCircumference(c.data[e])*(r.circumference/(2*Math.PI)),g=i&&s.animateScale?0:n.innerRadius,p=i&&s.animateScale?0:n.outerRadius,m=t._options||{};ut.extend(t,{_datasetIndex:n.index,_index:e,_model:{backgroundColor:m.backgroundColor,borderColor:m.borderColor,borderWidth:m.borderWidth,borderAlign:m.borderAlign,x:l+a.offsetX,y:d+a.offsetY,startAngle:u,endAngle:h,circumference:f,outerRadius:p,innerRadius:g,label:ut.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;i&&s.animateRotate||(v.startAngle=0===e?r.rotation:n.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),i=this.getMeta(),n=0;return ut.each(i.data,function(i,a){t=e.data[a],isNaN(t)||i.hidden||(n+=Math.abs(t))}),n},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?2*Math.PI*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,i,n,a,o,r,s,l,d=0,u=this.chart;if(!t)for(e=0,i=u.data.datasets.length;e<i;++e)if(u.isDatasetVisible(e)){t=(n=u.getDatasetMeta(e)).data,e!==this.index&&(o=n.controller);break}if(!t)return 0;for(e=0,i=t.length;e<i;++e)a=t[e],"inner"!==(r=o?o._resolveElementOptions(a,e):a._options).borderAlign&&(s=r.borderWidth,d=(l=r.hoverBorderWidth)>(d=s>d?s:d)?l:d);return d},setHoverStyle:function(t){var e=t._model,i=t._options,n=ut.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Zt(i.hoverBackgroundColor,n(i.backgroundColor)),e.borderColor=Zt(i.hoverBorderColor,n(i.borderColor)),e.borderWidth=Zt(i.hoverBorderWidth,i.borderWidth)},_resolveElementOptions:function(t,e){var i,n,a,o=this.chart,r=this.getDataset(),s=t.custom||{},l=o.options.elements.arc,d={},u={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},h=["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"];for(i=0,n=h.length;i<n;++i)d[a=h[i]]=Gt([s[a],r[a],l[a]],u,e);return d},_getRingWeightOffset:function(t){for(var e=0,i=0;i<t;++i)this.chart.isDatasetVisible(i)&&(e+=this._getRingWeight(i));return e},_getRingWeight:function(t){return Math.max(Zt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});st._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",categoryPercentage:.8,barPercentage:.9,offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}});var Jt=Yt.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Qt=ut.valueOrDefault,te=ut.options.resolve,ee=ut.canvas._isPointInArea;function ie(t,e){return Qt(t.showLine,e.showLines)}st._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var ne=Mt.extend({datasetElementType:Wt.Line,dataElementType:Wt.Point,update:function(t){var e,i,n=this,a=n.getMeta(),o=a.dataset,r=a.data||[],s=n.getScaleForId(a.yAxisID),l=n.getDataset(),d=ie(l,n.chart.options);for(d&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),o._scale=s,o._datasetIndex=n.index,o._children=r,o._model=n._resolveLineOptions(o),o.pivot()),e=0,i=r.length;e<i;++e)n.updateElement(r[e],e,t);for(d&&0!==o._model.tension&&n.updateBezierControlPoints(),e=0,i=r.length;e<i;++e)r[e].pivot()},updateElement:function(t,e,i){var n,a,o=this,r=o.getMeta(),s=t.custom||{},l=o.getDataset(),d=o.index,u=l.data[e],h=o.getScaleForId(r.yAxisID),c=o.getScaleForId(r.xAxisID),f=r.dataset._model,g=o._resolvePointOptions(t,e);n=c.getPixelForValue("object"==typeof u?u:NaN,e,d),a=i?h.getBasePixel():o.calculatePointY(u,e,d),t._xScale=c,t._yScale=h,t._options=g,t._datasetIndex=d,t._index=e,t._model={x:n,y:a,skip:s.skip||isNaN(n)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Qt(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolvePointOptions:function(t,e){var i,n,a,o=this.chart,r=o.data.datasets[this.index],s=t.custom||{},l=o.options.elements.point,d={},u={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},h={backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},c=Object.keys(h);for(i=0,n=c.length;i<n;++i)d[a=c[i]]=te([s[a],r[h[a]],r[a],l[a]],u,e);return d},_resolveLineOptions:function(t){var e,i,n,a=this.chart,o=a.data.datasets[this.index],r=t.custom||{},s=a.options,l=s.elements.line,d={},u=["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill","cubicInterpolationMode"];for(e=0,i=u.length;e<i;++e)d[n=u[e]]=te([r[n],o[n],l[n]]);return d.spanGaps=Qt(o.spanGaps,s.spanGaps),d.tension=Qt(o.lineTension,l.tension),d.steppedLine=te([r.steppedLine,o.steppedLine,l.stepped]),d},calculatePointY:function(t,e,i){var n,a,o,r=this.chart,s=this.getMeta(),l=this.getScaleForId(s.yAxisID),d=0,u=0;if(l.options.stacked){for(n=0;n<i;n++)if(a=r.data.datasets[n],"line"===(o=r.getDatasetMeta(n)).type&&o.yAxisID===l.id&&r.isDatasetVisible(n)){var h=Number(l.getRightValue(a.data[e]));h<0?u+=h||0:d+=h||0}var c=Number(l.getRightValue(t));return c<0?l.getPixelForValue(u+c):l.getPixelForValue(d+c)}return l.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,i,n,a=this.chart,o=this.getMeta(),r=o.dataset._model,s=a.chartArea,l=o.data||[];function d(t,e,i){return Math.max(Math.min(t,i),e)}if(r.spanGaps&&(l=l.filter(function(t){return!t._model.skip})),"monotone"===r.cubicInterpolationMode)ut.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)i=l[t]._model,n=ut.splineCurve(ut.previousItem(l,t)._model,i,ut.nextItem(l,t)._model,r.tension),i.controlPointPreviousX=n.previous.x,i.controlPointPreviousY=n.previous.y,i.controlPointNextX=n.next.x,i.controlPointNextY=n.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)i=l[t]._model,ee(i,s)&&(t>0&&ee(l[t-1]._model,s)&&(i.controlPointPreviousX=d(i.controlPointPreviousX,s.left,s.right),i.controlPointPreviousY=d(i.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&ee(l[t+1]._model,s)&&(i.controlPointNextX=d(i.controlPointNextX,s.left,s.right),i.controlPointNextY=d(i.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,i=this.getMeta(),n=i.data||[],a=e.chartArea,o=n.length,r=0;for(ie(this.getDataset(),e.options)&&(t=(i.dataset._model.borderWidth||0)/2,ut.canvas.clipArea(e.ctx,{left:a.left,right:a.right,top:a.top-t,bottom:a.bottom+t}),i.dataset.draw(),ut.canvas.unclipArea(e.ctx));r<o;++r)n[r].draw(a)},setHoverStyle:function(t){var e=t._model,i=t._options,n=ut.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Qt(i.hoverBackgroundColor,n(i.backgroundColor)),e.borderColor=Qt(i.hoverBorderColor,n(i.borderColor)),e.borderWidth=Qt(i.hoverBorderWidth,i.borderWidth),e.radius=Qt(i.hoverRadius,i.radius)}}),ae=ut.options.resolve;st._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e=[];e.push('<ul class="'+t.id+'-legend">');var i=t.data,n=i.datasets,a=i.labels;if(n.length)for(var o=0;o<n[0].data.length;++o)e.push('<li><span style="background-color:'+n[0].backgroundColor[o]+'"></span>'),a[o]&&e.push(a[o]),e.push("</li>");return e.push("</ul>"),e.join("")},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map(function(i,n){var a=t.getDatasetMeta(0),o=e.datasets[0],r=a.data[n].custom||{},s=t.options.elements.arc;return{text:i,fillStyle:ae([r.backgroundColor,o.backgroundColor,s.backgroundColor],void 0,n),strokeStyle:ae([r.borderColor,o.borderColor,s.borderColor],void 0,n),lineWidth:ae([r.borderWidth,o.borderWidth,s.borderWidth],void 0,n),hidden:isNaN(o.data[n])||a.data[n].hidden,index:n}}):[]}},onClick:function(t,e){var i,n,a,o=e.index,r=this.chart;for(i=0,n=(r.data.datasets||[]).length;i<n;++i)(a=r.getDatasetMeta(i)).data[o].hidden=!a.data[o].hidden;r.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var oe=Mt.extend({dataElementType:Wt.Arc,linkScales:ut.noop,update:function(t){var e,i,n,a=this,o=a.getDataset(),r=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],d=a._angles=[],u=r.data;for(a._updateRadius(),r.count=a.countVisibleElements(),e=0,i=o.data.length;e<i;e++)l[e]=s,n=a._computeAngle(e),d[e]=n,s+=n;for(e=0,i=u.length;e<i;++e)u[e]._options=a._resolveElementOptions(u[e],e),a.updateElement(u[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,i=e.chartArea,n=e.options,a=Math.min(i.right-i.left,i.bottom-i.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(n.cutoutPercentage?e.outerRadius/100*n.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,i){var n=this,a=n.chart,o=n.getDataset(),r=a.options,s=r.animation,l=a.scale,d=a.data.labels,u=l.xCenter,h=l.yCenter,c=r.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(o.data[e]),g=n._starts[e],p=g+(t.hidden?0:n._angles[e]),m=s.animateScale?0:l.getDistanceFromCenterForValue(o.data[e]),v=t._options||{};ut.extend(t,{_datasetIndex:n.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:u,y:h,innerRadius:0,outerRadius:i?m:f,startAngle:i&&s.animateRotate?c:g,endAngle:i&&s.animateRotate?c:p,label:ut.valueAtIndexOrDefault(d,e,d[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),i=0;return ut.each(e.data,function(e,n){isNaN(t.data[n])||e.hidden||i++}),i},setHoverStyle:function(t){var e=t._model,i=t._options,n=ut.getHoverColor,a=ut.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(i.hoverBackgroundColor,n(i.backgroundColor)),e.borderColor=a(i.hoverBorderColor,n(i.borderColor)),e.borderWidth=a(i.hoverBorderWidth,i.borderWidth)},_resolveElementOptions:function(t,e){var i,n,a,o=this.chart,r=this.getDataset(),s=t.custom||{},l=o.options.elements.arc,d={},u={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},h=["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"];for(i=0,n=h.length;i<n;++i)d[a=h[i]]=ae([s[a],r[a],l[a]],u,e);return d},_computeAngle:function(t){var e=this,i=this.getMeta().count,n=e.getDataset(),a=e.getMeta();if(isNaN(n.data[t])||a.data[t].hidden)return 0;var o={chart:e.chart,dataIndex:t,dataset:n,datasetIndex:e.index};return ae([e.chart.options.elements.arc.angle,2*Math.PI/i],o,t)}});st._set("pie",ut.clone(st.doughnut)),st._set("pie",{cutoutPercentage:0});var re=$t,se=ut.valueOrDefault,le=ut.options.resolve;st._set("radar",{scale:{type:"radialLinear"},elements:{line:{tension:0}}});var de=Mt.extend({datasetElementType:Wt.Line,dataElementType:Wt.Point,linkScales:ut.noop,update:function(t){var e,i,n=this,a=n.getMeta(),o=a.dataset,r=a.data||[],s=n.chart.scale,l=n.getDataset();for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),o._scale=s,o._datasetIndex=n.index,o._children=r,o._loop=!0,o._model=n._resolveLineOptions(o),o.pivot(),e=0,i=r.length;e<i;++e)n.updateElement(r[e],e,t);for(n.updateBezierControlPoints(),e=0,i=r.length;e<i;++e)r[e].pivot()},updateElement:function(t,e,i){var n=this,a=t.custom||{},o=n.getDataset(),r=n.chart.scale,s=r.getPointPositionForValue(e,o.data[e]),l=n._resolvePointOptions(t,e),d=n.getMeta().dataset._model,u=i?r.xCenter:s.x,h=i?r.yCenter:s.y;t._scale=r,t._options=l,t._datasetIndex=n.index,t._index=e,t._model={x:u,y:h,skip:a.skip||isNaN(u)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:se(a.tension,d?d.tension:0),hitRadius:l.hitRadius}},_resolvePointOptions:function(t,e){var i,n,a,o=this.chart,r=o.data.datasets[this.index],s=t.custom||{},l=o.options.elements.point,d={},u={chart:o,dataIndex:e,dataset:r,datasetIndex:this.index},h={backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},c=Object.keys(h);for(i=0,n=c.length;i<n;++i)d[a=c[i]]=le([s[a],r[h[a]],r[a],l[a]],u,e);return d},_resolveLineOptions:function(t){var e,i,n,a=this.chart,o=a.data.datasets[this.index],r=t.custom||{},s=a.options.elements.line,l={},d=["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"];for(e=0,i=d.length;e<i;++e)l[n=d[e]]=le([r[n],o[n],s[n]]);return l.tension=se(o.lineTension,s.tension),l},updateBezierControlPoints:function(){var t,e,i,n,a=this.getMeta(),o=this.chart.chartArea,r=a.data||[];function s(t,e,i){return Math.max(Math.min(t,i),e)}for(t=0,e=r.length;t<e;++t)i=r[t]._model,n=ut.splineCurve(ut.previousItem(r,t,!0)._model,i,ut.nextItem(r,t,!0)._model,i.tension),i.controlPointPreviousX=s(n.previous.x,o.left,o.right),i.controlPointPreviousY=s(n.previous.y,o.top,o.bottom),i.controlPointNextX=s(n.next.x,o.left,o.right),i.controlPointNextY=s(n.next.y,o.top,o.bottom)},setHoverStyle:function(t){var e=t._model,i=t._options,n=ut.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=se(i.hoverBackgroundColor,n(i.backgroundColor)),e.borderColor=se(i.hoverBorderColor,n(i.borderColor)),e.borderWidth=se(i.hoverBorderWidth,i.borderWidth),e.radius=se(i.hoverRadius,i.radius)}});st._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},showLines:!1,tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}});var ue={bar:Yt,bubble:Kt,doughnut:$t,horizontalBar:Jt,line:ne,polarArea:oe,pie:re,radar:de,scatter:ne};function he(t,e){return t.native?{x:t.x,y:t.y}:ut.getRelativePosition(t,e)}function ce(t,e){var i,n,a,o,r;for(n=0,o=t.data.datasets.length;n<o;++n)if(t.isDatasetVisible(n))for(a=0,r=(i=t.getDatasetMeta(n)).data.length;a<r;++a){var s=i.data[a];s._view.skip||e(s)}}function fe(t,e){var i=[];return ce(t,function(t){t.inRange(e.x,e.y)&&i.push(t)}),i}function ge(t,e,i,n){var a=Number.POSITIVE_INFINITY,o=[];return ce(t,function(t){if(!i||t.inRange(e.x,e.y)){var r=t.getCenterPoint(),s=n(e,r);s<a?(o=[t],a=s):s===a&&o.push(t)}}),o}function pe(t){var e=-1!==t.indexOf("x"),i=-1!==t.indexOf("y");return function(t,n){var a=e?Math.abs(t.x-n.x):0,o=i?Math.abs(t.y-n.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(o,2))}}function me(t,e,i){var n=he(e,t);i.axis=i.axis||"x";var a=pe(i.axis),o=i.intersect?fe(t,n):ge(t,n,!1,a),r=[];return o.length?(t.data.datasets.forEach(function(e,i){if(t.isDatasetVisible(i)){var n=t.getDatasetMeta(i).data[o[0]._index];n&&!n._view.skip&&r.push(n)}}),r):[]}var ve={modes:{single:function(t,e){var i=he(e,t),n=[];return ce(t,function(t){if(t.inRange(i.x,i.y))return n.push(t),n}),n.slice(0,1)},label:me,index:me,dataset:function(t,e,i){var n=he(e,t);i.axis=i.axis||"xy";var a=pe(i.axis),o=i.intersect?fe(t,n):ge(t,n,!1,a);return o.length>0&&(o=t.getDatasetMeta(o[0]._datasetIndex).data),o},"x-axis":function(t,e){return me(t,e,{intersect:!1})},point:function(t,e){return fe(t,he(e,t))},nearest:function(t,e,i){var n=he(e,t);i.axis=i.axis||"xy";var a=pe(i.axis);return ge(t,n,i.intersect,a)},x:function(t,e,i){var n=he(e,t),a=[],o=!1;return ce(t,function(t){t.inXRange(n.x)&&a.push(t),t.inRange(n.x,n.y)&&(o=!0)}),i.intersect&&!o&&(a=[]),a},y:function(t,e,i){var n=he(e,t),a=[],o=!1;return ce(t,function(t){t.inYRange(n.y)&&a.push(t),t.inRange(n.x,n.y)&&(o=!0)}),i.intersect&&!o&&(a=[]),a}}};function be(t,e){return ut.where(t,function(t){return t.position===e})}function xe(t,e){t.forEach(function(t,e){return t._tmpIndex_=e,t}),t.sort(function(t,i){var n=e?i:t,a=e?t:i;return n.weight===a.weight?n._tmpIndex_-a._tmpIndex_:n.weight-a.weight}),t.forEach(function(t){delete t._tmpIndex_})}function ye(t,e){ut.each(t,function(t){e[t.position]+=t.isHorizontal()?t.height:t.width})}st._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var ke={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,t.boxes.push(e)},removeBox:function(t,e){var i=t.boxes?t.boxes.indexOf(e):-1;-1!==i&&t.boxes.splice(i,1)},configure:function(t,e,i){for(var n,a=["fullWidth","position","weight"],o=a.length,r=0;r<o;++r)n=a[r],i.hasOwnProperty(n)&&(e[n]=i[n])},update:function(t,e,i){if(t){var n=t.options.layout||{},a=ut.options.toPadding(n.padding),o=a.left,r=a.right,s=a.top,l=a.bottom,d=be(t.boxes,"left"),u=be(t.boxes,"right"),h=be(t.boxes,"top"),c=be(t.boxes,"bottom"),f=be(t.boxes,"chartArea");xe(d,!0),xe(u,!1),xe(h,!0),xe(c,!1);var g,p=d.concat(u),m=h.concat(c),v=p.concat(m),b=e-o-r,x=i-s-l,y=(e-b/2)/p.length,k=b,w=x,M={top:s,left:o,bottom:l,right:r},_=[];ut.each(v,function(t){var e,i=t.isHorizontal();i?(e=t.update(t.fullWidth?b:k,x/2),w-=e.height):(e=t.update(y,w),k-=e.width),_.push({horizontal:i,width:e.width,box:t})}),g=function(t){var e=0,i=0,n=0,a=0;return ut.each(t,function(t){if(t.getPadding){var o=t.getPadding();e=Math.max(e,o.top),i=Math.max(i,o.left),n=Math.max(n,o.bottom),a=Math.max(a,o.right)}}),{top:e,left:i,bottom:n,right:a}}(v),ut.each(p,T),ye(p,M),ut.each(m,T),ye(m,M),ut.each(p,function(t){var e=ut.findNextWhere(_,function(e){return e.box===t}),i={left:0,right:0,top:M.top,bottom:M.bottom};e&&t.update(e.width,w,i)}),ye(v,M={top:s,left:o,bottom:l,right:r});var C=Math.max(g.left-M.left,0);M.left+=C,M.right+=Math.max(g.right-M.right,0);var S=Math.max(g.top-M.top,0);M.top+=S,M.bottom+=Math.max(g.bottom-M.bottom,0);var P=i-M.top-M.bottom,I=e-M.left-M.right;I===k&&P===w||(ut.each(p,function(t){t.height=P}),ut.each(m,function(t){t.fullWidth||(t.width=I)}),w=P,k=I);var A=o+C,D=s+S;ut.each(d.concat(h),F),A+=k,D+=w,ut.each(u,F),ut.each(c,F),t.chartArea={left:M.left,top:M.top,right:M.left+k,bottom:M.top+w},ut.each(f,function(e){e.left=t.chartArea.left,e.top=t.chartArea.top,e.right=t.chartArea.right,e.bottom=t.chartArea.bottom,e.update(k,w)})}function T(t){var e=ut.findNextWhere(_,function(e){return e.box===t});if(e)if(e.horizontal){var i={left:Math.max(M.left,g.left),right:Math.max(M.right,g.right),top:0,bottom:0};t.update(t.fullWidth?b:k,x/2,i)}else t.update(e.width,w)}function F(t){t.isHorizontal()?(t.left=t.fullWidth?o:M.left,t.right=t.fullWidth?e-r:M.left+k,t.top=D,t.bottom=D+t.height,D=t.bottom):(t.left=A,t.right=A+t.width,t.top=M.top,t.bottom=M.top+w,A=t.right)}}};var we,Me=(we=Object.freeze({default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&we.default||we,_e="$chartjs",Ce="chartjs-size-monitor",Se="chartjs-render-monitor",Pe="chartjs-render-animation",Ie=["animationstart","webkitAnimationStart"],Ae={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function De(t,e){var i=ut.getStyle(t,e),n=i&&i.match(/^(\d+)(\.\d+)?px$/);return n?Number(n[1]):void 0}var Te=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Fe(t,e,i){t.addEventListener(e,i,Te)}function Le(t,e,i){t.removeEventListener(e,i,Te)}function Re(t,e,i,n,a){return{type:t,chart:e,native:a||null,x:void 0!==i?i:null,y:void 0!==n?n:null}}function Oe(t){var e=document.createElement("div");return e.className=t||"",e}function ze(t,e,i){var n,a,o,r,s=t[_e]||(t[_e]={}),l=s.resizer=function(t){var e=Oe(Ce),i=Oe(Ce+"-expand"),n=Oe(Ce+"-shrink");i.appendChild(Oe()),n.appendChild(Oe()),e.appendChild(i),e.appendChild(n),e._reset=function(){i.scrollLeft=1e6,i.scrollTop=1e6,n.scrollLeft=1e6,n.scrollTop=1e6};var a=function(){e._reset(),t()};return Fe(i,"scroll",a.bind(i,"expand")),Fe(n,"scroll",a.bind(n,"shrink")),e}((n=function(){if(s.resizer){var n=i.options.maintainAspectRatio&&t.parentNode,a=n?n.clientWidth:0;e(Re("resize",i)),n&&n.clientWidth<a&&i.canvas&&e(Re("resize",i))}},o=!1,r=[],function(){r=Array.prototype.slice.call(arguments),a=a||this,o||(o=!0,ut.requestAnimFrame.call(window,function(){o=!1,n.apply(a,r)}))}));!function(t,e){var i=t[_e]||(t[_e]={}),n=i.renderProxy=function(t){t.animationName===Pe&&e()};ut.each(Ie,function(e){Fe(t,e,n)}),i.reflow=!!t.offsetParent,t.classList.add(Se)}(t,function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}})}function Be(t){var e=t[_e]||{},i=e.resizer;delete e.resizer,function(t){var e=t[_e]||{},i=e.renderProxy;i&&(ut.each(Ie,function(e){Le(t,e,i)}),delete e.renderProxy),t.classList.remove(Se)}(t),i&&i.parentNode&&i.parentNode.removeChild(i)}var Ne={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(){var t,e,i;this._loaded||(this._loaded=!0,this.disableCSSInjection||(e=Me,i=(t=this)._style||document.createElement("style"),t._style||(t._style=i,e="/* Chart.js */\n"+e,i.setAttribute("type","text/css"),document.getElementsByTagName("head")[0].appendChild(i)),i.appendChild(document.createTextNode(e))))},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var i=t&&t.getContext&&t.getContext("2d");return this._ensureLoaded(),i&&i.canvas===t?(function(t,e){var i=t.style,n=t.getAttribute("height"),a=t.getAttribute("width");if(t[_e]={initial:{height:n,width:a,style:{display:i.display,height:i.height,width:i.width}}},i.display=i.display||"block",null===a||""===a){var o=De(t,"width");void 0!==o&&(t.width=o)}if(null===n||""===n)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var r=De(t,"height");void 0!==o&&(t.height=r)}}(t,e),i):null},releaseContext:function(t){var e=t.canvas;if(e[_e]){var i=e[_e].initial;["height","width"].forEach(function(t){var n=i[t];ut.isNullOrUndef(n)?e.removeAttribute(t):e.setAttribute(t,n)}),ut.each(i.style||{},function(t,i){e.style[i]=t}),e.width=e.width,delete e[_e]}},addEventListener:function(t,e,i){var n=t.canvas;if("resize"!==e){var a=i[_e]||(i[_e]={});Fe(n,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){i(function(t,e){var i=Ae[t.type]||t.type,n=ut.getRelativePosition(t,e);return Re(i,e,n.x,n.y,t)}(e,t))})}else ze(n,i,t)},removeEventListener:function(t,e,i){var n=t.canvas;if("resize"!==e){var a=((i[_e]||{}).proxies||{})[t.id+"_"+e];a&&Le(n,e,a)}else Be(n)}};ut.addEvent=Fe,ut.removeEvent=Le;var We=Ne._enabled?Ne:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Ve=ut.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},We);st._set("global",{plugins:{}});var Ee={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach(function(t){-1===e.indexOf(t)&&e.push(t)}),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach(function(t){var i=e.indexOf(t);-1!==i&&e.splice(i,1)}),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,i){var n,a,o,r,s,l=this.descriptors(t),d=l.length;for(n=0;n<d;++n)if("function"==typeof(s=(o=(a=l[n]).plugin)[e])&&((r=[t].concat(i||[])).push(a.options),!1===s.apply(o,r)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var i=[],n=[],a=t&&t.config||{},o=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach(function(t){if(-1===i.indexOf(t)){var e=t.id,a=o[e];!1!==a&&(!0===a&&(a=ut.clone(st.global.plugins[e])),i.push(t),n.push({plugin:t,options:a||{}}))}}),e.descriptors=n,e.id=this._cacheId,n},_invalidate:function(t){delete t.$plugins}},He={constructors:{},defaults:{},registerScaleType:function(t,e,i){this.constructors[t]=e,this.defaults[t]=ut.clone(i)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?ut.merge({},[st.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=ut.extend(this.defaults[t],e))},addScalesToLayout:function(t){ut.each(t.scales,function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,ke.addBox(t,e)})}},je=ut.valueOrDefault;st._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:ut.noop,title:function(t,e){var i="",n=e.labels,a=n?n.length:0;if(t.length>0){var o=t[0];o.label?i=o.label:o.xLabel?i=o.xLabel:a>0&&o.index<a&&(i=n[o.index])}return i},afterTitle:ut.noop,beforeBody:ut.noop,beforeLabel:ut.noop,label:function(t,e){var i=e.datasets[t.datasetIndex].label||"";return i&&(i+=": "),ut.isNullOrUndef(t.value)?i+=t.yLabel:i+=t.value,i},labelColor:function(t,e){var i=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:i.borderColor,backgroundColor:i.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:ut.noop,afterBody:ut.noop,beforeFooter:ut.noop,footer:ut.noop,afterFooter:ut.noop}}});var qe={average:function(t){if(!t.length)return!1;var e,i,n=0,a=0,o=0;for(e=0,i=t.length;e<i;++e){var r=t[e];if(r&&r.hasValue()){var s=r.tooltipPosition();n+=s.x,a+=s.y,++o}}return{x:n/o,y:a/o}},nearest:function(t,e){var i,n,a,o=e.x,r=e.y,s=Number.POSITIVE_INFINITY;for(i=0,n=t.length;i<n;++i){var l=t[i];if(l&&l.hasValue()){var d=l.getCenterPoint(),u=ut.distanceBetweenPoints(e,d);u<s&&(s=u,a=l)}}if(a){var h=a.tooltipPosition();o=h.x,r=h.y}return{x:o,y:r}}};function Ye(t,e){return e&&(ut.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ue(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Xe(t){var e=st.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,bodyFontColor:t.bodyFontColor,_bodyFontFamily:je(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:je(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:je(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:je(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:je(t.titleFontStyle,e.defaultFontStyle),titleFontSize:je(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:je(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:je(t.footerFontStyle,e.defaultFontStyle),footerFontSize:je(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function Ke(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function Ge(t){return Ye([],Ue(t))}var Ze=pt.extend({initialize:function(){this._model=Xe(this._options),this._lastActive=[]},getTitle:function(){var t=this._options.callbacks,e=t.beforeTitle.apply(this,arguments),i=t.title.apply(this,arguments),n=t.afterTitle.apply(this,arguments),a=[];return a=Ye(a,Ue(e)),a=Ye(a,Ue(i)),a=Ye(a,Ue(n))},getBeforeBody:function(){return Ge(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var i=this,n=i._options.callbacks,a=[];return ut.each(t,function(t){var o={before:[],lines:[],after:[]};Ye(o.before,Ue(n.beforeLabel.call(i,t,e))),Ye(o.lines,n.label.call(i,t,e)),Ye(o.after,Ue(n.afterLabel.call(i,t,e))),a.push(o)}),a},getAfterBody:function(){return Ge(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this._options.callbacks,e=t.beforeFooter.apply(this,arguments),i=t.footer.apply(this,arguments),n=t.afterFooter.apply(this,arguments),a=[];return a=Ye(a,Ue(e)),a=Ye(a,Ue(i)),a=Ye(a,Ue(n))},update:function(t){var e,i,n,a,o,r,s,l,d,u,h=this,c=h._options,f=h._model,g=h._model=Xe(c),p=h._active,m=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},x={width:f.width,height:f.height},y={x:f.caretX,y:f.caretY};if(p.length){g.opacity=1;var k=[],w=[];y=qe[c.position].call(h,p,h._eventPosition);var M=[];for(e=0,i=p.length;e<i;++e)M.push((n=p[e],a=void 0,o=void 0,r=void 0,s=void 0,l=void 0,d=void 0,u=void 0,a=n._xScale,o=n._yScale||n._scale,r=n._index,s=n._datasetIndex,l=n._chart.getDatasetMeta(s).controller,d=l._getIndexScale(),u=l._getValueScale(),{xLabel:a?a.getLabelForIndex(r,s):"",yLabel:o?o.getLabelForIndex(r,s):"",label:d?""+d.getLabelForIndex(r,s):"",value:u?""+u.getLabelForIndex(r,s):"",index:r,datasetIndex:s,x:n._model.x,y:n._model.y}));c.filter&&(M=M.filter(function(t){return c.filter(t,m)})),c.itemSort&&(M=M.sort(function(t,e){return c.itemSort(t,e,m)})),ut.each(M,function(t){k.push(c.callbacks.labelColor.call(h,t,h._chart)),w.push(c.callbacks.labelTextColor.call(h,t,h._chart))}),g.title=h.getTitle(M,m),g.beforeBody=h.getBeforeBody(M,m),g.body=h.getBody(M,m),g.afterBody=h.getAfterBody(M,m),g.footer=h.getFooter(M,m),g.x=y.x,g.y=y.y,g.caretPadding=c.caretPadding,g.labelColors=k,g.labelTextColors=w,g.dataPoints=M,x=function(t,e){var i=t._chart.ctx,n=2*e.yPadding,a=0,o=e.body,r=o.reduce(function(t,e){return t+e.before.length+e.lines.length+e.after.length},0);r+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,d=e.titleFontSize,u=e.bodyFontSize,h=e.footerFontSize;n+=s*d,n+=s?(s-1)*e.titleSpacing:0,n+=s?e.titleMarginBottom:0,n+=r*u,n+=r?(r-1)*e.bodySpacing:0,n+=l?e.footerMarginTop:0,n+=l*h,n+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,i.measureText(t).width+c)};return i.font=ut.fontString(d,e._titleFontStyle,e._titleFontFamily),ut.each(e.title,f),i.font=ut.fontString(u,e._bodyFontStyle,e._bodyFontFamily),ut.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?u+2:0,ut.each(o,function(t){ut.each(t.before,f),ut.each(t.lines,f),ut.each(t.after,f)}),c=0,i.font=ut.fontString(h,e._footerFontStyle,e._footerFontFamily),ut.each(e.footer,f),{width:a+=2*e.xPadding,height:n}}(this,g),b=function(t,e,i,n){var a=t.x,o=t.y,r=t.caretSize,s=t.caretPadding,l=t.cornerRadius,d=i.xAlign,u=i.yAlign,h=r+s,c=l+s;return"right"===d?a-=e.width:"center"===d&&((a-=e.width/2)+e.width>n.width&&(a=n.width-e.width),a<0&&(a=0)),"top"===u?o+=h:o-="bottom"===u?e.height+h:e.height/2,"center"===u?"left"===d?a+=h:"right"===d&&(a-=h):"left"===d?a-=c:"right"===d&&(a+=c),{x:a,y:o}}(g,x,v=function(t,e){var i,n,a,o,r,s=t._model,l=t._chart,d=t._chart.chartArea,u="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(d.left+d.right)/2,f=(d.top+d.bottom)/2;"center"===h?(i=function(t){return t<=c},n=function(t){return t>c}):(i=function(t){return t<=e.width/2},n=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},o=function(t){return t-e.width-s.caretSize-s.caretPadding<0},r=function(t){return t<=f?"top":"bottom"},i(s.x)?(u="left",a(s.x)&&(u="center",h=r(s.y))):n(s.x)&&(u="right",o(s.x)&&(u="center",h=r(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:u,yAlign:g.yAlign?g.yAlign:h}}(this,x),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=x.width,g.height=x.height,g.caretX=y.x,g.caretY=y.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var i=this._chart.ctx,n=this._view,a=this.getCaretPosition(t,e,n);i.lineTo(a.x1,a.y1),i.lineTo(a.x2,a.y2),i.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,i){var n,a,o,r,s,l,d=i.caretSize,u=i.cornerRadius,h=i.xAlign,c=i.yAlign,f=t.x,g=t.y,p=e.width,m=e.height;if("center"===c)s=g+m/2,"left"===h?(a=(n=f)-d,o=n,r=s+d,l=s-d):(a=(n=f+p)+d,o=n,r=s-d,l=s+d);else if("left"===h?(n=(a=f+u+d)-d,o=a+d):"right"===h?(n=(a=f+p-u-d)-d,o=a+d):(n=(a=i.caretX)-d,o=a+d),"top"===c)s=(r=g)-d,l=r;else{s=(r=g+m)+d,l=r;var v=o;o=n,n=v}return{x1:n,x2:a,x3:o,y1:r,y2:s,y3:l}},drawTitle:function(t,e,i){var n=e.title;if(n.length){t.x=Ke(e,e._titleAlign),i.textAlign=e._titleAlign,i.textBaseline="top";var a,o,r=e.titleFontSize,s=e.titleSpacing;for(i.fillStyle=e.titleFontColor,i.font=ut.fontString(r,e._titleFontStyle,e._titleFontFamily),a=0,o=n.length;a<o;++a)i.fillText(n[a],t.x,t.y),t.y+=r+s,a+1===n.length&&(t.y+=e.titleMarginBottom-s)}},drawBody:function(t,e,i){var n,a=e.bodyFontSize,o=e.bodySpacing,r=e._bodyAlign,s=e.body,l=e.displayColors,d=e.labelColors,u=0,h=l?Ke(e,"left"):0;i.textAlign=r,i.textBaseline="top",i.font=ut.fontString(a,e._bodyFontStyle,e._bodyFontFamily),t.x=Ke(e,r);var c=function(e){i.fillText(e,t.x+u,t.y),t.y+=a+o};i.fillStyle=e.bodyFontColor,ut.each(e.beforeBody,c),u=l&&"right"!==r?"center"===r?a/2+1:a+2:0,ut.each(s,function(o,r){n=e.labelTextColors[r],i.fillStyle=n,ut.each(o.before,c),ut.each(o.lines,function(o){l&&(i.fillStyle=e.legendColorBackground,i.fillRect(h,t.y,a,a),i.lineWidth=1,i.strokeStyle=d[r].borderColor,i.strokeRect(h,t.y,a,a),i.fillStyle=d[r].backgroundColor,i.fillRect(h+1,t.y+1,a-2,a-2),i.fillStyle=n),c(o)}),ut.each(o.after,c)}),u=0,ut.each(e.afterBody,c),t.y-=o},drawFooter:function(t,e,i){var n=e.footer;n.length&&(t.x=Ke(e,e._footerAlign),t.y+=e.footerMarginTop,i.textAlign=e._footerAlign,i.textBaseline="top",i.fillStyle=e.footerFontColor,i.font=ut.fontString(e.footerFontSize,e._footerFontStyle,e._footerFontFamily),ut.each(n,function(n){i.fillText(n,t.x,t.y),t.y+=e.footerFontSize+e.footerSpacing}))},drawBackground:function(t,e,i,n){i.fillStyle=e.backgroundColor,i.strokeStyle=e.borderColor,i.lineWidth=e.borderWidth;var a=e.xAlign,o=e.yAlign,r=t.x,s=t.y,l=n.width,d=n.height,u=e.cornerRadius;i.beginPath(),i.moveTo(r+u,s),"top"===o&&this.drawCaret(t,n),i.lineTo(r+l-u,s),i.quadraticCurveTo(r+l,s,r+l,s+u),"center"===o&&"right"===a&&this.drawCaret(t,n),i.lineTo(r+l,s+d-u),i.quadraticCurveTo(r+l,s+d,r+l-u,s+d),"bottom"===o&&this.drawCaret(t,n),i.lineTo(r+u,s+d),i.quadraticCurveTo(r,s+d,r,s+d-u),"center"===o&&"left"===a&&this.drawCaret(t,n),i.lineTo(r,s+u),i.quadraticCurveTo(r,s,r+u,s),i.closePath(),i.fill(),e.borderWidth>0&&i.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var i={width:e.width,height:e.height},n={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,o=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&o&&(t.save(),t.globalAlpha=a,this.drawBackground(n,e,t,i),n.y+=e.yPadding,this.drawTitle(n,e,t),this.drawBody(n,e,t),this.drawFooter(n,e,t),t.restore())}},handleEvent:function(t){var e,i=this,n=i._options;return i._lastActive=i._lastActive||[],"mouseout"===t.type?i._active=[]:i._active=i._chart.getElementsAtEventForMode(t,n.mode,n),(e=!ut.arrayEquals(i._active,i._lastActive))&&(i._lastActive=i._active,(n.enabled||n.custom)&&(i._eventPosition={x:t.x,y:t.y},i.update(!0),i.pivot())),e}}),$e=qe,Je=Ze;Je.positioners=$e;var Qe=ut.valueOrDefault;function ti(){return ut.merge({},[].slice.call(arguments),{merger:function(t,e,i,n){if("xAxes"===t||"yAxes"===t){var a,o,r,s=i[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)r=i[t][a],o=Qe(r.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||r.type&&r.type!==e[t][a].type?ut.merge(e[t][a],[He.getScaleDefaults(o),r]):ut.merge(e[t][a],r)}else ut._merger(t,e,i,n)}})}function ei(){return ut.merge({},[].slice.call(arguments),{merger:function(t,e,i,n){var a=e[t]||{},o=i[t];"scales"===t?e[t]=ti(a,o):"scale"===t?e[t]=ut.merge(a,[He.getScaleDefaults(o.type),o]):ut._merger(t,e,i,n)}})}function ii(t){return"top"===t||"bottom"===t}st._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var ni=function(t,e){return this.construct(t,e),this};ut.extend(ni.prototype,{construct:function(t,e){var i=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=ei(st.global,st[t.type],t.options||{}),t}(e);var n=Ve.acquireContext(t,e),a=n&&n.canvas,o=a&&a.height,r=a&&a.width;i.id=ut.uid(),i.ctx=n,i.canvas=a,i.config=e,i.width=r,i.height=o,i.aspectRatio=o?r/o:null,i.options=e.options,i._bufferedRender=!1,i.chart=i,i.controller=i,ni.instances[i.id]=i,Object.defineProperty(i,"data",{get:function(){return i.config.data},set:function(t){i.config.data=t}}),n&&a?(i.initialize(),i.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Ee.notify(t,"beforeInit"),ut.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.initToolTip(),Ee.notify(t,"afterInit"),t},clear:function(){return ut.canvas.clear(this),this},stop:function(){return bt.cancelAnimation(this),this},resize:function(t){var e=this,i=e.options,n=e.canvas,a=i.maintainAspectRatio&&e.aspectRatio||null,o=Math.max(0,Math.floor(ut.getMaximumWidth(n))),r=Math.max(0,Math.floor(a?o/a:ut.getMaximumHeight(n)));if((e.width!==o||e.height!==r)&&(n.width=e.width=o,n.height=e.height=r,n.style.width=o+"px",n.style.height=r+"px",ut.retinaScale(e,i.devicePixelRatio),!t)){var s={width:o,height:r};Ee.notify(e,"resize",[s]),i.onResize&&i.onResize(e,s),e.stop(),e.update({duration:i.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},i=t.scale;ut.each(e.xAxes,function(t,e){t.id=t.id||"x-axis-"+e}),ut.each(e.yAxes,function(t,e){t.id=t.id||"y-axis-"+e}),i&&(i.id=i.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,i=t.scales||{},n=[],a=Object.keys(i).reduce(function(t,e){return t[e]=!1,t},{});e.scales&&(n=n.concat((e.scales.xAxes||[]).map(function(t){return{options:t,dtype:"category",dposition:"bottom"}}),(e.scales.yAxes||[]).map(function(t){return{options:t,dtype:"linear",dposition:"left"}}))),e.scale&&n.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),ut.each(n,function(e){var n=e.options,o=n.id,r=Qe(n.type,e.dtype);ii(n.position)!==ii(e.dposition)&&(n.position=e.dposition),a[o]=!0;var s=null;if(o in i&&i[o].type===r)(s=i[o]).options=n,s.ctx=t.ctx,s.chart=t;else{var l=He.getScaleConstructor(r);if(!l)return;s=new l({id:o,type:r,options:n,ctx:t.ctx,chart:t}),i[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)}),ut.each(a,function(t,e){t||delete i[e]}),t.scales=i,He.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t=this,e=[];return ut.each(t.data.datasets,function(i,n){var a=t.getDatasetMeta(n),o=i.type||t.config.type;if(a.type&&a.type!==o&&(t.destroyDatasetMeta(n),a=t.getDatasetMeta(n)),a.type=o,a.controller)a.controller.updateIndex(n),a.controller.linkScales();else{var r=ue[a.type];if(void 0===r)throw new Error('"'+a.type+'" is not a chart type.');a.controller=new r(t,n),e.push(a.controller)}},t),e},resetElements:function(){var t=this;ut.each(t.data.datasets,function(e,i){t.getDatasetMeta(i).controller.reset()},t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,i,n=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),i=(e=n).options,ut.each(e.scales,function(t){ke.removeBox(e,t)}),i=ei(st.global,st[e.config.type],i),e.options=e.config.options=i,e.ensureScalesHaveIDs(),e.buildOrUpdateScales(),e.tooltip._options=i.tooltips,e.tooltip.initialize(),Ee._invalidate(n),!1!==Ee.notify(n,"beforeUpdate")){n.tooltip._data=n.data;var a=n.buildOrUpdateControllers();ut.each(n.data.datasets,function(t,e){n.getDatasetMeta(e).controller.buildOrUpdateElements()},n),n.updateLayout(),n.options.animation&&n.options.animation.duration&&ut.each(a,function(t){t.reset()}),n.updateDatasets(),n.tooltip.initialize(),n.lastActive=[],Ee.notify(n,"afterUpdate"),n._bufferedRender?n._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:n.render(t)}},updateLayout:function(){!1!==Ee.notify(this,"beforeLayout")&&(ke.update(this,this.width,this.height),Ee.notify(this,"afterScaleUpdate"),Ee.notify(this,"afterLayout"))},updateDatasets:function(){if(!1!==Ee.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Ee.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),i={meta:e,index:t};!1!==Ee.notify(this,"beforeDatasetUpdate",[i])&&(e.controller.update(),Ee.notify(this,"afterDatasetUpdate",[i]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var i=e.options.animation,n=Qe(t.duration,i&&i.duration),a=t.lazy;if(!1!==Ee.notify(e,"beforeRender")){var o=function(t){Ee.notify(e,"afterRender"),ut.callback(i&&i.onComplete,[t],e)};if(i&&n){var r=new vt({numSteps:n/16.66,easing:t.easing||i.easing,render:function(t,e){var i=ut.easing.effects[e.easing],n=e.currentStep,a=n/e.numSteps;t.draw(i(a),a,n)},onAnimationProgress:i.onProgress,onAnimationComplete:o});bt.addAnimation(e,r,n,a)}else e.draw(),o(new vt({numSteps:0,chart:e}));return e}},draw:function(t){var e=this;e.clear(),ut.isNullOrUndef(t)&&(t=1),e.transition(t),e.width<=0||e.height<=0||!1!==Ee.notify(e,"beforeDraw",[t])&&(ut.each(e.boxes,function(t){t.draw(e.chartArea)},e),e.drawDatasets(t),e._drawTooltip(t),Ee.notify(e,"afterDraw",[t]))},transition:function(t){for(var e=0,i=(this.data.datasets||[]).length;e<i;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},drawDatasets:function(t){var e=this;if(!1!==Ee.notify(e,"beforeDatasetsDraw",[t])){for(var i=(e.data.datasets||[]).length-1;i>=0;--i)e.isDatasetVisible(i)&&e.drawDataset(i,t);Ee.notify(e,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var i=this.getDatasetMeta(t),n={meta:i,index:t,easingValue:e};!1!==Ee.notify(this,"beforeDatasetDraw",[n])&&(i.controller.draw(e),Ee.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,i={tooltip:e,easingValue:t};!1!==Ee.notify(this,"beforeTooltipDraw",[i])&&(e.draw(),Ee.notify(this,"afterTooltipDraw",[i]))},getElementAtEvent:function(t){return ve.modes.single(this,t)},getElementsAtEvent:function(t){return ve.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return ve.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,i){var n=ve.modes[e];return"function"==typeof n?n(this,t,i):[]},getDatasetAtEvent:function(t){return ve.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var i=e._meta[this.id];return i||(i=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null}),i},getVisibleDatasetCount:function(){for(var t=0,e=0,i=this.data.datasets.length;e<i;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,i=this.data.datasets[t],n=i._meta&&i._meta[e];n&&(n.controller.destroy(),delete i._meta[e])},destroy:function(){var t,e,i=this,n=i.canvas;for(i.stop(),t=0,e=i.data.datasets.length;t<e;++t)i.destroyDatasetMeta(t);n&&(i.unbindEvents(),ut.canvas.clear(i),Ve.releaseContext(i.ctx),i.canvas=null,i.ctx=null),Ee.notify(i,"destroy"),delete ni.instances[i.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Je({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},i=function(){t.eventHandler.apply(t,arguments)};ut.each(t.options.events,function(n){Ve.addEventListener(t,n,i),e[n]=i}),t.options.responsive&&(i=function(){t.resize()},Ve.addEventListener(t,"resize",i),e.resize=i)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,ut.each(e,function(e,i){Ve.removeEventListener(t,i,e)}))},updateHoverStyle:function(t,e,i){var n,a,o,r=i?"setHoverStyle":"removeHoverStyle";for(a=0,o=t.length;a<o;++a)(n=t[a])&&this.getDatasetMeta(n._datasetIndex).controller[r](n)},eventHandler:function(t){var e=this,i=e.tooltip;if(!1!==Ee.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var n=e.handleEvent(t);i&&(n=i._start?i.handleEvent(t):n|i.handleEvent(t)),Ee.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):n&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,i=this,n=i.options||{},a=n.hover;return i.lastActive=i.lastActive||[],"mouseout"===t.type?i.active=[]:i.active=i.getElementsAtEventForMode(t,a.mode,a),ut.callback(n.onHover||n.hover.onHover,[t.native,i.active],i),"mouseup"!==t.type&&"click"!==t.type||n.onClick&&n.onClick.call(i,t.native,i.active),i.lastActive.length&&i.updateHoverStyle(i.lastActive,a.mode,!1),i.active.length&&a.mode&&i.updateHoverStyle(i.active,a.mode,!0),e=!ut.arrayEquals(i.active,i.lastActive),i.lastActive=i.active,e}}),ni.instances={};var ai=ni;ni.Controller=ni,ni.types={},ut.configMerge=ei,ut.scaleMerge=ti;function oi(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function ri(t){this.options=t||{}}ut.extend(ri.prototype,{formats:oi,parse:oi,format:oi,add:oi,diff:oi,startOf:oi,endOf:oi,_create:function(t){return t}}),ri.override=function(t){ut.extend(ri.prototype,t)};var si={_date:ri},li={formatters:{values:function(t){return ut.isArray(t)?t:""+t},linear:function(t,e,i){var n=i.length>3?i[2]-i[1]:i[1]-i[0];Math.abs(n)>1&&t!==Math.floor(t)&&(n=t-Math.floor(t));var a=ut.log10(Math.abs(n)),o="";if(0!==t)if(Math.max(Math.abs(i[0]),Math.abs(i[i.length-1]))<1e-4){var r=ut.log10(Math.abs(t));o=t.toExponential(Math.floor(r)-Math.floor(a))}else{var s=-1*Math.floor(a);s=Math.max(Math.min(s,20),0),o=t.toFixed(s)}else o="0";return o},logarithmic:function(t,e,i){var n=t/Math.pow(10,Math.floor(ut.log10(t)));return 0===t?"0":1===n||2===n||5===n||0===e||e===i.length-1?t.toExponential():""}}},di=ut.valueOrDefault,ui=ut.valueAtIndexOrDefault;function hi(t){var e,i,n=[];for(e=0,i=t.length;e<i;++e)n.push(t[e].label);return n}function ci(t,e,i){return ut.isArray(e)?ut.longestText(t,i,e):t.measureText(e).width}st._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0, 0, 0, 0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:li.formatters.values,minor:{},major:{}}});var fi=pt.extend({getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},mergeTicksOptions:function(){var t=this.options.ticks;for(var e in!1===t.minor&&(t.minor={display:!1}),!1===t.major&&(t.major={display:!1}),t)"major"!==e&&"minor"!==e&&(void 0===t.minor[e]&&(t.minor[e]=t[e]),void 0===t.major[e]&&(t.major[e]=t[e]))},beforeUpdate:function(){ut.callback(this.options.beforeUpdate,[this])},update:function(t,e,i){var n,a,o,r,s,l,d=this;for(d.beforeUpdate(),d.maxWidth=t,d.maxHeight=e,d.margins=ut.extend({left:0,right:0,top:0,bottom:0},i),d._maxLabelLines=0,d.longestLabelWidth=0,d.longestTextCache=d.longestTextCache||{},d.beforeSetDimensions(),d.setDimensions(),d.afterSetDimensions(),d.beforeDataLimits(),d.determineDataLimits(),d.afterDataLimits(),d.beforeBuildTicks(),s=d.buildTicks()||[],s=d.afterBuildTicks(s)||s,d.beforeTickToLabelConversion(),o=d.convertTicksToLabels(s)||d.ticks,d.afterTickToLabelConversion(),d.ticks=o,n=0,a=o.length;n<a;++n)r=o[n],(l=s[n])?l.label=r:s.push(l={label:r,major:!1});return d._ticks=s,d.beforeCalculateTickRotation(),d.calculateTickRotation(),d.afterCalculateTickRotation(),d.beforeFit(),d.fit(),d.afterFit(),d.afterUpdate(),d.minSize},afterUpdate:function(){ut.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){ut.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){ut.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){ut.callback(this.options.beforeDataLimits,[this])},determineDataLimits:ut.noop,afterDataLimits:function(){ut.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){ut.callback(this.options.beforeBuildTicks,[this])},buildTicks:ut.noop,afterBuildTicks:function(t){var e=this;return ut.isArray(t)&&t.length?ut.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=ut.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){ut.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){ut.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){ut.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t=this,e=t.ctx,i=t.options.ticks,n=hi(t._ticks),a=ut.options._parseFont(i);e.font=a.string;var o=i.minRotation||0;if(n.length&&t.options.display&&t.isHorizontal())for(var r,s=ut.longestText(e,a.string,n,t.longestTextCache),l=s,d=t.getPixelForTick(1)-t.getPixelForTick(0)-6;l>d&&o<i.maxRotation;){var u=ut.toRadians(o);if(r=Math.cos(u),Math.sin(u)*s>t.maxHeight){o--;break}o++,l=r*s}t.labelRotation=o},afterCalculateTickRotation:function(){ut.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){ut.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},i=hi(t._ticks),n=t.options,a=n.ticks,o=n.scaleLabel,r=n.gridLines,s=t._isVisible(),l=n.position,d=t.isHorizontal(),u=ut.options._parseFont,h=u(a),c=n.gridLines.tickMarkLength;if(e.width=d?t.isFullWidth()?t.maxWidth-t.margins.left-t.margins.right:t.maxWidth:s&&r.drawTicks?c:0,e.height=d?s&&r.drawTicks?c:0:t.maxHeight,o.display&&s){var f=u(o),g=ut.options.toPadding(o.padding),p=f.lineHeight+g.height;d?e.height+=p:e.width+=p}if(a.display&&s){var m=ut.longestText(t.ctx,h.string,i,t.longestTextCache),v=ut.numberOfLabelLines(i),b=.5*h.size,x=t.options.ticks.padding;if(t._maxLabelLines=v,t.longestLabelWidth=m,d){var y=ut.toRadians(t.labelRotation),k=Math.cos(y),w=Math.sin(y)*m+h.lineHeight*v+b;e.height=Math.min(t.maxHeight,e.height+w+x),t.ctx.font=h.string;var M,_,C=ci(t.ctx,i[0],h.string),S=ci(t.ctx,i[i.length-1],h.string),P=t.getPixelForTick(0)-t.left,I=t.right-t.getPixelForTick(i.length-1);0!==t.labelRotation?(M="bottom"===l?k*C:k*b,_="bottom"===l?k*b:k*S):(M=C/2,_=S/2),t.paddingLeft=Math.max(M-P,0)+3,t.paddingRight=Math.max(_-I,0)+3}else a.mirror?m=0:m+=x+b,e.width=Math.min(t.maxWidth,e.width+m),t.paddingTop=h.size/2,t.paddingBottom=h.size/2}t.handleMargins(),t.width=e.width,t.height=e.height},handleMargins:function(){var t=this;t.margins&&(t.paddingLeft=Math.max(t.paddingLeft-t.margins.left,0),t.paddingTop=Math.max(t.paddingTop-t.margins.top,0),t.paddingRight=Math.max(t.paddingRight-t.margins.right,0),t.paddingBottom=Math.max(t.paddingBottom-t.margins.bottom,0))},afterFit:function(){ut.callback(this.options.afterFit,[this])},isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ut.isNullOrUndef(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},getLabelForIndex:ut.noop,getPixelForValue:ut.noop,getValueForPixel:ut.noop,getPixelForTick:function(t){var e=this,i=e.options.offset;if(e.isHorizontal()){var n=(e.width-(e.paddingLeft+e.paddingRight))/Math.max(e._ticks.length-(i?0:1),1),a=n*t+e.paddingLeft;i&&(a+=n/2);var o=e.left+a;return o+=e.isFullWidth()?e.margins.left:0}var r=e.height-(e.paddingTop+e.paddingBottom);return e.top+t*(r/(e._ticks.length-1))},getPixelForDecimal:function(t){var e=this;if(e.isHorizontal()){var i=(e.width-(e.paddingLeft+e.paddingRight))*t+e.paddingLeft,n=e.left+i;return n+=e.isFullWidth()?e.margins.left:0}return e.top+t*e.height},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,i,n=this,a=n.isHorizontal(),o=n.options.ticks.minor,r=t.length,s=!1,l=o.maxTicksLimit,d=n._tickSize()*(r-1),u=a?n.width-(n.paddingLeft+n.paddingRight):n.height-(n.paddingTop+n.PaddingBottom),h=[];for(d>u&&(s=1+Math.floor(d/u)),r>l&&(s=Math.max(s,1+Math.floor(r/l))),e=0;e<r;e++)i=t[e],s>1&&e%s>0&&delete i.label,h.push(i);return h},_tickSize:function(){var t=this,e=t.isHorizontal(),i=t.options.ticks.minor,n=ut.toRadians(t.labelRotation),a=Math.abs(Math.cos(n)),o=Math.abs(Math.sin(n)),r=i.autoSkipPadding||0,s=t.longestLabelWidth+r||0,l=ut.options._parseFont(i),d=t._maxLabelLines*l.lineHeight+r||0;return e?d*a>s*o?s/a:d/o:d*o<s*a?d/a:s/o},_isVisible:function(){var t,e,i,n=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=n.data.datasets.length;t<e;++t)if(n.isDatasetVisible(t)&&((i=n.getDatasetMeta(t)).xAxisID===this.id||i.yAxisID===this.id))return!0;return!1},draw:function(t){var e=this,i=e.options;if(e._isVisible()){var n,a,o,r=e.chart,s=e.ctx,l=st.global.defaultFontColor,d=i.ticks.minor,u=i.ticks.major||d,h=i.gridLines,c=i.scaleLabel,f=i.position,g=0!==e.labelRotation,p=d.mirror,m=e.isHorizontal(),v=ut.options._parseFont,b=d.display&&d.autoSkip?e._autoSkip(e.getTicks()):e.getTicks(),x=di(d.fontColor,l),y=v(d),k=y.lineHeight,w=di(u.fontColor,l),M=v(u),_=d.padding,C=d.labelOffset,S=h.drawTicks?h.tickMarkLength:0,P=di(c.fontColor,l),I=v(c),A=ut.options.toPadding(c.padding),D=ut.toRadians(e.labelRotation),T=[],F=h.drawBorder?ui(h.lineWidth,0,0):0,L=ut._alignPixel;"top"===f?(n=L(r,e.bottom,F),a=e.bottom-S,o=n-F/2):"bottom"===f?(n=L(r,e.top,F),a=n+F/2,o=e.top+S):"left"===f?(n=L(r,e.right,F),a=e.right-S,o=n-F/2):(n=L(r,e.left,F),a=n+F/2,o=e.left+S);if(ut.each(b,function(n,s){if(!ut.isNullOrUndef(n.label)){var l,d,u,c,v,b,x,y,w,M,P,I,A,R,O,z,B=n.label;s===e.zeroLineIndex&&i.offset===h.offsetGridLines?(l=h.zeroLineWidth,d=h.zeroLineColor,u=h.zeroLineBorderDash||[],c=h.zeroLineBorderDashOffset||0):(l=ui(h.lineWidth,s),d=ui(h.color,s),u=h.borderDash||[],c=h.borderDashOffset||0);var N=ut.isArray(B)?B.length:1,W=function(t,e,i){var n=t.getPixelForTick(e);return i&&(1===t.getTicks().length?n-=t.isHorizontal()?Math.max(n-t.left,t.right-n):Math.max(n-t.top,t.bottom-n):n-=0===e?(t.getPixelForTick(1)-n)/2:(n-t.getPixelForTick(e-1))/2),n}(e,s,h.offsetGridLines);if(m){var V=S+_;W<e.left-1e-7&&(d="rgba(0,0,0,0)"),v=x=w=P=L(r,W,l),b=a,y=o,A=e.getPixelForTick(s)+C,"top"===f?(M=L(r,t.top,F)+F/2,I=t.bottom,O=((g?1:.5)-N)*k,z=g?"left":"center",R=e.bottom-V):(M=t.top,I=L(r,t.bottom,F)-F/2,O=(g?0:.5)*k,z=g?"right":"center",R=e.top+V)}else{var E=(p?0:S)+_;W<e.top-1e-7&&(d="rgba(0,0,0,0)"),v=a,x=o,b=y=M=I=L(r,W,l),R=e.getPixelForTick(s)+C,O=(1-N)*k/2,"left"===f?(w=L(r,t.left,F)+F/2,P=t.right,z=p?"left":"right",A=e.right-E):(w=t.left,P=L(r,t.right,F)-F/2,z=p?"right":"left",A=e.left+E)}T.push({tx1:v,ty1:b,tx2:x,ty2:y,x1:w,y1:M,x2:P,y2:I,labelX:A,labelY:R,glWidth:l,glColor:d,glBorderDash:u,glBorderDashOffset:c,rotation:-1*D,label:B,major:n.major,textOffset:O,textAlign:z})}}),ut.each(T,function(t){var e=t.glWidth,i=t.glColor;if(h.display&&e&&i&&(s.save(),s.lineWidth=e,s.strokeStyle=i,s.setLineDash&&(s.setLineDash(t.glBorderDash),s.lineDashOffset=t.glBorderDashOffset),s.beginPath(),h.drawTicks&&(s.moveTo(t.tx1,t.ty1),s.lineTo(t.tx2,t.ty2)),h.drawOnChartArea&&(s.moveTo(t.x1,t.y1),s.lineTo(t.x2,t.y2)),s.stroke(),s.restore()),d.display){s.save(),s.translate(t.labelX,t.labelY),s.rotate(t.rotation),s.font=t.major?M.string:y.string,s.fillStyle=t.major?w:x,s.textBaseline="middle",s.textAlign=t.textAlign;var n=t.label,a=t.textOffset;if(ut.isArray(n))for(var o=0;o<n.length;++o)s.fillText(""+n[o],0,a),a+=k;else s.fillText(n,0,a);s.restore()}}),c.display){var R,O,z=0,B=I.lineHeight/2;if(m)R=e.left+(e.right-e.left)/2,O="bottom"===f?e.bottom-B-A.bottom:e.top+B+A.top;else{var N="left"===f;R=N?e.left+B+A.top:e.right-B-A.top,O=e.top+(e.bottom-e.top)/2,z=N?-.5*Math.PI:.5*Math.PI}s.save(),s.translate(R,O),s.rotate(z),s.textAlign="center",s.textBaseline="middle",s.fillStyle=P,s.font=I.string,s.fillText(c.labelString,0,0),s.restore()}if(F){var W,V,E,H,j=F,q=ui(h.lineWidth,b.length-1,0);m?(W=L(r,e.left,j)-j/2,V=L(r,e.right,q)+q/2,E=H=n):(E=L(r,e.top,j)-j/2,H=L(r,e.bottom,q)+q/2,W=V=n),s.lineWidth=F,s.strokeStyle=ui(h.color,0),s.beginPath(),s.moveTo(W,E),s.lineTo(V,H),s.stroke()}}}}),gi=fi.extend({getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels},determineDataLimits:function(){var t,e=this,i=e.getLabels();e.minIndex=0,e.maxIndex=i.length-1,void 0!==e.options.ticks.min&&(t=i.indexOf(e.options.ticks.min),e.minIndex=-1!==t?t:e.minIndex),void 0!==e.options.ticks.max&&(t=i.indexOf(e.options.ticks.max),e.maxIndex=-1!==t?t:e.maxIndex),e.min=i[e.minIndex],e.max=i[e.maxIndex]},buildTicks:function(){var t=this,e=t.getLabels();t.ticks=0===t.minIndex&&t.maxIndex===e.length-1?e:e.slice(t.minIndex,t.maxIndex+1)},getLabelForIndex:function(t,e){var i=this,n=i.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===i.id?i.getRightValue(n.data.datasets[e].data[t]):i.ticks[t-i.minIndex]},getPixelForValue:function(t,e){var i,n=this,a=n.options.offset,o=Math.max(n.maxIndex+1-n.minIndex-(a?0:1),1);if(null!=t&&(i=n.isHorizontal()?t.x:t.y),void 0!==i||void 0!==t&&isNaN(e)){t=i||t;var r=n.getLabels().indexOf(t);e=-1!==r?r:e}if(n.isHorizontal()){var s=n.width/o,l=s*(e-n.minIndex);return a&&(l+=s/2),n.left+l}var d=n.height/o,u=d*(e-n.minIndex);return a&&(u+=d/2),n.top+u},getPixelForTick:function(t){return this.getPixelForValue(this.ticks[t],t+this.minIndex,null)},getValueForPixel:function(t){var e=this,i=e.options.offset,n=Math.max(e._ticks.length-(i?0:1),1),a=e.isHorizontal(),o=(a?e.width:e.height)/n;return t-=a?e.left:e.top,i&&(t-=o/2),(t<=0?0:Math.round(t/o))+e.minIndex},getBasePixel:function(){return this.bottom}}),pi={position:"bottom"};gi._defaults=pi;var mi=ut.noop,vi=ut.isNullOrUndef;var bi=fi.extend({getRightValue:function(t){return"string"==typeof t?+t:fi.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var i=ut.sign(t.min),n=ut.sign(t.max);i<0&&n<0?t.max=0:i>0&&n>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,o=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==o&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,i=e.stepSize,n=e.maxTicksLimit;return i?t=Math.ceil(this.max/i)-Math.floor(this.min/i)+1:(t=this._computeTickLimit(),n=n||11),n&&(t=Math.min(n,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:mi,buildTicks:function(){var t=this,e=t.options.ticks,i=t.getTickLimit(),n={maxTicks:i=Math.max(2,i),min:e.min,max:e.max,precision:e.precision,stepSize:ut.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var i,n,a,o,r=[],s=t.stepSize,l=s||1,d=t.maxTicks-1,u=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,p=ut.niceNum((g-f)/d/l)*l;if(p<1e-14&&vi(u)&&vi(h))return[f,g];(o=Math.ceil(g/p)-Math.floor(f/p))>d&&(p=ut.niceNum(o*p/d/l)*l),s||vi(c)?i=Math.pow(10,ut._decimalPlaces(p)):(i=Math.pow(10,c),p=Math.ceil(p*i)/i),n=Math.floor(f/p)*p,a=Math.ceil(g/p)*p,s&&(!vi(u)&&ut.almostWhole(u/p,p/1e3)&&(n=u),!vi(h)&&ut.almostWhole(h/p,p/1e3)&&(a=h)),o=(a-n)/p,o=ut.almostEquals(o,Math.round(o),p/1e3)?Math.round(o):Math.ceil(o),n=Math.round(n*i)/i,a=Math.round(a*i)/i,r.push(vi(u)?n:u);for(var m=1;m<o;++m)r.push(Math.round((n+m*p)*i)/i);return r.push(vi(h)?a:h),r}(n,t);t.handleDirectionalChanges(),t.max=ut.max(a),t.min=ut.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),fi.prototype.convertTicksToLabels.call(t)}}),xi={position:"left",ticks:{callback:li.formatters.linear}},yi=bi.extend({determineDataLimits:function(){var t=this,e=t.options,i=t.chart,n=i.data.datasets,a=t.isHorizontal();function o(e){return a?e.xAxisID===t.id:e.yAxisID===t.id}t.min=null,t.max=null;var r=e.stacked;if(void 0===r&&ut.each(n,function(t,e){if(!r){var n=i.getDatasetMeta(e);i.isDatasetVisible(e)&&o(n)&&void 0!==n.stack&&(r=!0)}}),e.stacked||r){var s={};ut.each(n,function(n,a){var r=i.getDatasetMeta(a),l=[r.type,void 0===e.stacked&&void 0===r.stack?a:"",r.stack].join(".");void 0===s[l]&&(s[l]={positiveValues:[],negativeValues:[]});var d=s[l].positiveValues,u=s[l].negativeValues;i.isDatasetVisible(a)&&o(r)&&ut.each(n.data,function(i,n){var a=+t.getRightValue(i);isNaN(a)||r.data[n].hidden||(d[n]=d[n]||0,u[n]=u[n]||0,e.relativePoints?d[n]=100:a<0?u[n]+=a:d[n]+=a)})}),ut.each(s,function(e){var i=e.positiveValues.concat(e.negativeValues),n=ut.min(i),a=ut.max(i);t.min=null===t.min?n:Math.min(t.min,n),t.max=null===t.max?a:Math.max(t.max,a)})}else ut.each(n,function(e,n){var a=i.getDatasetMeta(n);i.isDatasetVisible(n)&&o(a)&&ut.each(e.data,function(e,i){var n=+t.getRightValue(e);isNaN(n)||a.data[i].hidden||(null===t.min?t.min=n:n<t.min&&(t.min=n),null===t.max?t.max=n:n>t.max&&(t.max=n))})});t.min=isFinite(t.min)&&!isNaN(t.min)?t.min:0,t.max=isFinite(t.max)&&!isNaN(t.max)?t.max:1,this.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=ut.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){var e=this,i=e.start,n=+e.getRightValue(t),a=e.end-i;return e.isHorizontal()?e.left+e.width/a*(n-i):e.bottom-e.height/a*(n-i)},getValueForPixel:function(t){var e=this,i=e.isHorizontal(),n=i?e.width:e.height,a=(i?t-e.left:e.bottom-t)/n;return e.start+(e.end-e.start)*a},getPixelForTick:function(t){return this.getPixelForValue(this.ticksAsNumbers[t])}}),ki=xi;yi._defaults=ki;var wi=ut.valueOrDefault;var Mi={position:"left",ticks:{callback:li.formatters.logarithmic}};function _i(t,e){return ut.isFinite(t)&&t>=0?t:e}var Ci=fi.extend({determineDataLimits:function(){var t=this,e=t.options,i=t.chart,n=i.data.datasets,a=t.isHorizontal();function o(e){return a?e.xAxisID===t.id:e.yAxisID===t.id}t.min=null,t.max=null,t.minNotZero=null;var r=e.stacked;if(void 0===r&&ut.each(n,function(t,e){if(!r){var n=i.getDatasetMeta(e);i.isDatasetVisible(e)&&o(n)&&void 0!==n.stack&&(r=!0)}}),e.stacked||r){var s={};ut.each(n,function(n,a){var r=i.getDatasetMeta(a),l=[r.type,void 0===e.stacked&&void 0===r.stack?a:"",r.stack].join(".");i.isDatasetVisible(a)&&o(r)&&(void 0===s[l]&&(s[l]=[]),ut.each(n.data,function(e,i){var n=s[l],a=+t.getRightValue(e);isNaN(a)||r.data[i].hidden||a<0||(n[i]=n[i]||0,n[i]+=a)}))}),ut.each(s,function(e){if(e.length>0){var i=ut.min(e),n=ut.max(e);t.min=null===t.min?i:Math.min(t.min,i),t.max=null===t.max?n:Math.max(t.max,n)}})}else ut.each(n,function(e,n){var a=i.getDatasetMeta(n);i.isDatasetVisible(n)&&o(a)&&ut.each(e.data,function(e,i){var n=+t.getRightValue(e);isNaN(n)||a.data[i].hidden||n<0||(null===t.min?t.min=n:n<t.min&&(t.min=n),null===t.max?t.max=n:n>t.max&&(t.max=n),0!==n&&(null===t.minNotZero||n<t.minNotZero)&&(t.minNotZero=n))})});this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=_i(e.min,t.min),t.max=_i(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(ut.log10(t.min))-1),t.max=Math.pow(10,Math.floor(ut.log10(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(ut.log10(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(ut.log10(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(ut.log10(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,i=!t.isHorizontal(),n={min:_i(e.min),max:_i(e.max)},a=t.ticks=function(t,e){var i,n,a=[],o=wi(t.min,Math.pow(10,Math.floor(ut.log10(e.min)))),r=Math.floor(ut.log10(e.max)),s=Math.ceil(e.max/Math.pow(10,r));0===o?(i=Math.floor(ut.log10(e.minNotZero)),n=Math.floor(e.minNotZero/Math.pow(10,i)),a.push(o),o=n*Math.pow(10,i)):(i=Math.floor(ut.log10(o)),n=Math.floor(o/Math.pow(10,i)));var l=i<0?Math.pow(10,Math.abs(i)):1;do{a.push(o),10==++n&&(n=1,l=++i>=0?1:l),o=Math.round(n*Math.pow(10,i)*l)/l}while(i<r||i===r&&n<s);var d=wi(t.max,o);return a.push(d),a}(n,t);t.max=ut.max(a),t.min=ut.min(a),e.reverse?(i=!i,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),i&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),fi.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){return this.getPixelForValue(this.tickValues[t])},_getFirstTickValue:function(t){var e=Math.floor(ut.log10(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},getPixelForValue:function(t){var e,i,n,a,o,r=this,s=r.options.ticks,l=s.reverse,d=ut.log10,u=r._getFirstTickValue(r.minNotZero),h=0;return t=+r.getRightValue(t),l?(n=r.end,a=r.start,o=-1):(n=r.start,a=r.end,o=1),r.isHorizontal()?(e=r.width,i=l?r.right:r.left):(e=r.height,o*=-1,i=l?r.top:r.bottom),t!==n&&(0===n&&(e-=h=wi(s.fontSize,st.global.defaultFontSize),n=u),0!==t&&(h+=e/(d(a)-d(n))*(d(t)-d(n))),i+=o*h),i},getValueForPixel:function(t){var e,i,n,a,o=this,r=o.options.ticks,s=r.reverse,l=ut.log10,d=o._getFirstTickValue(o.minNotZero);if(s?(i=o.end,n=o.start):(i=o.start,n=o.end),o.isHorizontal()?(e=o.width,a=s?o.right-t:t-o.left):(e=o.height,a=s?t-o.top:o.bottom-t),a!==i){if(0===i){var u=wi(r.fontSize,st.global.defaultFontSize);a-=u,e-=u,i=d}a*=l(n)-l(i),a/=e,a=Math.pow(10,l(i)+a)}return a}}),Si=Mi;Ci._defaults=Si;var Pi=ut.valueOrDefault,Ii=ut.valueAtIndexOrDefault,Ai=ut.options.resolve,Di={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0, 0, 0, 0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:li.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Ti(t){var e=t.options;return e.angleLines.display||e.pointLabels.display?t.chart.data.labels.length:0}function Fi(t){var e=t.ticks;return e.display&&t.display?Pi(e.fontSize,st.global.defaultFontSize)+2*e.backdropPaddingY:0}function Li(t,e,i,n,a){return t===n||t===a?{start:e-i/2,end:e+i/2}:t<n||t>a?{start:e-i,end:e}:{start:e,end:e+i}}function Ri(t){return 0===t||180===t?"center":t<180?"left":"right"}function Oi(t,e,i,n){var a,o,r=i.y+n/2;if(ut.isArray(e))for(a=0,o=e.length;a<o;++a)t.fillText(e[a],i.x,r),r+=n;else t.fillText(e,i.x,r)}function zi(t,e,i){90===t||270===t?i.y-=e.h/2:(t>270||t<90)&&(i.y-=e.h)}function Bi(t){return ut.isNumber(t)?t:0}var Ni=bi.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Fi(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,i=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;ut.each(e.data.datasets,function(a,o){if(e.isDatasetVisible(o)){var r=e.getDatasetMeta(o);ut.each(a.data,function(e,a){var o=+t.getRightValue(e);isNaN(o)||r.data[a].hidden||(i=Math.min(o,i),n=Math.max(o,n))})}}),t.min=i===Number.POSITIVE_INFINITY?0:i,t.max=n===Number.NEGATIVE_INFINITY?0:n,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Fi(this.options))},convertTicksToLabels:function(){var t=this;bi.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map(t.options.pointLabels.callback,t)},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,i,n,a=ut.options._parseFont(t.options.pointLabels),o={l:0,r:t.width,t:0,b:t.height-t.paddingTop},r={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,d,u=Ti(t);for(e=0;e<u;e++){n=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,d=t.pointLabels[e]||"",i=ut.isArray(d)?{w:ut.longestText(s,s.font,d),h:d.length*l}:{w:s.measureText(d).width,h:l},t._pointLabelSizes[e]=i;var h=t.getIndexAngle(e),c=ut.toDegrees(h)%360,f=Li(c,n.x,i.w,0,180),g=Li(c,n.y,i.h,90,270);f.start<o.l&&(o.l=f.start,r.l=h),f.end>o.r&&(o.r=f.end,r.r=h),g.start<o.t&&(o.t=g.start,r.t=h),g.end>o.b&&(o.b=g.end,r.b=h)}t.setReductions(t.drawingArea,o,r)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,i){var n=this,a=e.l/Math.sin(i.l),o=Math.max(e.r-n.width,0)/Math.sin(i.r),r=-e.t/Math.cos(i.t),s=-Math.max(e.b-(n.height-n.paddingTop),0)/Math.cos(i.b);a=Bi(a),o=Bi(o),r=Bi(r),s=Bi(s),n.drawingArea=Math.min(Math.floor(t-(a+o)/2),Math.floor(t-(r+s)/2)),n.setCenterPoint(a,o,r,s)},setCenterPoint:function(t,e,i,n){var a=this,o=a.width-e-a.drawingArea,r=t+a.drawingArea,s=i+a.drawingArea,l=a.height-a.paddingTop-n-a.drawingArea;a.xCenter=Math.floor((r+o)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){return t*(2*Math.PI/Ti(this))+(this.chart.options&&this.chart.options.startAngle?this.chart.options.startAngle:0)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(null===t)return 0;var i=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*i:(t-e.min)*i},getPointPosition:function(t,e){var i=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(i)*e+this.xCenter,y:Math.sin(i)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(){var t=this.min,e=this.max;return this.getPointPositionForValue(0,this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0)},draw:function(){var t=this,e=t.options,i=e.gridLines,n=e.ticks;if(e.display){var a=t.ctx,o=this.getIndexAngle(0),r=ut.options._parseFont(n);(e.angleLines.display||e.pointLabels.display)&&function(t){var e=t.ctx,i=t.options,n=i.angleLines,a=i.gridLines,o=i.pointLabels,r=Pi(n.lineWidth,a.lineWidth),s=Pi(n.color,a.color),l=Fi(i);e.save(),e.lineWidth=r,e.strokeStyle=s,e.setLineDash&&(e.setLineDash(Ai([n.borderDash,a.borderDash,[]])),e.lineDashOffset=Ai([n.borderDashOffset,a.borderDashOffset,0]));var d=t.getDistanceFromCenterForValue(i.ticks.reverse?t.min:t.max),u=ut.options._parseFont(o);e.font=u.string,e.textBaseline="middle";for(var h=Ti(t)-1;h>=0;h--){if(n.display&&r&&s){var c=t.getPointPosition(h,d);e.beginPath(),e.moveTo(t.xCenter,t.yCenter),e.lineTo(c.x,c.y),e.stroke()}if(o.display){var f=0===h?l/2:0,g=t.getPointPosition(h,d+f+5),p=Ii(o.fontColor,h,st.global.defaultFontColor);e.fillStyle=p;var m=t.getIndexAngle(h),v=ut.toDegrees(m);e.textAlign=Ri(v),zi(v,t._pointLabelSizes[h],g),Oi(e,t.pointLabels[h]||"",g,u.lineHeight)}}e.restore()}(t),ut.each(t.ticks,function(e,s){if(s>0||n.reverse){var l=t.getDistanceFromCenterForValue(t.ticksAsNumbers[s]);if(i.display&&0!==s&&function(t,e,i,n){var a,o=t.ctx,r=e.circular,s=Ti(t),l=Ii(e.color,n-1),d=Ii(e.lineWidth,n-1);if((r||s)&&l&&d){if(o.save(),o.strokeStyle=l,o.lineWidth=d,o.setLineDash&&(o.setLineDash(e.borderDash||[]),o.lineDashOffset=e.borderDashOffset||0),o.beginPath(),r)o.arc(t.xCenter,t.yCenter,i,0,2*Math.PI);else{a=t.getPointPosition(0,i),o.moveTo(a.x,a.y);for(var u=1;u<s;u++)a=t.getPointPosition(u,i),o.lineTo(a.x,a.y)}o.closePath(),o.stroke(),o.restore()}}(t,i,l,s),n.display){var d=Pi(n.fontColor,st.global.defaultFontColor);if(a.font=r.string,a.save(),a.translate(t.xCenter,t.yCenter),a.rotate(o),n.showLabelBackdrop){var u=a.measureText(e).width;a.fillStyle=n.backdropColor,a.fillRect(-u/2-n.backdropPaddingX,-l-r.size/2-n.backdropPaddingY,u+2*n.backdropPaddingX,r.size+2*n.backdropPaddingY)}a.textAlign="center",a.textBaseline="middle",a.fillStyle=d,a.fillText(e,0,-l),a.restore()}}})}}}),Wi=Di;Ni._defaults=Wi;var Vi=ut.valueOrDefault,Ei=Number.MIN_SAFE_INTEGER||-9007199254740991,Hi=Number.MAX_SAFE_INTEGER||9007199254740991,ji={millisecond:{common:!0,size:1,steps:[1,2,5,10,20,50,100,250,500]},second:{common:!0,size:1e3,steps:[1,2,5,10,15,30]},minute:{common:!0,size:6e4,steps:[1,2,5,10,15,30]},hour:{common:!0,size:36e5,steps:[1,2,3,6,12]},day:{common:!0,size:864e5,steps:[1,2,5]},week:{common:!1,size:6048e5,steps:[1,2,3,4]},month:{common:!0,size:2628e6,steps:[1,2,3]},quarter:{common:!1,size:7884e6,steps:[1,2,3,4]},year:{common:!0,size:3154e7}},qi=Object.keys(ji);function Yi(t,e){return t-e}function Ui(t){var e,i,n,a={},o=[];for(e=0,i=t.length;e<i;++e)a[n=t[e]]||(a[n]=!0,o.push(n));return o}function Xi(t,e,i,n){var a=function(t,e,i){for(var n,a,o,r=0,s=t.length-1;r>=0&&r<=s;){if(a=t[(n=r+s>>1)-1]||null,o=t[n],!a)return{lo:null,hi:o};if(o[e]<i)r=n+1;else{if(!(a[e]>i))return{lo:a,hi:o};s=n-1}}return{lo:o,hi:null}}(t,e,i),o=a.lo?a.hi?a.lo:t[t.length-2]:t[0],r=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=r[e]-o[e],l=s?(i-o[e])/s:0,d=(r[n]-o[n])*l;return o[n]+d}function Ki(t,e){var i=t._adapter,n=t.options.time,a=n.parser,o=a||n.format,r=e;return"function"==typeof a&&(r=a(r)),ut.isFinite(r)||(r="string"==typeof o?i.parse(r,o):i.parse(r)),null!==r?+r:(a||"function"!=typeof o||(r=o(e),ut.isFinite(r)||(r=i.parse(r))),r)}function Gi(t,e){if(ut.isNullOrUndef(e))return null;var i=t.options.time,n=Ki(t,t.getRightValue(e));return null===n?n:(i.round&&(n=+t._adapter.startOf(n,i.round)),n)}function Zi(t){for(var e=qi.indexOf(t)+1,i=qi.length;e<i;++e)if(ji[qi[e]].common)return qi[e]}function $i(t,e,i,n){var a,o=t._adapter,r=t.options,s=r.time,l=s.unit||function(t,e,i,n){var a,o,r,s=qi.length;for(a=qi.indexOf(t);a<s-1;++a)if(r=(o=ji[qi[a]]).steps?o.steps[o.steps.length-1]:Hi,o.common&&Math.ceil((i-e)/(r*o.size))<=n)return qi[a];return qi[s-1]}(s.minUnit,e,i,n),d=Zi(l),u=Vi(s.stepSize,s.unitStepSize),h="week"===l&&s.isoWeekday,c=r.ticks.major.enabled,f=ji[l],g=e,p=i,m=[];for(u||(u=function(t,e,i,n){var a,o,r,s=e-t,l=ji[i],d=l.size,u=l.steps;if(!u)return Math.ceil(s/(n*d));for(a=0,o=u.length;a<o&&(r=u[a],!(Math.ceil(s/(d*r))<=n));++a);return r}(e,i,l,n)),h&&(g=+o.startOf(g,"isoWeek",h),p=+o.startOf(p,"isoWeek",h)),g=+o.startOf(g,h?"day":l),(p=+o.startOf(p,h?"day":l))<i&&(p=+o.add(p,1,l)),a=g,c&&d&&!h&&!s.round&&(a=+o.startOf(a,d),a=+o.add(a,~~((g-a)/(f.size*u))*u,l));a<p;a=+o.add(a,u,l))m.push(+a);return m.push(+a),m}var Ji=fi.extend({initialize:function(){this.mergeTicksOptions(),fi.prototype.initialize.call(this)},update:function(){var t=this.options,e=t.time||(t.time={}),i=this._adapter=new si._date(t.adapters.date);return e.format&&console.warn("options.time.format is deprecated and replaced by options.time.parser."),ut.mergeIf(e.displayFormats,i.formats()),fi.prototype.update.apply(this,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),fi.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,i,n,a,o,r=this,s=r.chart,l=r._adapter,d=r.options.time,u=d.unit||"day",h=Hi,c=Ei,f=[],g=[],p=[],m=s.data.labels||[];for(t=0,i=m.length;t<i;++t)p.push(Gi(r,m[t]));for(t=0,i=(s.data.datasets||[]).length;t<i;++t)if(s.isDatasetVisible(t))if(a=s.data.datasets[t].data,ut.isObject(a[0]))for(g[t]=[],e=0,n=a.length;e<n;++e)o=Gi(r,a[e]),f.push(o),g[t][e]=o;else{for(e=0,n=p.length;e<n;++e)f.push(p[e]);g[t]=p.slice(0)}else g[t]=[];p.length&&(p=Ui(p).sort(Yi),h=Math.min(h,p[0]),c=Math.max(c,p[p.length-1])),f.length&&(f=Ui(f).sort(Yi),h=Math.min(h,f[0]),c=Math.max(c,f[f.length-1])),h=Gi(r,d.min)||h,c=Gi(r,d.max)||c,h=h===Hi?+l.startOf(Date.now(),u):h,c=c===Ei?+l.endOf(Date.now(),u)+1:c,r.min=Math.min(h,c),r.max=Math.max(h+1,c),r._horizontal=r.isHorizontal(),r._table=[],r._timestamps={data:f,datasets:g,labels:p}},buildTicks:function(){var t,e,i,n=this,a=n.min,o=n.max,r=n.options,s=r.time,l=[],d=[];switch(r.ticks.source){case"data":l=n._timestamps.data;break;case"labels":l=n._timestamps.labels;break;case"auto":default:l=$i(n,a,o,n.getLabelCapacity(a))}for("ticks"===r.bounds&&l.length&&(a=l[0],o=l[l.length-1]),a=Gi(n,s.min)||a,o=Gi(n,s.max)||o,t=0,e=l.length;t<e;++t)(i=l[t])>=a&&i<=o&&d.push(i);return n.min=a,n.max=o,n._unit=s.unit||function(t,e,i,n,a){var o,r;for(o=qi.length-1;o>=qi.indexOf(i);o--)if(r=qi[o],ji[r].common&&t._adapter.diff(a,n,r)>=e.length)return r;return qi[i?qi.indexOf(i):0]}(n,d,s.minUnit,n.min,n.max),n._majorUnit=Zi(n._unit),n._table=function(t,e,i,n){if("linear"===n||!t.length)return[{time:e,pos:0},{time:i,pos:1}];var a,o,r,s,l,d=[],u=[e];for(a=0,o=t.length;a<o;++a)(s=t[a])>e&&s<i&&u.push(s);for(u.push(i),a=0,o=u.length;a<o;++a)l=u[a+1],r=u[a-1],s=u[a],void 0!==r&&void 0!==l&&Math.round((l+r)/2)===s||d.push({time:s,pos:a/(o-1)});return d}(n._timestamps.data,a,o,r.distribution),n._offsets=function(t,e,i,n,a){var o,r,s=0,l=0;return a.offset&&e.length&&(a.time.min||(o=Xi(t,"time",e[0],"pos"),s=1===e.length?1-o:(Xi(t,"time",e[1],"pos")-o)/2),a.time.max||(r=Xi(t,"time",e[e.length-1],"pos"),l=1===e.length?r:(r-Xi(t,"time",e[e.length-2],"pos"))/2)),{start:s,end:l}}(n._table,d,0,0,r),r.ticks.reverse&&d.reverse(),function(t,e,i){var n,a,o,r,s=[];for(n=0,a=e.length;n<a;++n)o=e[n],r=!!i&&o===+t._adapter.startOf(o,i),s.push({value:o,major:r});return s}(n,d,n._majorUnit)},getLabelForIndex:function(t,e){var i=this,n=i._adapter,a=i.chart.data,o=i.options.time,r=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return ut.isObject(s)&&(r=i.getRightValue(s)),o.tooltipFormat?n.format(Ki(i,r),o.tooltipFormat):"string"==typeof r?r:n.format(Ki(i,r),o.displayFormats.datetime)},tickFormatFunction:function(t,e,i,n){var a=this._adapter,o=this.options,r=o.time.displayFormats,s=r[this._unit],l=this._majorUnit,d=r[l],u=+a.startOf(t,l),h=o.ticks.major,c=h.enabled&&l&&d&&t===u,f=a.format(t,n||(c?d:s)),g=c?h:o.ticks.minor,p=Vi(g.callback,g.userCallback);return p?p(f,e,i):f},convertTicksToLabels:function(t){var e,i,n=[];for(e=0,i=t.length;e<i;++e)n.push(this.tickFormatFunction(t[e].value,e,t));return n},getPixelForOffset:function(t){var e=this,i=e.options.ticks.reverse,n=e._horizontal?e.width:e.height,a=e._horizontal?i?e.right:e.left:i?e.bottom:e.top,o=Xi(e._table,"time",t,"pos"),r=n*(e._offsets.start+o)/(e._offsets.start+1+e._offsets.end);return i?a-r:a+r},getPixelForValue:function(t,e,i){var n=null;if(void 0!==e&&void 0!==i&&(n=this._timestamps.datasets[i][e]),null===n&&(n=Gi(this,t)),null!==n)return this.getPixelForOffset(n)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this,i=e._horizontal?e.width:e.height,n=e._horizontal?e.left:e.top,a=(i?(t-n)/i:0)*(e._offsets.start+1+e._offsets.start)-e._offsets.end,o=Xi(e._table,"pos",a,"time");return e._adapter._create(o)},getLabelWidth:function(t){var e=this.options.ticks,i=this.ctx.measureText(t).width,n=ut.toRadians(e.maxRotation),a=Math.cos(n),o=Math.sin(n);return i*a+Vi(e.fontSize,st.global.defaultFontSize)*o},getLabelCapacity:function(t){var e=this,i=e.options.time.displayFormats.millisecond,n=e.tickFormatFunction(t,0,[],i),a=e.getLabelWidth(n),o=e.isHorizontal()?e.width:e.height,r=Math.floor(o/a);return r>0?r:1}}),Qi={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,format:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};Ji._defaults=Qi;var tn={category:gi,linear:yi,logarithmic:Ci,radialLinear:Ni,time:Ji},en={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};si._date.override("function"==typeof t?{_id:"moment",formats:function(){return en},parse:function(e,i){return"string"==typeof e&&"string"==typeof i?e=t(e,i):e instanceof t||(e=t(e)),e.isValid()?e.valueOf():null},format:function(e,i){return t(e).format(i)},add:function(e,i,n){return t(e).add(i,n).valueOf()},diff:function(e,i,n){return t.duration(t(e).diff(t(i))).as(n)},startOf:function(e,i,n){return e=t(e),"isoWeek"===i?e.isoWeekday(n).valueOf():e.startOf(i).valueOf()},endOf:function(e,i){return t(e).endOf(i).valueOf()},_create:function(e){return t(e)}}:{}),st._set("global",{plugins:{filler:{propagate:!0}}});var nn={dataset:function(t){var e=t.fill,i=t.chart,n=i.getDatasetMeta(e),a=n&&i.isDatasetVisible(e)&&n.dataset._children||[],o=a.length||0;return o?function(t,e){return e<o&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,i=e?e.x:null,n=e?e.y:null;return function(t){return{x:null===i?t.x:i,y:null===n?t.y:n}}}};function an(t,e,i){var n,a=t._model||{},o=a.fill;if(void 0===o&&(o=!!a.backgroundColor),!1===o||null===o)return!1;if(!0===o)return"origin";if(n=parseFloat(o,10),isFinite(n)&&Math.floor(n)===n)return"-"!==o[0]&&"+"!==o[0]||(n=e+n),!(n===e||n<0||n>=i)&&n;switch(o){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return o;default:return!1}}function on(t){var e,i=t.el._model||{},n=t.el._scale||{},a=t.fill,o=null;if(isFinite(a))return null;if("start"===a?o=void 0===i.scaleBottom?n.bottom:i.scaleBottom:"end"===a?o=void 0===i.scaleTop?n.top:i.scaleTop:void 0!==i.scaleZero?o=i.scaleZero:n.getBasePosition?o=n.getBasePosition():n.getBasePixel&&(o=n.getBasePixel()),null!=o){if(void 0!==o.x&&void 0!==o.y)return o;if(ut.isFinite(o))return{x:(e=n.isHorizontal())?o:null,y:e?null:o}}return null}function rn(t,e,i){var n,a=t[e].fill,o=[e];if(!i)return a;for(;!1!==a&&-1===o.indexOf(a);){if(!isFinite(a))return a;if(!(n=t[a]))return!1;if(n.visible)return a;o.push(a),a=n.fill}return!1}function sn(t){var e=t.fill,i="dataset";return!1===e?null:(isFinite(e)||(i="boundary"),nn[i](t))}function ln(t){return t&&!t.skip}function dn(t,e,i,n,a){var o;if(n&&a){for(t.moveTo(e[0].x,e[0].y),o=1;o<n;++o)ut.canvas.lineTo(t,e[o-1],e[o]);for(t.lineTo(i[a-1].x,i[a-1].y),o=a-1;o>0;--o)ut.canvas.lineTo(t,i[o],i[o-1],!0)}}var un={id:"filler",afterDatasetsUpdate:function(t,e){var i,n,a,o,r=(t.data.datasets||[]).length,s=e.propagate,l=[];for(n=0;n<r;++n)o=null,(a=(i=t.getDatasetMeta(n)).dataset)&&a._model&&a instanceof Wt.Line&&(o={visible:t.isDatasetVisible(n),fill:an(a,n,r),chart:t,el:a}),i.$filler=o,l.push(o);for(n=0;n<r;++n)(o=l[n])&&(o.fill=rn(l,n,s),o.boundary=on(o),o.mapper=sn(o))},beforeDatasetDraw:function(t,e){var i=e.meta.$filler;if(i){var n=t.ctx,a=i.el,o=a._view,r=a._children||[],s=i.mapper,l=o.backgroundColor||st.global.defaultColor;s&&l&&r.length&&(ut.canvas.clipArea(n,t.chartArea),function(t,e,i,n,a,o){var r,s,l,d,u,h,c,f=e.length,g=n.spanGaps,p=[],m=[],v=0,b=0;for(t.beginPath(),r=0,s=f+!!o;r<s;++r)u=i(d=e[l=r%f]._view,l,n),h=ln(d),c=ln(u),h&&c?(v=p.push(d),b=m.push(u)):v&&b&&(g?(h&&p.push(d),c&&m.push(u)):(dn(t,p,m,v,b),v=b=0,p=[],m=[]));dn(t,p,m,v,b),t.closePath(),t.fillStyle=a,t.fill()}(n,r,s,o,l,a._loop),ut.canvas.unclipArea(n))}}},hn=ut.noop,cn=ut.valueOrDefault;function fn(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}st._set("global",{legend:{display:!0,position:"top",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var i=e.datasetIndex,n=this.chart,a=n.getDatasetMeta(i);a.hidden=null===a.hidden?!n.data.datasets[i].hidden:null,n.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data;return ut.isArray(e.datasets)?e.datasets.map(function(e,i){return{text:e.label,fillStyle:ut.isArray(e.backgroundColor)?e.backgroundColor[0]:e.backgroundColor,hidden:!t.isDatasetVisible(i),lineCap:e.borderCapStyle,lineDash:e.borderDash,lineDashOffset:e.borderDashOffset,lineJoin:e.borderJoinStyle,lineWidth:e.borderWidth,strokeStyle:e.borderColor,pointStyle:e.pointStyle,datasetIndex:i}},this):[]}}},legendCallback:function(t){var e=[];e.push('<ul class="'+t.id+'-legend">');for(var i=0;i<t.data.datasets.length;i++)e.push('<li><span style="background-color:'+t.data.datasets[i].backgroundColor+'"></span>'),t.data.datasets[i].label&&e.push(t.data.datasets[i].label),e.push("</li>");return e.push("</ul>"),e.join("")}});var gn=pt.extend({initialize:function(t){ut.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:hn,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:hn,beforeSetDimensions:hn,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:hn,beforeBuildLabels:hn,buildLabels:function(){var t=this,e=t.options.labels||{},i=ut.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(i=i.filter(function(i){return e.filter(i,t.chart.data)})),t.options.reverse&&i.reverse(),t.legendItems=i},afterBuildLabels:hn,beforeFit:hn,fit:function(){var t=this,e=t.options,i=e.labels,n=e.display,a=t.ctx,o=ut.options._parseFont(i),r=o.size,s=t.legendHitBoxes=[],l=t.minSize,d=t.isHorizontal();if(d?(l.width=t.maxWidth,l.height=n?10:0):(l.width=n?10:0,l.height=t.maxHeight),n)if(a.font=o.string,d){var u=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="top",ut.each(t.legendItems,function(t,e){var n=fn(i,r)+r/2+a.measureText(t.text).width;(0===e||u[u.length-1]+n+i.padding>l.width)&&(h+=r+i.padding,u[u.length-(e>0?0:1)]=i.padding),s[e]={left:0,top:0,width:n,height:r},u[u.length-1]+=n+i.padding}),l.height+=h}else{var c=i.padding,f=t.columnWidths=[],g=i.padding,p=0,m=0,v=r+c;ut.each(t.legendItems,function(t,e){var n=fn(i,r)+r/2+a.measureText(t.text).width;e>0&&m+v>l.height-c&&(g+=p+i.padding,f.push(p),p=0,m=0),p=Math.max(p,n),m+=v,s[e]={left:0,top:0,width:n,height:r}}),g+=p,f.push(p),l.width+=g}t.width=l.width,t.height=l.height},afterFit:hn,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,i=e.labels,n=st.global,a=n.defaultColor,o=n.elements.line,r=t.width,s=t.lineWidths;if(e.display){var l,d=t.ctx,u=cn(i.fontColor,n.defaultFontColor),h=ut.options._parseFont(i),c=h.size;d.textAlign="left",d.textBaseline="middle",d.lineWidth=.5,d.strokeStyle=u,d.fillStyle=u,d.font=h.string;var f=fn(i,c),g=t.legendHitBoxes,p=t.isHorizontal();l=p?{x:t.left+(r-s[0])/2+i.padding,y:t.top+i.padding,line:0}:{x:t.left+i.padding,y:t.top+i.padding,line:0};var m=c+i.padding;ut.each(t.legendItems,function(n,u){var h=d.measureText(n.text).width,v=f+c/2+h,b=l.x,x=l.y;p?u>0&&b+v+i.padding>t.left+t.minSize.width&&(x=l.y+=m,l.line++,b=l.x=t.left+(r-s[l.line])/2+i.padding):u>0&&x+m>t.top+t.minSize.height&&(b=l.x=b+t.columnWidths[l.line]+i.padding,x=l.y=t.top+i.padding,l.line++),function(t,i,n){if(!(isNaN(f)||f<=0)){d.save();var r=cn(n.lineWidth,o.borderWidth);if(d.fillStyle=cn(n.fillStyle,a),d.lineCap=cn(n.lineCap,o.borderCapStyle),d.lineDashOffset=cn(n.lineDashOffset,o.borderDashOffset),d.lineJoin=cn(n.lineJoin,o.borderJoinStyle),d.lineWidth=r,d.strokeStyle=cn(n.strokeStyle,a),d.setLineDash&&d.setLineDash(cn(n.lineDash,o.borderDash)),e.labels&&e.labels.usePointStyle){var s=f*Math.SQRT2/2,l=t+f/2,u=i+c/2;ut.canvas.drawPoint(d,n.pointStyle,s,l,u)}else 0!==r&&d.strokeRect(t,i,f,c),d.fillRect(t,i,f,c);d.restore()}}(b,x,n),g[u].left=b,g[u].top=x,function(t,e,i,n){var a=c/2,o=f+a+t,r=e+a;d.fillText(i.text,o,r),i.hidden&&(d.beginPath(),d.lineWidth=2,d.moveTo(o,r),d.lineTo(o+n,r),d.stroke())}(b,x,n,h),p?l.x+=v+i.padding:l.y+=m})}},_getLegendItemAt:function(t,e){var i,n,a,o=this;if(t>=o.left&&t<=o.right&&e>=o.top&&e<=o.bottom)for(a=o.legendHitBoxes,i=0;i<a.length;++i)if(t>=(n=a[i]).left&&t<=n.left+n.width&&e>=n.top&&e<=n.top+n.height)return o.legendItems[i];return null},handleEvent:function(t){var e,i=this,n=i.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!n.onHover&&!n.onLeave)return}else{if("click"!==a)return;if(!n.onClick)return}e=i._getLegendItemAt(t.x,t.y),"click"===a?e&&n.onClick&&n.onClick.call(i,t.native,e):(n.onLeave&&e!==i._hoveredItem&&(i._hoveredItem&&n.onLeave.call(i,t.native,i._hoveredItem),i._hoveredItem=e),n.onHover&&e&&n.onHover.call(i,t.native,e))}});function pn(t,e){var i=new gn({ctx:t.ctx,options:e,chart:t});ke.configure(t,i,e),ke.addBox(t,i),t.legend=i}var mn={id:"legend",_element:gn,beforeInit:function(t){var e=t.options.legend;e&&pn(t,e)},beforeUpdate:function(t){var e=t.options.legend,i=t.legend;e?(ut.mergeIf(e,st.global.legend),i?(ke.configure(t,i,e),i.options=e):pn(t,e)):i&&(ke.removeBox(t,i),delete t.legend)},afterEvent:function(t,e){var i=t.legend;i&&i.handleEvent(e)}},vn=ut.noop;st._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var bn=pt.extend({initialize:function(t){ut.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:vn,update:function(t,e,i){var n=this;return n.beforeUpdate(),n.maxWidth=t,n.maxHeight=e,n.margins=i,n.beforeSetDimensions(),n.setDimensions(),n.afterSetDimensions(),n.beforeBuildLabels(),n.buildLabels(),n.afterBuildLabels(),n.beforeFit(),n.fit(),n.afterFit(),n.afterUpdate(),n.minSize},afterUpdate:vn,beforeSetDimensions:vn,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:vn,beforeBuildLabels:vn,buildLabels:vn,afterBuildLabels:vn,beforeFit:vn,fit:function(){var t=this,e=t.options,i=e.display,n=t.minSize,a=ut.isArray(e.text)?e.text.length:1,o=ut.options._parseFont(e),r=i?a*o.lineHeight+2*e.padding:0;t.isHorizontal()?(n.width=t.maxWidth,n.height=r):(n.width=r,n.height=t.maxHeight),t.width=n.width,t.height=n.height},afterFit:vn,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,i=t.options;if(i.display){var n,a,o,r=ut.options._parseFont(i),s=r.lineHeight,l=s/2+i.padding,d=0,u=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=ut.valueOrDefault(i.fontColor,st.global.defaultFontColor),e.font=r.string,t.isHorizontal()?(a=h+(f-h)/2,o=u+l,n=f-h):(a="left"===i.position?h+l:f-l,o=u+(c-u)/2,n=c-u,d=Math.PI*("left"===i.position?-.5:.5)),e.save(),e.translate(a,o),e.rotate(d),e.textAlign="center",e.textBaseline="middle";var g=i.text;if(ut.isArray(g))for(var p=0,m=0;m<g.length;++m)e.fillText(g[m],0,p,n),p+=s;else e.fillText(g,0,0,n);e.restore()}}});function xn(t,e){var i=new bn({ctx:t.ctx,options:e,chart:t});ke.configure(t,i,e),ke.addBox(t,i),t.titleBlock=i}var yn={},kn=un,wn=mn,Mn={id:"title",_element:bn,beforeInit:function(t){var e=t.options.title;e&&xn(t,e)},beforeUpdate:function(t){var e=t.options.title,i=t.titleBlock;e?(ut.mergeIf(e,st.global.title),i?(ke.configure(t,i,e),i.options=e):xn(t,e)):i&&(ke.removeBox(t,i),delete t.titleBlock)}};for(var _n in yn.filler=kn,yn.legend=wn,yn.title=Mn,ai.helpers=ut,function(){function t(t,e,i){var n;return"string"==typeof t?(n=parseInt(t,10),-1!==t.indexOf("%")&&(n=n/100*e.parentNode[i])):n=t,n}function e(t){return null!=t&&"none"!==t}function i(i,n,a){var o=document.defaultView,r=ut._getParentNode(i),s=o.getComputedStyle(i)[n],l=o.getComputedStyle(r)[n],d=e(s),u=e(l),h=Number.POSITIVE_INFINITY;return d||u?Math.min(d?t(s,i,a):h,u?t(l,r,a):h):"none"}ut.where=function(t,e){if(ut.isArray(t)&&Array.prototype.filter)return t.filter(e);var i=[];return ut.each(t,function(t){e(t)&&i.push(t)}),i},ut.findIndex=Array.prototype.findIndex?function(t,e,i){return t.findIndex(e,i)}:function(t,e,i){i=void 0===i?t:i;for(var n=0,a=t.length;n<a;++n)if(e.call(i,t[n],n,t))return n;return-1},ut.findNextWhere=function(t,e,i){ut.isNullOrUndef(i)&&(i=-1);for(var n=i+1;n<t.length;n++){var a=t[n];if(e(a))return a}},ut.findPreviousWhere=function(t,e,i){ut.isNullOrUndef(i)&&(i=t.length);for(var n=i-1;n>=0;n--){var a=t[n];if(e(a))return a}},ut.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},ut.almostEquals=function(t,e,i){return Math.abs(t-e)<i},ut.almostWhole=function(t,e){var i=Math.round(t);return i-e<t&&i+e>t},ut.max=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.max(t,e)},Number.NEGATIVE_INFINITY)},ut.min=function(t){return t.reduce(function(t,e){return isNaN(e)?t:Math.min(t,e)},Number.POSITIVE_INFINITY)},ut.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0==(t=+t)||isNaN(t)?t:t>0?1:-1},ut.log10=Math.log10?function(t){return Math.log10(t)}:function(t){var e=Math.log(t)*Math.LOG10E,i=Math.round(e);return t===Math.pow(10,i)?i:e},ut.toRadians=function(t){return t*(Math.PI/180)},ut.toDegrees=function(t){return t*(180/Math.PI)},ut._decimalPlaces=function(t){if(ut.isFinite(t)){for(var e=1,i=0;Math.round(t*e)/e!==t;)e*=10,i++;return i}},ut.getAngleFromPoint=function(t,e){var i=e.x-t.x,n=e.y-t.y,a=Math.sqrt(i*i+n*n),o=Math.atan2(n,i);return o<-.5*Math.PI&&(o+=2*Math.PI),{angle:o,distance:a}},ut.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},ut.aliasPixel=function(t){return t%2==0?0:.5},ut._alignPixel=function(t,e,i){var n=t.currentDevicePixelRatio,a=i/2;return Math.round((e-a)*n)/n+a},ut.splineCurve=function(t,e,i,n){var a=t.skip?e:t,o=e,r=i.skip?e:i,s=Math.sqrt(Math.pow(o.x-a.x,2)+Math.pow(o.y-a.y,2)),l=Math.sqrt(Math.pow(r.x-o.x,2)+Math.pow(r.y-o.y,2)),d=s/(s+l),u=l/(s+l),h=n*(d=isNaN(d)?0:d),c=n*(u=isNaN(u)?0:u);return{previous:{x:o.x-h*(r.x-a.x),y:o.y-h*(r.y-a.y)},next:{x:o.x+c*(r.x-a.x),y:o.y+c*(r.y-a.y)}}},ut.EPSILON=Number.EPSILON||1e-14,ut.splineCurveMonotone=function(t){var e,i,n,a,o,r,s,l,d,u=(t||[]).map(function(t){return{model:t._model,deltaK:0,mK:0}}),h=u.length;for(e=0;e<h;++e)if(!(n=u[e]).model.skip){if(i=e>0?u[e-1]:null,(a=e<h-1?u[e+1]:null)&&!a.model.skip){var c=a.model.x-n.model.x;n.deltaK=0!==c?(a.model.y-n.model.y)/c:0}!i||i.model.skip?n.mK=n.deltaK:!a||a.model.skip?n.mK=i.deltaK:this.sign(i.deltaK)!==this.sign(n.deltaK)?n.mK=0:n.mK=(i.deltaK+n.deltaK)/2}for(e=0;e<h-1;++e)n=u[e],a=u[e+1],n.model.skip||a.model.skip||(ut.almostEquals(n.deltaK,0,this.EPSILON)?n.mK=a.mK=0:(o=n.mK/n.deltaK,r=a.mK/n.deltaK,(l=Math.pow(o,2)+Math.pow(r,2))<=9||(s=3/Math.sqrt(l),n.mK=o*s*n.deltaK,a.mK=r*s*n.deltaK)));for(e=0;e<h;++e)(n=u[e]).model.skip||(i=e>0?u[e-1]:null,a=e<h-1?u[e+1]:null,i&&!i.model.skip&&(d=(n.model.x-i.model.x)/3,n.model.controlPointPreviousX=n.model.x-d,n.model.controlPointPreviousY=n.model.y-d*n.mK),a&&!a.model.skip&&(d=(a.model.x-n.model.x)/3,n.model.controlPointNextX=n.model.x+d,n.model.controlPointNextY=n.model.y+d*n.mK))},ut.nextItem=function(t,e,i){return i?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},ut.previousItem=function(t,e,i){return i?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},ut.niceNum=function(t,e){var i=Math.floor(ut.log10(t)),n=t/Math.pow(10,i);return(e?n<1.5?1:n<3?2:n<7?5:10:n<=1?1:n<=2?2:n<=5?5:10)*Math.pow(10,i)},ut.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},ut.getRelativePosition=function(t,e){var i,n,a=t.originalEvent||t,o=t.target||t.srcElement,r=o.getBoundingClientRect(),s=a.touches;s&&s.length>0?(i=s[0].clientX,n=s[0].clientY):(i=a.clientX,n=a.clientY);var l=parseFloat(ut.getStyle(o,"padding-left")),d=parseFloat(ut.getStyle(o,"padding-top")),u=parseFloat(ut.getStyle(o,"padding-right")),h=parseFloat(ut.getStyle(o,"padding-bottom")),c=r.right-r.left-l-u,f=r.bottom-r.top-d-h;return{x:i=Math.round((i-r.left-l)/c*o.width/e.currentDevicePixelRatio),y:n=Math.round((n-r.top-d)/f*o.height/e.currentDevicePixelRatio)}},ut.getConstraintWidth=function(t){return i(t,"max-width","clientWidth")},ut.getConstraintHeight=function(t){return i(t,"max-height","clientHeight")},ut._calculatePadding=function(t,e,i){return(e=ut.getStyle(t,e)).indexOf("%")>-1?i*parseInt(e,10)/100:parseInt(e,10)},ut._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},ut.getMaximumWidth=function(t){var e=ut._getParentNode(t);if(!e)return t.clientWidth;var i=e.clientWidth,n=i-ut._calculatePadding(e,"padding-left",i)-ut._calculatePadding(e,"padding-right",i),a=ut.getConstraintWidth(t);return isNaN(a)?n:Math.min(n,a)},ut.getMaximumHeight=function(t){var e=ut._getParentNode(t);if(!e)return t.clientHeight;var i=e.clientHeight,n=i-ut._calculatePadding(e,"padding-top",i)-ut._calculatePadding(e,"padding-bottom",i),a=ut.getConstraintHeight(t);return isNaN(a)?n:Math.min(n,a)},ut.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},ut.retinaScale=function(t,e){var i=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==i){var n=t.canvas,a=t.height,o=t.width;n.height=a*i,n.width=o*i,t.ctx.scale(i,i),n.style.height||n.style.width||(n.style.height=a+"px",n.style.width=o+"px")}},ut.fontString=function(t,e,i){return e+" "+t+"px "+i},ut.longestText=function(t,e,i,n){var a=(n=n||{}).data=n.data||{},o=n.garbageCollect=n.garbageCollect||[];n.font!==e&&(a=n.data={},o=n.garbageCollect=[],n.font=e),t.font=e;var r=0;ut.each(i,function(e){null!=e&&!0!==ut.isArray(e)?r=ut.measureText(t,a,o,r,e):ut.isArray(e)&&ut.each(e,function(e){null==e||ut.isArray(e)||(r=ut.measureText(t,a,o,r,e))})});var s=o.length/2;if(s>i.length){for(var l=0;l<s;l++)delete a[o[l]];o.splice(0,s)}return r},ut.measureText=function(t,e,i,n,a){var o=e[a];return o||(o=e[a]=t.measureText(a).width,i.push(a)),o>n&&(n=o),n},ut.numberOfLabelLines=function(t){var e=1;return ut.each(t,function(t){ut.isArray(t)&&t.length>e&&(e=t.length)}),e},ut.color=X?function(t){return t instanceof CanvasGradient&&(t=st.global.defaultColor),X(t)}:function(t){return console.error("Color.js not found!"),t},ut.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:ut.color(t).saturate(.5).darken(.1).rgbString()}}(),ai._adapters=si,ai.Animation=vt,ai.animationService=bt,ai.controllers=ue,ai.DatasetController=Mt,ai.defaults=st,ai.Element=pt,ai.elements=Wt,ai.Interaction=ve,ai.layouts=ke,ai.platform=Ve,ai.plugins=Ee,ai.Scale=fi,ai.scaleService=He,ai.Ticks=li,ai.Tooltip=Je,ai.helpers.each(tn,function(t,e){ai.scaleService.registerScaleType(e,t,t._defaults)}),yn)yn.hasOwnProperty(_n)&&ai.plugins.register(yn[_n]);ai.platform.initialize();var Cn=ai;return"undefined"!=typeof window&&(window.Chart=ai),ai.Chart=ai,ai.Legend=yn.legend._element,ai.Title=yn.title._element,ai.pluginService=ai.plugins,ai.PluginBase=ai.Element.extend({}),ai.canvasHelpers=ai.helpers.canvas,ai.layoutService=ai.layouts,ai.LinearScaleBase=bi,ai.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],function(t){ai[t]=function(e,i){return new ai(e,ai.helpers.merge(i||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}}),Cn});
pub/js/lightbox.js CHANGED
@@ -382,13 +382,14 @@ function w3tc_lightbox_self_test(nonce) {
382
  });
383
  }
384
 
385
- function w3tc_lightbox_upgrade(nonce) {
386
  W3tc_Lightbox.open({
387
  id: 'w3tc-overlay',
388
  close: '',
389
  width: 800,
390
  height: 350,
391
- url: 'admin.php?page=w3tc_dashboard&w3tc_licensing_upgrade&_wpnonce=' + nonce,
 
392
  callback: function(lightbox) {
393
  lightbox.options.height = jQuery('#w3tc-upgrade').height() - 57;
394
  jQuery('.button-primary', lightbox.container).click(function() {
@@ -398,6 +399,13 @@ function w3tc_lightbox_upgrade(nonce) {
398
  lightbox.close();
399
  w3tc_lightbox_buy_plugin(nonce);
400
  });
 
 
 
 
 
 
 
401
  lightbox.resize();
402
  }
403
  });
@@ -463,20 +471,6 @@ function w3tc_lightbox_save_licence_key(license_key, nonce, callback) {
463
  }, 'json').fail(callback);
464
  }
465
 
466
- function w3tc_lightbox_cdn_s3_bucket_location(type, nonce) {
467
- W3tc_Lightbox.open({
468
- width: 500,
469
- height: 130,
470
- url: 'admin.php?page=w3tc_dashboard&w3tc_cdn_s3_bucket_location&type=' + type + '&_wpnonce=' + nonce,
471
- callback: function(lightbox) {
472
- jQuery('.button', lightbox.container).click(function() {
473
- lightbox.close();
474
- });
475
- }
476
- });
477
- }
478
-
479
-
480
  jQuery(function() {
481
  jQuery('.button-minify-recommendations').click(function() {
482
  var nonce = jQuery(this).metadata().nonce;
@@ -491,25 +485,12 @@ jQuery(function() {
491
  });
492
 
493
  jQuery('.button-buy-plugin').click(function() {
494
- w3tc_lightbox_upgrade(w3tc_nonce);
 
495
  jQuery('#w3tc-license-instruction').show();
496
  return false;
497
  });
498
 
499
- jQuery('.button-cdn-s3-bucket-location,.button-cdn-cf-bucket-location').click(function() {
500
- var type = '';
501
- var nonce = jQuery(this).metadata().nonce;
502
-
503
- if (jQuery(this).hasClass('cdn_s3')) {
504
- type = 's3';
505
- } else if (jQuery(this).hasClass('cdn_cf')) {
506
- type = 'cf';
507
- }
508
-
509
- w3tc_lightbox_cdn_s3_bucket_location(type, nonce);
510
- return false;
511
- });
512
-
513
  jQuery('body').on('click', '.w3tc_lightbox_close', function() {
514
  W3tc_Lightbox.close();
515
  });
382
  });
383
  }
384
 
385
+ function w3tc_lightbox_upgrade(nonce, data_src) {
386
  W3tc_Lightbox.open({
387
  id: 'w3tc-overlay',
388
  close: '',
389
  width: 800,
390
  height: 350,
391
+ url: 'admin.php?page=w3tc_dashboard&w3tc_licensing_upgrade&_wpnonce=' +
392
+ encodeURIComponent(nonce) + '&data_src=' + encodeURIComponent(data_src),
393
  callback: function(lightbox) {
394
  lightbox.options.height = jQuery('#w3tc-upgrade').height() - 57;
395
  jQuery('.button-primary', lightbox.container).click(function() {
399
  lightbox.close();
400
  w3tc_lightbox_buy_plugin(nonce);
401
  });
402
+ jQuery('#w3tc-purchase-link', lightbox.container).click(function() {
403
+ lightbox.close();
404
+
405
+ jQuery([document.documentElement, document.body]).animate({
406
+ scrollTop: jQuery("#licensing").offset().top
407
+ }, 2000);
408
+ });
409
  lightbox.resize();
410
  }
411
  });
471
  }, 'json').fail(callback);
472
  }
473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  jQuery(function() {
475
  jQuery('.button-minify-recommendations').click(function() {
476
  var nonce = jQuery(this).metadata().nonce;
485
  });
486
 
487
  jQuery('.button-buy-plugin').click(function() {
488
+ var data_src = jQuery(this).attr('data-src');
489
+ w3tc_lightbox_upgrade(w3tc_nonce, data_src);
490
  jQuery('#w3tc-license-instruction').show();
491
  return false;
492
  });
493
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  jQuery('body').on('click', '.w3tc_lightbox_close', function() {
495
  W3tc_Lightbox.close();
496
  });
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: fredericktownes
3
  Tags: seo, cache, caching, compression, maxcdn, nginx, varnish, redis, new relic, aws, amazon web services, s3, cloudfront, rackspace, cloudflare, azure, apache
4
  Requires at least: 3.2
5
  Tested up to: 5.2
6
- Stable tag: 0.9.7.5
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -270,6 +270,22 @@ Please reach out to all of these people and support their projects if you're so
270
 
271
  == Changelog ==
272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  = 0.9.7.5 =
274
  * Updated AWS library
275
  * Added support of set_sql_mode by dbcluster
3
  Tags: seo, cache, caching, compression, maxcdn, nginx, varnish, redis, new relic, aws, amazon web services, s3, cloudfront, rackspace, cloudflare, azure, apache
4
  Requires at least: 3.2
5
  Tested up to: 5.2
6
+ Stable tag: 0.10.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
270
 
271
  == Changelog ==
272
 
273
+ = 0.10.0 =
274
+ * Improved Statistics component for pro users
275
+ * Improved support for CloudFront distributions with multiple origins
276
+ * Improved redirects by using safter wp_safe redirect
277
+ * Improved .htaccess usage when pagecache does not require it
278
+ * Improved protection of unexpected values in global variables
279
+ * Added more Amazon S3 regions
280
+ * Added support for memcached binary protocol when available
281
+ * Added caching for webp MIME type
282
+ * Updated S3 bucket creation by settings CORS policy
283
+ * Updated blogmap to allow urls with custom ports
284
+ * Fixed usage of base url with minify
285
+ * Fixed mixing content of sync & async scripts with minify
286
+
287
+ * Fixed S3 + CloudFront urls when CNAMEs not used
288
+
289
  = 0.9.7.5 =
290
  * Updated AWS library
291
  * Added support of set_sql_mode by dbcluster
w3-total-cache-api.php CHANGED
@@ -5,7 +5,7 @@ if ( !defined( 'ABSPATH' ) ) {
5
  }
6
 
7
  define( 'W3TC', true );
8
- define( 'W3TC_VERSION', '0.9.7.5' );
9
  define( 'W3TC_POWERED_BY', 'W3 Total Cache' );
10
  define( 'W3TC_EMAIL', 'w3tc@w3-edge.com' );
11
  define( 'W3TC_TEXT_DOMAIN', 'w3-total-cache' );
5
  }
6
 
7
  define( 'W3TC', true );
8
+ define( 'W3TC_VERSION', '0.10.0' );
9
  define( 'W3TC_POWERED_BY', 'W3 Total Cache' );
10
  define( 'W3TC_EMAIL', 'w3tc@w3-edge.com' );
11
  define( 'W3TC_TEXT_DOMAIN', 'w3-total-cache' );
w3-total-cache.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: W3 Total Cache
4
  Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress.
5
- Version: 0.9.7.5
6
  Plugin URI: https://www.w3-edge.com/wordpress-plugins/w3-total-cache/
7
  Author: Frederick Townes
8
  Author URI: http://www.linkedin.com/in/fredericktownes
2
  /*
3
  Plugin Name: W3 Total Cache
4
  Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress.
5
+ Version: 0.10.0
6
  Plugin URI: https://www.w3-edge.com/wordpress-plugins/w3-total-cache/
7
  Author: Frederick Townes
8
  Author URI: http://www.linkedin.com/in/fredericktownes