Count per Day - Version 2.17

Version Description

  • Bugfix: JavaScript error on dashboard page, boxes not movable
  • Bugfix: PHP4 compatibility
  • New: function and shortcode to show world map in frontend
  • New: option - who is allowed to see the statistics page
  • New language: Turkish, thanks to Emrullah Tahir Ekmek├ži
Download this release

Release Info

Developer Tom Braider
Plugin Icon 128x128 Count per Day
Version 2.17
Comparing to
See all releases

Code changes from version 2.16.1 to 2.17

ajax.php CHANGED
@@ -1,32 +1,32 @@
1
- <?php
2
- if ( $_GET['f'] == 'count' )
3
- {
4
- if (!session_id()) session_start();
5
- require_once($_SESSION['cpd_wp'].'wp-load.php');
6
-
7
- // $cpd_funcs = CountPerDay_Widget::getWidgetFuncs();
8
- $cpd_funcs = array ( 'show',
9
- 'getReadsAll', 'getReadsToday', 'getReadsYesterday', 'getReadsLastWeek', 'getReadsThisMonth',
10
- 'getUserAll', 'getUserToday', 'getUserYesterday', 'getUserLastWeek', 'getUserThisMonth',
11
- 'getUserPerDay', 'getUserOnline', 'getFirstCount' );
12
-
13
- $page = intval($_GET['page']);
14
- if ( is_numeric($page) )
15
- {
16
- $count_per_day->count( '', $page );
17
- foreach ( $cpd_funcs as $f )
18
- {
19
- if ( ($f == 'show' && $page) || $f != 'show' )
20
- {
21
- echo $f.'===';
22
- if ( $f == 'getUserPerDay' )
23
- eval('echo $count_per_day->getUserPerDay('.$count_per_day->options['dashboard_last_days'].');');
24
- else if ( $f == 'show' )
25
- eval('echo $count_per_day->show("", "", false, false, '.$page.');');
26
- else
27
- eval('echo $count_per_day->'.$f.'();');
28
- echo '|';
29
- }
30
- }
31
- }
32
- }
1
+ <?php
2
+ if ( $_GET['f'] == 'count' )
3
+ {
4
+ if (!session_id()) session_start();
5
+ require_once($_SESSION['cpd_wp'].'wp-load.php');
6
+
7
+ // $cpd_funcs = CountPerDay_Widget::getWidgetFuncs();
8
+ $cpd_funcs = array ( 'show',
9
+ 'getReadsAll', 'getReadsToday', 'getReadsYesterday', 'getReadsLastWeek', 'getReadsThisMonth',
10
+ 'getUserAll', 'getUserToday', 'getUserYesterday', 'getUserLastWeek', 'getUserThisMonth',
11
+ 'getUserPerDay', 'getUserOnline', 'getFirstCount' );
12
+
13
+ $page = intval($_GET['page']);
14
+ if ( is_numeric($page) )
15
+ {
16
+ $count_per_day->count( '', $page );
17
+ foreach ( $cpd_funcs as $f )
18
+ {
19
+ if ( ($f == 'show' && $page) || $f != 'show' )
20
+ {
21
+ echo $f.'===';
22
+ if ( $f == 'getUserPerDay' )
23
+ eval('echo $count_per_day->getUserPerDay('.$count_per_day->options['dashboard_last_days'].');');
24
+ else if ( $f == 'show' )
25
+ eval('echo $count_per_day->show("", "", false, false, '.$page.');');
26
+ else
27
+ eval('echo $count_per_day->'.$f.'();');
28
+ echo '|';
29
+ }
30
+ }
31
+ }
32
+ }
counter-options.php CHANGED
@@ -30,11 +30,12 @@ if(!empty($_POST['do']))
30
$count_per_day->options['ajax'] = empty( $_POST['cpd_ajax'] ) ? 0 : 1 ;
31
$count_per_day->options['debug'] = empty( $_POST['cpd_debug'] ) ? 0 : 1 ;
32
$count_per_day->options['localref'] = empty( $_POST['cpd_localref'] ) ? 0 : 1 ;
33
- $count_per_day->options['referrers'] = empty( $_POST['cpd_referrers'] ) ? 0 : 1 ;
34
$count_per_day->options['dashboard_referers'] = $_POST['cpd_dashboard_referers'];
35
$count_per_day->options['referers_last_days'] = $_POST['cpd_referers_last_days'];
36
$count_per_day->options['chart_old'] = empty( $_POST['cpd_chart_old'] ) ? 0 : 1 ;
37
$count_per_day->options['no_front_css'] = empty( $_POST['cpd_no_front_css'] ) ? 0 : 1 ;
38
39
if (empty($count_per_day->options['clients']))
40
$count_per_day->options['clients'] = 'Firefox, MSIE, Chrome, Safari, Opera';
@@ -223,7 +224,7 @@ switch($mode) {
223
</tr>
224
<tr>
225
<th nowrap="nowrap" scope="row" style="vertical-align:middle;"><?php _e('Clients and referrers', 'cpd') ?>:</th>
226
- <td><label for="cpd_referrers"><input type="checkbox" name="cpd_referrers" id="cpd_referrers" <?php if($o['referrers']==1) echo 'checked="checked"'; ?> /> <?php _e('Save and show clients and referrers.<br />Needs a lot of space in the database but gives you more detailed informations of your visitors.', 'cpd') ?> (1000000 <?php _e('Reads', 'cpd') ?> ~ 130 MB)</label></td>
227
</tr>
228
</table>
229
</fieldset>
@@ -232,7 +233,39 @@ switch($mode) {
232
233
<fieldset>
234
<legend><?php _e('Dashboard') ?></legend>
235
<table class="form-table">
236
<tr>
237
<th nowrap="nowrap" scope="row" style="vertical-align:middle;"><?php _e('Visitors per post', 'cpd') ?>:</th>
238
<td><input class="code" type="text" name="cpd_dashboard_posts" size="3" value="<?php echo $o['dashboard_posts']; ?>" /> <?php _e('How many posts do you want to see on dashboard page?', 'cpd') ?></td>
@@ -430,7 +463,7 @@ switch($mode) {
430
echo '<a href="?page=count-per-day/counter-options.php&amp;dmbip='.$row['longip'].'&amp;dmbdate='.$row['date'].'"
431
title="'.sprintf(__('Delete these %s counts', 'cpd'), $row['posts']).'"
432
style="color:red; font-weight: bold;">X</a> &nbsp;';
433
- echo '<a href="http://www.easywhois.com/index.php?mode=iplookup&amp;domain='.$ip.'">'.$ip.'</a></td>'
434
.'<td style="white-space:nowrap;">'.mysql2date(get_option('date_format'), $row['date'] ).'</td>'
435
.'<td>'.$row['client'].'</td>'
436
.'<td style="text-align:right;"><a href="'.$count_per_day->dir.'/massbots.php?dmbip='.$row['longip'].'&amp;dmbdate='.$row['date'].'&amp;KeepThis=true&amp;TB_iframe=true" title="Count per Day" class="thickbox">'
@@ -453,7 +486,7 @@ switch($mode) {
453
</div>
454
455
<!-- Cleaner -->
456
- <?php if ( $count_per_day->options['referrers'] ) : ?>
457
<div class="postbox">
458
<h3><?php _e('Clean the database', 'cpd') ?></h3>
459
<div class="inside">
30
$count_per_day->options['ajax'] = empty( $_POST['cpd_ajax'] ) ? 0 : 1 ;
31
$count_per_day->options['debug'] = empty( $_POST['cpd_debug'] ) ? 0 : 1 ;
32
$count_per_day->options['localref'] = empty( $_POST['cpd_localref'] ) ? 0 : 1 ;
33
+ $count_per_day->options['referers'] = empty( $_POST['cpd_referers'] ) ? 0 : 1 ;
34
$count_per_day->options['dashboard_referers'] = $_POST['cpd_dashboard_referers'];
35
$count_per_day->options['referers_last_days'] = $_POST['cpd_referers_last_days'];
36
$count_per_day->options['chart_old'] = empty( $_POST['cpd_chart_old'] ) ? 0 : 1 ;
37
$count_per_day->options['no_front_css'] = empty( $_POST['cpd_no_front_css'] ) ? 0 : 1 ;
38
+ $count_per_day->options['whocansee'] = ($_POST['cpd_whocansee'] == 'custom') ? $_POST['cpd_whocansee_custom'] : $_POST['cpd_whocansee'];
39
40
if (empty($count_per_day->options['clients']))
41
$count_per_day->options['clients'] = 'Firefox, MSIE, Chrome, Safari, Opera';
224
</tr>
225
<tr>
226
<th nowrap="nowrap" scope="row" style="vertical-align:middle;"><?php _e('Clients and referrers', 'cpd') ?>:</th>
227
+ <td><label for="cpd_referers"><input type="checkbox" name="cpd_referers" id="cpd_referers" <?php if($o['referers']==1) echo 'checked="checked"'; ?> /> <?php _e('Save and show clients and referrers.<br />Needs a lot of space in the database but gives you more detailed informations of your visitors.', 'cpd') ?> (1000000 <?php _e('Reads', 'cpd') ?> ~ 130 MB)</label></td>
228
</tr>
229
</table>
230
</fieldset>
233
234
<fieldset>
235
<legend><?php _e('Dashboard') ?></legend>
236
+
237
+ <script>
238
+ function checkcustom()
239
+ {
240
+ var b = document.getElementById('cpd_whocansee');
241
+ var i = document.getElementById('cpd_whocansee_custom_div');
242
+ if ( b.value == 'custom' )
243
+ i.style.display = 'block';
244
+ else
245
+ i.style.display = 'none';
246
+ }
247
+ </script>
248
+
249
<table class="form-table">
250
+ <tr>
251
+ <th nowrap="nowrap" scope="row" style="vertical-align:middle;"><?php _e('Who can see it', 'cpd') ?>:</th>
252
+ <td>
253
+ <?php $cus = (in_array($o['whocansee'], array('manage_options','manage_links','publish_posts','edit_posts','read'))) ? 0 : 1 ?>
254
+ <select id="cpd_whocansee" name="cpd_whocansee" onchange="checkcustom()">
255
+ <option value="manage_options" <?php if ($o['whocansee'] == 'manage_options') echo 'selected="selected"' ?>><?php echo translate_user_role('Administrator') ?> </option>
256
+ <option value="manage_links" <?php if ($o['whocansee'] == 'manage_links') echo 'selected="selected"' ?>><?php echo translate_user_role('Editor') ?></option>
257
+ <option value="publish_posts" <?php if ($o['whocansee'] == 'publish_posts') echo 'selected="selected"' ?>><?php echo translate_user_role('Author') ?></option>
258
+ <option value="edit_posts" <?php if ($o['whocansee'] == 'edit_posts') echo 'selected="selected"' ?>><?php echo translate_user_role('Contributor') ?></option>
259
+ <option value="read" <?php if ($o['whocansee'] == 'read') echo 'selected="selected"' ?>><?php echo translate_user_role('Subscriber') ?></option>
260
+ <option value="custom" <?php if ($cus) echo 'selected="selected"' ?>>- <?php echo _e('custom', 'cpd') ?> -</option>
261
+ </select>
262
+ <?php _e('and higher are allowed to see the statistics page.', 'cpd') ?>
263
+ <div id="cpd_whocansee_custom_div" <?php if (!$cus) echo 'style="display:none"' ?>>
264
+ <?php printf(__('Set the %s capability %s a user need:', 'cpd'), '<a href="https://codex.wordpress.org/Roles_and_Capabilities">', '</a>'); ?>
265
+ <input type="text" name="cpd_whocansee_custom" value="<?php echo $o['whocansee'] ?>" />
266
+ </div>
267
+ </td>
268
+ </tr>
269
<tr>
270
<th nowrap="nowrap" scope="row" style="vertical-align:middle;"><?php _e('Visitors per post', 'cpd') ?>:</th>
271
<td><input class="code" type="text" name="cpd_dashboard_posts" size="3" value="<?php echo $o['dashboard_posts']; ?>" /> <?php _e('How many posts do you want to see on dashboard page?', 'cpd') ?></td>
463
echo '<a href="?page=count-per-day/counter-options.php&amp;dmbip='.$row['longip'].'&amp;dmbdate='.$row['date'].'"
464
title="'.sprintf(__('Delete these %s counts', 'cpd'), $row['posts']).'"
465
style="color:red; font-weight: bold;">X</a> &nbsp;';
466
+ echo '<a href="http://www.utrace.de/?query='.$ip.'">'.$ip.'</a></td>'
467
.'<td style="white-space:nowrap;">'.mysql2date(get_option('date_format'), $row['date'] ).'</td>'
468
.'<td>'.$row['client'].'</td>'
469
.'<td style="text-align:right;"><a href="'.$count_per_day->dir.'/massbots.php?dmbip='.$row['longip'].'&amp;dmbdate='.$row['date'].'&amp;KeepThis=true&amp;TB_iframe=true" title="Count per Day" class="thickbox">'
486
</div>
487
488
<!-- Cleaner -->
489
+ <?php if ( $count_per_day->options['referers'] ) : ?>
490
<div class="postbox">
491
<h3><?php _e('Clean the database', 'cpd') ?></h3>
492
<div class="inside">
counter.css CHANGED
@@ -77,7 +77,7 @@
77
height: 50px;
78
border: 1px solid #ddd;
79
background: -moz-linear-gradient(top, #ccc 0, #fff 80%);
80
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #005), color-stop(81%, #4a7eb8), color-stop(82%, #5A646D), color-stop(87%, #fafafa));
81
}
82
83
.cpd-dashboard .inside {
@@ -140,6 +140,7 @@
140
.cpd_widget_item .widefat {
141
width: 235px !important;
142
margin-left: 15px;
143
}
144
145
/* thickbox */
77
height: 50px;
78
border: 1px solid #ddd;
79
background: -moz-linear-gradient(top, #ccc 0, #fff 80%);
80
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ccc), color-stop(80%, #fff));
81
}
82
83
.cpd-dashboard .inside {
140
.cpd_widget_item .widefat {
141
width: 235px !important;
142
margin-left: 15px;
143
+
144
}
145
146
/* thickbox */
counter.php CHANGED
@@ -3,14 +3,14 @@
3
Plugin Name: Count Per Day
4
Plugin URI: http://www.tomsdimension.de/wp-plugins/count-per-day
5
Description: Counter, shows reads per page; today, yesterday, last week, last months ... on dashboard, per shortcode or in widget.
6
- Version: 2.16.1
7
License: Postcardware :)
8
Author: Tom Braider
9
Author URI: http://www.tomsdimension.de
10
*/
11
12
$cpd_dir_name = 'count-per-day';
13
- $cpd_version = '2.16.1';
14
15
/**
16
* include GeoIP addon
@@ -80,33 +80,40 @@ function CountPerDay()
80
}
81
82
// widget on dashboard page
83
- add_action('wp_dashboard_setup', array(&$this, 'dashboardWidgetSetup'));
84
85
// CpD dashboard page
86
- add_filter('screen_layout_columns', array(&$this, 'screenLayoutColumns'), 10, 2);
87
88
- // register callback for admin menu setup
89
- add_action('admin_menu', array(&$this, 'setAdminMenu'));
90
91
// column page list
92
- add_action('manage_pages_custom_column', array(&$this, 'cpdColumnContent'), 10, 2);
93
- add_filter('manage_pages_columns', array(&$this, 'cpdColumn'));
94
95
// column post list
96
- add_action('manage_posts_custom_column', array(&$this, 'cpdColumnContent'), 10, 2);
97
- add_filter('manage_posts_columns', array(&$this, 'cpdColumn'));
98
99
// locale support
100
if (defined('WPLANG') && function_exists('load_plugin_textdomain'))
101
load_plugin_textdomain('cpd', false, $cpd_dir_name.'/locale');
102
103
// adds stylesheet
104
- add_action('admin_head', array(&$this, 'addCss'));
105
if ( empty($this->options['no_front_css']) )
106
add_action('wp_head', array(&$this, 'addCss'));
107
108
// adds javascript
109
- add_action('admin_head', array(&$this, 'addJS'));
110
111
// widget setup
112
add_action('widgets_init', array( &$this, 'register_widgets'));
@@ -163,7 +170,7 @@ function connectDB()
163
164
$this->dbcon = @mysql_connect(DB_HOST, DB_USER, DB_PASSWORD, true);
165
@mysql_select_db(DB_NAME, $this->dbcon);
166
- $this->getQuery("SET NAMES '".$wpdb->charset."'", 'SET NAMES');
167
}
168
169
/**
@@ -209,7 +216,7 @@ function show( $before='', $after=' reads', $show = true, $count = true, $page =
209
$this->count();
210
if ( $page == 'x' )
211
$page = get_the_ID();
212
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE page='$page'", 'show');
213
$row = mysql_fetch_row($res);
214
if ( $show )
215
echo $before.$row[0].$after;
@@ -340,7 +347,7 @@ function count( $x, $page = 'x' )
340
$date = date_i18n('Y-m-d');
341
342
// new visitor on page?
343
- $res = $this->getQuery("SELECT count(*) FROM ".CPD_C_TABLE." WHERE ip=INET_ATON('$userip') AND date='$date' AND page='$page'", 'count check');
344
$row = mysql_fetch_row($res);
345
if ( $row[0] == 0 )
346
{
@@ -351,20 +358,20 @@ function count( $x, $page = 'x' )
351
$gi = cpd_geoip_open($cpd_path.'geoip/GeoIP.dat', GEOIP_STANDARD);
352
$country = strtolower(cpd_geoip_country_code_by_addr($gi, $userip));
353
$this->getQuery($wpdb->prepare("INSERT INTO ".CPD_C_TABLE." (page, ip, client, date, country, referer)
354
- VALUES (%s, INET_ATON(%s), %s, %s, %s, %s)", $page, $userip, $client, $date, $country, $referer), 'count insert');
355
}
356
else
357
{
358
// without country
359
$this->getQuery($wpdb->prepare("INSERT INTO ".CPD_C_TABLE." (page, ip, client, date, referer)
360
- VALUES (%s, INET_ATON(%s), %s, %s, %s)", $page, $userip, $client, $date, $referer), 'count insert');
361
}
362
}
363
364
// online counter
365
$timestamp = time();
366
$this->getQuery($wpdb->prepare("REPLACE INTO ".CPD_CO_TABLE." (timestamp, ip, page)
367
- VALUES ( %s, INET_ATON(%s), %s)", $timestamp, $userip, $page), 'count online');
368
}
369
}
370
@@ -374,7 +381,7 @@ function count( $x, $page = 'x' )
374
function deleteOnlineCounter()
375
{
376
$timeout = time() - $this->options['onlinetime'];
377
- $this->getQuery("DELETE FROM ".CPD_CO_TABLE." WHERE timestamp < $timeout", 'deleteOnlineCounter');
378
}
379
380
/**
@@ -442,7 +449,7 @@ function createTables()
442
KEY `idx_page` (`page`),
443
KEY `idx_dateip` (`date`,`ip`) )
444
$charset_collate;";
445
- $this->getQuery($sql);
446
447
// update fields in old table
448
$field = $this->getQuery( "SHOW FIELDS FROM `$cpd_c` LIKE 'ip'" );
@@ -458,7 +465,7 @@ function createTables()
458
"ALTER TABLE `$cpd_c` CHANGE `page` `page` mediumint(9) NOT NULL");
459
460
foreach ( $queries as $sql)
461
- $this->getQuery($sql, 'update old fields');
462
}
463
464
// make new keys
@@ -575,7 +582,7 @@ function getFlotChart( $limit = 0 )
575
FROM ".CPD_C_TABLE." AS c
576
WHERE c.date BETWEEN '$start_sql' AND '$end_sql'
577
GROUP BY c.date";
578
- $res = $this->getQuery($sql, 'ChartReads');
579
if ( @mysql_num_rows($res) )
580
while ( $row = mysql_fetch_array($res) )
581
$data[strtotime($row['date'])][0] = $row['count'];
@@ -589,7 +596,7 @@ function getFlotChart( $limit = 0 )
589
) AS t
590
WHERE t.date BETWEEN '$start_sql' AND '$end_sql'
591
GROUP BY t.date";
592
- $res = $this->getQuery($sql, 'ChartVisitors');
593
if ( @mysql_num_rows($res) )
594
while ( $row = mysql_fetch_array($res) )
595
$data[strtotime($row['date'])][1] = $row['count'];
@@ -776,7 +783,7 @@ function dashboardChartDataRequest( $sql = '', $limit, $frontend = false )
776
// get options
777
$max_height = ( !empty($this->options['chart_height']) ) ? $this->options['chart_height'] : 200;
778
779
- $res = $this->getQuery($sql, 'Chart');
780
if ( mysql_errno() || !mysql_num_rows($res) )
781
return;
782
@@ -891,14 +898,14 @@ function getUserOnline( $frontend = false, $country = false )
891
{
892
// map link
893
if (!$frontend && file_exists($cpd_path.'map/map.php') )
894
- $c .= '<div style="margin: 5px 0 10px 0;"><a href="'.$this->dir.'/map/map.php?map=visitors%20online'
895
.'&amp;KeepThis=true&amp;TB_iframe=true" title="Count per Day - '.__('Map', 'cpd').'" class="thickbox button">'.__('Map', 'cpd').'</a></div>';
896
897
// countries list
898
$geoip = new GeoIPCpd();
899
$gi = cpd_geoip_open($cpd_path.'geoip/GeoIP.dat', GEOIP_STANDARD);
900
901
- $res = $this->getQuery("SELECT INET_NTOA(ip) AS ip FROM ".CPD_CO_TABLE, 'getUserOnline');
902
if ( @mysql_num_rows($res) )
903
{
904
$vo = array();
@@ -926,7 +933,7 @@ function getUserOnline( $frontend = false, $country = false )
926
else
927
{
928
// number only
929
- $res = $this->getQuery("SELECT count(*) FROM ".CPD_CO_TABLE, 'getUserOnline');
930
$row = mysql_fetch_row($res);
931
$c = $row[0];
932
}
@@ -942,7 +949,7 @@ function getUserOnline( $frontend = false, $country = false )
942
*/
943
function getUserAll( $frontend = false )
944
{
945
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." GROUP BY date, ip", 'getUserAll');
946
$c = mysql_num_rows($res) + intval($this->options['startcount']);
947
if ($frontend)
948
return $c;
@@ -955,7 +962,7 @@ function getUserAll( $frontend = false )
955
*/
956
function getReadsAll( $frontend = false )
957
{
958
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE, 'getReadsAll');
959
$row = mysql_fetch_row($res);
960
$c = $row[0] + intval($this->options['startreads']);
961
if ($frontend)
@@ -970,7 +977,7 @@ function getReadsAll( $frontend = false )
970
function getUserToday( $frontend = false )
971
{
972
$date = date_i18n('Y-m-d');
973
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date = '$date' GROUP BY ip", 'getUserToday');
974
$c = mysql_num_rows($res);
975
if ($frontend)
976
return $c;
@@ -984,7 +991,7 @@ function getUserToday( $frontend = false )
984
function getReadsToday( $frontend = false )
985
{
986
$date = date_i18n('Y-m-d');
987
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date = '$date'", 'getReadsToday');
988
$row = mysql_fetch_row($res);
989
if ($frontend)
990
return $row[0];
@@ -998,7 +1005,7 @@ function getReadsToday( $frontend = false )
998
function getUserYesterday( $frontend = false )
999
{
1000
$date = date_i18n('Y-m-d', current_time('timestamp')-86400);
1001
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date = '$date' GROUP BY ip", 'getUserYesterday');
1002
$c = mysql_num_rows($res);
1003
if ($frontend)
1004
return $c;
@@ -1012,7 +1019,7 @@ function getUserYesterday( $frontend = false )
1012
function getReadsYesterday( $frontend = false )
1013
{
1014
$date = date_i18n('Y-m-d', current_time('timestamp')-86400);
1015
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date = '$date'", 'getReadsYesterday');
1016
$row = mysql_fetch_row($res);
1017
if ($frontend)
1018
return $row[0];
@@ -1026,7 +1033,7 @@ function getReadsYesterday( $frontend = false )
1026
function getUserLastWeek( $frontend = false )
1027
{
1028
$date = date_i18n('Y-m-d', current_time('timestamp')-86400*7);
1029
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date >= '$date' GROUP BY date, ip;", 'getUserLastWeek');
1030
$c = mysql_num_rows($res);
1031
if ($frontend)
1032
return $c;
@@ -1040,7 +1047,7 @@ function getUserLastWeek( $frontend = false )
1040
function getReadsLastWeek( $frontend = false )
1041
{
1042
$date = date_i18n('Y-m-d', current_time('timestamp')-86400*7);
1043
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date >= '$date';", 'getReadsLastWeek');
1044
$row = mysql_fetch_row($res);
1045
if ($frontend)
1046
return $row[0];
@@ -1054,7 +1061,7 @@ function getReadsLastWeek( $frontend = false )
1054
function getUserThisMonth( $frontend = false )
1055
{
1056
$first = date_i18n('Y-m-', current_time('timestamp')).'01';
1057
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date >= '$first' GROUP BY date, ip;", 'getUserThisMonth');
1058
$c = mysql_num_rows($res);
1059
if ($frontend)
1060
return $c;
@@ -1068,7 +1075,7 @@ function getUserThisMonth( $frontend = false )
1068
function getReadsThisMonth( $frontend = false )
1069
{
1070
$first = date_i18n('Y-m-', current_time('timestamp')).'01';
1071
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date >= '$first';", 'getReadsThisMonth');
1072
$row = mysql_fetch_row($res);
1073
if ($frontend)
1074
return $row[0];
@@ -1081,13 +1088,13 @@ function getReadsThisMonth( $frontend = false )
1081
*/
1082
function getUserPerMonth( $frontend = false )
1083
{
1084
- $m = $this->getQuery("SELECT LEFT(date,7) FROM ".CPD_C_TABLE." GROUP BY year(date), month(date) ORDER BY date DESC", 'getUserPerMonths');
1085
$r = '<ul class="cpd_front_list">';
1086
$d = array();
1087
$i = 1;
1088
while ( $row = mysql_fetch_row($m) )
1089
{
1090
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE LEFT(date,7) = '".$row[0]."' GROUP BY date, ip", 'getUserPerMonth');
1091
$r .= '<li><b>'.mysql_num_rows($res).'</b> '.$row[0].'</li>'."\n";
1092
$d[] = '[-'.$i++.','.mysql_num_rows($res).']';
1093
}
@@ -1106,7 +1113,7 @@ function getUserPerMonth( $frontend = false )
1106
*/
1107
function getReadsPerMonth( $frontend = false )
1108
{
1109
- $res = $this->getQuery("SELECT COUNT(*), LEFT(date,7) FROM ".CPD_C_TABLE." GROUP BY year(date), month(date) ORDER BY date DESC", 'getReadsPerMonths');
1110
$r = '<ul class="cpd_front_list">';
1111
$d = array();
1112
$i = 1;
@@ -1155,7 +1162,7 @@ function getUserPerPost( $limit = 0, $frontend = false )
1155
ORDER BY count DESC";
1156
if ( $limit > 0 )
1157
$sql .= " LIMIT ".$limit;
1158
- $r = $this->getUserPer_SQL( $sql, 'getUserPerPost', $frontend );
1159
if ($frontend)
1160
return $r;
1161
else
@@ -1172,7 +1179,7 @@ function getFirstCount( $frontend = false )
1172
$c = mysql2date(get_option('date_format'), $this->options['startdate'] );
1173
else
1174
{
1175
- $res = $this->getQuery("SELECT date FROM ".CPD_C_TABLE." ORDER BY date LIMIT 1", 'getFirstCount');
1176
$row = mysql_fetch_row($res);
1177
$c = mysql2date(get_option('date_format'), $row[0] );
1178
}
@@ -1205,7 +1212,7 @@ function getUserPerDay( $days = 0, $frontend = false )
1205
}
1206
}
1207
1208
- $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date > '$datemin' AND date < '$datemax' GROUP BY ip, date", 'getUserPerDay');
1209
$count = @mysql_num_rows($res) / $days;
1210
1211
$c = '<abbr title="last '.$days.' days without today">';
@@ -1305,7 +1312,7 @@ function getMostVisitedPostIDs( $days = 365, $limit = 10, $cats = false, $return
1305
GROUP BY c.page
1306
ORDER BY count DESC
1307
LIMIT $limit";
1308
- $res = $this->getQuery($sql, 'getMostVisitedPostIDs');
1309
1310
$ids = array();
1311
if ( @mysql_num_rows($res) )
@@ -1396,7 +1403,7 @@ function getClients( $frontend = false )
1396
$c_string = $this->options['clients'];
1397
$clients = explode(',', $c_string);
1398
1399
- $res = $this->getQuery("SELECT COUNT(*) count FROM ".CPD_C_TABLE, 'getClients_all');
1400
$row = @mysql_fetch_row($res);
1401
$all = max(1, $row[0]);
1402
$rest = 100;
@@ -1404,7 +1411,7 @@ function getClients( $frontend = false )
1404
foreach ($clients as $c)
1405
{
1406
$c = trim($c);
1407
- $res = $this->getQuery("SELECT COUNT(*) count FROM ".CPD_C_TABLE." WHERE client like '%".$c."%'", 'getClients_'.$c);
1408
$row = @mysql_fetch_row($res);
1409
$percent = number_format(100 * $row[0] / $all, 0);
1410
$rest -= $percent;
@@ -1435,7 +1442,7 @@ function getReferers( $limit = 0, $frontend = false, $days = 0 )
1435
$dayfiltre = "AND date > DATE_SUB('".date_i18n('Y-m-d')."', INTERVAL $days DAY)";
1436
1437
$localref = ($this->options['localref']) ? '' : " AND referer NOT LIKE '".get_bloginfo('url')."%' ";
1438
- $res = $this->getQuery("SELECT COUNT(*) count, referer FROM ".CPD_C_TABLE." WHERE referer > '' $dayfiltre $localref GROUP BY referer ORDER BY count DESC LIMIT $limit", 'getReferers');
1439
$r = '<small>'.sprintf(__('The %s referrers in last %s days:', 'cpd'), $limit, $days).'<br/>&nbsp;</small>';
1440
$r .= '<ul id="cpd_referrers" class="cpd_front_list">';
1441
if ( @mysql_num_rows($res) )
@@ -1472,7 +1479,7 @@ function getMassBots( $limit = 0 )
1472
LEFT JOIN ".CPD_C_TABLE." c
1473
ON c.id = t.id
1474
WHERE posts > $limit";
1475
- return $this->getQuery($sql, 'getMassBots');
1476
}
1477
1478
/**
@@ -1484,7 +1491,7 @@ function getMassBots( $limit = 0 )
1484
function getUserPer_SQL( $sql, $name = '', $frontend = false )
1485
{
1486
global $userdata;
1487
- $m = $this->getQuery($sql, $name);
1488
$r = '<ul class="cpd_front_list">';
1489
while ( $row = mysql_fetch_assoc($m) )
1490
{
@@ -1543,14 +1550,14 @@ function cleanDB()
1543
// delete by ip
1544
foreach( $bots as $ip )
1545
if ( ip2long($ip) !== false )
1546
- $this->getQuery('DELETE FROM '.CPD_C_TABLE.' WHERE INET_NTOA(ip) LIKE \''.$ip.'%\'', 'clenaDB_ip');
1547
1548
// delete by client
1549
foreach ($bots as $bot)
1550
- $this->getQuery("DELETE FROM ".CPD_C_TABLE." WHERE client LIKE '%$bot%'", 'cleanDB_client');
1551
1552
// delete if a previously countered page was deleted
1553
- $this->getQuery("DELETE FROM ".CPD_C_TABLE." WHERE page NOT IN ( SELECT id FROM ".$wpdb->posts.") AND page > 0", 'cleanDB_delPosts');
1554
1555
$rows_after = $wpdb->get_var('SELECT COUNT(*) FROM '.CPD_C_TABLE);
1556
return $rows_before - $rows_after;
@@ -1639,7 +1646,8 @@ function updateOptions()
1639
'dashboard_referers' => (isset($o['dashboard_referers'])) ? $o['dashboard_referers'] : 20,
1640
'referers_last_days' => (isset($o['referers_last_days'])) ? $o['referers_last_days'] : 7,
1641
'chart_old' => (isset($o['chart_old'])) ? $o['chart_old'] : 0,
1642
- 'no_front_css' => (isset($o['no_front_css'])) ? $o['no_front_css'] : 0
1643
);
1644
update_option('count_per_day', $onew);
1645
}
@@ -1662,7 +1670,7 @@ function cpdColumnContent($column_name, $id = 0)
1662
global $wpdb;
1663
if( $column_name == 'cpd_reads' )
1664
{
1665
- $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE page='$id'", 'cpdColumn_'.$id);
1666
$row = mysql_fetch_row($res);
1667
echo (int) $row[0];
1668
}
@@ -1692,7 +1700,7 @@ function screenLayoutColumns($columns, $screen)
1692
function setAdminMenu()
1693
{
1694
$menutitle = '<img src="'.$this->getResource('cpd_menu.gif').'" alt="" style="width:12px;height:12px;" /> Count per Day';
1695
- $this->pagehook = add_submenu_page('index.php', 'CountPerDay', $menutitle, 1, CPD_METABOX, array(&$this, 'onShowPage'));
1696
add_action('load-'.$this->pagehook, array(&$this, 'onLoadPage'));
1697
}
1698
@@ -1830,10 +1838,10 @@ function getCountries( $limit = 0, $frontend, $visitors = false )
1830
GROUP BY country, ip ) as t
1831
GROUP BY country
1832
ORDER BY c desc
1833
- LIMIT $limit", 'getCountries');
1834
else
1835
// reads
1836
- $res = $this->getQuery("SELECT country, COUNT(*) c FROM ".CPD_C_TABLE." WHERE ip > 0 GROUP BY country ORDER BY c DESC LIMIT $limit", 'getCountries');
1837
1838
// map link
1839
if (!$frontend && file_exists($cpd_path.'map/map.php') )
@@ -1872,6 +1880,38 @@ function getCountries( $limit = 0, $frontend, $visitors = false )
1872
echo $c;
1873
}
1874
1875
/**
1876
* adds some shortcodes to use functions in frontend
1877
*/
@@ -1901,6 +1941,7 @@ function addShortcodes()
1901
add_shortcode('CPD_MOST_VISITED_POSTS', array( &$this, 'shortMostVisitedPosts'));
1902
add_shortcode('CPD_REFERERS', array( &$this, 'shortReferers'));
1903
add_shortcode('CPD_POSTS_ON_DAY', array( &$this, 'shortPostsOnDay'));
1904
}
1905
function shortShow() { return $this->show('', '', false, false); }
1906
function shortReadsTotal() { return $this->getReadsAll(true); }
@@ -1933,6 +1974,16 @@ function shortPostsOnDay( $atts )
1933
), $atts) );
1934
return $this->getVisitedPostsOnDay( $date, $limit, false, false, true );
1935
}
1936
1937
/**
1938
* adds style sheet to admin header
@@ -2094,7 +2145,8 @@ function includeChartJS( $id, $data, $html )
2094
$code = '<div id="'.$id.'" class="cpd-list-chart"></div>
2095
<script type="text/javascript">
2096
//<![CDATA[
2097
- jQuery(function(){jQuery.plot(jQuery("#'.$id.'"),'.$d.',{series:{lines:{fill:true,lineWidth:1}},colors:["red"],grid:{show:false}});});
2098
//]]>
2099
</script>
2100
'.$html;
@@ -2300,10 +2352,11 @@ class CountPerDay_Widget extends WP_Widget
2300
function count_per_day_uninstall()
2301
{
2302
global $wpdb;
2303
- $wpdb->query('DROP TABLE IF EXISTS '.CPD_C_TABLE);
2304
- $wpdb->query('DROP TABLE IF EXISTS '.CPD_CO_TABLE);
2305
- $wpdb->query('DROP TABLE IF EXISTS '.CPD_N_TABLE);
2306
- delete_option('count_per_day');
2307
}
2308
2309
3
Plugin Name: Count Per Day
4
Plugin URI: http://www.tomsdimension.de/wp-plugins/count-per-day
5
Description: Counter, shows reads per page; today, yesterday, last week, last months ... on dashboard, per shortcode or in widget.
6
+ Version: 2.17
7
License: Postcardware :)
8
Author: Tom Braider
9
Author URI: http://www.tomsdimension.de
10
*/
11
12
$cpd_dir_name = 'count-per-day';
13
+ $cpd_version = '2.17';
14
15
/**
16
* include GeoIP addon
80
}
81
82
// widget on dashboard page
83
+ if (is_admin())
84
+ add_action('wp_dashboard_setup', array(&$this, 'dashboardWidgetSetup'));
85
86
// CpD dashboard page
87
+ if (is_admin())
88
+ add_filter('screen_layout_columns', array(&$this, 'screenLayoutColumns'), 10, 2);
89
90
+ // CpD dashboard
91
+ if (is_admin())
92
+ add_action('admin_menu', array(&$this, 'setAdminMenu'));
93
94
// column page list
95
+ if (is_admin())
96
+ add_action('manage_pages_custom_column', array(&$this, 'cpdColumnContent'), 10, 2);
97
+ add_filter('manage_pages_columns', array(&$this, 'cpdColumn'));
98
99
// column post list
100
+ if (is_admin())
101
+ add_action('manage_posts_custom_column', array(&$this, 'cpdColumnContent'), 10, 2);
102
+ add_filter('manage_posts_columns', array(&$this, 'cpdColumn'));
103
104
// locale support
105
if (defined('WPLANG') && function_exists('load_plugin_textdomain'))
106
load_plugin_textdomain('cpd', false, $cpd_dir_name.'/locale');
107
108
// adds stylesheet
109
+ if (is_admin())
110
+ add_action('admin_head', array(&$this, 'addCss'));
111
if ( empty($this->options['no_front_css']) )
112
add_action('wp_head', array(&$this, 'addCss'));
113
114
// adds javascript
115
+ if (is_admin())
116
+ add_action('admin_head', array(&$this, 'addJS'));
117
118
// widget setup
119
add_action('widgets_init', array( &$this, 'register_widgets'));
170
171
$this->dbcon = @mysql_connect(DB_HOST, DB_USER, DB_PASSWORD, true);
172
@mysql_select_db(DB_NAME, $this->dbcon);
173
+ $this->getQuery("SET NAMES '".$wpdb->charset."'", 'SET NAMES'.__LINE__);
174
}
175
176
/**
216
$this->count();
217
if ( $page == 'x' )
218
$page = get_the_ID();
219
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE page='$page'", 'show'.__LINE__);
220
$row = mysql_fetch_row($res);
221
if ( $show )
222
echo $before.$row[0].$after;
347
$date = date_i18n('Y-m-d');
348
349
// new visitor on page?
350
+ $res = $this->getQuery("SELECT count(*) FROM ".CPD_C_TABLE." WHERE ip=INET_ATON('$userip') AND date='$date' AND page='$page'", 'count check'.__LINE__);
351
$row = mysql_fetch_row($res);
352
if ( $row[0] == 0 )
353
{
358
$gi = cpd_geoip_open($cpd_path.'geoip/GeoIP.dat', GEOIP_STANDARD);
359
$country = strtolower(cpd_geoip_country_code_by_addr($gi, $userip));
360
$this->getQuery($wpdb->prepare("INSERT INTO ".CPD_C_TABLE." (page, ip, client, date, country, referer)
361
+ VALUES (%s, INET_ATON(%s), %s, %s, %s, %s)", $page, $userip, $client, $date, $country, $referer), 'count insert'.__LINE__);
362
}
363
else
364
{
365
// without country
366
$this->getQuery($wpdb->prepare("INSERT INTO ".CPD_C_TABLE." (page, ip, client, date, referer)
367
+ VALUES (%s, INET_ATON(%s), %s, %s, %s)", $page, $userip, $client, $date, $referer), 'count insert'.__LINE__);
368
}
369
}
370
371
// online counter
372
$timestamp = time();
373
$this->getQuery($wpdb->prepare("REPLACE INTO ".CPD_CO_TABLE." (timestamp, ip, page)
374
+ VALUES ( %s, INET_ATON(%s), %s)", $timestamp, $userip, $page), 'count online'.__LINE__);
375
}
376
}
377
381
function deleteOnlineCounter()
382
{
383
$timeout = time() - $this->options['onlinetime'];
384
+ $this->getQuery("DELETE FROM ".CPD_CO_TABLE." WHERE timestamp < $timeout", 'deleteOnlineCounter'.__LINE__);
385
}
386
387
/**
449
KEY `idx_page` (`page`),
450
KEY `idx_dateip` (`date`,`ip`) )
451
$charset_collate;";
452
+ $this->getQuery($sql, __LINE__);
453
454
// update fields in old table
455
$field = $this->getQuery( "SHOW FIELDS FROM `$cpd_c` LIKE 'ip'" );
465
"ALTER TABLE `$cpd_c` CHANGE `page` `page` mediumint(9) NOT NULL");
466
467
foreach ( $queries as $sql)
468
+ $this->getQuery($sql, 'update old fields'.__LINE__);
469
}
470
471
// make new keys
582
FROM ".CPD_C_TABLE." AS c
583
WHERE c.date BETWEEN '$start_sql' AND '$end_sql'
584
GROUP BY c.date";
585
+ $res = $this->getQuery($sql, 'ChartReads'.__LINE__);
586
if ( @mysql_num_rows($res) )
587
while ( $row = mysql_fetch_array($res) )
588
$data[strtotime($row['date'])][0] = $row['count'];
596
) AS t
597
WHERE t.date BETWEEN '$start_sql' AND '$end_sql'
598
GROUP BY t.date";
599
+ $res = $this->getQuery($sql, 'ChartVisitors'.__LINE__);
600
if ( @mysql_num_rows($res) )
601
while ( $row = mysql_fetch_array($res) )
602
$data[strtotime($row['date'])][1] = $row['count'];
783
// get options
784
$max_height = ( !empty($this->options['chart_height']) ) ? $this->options['chart_height'] : 200;
785
786
+ $res = $this->getQuery($sql, 'Chart'.__LINE__);
787
if ( mysql_errno() || !mysql_num_rows($res) )
788
return;
789
898
{
899
// map link
900
if (!$frontend && file_exists($cpd_path.'map/map.php') )
901
+ $c .= '<div style="margin: 5px 0 10px 0;"><a href="'.$this->dir.'/map/map.php?map=online'
902
.'&amp;KeepThis=true&amp;TB_iframe=true" title="Count per Day - '.__('Map', 'cpd').'" class="thickbox button">'.__('Map', 'cpd').'</a></div>';
903
904
// countries list
905
$geoip = new GeoIPCpd();
906
$gi = cpd_geoip_open($cpd_path.'geoip/GeoIP.dat', GEOIP_STANDARD);
907
908
+ $res = $this->getQuery("SELECT INET_NTOA(ip) AS ip FROM ".CPD_CO_TABLE, 'getUserOnline'.__LINE__);
909
if ( @mysql_num_rows($res) )
910
{
911
$vo = array();
933
else
934
{
935
// number only
936
+ $res = $this->getQuery("SELECT count(*) FROM ".CPD_CO_TABLE, 'getUserOnline'.__LINE__);
937
$row = mysql_fetch_row($res);
938
$c = $row[0];
939
}
949
*/
950
function getUserAll( $frontend = false )
951
{
952
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." GROUP BY date, ip", 'getUserAll'.__LINE__);
953
$c = mysql_num_rows($res) + intval($this->options['startcount']);
954
if ($frontend)
955
return $c;
962
*/
963
function getReadsAll( $frontend = false )
964
{
965
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE, 'getReadsAll'.__LINE__);
966
$row = mysql_fetch_row($res);
967
$c = $row[0] + intval($this->options['startreads']);
968
if ($frontend)
977
function getUserToday( $frontend = false )
978
{
979
$date = date_i18n('Y-m-d');
980
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date = '$date' GROUP BY ip", 'getUserToday'.__LINE__);
981
$c = mysql_num_rows($res);
982
if ($frontend)
983
return $c;
991
function getReadsToday( $frontend = false )
992
{
993
$date = date_i18n('Y-m-d');
994
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date = '$date'", 'getReadsToday'.__LINE__);
995
$row = mysql_fetch_row($res);
996
if ($frontend)
997
return $row[0];
1005
function getUserYesterday( $frontend = false )
1006
{
1007
$date = date_i18n('Y-m-d', current_time('timestamp')-86400);
1008
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date = '$date' GROUP BY ip", 'getUserYesterday'.__LINE__);
1009
$c = mysql_num_rows($res);
1010
if ($frontend)
1011
return $c;
1019
function getReadsYesterday( $frontend = false )
1020
{
1021
$date = date_i18n('Y-m-d', current_time('timestamp')-86400);
1022
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date = '$date'", 'getReadsYesterday'.__LINE__);
1023
$row = mysql_fetch_row($res);
1024
if ($frontend)
1025
return $row[0];
1033
function getUserLastWeek( $frontend = false )
1034
{
1035
$date = date_i18n('Y-m-d', current_time('timestamp')-86400*7);
1036
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date >= '$date' GROUP BY date, ip;", 'getUserLastWeek'.__LINE__);
1037
$c = mysql_num_rows($res);
1038
if ($frontend)
1039
return $c;
1047
function getReadsLastWeek( $frontend = false )
1048
{
1049
$date = date_i18n('Y-m-d', current_time('timestamp')-86400*7);
1050
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date >= '$date';", 'getReadsLastWeek'.__LINE__);
1051
$row = mysql_fetch_row($res);
1052
if ($frontend)
1053
return $row[0];
1061
function getUserThisMonth( $frontend = false )
1062
{
1063
$first = date_i18n('Y-m-', current_time('timestamp')).'01';
1064
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date >= '$first' GROUP BY date, ip;", 'getUserThisMonth'.__LINE__);
1065
$c = mysql_num_rows($res);
1066
if ($frontend)
1067
return $c;
1075
function getReadsThisMonth( $frontend = false )
1076
{
1077
$first = date_i18n('Y-m-', current_time('timestamp')).'01';
1078
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE date >= '$first';", 'getReadsThisMonth'.__LINE__);
1079
$row = mysql_fetch_row($res);
1080
if ($frontend)
1081
return $row[0];
1088
*/
1089
function getUserPerMonth( $frontend = false )
1090
{
1091
+ $m = $this->getQuery("SELECT LEFT(date,7) FROM ".CPD_C_TABLE." GROUP BY year(date), month(date) ORDER BY date DESC", 'getUserPerMonths'.__LINE__);
1092
$r = '<ul class="cpd_front_list">';
1093
$d = array();
1094
$i = 1;
1095
while ( $row = mysql_fetch_row($m) )
1096
{
1097
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE LEFT(date,7) = '".$row[0]."' GROUP BY date, ip", 'getUserPerMonth'.__LINE__);
1098
$r .= '<li><b>'.mysql_num_rows($res).'</b> '.$row[0].'</li>'."\n";
1099
$d[] = '[-'.$i++.','.mysql_num_rows($res).']';
1100
}
1113
*/
1114
function getReadsPerMonth( $frontend = false )
1115
{
1116
+ $res = $this->getQuery("SELECT COUNT(*), LEFT(date,7) FROM ".CPD_C_TABLE." GROUP BY year(date), month(date) ORDER BY date DESC", 'getReadsPerMonths'.__LINE__);
1117
$r = '<ul class="cpd_front_list">';
1118
$d = array();
1119
$i = 1;
1162
ORDER BY count DESC";
1163
if ( $limit > 0 )
1164
$sql .= " LIMIT ".$limit;
1165
+ $r = $this->getUserPer_SQL( $sql, 'getUserPerPost'.__LINE__, $frontend );
1166
if ($frontend)
1167
return $r;
1168
else
1179
$c = mysql2date(get_option('date_format'), $this->options['startdate'] );
1180
else
1181
{
1182
+ $res = $this->getQuery("SELECT date FROM ".CPD_C_TABLE." ORDER BY date LIMIT 1", 'getFirstCount'.__LINE__);
1183
$row = mysql_fetch_row($res);
1184
$c = mysql2date(get_option('date_format'), $row[0] );
1185
}
1212
}
1213
}
1214
1215
+ $res = $this->getQuery("SELECT 1 FROM ".CPD_C_TABLE." WHERE date > '$datemin' AND date < '$datemax' GROUP BY ip, date", 'getUserPerDay'.__LINE__);
1216
$count = @mysql_num_rows($res) / $days;
1217
1218
$c = '<abbr title="last '.$days.' days without today">';
1312
GROUP BY c.page
1313
ORDER BY count DESC
1314
LIMIT $limit";
1315
+ $res = $this->getQuery($sql, 'getMostVisitedPostIDs'.__LINE__);
1316
1317
$ids = array();
1318
if ( @mysql_num_rows($res) )
1403
$c_string = $this->options['clients'];
1404
$clients = explode(',', $c_string);
1405
1406
+ $res = $this->getQuery("SELECT COUNT(*) count FROM ".CPD_C_TABLE, 'getClients_all'.__LINE__);
1407
$row = @mysql_fetch_row($res);
1408
$all = max(1, $row[0]);
1409
$rest = 100;
1411
foreach ($clients as $c)
1412
{
1413
$c = trim($c);
1414
+ $res = $this->getQuery("SELECT COUNT(*) count FROM ".CPD_C_TABLE." WHERE client like '%".$c."%'", 'getClients_'.$c.'_'.__LINE__);
1415
$row = @mysql_fetch_row($res);
1416
$percent = number_format(100 * $row[0] / $all, 0);
1417
$rest -= $percent;
1442
$dayfiltre = "AND date > DATE_SUB('".date_i18n('Y-m-d')."', INTERVAL $days DAY)";
1443
1444
$localref = ($this->options['localref']) ? '' : " AND referer NOT LIKE '".get_bloginfo('url')."%' ";
1445
+ $res = $this->getQuery("SELECT COUNT(*) count, referer FROM ".CPD_C_TABLE." WHERE referer > '' $dayfiltre $localref GROUP BY referer ORDER BY count DESC LIMIT $limit", 'getReferers'.__LINE__);
1446
$r = '<small>'.sprintf(__('The %s referrers in last %s days:', 'cpd'), $limit, $days).'<br/>&nbsp;</small>';
1447
$r .= '<ul id="cpd_referrers" class="cpd_front_list">';
1448
if ( @mysql_num_rows($res) )
1479
LEFT JOIN ".CPD_C_TABLE." c
1480
ON c.id = t.id
1481
WHERE posts > $limit";
1482
+ return $this->getQuery($sql, 'getMassBots'.__LINE__);
1483
}
1484
1485
/**
1491
function getUserPer_SQL( $sql, $name = '', $frontend = false )
1492
{
1493
global $userdata;
1494
+ $m = $this->getQuery($sql, $name.__LINE__);
1495
$r = '<ul class="cpd_front_list">';
1496
while ( $row = mysql_fetch_assoc($m) )
1497
{
1550
// delete by ip
1551
foreach( $bots as $ip )
1552
if ( ip2long($ip) !== false )
1553
+ $this->getQuery('DELETE FROM '.CPD_C_TABLE.' WHERE INET_NTOA(ip) LIKE \''.$ip.'%\'', 'clenaDB_ip'.__LINE__);
1554
1555
// delete by client
1556
foreach ($bots as $bot)
1557
+ $this->getQuery("DELETE FROM ".CPD_C_TABLE." WHERE client LIKE '%$bot%'", 'cleanDB_client'.__LINE__);
1558
1559
// delete if a previously countered page was deleted
1560
+ $this->getQuery("DELETE FROM ".CPD_C_TABLE." WHERE page NOT IN ( SELECT id FROM ".$wpdb->posts.") AND page > 0", 'cleanDB_delPosts'.__LINE__);
1561
1562
$rows_after = $wpdb->get_var('SELECT COUNT(*) FROM '.CPD_C_TABLE);
1563
return $rows_before - $rows_after;
1646
'dashboard_referers' => (isset($o['dashboard_referers'])) ? $o['dashboard_referers'] : 20,
1647
'referers_last_days' => (isset($o['referers_last_days'])) ? $o['referers_last_days'] : 7,
1648
'chart_old' => (isset($o['chart_old'])) ? $o['chart_old'] : 0,
1649
+ 'no_front_css' => (isset($o['no_front_css'])) ? $o['no_front_css'] : 0,
1650
+ 'whocansee' => (isset($o['whocansee'])) ? $o['whocansee'] : 'publish_posts'
1651
);
1652
update_option('count_per_day', $onew);
1653
}
1670
global $wpdb;
1671
if( $column_name == 'cpd_reads' )
1672
{
1673
+ $res = $this->getQuery("SELECT COUNT(*) FROM ".CPD_C_TABLE." WHERE page='$id'", 'cpdColumn_'.$id.'_'.__LINE__);
1674
$row = mysql_fetch_row($res);
1675
echo (int) $row[0];
1676
}
1700
function setAdminMenu()
1701
{
1702
$menutitle = '<img src="'.$this->getResource('cpd_menu.gif').'" alt="" style="width:12px;height:12px;" /> Count per Day';
1703
+ $this->pagehook = add_submenu_page('index.php', 'CountPerDay', $menutitle, $this->options['whocansee'], CPD_METABOX, array(&$this, 'onShowPage'));
1704
add_action('load-'.$this->pagehook, array(&$this, 'onLoadPage'));
1705
}
1706
1838
GROUP BY country, ip ) as t
1839
GROUP BY country
1840
ORDER BY c desc
1841
+ LIMIT $limit", 'getCountries'.__LINE__);
1842
else
1843
// reads
1844
+ $res = $this->getQuery("SELECT country, COUNT(*) c FROM ".CPD_C_TABLE." WHERE ip > 0 GROUP BY country ORDER BY c DESC LIMIT $limit", 'getCountries'.__LINE__);
1845
1846
// map link
1847
if (!$frontend && file_exists($cpd_path.'map/map.php') )
1880
echo $c;
1881
}
1882
1883
+ /**
1884
+ * gets a world map
1885
+ * @param string $what visitors|reads|online
1886
+ * @param int $width size
1887
+ * @param int $height size
1888
+ * @param int $min : 1 disable title, legend and zoombar
1889
+ */
1890
+ function getMap( $what = 'visitors', $width = 500, $height = 340, $min = 0 )
1891
+ {
1892
+ $divid = uniqid('cpdmap_');
1893
+ $dir = $this->dir.'/map/';
1894
+ ?>
1895
+ <script type="text/javascript" src="<?php echo $dir ?>swfobject.js"></script>
1896
+ <div id="<?php echo $divid ?>" class="cpd_worldmap" style="width:<?php echo $width ?>px; height:<?php echo $height ?>px; background:#4499FF;">
1897
+ <strong>Flash content</strong>
1898
+ </div>
1899
+ <script type="text/javascript">
1900
+ // <![CDATA[
1901
+ var so = new SWFObject("<?php echo $dir ?>ammap.swf", "ammap", "100%", "100%", "8", "#4499FF");
1902
+ so.addVariable("path", "<?php echo $dir ?>");
1903
+ so.addVariable("settings_file", escape("<?php echo $dir ?>settings.xml.php?map=<?php echo $what ?>&min=<?php echo $min ?>"));
1904
+ so.addVariable("data_file", escape("<?php echo $dir ?>data.xml.php?map=<?php echo $what ?>&min=<?php echo $min ?>"));
1905
+ so.write("<?php echo $divid ?>");
1906
+ // ]]>
1907
+ </script>
1908
+ <?php
1909
+ }
1910
+
1911
+
1912
+
1913
+
1914
+
1915
/**
1916
* adds some shortcodes to use functions in frontend
1917
*/
1941
add_shortcode('CPD_MOST_VISITED_POSTS', array( &$this, 'shortMostVisitedPosts'));
1942
add_shortcode('CPD_REFERERS', array( &$this, 'shortReferers'));
1943
add_shortcode('CPD_POSTS_ON_DAY', array( &$this, 'shortPostsOnDay'));
1944
+ add_shortcode('CPD_MAP', array( &$this, 'shortShowMap'));
1945
}
1946
function shortShow() { return $this->show('', '', false, false); }
1947
function shortReadsTotal() { return $this->getReadsAll(true); }
1974
), $atts) );
1975
return $this->getVisitedPostsOnDay( $date, $limit, false, false, true );
1976
}
1977
+ function shortShowMap( $atts )
1978
+ {
1979
+ extract( shortcode_atts( array(
1980
+ 'width' => 500,
1981
+ 'height' => 340,
1982
+ 'what' => 'reads',
1983
+ 'min' => 0
1984
+ ), $atts) );
1985
+ return $this->getMap( $what, $width, $height, $min );
1986
+ }
1987
1988
/**
1989
* adds style sheet to admin header
2145
$code = '<div id="'.$id.'" class="cpd-list-chart"></div>
2146
<script type="text/javascript">
2147
//<![CDATA[
2148
+ if (jQuery("#'.$id.'").width() > 0)
2149
+ jQuery(function(){jQuery.plot(jQuery("#'.$id.'"),'.$d.',{series:{lines:{fill:true,lineWidth:1}},colors:["red"],grid:{show:false}});});
2150
//]]>
2151
</script>
2152
'.$html;
2352
function count_per_day_uninstall()
2353
{
2354
global $wpdb;
2355
+ // $wpdb->query('DROP TABLE IF EXISTS '.CPD_C_TABLE);
2356
+ // $wpdb->query('DROP TABLE IF EXISTS '.CPD_CO_TABLE);
2357
+ // $wpdb->query('DROP TABLE IF EXISTS '.CPD_N_TABLE);
2358
+ // delete_option('count_per_day');
2359
+ $wpdb->query("DELETE FROM ".$wpdb->usermeta." WHERE meta_key like '%_cpd_metaboxes%';");
2360
}
2361
2362
geoip/GeoIP.dat CHANGED
Binary file
geoip/geoip.php CHANGED
@@ -15,7 +15,7 @@ class CpdGeoIp
15
/**
16
* gets country of ip adress
17
* @param $ip IP
18
- * @return array e.g. ( 'de', image div , 'Germany' )
19
*/
20
function getCountry( $ip )
21
{
@@ -33,47 +33,62 @@ function getCountry( $ip )
33
return $country;
34
}
35
36
/**
37
* updates CountPerDay table
38
*/
39
function updateDB()
40
{
41
- global $count_per_day, $cpd_path, $wpdb;
42
43
- $count_per_day->mysqlQuery('rows', "SELECT country FROM $wpdb->cpd_counter LIMIT 1", 'GeoIP updateDB Table '.__LINE__);
44
if ((int) mysql_errno() == 1054)
45
// add row "country" to table
46
- $count_per_day->mysqlQuery('', "ALTER TABLE $wpdb->cpd_counter ADD `country` CHAR( 2 ) NOT NULL", 'GeoIP updateDB create column '.__LINE__);
47
48
- $limit = 20;
49
- $res = $count_per_day->mysqlQuery('rows', "SELECT ip, INET_NTOA(ip) realip FROM $wpdb->cpd_counter WHERE country LIKE '' GROUP BY ip LIMIT $limit", 'GeoIP updateDB '.__LINE__);
50
$gi = cpd_geoip_open($cpd_path.'/geoip/GeoIP.dat', GEOIP_STANDARD);
51
52
- foreach ($res as $r)
53
- {
54
- $c = '';
55
- $ip = explode('.', $r->realip);
56
- if ( $ip[0] == 10
57
- || $ip[0] == 127
58
- || ($ip[0] == 169 && $ip[1] == 254)
59
- || ($ip[0] == 172 && $ip[1] >= 16 && $ip[1] <= 31)
60
- || ($ip[0] == 192 && $ip[1] == 168) )
61
- // set local IPs to '-'
62
- $c = '-';
63
- else
64
- // get country
65
- $c = strtolower(cpd_geoip_country_code_by_addr($gi, $r->realip));
66
-
67
- if ( !empty($c) )
68
- $count_per_day->mysqlQuery('', "UPDATE $wpdb->cpd_counter SET country = '$c' WHERE ip = '$r->ip'", 'GeoIP updateDB '.__LINE__);
69
- }
70
71
cpd_geoip_close($gi);
72
73
- $rest = $count_per_day->mysqlQuery('var', "SELECT COUNT(*) FROM $wpdb->cpd_counter WHERE country like ''", 'GeoIP updateDB '.__LINE__);
74
- return (int) $rest;
75
}
76
77
/**
78
* updates the GeoIP database file
79
* works only if directory geoip has rights 777, set it in ftp client
15
/**
16
* gets country of ip adress
17
* @param $ip IP
18
+ * @return array e.g. ( 'de', image link to easywhois.com , 'Germany' )
19
*/
20
function getCountry( $ip )
21
{
33
return $country;
34
}
35
36
+
37
+
38
/**
39
* updates CountPerDay table
40
*/
41
function updateDB()
42
{
43
+ global $count_per_day;
44
+ global $cpd_path;
45
+ global $wpdb;
46
47
+ $count_per_day->getQuery("SELECT country FROM `".CPD_C_TABLE."`", 'GeoIP updateDB Table');
48
if ((int) mysql_errno() == 1054)
49
// add row "country" to table
50
+ $count_per_day->getQuery("ALTER TABLE `".CPD_C_TABLE."` ADD `country` CHAR( 2 ) NOT NULL", 'GeoIP updateDB create column');
51
52
+ $limit = 10;
53
+ $res = $count_per_day->getQuery("SELECT ip, INET_NTOA(ip) AS realip FROM ".CPD_C_TABLE." WHERE country LIKE '' GROUP BY ip LIMIT $limit;", 'GeoIP updateDB');
54
$gi = cpd_geoip_open($cpd_path.'/geoip/GeoIP.dat', GEOIP_STANDARD);
55
56
+ if ( @mysql_num_rows($res) )
57
+ while ( $r = mysql_fetch_array($res) )
58
+ {
59
+ $c = '';
60
+ $ip = explode('.', $r['realip']);
61
+ if ( $ip[0] == 10
62
+ || $ip[0] == 127
63
+ || ($ip[0] == 169 && $ip[1] == 254)
64
+ || ($ip[0] == 172 && $ip[1] >= 16 && $ip[1] <= 31)
65
+ || ($ip[0] == 192 && $ip[1] == 168) )
66
+ // set local IPs to '-'
67
+ $c = '-';
68
+ else
69
+ // get country
70
+ $c = strtolower(cpd_geoip_country_code_by_addr($gi, $r['realip']));
71
+
72
+ if ( !empty($c) )
73
+ $count_per_day->getQuery("UPDATE ".CPD_C_TABLE." SET country = '".$c."' WHERE ip = '".$r['ip']."'", 'GeoIP updateDB');
74
+ }
75
76
cpd_geoip_close($gi);
77
78
+ $res = $count_per_day->getQuery("SELECT count(*) FROM ".CPD_C_TABLE." WHERE country like ''", 'GeoIP updateDB');
79
+ if ( @mysql_num_rows($res) )
80
+ {
81
+ $row = mysql_fetch_array($res);
82
+ $rest = (!empty($row[0])) ? $row[0] : 0;
83
+ }
84
+ else
85
+ $rest = 0;
86
+
87
+ return $rest;
88
}
89
90
+
91
+
92
/**
93
* updates the GeoIP database file
94
* works only if directory geoip has rights 777, set it in ftp client
img/cpd_calendar.png CHANGED
Binary file
img/cpd_flags.png CHANGED
Binary file
img/cpd_pen.png CHANGED
Binary file
img/cpd_sprites.png DELETED
Binary file
js/jquery.flot.min.js CHANGED
@@ -1,1988 +1 @@
1
- (function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],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],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
2
- (function($) {
3
- function Plot(placeholder, data_, options_, plugins) {
4
- var series = [],
5
- options = {
6
- colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
7
- legend: {
8
- show: true,
9
- noColumns: 1,
10
- labelFormatter: null,
11
- labelBoxBorderColor: "#ccc",
12
- container: null,
13
- position: "ne",
14
- margin: 5,
15
- backgroundColor: null,
16
- backgroundOpacity: 0.85
17
- },
18
- xaxis: {
19
- show: null,
20
- position: "bottom",
21
- mode: null,
22
- font: null,
23
- color: null,
24
- tickColor: null,
25
- transform: null,
26
- inverseTransform: null,
27
- min: null,
28
- max: null,
29
- autoscaleMargin: null,
30
- ticks: null,
31
- tickFormatter: null,
32
- labelWidth: null,
33
- labelHeight: null,
34
- reserveSpace: null,
35
- tickLength: null,
36
- alignTicksWithAxis: null,
37
- tickDecimals: null,
38
- tickSize: null,
39
- minTickSize: null,
40
- monthNames: null,
41
- timeformat: null,
42
- twelveHourClock: false
43
- },
44
- yaxis: {
45
- autoscaleMargin: 0.02,
46
- position: "left"
47
- },
48
- xaxes: [],
49
- yaxes: [],
50
- series: {
51
- points: {
52
- show: false,
53
- radius: 3,
54
- lineWidth: 2,
55
- fill: true,
56
- fillColor: "#ffffff",
57
- symbol: "circle"
58
- },
59
- lines: {
60
- lineWidth: 2,
61
- fill: false,
62
- fillColor: null,
63
- steps: false
64
- },
65
- bars: {
66
- show: false,
67
- lineWidth: 2,
68
- barWidth: 1,
69
- fill: true,
70
- fillColor: null,
71
- align: "left",
72
- horizontal: false
73
- },
74
- shadowSize: 3
75
- },
76
- grid: {
77
- show: true,
78
- aboveData: false,
79
- color: "#545454",
80
- backgroundColor: null,
81
- borderColor: null,
82
- tickColor: null,
83
- labelMargin: 5,
84
- axisMargin: 8,
85
- borderWidth: 2,
86
- minBorderMargin: null,
87
- markings: null,
88
- markingsColor: "#f4f4f4",
89
- markingsLineWidth: 2,
90
- clickable: false,
91
- hoverable: false,
92
- autoHighlight: true,
93
- mouseActiveRadius: 10
94
- },
95
- interaction: {
96
- redrawOverlayInterval: 1000/60
97
- },
98
- hooks: {}
99
- },
100
- canvas = null,
101
- overlay = null,
102
- eventHolder = null,
103
- ctx = null, octx = null,
104
- xaxes = [], yaxes = [],
105
- plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
106
- canvasWidth = 0, canvasHeight = 0,
107
- plotWidth = 0, plotHeight = 0,
108
- hooks = {
109
- processOptions: [],
110
- processRawData: [],
111
- processDatapoints: [],
112
- drawSeries: [],
113
- draw: [],
114
- bindEvents: [],
115
- drawOverlay: [],
116
- shutdown: []
117
- },
118
- plot = this;
119
- plot.setData = setData;
120
- plot.setupGrid = setupGrid;
121
- plot.draw = draw;
122
- plot.getPlaceholder = function() { return placeholder; };
123
- plot.getCanvas = function() { return canvas; };
124
- plot.getPlotOffset = function() { return plotOffset; };
125
- plot.width = function () { return plotWidth; };
126
- plot.height = function () { return plotHeight; };
127
- plot.offset = function () {
128
- var o = eventHolder.offset();
129
- o.left += plotOffset.left;
130
- o.top += plotOffset.top;
131
- return o;
132
- };
133
- plot.getData = function () { return series; };
134
- plot.getAxes = function () {
135
- var res = {}, i;
136
- $.each(xaxes.concat(yaxes), function (_, axis) {
137
- if (axis)
138
- res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
139
- });
140
- return res;
141
- };
142
- plot.getXAxes = function () { return xaxes; };
143
- plot.getYAxes = function () { return yaxes; };
144
- plot.c2p = canvasToAxisCoords;
145
- plot.p2c = axisToCanvasCoords;
146
- plot.getOptions = function () { return options; };
147
- plot.highlight = highlight;
148
- plot.unhighlight = unhighlight;
149
- plot.triggerRedrawOverlay = triggerRedrawOverlay;
150
- plot.pointOffset = function(point) {
151
- return {
152
- left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left),
153
- top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top)
154
- };
155
- };
156
- plot.shutdown = shutdown;
157
- plot.resize = function () {
158
- getCanvasDimensions();
159
- resizeCanvas(canvas);
160
- resizeCanvas(overlay);
161
- };
162
- plot.hooks = hooks;
163
- initPlugins(plot);
164
- parseOptions(options_);
165
- setupCanvases();
166
- setData(data_);
167
- setupGrid();
168
- draw();
169
- bindEvents();
170
- function executeHooks(hook, args) {
171
- args = [plot].concat(args);
172
- for (var i = 0; i < hook.length; ++i)
173
- hook[i].apply(this, args);
174
- }
175
- function initPlugins() {
176
- for (var i = 0; i < plugins.length; ++i) {
177
- var p = plugins[i];
178
- p.init(plot);
179
- if (p.options)
180
- $.extend(true, options, p.options);
181
- }
182
- }
183
- function parseOptions(opts) {
184
- var i;
185
- $.extend(true, options, opts);
186
- if (options.xaxis.color == null)
187
- options.xaxis.color = options.grid.color;
188
- if (options.yaxis.color == null)
189
- options.yaxis.color = options.grid.color;
190
- if (options.xaxis.tickColor == null)
191
- options.xaxis.tickColor = options.grid.tickColor;
192
- if (options.yaxis.tickColor == null)
193
- options.yaxis.tickColor = options.grid.tickColor;
194
- if (options.grid.borderColor == null)
195
- options.grid.borderColor = options.grid.color;
196
- if (options.grid.tickColor == null)
197
- options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
198
- for (i = 0; i < Math.max(1, options.xaxes.length); ++i)
199
- options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]);
200
- for (i = 0; i < Math.max(1, options.yaxes.length); ++i)
201
- options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]);
202
- if (options.xaxis.noTicks && options.xaxis.ticks == null)
203
- options.xaxis.ticks = options.xaxis.noTicks;
204
- if (options.yaxis.noTicks && options.yaxis.ticks == null)
205
- options.yaxis.ticks = options.yaxis.noTicks;
206
- if (options.x2axis) {
207
- options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
208
- options.xaxes[1].position = "top";
209
- }
210
- if (options.y2axis) {
211
- options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
212
- options.yaxes[1].position = "right";
213
- }
214
- if (options.grid.coloredAreas)
215
- options.grid.markings = options.grid.coloredAreas;
216
- if (options.grid.coloredAreasColor)
217
- options.grid.markingsColor = options.grid.coloredAreasColor;
218
- if (options.lines)
219
- $.extend(true, options.series.lines, options.lines);
220
- if (options.points)
221
- $.extend(true, options.series.points, options.points);
222
- if (options.bars)
223
- $.extend(true, options.series.bars, options.bars);
224
- if (options.shadowSize != null)
225
- options.series.shadowSize = options.shadowSize;
226
- for (i = 0; i < options.xaxes.length; ++i)
227
- getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
228
- for (i = 0; i < options.yaxes.length; ++i)
229
- getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
230
- for (var n in hooks)
231
- if (options.hooks[n] && options.hooks[n].length)
232
- hooks[n] = hooks[n].concat(options.hooks[n]);
233
- executeHooks(hooks.processOptions, [options]);
234
- }
235
- function setData(d) {
236
- series = parseData(d);
237
- fillInSeriesOptions();
238
- processData();
239
- }
240
- function parseData(d) {
241
- var res = [];
242
- for (var i = 0; i < d.length; ++i) {
243
- var s = $.extend(true, {}, options.series);
244
- if (d[i].data != null) {
245
- s.data = d[i].data;
246
- delete d[i].data;
247
- $.extend(true, s, d[i]);
248
- d[i].data = s.data;
249
- }
250
- else
251
- s.data = d[i];
252
- res.push(s);
253
- }
254
- return res;
255
- }
256
- function axisNumber(obj, coord) {
257
- var a = obj[coord + "axis"];
258
- if (typeof a == "object")
259
- a = a.n;
260
- if (typeof a != "number")
261
- a = 1;
262
- return a;
263
- }
264
- function allAxes() {
265
- return $.grep(xaxes.concat(yaxes), function (a) { return a; });
266
- }
267
- function canvasToAxisCoords(pos) {
268
- var res = {}, i, axis;
269
- for (i = 0; i < xaxes.length; ++i) {
270
- axis = xaxes[i];
271
- if (axis && axis.used)
272
- res["x" + axis.n] = axis.c2p(pos.left);
273
- }
274
- for (i = 0; i < yaxes.length; ++i) {
275
- axis = yaxes[i];
276
- if (axis && axis.used)
277
- res["y" + axis.n] = axis.c2p(pos.top);
278
- }
279
- if (res.x1 !== undefined)
280
- res.x = res.x1;
281
- if (res.y1 !== undefined)
282
- res.y = res.y1;
283
- return res;
284
- }
285
- function axisToCanvasCoords(pos) {
286
- var res = {}, i, axis, key;
287
- for (i = 0; i < xaxes.length; ++i) {
288
- axis = xaxes[i];
289
- if (axis && axis.used) {
290
- key = "x" + axis.n;
291
- if (pos[key] == null && axis.n == 1)
292
- key = "x";
293
- if (pos[key] != null) {
294
- res.left = axis.p2c(pos[key]);
295
- break;
296
- }
297
- }
298
- }
299
- for (i = 0; i < yaxes.length; ++i) {
300
- axis = yaxes[i];
301
- if (axis && axis.used) {
302
- key = "y" + axis.n;
303
- if (pos[key] == null && axis.n == 1)
304
- key = "y";
305
- if (pos[key] != null) {
306
- res.top = axis.p2c(pos[key]);
307
- break;
308
- }
309
- }
310
- }
311
- return res;
312
- }
313
- function getOrCreateAxis(axes, number) {
314
- if (!axes[number - 1])
315
- axes[number - 1] = {
316
- n: number,
317
- direction: axes == xaxes ? "x" : "y",
318
- options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
319
- };
320
- return axes[number - 1];
321
- }
322
- function fillInSeriesOptions() {
323
- var i;
324
- var neededColors = series.length,
325
- usedColors = [],
326
- assignedColors = [];
327
- for (i = 0; i < series.length; ++i) {
328
- var sc = series[i].color;
329
- if (sc != null) {
330
- --neededColors;
331
- if (typeof sc == "number")
332
- assignedColors.push(sc);
333
- else
334
- usedColors.push($.color.parse(series[i].color));
335
- }
336
- }
337
- for (i = 0; i < assignedColors.length; ++i) {
338
- neededColors = Math.max(neededColors, assignedColors[i] + 1);
339
- }
340
- var colors = [], variation = 0;
341
- i = 0;
342
- while (colors.length < neededColors) {
343
- var c;
344
- if (options.colors.length == i)
345
- c = $.color.make(100, 100, 100);
346
- else
347
- c = $.color.parse(options.colors[i]);
348
- var sign = variation % 2 == 1 ? -1 : 1;
349
- c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2)
350
- colors.push(c);
351
- ++i;
352
- if (i >= options.colors.length) {
353
- i = 0;
354
- ++variation;
355
- }
356
- }
357
- var colori = 0, s;
358
- for (i = 0; i < series.length; ++i) {
359
- s = series[i];
360
- if (s.color == null) {
361
- s.color = colors[colori].toString();
362
- ++colori;
363
- }
364
- else if (typeof s.color == "number")
365
- s.color = colors[s.color].toString();
366
- if (s.lines.show == null) {
367
- var v, show = true;
368
- for (v in s)
369
- if (s[v] && s[v].show) {
370
- show = false;
371
- break;
372
- }
373
- if (show)
374
- s.lines.show = true;
375
- }
376
- s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
377
- s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
378
- }
379
- }
380
- function processData() {
381
- var topSentry = Number.POSITIVE_INFINITY,
382
- bottomSentry = Number.NEGATIVE_INFINITY,
383
- fakeInfinity = Number.MAX_VALUE,
384
- i, j, k, m, length,
385
- s, points, ps, x, y, axis, val, f, p;
386
- function updateAxis(axis, min, max) {
387
- if (min < axis.datamin && min != -fakeInfinity)
388
- axis.datamin = min;
389
- if (max > axis.datamax && max != fakeInfinity)
390
- axis.datamax = max;
391
- }
392
- $.each(allAxes(), function (_, axis) {
393
- axis.datamin = topSentry;
394
- axis.datamax = bottomSentry;
395
- axis.used = false;
396
- });
397
- for (i = 0; i < series.length; ++i) {
398
- s = series[i];
399
- s.datapoints = { points: [] };
400
- executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
401
- }
402
- for (i = 0; i < series.length; ++i) {
403
- s = series[i];
404
- var data = s.data, format = s.datapoints.format;
405
- if (!format) {
406
- format = [];
407
- format.push({ x: true, number: true, required: true });
408
- format.push({ y: true, number: true, required: true });
409
- if (s.bars.show || (s.lines.show && s.lines.fill)) {
410
- format.push({ y: true, number: true, required: false, defaultValue: 0 });
411
- if (s.bars.horizontal) {
412
- delete format[format.length - 1].y;
413
- format[format.length - 1].x = true;
414
- }
415
- }
416
- s.datapoints.format = format;
417
- }
418
- if (s.datapoints.pointsize != null)
419
- continue;
420
- s.datapoints.pointsize = format.length;
421
- ps = s.datapoints.pointsize;
422
- points = s.datapoints.points;
423
- insertSteps = s.lines.show && s.lines.steps;
424
- s.xaxis.used = s.yaxis.used = true;
425
- for (j = k = 0; j < data.length; ++j, k += ps) {
426
- p = data[j];
427
- var nullify = p == null;
428
- if (!nullify) {
429
- for (m = 0; m < ps; ++m) {
430
- val = p[m];
431
- f = format[m];
432
- if (f) {
433
- if (f.number && val != null) {
434
- val = +val;
435
- if (isNaN(val))
436
- val = null;
437
- else if (val == Infinity)
438
- val = fakeInfinity;
439
- else if (val == -Infinity)
440
- val = -fakeInfinity;
441
- }
442
- if (val == null) {
443
- if (f.required)
444
- nullify = true;
445
- if (f.defaultValue != null)
446
- val = f.defaultValue;
447
- }
448
- }
449
- points[k + m] = val;
450
- }
451
- }
452
- if (nullify) {
453
- for (m = 0; m < ps; ++m) {
454
- val = points[k + m];
455
- if (val != null) {
456
- f = format[m];
457
- if (f.x)
458
- updateAxis(s.xaxis, val, val);
459
- if (f.y)
460
- updateAxis(s.yaxis, val, val);
461
- }
462
- points[k + m] = null;
463
- }
464
- }
465
- else {
466
- if (insertSteps && k > 0
467
- && points[k - ps] != null
468
- && points[k - ps] != points[k]
469
- && points[k - ps + 1] != points[k + 1]) {
470
- for (m = 0; m < ps; ++m)
471
- points[k + ps + m] = points[k + m];
472
- points[k + 1] = points[k - ps + 1];
473
- k += ps;
474
- }
475
- }
476
- }
477
- }
478
- for (i = 0; i < series.length; ++i) {
479
- s = series[i];
480
- executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
481
- }
482
- for (i = 0; i < series.length; ++i) {
483
- s = series[i];
484
- points = s.datapoints.points,
485
- ps = s.datapoints.pointsize;
486
- var xmin = topSentry, ymin = topSentry,
487
- xmax = bottomSentry, ymax = bottomSentry;
488
- for (j = 0; j < points.length; j += ps) {
489
- if (points[j] == null)
490
- continue;
491
- for (m = 0; m < ps; ++m) {
492
- val = points[j + m];
493
- f = format[m];
494
- if (!f || val == fakeInfinity || val == -fakeInfinity)
495
- continue;
496
- if (f.x) {
497
- if (val < xmin)
498
- xmin = val;
499
- if (val > xmax)
500
- xmax = val;
501
- }
502
- if (f.y) {
503
- if (val < ymin)
504
- ymin = val;
505
- if (val > ymax)
506
- ymax = val;
507
- }
508
- }
509
- }
510
- if (s.bars.show) {
511
- var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2;
512
- if (s.bars.horizontal) {
513
- ymin += delta;
514
- ymax += delta + s.bars.barWidth;
515
- }
516
- else {
517
- xmin += delta;
518
- xmax += delta + s.bars.barWidth;
519
- }
520
- }
521
- updateAxis(s.xaxis, xmin, xmax);
522
- updateAxis(s.yaxis, ymin, ymax);
523
- }
524
- $.each(allAxes(), function (_, axis) {
525
- if (axis.datamin == topSentry)
526
- axis.datamin = null;
527
- if (axis.datamax == bottomSentry)
528
- axis.datamax = null;
529
- });
530
- }
531
- function makeCanvas(skipPositioning, cls) {
532
- var c = document.createElement('canvas');
533
- c.className = cls;
534
- c.width = canvasWidth;
535
- c.height = canvasHeight;
536
- if (!skipPositioning)
537
- $(c).css({ position: 'absolute', left: 0, top: 0 });
538
- $(c).appendTo(placeholder);
539
- if (!c.getContext)
540
- c = window.G_vmlCanvasManager.initElement(c);
541
- c.getContext("2d").save();
542
- return c;
543
- }
544
- function getCanvasDimensions() {
545
- canvasWidth = placeholder.width();
546
- canvasHeight = placeholder.height();
547
- if (canvasWidth <= 0 || canvasHeight <= 0)
548
- throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
549
- }
550
- function resizeCanvas(c) {
551
- if (c.width != canvasWidth)
552
- c.width = canvasWidth;
553
- if (c.height != canvasHeight)
554
- c.height = canvasHeight;
555
- var cctx = c.getContext("2d");
556
- cctx.restore();
557
- cctx.save();
558
- }
559
- function setupCanvases() {
560
- var reused,
561
- existingCanvas = placeholder.children("canvas.base"),
562
- existingOverlay = placeholder.children("canvas.overlay");
563
- if (existingCanvas.length == 0 || existingOverlay == 0) {
564
- placeholder.html("");
565
- placeholder.css({ padding: 0 });
566
- if (placeholder.css("position") == 'static')
567
- placeholder.css("position", "relative");
568
- getCanvasDimensions();
569
- canvas = makeCanvas(true, "base");
570
- overlay = makeCanvas(false, "overlay");
571
- reused = false;
572
- }
573
- else {
574
- canvas = existingCanvas.get(0);
575
- overlay = existingOverlay.get(0);
576
- reused = true;
577
- }
578
- ctx = canvas.getContext("2d");
579
- octx = overlay.getContext("2d");
580
- eventHolder = $(overlay);
581
- if (reused) {
582
- placeholder.data("plot").shutdown();
583
- plot.resize();
584
- octx.clearRect(0, 0, canvasWidth, canvasHeight);
585
- eventHolder.unbind();
586
- placeholder.children().not([canvas, overlay]).remove();
587
- }
588
- placeholder.data("plot", plot);
589
- }
590
- function bindEvents() {
591
- if (options.grid.hoverable) {
592
- eventHolder.mousemove(onMouseMove);
593
- eventHolder.mouseleave(onMouseLeave);
594
- }
595
- if (options.grid.clickable)
596
- eventHolder.click(onClick);
597
- executeHooks(hooks.bindEvents, [eventHolder]);
598
- }
599
- function shutdown() {
600
- if (redrawTimeout)
601
- clearTimeout(redrawTimeout);
602
- eventHolder.unbind("mousemove", onMouseMove);
603
- eventHolder.unbind("mouseleave", onMouseLeave);
604
- eventHolder.unbind("click", onClick);
605
- executeHooks(hooks.shutdown, [eventHolder]);
606
- }
607
- function setTransformationHelpers(axis) {
608
- function identity(x) { return x; }
609
- var s, m, t = axis.options.transform || identity,
610
- it = axis.options.inverseTransform;
611
- if (axis.direction == "x") {
612
- s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
613
- m = Math.min(t(axis.max), t(axis.min));
614
- }
615
- else {
616
- s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
617
- s = -s;
618
- m = Math.max(t(axis.max), t(axis.min));
619
- }
620
- if (t == identity)
621
- axis.p2c = function (p) { return (p - m) * s; };
622
- else
623
- axis.p2c = function (p) { return (t(p) - m) * s; };
624
- if (!it)
625
- axis.c2p = function (c) { return m + c / s; };
626
- else
627
- axis.c2p = function (c) { return it(m + c / s); };
628
- }
629
- function measureTickLabels(axis) {
630
- var opts = axis.options, ticks = axis.ticks || [],
631
- axisw = opts.labelWidth || 0, axish = opts.labelHeight || 0,
632
- f = axis.font;
633
- if (opts.labelWidth == null || opts.labelHeight == null) {
634
- ctx.save();
635
- ctx.font = f.style + " " + f.variant + " " + f.weight + " " + f.size + "px '" + f.family + "'";
636
- for (var i = 0; i < ticks.length; ++i) {
637
- var t = ticks[i];
638
- t.lines = [];
639
- t.width = t.height = 0;
640
- if (!t.label)
641
- continue;
642
- var lines = t.label.replace(/<br ?\/?>|\r\n|\r/g, "\n").split("\n");
643
- for (var j = 0; j < lines.length; ++j) {
644
- var line = { text: lines[j] },
645
- m = ctx.measureText(line.text);
646
- line.width = m.width;
647
- line.height = m.height != null ? m.height : f.size;
648
- line.height += Math.round(f.size * 0.15);
649
- t.width = Math.max(line.width, t.width);
650
- t.height += line.height;
651
- t.lines.push(line);
652
- }
653
- if (opts.labelWidth == null)
654
- axisw = Math.max(axisw, t.width);
655
- if (opts.labelHeight == null)
656
- axish = Math.max(axish, t.height);
657
- }
658
- ctx.restore();
659
- }
660
- axis.labelWidth = Math.ceil(axisw);
661
- axis.labelHeight = Math.ceil(axish);
662
- }
663
- function allocateAxisBoxFirstPhase(axis) {
664
- var lw = axis.labelWidth,
665
- lh = axis.labelHeight,
666
- pos = axis.options.position,
667
- tickLength = axis.options.tickLength,
668
- axisMargin = options.grid.axisMargin,
669
- padding = options.grid.labelMargin,
670
- all = axis.direction == "x" ? xaxes : yaxes,
671
- index;
672
- var samePosition = $.grep(all, function (a) {
673
- return a && a.options.position == pos && a.reserveSpace;
674
- });
675
- if ($.inArray(axis, samePosition) == samePosition.length - 1)
676
- axisMargin = 0;
677
- if (tickLength == null) {
678
- var sameDirection = $.grep(all, function (a) {
679
- return a && a.reserveSpace;
680
- });
681
- var innermost = $.inArray(axis, sameDirection) == 0;
682
- if (innermost)
683
- tickLength = "full"
684
- else
685
- tickLength = 5;
686
- }
687
- if (!isNaN(+tickLength))
688
- padding += +tickLength;
689
- if (axis.direction == "x") {
690
- lh += padding;
691
- if (pos == "bottom") {
692
- plotOffset.bottom += lh + axisMargin;
693
- axis.box = { top: canvasHeight - plotOffset.bottom, height: lh };
694
- }
695
- else {
696
- axis.box = { top: plotOffset.top + axisMargin, height: lh };
697
- plotOffset.top += lh + axisMargin;
698
- }
699
- }
700
- else {
701
- lw += padding;
702
- if (pos == "left") {
703
- axis.box = { left: plotOffset.left + axisMargin, width: lw };
704
- plotOffset.left += lw + axisMargin;
705
- }
706
- else {
707
- plotOffset.right += lw + axisMargin;
708
- axis.box = { left: canvasWidth - plotOffset.right, width: lw };
709
- }
710
- }
711
- axis.position = pos;
712
- axis.tickLength = tickLength;
713
- axis.box.padding = padding;
714
- axis.innermost = innermost;
715
- }
716
- function allocateAxisBoxSecondPhase(axis) {
717
- if (axis.direction == "x") {
718
- axis.box.left = plotOffset.left - axis.labelWidth / 2;
719
- axis.box.width = canvasWidth - plotOffset.left - plotOffset.right + axis.labelWidth;
720
- }
721
- else {
722
- axis.box.top = plotOffset.top - axis.labelHeight / 2;
723
- axis.box.height = canvasHeight - plotOffset.bottom - plotOffset.top + axis.labelHeight;
724
- }
725
- }
726
- function adjustLayoutForThingsStickingOut() {
727
- var minMargin = options.grid.minBorderMargin,
728
- margins = { x: 0, y: 0 }, i, axis;
729
- if (minMargin == null) {
730
- minMargin = 0;
731
- for (i = 0; i < series.length; ++i)
732
- minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
733
- }
734
- margins.x = margins.y = minMargin;
735
- $.each(allAxes(), function (_, axis) {
736
- var dir = axis.direction;
737
- if (axis.reserveSpace)
738
- margins[dir] = Math.max(margins[dir], (dir == "x" ? axis.labelWidth : axis.labelHeight) / 2);
739
- });
740
- plotOffset.left = Math.max(margins.x, plotOffset.left);
741
- plotOffset.right = Math.max(margins.x, plotOffset.right);
742
- plotOffset.top = Math.max(margins.y, plotOffset.top);
743
- plotOffset.bottom = Math.max(margins.y, plotOffset.bottom);
744
- }
745
- function setupGrid() {
746
- var i, axes = allAxes(), showGrid = options.grid.show;
747
- for (var a in plotOffset)
748
- plotOffset[a] = showGrid ? options.grid.borderWidth : 0;
749
- $.each(axes, function (_, axis) {
750
- axis.show = axis.options.show;
751
- if (axis.show == null)
752
- axis.show = axis.used;
753
- axis.reserveSpace = axis.show || axis.options.reserveSpace;
754
- setRange(axis);
755
- });
756
- if (showGrid) {
757
- var fontDefaults = {
758
- style: placeholder.css("font-style"),
759
- size: Math.round(0.8 * (+placeholder.css("font-size").replace("px", "") || 13)),
760
- variant: placeholder.css("font-variant"),
761
- weight: placeholder.css("font-weight"),
762
- family: placeholder.css("font-family")
763
- };
764
- var allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; });
765
- $.each(allocatedAxes, function (_, axis) {
766
- setupTickGeneration(axis);
767
- setTicks(axis);
768
- snapRangeToTicks(axis, axis.ticks);
769
- axis.font = $.extend({}, fontDefaults, axis.options.font);
770
- measureTickLabels(axis);
771
- });
772
- for (i = allocatedAxes.length - 1; i >= 0; --i)
773
- allocateAxisBoxFirstPhase(allocatedAxes[i]);
774
- adjustLayoutForThingsStickingOut();
775
- $.each(allocatedAxes, function (_, axis) {
776
- allocateAxisBoxSecondPhase(axis);
777
- });
778
- }
779
- plotWidth = canvasWidth - plotOffset.left - plotOffset.right;
780
- plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;
781
- $.each(axes, function (_, axis) {
782
- setTransformationHelpers(axis);
783
- });
784
- insertLegend();
785
- }
786
- function setRange(axis) {
787
- var opts = axis.options,
788
- min = +(opts.min != null ? opts.min : axis.datamin),
789
- max = +(opts.max != null ? opts.max : axis.datamax),
790
- delta = max - min;
791
- if (delta == 0.0) {
792
- var widen = max == 0 ? 1 : 0.01;
793
- if (opts.min == null)
794
- min -= widen;
795
- if (opts.max == null || opts.min != null)
796
- max += widen;
797
- }
798
- else {
799
- var margin = opts.autoscaleMargin;
800
- if (margin != null) {
801
- if (opts.min == null) {
802
- min -= delta * margin;
803
- if (min < 0 && axis.datamin != null && axis.datamin >= 0)
804
- min = 0;
805
- }
806
- if (opts.max == null) {
807
- max += delta * margin;
808
- if (max > 0 && axis.datamax != null && axis.datamax <= 0)
809
- max = 0;
810
- }
811
- }
812
- }
813
- axis.min = min;
814
- axis.max = max;
815
- }
816
- function setupTickGeneration(axis) {
817
- var opts = axis.options;
818
- var noTicks;
819
- if (typeof opts.ticks == "number" && opts.ticks > 0)
820
- noTicks = opts.ticks;
821
- else
822
- noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight);
823
- var delta = (axis.max - axis.min) / noTicks,
824
- size, generator, unit, formatter, i, magn, norm;
825
- if (opts.mode == "time") {
826
- var timeUnitSize = {
827
- "second": 1000,
828
- "minute": 60 * 1000,
829
- "hour": 60 * 60 * 1000,
830
- "day": 24 * 60 * 60 * 1000,
831
- "month": 30 * 24 * 60 * 60 * 1000,
832
- "year": 365.2425 * 24 * 60 * 60 * 1000
833
- };
834
- var spec = [
835
- [1, "second"], [2, "second"], [5, "second"], [10, "second"],
836
- [30, "second"],
837
- [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
838
- [30, "minute"],
839
- [1, "hour"], [2, "hour"], [4, "hour"],
840
- [8, "hour"], [12, "hour"],
841
- [1, "day"], [2, "day"], [3, "day"],
842
- [0.25, "month"], [0.5, "month"], [1, "month"],
843
- [2, "month"], [3, "month"], [6, "month"],
844
- [1, "year"]
845
- ];
846
- var minSize = 0;
847
- if (opts.minTickSize != null) {
848
- if (typeof opts.tickSize == "number")
849
- minSize = opts.tickSize;
850
- else
851
- minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
852
- }
853
- for (var i = 0; i < spec.length - 1; ++i)
854
- if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]
855
- + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
856
- && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)
857
- break;
858
- size = spec[i][0];
859
- unit = spec[i][1];
860
- if (unit == "year") {
861
- magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));
862
- norm = (delta / timeUnitSize.year) / magn;
863
- if (norm < 1.5)
864
- size = 1;
865
- else if (norm < 3)
866
- size = 2;
867
- else if (norm < 7.5)
868
- size = 5;
869
- else
870
- size = 10;
871
- size *= magn;
872
- }
873
- axis.tickSize = opts.tickSize || [size, unit];
874
- generator = function(axis) {
875
- var ticks = [],
876
- tickSize = axis.tickSize[0], unit = axis.tickSize[1],
877
- d = new Date(axis.min);
878
- var step = tickSize * timeUnitSize[unit];
879
- if (unit == "second")
880
- d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));
881
- if (unit == "minute")
882
- d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));
883
- if (unit == "hour")
884
- d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));
885
- if (unit == "month")
886
- d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));
887
- if (unit == "year")
888
- d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));
889
- d.setUTCMilliseconds(0);
890
- if (step >= timeUnitSize.minute)
891
- d.setUTCSeconds(0);
892
- if (step >= timeUnitSize.hour)
893
- d.setUTCMinutes(0);
894
- if (step >= timeUnitSize.day)
895
- d.setUTCHours(0);
896
- if (step >= timeUnitSize.day * 4)
897
- d.setUTCDate(1);
898
- if (step >= timeUnitSize.year)
899
- d.setUTCMonth(0);
900
- var carry = 0, v = Number.NaN, prev;
901
- do {
902
- prev = v;
903
- v = d.getTime();
904
- ticks.push(v);
905
- if (unit == "month") {
906
- if (tickSize < 1) {
907
- d.setUTCDate(1);
908
- var start = d.getTime();
909
- d.setUTCMonth(d.getUTCMonth() + 1);
910
- var end = d.getTime();
911
- d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
912
- carry = d.getUTCHours();
913
- d.setUTCHours(0);
914
- }
915
- else
916
- d.setUTCMonth(d.getUTCMonth() + tickSize);
917
- }
918
- else if (unit == "year") {
919
- d.setUTCFullYear(d.getUTCFullYear() + tickSize);
920
- }
921
- else
922
- d.setTime(v + step);
923
- } while (v < axis.max && v != prev);
924
- return ticks;
925
- };
926
- formatter = function (v, axis) {
927
- var d = new Date(v);
928
- if (opts.timeformat != null)
929
- return $.plot.formatDate(d, opts.timeformat, opts.monthNames);
930
- var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
931
- var span = axis.max - axis.min;
932
- var suffix = (opts.twelveHourClock) ? " %p" : "";
933
- if (t < timeUnitSize.minute)
934
- fmt = "%h:%M:%S" + suffix;
935
- else if (t < timeUnitSize.day) {
936
- if (span < 2 * timeUnitSize.day)
937
- fmt = "%h:%M" + suffix;
938
- else
939
- fmt = "%b %d %h:%M" + suffix;
940
- }
941
- else if (t < timeUnitSize.month)
942
- fmt = "%b %d";
943
- else if (t < timeUnitSize.year) {
944
- if (span < timeUnitSize.year)
945
- fmt = "%b";
946
- else
947
- fmt = "%b %y";
948
- }
949
- else
950
- fmt = "%y";
951
- return $.plot.formatDate(d, fmt, opts.monthNames);
952
- };
953
- }
954
- else {
955
- var maxDec = opts.tickDecimals;
956
- var dec = -Math.floor(Math.log(delta) / Math.LN10);
957
- if (maxDec != null && dec > maxDec)
958
- dec = maxDec;
959
- magn = Math.pow(10, -dec);
960
- norm = delta / magn;
961
- if (norm < 1.5)
962
- size = 1;
963
- else if (norm < 3) {
964
- size = 2;
965
- if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
966
- size = 2.5;
967
- ++dec;
968
- }
969
- }
970
- else if (norm < 7.5)
971
- size = 5;
972
- else
973
- size = 10;
974
- size *= magn;
975
- if (opts.minTickSize != null && size < opts.minTickSize)
976
- size = opts.minTickSize;
977
- axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
978
- axis.tickSize = opts.tickSize || size;
979
- generator = function (axis) {
980
- var ticks = [];
981
- var start = floorInBase(axis.min, axis.tickSize),
982
- i = 0, v = Number.NaN, prev;
983
- do {
984
- prev = v;
985
- v = start + i * axis.tickSize;
986
- ticks.push(v);
987
- ++i;
988
- } while (v < axis.max && v != prev);
989
- return ticks;
990
- };
991
- formatter = function (v, axis) {
992
- return v.toFixed(axis.tickDecimals);
993
- };
994
- }
995
- if (opts.alignTicksWithAxis != null) {
996
- var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
997
- if (otherAxis && otherAxis.used && otherAxis != axis) {
998
- var niceTicks = generator(axis);
999
- if (niceTicks.length > 0) {
1000
- if (opts.min == null)
1001
- axis.min = Math.min(axis.min, niceTicks[0]);
1002
- if (opts.max == null && niceTicks.length > 1)
1003
- axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
1004
- }
1005
- generator = function (axis) {
1006
- var ticks = [], v, i;
1007
- for (i = 0; i < otherAxis.ticks.length; ++i) {
1008
- v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
1009
- v = axis.min + v * (axis.max - axis.min);
1010
- ticks.push(v);
1011
- }
1012
- return ticks;
1013
- };
1014
- if (axis.mode != "time" && opts.tickDecimals == null) {
1015
- var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1),
1016
- ts = generator(axis);
1017
- if (!(ts.length > 1 && /\..*0#x2F;.test((ts[1] - ts[0]).toFixed(extraDec))))
1018
- axis.tickDecimals = extraDec;
1019
- }
1020
- }
1021
- }
1022
- axis.tickGenerator = generator;
1023
- if ($.isFunction(opts.tickFormatter))
1024
- axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
1025
- else
1026
- axis.tickFormatter = formatter;
1027
- }
1028
- function setTicks(axis) {
1029
- var oticks = axis.options.ticks, ticks = [];
1030
- if (oticks == null || (typeof oticks == "number" && oticks > 0))
1031
- ticks = axis.tickGenerator(axis);
1032
- else if (oticks) {
1033
- if ($.isFunction(oticks))
1034
- ticks = oticks({ min: axis.min, max: axis.max });
1035
- else
1036
- ticks = oticks;
1037
- }
1038
- var i, v;
1039
- axis.ticks = [];
1040
- for (i = 0; i < ticks.length; ++i) {
1041
- var label = null;
1042
- var t = ticks[i];
1043
- if (typeof t == "object") {
1044
- v = +t[0];
1045
- if (t.length > 1)
1046
- label = t[1];
1047
- }
1048
- else
1049
- v = +t;
1050
- if (label == null)
1051
- label = axis.tickFormatter(v, axis);
1052
- if (!isNaN(v))
1053
- axis.ticks.push({ v: v, label: label });
1054
- }
1055
- }
1056
- function snapRangeToTicks(axis, ticks) {
1057
- if (axis.options.autoscaleMargin && ticks.length > 0) {
1058
- if (axis.options.min == null)
1059
- axis.min = Math.min(axis.min, ticks[0].v);
1060
- if (axis.options.max == null && ticks.length > 1)
1061
- axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
1062
- }
1063
- }
1064
- function draw() {
1065
- ctx.clearRect(0, 0, canvasWidth, canvasHeight);
1066
- var grid = options.grid;
1067
- if (grid.show && grid.backgroundColor)
1068
- drawBackground();
1069
- if (grid.show && !grid.aboveData) {
1070
- drawGrid();
1071
- drawAxisLabels();
1072
- }
1073
- for (var i = 0; i < series.length; ++i) {
1074
- executeHooks(hooks.drawSeries, [ctx, series[i]]);
1075
- drawSeries(series[i]);
1076
- }
1077
- executeHooks(hooks.draw, [ctx]);
1078
- if (grid.show && grid.aboveData) {
1079
- drawGrid();
1080
- drawAxisLabels();
1081
- }
1082
- }
1083
- function extractRange(ranges, coord) {
1084
- var axis, from, to, key, axes = allAxes();
1085
- for (i = 0; i < axes.length; ++i) {
1086
- axis = axes[i];
1087
- if (axis.direction == coord) {
1088
- key = coord + axis.n + "axis";
1089
- if (!ranges[key] && axis.n == 1)
1090
- key = coord + "axis";
1091
- if (ranges[key]) {
1092
- from = ranges[key].from;
1093
- to = ranges[key].to;
1094
- break;
1095
- }
1096
- }
1097
- }
1098
- if (!ranges[key]) {
1099
- axis = coord == "x" ? xaxes[0] : yaxes[0];
1100
- from = ranges[coord + "1"];
1101
- to = ranges[coord + "2"];
1102
- }
1103
- if (from != null && to != null && from > to) {
1104
- var tmp = from;
1105
- from = to;
1106
- to = tmp;
1107
- }
1108
- return { from: from, to: to, axis: axis };
1109
- }
1110
- function drawBackground() {
1111
- ctx.save();
1112
- ctx.translate(plotOffset.left, plotOffset.top);
1113
- ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
1114
- ctx.fillRect(0, 0, plotWidth, plotHeight);
1115
- ctx.restore();
1116
- }
1117
- function drawGrid() {
1118
- var i;
1119
- ctx.save();
1120
- ctx.translate(plotOffset.left, plotOffset.top);
1121
- var markings = options.grid.markings;
1122
- if (markings) {
1123
- if ($.isFunction(markings)) {
1124
- var axes = plot.getAxes();
1125
- axes.xmin = axes.xaxis.min;
1126
- axes.xmax = axes.xaxis.max;
1127
- axes.ymin = axes.yaxis.min;
1128
- axes.ymax = axes.yaxis.max;
1129
- markings = markings(axes);
1130
- }
1131
- for (i = 0; i < markings.length; ++i) {
1132
- var m = markings[i],
1133
- xrange = extractRange(m, "x"),
1134
- yrange = extractRange(m, "y");
1135
- if (xrange.from == null)
1136
- xrange.from = xrange.axis.min;
1137
- if (xrange.to == null)
1138
- xrange.to = xrange.axis.max;
1139
- if (yrange.from == null)
1140
- yrange.from = yrange.axis.min;
1141
- if (yrange.to == null)
1142
- yrange.to = yrange.axis.max;
1143
- if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
1144
- yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
1145
- continue;
1146
- xrange.from = Math.max(xrange.from, xrange.axis.min);
1147
- xrange.to = Math.min(xrange.to, xrange.axis.max);
1148
- yrange.from = Math.max(yrange.from, yrange.axis.min);
1149
- yrange.to = Math.min(yrange.to, yrange.axis.max);
1150
- if (xrange.from == xrange.to && yrange.from == yrange.to)
1151
- continue;
1152
- xrange.from = xrange.axis.p2c(xrange.from);
1153
- xrange.to = xrange.axis.p2c(xrange.to);
1154
- yrange.from = yrange.axis.p2c(yrange.from);
1155
- yrange.to = yrange.axis.p2c(yrange.to);
1156
- if (xrange.from == xrange.to || yrange.from == yrange.to) {
1157
- ctx.beginPath();
1158
- ctx.strokeStyle = m.color || options.grid.markingsColor;
1159
- ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;
1160
- ctx.moveTo(xrange.from, yrange.from);
1161
- ctx.lineTo(xrange.to, yrange.to);
1162
- ctx.stroke();
1163
- }
1164
- else {
1165
- ctx.fillStyle = m.color || options.grid.markingsColor;
1166
- ctx.fillRect(xrange.from, yrange.to,
1167
- xrange.to - xrange.from,
1168
- yrange.from - yrange.to);
1169
- }
1170
- }
1171
- }
1172
- var axes = allAxes(), bw = options.grid.borderWidth;
1173
- for (var j = 0; j < axes.length; ++j) {
1174
- var axis = axes[j], box = axis.box,
1175
- t = axis.tickLength, x, y, xoff, yoff;
1176
- if (!axis.show || axis.ticks.length == 0)
1177
- continue;
1178
- ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString();
1179
- ctx.lineWidth = 1;
1180
- if (axis.direction == "x") {
1181
- x = 0;
1182
- if (t == "full")
1183
- y = (axis.position == "top" ? 0 : plotHeight);
1184
- else
1185
- y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
1186
- }
1187
- else {
1188
- y = 0;
1189
- if (t == "full")
1190
- x = (axis.position == "left" ? 0 : plotWidth);
1191
- else
1192
- x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
1193
- }
1194
- if (!axis.innermost) {
1195
- ctx.beginPath();
1196
- xoff = yoff = 0;
1197
- if (axis.direction == "x")
1198
- xoff = plotWidth;
1199
- else
1200
- yoff = plotHeight;
1201
- if (ctx.lineWidth == 1) {
1202
- x = Math.floor(x) + 0.5;
1203
- y = Math.floor(y) + 0.5;
1204
- }
1205
- ctx.moveTo(x, y);
1206
- ctx.lineTo(x + xoff, y + yoff);
1207
- ctx.stroke();
1208
- }
1209
- ctx.beginPath();
1210
- for (i = 0; i < axis.ticks.length; ++i) {
1211
- var v = axis.ticks[i].v;
1212
- xoff = yoff = 0;
1213
- if (v < axis.min || v > axis.max
1214
- || (t == "full" && bw > 0
1215
- && (v == axis.min || v == axis.max)))
1216
- continue;
1217
- if (axis.direction == "x") {
1218
- x = axis.p2c(v);
1219
- yoff = t == "full" ? -plotHeight : t;
1220
- if (axis.position == "top")
1221
- yoff = -yoff;
1222
- }
1223
- else {
1224
- y = axis.p2c(v);
1225
- xoff = t == "full" ? -plotWidth : t;
1226
- if (axis.position == "left")
1227
- xoff = -xoff;
1228
- }
1229
- if (ctx.lineWidth == 1) {
1230
- if (axis.direction == "x")
1231
- x = Math.floor(x) + 0.5;
1232
- else
1233
- y = Math.floor(y) + 0.5;
1234
- }
1235
- ctx.moveTo(x, y);
1236
- ctx.lineTo(x + xoff, y + yoff);
1237
- }
1238
- ctx.stroke();
1239
- }
1240
- if (bw) {
1241
- ctx.lineWidth = bw;
1242
- ctx.strokeStyle = options.grid.borderColor;
1243
- ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
1244
- }
1245
- ctx.restore();
1246
- }
1247
- function drawAxisLabels() {
1248
- ctx.save();
1249
- $.each(allAxes(), function (_, axis) {
1250
- if (!axis.show || axis.ticks.length == 0)
1251
- return;
1252
- var box = axis.box, f = axis.font;
1253
- ctx.fillStyle = axis.options.color;
1254
- ctx.font = f.style + " " + f.variant + " " + f.weight + " " + f.size + "px '" + f.family + "'";
1255
- ctx.textAlign = "start";
1256
- ctx.textBaseline = "middle";
1257
- for (var i = 0; i < axis.ticks.length; ++i) {
1258
- var tick = axis.ticks[i];
1259
- if (!tick.label || tick.v < axis.min || tick.v > axis.max)
1260
- continue;
1261
- var x, y, offset = 0, line;
1262
- for (var k = 0; k < tick.lines.length; ++k) {
1263
- line = tick.lines[k];
1264
- if (axis.direction == "x") {
1265
- x = plotOffset.left + axis.p2c(tick.v) - line.width/2;
1266
- if (axis.position == "bottom")
1267
- y = box.top + box.padding;
1268
- else
1269
- y = box.top + box.height - box.padding - tick.height;
1270
- }
1271
- else {
1272
- y = plotOffset.top + axis.p2c(tick.v) - tick.height/2;
1273
- if (axis.position == "left")
1274
- x = box.left + box.width - box.padding - line.width;
1275
- else
1276
- x = box.left + box.padding;
1277
- }
1278
- y += line.height/2 + offset;
1279
- offset += line.height;
1280
- if ($.browser.opera) {
1281
- x = Math.floor(x);
1282
- y = Math.ceil(y - 2);
1283
- }
1284
- ctx.fillText(line.text, x, y);
1285
- }
1286
- }
1287
- });
1288
- ctx.restore();
1289
- }
1290
- function drawSeries(series) {
1291
- if (series.lines.show)
1292
- drawSeriesLines(series);
1293
- if (series.bars.show)
1294
- drawSeriesBars(series);
1295
- if (series.points.show)
1296
- drawSeriesPoints(series);
1297
- }
1298
- function drawSeriesLines(series) {
1299
- function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
1300
- var points = datapoints.points,
1301
- ps = datapoints.pointsize,
1302
- prevx = null, prevy = null;
1303
- ctx.beginPath();
1304
- for (var i = ps; i < points.length; i += ps) {
1305
- var x1 = points[i - ps], y1 = points[i - ps + 1],
1306
- x2 = points[i], y2 = points[i + 1];
1307
- if (x1 == null || x2 == null)
1308
- continue;
1309
- if (y1 <= y2 && y1 < axisy.min) {
1310
- if (y2 < axisy.min)
1311
- continue;
1312
- x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
1313
- y1 = axisy.min;
1314
- }
1315
- else if (y2 <= y1 && y2 < axisy.min) {
1316
- if (y1 < axisy.min)
1317
- continue;
1318
- x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
1319
- y2 = axisy.min;
1320
- }
1321
- if (y1 >= y2 && y1 > axisy.max) {
1322
- if (y2 > axisy.max)
1323
- continue;
1324
- x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
1325
- y1 = axisy.max;
1326
- }
1327
- else if (y2 >= y1 && y2 > axisy.max) {
1328
- if (y1 > axisy.max)
1329
- continue;
1330
- x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
1331
- y2 = axisy.max;
1332
- }
1333
- if (x1 <= x2 && x1 < axisx.min) {
1334
- if (x2 < axisx.min)
1335
- continue;
1336
- y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
1337
- x1 = axisx.min;
1338
- }
1339
- else if (x2 <= x1 && x2 < axisx.min) {
1340
- if (x1 < axisx.min)
1341
- continue;
1342
- y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
1343
- x2 = axisx.min;
1344
- }
1345
- if (x1 >= x2 && x1 > axisx.max) {
1346
- if (x2 > axisx.max)
1347
- continue;
1348
- y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
1349
- x1 = axisx.max;
1350
- }
1351
- else if (x2 >= x1 && x2 > axisx.max) {
1352
- if (x1 > axisx.max)
1353
- continue;
1354
- y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
1355
- x2 = axisx.max;
1356
- }
1357
- if (x1 != prevx || y1 != prevy)
1358
- ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
1359
- prevx = x2;
1360
- prevy = y2;
1361
- ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
1362
- }
1363
- ctx.stroke();
1364
- }
1365
- function plotLineArea(datapoints, axisx, axisy) {
1366
- var points = datapoints.points,
1367
- ps = datapoints.pointsize,
1368
- bottom = Math.min(Math.max(0, axisy.min), axisy.max),
1369
- i = 0, top, areaOpen = false,
1370
- ypos = 1, segmentStart = 0, segmentEnd = 0;
1371
- while (true) {
1372
- if (ps > 0 && i > points.length + ps)
1373
- break;
1374
- i += ps;
1375
- var x1 = points[i - ps],
1376
- y1 = points[i - ps + ypos],
1377
- x2 = points[i], y2 = points[i + ypos];
1378
- if (areaOpen) {
1379
- if (ps > 0 && x1 != null && x2 == null) {
1380
- segmentEnd = i;
1381
- ps = -ps;
1382
- ypos = 2;
1383
- continue;
1384
- }
1385
- if (ps < 0 && i == segmentStart + ps) {
1386
- ctx.fill();
1387
- areaOpen = false;
1388
- ps = -ps;
1389
- ypos = 1;
1390
- i = segmentStart = segmentEnd + ps;
1391
- continue;
1392
- }
1393
- }
1394
- if (x1 == null || x2 == null)
1395
- continue;
1396
- if (x1 <= x2 && x1 < axisx.min) {
1397
- if (x2 < axisx.min)
1398
- continue;
1399
- y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
1400
- x1 = axisx.min;
1401
- }
1402
- else if (x2 <= x1 && x2 < axisx.min) {
1403
- if (x1 < axisx.min)
1404
- continue;
1405
- y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
1406
- x2 = axisx.min;
1407
- }
1408
- if (x1 >= x2 && x1 > axisx.max) {
1409
- if (x2 > axisx.max)
1410
- continue;
1411
- y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
1412
- x1 = axisx.max;
1413
- }
1414
- else if (x2 >= x1 && x2 > axisx.max) {
1415
- if (x1 > axisx.max)
1416
- continue;
1417
- y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
1418
- x2 = axisx.max;
1419
- }
1420
- if (!areaOpen) {
1421
- ctx.beginPath();
1422
- ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
1423
- areaOpen = true;
1424
- }
1425
- if (y1 >= axisy.max && y2 >= axisy.max) {
1426
- ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
1427
- ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
1428
- continue;
1429
- }
1430
- else if (y1 <= axisy.min && y2 <= axisy.min) {
1431
- ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
1432
- ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
1433
- continue;
1434
- }
1435
- var x1old = x1, x2old = x2;
1436
- if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
1437
- x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
1438
- y1 = axisy.min;
1439
- }
1440
- else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
1441
- x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
1442
- y2 = axisy.min;
1443
- }
1444
- if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
1445
- x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
1446
- y1 = axisy.max;
1447
- }
1448
- else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
1449
- x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
1450
- y2 = axisy.max;
1451
- }
1452
- if (x1 != x1old) {
1453
- ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
1454
- }
1455
- ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
1456
- ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
1457
- if (x2 != x2old) {
1458
- ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
1459
- ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
1460
- }
1461
- }
1462
- }
1463
- ctx.save();
1464
- ctx.translate(plotOffset.left, plotOffset.top);
1465
- ctx.lineJoin = "round";
1466
- var lw = series.lines.lineWidth,
1467
- sw = series.shadowSize;
1468
- if (lw > 0 && sw > 0) {
1469
- ctx.lineWidth = sw;
1470
- ctx.strokeStyle = "rgba(0,0,0,0.1)";
1471
- var angle = Math.PI/18;
1472
- plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
1473
- ctx.lineWidth = sw/2;
1474
- plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
1475
- }
1476
- ctx.lineWidth = lw;
1477
- ctx.strokeStyle = series.color;
1478
- var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
1479
- if (fillStyle) {
1480
- ctx.fillStyle = fillStyle;
1481
- plotLineArea(series.datapoints, series.xaxis, series.yaxis);
1482
- }
1483
- if (lw > 0)
1484
- plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
1485
- ctx.restore();
1486
- }
1487
- function drawSeriesPoints(series) {
1488
- function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {
1489
- var points = datapoints.points, ps = datapoints.pointsize;
1490
- for (var i = 0; i < points.length; i += ps) {
1491
- var x = points[i], y = points[i + 1];
1492
- if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
1493
- continue;
1494
- ctx.beginPath();
1495
- x = axisx.p2c(x);
1496
- y = axisy.p2c(y) + offset;
1497
- if (symbol == "circle")
1498
- ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
1499
- else
1500
- symbol(ctx, x, y, radius, shadow);
1501
- ctx.closePath();
1502
- if (fillStyle) {
1503
- ctx.fillStyle = fillStyle;
1504
- ctx.fill();
1505
- }
1506
- ctx.stroke();
1507
- }
1508
- }
1509
- ctx.save();
1510
- ctx.translate(plotOffset.left, plotOffset.top);
1511
- var lw = series.points.lineWidth,
1512
- sw = series.shadowSize,
1513
- radius = series.points.radius,
1514
- symbol = series.points.symbol;
1515
- if (lw > 0 && sw > 0) {
1516
- var w = sw / 2;
1517
- ctx.lineWidth = w;
1518
- ctx.strokeStyle = "rgba(0,0,0,0.1)";
1519
- plotPoints(series.datapoints, radius, null, w + w/2, true,
1520
- series.xaxis, series.yaxis, symbol);
1521
- ctx.strokeStyle = "rgba(0,0,0,0.2)";
1522
- plotPoints(series.datapoints, radius, null, w/2, true,
1523
- series.xaxis, series.yaxis, symbol);
1524
- }
1525
- ctx.lineWidth = lw;
1526
- ctx.strokeStyle = series.color;
1527
- plotPoints(series.datapoints, radius,
1528
- getFillStyle(series.points, series.color), 0, false,
1529
- series.xaxis, series.yaxis, symbol);
1530
- ctx.restore();
1531
- }
1532
- function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
1533
- var left, right, bottom, top,
1534
- drawLeft, drawRight, drawTop, drawBottom,
1535
- tmp;
1536
- if (horizontal) {
1537
- drawBottom = drawRight = drawTop = true;
1538
- drawLeft = false;
1539
- left = b;
1540
- right = x;
1541
- top = y + barLeft;
1542
- bottom = y + barRight;
1543
- if (right < left) {
1544
- tmp = right;
1545
- right = left;
1546
- left = tmp;
1547
- drawLeft = true;
1548
- drawRight = false;
1549
- }
1550
- }
1551
- else {
1552
- drawLeft = drawRight = drawTop = true;
1553
- drawBottom = false;
1554
- left = x + barLeft;
1555
- right = x + barRight;
1556
- bottom = b;
1557
- top = y;
1558
- if (top < bottom) {
1559
- tmp = top;
1560
- top = bottom;
1561
- bottom = tmp;
1562
- drawBottom = true;
1563
- drawTop = false;
1564
- }
1565
- }
1566
- if (right < axisx.min || left > axisx.max ||
1567
- top < axisy.min || bottom > axisy.max)
1568
- return;
1569
- if (left < axisx.min) {
1570
- left = axisx.min;
1571
- drawLeft = false;
1572
- }
1573
- if (right > axisx.max) {
1574
- right = axisx.max;
1575
- drawRight = false;
1576
- }
1577
- if (bottom < axisy.min) {
1578
- bottom = axisy.min;
1579
- drawBottom = false;
1580
- }
1581
- if (top > axisy.max) {
1582
- top = axisy.max;
1583
- drawTop = false;
1584
- }
1585
- left = axisx.p2c(left);
1586
- bottom = axisy.p2c(bottom);
1587
- right = axisx.p2c(right);
1588
- top = axisy.p2c(top);
1589
- if (fillStyleCallback) {
1590
- c.beginPath();
1591
- c.moveTo(left, bottom);
1592
- c.lineTo(left, top);
1593
- c.lineTo(right, top);
1594
- c.lineTo(right, bottom);
1595
- c.fillStyle = fillStyleCallback(bottom, top);
1596
- c.fill();
1597
- }
1598
- if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
1599
- c.beginPath();
1600
- c.moveTo(left, bottom + offset);
1601
- if (drawLeft)
1602
- c.lineTo(left, top + offset);
1603
- else
1604
- c.moveTo(left, top + offset);
1605
- if (drawTop)
1606
- c.lineTo(right, top + offset);
1607
- else
1608
- c.moveTo(right, top + offset);
1609
- if (drawRight)
1610
- c.lineTo(right, bottom + offset);
1611
- else
1612
- c.moveTo(right, bottom + offset);
1613
- if (drawBottom)
1614
- c.lineTo(left, bottom + offset);
1615
- else
1616
- c.moveTo(left, bottom + offset);
1617
- c.stroke();
1618
- }
1619
- }
1620
- function drawSeriesBars(series) {
1621
- function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {
1622
- var points = datapoints.points, ps = datapoints.pointsize;
1623
- for (var i = 0; i < points.length; i += ps) {
1624
- if (points[i] == null)
1625
- continue;
1626
- drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
1627
- }
1628
- }
1629
- ctx.save();
1630
- ctx.translate(plotOffset.left, plotOffset.top);
1631
- ctx.lineWidth = series.bars.lineWidth;
1632
- ctx.strokeStyle = series.color;
1633
- var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2;
1634
- var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
1635
- plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);
1636
- ctx.restore();
1637
- }
1638
- function getFillStyle(filloptions, seriesColor, bottom, top) {
1639
- var fill = filloptions.fill;
1640
- if (!fill)
1641
- return null;
1642
- if (filloptions.fillColor)
1643
- return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
1644
- var c = $.color.parse(seriesColor);
1645
- c.a = typeof fill == "number" ? fill : 0.4;
1646
- c.normalize();
1647
- return c.toString();
1648
- }
1649
- function insertLegend() {
1650
- placeholder.find(".legend").remove();
1651
- if (!options.legend.show)
1652
- return;
1653
- var fragments = [], rowStarted = false,
1654
- lf = options.legend.labelFormatter, s, label;
1655
- for (var i = 0; i < series.length; ++i) {
1656
- s = series[i];
1657
- label = s.label;
1658
- if (!label)
1659
- continue;
1660
- if (i % options.legend.noColumns == 0) {
1661
- if (rowStarted)
1662
- fragments.push('</tr>');
1663
- fragments.push('<tr>');
1664
- rowStarted = true;
1665
- }
1666
- if (lf)
1667
- label = lf(label, s);
1668
- fragments.push(
1669
- '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + s.color + ';overflow:hidden"></div></div></td>' +
1670
- '<td class="legendLabel">' + label + '</td>');
1671
- }
1672
- if (rowStarted)
1673
- fragments.push('</tr>');
1674
- if (fragments.length == 0)
1675
- return;
1676
- var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
1677
- if (options.legend.container != null)
1678
- $(options.legend.container).html(table);
1679
- else {
1680
- var pos = "",
1681
- p = options.legend.position,
1682
- m = options.legend.margin;
1683
- if (m[0] == null)
1684
- m = [m, m];
1685
- if (p.charAt(0) == "n")
1686
- pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
1687
- else if (p.charAt(0) == "s")
1688
- pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
1689
- if (p.charAt(1) == "e")
1690
- pos += 'right:' + (m[0] + plotOffset.right) + 'px;';