P3 (Plugin Performance Profiler) - Version 1.0

Version Description

Initial release.

=

Download this release

Release Info

Developer StarfieldTech
Plugin Icon wp plugin P3 (Plugin Performance Profiler)
Version 1.0
Comparing to
See all releases

Version 1.0

Files changed (51) hide show
  1. .htaccess +1 -0
  2. .profiling_enabled +1 -0
  3. class.p3-profile-reader.php +271 -0
  4. class.p3-profile-table-sorter.php +57 -0
  5. class.p3-profile-table.php +293 -0
  6. class.p3-profiler.php +634 -0
  7. css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  8. css/custom-theme/images/ui-bg_flat_100_ffffff_40x100.png +0 -0
  9. css/custom-theme/images/ui-bg_highlight-hard_100_206d92_1x100.png +0 -0
  10. css/custom-theme/images/ui-bg_highlight-hard_100_f1f1f1_1x100.png +0 -0
  11. css/custom-theme/images/ui-bg_highlight-hard_100_f8f8f8_1x100.png +0 -0
  12. css/custom-theme/images/ui-bg_highlight-hard_100_fbfbfb_1x100.png +0 -0
  13. css/custom-theme/images/ui-bg_highlight-soft_50_206d92_1x100.png +0 -0
  14. css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  15. css/custom-theme/images/ui-icons_000000_256x240.png +0 -0
  16. css/custom-theme/images/ui-icons_222222_256x240.png +0 -0
  17. css/custom-theme/images/ui-icons_464646_256x240.png +0 -0
  18. css/custom-theme/images/ui-icons_cd0a0a_256x240.png +0 -0
  19. css/custom-theme/images/ui-icons_ffffff_256x240.png +0 -0
  20. css/custom-theme/jquery-ui-1.8.16.custom.css +568 -0
  21. css/icon_mail.gif +0 -0
  22. css/jquery.qtip.min.css +1 -0
  23. css/p3.css +345 -0
  24. css/wordwrap.xml +22 -0
  25. index.php +2 -0
  26. js/jquery.corner.js +22 -0
  27. js/jquery.flot.min.js +5 -0
  28. js/jquery.flot.navigate.js +6 -0
  29. js/jquery.flot.pie.min.js +5 -0
  30. js/jquery.qtip.min.js +13 -0
  31. license.txt +339 -0
  32. logo.gif +0 -0
  33. p3-profiler.php +798 -0
  34. readme.txt +67 -0
  35. screenshot-1.png +0 -0
  36. screenshot-2.png +0 -0
  37. screenshot-3.png +0 -0
  38. screenshot-4.png +0 -0
  39. screenshot-5.png +0 -0
  40. screenshot-6.png +0 -0
  41. screenshot-7.png +0 -0
  42. screenshot-8.png +0 -0
  43. start-profile.php +8 -0
  44. templates/.htaccess +1 -0
  45. templates/callouts.php +624 -0
  46. templates/fix-flag-file.php +11 -0
  47. templates/help.php +471 -0
  48. templates/index.php +2 -0
  49. templates/list-scans.php +39 -0
  50. templates/template.php +117 -0
  51. templates/view-scan.php +993 -0
.htaccess ADDED
@@ -0,0 +1 @@
 
1
+ Options -Indexes
.profiling_enabled ADDED
@@ -0,0 +1 @@
 
1
+ []
class.p3-profile-reader.php ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Performance Profile Reader
4
+ *
5
+ * @author GoDaddy.com
6
+ * @version 1.0
7
+ * @package P3_Profiler
8
+ */
9
+ class P3_Profile_Reader {
10
+
11
+ /**
12
+ * Total site load time (profile + theme + core + plugins)
13
+ * @var float
14
+ */
15
+ public $total_time = 0;
16
+
17
+ /**
18
+ * Total site load time (theme + core + plugins - profile)
19
+ * @var float
20
+ */
21
+ public $site_time = 0;
22
+
23
+ /**
24
+ * Time spent in themes
25
+ * Calls which spend time in multiple areas are prioritized prioritized
26
+ * first as plugins, second as themes, lastly as core.
27
+ * @var float
28
+ */
29
+ public $theme_time = 0;
30
+
31
+ /**
32
+ * Time spent in plugins.
33
+ * Calls which spend time in multiple areas are prioritized prioritized
34
+ * first as plugins, second as themes, lastly as core.
35
+ * @var float
36
+ */
37
+ public $plugin_time = 0;
38
+
39
+ /**
40
+ * Time spent in the profiler code.
41
+ * @var float
42
+ */
43
+ public $profile_time = 0;
44
+
45
+ /**
46
+ * Time spent in themes
47
+ * Calls which spend time in multiple areas are prioritized prioritized
48
+ * first as plugins, second as themes, lastly as core.
49
+ * @var float
50
+ */
51
+ public $core_time = 0;
52
+
53
+ /**
54
+ * Memory usage per visit as reported by memory_get_peak_usage(true)
55
+ * @var float
56
+ */
57
+ public $memory = 0;
58
+
59
+ /**
60
+ * Number of plugin related function calls (does not include php internal
61
+ * calls due to a limitation of how the tick handler works)
62
+ * @var int
63
+ */
64
+ public $plugin_calls = 0;
65
+
66
+ /**
67
+ * Extracted URL from the profile
68
+ * @var string
69
+ */
70
+ public $report_url = '';
71
+
72
+ /**
73
+ * Extracted date from the first visit in the profile
74
+ * @var int
75
+ */
76
+ public $report_date = '';
77
+
78
+ /**
79
+ * Total number of mysql queries as reported by get_num_queries()
80
+ * @var int
81
+ */
82
+ public $queries = 0;
83
+
84
+ /**
85
+ * Total number of visits recorded in the profile
86
+ * @var int
87
+ */
88
+ public $visits = 0;
89
+
90
+ /**
91
+ * List of detected plugins
92
+ * @var array
93
+ */
94
+ public $detected_plugins = array();
95
+
96
+ /**
97
+ * Array of total time spent in each plugin
98
+ * key = plugin name
99
+ * value = seconds (float)
100
+ * @var array
101
+ */
102
+ public $plugin_times = array();
103
+
104
+ /**
105
+ * Averaged values for the report
106
+ * @var array
107
+ */
108
+ public $averages = array(
109
+ 'total' => 0,
110
+ 'site' => 0,
111
+ 'core' => 0,
112
+ 'plugins' => 0,
113
+ 'profile' => 0,
114
+ 'theme' => 0,
115
+ 'memory' => 0,
116
+ 'plugin_calls' => 0,
117
+ 'queries' => 0,
118
+ 'observed' => 0,
119
+ 'expected' => 0,
120
+ 'drift' => 0,
121
+ 'plugin_impact' => 0,
122
+ );
123
+
124
+ /**
125
+ * Internal profile data
126
+ * @var array
127
+ */
128
+ private $_data = array();
129
+
130
+ /**
131
+ * Constructor
132
+ * @param string $file Full path to the profile json file
133
+ * @return P3_Profile_Reader
134
+ */
135
+ public function __construct( $file ) {
136
+
137
+ // Open the file
138
+ $fp = fopen( $file, 'r' );
139
+ if ( FALSE === $fp ) {
140
+ throw new Exception( 'Cannot open ' . $file );
141
+ }
142
+
143
+ // Decode each line. Each line is a separate json object. Whenever a
144
+ // a visit is recorded, a new line is added to the file.
145
+ while ( !feof( $fp ) ) {
146
+ $line = fgets( $fp );
147
+ if ( empty( $line) ) {
148
+ continue;
149
+ }
150
+ $tmp = json_decode( $line );
151
+ if ( null === $tmp ) {
152
+ throw new Exception( 'Cannot parse ' . $file );
153
+ fclose( $fp );
154
+ }
155
+ $this->_data[] = $tmp;
156
+ }
157
+
158
+ // Close the file
159
+ fclose( $fp );
160
+
161
+ // Parse the data
162
+ $this->_parse();
163
+ }
164
+
165
+ /**
166
+ * Parse from $this->_data and fill in the rest of the member vars
167
+ * @return void
168
+ */
169
+ private function _parse() {
170
+ foreach ( $this->_data as $o ) {
171
+ // Set report meta-data
172
+ if ( empty( $this->report_date ) ) {
173
+ $this->report_date = strtotime( $o->date );
174
+ $scheme = parse_url( $o->url, PHP_URL_SCHEME );
175
+ $host = parse_url( $o->url, PHP_URL_HOST );
176
+ $path = parse_url( $o->url, PHP_URL_PATH );
177
+ $this->report_url = sprintf( '%s://%s%s', $scheme, $host, $path );
178
+ $this->visits = count( $this->_data );
179
+ }
180
+
181
+ // Set total times / queries / function calls
182
+ $this->total_time += $o->runtime->total;
183
+ $this->site_time += ( $o->runtime->total - $o->runtime->profile );
184
+ $this->theme_time += $o->runtime->theme;
185
+ $this->plugin_time += $o->runtime->plugins;
186
+ $this->profile_time += $o->runtime->profile;
187
+ $this->core_time += $o->runtime->wordpress;
188
+ $this->memory += $o->memory;
189
+ $this->plugin_calls += $o->stacksize;
190
+ $this->queries += $o->queries;
191
+
192
+ // Loop through the plugin data
193
+ foreach ( $o->runtime->breakdown as $k => $v ) {
194
+ if ( !array_key_exists( $k, $this->plugin_times ) ) {
195
+ $this->plugin_times[$k] = 0;
196
+ }
197
+ $this->plugin_times[$k] += $v;
198
+ }
199
+ }
200
+
201
+ // Fix plugin names and average out plugin times
202
+ $tmp = $this->plugin_times;
203
+ $this->plugin_times = array();
204
+ foreach ( $tmp as $k => $v ) {
205
+ $k = ucwords( str_replace( array( '-', '_' ), ' ', $k ) );
206
+ $this->plugin_times[$k] = $v / $this->visits;
207
+ }
208
+
209
+ // Get a list of the plugins we detected
210
+ $this->detected_plugins = array_keys( $this->plugin_times );
211
+ sort( $this->detected_plugins );
212
+
213
+ // Calculate the averages
214
+ $this->_get_averages();
215
+ }
216
+
217
+ /**
218
+ * Calculate the average values
219
+ * @return void
220
+ */
221
+ private function _get_averages() {
222
+ if ( $this->visits <= 0 ) {
223
+ return;
224
+ }
225
+ $this->averages = array(
226
+ 'total' => $this->total_time / $this->visits,
227
+ 'site' => ( $this->total_time - $this->profile_time) / $this->visits,
228
+ 'core' => $this->core_time / $this->visits,
229
+ 'plugins' => $this->plugin_time / $this->visits,
230
+ 'profile' => $this->profile_time / $this->visits,
231
+ 'theme' => $this->theme_time / $this->visits,
232
+ 'memory' => $this->memory / $this->visits,
233
+ 'plugin_calls' => $this->plugin_calls / $this->visits,
234
+ 'queries' => $this->queries / $this->visits,
235
+ 'observed' => $this->total_time / $this->visits,
236
+ 'expected' => ( $this->theme_time + $this->core_time + $this->profile_time + $this->plugin_time) / $this->visits,
237
+ );
238
+ $this->averages['drift'] = $this->averages['observed'] - $this->averages['expected'];
239
+ $this->averages['plugin_impact'] = $this->averages['plugins'] / $this->averages['site'] * 100;
240
+ }
241
+
242
+ /**
243
+ * Return a list of runtimes times by url
244
+ * Where the key is the url and the value is an array of runtime values
245
+ * in seconds (float)
246
+ * @return array
247
+ */
248
+ public function get_stats_by_url() {
249
+ $ret = array();
250
+ foreach ( $this->_data as $o ) {
251
+ $tmp = array(
252
+ 'url' => $o->url,
253
+ 'core' => $o->runtime->wordpress,
254
+ 'plugins' => $o->runtime->plugins,
255
+ 'profile' => $o->runtime->profile,
256
+ 'theme' => $o->runtime->theme,
257
+ 'queries' => $o->queries,
258
+ 'breakdown' => array()
259
+ );
260
+ foreach ( $o->runtime->breakdown as $k => $v ) {
261
+ $name = ucwords( str_replace( array( '-', '_' ), ' ', $k ) );
262
+ if ( !array_key_exists( $name, $tmp['breakdown'] ) ) {
263
+ $tmp['breakdown'][$name] = 0;
264
+ }
265
+ $tmp['breakdown'][$name] += $v;
266
+ }
267
+ $ret[] = $tmp;
268
+ }
269
+ return $ret;
270
+ }
271
+ }
class.p3-profile-table-sorter.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Profile table sorter
4
+ *
5
+ * @author GoDaddy.com
6
+ * @version 1.0
7
+ * @package P3_Profiler
8
+ */
9
+ class P3_Profile_Table_Sorter {
10
+
11
+ /**
12
+ * The field name to sort by
13
+ * @var string
14
+ */
15
+ private $field = null;
16
+
17
+ /**
18
+ * The data to sort
19
+ * @var array
20
+ */
21
+ private $data = null;
22
+
23
+ /**
24
+ * Constructor.
25
+ * @param array $data
26
+ * @param string $field Default is 'name'
27
+ */
28
+ public function __construct( array $data, $field = 'name' ) {
29
+ $this->data = $data;
30
+ $this->field = $field;
31
+ }
32
+
33
+ /**
34
+ * Sort the data in 'asc' or 'desc' directions
35
+ * @param string $direction Default is 'asc'
36
+ * @return array
37
+ */
38
+ public function sort( $direction = 'asc' ) {
39
+ usort( &$this->data, array( $this, '_compare' ) );
40
+ return ( 'asc' == $direction ) ? $this->data : array_reverse( $this->data );
41
+ }
42
+
43
+ /**
44
+ * Compare the data
45
+ * @link http://us.php.net/usort
46
+ * @param type $a
47
+ * @param type $b
48
+ * @return bool
49
+ */
50
+ private function _compare( $a, $b ) {
51
+ if ( in_array( $this->field, array( '_filesize', '_count', '_date' ) ) ) {
52
+ return $a[$this->field] < $b[$this->field];
53
+ } else {
54
+ return strcmp( $a[$this->field], $b[$this->field] );
55
+ }
56
+ }
57
+ }
class.p3-profile-table.php ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Lists the performance profiles
4
+ *
5
+ * @author GoDaddy.com
6
+ * @version 1.0
7
+ * @package P3_Profiler
8
+ */
9
+ class P3_Profile_Table extends WP_List_Table {
10
+
11
+ /**************************************************************************/
12
+ /** SETUP **/
13
+ /**************************************************************************/
14
+
15
+ /**
16
+ * Constructor
17
+ * @return P3_Profile_Table
18
+ */
19
+ public function __construct() {
20
+ parent::__construct(
21
+ array(
22
+ 'singular' => 'scan',
23
+ 'plural' => 'scans',
24
+ )
25
+ );
26
+ }
27
+
28
+ /**
29
+ * Set up the columns, dataset, paginator
30
+ * @return void
31
+ */
32
+ public function prepare_items() {
33
+
34
+ // Set up columns
35
+ $columns = $this->get_columns();
36
+ $hidden = array();
37
+ $sortable = $this->get_sortable_columns();
38
+ $this->_column_headers = array( $columns, $hidden, $sortable );
39
+
40
+ // Perform bulk actions
41
+ $this->do_bulk_action();
42
+ $data = $this->_get_profiles();
43
+
44
+ // Sort data
45
+ $orderby = ( !empty( $_REQUEST['orderby']) ) ? $_REQUEST['orderby'] : 'name';
46
+ $order = ( !empty( $_REQUEST['order']) ) ? $_REQUEST['order'] : 'asc';
47
+ $data = $this->_sort( $data, $orderby, $order );
48
+
49
+ // 20 items per page
50
+ $per_page = 20;
51
+
52
+ // Get page number
53
+ $current_page = $this->get_pagenum();
54
+
55
+ // Get total items
56
+ $total_items = count( $data );
57
+
58
+ // Carve out only the visible dataset
59
+ $data = array_slice( $data, $current_page - 1 * $per_page, $per_page );
60
+ $this->items = $data;
61
+
62
+ // Set up the paginator
63
+ $this->set_pagination_args(
64
+ array(
65
+ 'total_items' => $total_items,
66
+ 'per_page' => $per_page,
67
+ 'total_pages' => ceil( $total_items / $per_page ),
68
+ )
69
+ );
70
+ }
71
+
72
+ /**************************************************************************/
73
+ /** COLUMN PREP **/
74
+ /**************************************************************************/
75
+
76
+ /**
77
+ * If there's no column_[whatever] method available, use this to render
78
+ * the column
79
+ * @param array $item
80
+ * @param string $column_name
81
+ * @return string
82
+ */
83
+ public function column_default( $item, $column_name ) {
84
+ switch ( $column_name ) {
85
+ case 'name' :
86
+ case 'date' :
87
+ case 'count' :
88
+ case 'filesize' :
89
+ return $item[$column_name];
90
+ break;
91
+ default:
92
+ return '';
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Render the "title" column
98
+ * @param array $item
99
+ * @return string
100
+ */
101
+ public function column_title( $item ) {
102
+ $actions = array(
103
+ 'delete' => sprintf( '<a href="?page=%s&action=%s&name=%s">Delete</a>', $_REQUEST['name'], 'delete', $item['name'] ),
104
+ );
105
+
106
+ //Return the title contents
107
+ return sprintf(
108
+ '%1$s <span style="color:silver">(id:%2$s )</span>%3$s',
109
+ $item['name'],
110
+ $item['name'],
111
+ $this->row_actions( $actions )
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Render the checkbox column
117
+ * @param type $item
118
+ * @return string
119
+ */
120
+ public function column_cb( $item ) {
121
+ return sprintf(
122
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
123
+ $this->_args['singular'],
124
+ $item['filename']
125
+ );
126
+ }
127
+
128
+ /**
129
+ * Get a list of columns
130
+ * @return array
131
+ */
132
+ public function get_columns() {
133
+ $columns = array(
134
+ 'cb' => '<input type="checkbox" />',
135
+ 'name' => 'Name',
136
+ 'date' => 'Date',
137
+ 'count' => 'Visits',
138
+ 'filesize' => 'Size',
139
+ );
140
+ return $columns;
141
+ }
142
+
143
+ /**
144
+ * Get a list of sortable columns (note, do not return the checkbox column )
145
+ * @return array
146
+ */
147
+ public function get_sortable_columns() {
148
+ $sortable_columns = array(
149
+ 'name' => array( 'name', true ),
150
+ 'date' => array( 'date', true ),
151
+ 'count' => array( 'count', true ),
152
+ 'filesize' => array( 'filesize', true ),
153
+ );
154
+ return $sortable_columns;
155
+ }
156
+
157
+ /**
158
+ * Add some the "view" and "delete" links to the scan
159
+ * @param string $key Internal key (scan filename )
160
+ * @param string $display Display key (scan filename )
161
+ * @return string
162
+ */
163
+ private function _action_links( $key, $display ) {
164
+ $url = add_query_arg(
165
+ array(
166
+ 'p3_action' => 'view-scan',
167
+ 'name' => $key,
168
+ )
169
+ );
170
+ return <<<EOD
171
+ <a href="$url"><strong>$display</strong></a>
172
+ <div class="row-actions-visible">
173
+ <span class="view">
174
+ <a href="$url" data-name="$key" title="View the results of this scan" class="view-results">View</a> |
175
+ </span>
176
+ <span>
177
+ <a href="javascript:;" data-name="$key" title="Continue this scan" class="p3-continue-scan">Continue</a> |
178
+ </span>
179
+ <span class="delete">
180
+ <a href="javascript:;" data-name="$key" title="Delete this scan" class="delete-scan delete">Delete</a>
181
+ </span>
182
+ </div>
183
+ EOD;
184
+ }
185
+
186
+ /**************************************************************************/
187
+ /** BULK ACTIONS **/
188
+ /**************************************************************************/
189
+
190
+ /**
191
+ * Get a list of which actions are available in the bulk actions dropdown
192
+ * @return string
193
+ */
194
+ public function get_bulk_actions() {
195
+ $actions = array( 'delete' => 'Delete' );
196
+ return $actions;
197
+ }
198
+
199
+ /**
200
+ * Performan any bulk actions
201
+ * @return void
202
+ */
203
+ public function do_bulk_action() {
204
+ global $p3_profiler_plugin;
205
+ if ( 'delete' === $this->current_action() && !empty( $_REQUEST['scan'] ) ) {
206
+ if ( !wp_verify_nonce( $_REQUEST['p3_nonce'], 'delete_scans' ) ) {
207
+ wp_die( 'Invalid nonce' );
208
+ }
209
+ foreach ( $_REQUEST['scan'] as $scan ) {
210
+ $file = P3_PROFILES_PATH . DIRECTORY_SEPARATOR . basename( $scan );
211
+ if ( !file_exists( $file ) || !is_writable( $file ) || !unlink( $file ) ) {
212
+ wp_die( 'Error removing file ' . $file );
213
+ }
214
+ }
215
+ $count = count( $_REQUEST['scan'] );
216
+ if ( $count == 1 ) {
217
+ $p3_profiler_plugin->add_notice( "Deleted $count scan." );
218
+ } else {
219
+ $p3_profiler_plugin->add_notice( "Deleted $count scans." );
220
+ }
221
+ }
222
+ }
223
+
224
+ /**************************************************************************/
225
+ /** DATA PREP **/
226
+ /**************************************************************************/
227
+
228
+ /**
229
+ * Sort the data
230
+ * @param array $data
231
+ * @param string $field Field name (e.g. 'name' or 'count')
232
+ * @param string $direction asc / desc
233
+ * @return array
234
+ */
235
+ private function _sort( $data, $field, $direction ) {
236
+
237
+ // Override the count / date fields as they've had some display markup
238
+ // applied to them and need to be sorted on the original values
239
+ switch ( $field ) {
240
+ case 'count' :
241
+ $field = '_count';
242
+ break;
243
+ case 'date' :
244
+ $field = '_date';
245
+ break;
246
+ case 'filesize' :
247
+ $field = '_filesize';
248
+ break;
249
+ }
250
+ $sorter = new P3_Profile_Table_Sorter( $data, $field );
251
+ return $sorter->sort( $direction );
252
+ }
253
+
254
+ /**
255
+ * Get a list of the profiles in the profiles folder
256
+ * Profiles are named as "*.json". Add additional info, too, like
257
+ * date and number of visits in the file
258
+ * @uses list_files
259
+ * @return type
260
+ */
261
+ private function _get_profiles() {
262
+ $p3_profile_dir = P3_PROFILES_PATH;
263
+ $files = list_files( $p3_profile_dir );
264
+ $files = array_filter( $files, array( &$this, '_filter_json_files' ) );
265
+ $ret = array();
266
+ foreach ( $files as $file ) {
267
+ $time = filemtime( $file );
268
+ $count = count( file( $file ) );
269
+ $key = basename( $file );
270
+ $name = substr( $key, 0, -5 ); // strip off .json
271
+ $ret[] = array(
272
+ 'filename' => basename( $file ),
273
+ 'name' => $this->_action_links( $key, $name ),
274
+ 'date' => date( 'D, M jS', $time ) . ' at ' . date( 'g:i a', $time ),
275
+ 'count' => number_format( $count ),
276
+ 'filesize' => $GLOBALS['p3_profiler_plugin']->readable_size( filesize( $file ) ),
277
+ '_filesize' => filesize( $file ),
278
+ '_date' => $time,
279
+ '_count' => $count,
280
+ );
281
+ }
282
+ return $ret;
283
+ }
284
+
285
+ /**
286
+ * Only let "*.json" files pass through
287
+ * @param type $file
288
+ * @return type
289
+ */
290
+ private function _filter_json_files( $file ) {
291
+ return ( '.json' == substr( strtolower( $file ), -5 ) );
292
+ }
293
+ }
class.p3-profiler.php ADDED
@@ -0,0 +1,634 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Profiles a WordPress site
4
+ *
5
+ * @author GoDaddy.com
6
+ * @version 1.0
7
+ * @package P3_Profiler
8
+ */
9
+ class P3_Profiler {
10
+
11
+ /**
12
+ * Time spent in WordPress Core
13
+ * @var float
14
+ */
15
+ private $_core = 0;
16
+
17
+ /**
18
+ * Time spent in theme
19
+ * @var float
20
+ */
21
+ private $_theme = 0;
22
+
23
+ /**
24
+ * Time spent in the profiler code
25
+ * @var float
26
+ */
27
+ private $_runtime = 0;
28
+
29
+ /**
30
+ * Time spent in plugins
31
+ * @var float
32
+ */
33
+ private $_plugin_runtime = 0;
34
+
35
+ /**
36
+ * Profile information, built up during the application's execution
37
+ * @var array
38
+ */
39
+ private $_profile = array();
40
+
41
+ /**
42
+ * Stack trace of the last function call. The stack is held here until
43
+ * it's recorded. It's not recorded until it's been timed. It won't be
44
+ * timed until after it's complete and the next function is in being
45
+ * examined, so the $_last_stack will be moved to $_profile and the current
46
+ * function will be moved to $_last_stack.
47
+ * @var array
48
+ */
49
+ private $_last_stack = array();
50
+
51
+ /**
52
+ * Time spent in last function call
53
+ * @var float
54
+ */
55
+ private $_last_call_time = 0;
56
+
57
+ /**
58
+ * Timestamp when the last function call was started
59
+ * @var float
60
+ */
61
+ private $_last_call_start = 0;
62
+
63
+ /**
64
+ * How to categorize the last call ( core, theme, plugin )
65
+ * @var int
66
+ */
67
+ private $_last_call_category = '';
68
+
69
+ /**
70
+ * Where to save the profile when it's done
71
+ * @var string
72
+ */
73
+ private $_profile_filename = '';
74
+
75
+ /**
76
+ * App start time ( as close as we can measure )
77
+ * @var float
78
+ */
79
+ private $_start_time = 0;
80
+
81
+ /**
82
+ * Path to ourselves
83
+ * @var string
84
+ */
85
+ private $_P3_PATH = ''; // Cannot rely on P3_PATH, may be instantiated before the plugin
86
+
87
+ /**
88
+ * Path to the ".profiling_enabled" flag file
89
+ * @var string
90
+ */
91
+ private $_P3_FLAG_FILE = '';
92
+
93
+ /**
94
+ * Last stack should be marked as plugin time
95
+ * @const
96
+ */
97
+ const CATEGORY_PLUGIN = 1;
98
+
99
+ /**
100
+ * Last stack should be marked as theme time
101
+ * @const
102
+ */
103
+ const CATEGORY_THEME = 2;
104
+
105
+ /**
106
+ * Last stack should be marked as core time
107
+ * @const
108
+ */
109
+ const CATEGORY_CORE = 3;
110
+
111
+ /**
112
+ * Constructor
113
+ * Initialize the object, figure out if profiling is enabled, and if so,
114
+ * start the profile.
115
+ * @return p3_profiler
116
+ */
117
+ public function __construct() {
118
+
119
+ // Set up paths
120
+ $this->_P3_PATH = realpath( dirname( __FILE__ ) );
121
+ $this->_P3_FLAG_FILE = $this->_P3_PATH . DIRECTORY_SEPARATOR . '.profiling_enabled';
122
+
123
+ // Check to see if we should profile
124
+ $p3_json = ( file_exists( $this->_P3_FLAG_FILE ) ? json_decode( file_get_contents( $this->_P3_FLAG_FILE ) ) : null );
125
+ if ( empty( $p3_json ) ) {
126
+ return $this;
127
+ }
128
+ $found = false;
129
+ foreach ( (array) $p3_json as $k => $v ) {
130
+ if ( 0 === strpos( $_SERVER['REQUEST_URI'], $v->site_url ) && preg_match( '/' . preg_quote( $v->ip ) . '/', $this->get_ip() ) ) {
131
+ $found = true;
132
+ break;
133
+ }
134
+ }
135
+ if ( !$found ) {
136
+ return $this;
137
+ }
138
+
139
+ // Kludge memory limit / time limit
140
+ ini_set( 'memory_limit', '128M' );
141
+ set_time_limit( 90 );
142
+
143
+ // Set the profile file
144
+ $this->_profile_filename = $v->name . '.json';
145
+
146
+ // Start timing
147
+ $this->_start_time = microtime( true );
148
+ $this->_last_call_start = microtime( true );
149
+
150
+ // Reset state
151
+ $this->_last_call_time = 0;
152
+ $this->_runtime = 0;
153
+ $this->_plugin_runtime = 0;
154
+ $this->_core = 0;
155
+ $this->_theme = 0;
156
+ $this->_last_call_category = self::CATEGORY_CORE;
157
+ $this->_last_stack = array();
158
+
159
+ // Add a global flag to let everyone know we're profiling
160
+ define( 'WPP_PROFILING_STARTED', true );
161
+
162
+ // Add some startup information
163
+ $this->_profile = array(
164
+ 'url' => $this->_get_url(),
165
+ 'ip' => ( array_key_exists( 'HTTP_X_FORWARDED_FOR', $_SERVER ) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'] ),
166
+ 'pid' => getmypid(),
167
+ 'date' => @date( 'c' ),
168
+ 'stack' => array()
169
+ );
170
+
171
+ // Clear any opcode caches, the optimization / caching from these can
172
+ // hide calls from the tick handler and backtraces
173
+ if ( $v->disable_opcode_cache ) {
174
+ if ( extension_loaded( 'xcache' ) && function_exists( 'xcache_clear_cache' ) && !ini_get( 'xcache.admin.enable_auth' ) ) {
175
+ for ( $i = 0 ; $i < xcache_count( XC_TYPE_PHP ); $i++ ) {
176
+ xcache_clear_cache( XC_TYPE_PHP, 0 );
177
+ }
178
+ } elseif ( extension_loaded( 'apc' ) && function_exists( 'apc_clear_cache' ) ) {
179
+ apc_clear_cache();
180
+ } elseif ( extension_loaded( 'eaccelerator' ) && function_exists( 'eaccelerator_clean' ) ) {
181
+ @eaccelerator_clean();
182
+ }
183
+ }
184
+
185
+ // Monitor all function-calls
186
+ declare( ticks = 1 );
187
+ register_tick_function( array( $this, 'tick_handler' ) );
188
+ }
189
+
190
+ /**
191
+ * In between every call, examine the stack trace time the calls, and record
192
+ * the calls if the operations went through a plugin
193
+ * @return void
194
+ */
195
+ public function tick_handler() {
196
+ static $theme_files_cache = array(); // Cache for theme files
197
+ static $actions_hooked = false;
198
+ static $themes_folder = 'themes';
199
+ static $content_folder = 'wp-content'; // Guess, if it's not defined
200
+ static $folder_flag = false;
201
+ static $in_wp = false;
202
+
203
+ // See if we're in WP
204
+ $in_wp = ($in_wp || defined( 'WP_USE_THEMES' ) || defined( 'DOING_CRON' ) || defined( 'WP_ADMIN' ));
205
+
206
+ // Set the content folder
207
+ if ( !$folder_flag && defined( 'WP_CONTENT_DIR' ) ) {
208
+ $content_folder = basename( WP_CONTENT_DIR );
209
+ $folder_flag = true;
210
+ }
211
+
212
+ // Start timing time spent in the profiler
213
+ $start = microtime( true );
214
+
215
+ // Calculate the last call time
216
+ $this->_last_call_time = ( $start - $this->_last_call_start );
217
+
218
+ // Don't profile in non-WP scripts
219
+ if ( !$in_wp && !$this->_is_a_plugin_file( $_SERVER['SCRIPT_FILENAME'] ) ) {
220
+ $tmp = microtime( true );
221
+ $this->_runtime += ( $tmp - $start );
222
+ $this->_last_call_start = $tmp;
223
+ return;
224
+ }
225
+
226
+ // Hook actions
227
+ if ( !$actions_hooked && function_exists( 'add_action' ) ) {
228
+ // Hook the shutdown action to save the profile when we're done
229
+ add_action( 'shutdown', array( $this, 'shutdown_handler' ) );
230
+
231
+ // Don't re-hook again
232
+ $actions_hooked = true;
233
+ }
234
+
235
+ // If we had a stack in the queue, track the runtime, and write it to the log
236
+ // array() !== $this->_last_stack is slightly faster than !empty( $this->_last_stack )
237
+ // which is important since this is called on every tick
238
+ if ( self::CATEGORY_PLUGIN == $this->_last_call_category && array() !== $this->_last_stack ) {
239
+ // Write the stack to the profile
240
+ $this->_plugin_runtime += $this->_last_call_time;
241
+
242
+ // Add this stack to the profile
243
+ $this->_profile['stack'][] = array(
244
+ 'plugin' => $this->_last_stack['plugin'],
245
+ 'runtime' => $this->_last_call_time,
246
+ );
247
+
248
+ // Reset the stack
249
+ $this->_last_stack = array();
250
+ } elseif ( self::CATEGORY_THEME == $this->_last_call_category ) {
251
+ $this->_theme += $this->_last_call_time;
252
+ } elseif ( self::CATEGORY_CORE == $this->_last_call_category ) {
253
+ $this->_core += $this->_last_call_time;
254
+ }
255
+
256
+ // Examine the current stack, see if we should track it. It should be
257
+ // related to a plugin file if we're going to track it
258
+ if ( defined( 'DEBUG_BACKTRACE_IGNORE_ARGS' ) ) {
259
+ $bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT );
260
+ } else {
261
+ $bt = debug_backtrace( true );
262
+ }
263
+
264
+ // Find our function
265
+ $frame = $bt[0];
266
+ if ( count( $bt ) >= 2 ) {
267
+ $frame = $bt[1];
268
+ }
269
+
270
+ // Include/require
271
+ if ( in_array( strtolower( $frame['function'] ), array( 'include', 'require', 'include_once', 'require_once' ) ) ) {
272
+ $file = $frame['args'][0];
273
+
274
+ // Object instances
275
+ } elseif ( isset( $frame['object'] ) && method_exists( $frame['object'], $frame['function'] ) ) {
276
+ try {
277
+ $reflector = new ReflectionMethod( $frame['object'], $frame['function'] );
278
+ $file = $reflector->getFileName();
279
+ } catch ( Exception $e ) {
280
+ }
281
+
282
+ // Static object calls
283
+ } elseif ( isset( $frame['class'] ) && method_exists( $frame['class'], $frame['function'] ) ) {
284
+ try {
285
+ $reflector = new ReflectionMethod( $frame['class'], $frame['function'] );
286
+ $file = $reflector->getFileName();
287
+ } catch ( Exception $e ) {
288
+ }
289
+
290
+ // Functions
291
+ } elseif ( !empty( $frame['function'] ) && function_exists( $frame['function'] ) ) {
292
+ try {
293
+ $reflector = new ReflectionFunction( $frame['function'] );
294
+ $file = $reflector->getFileName();
295
+ } catch ( Exception $e ) {
296
+ }
297
+
298
+ // Lambdas / closures
299
+ } elseif ( '__lambda_func' == $frame['function'] || '{closure}' == $frame['function'] ) {
300
+ $file = preg_replace( '/\(\d+\)\s+:\s+runtime-created function/', '', $bt[0]['file'] );
301
+
302
+ // Files, no other hints
303
+ } elseif ( isset( $frame['file'] ) ) {
304
+ $file = $frame['file'];
305
+
306
+ // No idea
307
+ } else {
308
+ $file = $_SERVER['SCRIPT_FILENAME'];
309
+ }
310
+ unset( $bt );
311
+
312
+ // Is it a plugin?
313
+ $plugin = $this->_is_a_plugin_file( $file );
314
+ if ( $plugin ) {
315
+ $plugin_name = $this->_get_plugin_name( $file );
316
+ }
317
+
318
+ // Is it a theme?
319
+ $is_a_theme = false;
320
+ if ( FALSE === $plugin ) {
321
+ if ( !$is_a_theme && isset( $theme_files_cache[$file] ) ) {
322
+ $is_a_theme = $theme_files_cache[$file];
323
+ }
324
+
325
+ $theme_files_cache[$file] = (
326
+ ( FALSE !== strpos( $file, '/' . $themes_folder . '/' ) || FALSE !== strpos( $file, '\\'. $themes_folder . '\\' ) ) &&
327
+ ( FALSE !== strpos( $file, '/' . $content_folder . '/' ) || FALSE !== strpos( $file, '\\' . $content_folder . '\\' ) )
328
+ );
329
+ $theme_files_cache[$file];
330
+
331
+ if ( $theme_files_cache[$file] ) {
332
+ $is_a_theme = true;
333
+ }
334
+ }
335
+
336
+ // If we're in a plugin, queue up the stack to be timed and logged during the next tick
337
+ if ( FALSE !== $plugin ) {
338
+ $this->_last_stack = array( 'plugin' => $plugin_name );
339
+ $this->_last_call_category = self::CATEGORY_PLUGIN;
340
+
341
+ // Track theme times - code can travel from core -> theme -> plugin, and the whole trace
342
+ // will show up in the stack, but we can only categorize it as one time, so we prioritize
343
+ // timing plugins over themes, and thems over the core.
344
+ } elseif ( FALSE !== $is_a_theme ) {
345
+ $this->_last_call_category = self::CATEGORY_THEME;
346
+ // We must be in the core
347
+ } else {
348
+ $this->_last_call_category = self::CATEGORY_CORE;
349
+ }
350
+
351
+ // Count the time spent in here as profiler runtime
352
+ $tmp = microtime( true );
353
+ $this->_runtime += ( $tmp - $start );
354
+
355
+ // Reset the timer for the next tick
356
+ $this->_last_call_start = microtime( true );
357
+ }
358
+
359
+ /**
360
+ * Check if the given file is in the plugins folder
361
+ * @param string $file
362
+ * @return bool
363
+ */
364
+ private function _is_a_plugin_file( $file ) {
365
+ static $plugin_files_cache = array();
366
+ static $plugins_folder = 'plugins'; // Guess, if it's not defined
367
+ static $muplugins_folder = 'mu-plugins';
368
+ static $content_folder = 'wp-content';
369
+ static $folder_flag = false;
370
+
371
+ // Set the plugins folder
372
+ if ( !$folder_flag && defined( 'WPMU_PLUGIN_DIR' ) ) {
373
+ $plugins_folder = basename( WP_PLUGIN_DIR );
374
+ $muplugins_folder = basename( WPMU_PLUGIN_DIR );
375
+ $content_folder = basename( WP_CONTENT_DIR );
376
+ $folder_flag = true;
377
+ }
378
+
379
+ if ( isset( $plugin_files_cache[$file] ) ) {
380
+ return $plugin_files_cache[$file];
381
+ }
382
+
383
+ $plugin_files_cache[$file] = (
384
+ (
385
+ ( FALSE !== strpos( $file, '/' . $plugins_folder . '/' ) || FALSE !== stripos( $file, '\\' . $plugins_folder . '\\' ) ) ||
386
+ ( FALSE !== strpos( $file, '/' . $muplugins_folder . '/' ) || FALSE !== stripos( $file, '\\' . $muplugins_folder . '\\' ) )
387
+ ) &&
388
+ ( FALSE !== strpos( $file, '/' . $content_folder . '/' ) || FALSE !== stripos( $file, '\\' . $content_folder . '\\' ) )
389
+ );
390
+
391
+ return $plugin_files_cache[$file];
392
+ }
393
+
394
+ /**
395
+ * Guess a plugin's name from the file path
396
+ * @param string $path
397
+ * @return string
398
+ */
399
+ private function _get_plugin_name( $path ) {
400
+ static $seen_files_cache = array();
401
+ static $plugins_folder = 'plugins'; // Guess, if it's not defined
402
+ static $muplugins_folder = 'mu-plugins';
403
+ static $content_folder = 'wp-content';
404
+ static $folder_flag = false;
405
+
406
+ // Set the plugins folder
407
+ if ( !$folder_flag && defined( 'WP_PLUGIN_DIR' ) ) {
408
+ $plugins_folder = basename( WP_PLUGIN_DIR );
409
+ $muplugins_folder = basename( WPMU_PLUGIN_DIR );
410
+ $content_folder = basename( WP_CONTENT_DIR );
411
+ $folder_flag = true;
412
+ }
413
+
414
+ // Check the cache
415
+ if ( isset( $seen_files_cache[$path] ) ) {
416
+ return $seen_files_cache[$path];
417
+ }
418
+
419
+ // Trim off the base path
420
+ $_path = realpath( $path );
421
+ if ( FALSE !== strpos( $_path, '/' . $content_folder . '/' . $plugins_folder . '/' ) ) {
422
+ $_path = substr(
423
+ $_path,
424
+ strpos( $_path, '/' . $content_folder . '/' . $plugins_folder . '/' ) +
425
+ strlen( '/' . $content_folder . '/' . $plugins_folder . '/' )
426
+ );
427
+ } elseif ( FALSE !== stripos( $_path, '\\' . $content_folder . '\\' . $plugins_folder . '\\' ) ) {
428
+ $_path = substr(
429
+ $_path,
430
+ stripos( $_path, '\\' . $content_folder . '\\' . $plugins_folder . '\\' ) +
431
+ strlen( '\\' . $content_folder . '\\' . $plugins_folder . '\\' )
432
+ );
433
+ } elseif ( FALSE !== strpos( $_path, '/' . $content_folder . '/' . $muplugins_folder . '/' ) ) {
434
+ $_path = substr(
435
+ $_path,
436
+ strpos( $_path, '/' . $content_folder . '/' . $muplugins_folder . '/' ) +
437
+ strlen( '/' . $content_folder . '/' . $muplugins_folder . '/' )
438
+ );
439
+ } elseif ( FALSE !== stripos( $_path, '\\' . $content_folder . '\\' . $muplugins_folder . '\\' ) ) {
440
+ $_path = substr(
441
+ $_path, stripos( $_path, '\\' . $content_folder . '\\' . $muplugins_folder . '\\' ) +
442
+ strlen( '\\' . $content_folder . '\\' . $muplugins_folder . '\\' )
443
+ );
444
+ }
445
+
446
+ // Grab the plugin name as a folder or a file
447
+ if ( FALSE !== strpos( $_path, DIRECTORY_SEPARATOR ) ) {
448
+ $plugin = substr( $_path, 0, strpos( $_path, DIRECTORY_SEPARATOR ) );
449
+ } else {
450
+ $plugin = substr( $_path, 0, stripos( $_path, '.php' ) );
451
+ }
452
+
453
+ // Save it to the cache
454
+ $seen_files_cache[$path] = $plugin;
455
+
456
+ // Return
457
+ return $plugin;
458
+ }
459
+
460
+ /**
461
+ * Shutdown handler function
462
+ * @return void
463
+ */
464
+ public function shutdown_handler() {
465
+
466
+ // Make sure we've actually started ( wp-cron??)
467
+ if ( !defined( 'WPP_PROFILING_STARTED' ) || !WPP_PROFILING_STARTED ) {
468
+ return;
469
+ }
470
+
471
+ // Last call time
472
+ $this->_last_call_time = ( microtime( true ) - $this->_last_call_start );
473
+
474
+ // Account for the last stack we measured
475
+ if ( self::CATEGORY_PLUGIN == $this->_last_call_category && array() !== $this->_last_stack ) {
476
+ // Write the stack to the profile
477
+ $this->_plugin_runtime += $this->_last_call_time;
478
+
479
+ // Add this stack to the profile
480
+ $this->_profile['stack'][] = array(
481
+ 'plugin' => $this->_last_stack['plugin'],
482
+ 'runtime' => $this->_last_call_time,
483
+ );
484
+
485
+ // Reset the stack
486
+ $this->_last_stack = array();
487
+ } elseif ( self::CATEGORY_THEME == $this->_last_call_category ) {
488
+ $this->_theme += $this->_last_call_time;
489
+ } elseif ( self::CATEGORY_CORE == $this->_last_call_category ) {
490
+ $this->_core += $this->_last_call_time;
491
+ }
492
+
493
+ // Total runtime by plugin
494
+ $plugin_totals = array();
495
+ if ( !empty( $this->_profile['stack'] ) ) {
496
+ foreach ( $this->_profile['stack'] as $stack ) {
497
+ if ( empty( $plugin_totals[$stack['plugin']] ) ) {
498
+ $plugin_totals[$stack['plugin']] = 0;
499
+ }
500
+ $plugin_totals[$stack['plugin']] += $stack['runtime'];
501
+ }
502
+ }
503
+ foreach ( $plugin_totals as $k => $v ) {
504
+ $plugin_totals[$k] = $v;
505
+ }
506
+
507
+ // Stop timing total run
508
+ $tmp = microtime( true );
509
+ $runtime = ( $tmp - $this->_start_time );
510
+
511
+ // Count the time spent in here as profiler runtime
512
+ $this->_runtime += ( $tmp - $this->_last_call_start );
513
+
514
+ // Is the whole script a plugin? ( e.g. http://mysite.com/wp-content/plugins/somescript.php )
515
+ if ( $this->_is_a_plugin_file( $_SERVER['SCRIPT_FILENAME'] ) ) {
516
+ $this->_profile['runtime'] = array(
517
+ 'total' => $runtime,
518
+ 'wordpress' => 0,
519
+ 'theme' => 0,
520
+ 'plugins' => ( $runtime - $this->_runtime ),
521
+ 'profile' => $this->_runtime,
522
+ 'breakdown' => array(
523
+ $this->_get_plugin_name( $_SERVER['SCRIPT_FILENAME'] ) => ( $runtime - $this->_runtime ),
524
+ )
525
+ );
526
+ } elseif (
527
+ ( FALSE !== strpos( $_SERVER['SCRIPT_FILENAME'], '/themes/' ) || FALSE !== stripos( $_SERVER['SCRIPT_FILENAME'], '\\themes\\' ) ) &&
528
+ (
529
+ FALSE !== strpos( $_SERVER['SCRIPT_FILENAME'], '/' . basename( WP_CONTENT_DIR ) . '/' ) ||
530
+ FALSE !== stripos( $file, '\\' . basename( WP_CONTENT_DIR ) . '\\' )
531
+ )
532
+ ) {
533
+ $this->_profile['runtime'] = array(
534
+ 'total' => $runtime,
535
+ 'wordpress' => 0.0,
536
+ 'theme' => ( $runtime - $this->_runtime ),
537
+ 'plugins' => 0.0,
538
+ 'profile' => $this->_runtime,
539
+ 'breakdown' => array()
540
+ );
541
+ } else {
542
+ // Add runtime information
543
+ $this->_profile['runtime'] = array(
544
+ 'total' => $runtime,
545
+ 'wordpress' => $this->_core,
546
+ 'theme' => $this->_theme,
547
+ 'plugins' => $this->_plugin_runtime,
548
+ 'profile' => $this->_runtime,
549
+ 'breakdown' => $plugin_totals,
550
+ );
551
+ }
552
+
553
+ // Additional metrics
554
+ $this->_profile['memory'] = memory_get_peak_usage( true );
555
+ $this->_profile['stacksize'] = count( $this->_profile['stack'] );
556
+ $this->_profile['queries'] = get_num_queries();
557
+
558
+ // Throw away unneeded information to make the profiles smaller
559
+ unset( $this->_profile['stack'] );
560
+
561
+ // Open the file and acquire an exclusive lock ( prevent multiple hits from stomping on our
562
+ // previous profiles
563
+ $uploads_dir = wp_upload_dir();
564
+ $path = $uploads_dir['basedir'] . DIRECTORY_SEPARATOR . 'profiles' . DIRECTORY_SEPARATOR . $this->_profile_filename;
565
+ $fp = fopen( $path, 'a+' );
566
+ $wait = 30; // Wait 30 iterations ( 3 seconds )
567
+ while ( !flock( $fp, LOCK_EX ) && $wait-- ) {
568
+ usleep( 100 * 1000 );
569
+ }
570
+
571
+ // If we've waited too long, bail, don't add this profile, there's too
572
+ // much traffic
573
+ if ( $wait <= 0 ) {
574
+ return;
575
+ }
576
+
577
+ fwrite( $fp, json_encode( $this->_profile ) . PHP_EOL );
578
+
579
+ // Release the lock and close the file
580
+ flock( $fp, LOCK_UN );
581
+ fclose( $fp );
582
+ }
583
+
584
+ /**
585
+ * Get the current URL
586
+ * @return string
587
+ */
588
+ private function _get_url() {
589
+ $protocol = 'http://';
590
+ if ( ( !empty( $_SERVER['HTTPS'] ) && 'on' == strtolower( $_SERVER['HTTPS'] ) ) || 443 == $_SERVER['SERVER_PORT'] ) {
591
+ $protocol = 'https://';
592
+ }
593
+ $domain = $_SERVER['HTTP_HOST'];
594
+ if ( !empty( $_SERVER['REQUEST_URI'] ) ) {
595
+ $file = '';
596
+ $query_string = '';
597
+ $path = $_SERVER['REQUEST_URI'];
598
+ } else {
599
+ $file = '';
600
+ if ( !empty( $_SERVER['SCRIPT_NAME'] ) ) {
601
+ $file = $_SERVER['SCRIPT_NAME'];
602
+ }
603
+ $path = '';
604
+ if ( !empty( $_SERVER['PATH_INFO'] ) ) {
605
+ $path = $_SERVER['PATH_INFO'];
606
+ } elseif ( !empty( $_SERVER['REDIRECT_URL'] ) ) {
607
+ $path = $_SERVER['REDIRECT_URL'];
608
+ }
609
+ $query_string = '';
610
+ if ( !empty( $_SERVER['QUERY_STRING'] ) ) {
611
+ $query_string = '?' . $_SERVER['QUERY_STRING'];
612
+ }
613
+ }
614
+ return $protocol.$domain.$file.$path.$query_string;
615
+ }
616
+
617
+ /**
618
+ * Get the user's IP
619
+ * @return string
620
+ */
621
+ public function get_ip() {
622
+ static $ip = '';
623
+ if ( !empty( $ip ) ) {
624
+ return $ip;
625
+ } else {
626
+ if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
627
+ $ip = filter_var( $_SERVER['HTTP_X_FORWARDED_FOR'], FILTER_SANITIZE_STRING );
628
+ } else {
629
+ $ip = filter_var( $_SERVER['REMOTE_ADDR'], FILTER_SANITIZE_STRING );
630
+ }
631
+ return $ip;
632
+ }
633
+ }
634
+ }
css/custom-theme/images/ui-bg_flat_0_aaaaaa_40x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_flat_100_ffffff_40x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_highlight-hard_100_206d92_1x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_highlight-hard_100_f1f1f1_1x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_highlight-hard_100_f8f8f8_1x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_highlight-hard_100_fbfbfb_1x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_highlight-soft_50_206d92_1x100.png ADDED
Binary file
css/custom-theme/images/ui-bg_inset-soft_95_fef1ec_1x100.png ADDED
Binary file
css/custom-theme/images/ui-icons_000000_256x240.png ADDED
Binary file
css/custom-theme/images/ui-icons_222222_256x240.png ADDED
Binary file
css/custom-theme/images/ui-icons_464646_256x240.png ADDED
Binary file
css/custom-theme/images/ui-icons_cd0a0a_256x240.png ADDED
Binary file
css/custom-theme/images/ui-icons_ffffff_256x240.png ADDED
Binary file
css/custom-theme/jquery-ui-1.8.16.custom.css ADDED
@@ -0,0 +1,568 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * jQuery UI CSS Framework 1.8.16
3
+ *
4
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
5
+ * Dual licensed under the MIT or GPL Version 2 licenses.
6
+ * http://jquery.org/license
7
+ *
8
+ * http://docs.jquery.com/UI/Theming/API
9
+ */
10
+
11
+ /* Layout helpers
12
+ ----------------------------------*/
13
+ .ui-helper-hidden { display: none; }
14
+ .ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
15
+ .ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
16
+ .ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
17
+ .ui-helper-clearfix { display: inline-block; }
18
+ /* required comment for clearfix to work in Opera \*/
19
+ * html .ui-helper-clearfix { height:1%; }
20
+ .ui-helper-clearfix { display:block; }
21
+ /* end clearfix */
22
+ .ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
23
+
24
+
25
+ /* Interaction Cues
26
+ ----------------------------------*/
27
+ .ui-state-disabled { cursor: default !important; }
28
+
29
+
30
+ /* Icons
31
+ ----------------------------------*/
32
+
33
+ /* states and images */
34
+ .ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
35
+
36
+
37
+ /* Misc visuals
38
+ ----------------------------------*/
39
+
40
+ /* Overlays */
41
+ .ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
42
+
43
+
44
+ /*
45
+ * jQuery UI CSS Framework 1.8.16
46
+ *
47
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
48
+ * Dual licensed under the MIT or GPL Version 2 licenses.
49
+ * http://jquery.org/license
50
+ *
51
+ * http://docs.jquery.com/UI/Theming/API
52
+ *
53
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=8px&bgColorHeader=f8f8f8&bgTextureHeader=04_highlight_hard.png&bgImgOpacityHeader=100&borderColorHeader=dfdfdf&fcHeader=464646&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=100&borderColorContent=dfdfdf&fcContent=333333&iconColorContent=222222&bgColorDefault=f1f1f1&bgTextureDefault=04_highlight_hard.png&bgImgOpacityDefault=100&borderColorDefault=bbbbbb&fcDefault=464646&iconColorDefault=464646&bgColorHover=f1f1f1&bgTextureHover=04_highlight_hard.png&bgImgOpacityHover=100&borderColorHover=666666&fcHover=464646&iconColorHover=464646&bgColorActive=206d92&bgTextureActive=04_highlight_hard.png&bgImgOpacityActive=100&borderColorActive=298cba&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=206d92&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=50&borderColorHighlight=298cba&fcHighlight=ffffff&iconColorHighlight=ffffff&bgColorError=fef1ec&bgTextureError=05_inset_soft.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
54
+ */
55
+
56
+
57
+ /* Component containers
58
+ ----------------------------------*/
59
+ .ui-widget { font-family: Verdana,Arial,sans-serif; font-size: 1.1em; }
60
+ .ui-widget .ui-widget { font-size: 1em; }
61
+ .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; }
62
+ .ui-widget-content { border: 1px solid #dfdfdf; background: #ffffff url(images/ui-bg_flat_100_ffffff_40x100.png) 50% 50% repeat-x; color: #333333; }
63
+ .ui-widget-content a { color: #333333; }
64
+ .ui-widget-header { border: 1px solid #dfdfdf; background: #f8f8f8 url(images/ui-bg_highlight-hard_100_f8f8f8_1x100.png) 50% 50% repeat-x; color: #464646; font-weight: bold; }
65
+ .ui-widget-header a { color: #464646; }
66
+
67
+ /* Interaction states
68
+ ----------------------------------*/
69
+ .ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #bbbbbb; background: #f1f1f1 url(images/ui-bg_highlight-hard_100_f1f1f1_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #464646; }
70
+ .ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #464646; text-decoration: none; }
71
+ .ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #666666; background: #f1f1f1 url(images/ui-bg_highlight-hard_100_f1f1f1_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #464646; }
72
+ .ui-state-hover a, .ui-state-hover a:hover { color: #464646; text-decoration: none; }
73
+ .ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #298cba; background: #206d92 url(images/ui-bg_highlight-hard_100_206d92_1x100.png) 50% 50% repeat-x; font-weight: normal; color: #ffffff; }
74
+ .ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #ffffff; text-decoration: none; }
75
+ .ui-widget :active { outline: none; }
76
+
77
+ /* Interaction Cues
78
+ ----------------------------------*/
79
+ .ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #298cba; background: #206d92 url(images/ui-bg_highlight-soft_50_206d92_1x100.png) 50% top repeat-x; color: #ffffff; }
80
+ .ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #ffffff; }
81
+ .ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_inset-soft_95_fef1ec_1x100.png) 50% bottom repeat-x; color: #cd0a0a; }
82
+ .ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; }
83
+ .ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; }
84
+ .ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
85
+ .ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
86
+ .ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
87
+
88
+ /* Icons
89
+ ----------------------------------*/
90
+
91
+ /* states and images */
92
+ .ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
93
+ .ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
94
+ .ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
95
+ .ui-state-default .ui-icon { background-image: url(images/ui-icons_464646_256x240.png); }
96
+ .ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_464646_256x240.png); }
97
+ .ui-state-active .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
98
+ .ui-state-highlight .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
99
+ .ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); }
100
+
101
+ /* positioning */
102
+ .ui-icon-carat-1-n { background-position: 0 0; }
103
+ .ui-icon-carat-1-ne { background-position: -16px 0; }
104
+ .ui-icon-carat-1-e { background-position: -32px 0; }
105
+ .ui-icon-carat-1-se { background-position: -48px 0; }
106
+ .ui-icon-carat-1-s { background-position: -64px 0; }
107
+ .ui-icon-carat-1-sw { background-position: -80px 0; }
108
+ .ui-icon-carat-1-w { background-position: -96px 0; }
109
+ .ui-icon-carat-1-nw { background-position: -112px 0; }
110
+ .ui-icon-carat-2-n-s { background-position: -128px 0; }
111
+ .ui-icon-carat-2-e-w { background-position: -144px 0; }
112
+ .ui-icon-triangle-1-n { background-position: 0 -16px; }
113
+ .ui-icon-triangle-1-ne { background-position: -16px -16px; }
114
+ .ui-icon-triangle-1-e { background-position: -32px -16px; }
115
+ .ui-icon-triangle-1-se { background-position: -48px -16px; }
116
+ .ui-icon-triangle-1-s { background-position: -64px -16px; }
117
+ .ui-icon-triangle-1-sw { background-position: -80px -16px; }
118
+ .ui-icon-triangle-1-w { background-position: -96px -16px; }
119
+ .ui-icon-triangle-1-nw { background-position: -112px -16px; }
120
+ .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
121
+ .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
122
+ .ui-icon-arrow-1-n { background-position: 0 -32px; }
123
+ .ui-icon-arrow-1-ne { background-position: -16px -32px; }
124
+ .ui-icon-arrow-1-e { background-position: -32px -32px; }
125
+ .ui-icon-arrow-1-se { background-position: -48px -32px; }
126
+ .ui-icon-arrow-1-s { background-position: -64px -32px; }
127
+ .ui-icon-arrow-1-sw { background-position: -80px -32px; }
128
+ .ui-icon-arrow-1-w { background-position: -96px -32px; }
129
+ .ui-icon-arrow-1-nw { background-position: -112px -32px; }
130
+ .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
131
+ .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
132
+ .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
133
+ .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
134
+ .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
135
+ .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
136
+ .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
137
+ .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
138
+ .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
139
+ .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
140
+ .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
141
+ .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
142
+ .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
143
+ .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
144
+ .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
145
+ .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
146
+ .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
147
+ .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
148
+ .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
149
+ .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
150
+ .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
151
+ .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
152
+ .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
153
+ .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
154
+ .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
155
+ .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
156
+ .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
157
+ .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
158
+ .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
159
+ .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
160
+ .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
161
+ .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
162
+ .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
163
+ .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
164
+ .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
165
+ .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
166
+ .ui-icon-arrow-4 { background-position: 0 -80px; }
167
+ .ui-icon-arrow-4-diag { background-position: -16px -80px; }
168
+ .ui-icon-extlink { background-position: -32px -80px; }
169
+ .ui-icon-newwin { background-position: -48px -80px; }
170
+ .ui-icon-refresh { background-position: -64px -80px; }
171
+ .ui-icon-shuffle { background-position: -80px -80px; }
172
+ .ui-icon-transfer-e-w { background-position: -96px -80px; }
173
+ .ui-icon-transferthick-e-w { background-position: -112px -80px; }
174
+ .ui-icon-folder-collapsed { background-position: 0 -96px; }
175
+ .ui-icon-folder-open { background-position: -16px -96px; }
176
+ .ui-icon-document { background-position: -32px -96px; }
177
+ .ui-icon-document-b { background-position: -48px -96px; }
178
+ .ui-icon-note { background-position: -64px -96px; }
179
+ .ui-icon-mail-closed { background-position: -80px -96px; }
180
+ .ui-icon-mail-open { background-position: -96px -96px; }
181
+ .ui-icon-suitcase { background-position: -112px -96px; }
182
+ .ui-icon-comment { background-position: -128px -96px; }
183
+ .ui-icon-person { background-position: -144px -96px; }
184
+ .ui-icon-print { background-position: -160px -96px; }
185
+ .ui-icon-trash { background-position: -176px -96px; }
186
+ .ui-icon-locked { background-position: -192px -96px; }
187
+ .ui-icon-unlocked { background-position: -208px -96px; }
188
+ .ui-icon-bookmark { background-position: -224px -96px; }
189
+ .ui-icon-tag { background-position: -240px -96px; }
190
+ .ui-icon-home { background-position: 0 -112px; }
191
+ .ui-icon-flag { background-position: -16px -112px; }
192
+ .ui-icon-calendar { background-position: -32px -112px; }
193
+ .ui-icon-cart { background-position: -48px -112px; }
194
+ .ui-icon-pencil { background-position: -64px -112px; }
195
+ .ui-icon-clock { background-position: -80px -112px; }
196
+ .ui-icon-disk { background-position: -96px -112px; }
197
+ .ui-icon-calculator { background-position: -112px -112px; }
198
+ .ui-icon-zoomin { background-position: -128px -112px; }
199
+ .ui-icon-zoomout { background-position: -144px -112px; }
200
+ .ui-icon-search { background-position: -160px -112px; }
201
+ .ui-icon-wrench { background-position: -176px -112px; }
202
+ .ui-icon-gear { background-position: -192px -112px; }
203
+ .ui-icon-heart { background-position: -208px -112px; }
204
+ .ui-icon-star { background-position: -224px -112px; }
205
+ .ui-icon-link { background-position: -240px -112px; }
206
+ .ui-icon-cancel { background-position: 0 -128px; }
207
+ .ui-icon-plus { background-position: -16px -128px; }
208
+ .ui-icon-plusthick { background-position: -32px -128px; }
209
+ .ui-icon-minus { background-position: -48px -128px; }
210
+ .ui-icon-minusthick { background-position: -64px -128px; }
211
+ .ui-icon-close { background-position: -80px -128px; }
212
+ .ui-icon-closethick { background-position: -96px -128px; }
213
+ .ui-icon-key { background-position: -112px -128px; }
214
+ .ui-icon-lightbulb { background-position: -128px -128px; }
215
+ .ui-icon-scissors { background-position: -144px -128px; }
216
+ .ui-icon-clipboard { background-position: -160px -128px; }
217
+ .ui-icon-copy { background-position: -176px -128px; }
218
+ .ui-icon-contact { background-position: -192px -128px; }
219
+ .ui-icon-image { background-position: -208px -128px; }
220
+ .ui-icon-video { background-position: -224px -128px; }
221
+ .ui-icon-script { background-position: -240px -128px; }
222
+ .ui-icon-alert { background-position: 0 -144px; }
223
+ .ui-icon-info { background-position: -16px -144px; }
224
+ .ui-icon-notice { background-position: -32px -144px; }
225
+ .ui-icon-help { background-position: -48px -144px; }
226
+ .ui-icon-check { background-position: -64px -144px; }
227
+ .ui-icon-bullet { background-position: -80px -144px; }
228
+ .ui-icon-radio-off { background-position: -96px -144px; }
229
+ .ui-icon-radio-on { background-position: -112px -144px; }
230
+ .ui-icon-pin-w { background-position: -128px -144px; }
231
+ .ui-icon-pin-s { background-position: -144px -144px; }
232
+ .ui-icon-play { background-position: 0 -160px; }
233
+ .ui-icon-pause { background-position: -16px -160px; }
234
+ .ui-icon-seek-next { background-position: -32px -160px; }
235
+ .ui-icon-seek-prev { background-position: -48px -160px; }
236
+ .ui-icon-seek-end { background-position: -64px -160px; }
237
+ .ui-icon-seek-start { background-position: -80px -160px; }
238
+ /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
239
+ .ui-icon-seek-first { background-position: -80px -160px; }
240
+ .ui-icon-stop { background-position: -96px -160px; }
241
+ .ui-icon-eject { background-position: -112px -160px; }
242
+ .ui-icon-volume-off { background-position: -128px -160px; }
243
+ .ui-icon-volume-on { background-position: -144px -160px; }
244
+ .ui-icon-power { background-position: 0 -176px; }
245
+ .ui-icon-signal-diag { background-position: -16px -176px; }
246
+ .ui-icon-signal { background-position: -32px -176px; }
247
+ .ui-icon-battery-0 { background-position: -48px -176px; }
248
+ .ui-icon-battery-1 { background-position: -64px -176px; }
249
+ .ui-icon-battery-2 { background-position: -80px -176px; }
250
+ .ui-icon-battery-3 { background-position: -96px -176px; }
251
+ .ui-icon-circle-plus { background-position: 0 -192px; }
252
+ .ui-icon-circle-minus { background-position: -16px -192px; }
253
+ .ui-icon-circle-close { background-position: -32px -192px; }
254
+ .ui-icon-circle-triangle-e { background-position: -48px -192px; }
255
+ .ui-icon-circle-triangle-s { background-position: -64px -192px; }
256
+ .ui-icon-circle-triangle-w { background-position: -80px -192px; }
257
+ .ui-icon-circle-triangle-n { background-position: -96px -192px; }
258
+ .ui-icon-circle-arrow-e { background-position: -112px -192px; }
259
+ .ui-icon-circle-arrow-s { background-position: -128px -192px; }
260
+ .ui-icon-circle-arrow-w { background-position: -144px -192px; }
261
+ .ui-icon-circle-arrow-n { background-position: -160px -192px; }
262
+ .ui-icon-circle-zoomin { background-position: -176px -192px; }
263
+ .ui-icon-circle-zoomout { background-position: -192px -192px; }
264
+ .ui-icon-circle-check { background-position: -208px -192px; }
265
+ .ui-icon-circlesmall-plus { background-position: 0 -208px; }
266
+ .ui-icon-circlesmall-minus { background-position: -16px -208px; }
267
+ .ui-icon-circlesmall-close { background-position: -32px -208px; }
268
+ .ui-icon-squaresmall-plus { background-position: -48px -208px; }
269
+ .ui-icon-squaresmall-minus { background-position: -64px -208px; }
270
+ .ui-icon-squaresmall-close { background-position: -80px -208px; }
271
+ .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
272
+ .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
273
+ .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
274
+ .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
275
+ .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
276
+ .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
277
+
278
+
279
+ /* Misc visuals
280
+ ----------------------------------*/
281
+
282
+ /* Corner radius */
283
+ .ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 8px; -webkit-border-top-left-radius: 8px; -khtml-border-top-left-radius: 8px; border-top-left-radius: 8px; }
284
+ .ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 8px; -webkit-border-top-right-radius: 8px; -khtml-border-top-right-radius: 8px; border-top-right-radius: 8px; }
285
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 8px; -webkit-border-bottom-left-radius: 8px; -khtml-border-bottom-left-radius: 8px; border-bottom-left-radius: 8px; }
286
+ .ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 8px; -webkit-border-bottom-right-radius: 8px; -khtml-border-bottom-right-radius: 8px; border-bottom-right-radius: 8px; }
287
+
288
+ /* Overlays */
289
+ .ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); }
290
+ .ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/*
291
+ * jQuery UI Resizable 1.8.16
292
+ *
293
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
294
+ * Dual licensed under the MIT or GPL Version 2 licenses.
295
+ * http://jquery.org/license
296
+ *
297
+ * http://docs.jquery.com/UI/Resizable#theming
298
+ */
299
+ .ui-resizable { position: relative;}
300
+ .ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
301
+ .ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
302
+ .ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
303
+ .ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
304
+ .ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
305
+ .ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
306
+ .ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
307
+ .ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
308
+ .ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
309
+ .ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
310
+ * jQuery UI Selectable 1.8.16
311
+ *
312
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
313
+ * Dual licensed under the MIT or GPL Version 2 licenses.
314
+ * http://jquery.org/license
315
+ *
316
+ * http://docs.jquery.com/UI/Selectable#theming
317
+ */
318
+ .ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
319
+ /*
320
+ * jQuery UI Accordion 1.8.16
321
+ *
322
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
323
+ * Dual licensed under the MIT or GPL Version 2 licenses.
324
+ * http://jquery.org/license
325
+ *
326
+ * http://docs.jquery.com/UI/Accordion#theming
327
+ */
328
+ /* IE/Win - Fix animation bug - #4615 */
329
+ .ui-accordion { width: 100%; }
330
+ .ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
331
+ .ui-accordion .ui-accordion-li-fix { display: inline; }
332
+ .ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
333
+ .ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
334
+ .ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
335
+ .ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
336
+ .ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
337
+ .ui-accordion .ui-accordion-content-active { display: block; }
338
+ /*
339
+ * jQuery UI Autocomplete 1.8.16
340
+ *
341
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
342
+ * Dual licensed under the MIT or GPL Version 2 licenses.
343
+ * http://jquery.org/license
344
+ *
345
+ * http://docs.jquery.com/UI/Autocomplete#theming
346
+ */
347
+ .ui-autocomplete { position: absolute; cursor: default; }
348
+
349
+ /* workarounds */
350
+ * html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
351
+
352
+ /*
353
+ * jQuery UI Menu 1.8.16
354
+ *
355
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
356
+ * Dual licensed under the MIT or GPL Version 2 licenses.
357
+ * http://jquery.org/license
358
+ *
359
+ * http://docs.jquery.com/UI/Menu#theming
360
+ */
361
+ .ui-menu {
362
+ list-style:none;
363
+ padding: 2px;
364
+ margin: 0;
365
+ display:block;
366
+ float: left;
367
+ }
368
+ .ui-menu .ui-menu {
369
+ margin-top: -3px;
370
+ }
371
+ .ui-menu .ui-menu-item {
372
+ margin:0;
373
+ padding: 0;
374
+ zoom: 1;
375
+ float: left;
376
+ clear: left;
377
+ width: 100%;
378
+ }
379
+ .ui-menu .ui-menu-item a {
380
+ text-decoration:none;
381
+ display:block;
382
+ padding:.2em .4em;
383
+ line-height:1.5;
384
+ zoom:1;
385
+ }
386
+ .ui-menu .ui-menu-item a.ui-state-hover,
387
+ .ui-menu .ui-menu-item a.ui-state-active {
388
+ font-weight: normal;
389
+ margin: -1px;
390
+ }
391
+ /*
392
+ * jQuery UI Button 1.8.16
393
+ *
394
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
395
+ * Dual licensed under the MIT or GPL Version 2 licenses.
396
+ * http://jquery.org/license
397
+ *
398
+ * http://docs.jquery.com/UI/Button#theming
399
+ */
400
+ .ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
401
+ .ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
402
+ button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
403
+ .ui-button-icons-only { width: 3.4em; }
404
+ button.ui-button-icons-only { width: 3.7em; }
405
+
406
+ /*button text element */
407
+ .ui-button .ui-button-text { display: block; line-height: 1.4; }
408
+ .ui-button-text-only .ui-button-text { padding: .4em 1em; }
409
+ .ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
410
+ .ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
411
+ .ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
412
+ .ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
413
+ /* no icon support for input elements, provide padding by default */
414
+ input.ui-button { padding: .4em 1em; }
415
+
416
+ /*button icon element(s) */
417
+ .ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
418
+ .ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
419
+ .ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
420
+ .ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
421
+ .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
422
+
423
+ /*button sets*/
424
+ .ui-buttonset { margin-right: 7px; }
425
+ .ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
426
+
427
+ /* workarounds */
428
+ button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
429
+ /*
430
+ * jQuery UI Dialog 1.8.16
431
+ *
432
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
433
+ * Dual licensed under the MIT or GPL Version 2 licenses.
434
+ * http://jquery.org/license
435
+ *
436
+ * http://docs.jquery.com/UI/Dialog#theming
437
+ */
438
+ .ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
439
+ .ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative; }
440
+ .ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; }
441
+ .ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
442
+ .ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
443
+ .ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
444
+ .ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
445
+ .ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
446
+ .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
447
+ .ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
448
+ .ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
449
+ .ui-draggable .ui-dialog-titlebar { cursor: move; }
450
+ /*
451
+ * jQuery UI Slider 1.8.16
452
+ *
453
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
454
+ * Dual licensed under the MIT or GPL Version 2 licenses.
455
+ * http://jquery.org/license
456
+ *
457
+ * http://docs.jquery.com/UI/Slider#theming
458
+ */
459
+ .ui-slider { position: relative; text-align: left; }
460
+ .ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
461
+ .ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
462
+
463
+ .ui-slider-horizontal { height: .8em; }
464
+ .ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
465
+ .ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
466
+ .ui-slider-horizontal .ui-slider-range-min { left: 0; }
467
+ .ui-slider-horizontal .ui-slider-range-max { right: 0; }
468
+
469
+ .ui-slider-vertical { width: .8em; height: 100px; }
470
+ .ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
471
+ .ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
472
+ .ui-slider-vertical .ui-slider-range-min { bottom: 0; }
473
+ .ui-slider-vertical .ui-slider-range-max { top: 0; }/*
474
+ * jQuery UI Tabs 1.8.16
475
+ *
476
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
477
+ * Dual licensed under the MIT or GPL Version 2 licenses.
478
+ * http://jquery.org/license
479
+ *
480
+ * http://docs.jquery.com/UI/Tabs#theming
481
+ */
482
+ .ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
483
+ .ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
484
+ .ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
485
+ .ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
486
+ .ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
487
+ .ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
488
+ .ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
489
+ .ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
490
+ .ui-tabs .ui-tabs-hide { display: none !important; }
491
+ /*
492
+ * jQuery UI Datepicker 1.8.16
493
+ *
494
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
495
+ * Dual licensed under the MIT or GPL Version 2 licenses.
496
+ * http://jquery.org/license
497
+ *
498
+ * http://docs.jquery.com/UI/Datepicker#theming
499
+ */
500
+ .ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
501
+ .ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
502
+ .ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
503
+ .ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
504
+ .ui-datepicker .ui-datepicker-prev { left:2px; }
505
+ .ui-datepicker .ui-datepicker-next { right:2px; }
506
+ .ui-datepicker .ui-datepicker-prev-hover { left:1px; }
507
+ .ui-datepicker .ui-datepicker-next-hover { right:1px; }
508
+ .ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; }
509
+ .ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
510
+ .ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
511
+ .ui-datepicker select.ui-datepicker-month-year {width: 100%;}
512
+ .ui-datepicker select.ui-datepicker-month,
513
+ .ui-datepicker select.ui-datepicker-year { width: 49%;}
514
+ .ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
515
+ .ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; }
516
+ .ui-datepicker td { border: 0; padding: 1px; }
517
+ .ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
518
+ .ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
519
+ .ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
520
+ .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
521
+
522
+ /* with multiple calendars */
523
+ .ui-datepicker.ui-datepicker-multi { width:auto; }
524
+ .ui-datepicker-multi .ui-datepicker-group { float:left; }
525
+ .ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
526
+ .ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
527
+ .ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
528
+ .ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
529
+ .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
530
+ .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
531
+ .ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
532
+ .ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
533
+
534
+ /* RTL support */
535
+ .ui-datepicker-rtl { direction: rtl; }
536
+ .ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
537
+ .ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
538
+ .ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
539
+ .ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
540
+ .ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
541
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
542
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
543
+ .ui-datepicker-rtl .ui-datepicker-group { float:right; }
544
+ .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
545
+ .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
546
+
547
+ /* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
548
+ .ui-datepicker-cover {
549
+ display: none; /*sorry for IE5*/
550
+ display/**/: block; /*sorry for IE5*/
551
+ position: absolute; /*must have*/
552
+ z-index: -1; /*must have*/
553
+ filter: mask(); /*must have*/
554
+ top: -4px; /*must have*/
555
+ left: -4px; /*must have*/
556
+ width: 200px; /*must have*/
557
+ height: 200px; /*must have*/
558
+ }/*
559
+ * jQuery UI Progressbar 1.8.16
560
+ *
561
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
562
+ * Dual licensed under the MIT or GPL Version 2 licenses.
563
+ * http://jquery.org/license
564
+ *
565
+ * http://docs.jquery.com/UI/Progressbar#theming
566
+ */
567
+ .ui-progressbar { height:2em; text-align: left; }
568
+ .ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
css/icon_mail.gif ADDED
Binary file
css/jquery.qtip.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .ui-tooltip,.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;z-index:15000;}.ui-tooltip-fluid{display:block;visibility:hidden;position:static!important;float:left!important;}.ui-tooltip-content{position:relative;padding:5px 9px;overflow:hidden;border-width:1px;border-style:solid;text-align:left;word-wrap:break-word;overflow:hidden;}.ui-tooltip-titlebar{position:relative;min-height:14px;padding:5px 35px 5px 10px;overflow:hidden;border-width:1px 1px 0;border-style:solid;font-weight:bold;}.ui-tooltip-titlebar+.ui-tooltip-content{border-top-width:0!important;}/*!Default close button class */ .ui-tooltip-titlebar .ui-state-default{position:absolute;right:4px;top:50%;margin-top:-9px;cursor:pointer;outline:medium none;border-width:1px;border-style:solid;}* html .ui-tooltip-titlebar .ui-state-default{top:16px;}.ui-tooltip-titlebar .ui-icon,.ui-tooltip-icon .ui-icon{display:block;text-indent:-1000em;}.ui-tooltip-icon,.ui-tooltip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;}.ui-tooltip-icon .ui-icon{width:18px;height:14px;text-align:center;text-indent:0;font:normal bold 10px/13px Tahoma,sans-serif;color:inherit;background:transparent none no-repeat -100em -100em;}/*!Default tooltip style */ .ui-tooltip-default .ui-tooltip-titlebar,.ui-tooltip-default .ui-tooltip-content{border-color:#F1D031;background-color:#FFFFA3;color:#555;}.ui-tooltip-default .ui-tooltip-titlebar{background-color:#FFEF93;}.ui-tooltip-default .ui-tooltip-icon{border-color:#CCC;background:#F1F1F1;color:#777;}.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{border-color:#AAA;color:#111;}.ui-tooltip .ui-tooltip-tip{margin:0 auto;overflow:hidden;background:transparent!important;border:0 dashed transparent!important;z-index:10;}.ui-tooltip .ui-tooltip-tip,.ui-tooltip .ui-tooltip-tip *{position:absolute;line-height:.1px!important;font-size:.1px!important;color:#123456;background:transparent;border:0 dashed transparent;}.ui-tooltip .ui-tooltip-tip canvas{top:0;left:0;}/*!Light tooltip style */ .ui-tooltip-light .ui-tooltip-titlebar,.ui-tooltip-light .ui-tooltip-content{border-color:#E2E2E2;color:#454545;}.ui-tooltip-light .ui-tooltip-content{background-color:white;}.ui-tooltip-light .ui-tooltip-titlebar{background-color:#f1f1f1;}/*!Dark tooltip style */ .ui-tooltip-dark .ui-tooltip-titlebar,.ui-tooltip-dark .ui-tooltip-content{border-color:#303030;color:#f3f3f3;}.ui-tooltip-dark .ui-tooltip-content{background-color:#505050;}.ui-tooltip-dark .ui-tooltip-titlebar{background-color:#404040;}.ui-tooltip-dark .ui-tooltip-icon{border-color:#444;}.ui-tooltip-dark .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}/*!Cream tooltip style */ .ui-tooltip-cream .ui-tooltip-titlebar,.ui-tooltip-cream .ui-tooltip-content{border-color:#F9E98E;color:#A27D35;}.ui-tooltip-cream .ui-tooltip-content{background-color:#FBF7AA;}.ui-tooltip-cream .ui-tooltip-titlebar{background-color:#F0DE7D;}.ui-tooltip-cream .ui-state-default .ui-tooltip-icon{background-position:-82px 0;}/*!Red tooltip style */ .ui-tooltip-red .ui-tooltip-titlebar,.ui-tooltip-red .ui-tooltip-content{border-color:#D95252;color:#912323;}.ui-tooltip-red .ui-tooltip-content{background-color:#F78B83;}.ui-tooltip-red .ui-tooltip-titlebar{background-color:#F06D65;}.ui-tooltip-red .ui-state-default .ui-tooltip-icon{background-position:-102px 0;}.ui-tooltip-red .ui-tooltip-icon{border-color:#D95252;}.ui-tooltip-red .ui-tooltip-titlebar .ui-state-hover{border-color:#D95252;}/*!Green tooltip style */ .ui-tooltip-green .ui-tooltip-titlebar,.ui-tooltip-green .ui-tooltip-content{border-color:#90D93F;color:#3F6219;}.ui-tooltip-green .ui-tooltip-content{background-color:#CAED9E;}.ui-tooltip-green .ui-tooltip-titlebar{background-color:#B0DE78;}.ui-tooltip-green .ui-state-default .ui-tooltip-icon{background-position:-42px 0;}/*!Blue tooltip style */ .ui-tooltip-blue .ui-tooltip-titlebar,.ui-tooltip-blue .ui-tooltip-content{border-color:#ADD9ED;color:#5E99BD;}.ui-tooltip-blue .ui-tooltip-content{background-color:#E5F6FE;}.ui-tooltip-blue .ui-tooltip-titlebar{background-color:#D0E9F5;}.ui-tooltip-blue .ui-state-default .ui-tooltip-icon{background-position:-2px 0;}/*!Add shadows to your tooltips in:FF3+,Chrome 2+,Opera 10.6+,IE6+,Safari 2+*/ .ui-tooltip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);}.ui-tooltip-shadow .ui-tooltip-titlebar,.ui-tooltip-shadow .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.Shadow(Color='gray',Direction=135,Strength=3);-ms-filter:"progid:DXImageTransform.Microsoft.Shadow(Color='gray',Direction=135,Strength=3)";_margin-bottom:-3px;.margin-bottom:-3px;}/*!Add rounded corners to your tooltips in:FF3+,Chrome 2+,Opera 10.6+,IE9+,Safari 2+*/ .ui-tooltip-rounded,.ui-tooltip-rounded .ui-tooltip-content,.ui-tooltip-tipsy,.ui-tooltip-tipsy .ui-tooltip-content,.ui-tooltip-youtube,.ui-tooltip-youtube .ui-tooltip-content{-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px;}.ui-tooltip-rounded .ui-tooltip-titlebar,.ui-tooltip-tipsy .ui-tooltip-titlebar,.ui-tooltip-youtube .ui-tooltip-titlebar{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;}.ui-tooltip-rounded .ui-tooltip-titlebar+.ui-tooltip-content,.ui-tooltip-tipsy .ui-tooltip-titlebar+.ui-tooltip-content,.ui-tooltip-youtube .ui-tooltip-titlebar+.ui-tooltip-content{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0 0 5px 5px;border-radius:0 0 5px 5px;}/*!Youtube tooltip style */ .ui-tooltip-youtube{-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;}.ui-tooltip-youtube .ui-tooltip-titlebar,.ui-tooltip-youtube .ui-tooltip-content{_margin-bottom:0;.margin-bottom:0;background:transparent;background:rgba(0,0,0,0.85);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000)";color:white;border-color:#CCC;}.ui-tooltip-youtube .ui-tooltip-icon{border-color:#222;}.ui-tooltip-youtube .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}.ui-tooltip-jtools{background:#232323;background:rgba(0,0,0,0.7);background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333;}.ui-tooltip-jtools .ui-tooltip-titlebar{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";}.ui-tooltip-jtools .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";}.ui-tooltip-jtools .ui-tooltip-titlebar,.ui-tooltip-jtools .ui-tooltip-content{background:transparent;color:white;border:0 dashed transparent;}.ui-tooltip-jtools .ui-tooltip-icon{border-color:#555;}.ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{border-color:#333;}.ui-tooltip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,0.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,0.4);box-shadow:4px 4px 5px rgba(0,0,0,0.4);}.ui-tooltip-cluetip .ui-tooltip-titlebar{background-color:#87876A;color:white;border:0 dashed transparent;}.ui-tooltip-cluetip .ui-tooltip-content{background-color:#D9D9C2;color:#111;border:0 dashed transparent;}.ui-tooltip-cluetip .ui-tooltip-icon{border-color:#808064;}.ui-tooltip-cluetip .ui-tooltip-titlebar .ui-state-hover{border-color:#696952;color:#696952;}.ui-tooltip-tipsy{border:0;}.ui-tooltip-tipsy .ui-tooltip-titlebar,.ui-tooltip-tipsy .ui-tooltip-content{_margin-bottom:0;.margin-bottom:0;background:transparent;background:rgba(0,0,0,.87);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#D9000000,endColorstr=#D9000000)";color:white;border:0 transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:bold;line-height:16px;text-shadow:0 1px black;}.ui-tooltip-tipsy .ui-tooltip-titlebar{padding:6px 35px 0 10;}.ui-tooltip-tipsy .ui-tooltip-content{padding:6px 10;}.ui-tooltip-tipsy .ui-tooltip-icon{border-color:#222;text-shadow:none;}.ui-tooltip-tipsy .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}.ui-tooltip-tipped .ui-tooltip-titlebar,.ui-tooltip-tipped .ui-tooltip-content{border:3px solid #959FA9;filter:none;-ms-filter:none;}.ui-tooltip-tipped .ui-tooltip-titlebar{background:#3A79B8;background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";color:white;font-weight:normal;font-family:serif;border-bottom-width:0;-moz-border-radius:3px 3px 0 0;-webkit-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;}.ui-tooltip-tipped .ui-tooltip-content{background-color:#F9F9F9;color:#454545;-moz-border-radius:0 0 3px 3px;-webkit-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.ui-tooltip-tipped .ui-tooltip-icon{border:2px solid #285589;background:#285589;}.ui-tooltip-tipped .ui-tooltip-icon .ui-icon{background-color:#FBFBFB;color:#555;}.ui-tooltip:not(.ie9haxors) div.ui-tooltip-content,.ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{filter:none;-ms-filter:none;}
css/p3.css ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /** Top grey nav bar **/
3
+ #p3-navbar {
4
+ margin-top: 10px;
5
+ padding: 4px;
6
+ cursor: default;
7
+ }
8
+
9
+
10
+ /** The grey box behind the big buttons **/
11
+ #p3-scan-form-wrapper {
12
+ cursor: default;
13
+ padding: 10px;
14
+ width: 170px;
15
+ height: 80px;
16
+ text-align: center;
17
+ margin-top: 10px;
18
+ }
19
+
20
+ /** Big buttons **/
21
+ .p3-big-button {
22
+ margin: auto;
23
+ text-align: center;
24
+ }
25
+ .p3-big-button label {
26
+ margin: 5px;
27
+ }
28
+ .p3-big-button label span {
29
+ font-size: 110%;
30
+ font-weight: bold;
31
+ width: 110px;
32
+ margin: 2px;
33
+ }
34
+
35
+
36
+ /** Light grey italic captions **/
37
+ em.p3-em {
38
+ color: #999999;
39
+ }
40
+
41
+
42
+ /** Auto scanner frame **/
43
+ #p3-scan-frame {
44
+ border-width: 0px;
45
+ height: 500px;
46
+ width: 100%;
47
+ }
48
+
49
+
50
+ /** jQuery UI Dialog overrides **/
51
+ .noPadding.ui-dialog .ui-dialog-content {
52
+ padding: 0;
53
+ }
54
+ .noTitle .ui-dialog-titlebar {
55
+ display: none;
56
+ }
57
+ .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
58
+ float: none;
59
+ text-align: center;
60
+ }
61
+ .p3-dialog {
62
+ display: none;
63
+ }
64
+
65
+
66
+
67
+ /** Turn "Cancel" button into link **/
68
+ button.p3-cancel-button{
69
+ border-width: 0px !important;
70
+ background-image: none !important;
71
+ background-color: transparent !important;
72
+ }
73
+ .ui-dialog .ui-dialog-buttonpane button.p3-cancel-button span {
74
+ text-decoration: underline;
75
+ padding-left: 0;
76
+ padding-right: 0;
77
+ margin-left: 1px;
78
+ margin-right: 0;
79
+ color: #21759b;
80
+ }
81
+ #p3-manual-scan-cancel {
82
+ color: #21759b;
83
+ }
84
+
85
+
86
+ /** Captions for the scan "This will take several minutes ..." **/
87
+ #p3-scan-caption {
88
+ margin: 8px auto 0 auto;
89
+ width: 85%;
90
+ }
91
+
92
+
93
+ /** Auto scanner display status **/
94
+ #p3-scanning-caption {
95
+ text-overflow: ellipsis;
96
+ white-space: nowrap;
97
+ -moz-binding: url('wordwrap.xml#wordwrap');
98
+ word-wrap: break-word;
99
+ overflow: hidden;
100
+ width: 100%;
101
+ padding-left: 4px;
102
+ height: 18px;
103
+ color: #999999;
104
+ font-style: italic;
105
+ }
106
+
107
+
108
+ /** Progress bar **/
109
+ #p3-progress {
110
+ margin: 5px auto 5px auto;
111
+ }
112
+
113
+
114
+ /** Charts and graphs **/
115
+ div.p3-plugin-graph {
116
+ margin: 10px auto 20px auto;
117
+ width: 620px;
118
+ height: 320px;
119
+ }
120
+ div.p3-graph-holder {
121
+ width: 500px;
122
+ height: 300px;
123
+ margin: auto;
124
+ }
125
+ div.p3-custom-legend {
126
+ float: left;
127
+ width: 120px;
128
+ height: 280px;
129
+ overflow: auto;
130
+ }
131
+ #p3-tabs h2 {
132
+ margin: auto;
133
+ text-align: center;
134
+ }
135
+ div.p3-plugin-graph td {
136
+ vertical-align: top;
137
+ }
138
+ div.p3-plugin-graph td h3{
139
+ text-align: center;
140
+ margin: auto;
141
+ }
142
+ div.p3-plugin-graph div.yAxis div.tickLabel {
143
+ margin-right: 25px;
144
+ margin-top: -10px;
145
+ }
146
+
147
+
148
+ /** Advanced metrics table and glossary table **/
149
+ table.p3-results-table td {
150
+ background-color: #ffffff;
151
+ padding: 6px;
152
+ }
153
+ table.p3-results-table td {
154
+ border-style: solid;
155
+ border-color: #eaeaea;
156
+ border-width: 0px 1px 1px 1px;
157
+ vertical-align: top;
158
+ }
159
+ table.p3-results-table th:last-child {
160
+ border-left-width: 0px;
161
+ }
162
+ table.p3-results-table tr.even td {
163
+ background-color: #fcfcfc;
164
+ }
165
+ table.p3-results-table {
166
+ width: 100%;
167
+ border-collapse: collapse;
168
+ margin: 0 auto 20px auto;
169
+ }
170
+ p3-metrics-header {
171
+ width: 100%;
172
+ }
173
+ p3-metrics-container {
174
+ min-width: 650px;
175
+ max-width: 800px;
176
+ width: 80%;
177
+ }
178
+ p3-glossary-header {
179
+ width: 100%;
180
+ }
181
+ p3-glossary-container {
182
+ min-width: 650px;
183
+ max-width: 800px;
184
+ width: 80%;
185
+ }
186
+
187
+
188
+
189
+ /** Glossary **/
190
+ #p3-glossary-term-display {
191
+ vertical-align: text-top;
192
+ border-width: 1px !important;
193
+ font-size: 14px;
194
+ }
195
+ #p3-glossary-table td.term {
196
+ border-width: 1px !important;
197
+ border-color: transparent !important;
198
+ cursor: pointer;
199
+ }
200
+ #p3-glossary-table td.term.hover {
201
+ background-color: #206d92;
202
+ border-width: 1px 0px 1px 1px !important;
203
+ border-color: #298cba !important;
204
+ background-image: url(custom-theme/images/ui-bg_highlight-hard_100_206d92_1x100.png);
205
+ background-position: 50% 50%;
206
+ background-repeat: repeat-x;
207
+ color: white;
208
+ }
209
+
210
+ /** Quick report table **/
211
+ #p3-quick-report {
212
+ width: 100%;
213
+ }
214
+ #p3-quick-report td {
215
+ width: 20%;
216
+ vertical-align: top;
217
+ padding: 0px;
218
+ }
219
+ #p3-quick-report td.p3-callout {
220
+ text-align: center;
221
+ }
222
+ #p3-quick-report div.p3-callout-outer-wrapper {
223
+ border: 1px solid #cccccc;
224
+ margin: 10px auto;
225
+ width: 140px;
226
+ }
227
+ #p3-quick-report div.p3-callout-inner-wrapper {
228
+ width: 140px;
229
+ height: 92px;
230
+ }
231
+ #p3-quick-report td div.p3-callout-caption {
232
+ text-align: center;
233
+ position: relative;
234
+ }
235
+ #p3-quick-report td div.p3-callout-caption:first-child {
236
+ top: 5px;
237
+ }
238
+ #p3-quick-report td div.p3-callout-caption:last-child {
239
+ top: -6px;
240
+ }
241
+ #p3-quick-report div.p3-callout-data {
242
+ font-weight: bold;
243
+ text-align: center;
244
+ font-size: 40px;
245
+ position: relative;
246
+ top: 22px;
247
+ height: 60px;
248
+ }
249
+ .p3-faded-grey {
250
+ color: #dddddd;
251
+ }
252
+
253
+
254
+
255
+ /** Enter your IP dialog **/
256
+ #p3-ip-dialog {
257
+ padding: 15px;
258
+ }
259
+
260
+
261
+ /** Tabs shim **/
262
+ #p3-tabs ul li:first-child {
263
+ margin-left: 4px;
264
+ }
265
+
266
+
267
+ /** Copyright notice **/
268
+ #p3-copyright {
269
+ text-align: center;
270
+ margin: 45px auto 5px auto;
271
+ }
272
+ #p3-copyright img {
273
+ margin-bottom: 7px;
274
+ height: 47px;
275
+ width: 125px;
276
+ }
277
+
278
+
279
+ /** Rotated y-axis label **/
280
+ div.p3-y-axis-label {
281
+
282
+ position: relative;
283
+ top: 135px;
284
+ left: -20px;
285
+
286
+ /* Safari */
287
+ -webkit-transform: rotate(-90deg);
288
+
289
+ /* Firefox */
290
+ -moz-transform: rotate(-90deg);
291
+
292
+ /* Opera */
293
+ -o-transform: rotate(-90deg);
294
+
295
+ /* Internet Explorer */
296
+ filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
297
+ }
298
+
299
+
300
+ /** X axis label **/
301
+ div.p3-x-axis-label {
302
+ position: relative;
303
+ top: 20px;
304
+ left: 235px;
305
+ }
306
+
307
+
308
+ /** Jack up the font size on the tooltips **/
309
+ .ui-tooltip {
310
+ font-size: 120%;
311
+ line-height: 125%;
312
+ }
313
+
314
+
315
+ /** Email these results button **/
316
+ #p3-email-results {
317
+ margin: 20px auto 30px auto;
318
+ }
319
+ #p3-email-results a {
320
+ display: inline-block;
321
+ position: relative;
322
+ top: 1px;
323
+ left: 4px;
324
+ }
325
+ #p3-email-sending-dialog img {
326
+ left: 132px;
327
+ top: 40px;
328
+ position: relative;
329
+ }
330
+ #p3-email-sending-close {
331
+ margin: 20px auto;
332
+ text-align: center;
333
+ }
334
+
335
+
336
+ /** Help page questions **/
337
+ div.p3-question {
338
+ border: 1px solid #cccccc;
339
+ margin-bottom: 10px;
340
+ padding: 8px;
341
+ }
342
+ div.p3-question h2.p3-help-question {
343
+ margin-top: -15px;
344
+ border-bottom: 1px solid #cccccc;
345
+ }
css/wordwrap.xml ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version = "1.0"?>
2
+ <bindings xmlns = "http://www.mozilla.org/xbl" xmlns:html = "http://www.w3.org/1999/xhtml">
3
+ <binding id = "wordwrap" applyauthorstyles = "false">
4
+ <implementation>
5
+ <constructor>
6
+ //<![CDATA[
7
+ var elem = this;
8
+ doWrap();
9
+ elem.addEventListener('overflow', doWrap, false);
10
+ function doWrap() {
11
+ var walker = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT, null, false);
12
+ while (walker.nextNode()) {
13
+ var node = walker.currentNode;
14
+ node.nodeValue = node.nodeValue.split('').join(String.fromCharCode('8203'));
15
+ }
16
+ elem.innerHTML = elem.innerHTML.replace(/^\s+|\s+$/g,"");
17
+ }
18
+ //]]>
19
+ </constructor>
20
+ </implementation>
21
+ </binding>
22
+ </bindings>
index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php header( 'Status: 404 Not found' ); ?>
2
+ Not found
js/jquery.corner.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery corner plugin: simple corner rounding
3
+ * Examples and documentation at: http://jquery.malsup.com/corner/
4
+ * version 2.12 (23-MAY-2011)
5
+ * Requires jQuery v1.3.2 or later
6
+ * Dual licensed under the MIT and GPL licenses:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.gnu.org/licenses/gpl.html
9
+ * Authors: Dave Methvin and Mike Alsup
10
+ */
11
+
12
+ /**
13
+ * corner() takes a single string argument: $('#myDiv').corner("effect corners width")
14
+ *
15
+ * effect: name of the effect to apply, such as round, bevel, notch, bite, etc (default is round).
16
+ * corners: one or more of: top, bottom, tr, tl, br, or bl. (default is all corners)
17
+ * width: width of the effect; in the case of rounded corners this is the radius.
18
+ * specify this value using the px suffix such as 10px (yes, it must be pixels).
19
+ */
20
+ ;(function($){var style=document.createElement('div').style,moz=style['MozBorderRadius']!==undefined,webkit=style['WebkitBorderRadius']!==undefined,radius=style['borderRadius']!==undefined||style['BorderRadius']!==undefined,mode=document.documentMode||0,noBottomFold=$.browser.msie&&(($.browser.version<8&&!mode)||mode<8),expr=$.browser.msie&&(function(){var div=document.createElement('div');try{div.style.setExpression('width','0+0');div.style.removeExpression('width');}catch(e){return false;}return true;})();$.support=$.support||{};$.support.borderRadius=moz||webkit||radius;function sz(el,p){return parseInt($.css(el,p))||0;};function hex2(s){s=parseInt(s).toString(16);return(s.length<2)?'0'+s:s;};function gpc(node){while(node){var v=$.css(node,'backgroundColor'),rgb;if(v&&v!='transparent'&&v!='rgba(0, 0, 0, 0)'){if(v.indexOf('rgb')>=0){rgb=v.match(/\d+/g);return'#'+hex2(rgb[0])+hex2(rgb[1])+hex2(rgb[2]);}return v;}if(node.nodeName.toLowerCase()=='html')break;node=node.parentNode;}return'#ffffff';};function getWidth(fx,i,width){switch(fx){case'round':return Math.round(width*(1-Math.cos(Math.asin(i/width))));case'cool':return Math.round(width*(1+Math.cos(Math.asin(i/width))));case'sharp':return width-i;case'bite':return Math.round(width*(Math.cos(Math.asin((width-i-1)/width))));case'slide':return Math.round(width*(Math.atan2(i,width/i)));case'jut':return Math.round(width*(Math.atan2(width,(width-i-1))));case'curl':return Math.round(width*(Math.atan(i)));case'tear':return Math.round(width*(Math.cos(i)));case'wicked':return Math.round(width*(Math.tan(i)));case'long':return Math.round(width*(Math.sqrt(i)));case'sculpt':return Math.round(width*(Math.log((width-i-1),width)));case'dogfold':case'dog':return(i&1)?(i+1):width;case'dog2':return(i&2)?(i+1):width;case'dog3':return(i&3)?(i+1):width;case'fray':return(i%2)*width;case'notch':return width;case'bevelfold':case'bevel':return i+1;case'steep':return i/2+1;case'invsteep':return(width-i)/2+1;}};$.fn.corner=function(options){if(this.length==0){if(!$.isReady&&this.selector){var s=this.selector,c=this.context;$(function(){$(s,c).corner(options);});}return this;}return this.each(function(index){var $this=$(this),o=[$this.attr($.fn.corner.defaults.metaAttr)||'',options||''].join(' ').toLowerCase(),keep=/keep/.test(o),cc=((o.match(/cc:(#[0-9a-f]+)/)||[])[1]),sc=((o.match(/sc:(#[0-9a-f]+)/)||[])[1]),width=parseInt((o.match(/(\d+)px/)||[])[1])||10,re=/round|bevelfold|bevel|notch|bite|cool|sharp|slide|jut|curl|tear|fray|wicked|sculpt|long|dog3|dog2|dogfold|dog|invsteep|steep/,fx=((o.match(re)||['round'])[0]),fold=/dogfold|bevelfold/.test(o),edges={T:0,B:1},opts={TL:/top|tl|left/.test(o),TR:/top|tr|right/.test(o),BL:/bottom|bl|left/.test(o),BR:/bottom|br|right/.test(o)},strip,pad,cssHeight,j,bot,d,ds,bw,i,w,e,c,common,$horz;if(!opts.TL&&!opts.TR&&!opts.BL&&!opts.BR)opts={TL:1,TR:1,BL:1,BR:1};if($.fn.corner.defaults.useNative&&fx=='round'&&(radius||moz||webkit)&&!cc&&!sc){if(opts.TL)$this.css(radius?'border-top-left-radius':moz?'-moz-border-radius-topleft':'-webkit-border-top-left-radius',width+'px');if(opts.TR)$this.css(radius?'border-top-right-radius':moz?'-moz-border-radius-topright':'-webkit-border-top-right-radius',width+'px');if(opts.BL)$this.css(radius?'border-bottom-left-radius':moz?'-moz-border-radius-bottomleft':'-webkit-border-bottom-left-radius',width+'px');if(opts.BR)$this.css(radius?'border-bottom-right-radius':moz?'-moz-border-radius-bottomright':'-webkit-border-bottom-right-radius',width+'px');return;}strip=document.createElement('div');$(strip).css({overflow:'hidden',height:'1px',minHeight:'1px',fontSize:'1px',backgroundColor:sc||'transparent',borderStyle:'solid'});pad={T:parseInt($.css(this,'paddingTop'))||0,R:parseInt($.css(this,'paddingRight'))||0,B:parseInt($.css(this,'paddingBottom'))||0,L:parseInt($.css(this,'paddingLeft'))||0};if(typeof this.style.zoom!=undefined)this.style.zoom=1;if(!keep)this.style.border='none';strip.style.borderColor=cc||gpc(this.parentNode);cssHeight=$(this).outerHeight();for(j in edges){bot=edges[j];if((bot&&(opts.BL||opts.BR))||(!bot&&(opts.TL||opts.TR))){strip.style.borderStyle='none '+(opts[j+'R']?'solid':'none')+' none '+(opts[j+'L']?'solid':'none');d=document.createElement('div');$(d).addClass('jquery-corner');ds=d.style;bot?this.appendChild(d):this.insertBefore(d,this.firstChild);if(bot&&cssHeight!='auto'){if($.css(this,'position')=='static')this.style.position='relative';ds.position='absolute';ds.bottom=ds.left=ds.padding=ds.margin='0';if(expr)ds.setExpression('width','this.parentNode.offsetWidth');else
21
+ ds.width='100%';}else if(!bot&&$.browser.msie){if($.css(this,'position')=='static')this.style.position='relative';ds.position='absolute';ds.top=ds.left=ds.right=ds.padding=ds.margin='0';if(expr){bw=sz(this,'borderLeftWidth')+sz(this,'borderRightWidth');ds.setExpression('width','this.parentNode.offsetWidth - '+bw+'+ "px"');}else
22
+ ds.width='100%';}else{ds.position='relative';ds.margin=!bot?'-'+pad.T+'px -'+pad.R+'px '+(pad.T-width)+'px -'+pad.L+'px':(pad.B-width)+'px -'+pad.R+'px -'+pad.B+'px -'+pad.L+'px';}for(i=0;i<width;i++){w=Math.max(0,getWidth(fx,i,width));e=strip.cloneNode(false);e.style.borderWidth='0 '+(opts[j+'R']?w:0)+'px 0 '+(opts[j+'L']?w:0)+'px';bot?d.appendChild(e):d.insertBefore(e,d.firstChild);}if(fold&&$.support.boxModel){if(bot&&noBottomFold)continue;for(c in opts){if(!opts[c])continue;if(bot&&(c=='TL'||c=='TR'))continue;if(!bot&&(c=='BL'||c=='BR'))continue;common={position:'absolute',border:'none',margin:0,padding:0,overflow:'hidden',backgroundColor:strip.style.borderColor};$horz=$('<div/>').css(common).css({width:width+'px',height:'1px'});switch(c){case'TL':$horz.css({bottom:0,left:0});break;case'TR':$horz.css({bottom:0,right:0});break;case'BL':$horz.css({top:0,left:0});break;case'BR':$horz.css({top:0,right:0});break;}d.appendChild($horz[0]);var $vert=$('<div/>').css(common).css({top:0,bottom:0,width:'1px',height:width+'px'});switch(c){case'TL':$vert.css({left:width});break;case'TR':$vert.css({right:width});break;case'BL':$vert.css({left:width});break;case'BR':$vert.css({right:width});break;}d.appendChild($vert[0]);}}}}});};$.fn.uncorner=function(){if(radius||moz||webkit)this.css(radius?'border-radius':moz?'-moz-border-radius':'-webkit-border-radius',0);$('div.jquery-corner',this).remove();return this;};$.fn.corner.defaults={useNative:true,metaAttr:'data-corner'};})(jQuery);
js/jquery.flot.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /*
2
+ Javascript plotting library for jQuery ~ v0.7 ~ Copyright (c) 2007-2011 IOLA and Ole Laursen
3
+ Licensed under the MIT License ~ https://raw.github.com/flot/flot/master/LICENSE.txt
4
+ */
5
+ (function(b){b.color={};b.color.make=function(d,e,g,f){var c={};c.r=d||0;c.g=e||0;c.b=g||0;c.a=f!=null?f:1;c.add=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]+=j}return c.normalize()};c.scale=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]*=j}return c.normalize()};c.toString=function(){if(c.a>=1){return"rgb("+[c.r,c.g,c.b].join(",")+")"}else{return"rgba("+[c.r,c.g,c.b,c.a].join(",")+")"}};c.normalize=function(){function h(k,j,l){return j<k?k:(j>l?l:j)}c.r=h(0,parseInt(c.r),255);c.g=h(0,parseInt(c.g),255);c.b=h(0,parseInt(c.b),255);c.a=h(0,c.a,1);return c};c.clone=function(){return b.color.make(c.r,c.b,c.g,c.a)};return c.normalize()};b.color.extract=function(d,e){var c;do{c=d.css(e).toLowerCase();if(c!=""&&c!="transparent"){break}d=d.parent()}while(!b.nodeName(d.get(0),"body"));if(c=="rgba(0, 0, 0, 0)"){c="transparent"}return b.color.parse(c)};b.color.parse=function(c){var d,f=b.color.make;if(d=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10))}if(d=/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(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10),parseFloat(d[4]))}if(d=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55)}if(d=/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(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55,parseFloat(d[4]))}if(d=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)){return f(parseInt(d[1],16),parseInt(d[2],16),parseInt(d[3],16))}if(d=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)){return f(parseInt(d[1]+d[1],16),parseInt(d[2]+d[2],16),parseInt(d[3]+d[3],16))}var e=b.trim(c).toLowerCase();if(e=="transparent"){return f(255,255,255,0)}else{d=a[e]||[0,0,0];return f(d[0],d[1],d[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);(function(c){function b(av,ai,J,af){var Q=[],O={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{show:null,position:"bottom",mode:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},az=null,ad=null,y=null,H=null,A=null,p=[],aw=[],q={left:0,right:0,top:0,bottom:0},G=0,I=0,h=0,w=0,ak={processOptions:[],processRawData:[],processDatapoints:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},aq=this;aq.setData=aj;aq.setupGrid=t;aq.draw=W;aq.getPlaceholder=function(){return av};aq.getCanvas=function(){return az};aq.getPlotOffset=function(){return q};aq.width=function(){return h};aq.height=function(){return w};aq.offset=function(){var aB=y.offset();aB.left+=q.left;aB.top+=q.top;return aB};aq.getData=function(){return Q};aq.getAxes=function(){var aC={},aB;c.each(p.concat(aw),function(aD,aE){if(aE){aC[aE.direction+(aE.n!=1?aE.n:"")+"axis"]=aE}});return aC};aq.getXAxes=function(){return p};aq.getYAxes=function(){return aw};aq.c2p=C;aq.p2c=ar;aq.getOptions=function(){return O};aq.highlight=x;aq.unhighlight=T;aq.triggerRedrawOverlay=f;aq.pointOffset=function(aB){return{left:parseInt(p[aA(aB,"x")-1].p2c(+aB.x)+q.left),top:parseInt(aw[aA(aB,"y")-1].p2c(+aB.y)+q.top)}};aq.shutdown=ag;aq.resize=function(){B();g(az);g(ad)};aq.hooks=ak;F(aq);Z(J);X();aj(ai);t();W();ah();function an(aD,aB){aB=[aq].concat(aB);for(var aC=0;aC<aD.length;++aC){aD[aC].apply(this,aB)}}function F(){for(var aB=0;aB<af.length;++aB){var aC=af[aB];aC.init(aq);if(aC.options){c.extend(true,O,aC.options)}}}function Z(aC){var aB;c.extend(true,O,aC);if(O.xaxis.color==null){O.xaxis.color=O.grid.color}if(O.yaxis.color==null){O.yaxis.color=O.grid.color}if(O.xaxis.tickColor==null){O.xaxis.tickColor=O.grid.tickColor}if(O.yaxis.tickColor==null){O.yaxis.tickColor=O.grid.tickColor}if(O.grid.borderColor==null){O.grid.borderColor=O.grid.color}if(O.grid.tickColor==null){O.grid.tickColor=c.color.parse(O.grid.color).scale("a",0.22).toString()}for(aB=0;aB<Math.max(1,O.xaxes.length);++aB){O.xaxes[aB]=c.extend(true,{},O.xaxis,O.xaxes[aB])}for(aB=0;aB<Math.max(1,O.yaxes.length);++aB){O.yaxes[aB]=c.extend(true,{},O.yaxis,O.yaxes[aB])}if(O.xaxis.noTicks&&O.xaxis.ticks==null){O.xaxis.ticks=O.xaxis.noTicks}if(O.yaxis.noTicks&&O.yaxis.ticks==null){O.yaxis.ticks=O.yaxis.noTicks}if(O.x2axis){O.xaxes[1]=c.extend(true,{},O.xaxis,O.x2axis);O.xaxes[1].position="top"}if(O.y2axis){O.yaxes[1]=c.extend(true,{},O.yaxis,O.y2axis);O.yaxes[1].position="right"}if(O.grid.coloredAreas){O.grid.markings=O.grid.coloredAreas}if(O.grid.coloredAreasColor){O.grid.markingsColor=O.grid.coloredAreasColor}if(O.lines){c.extend(true,O.series.lines,O.lines)}if(O.points){c.extend(true,O.series.points,O.points)}if(O.bars){c.extend(true,O.series.bars,O.bars)}if(O.shadowSize!=null){O.series.shadowSize=O.shadowSize}for(aB=0;aB<O.xaxes.length;++aB){V(p,aB+1).options=O.xaxes[aB]}for(aB=0;aB<O.yaxes.length;++aB){V(aw,aB+1).options=O.yaxes[aB]}for(var aD in ak){if(O.hooks[aD]&&O.hooks[aD].length){ak[aD]=ak[aD].concat(O.hooks[aD])}}an(ak.processOptions,[O])}function aj(aB){Q=Y(aB);ax();z()}function Y(aE){var aC=[];for(var aB=0;aB<aE.length;++aB){var aD=c.extend(true,{},O.series);if(aE[aB].data!=null){aD.data=aE[aB].data;delete aE[aB].data;c.extend(true,aD,aE[aB]);aE[aB].data=aD.data}else{aD.data=aE[aB]}aC.push(aD)}return aC}function aA(aC,aD){var aB=aC[aD+"axis"];if(typeof aB=="object"){aB=aB.n}if(typeof aB!="number"){aB=1}return aB}function m(){return c.grep(p.concat(aw),function(aB){return aB})}function C(aE){var aC={},aB,aD;for(aB=0;aB<p.length;++aB){aD=p[aB];if(aD&&aD.used){aC["x"+aD.n]=aD.c2p(aE.left)}}for(aB=0;aB<aw.length;++aB){aD=aw[aB];if(aD&&aD.used){aC["y"+aD.n]=aD.c2p(aE.top)}}if(aC.x1!==undefined){aC.x=aC.x1}if(aC.y1!==undefined){aC.y=aC.y1}return aC}function ar(aF){var aD={},aC,aE,aB;for(aC=0;aC<p.length;++aC){aE=p[aC];if(aE&&aE.used){aB="x"+aE.n;if(aF[aB]==null&&aE.n==1){aB="x"}if(aF[aB]!=null){aD.left=aE.p2c(aF[aB]);break}}}for(aC=0;aC<aw.length;++aC){aE=aw[aC];if(aE&&aE.used){aB="y"+aE.n;if(aF[aB]==null&&aE.n==1){aB="y"}if(aF[aB]!=null){aD.top=aE.p2c(aF[aB]);break}}}return aD}function V(aC,aB){if(!aC[aB-1]){aC[aB-1]={n:aB,direction:aC==p?"x":"y",options:c.extend(true,{},aC==p?O.xaxis:O.yaxis)}}return aC[aB-1]}function ax(){var aG;var aM=Q.length,aB=[],aE=[];for(aG=0;aG<Q.length;++aG){var aJ=Q[aG].color;if(aJ!=null){--aM;if(typeof aJ=="number"){aE.push(aJ)}else{aB.push(c.color.parse(Q[aG].color))}}}for(aG=0;aG<aE.length;++aG){aM=Math.max(aM,aE[aG]+1)}var aC=[],aF=0;aG=0;while(aC.length<aM){var aI;if(O.colors.length==aG){aI=c.color.make(100,100,100)}else{aI=c.color.parse(O.colors[aG])}var aD=aF%2==1?-1:1;aI.scale("rgb",1+aD*Math.ceil(aF/2)*0.2);aC.push(aI);++aG;if(aG>=O.colors.length){aG=0;++aF}}var aH=0,aN;for(aG=0;aG<Q.length;++aG){aN=Q[aG];if(aN.color==null){aN.color=aC[aH].toString();++aH}else{if(typeof aN.color=="number"){aN.color=aC[aN.color].toString()}}if(aN.lines.show==null){var aL,aK=true;for(aL in aN){if(aN[aL]&&aN[aL].show){aK=false;break}}if(aK){aN.lines.show=true}}aN.xaxis=V(p,aA(aN,"x"));aN.yaxis=V(aw,aA(aN,"y"))}}function z(){var aO=Number.POSITIVE_INFINITY,aI=Number.NEGATIVE_INFINITY,aB=Number.MAX_VALUE,aU,aS,aR,aN,aD,aJ,aT,aP,aH,aG,aC,a0,aX,aL;function aF(a3,a2,a1){if(a2<a3.datamin&&a2!=-aB){a3.datamin=a2}if(a1>a3.datamax&&a1!=aB){a3.datamax=a1}}c.each(m(),function(a1,a2){a2.datamin=aO;a2.datamax=aI;a2.used=false});for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aJ.datapoints={points:[]};an(ak.processRawData,[aJ,aJ.data,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];var aZ=aJ.data,aW=aJ.datapoints.format;if(!aW){aW=[];aW.push({x:true,number:true,required:true});aW.push({y:true,number:true,required:true});if(aJ.bars.show||(aJ.lines.show&&aJ.lines.fill)){aW.push({y:true,number:true,required:false,defaultValue:0});if(aJ.bars.horizontal){delete aW[aW.length-1].y;aW[aW.length-1].x=true}}aJ.datapoints.format=aW}if(aJ.datapoints.pointsize!=null){continue}aJ.datapoints.pointsize=aW.length;aP=aJ.datapoints.pointsize;aT=aJ.datapoints.points;insertSteps=aJ.lines.show&&aJ.lines.steps;aJ.xaxis.used=aJ.yaxis.used=true;for(aS=aR=0;aS<aZ.length;++aS,aR+=aP){aL=aZ[aS];var aE=aL==null;if(!aE){for(aN=0;aN<aP;++aN){a0=aL[aN];aX=aW[aN];if(aX){if(aX.number&&a0!=null){a0=+a0;if(isNaN(a0)){a0=null}else{if(a0==Infinity){a0=aB}else{if(a0==-Infinity){a0=-aB}}}}if(a0==null){if(aX.required){aE=true}if(aX.defaultValue!=null){a0=aX.defaultValue}}}aT[aR+aN]=a0}}if(aE){for(aN=0;aN<aP;++aN){a0=aT[aR+aN];if(a0!=null){aX=aW[aN];if(aX.x){aF(aJ.xaxis,a0,a0)}if(aX.y){aF(aJ.yaxis,a0,a0)}}aT[aR+aN]=null}}else{if(insertSteps&&aR>0&&aT[aR-aP]!=null&&aT[aR-aP]!=aT[aR]&&aT[aR-aP+1]!=aT[aR+1]){for(aN=0;aN<aP;++aN){aT[aR+aP+aN]=aT[aR+aN]}aT[aR+1]=aT[aR-aP+1];aR+=aP}}}}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];an(ak.processDatapoints,[aJ,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aT=aJ.datapoints.points,aP=aJ.datapoints.pointsize;var aK=aO,aQ=aO,aM=aI,aV=aI;for(aS=0;aS<aT.length;aS+=aP){if(aT[aS]==null){continue}for(aN=0;aN<aP;++aN){a0=aT[aS+aN];aX=aW[aN];if(!aX||a0==aB||a0==-aB){continue}if(aX.x){if(a0<aK){aK=a0}if(a0>aM){aM=a0}}if(aX.y){if(a0<aQ){aQ=a0}if(a0>aV){aV=a0}}}}if(aJ.bars.show){var aY=aJ.bars.align=="left"?0:-aJ.bars.barWidth/2;if(aJ.bars.horizontal){aQ+=aY;aV+=aY+aJ.bars.barWidth}else{aK+=aY;aM+=aY+aJ.bars.barWidth}}aF(aJ.xaxis,aK,aM);aF(aJ.yaxis,aQ,aV)}c.each(m(),function(a1,a2){if(a2.datamin==aO){a2.datamin=null}if(a2.datamax==aI){a2.datamax=null}})}function j(aB,aC){var aD=document.createElement("canvas");aD.className=aC;aD.width=G;aD.height=I;if(!aB){c(aD).css({position:"absolute",left:0,top:0})}c(aD).appendTo(av);if(!aD.getContext){aD=window.G_vmlCanvasManager.initElement(aD)}aD.getContext("2d").save();return aD}function B(){G=av.width();I=av.height();if(G<=0||I<=0){throw"Invalid dimensions for plot, width = "+G+", height = "+I}}function g(aC){if(aC.width!=G){aC.width=G}if(aC.height!=I){aC.height=I}var aB=aC.getContext("2d");aB.restore();aB.save()}function X(){var aC,aB=av.children("canvas.base"),aD=av.children("canvas.overlay");if(aB.length==0||aD==0){av.html("");av.css({padding:0});if(av.css("position")=="static"){av.css("position","relative")}B();az=j(true,"base");ad=j(false,"overlay");aC=false}else{az=aB.get(0);ad=aD.get(0);aC=true}H=az.getContext("2d");A=ad.getContext("2d");y=c([ad,az]);if(aC){av.data("plot").shutdown();aq.resize();A.clearRect(0,0,G,I);y.unbind();av.children().not([az,ad]).remove()}av.data("plot",aq)}function ah(){if(O.grid.hoverable){y.mousemove(aa);y.mouseleave(l)}if(O.grid.clickable){y.click(R)}an(ak.bindEvents,[y])}function ag(){if(M){clearTimeout(M)}y.unbind("mousemove",aa);y.unbind("mouseleave",l);y.unbind("click",R);an(ak.shutdown,[y])}function r(aG){function aC(aH){return aH}var aF,aB,aD=aG.options.transform||aC,aE=aG.options.inverseTransform;if(aG.direction=="x"){aF=aG.scale=h/Math.abs(aD(aG.max)-aD(aG.min));aB=Math.min(aD(aG.max),aD(aG.min))}else{aF=aG.scale=w/Math.abs(aD(aG.max)-aD(aG.min));aF=-aF;aB=Math.max(aD(aG.max),aD(aG.min))}if(aD==aC){aG.p2c=function(aH){return(aH-aB)*aF}}else{aG.p2c=function(aH){return(aD(aH)-aB)*aF}}if(!aE){aG.c2p=function(aH){return aB+aH/aF}}else{aG.c2p=function(aH){return aE(aB+aH/aF)}}}function L(aD){var aB=aD.options,aF,aJ=aD.ticks||[],aI=[],aE,aK=aB.labelWidth,aG=aB.labelHeight,aC;function aH(aM,aL){return c('<div style="position:absolute;top:-10000px;'+aL+'font-size:smaller"><div class="'+aD.direction+"Axis "+aD.direction+aD.n+'Axis">'+aM.join("")+"</div></div>").appendTo(av)}if(aD.direction=="x"){if(aK==null){aK=Math.floor(G/(aJ.length>0?aJ.length:1))}if(aG==null){aI=[];for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel" style="float:left;width:'+aK+'px">'+aE+"</div>")}}if(aI.length>0){aI.push('<div style="clear:left"></div>');aC=aH(aI,"width:10000px;");aG=aC.height();aC.remove()}}}else{if(aK==null||aG==null){for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel">'+aE+"</div>")}}if(aI.length>0){aC=aH(aI,"");if(aK==null){aK=aC.children().width()}if(aG==null){aG=aC.find("div.tickLabel").height()}aC.remove()}}}if(aK==null){aK=0}if(aG==null){aG=0}aD.labelWidth=aK;aD.labelHeight=aG}function au(aD){var aC=aD.labelWidth,aL=aD.labelHeight,aH=aD.options.position,aF=aD.options.tickLength,aG=O.grid.axisMargin,aJ=O.grid.labelMargin,aK=aD.direction=="x"?p:aw,aE;var aB=c.grep(aK,function(aN){return aN&&aN.options.position==aH&&aN.reserveSpace});if(c.inArray(aD,aB)==aB.length-1){aG=0}if(aF==null){aF="full"}var aI=c.grep(aK,function(aN){return aN&&aN.reserveSpace});var aM=c.inArray(aD,aI)==0;if(!aM&&aF=="full"){aF=5}if(!isNaN(+aF)){aJ+=+aF}if(aD.direction=="x"){aL+=aJ;if(aH=="bottom"){q.bottom+=aL+aG;aD.box={top:I-q.bottom,height:aL}}else{aD.box={top:q.top+aG,height:aL};q.top+=aL+aG}}else{aC+=aJ;if(aH=="left"){aD.box={left:q.left+aG,width:aC};q.left+=aC+aG}else{q.right+=aC+aG;aD.box={left:G-q.right,width:aC}}}aD.position=aH;aD.tickLength=aF;aD.box.padding=aJ;aD.innermost=aM}function U(aB){if(aB.direction=="x"){aB.box.left=q.left;aB.box.width=h}else{aB.box.top=q.top;aB.box.height=w}}function t(){var aC,aE=m();c.each(aE,function(aF,aG){aG.show=aG.options.show;if(aG.show==null){aG.show=aG.used}aG.reserveSpace=aG.show||aG.options.reserveSpace;n(aG)});allocatedAxes=c.grep(aE,function(aF){return aF.reserveSpace});q.left=q.right=q.top=q.bottom=0;if(O.grid.show){c.each(allocatedAxes,function(aF,aG){S(aG);P(aG);ap(aG,aG.ticks);L(aG)});for(aC=allocatedAxes.length-1;aC>=0;--aC){au(allocatedAxes[aC])}var aD=O.grid.minBorderMargin;if(aD==null){aD=0;for(aC=0;aC<Q.length;++aC){aD=Math.max(aD,Q[aC].points.radius+Q[aC].points.lineWidth/2)}}for(var aB in q){q[aB]+=O.grid.borderWidth;q[aB]=Math.max(aD,q[aB])}}h=G-q.left-q.right;w=I-q.bottom-q.top;c.each(aE,function(aF,aG){r(aG)});if(O.grid.show){c.each(allocatedAxes,function(aF,aG){U(aG)});k()}o()}function n(aE){var aF=aE.options,aD=+(aF.min!=null?aF.min:aE.datamin),aB=+(aF.max!=null?aF.max:aE.datamax),aH=aB-aD;if(aH==0){var aC=aB==0?1:0.01;if(aF.min==null){aD-=aC}if(aF.max==null||aF.min!=null){aB+=aC}}else{var aG=aF.autoscaleMargin;if(aG!=null){if(aF.min==null){aD-=aH*aG;if(aD<0&&aE.datamin!=null&&aE.datamin>=0){aD=0}}if(aF.max==null){aB+=aH*aG;if(aB>0&&aE.datamax!=null&&aE.datamax<=0){aB=0}}}}aE.min=aD;aE.max=aB}function S(aG){var aM=aG.options;var aH;if(typeof aM.ticks=="number"&&aM.ticks>0){aH=aM.ticks}else{aH=0.3*Math.sqrt(aG.direction=="x"?G:I)}var aT=(aG.max-aG.min)/aH,aO,aB,aN,aR,aS,aQ,aI;if(aM.mode=="time"){var aJ={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var aK=[[1,"second"],[2,"second"],[5,"second"],[10,"second"],[30,"second"],[1,"minute"],[2,"minute"],[5,"minute"],[10,"minute"],[30,"minute"],[1,"hour"],[2,"hour"],[4,"hour"],[8,"hour"],[12,"hour"],[1,"day"],[2,"day"],[3,"day"],[0.25,"month"],[0.5,"month"],[1,"month"],[2,"month"],[3,"month"],[6,"month"],[1,"year"]];var aC=0;if(aM.minTickSize!=null){if(typeof aM.tickSize=="number"){aC=aM.tickSize}else{aC=aM.minTickSize[0]*aJ[aM.minTickSize[1]]}}for(var aS=0;aS<aK.length-1;++aS){if(aT<(aK[aS][0]*aJ[aK[aS][1]]+aK[aS+1][0]*aJ[aK[aS+1][1]])/2&&aK[aS][0]*aJ[aK[aS][1]]>=aC){break}}aO=aK[aS][0];aN=aK[aS][1];if(aN=="year"){aQ=Math.pow(10,Math.floor(Math.log(aT/aJ.year)/Math.LN10));aI=(aT/aJ.year)/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ}aG.tickSize=aM.tickSize||[aO,aN];aB=function(aX){var a2=[],a0=aX.tickSize[0],a3=aX.tickSize[1],a1=new Date(aX.min);var aW=a0*aJ[a3];if(a3=="second"){a1.setUTCSeconds(a(a1.getUTCSeconds(),a0))}if(a3=="minute"){a1.setUTCMinutes(a(a1.getUTCMinutes(),a0))}if(a3=="hour"){a1.setUTCHours(a(a1.getUTCHours(),a0))}if(a3=="month"){a1.setUTCMonth(a(a1.getUTCMonth(),a0))}if(a3=="year"){a1.setUTCFullYear(a(a1.getUTCFullYear(),a0))}a1.setUTCMilliseconds(0);if(aW>=aJ.minute){a1.setUTCSeconds(0)}if(aW>=aJ.hour){a1.setUTCMinutes(0)}if(aW>=aJ.day){a1.setUTCHours(0)}if(aW>=aJ.day*4){a1.setUTCDate(1)}if(aW>=aJ.year){a1.setUTCMonth(0)}var a5=0,a4=Number.NaN,aY;do{aY=a4;a4=a1.getTime();a2.push(a4);if(a3=="month"){if(a0<1){a1.setUTCDate(1);var aV=a1.getTime();a1.setUTCMonth(a1.getUTCMonth()+1);var aZ=a1.getTime();a1.setTime(a4+a5*aJ.hour+(aZ-aV)*a0);a5=a1.getUTCHours();a1.setUTCHours(0)}else{a1.setUTCMonth(a1.getUTCMonth()+a0)}}else{if(a3=="year"){a1.setUTCFullYear(a1.getUTCFullYear()+a0)}else{a1.setTime(a4+aW)}}}while(a4<aX.max&&a4!=aY);return a2};aR=function(aV,aY){var a0=new Date(aV);if(aM.timeformat!=null){return c.plot.formatDate(a0,aM.timeformat,aM.monthNames)}var aW=aY.tickSize[0]*aJ[aY.tickSize[1]];var aX=aY.max-aY.min;var aZ=(aM.twelveHourClock)?" %p":"";if(aW<aJ.minute){fmt="%h:%M:%S"+aZ}else{if(aW<aJ.day){if(aX<2*aJ.day){fmt="%h:%M"+aZ}else{fmt="%b %d %h:%M"+aZ}}else{if(aW<aJ.month){fmt="%b %d"}else{if(aW<aJ.year){if(aX<aJ.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return c.plot.formatDate(a0,fmt,aM.monthNames)}}else{var aU=aM.tickDecimals;var aP=-Math.floor(Math.log(aT)/Math.LN10);if(aU!=null&&aP>aU){aP=aU}aQ=Math.pow(10,-aP);aI=aT/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2;if(aI>2.25&&(aU==null||aP+1<=aU)){aO=2.5;++aP}}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ;if(aM.minTickSize!=null&&aO<aM.minTickSize){aO=aM.minTickSize}aG.tickDecimals=Math.max(0,aU!=null?aU:aP);aG.tickSize=aM.tickSize||aO;aB=function(aX){var aZ=[];var a0=a(aX.min,aX.tickSize),aW=0,aV=Number.NaN,aY;do{aY=aV;aV=a0+aW*aX.tickSize;aZ.push(aV);++aW}while(aV<aX.max&&aV!=aY);return aZ};aR=function(aV,aW){return aV.toFixed(aW.tickDecimals)}}if(aM.alignTicksWithAxis!=null){var aF=(aG.direction=="x"?p:aw)[aM.alignTicksWithAxis-1];if(aF&&aF.used&&aF!=aG){var aL=aB(aG);if(aL.length>0){if(aM.min==null){aG.min=Math.min(aG.min,aL[0])}if(aM.max==null&&aL.length>1){aG.max=Math.max(aG.max,aL[aL.length-1])}}aB=function(aX){var aY=[],aV,aW;for(aW=0;aW<aF.ticks.length;++aW){aV=(aF.ticks[aW].v-aF.min)/(aF.max-aF.min);aV=aX.min+aV*(aX.max-aX.min);aY.push(aV)}return aY};if(aG.mode!="time"&&aM.tickDecimals==null){var aE=Math.max(0,-Math.floor(Math.log(aT)/Math.LN10)+1),aD=aB(aG);if(!(aD.length>1&&/\..*0$/.test((aD[1]-aD[0]).toFixed(aE)))){aG.tickDecimals=aE}}}}aG.tickGenerator=aB;if(c.isFunction(aM.tickFormatter)){aG.tickFormatter=function(aV,aW){return""+aM.tickFormatter(aV,aW)}}else{aG.tickFormatter=aR}}function P(aF){var aH=aF.options.ticks,aG=[];if(aH==null||(typeof aH=="number"&&aH>0)){aG=aF.tickGenerator(aF)}else{if(aH){if(c.isFunction(aH)){aG=aH({min:aF.min,max:aF.max})}else{aG=aH}}}var aE,aB;aF.ticks=[];for(aE=0;aE<aG.length;++aE){var aC=null;var aD=aG[aE];if(typeof aD=="object"){aB=+aD[0];if(aD.length>1){aC=aD[1]}}else{aB=+aD}if(aC==null){aC=aF.tickFormatter(aB,aF)}if(!isNaN(aB)){aF.ticks.push({v:aB,label:aC})}}}function ap(aB,aC){if(aB.options.autoscaleMargin&&aC.length>0){if(aB.options.min==null){aB.min=Math.min(aB.min,aC[0].v)}if(aB.options.max==null&&aC.length>1){aB.max=Math.max(aB.max,aC[aC.length-1].v)}}}function W(){H.clearRect(0,0,G,I);var aC=O.grid;if(aC.show&&aC.backgroundColor){N()}if(aC.show&&!aC.aboveData){ac()}for(var aB=0;aB<Q.length;++aB){an(ak.drawSeries,[H,Q[aB]]);d(Q[aB])}an(ak.draw,[H]);if(aC.show&&aC.aboveData){ac()}}function D(aB,aI){var aE,aH,aG,aD,aF=m();for(i=0;i<aF.length;++i){aE=aF[i];if(aE.direction==aI){aD=aI+aE.n+"axis";if(!aB[aD]&&aE.n==1){aD=aI+"axis"}if(aB[aD]){aH=aB[aD].from;aG=aB[aD].to;break}}}if(!aB[aD]){aE=aI=="x"?p[0]:aw[0];aH=aB[aI+"1"];aG=aB[aI+"2"]}if(aH!=null&&aG!=null&&aH>aG){var aC=aH;aH=aG;aG=aC}return{from:aH,to:aG,axis:aE}}function N(){H.save();H.translate(q.left,q.top);H.fillStyle=am(O.grid.backgroundColor,w,0,"rgba(255, 255, 255, 0)");H.fillRect(0,0,h,w);H.restore()}function ac(){var aF;H.save();H.translate(q.left,q.top);var aH=O.grid.markings;if(aH){if(c.isFunction(aH)){var aK=aq.getAxes();aK.xmin=aK.xaxis.min;aK.xmax=aK.xaxis.max;aK.ymin=aK.yaxis.min;aK.ymax=aK.yaxis.max;aH=aH(aK)}for(aF=0;aF<aH.length;++aF){var aD=aH[aF],aC=D(aD,"x"),aI=D(aD,"y");if(aC.from==null){aC.from=aC.axis.min}if(aC.to==null){aC.to=aC.axis.max}if(aI.from==null){aI.from=aI.axis.min}if(aI.to==null){aI.to=aI.axis.max}if(aC.to<aC.axis.min||aC.from>aC.axis.max||aI.to<aI.axis.min||aI.from>aI.axis.max){continue}aC.from=Math.max(aC.from,aC.axis.min);aC.to=Math.min(aC.to,aC.axis.max);aI.from=Math.max(aI.from,aI.axis.min);aI.to=Math.min(aI.to,aI.axis.max);if(aC.from==aC.to&&aI.from==aI.to){continue}aC.from=aC.axis.p2c(aC.from);aC.to=aC.axis.p2c(aC.to);aI.from=aI.axis.p2c(aI.from);aI.to=aI.axis.p2c(aI.to);if(aC.from==aC.to||aI.from==aI.to){H.beginPath();H.strokeStyle=aD.color||O.grid.markingsColor;H.lineWidth=aD.lineWidth||O.grid.markingsLineWidth;H.moveTo(aC.from,aI.from);H.lineTo(aC.to,aI.to);H.stroke()}else{H.fillStyle=aD.color||O.grid.markingsColor;H.fillRect(aC.from,aI.to,aC.to-aC.from,aI.from-aI.to)}}}var aK=m(),aM=O.grid.borderWidth;for(var aE=0;aE<aK.length;++aE){var aB=aK[aE],aG=aB.box,aQ=aB.tickLength,aN,aL,aP,aJ;if(!aB.show||aB.ticks.length==0){continue}H.strokeStyle=aB.options.tickColor||c.color.parse(aB.options.color).scale("a",0.22).toString();H.lineWidth=1;if(aB.direction=="x"){aN=0;if(aQ=="full"){aL=(aB.position=="top"?0:w)}else{aL=aG.top-q.top+(aB.position=="top"?aG.height:0)}}else{aL=0;if(aQ=="full"){aN=(aB.position=="left"?0:h)}else{aN=aG.left-q.left+(aB.position=="left"?aG.width:0)}}if(!aB.innermost){H.beginPath();aP=aJ=0;if(aB.direction=="x"){aP=h}else{aJ=w}if(H.lineWidth==1){aN=Math.floor(aN)+0.5;aL=Math.floor(aL)+0.5}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ);H.stroke()}H.beginPath();for(aF=0;aF<aB.ticks.length;++aF){var aO=aB.ticks[aF].v;aP=aJ=0;if(aO<aB.min||aO>aB.max||(aQ=="full"&&aM>0&&(aO==aB.min||aO==aB.max))){continue}if(aB.direction=="x"){aN=aB.p2c(aO);aJ=aQ=="full"?-w:aQ;if(aB.position=="top"){aJ=-aJ}}else{aL=aB.p2c(aO);aP=aQ=="full"?-h:aQ;if(aB.position=="left"){aP=-aP}}if(H.lineWidth==1){if(aB.direction=="x"){aN=Math.floor(aN)+0.5}else{aL=Math.floor(aL)+0.5}}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ)}H.stroke()}if(aM){H.lineWidth=aM;H.strokeStyle=O.grid.borderColor;H.strokeRect(-aM/2,-aM/2,h+aM,w+aM)}H.restore()}function k(){av.find(".tickLabels").remove();var aG=['<div class="tickLabels" style="font-size:smaller">'];var aJ=m();for(var aD=0;aD<aJ.length;++aD){var aC=aJ[aD],aF=aC.box;if(!aC.show){continue}aG.push('<div class="'+aC.direction+"Axis "+aC.direction+aC.n+'Axis" style="color:'+aC.options.color+'">');for(var aE=0;aE<aC.ticks.length;++aE){var aH=aC.ticks[aE];if(!aH.label||aH.v<aC.min||aH.v>aC.max){continue}var aK={},aI;if(aC.direction=="x"){aI="center";aK.left=Math.round(q.left+aC.p2c(aH.v)-aC.labelWidth/2);if(aC.position=="bottom"){aK.top=aF.top+aF.padding}else{aK.bottom=I-(aF.top+aF.height-aF.padding)}}else{aK.top=Math.round(q.top+aC.p2c(aH.v)-aC.labelHeight/2);if(aC.position=="left"){aK.right=G-(aF.left+aF.width-aF.padding);aI="right"}else{aK.left=aF.left+aF.padding;aI="left"}}aK.width=aC.labelWidth;var aB=["position:absolute","text-align:"+aI];for(var aL in aK){aB.push(aL+":"+aK[aL]+"px")}aG.push('<div class="tickLabel" style="'+aB.join(";")+'">'+aH.label+"</div>")}aG.push("</div>")}aG.push("</div>");av.append(aG.join(""))}function d(aB){if(aB.lines.show){at(aB)}if(aB.bars.show){e(aB)}if(aB.points.show){ao(aB)}}function at(aE){function aD(aP,aQ,aI,aU,aT){var aV=aP.points,aJ=aP.pointsize,aN=null,aM=null;H.beginPath();for(var aO=aJ;aO<aV.length;aO+=aJ){var aL=aV[aO-aJ],aS=aV[aO-aJ+1],aK=aV[aO],aR=aV[aO+1];if(aL==null||aK==null){continue}if(aS<=aR&&aS<aT.min){if(aR<aT.min){continue}aL=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.min}else{if(aR<=aS&&aR<aT.min){if(aS<aT.min){continue}aK=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.min}}if(aS>=aR&&aS>aT.max){if(aR>aT.max){continue}aL=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.max}else{if(aR>=aS&&aR>aT.max){if(aS>aT.max){continue}aK=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.max}}if(aL<=aK&&aL<aU.min){if(aK<aU.min){continue}aS=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.min}else{if(aK<=aL&&aK<aU.min){if(aL<aU.min){continue}aR=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.min}}if(aL>=aK&&aL>aU.max){if(aK>aU.max){continue}aS=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.max}else{if(aK>=aL&&aK>aU.max){if(aL>aU.max){continue}aR=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.max}}if(aL!=aN||aS!=aM){H.moveTo(aU.p2c(aL)+aQ,aT.p2c(aS)+aI)}aN=aK;aM=aR;H.lineTo(aU.p2c(aK)+aQ,aT.p2c(aR)+aI)}H.stroke()}function aF(aI,aQ,aP){var aW=aI.points,aV=aI.pointsize,aN=Math.min(Math.max(0,aP.min),aP.max),aX=0,aU,aT=false,aM=1,aL=0,aR=0;while(true){if(aV>0&&aX>aW.length+aV){break}aX+=aV;var aZ=aW[aX-aV],aK=aW[aX-aV+aM],aY=aW[aX],aJ=aW[aX+aM];if(aT){if(aV>0&&aZ!=null&&aY==null){aR=aX;aV=-aV;aM=2;continue}if(aV<0&&aX==aL+aV){H.fill();aT=false;aV=-aV;aM=1;aX=aL=aR+aV;continue}}if(aZ==null||aY==null){continue}if(aZ<=aY&&aZ<aQ.min){if(aY<aQ.min){continue}aK=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.min}else{if(aY<=aZ&&aY<aQ.min){if(aZ<aQ.min){continue}aJ=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.min}}if(aZ>=aY&&aZ>aQ.max){if(aY>aQ.max){continue}aK=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.max}else{if(aY>=aZ&&aY>aQ.max){if(aZ>aQ.max){continue}aJ=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.max}}if(!aT){H.beginPath();H.moveTo(aQ.p2c(aZ),aP.p2c(aN));aT=true}if(aK>=aP.max&&aJ>=aP.max){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.max));H.lineTo(aQ.p2c(aY),aP.p2c(aP.max));continue}else{if(aK<=aP.min&&aJ<=aP.min){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.min));H.lineTo(aQ.p2c(aY),aP.p2c(aP.min));continue}}var aO=aZ,aS=aY;if(aK<=aJ&&aK<aP.min&&aJ>=aP.min){aZ=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.min}else{if(aJ<=aK&&aJ<aP.min&&aK>=aP.min){aY=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.min}}if(aK>=aJ&&aK>aP.max&&aJ<=aP.max){aZ=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.max}else{if(aJ>=aK&&aJ>aP.max&&aK<=aP.max){aY=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.max}}if(aZ!=aO){H.lineTo(aQ.p2c(aO),aP.p2c(aK))}H.lineTo(aQ.p2c(aZ),aP.p2c(aK));H.lineTo(aQ.p2c(aY),aP.p2c(aJ));if(aY!=aS){H.lineTo(aQ.p2c(aY),aP.p2c(aJ));H.lineTo(aQ.p2c(aS),aP.p2c(aJ))}}}H.save();H.translate(q.left,q.top);H.lineJoin="round";var aG=aE.lines.lineWidth,aB=aE.shadowSize;if(aG>0&&aB>0){H.lineWidth=aB;H.strokeStyle="rgba(0,0,0,0.1)";var aH=Math.PI/18;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/2),Math.cos(aH)*(aG/2+aB/2),aE.xaxis,aE.yaxis);H.lineWidth=aB/2;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/4),Math.cos(aH)*(aG/2+aB/4),aE.xaxis,aE.yaxis)}H.lineWidth=aG;H.strokeStyle=aE.color;var aC=ae(aE.lines,aE.color,0,w);if(aC){H.fillStyle=aC;aF(aE.datapoints,aE.xaxis,aE.yaxis)}if(aG>0){aD(aE.datapoints,0,0,aE.xaxis,aE.yaxis)}H.restore()}function ao(aE){function aH(aN,aM,aU,aK,aS,aT,aQ,aJ){var aR=aN.points,aI=aN.pointsize;for(var aL=0;aL<aR.length;aL+=aI){var aP=aR[aL],aO=aR[aL+1];if(aP==null||aP<aT.min||aP>aT.max||aO<aQ.min||aO>aQ.max){continue}H.beginPath();aP=aT.p2c(aP);aO=aQ.p2c(aO)+aK;if(aJ=="circle"){H.arc(aP,aO,aM,0,aS?Math.PI:Math.PI*2,false)}else{aJ(H,aP,aO,aM,aS)}H.closePath();if(aU){H.fillStyle=aU;H.fill()}H.stroke()}}H.save();H.translate(q.left,q.top);var aG=aE.points.lineWidth,aC=aE.shadowSize,aB=aE.points.radius,aF=aE.points.symbol;if(aG>0&&aC>0){var aD=aC/2;H.lineWidth=aD;H.strokeStyle="rgba(0,0,0,0.1)";aH(aE.datapoints,aB,null,aD+aD/2,true,aE.xaxis,aE.yaxis,aF);H.strokeStyle="rgba(0,0,0,0.2)";aH(aE.datapoints,aB,null,aD/2,true,aE.xaxis,aE.yaxis,aF)}H.lineWidth=aG;H.strokeStyle=aE.color;aH(aE.datapoints,aB,ae(aE.points,aE.color),0,false,aE.xaxis,aE.yaxis,aF);H.restore()}function E(aN,aM,aV,aI,aQ,aF,aD,aL,aK,aU,aR,aC){var aE,aT,aJ,aP,aG,aB,aO,aH,aS;if(aR){aH=aB=aO=true;aG=false;aE=aV;aT=aN;aP=aM+aI;aJ=aM+aQ;if(aT<aE){aS=aT;aT=aE;aE=aS;aG=true;aB=false}}else{aG=aB=aO=true;aH=false;aE=aN+aI;aT=aN+aQ;aJ=aV;aP=aM;if(aP<aJ){aS=aP;aP=aJ;aJ=aS;aH=true;aO=false}}if(aT<aL.min||aE>aL.max||aP<aK.min||aJ>aK.max){return}if(aE<aL.min){aE=aL.min;aG=false}if(aT>aL.max){aT=aL.max;aB=false}if(aJ<aK.min){aJ=aK.min;aH=false}if(aP>aK.max){aP=aK.max;aO=false}aE=aL.p2c(aE);aJ=aK.p2c(aJ);aT=aL.p2c(aT);aP=aK.p2c(aP);if(aD){aU.beginPath();aU.moveTo(aE,aJ);aU.lineTo(aE,aP);aU.lineTo(aT,aP);aU.lineTo(aT,aJ);aU.fillStyle=aD(aJ,aP);aU.fill()}if(aC>0&&(aG||aB||aO||aH)){aU.beginPath();aU.moveTo(aE,aJ+aF);if(aG){aU.lineTo(aE,aP+aF)}else{aU.moveTo(aE,aP+aF)}if(aO){aU.lineTo(aT,aP+aF)}else{aU.moveTo(aT,aP+aF)}if(aB){aU.lineTo(aT,aJ+aF)}else{aU.moveTo(aT,aJ+aF)}if(aH){aU.lineTo(aE,aJ+aF)}else{aU.moveTo(aE,aJ+aF)}aU.stroke()}}function e(aD){function aC(aJ,aI,aL,aG,aK,aN,aM){var aO=aJ.points,aF=aJ.pointsize;for(var aH=0;aH<aO.length;aH+=aF){if(aO[aH]==null){continue}E(aO[aH],aO[aH+1],aO[aH+2],aI,aL,aG,aK,aN,aM,H,aD.bars.horizontal,aD.bars.lineWidth)}}H.save();H.translate(q.left,q.top);H.lineWidth=aD.bars.lineWidth;H.strokeStyle=aD.color;var aB=aD.bars.align=="left"?0:-aD.bars.barWidth/2;var aE=aD.bars.fill?function(aF,aG){return ae(aD.bars,aD.color,aF,aG)}:null;aC(aD.datapoints,aB,aB+aD.bars.barWidth,0,aE,aD.xaxis,aD.yaxis);H.restore()}function ae(aD,aB,aC,aF){var aE=aD.fill;if(!aE){return null}if(aD.fillColor){return am(aD.fillColor,aC,aF,aB)}var aG=c.color.parse(aB);aG.a=typeof aE=="number"?aE:0.4;aG.normalize();return aG.toString()}function o(){av.find(".legend").remove();if(!O.legend.show){return}var aH=[],aF=false,aN=O.legend.labelFormatter,aM,aJ;for(var aE=0;aE<Q.length;++aE){aM=Q[aE];aJ=aM.label;if(!aJ){continue}if(aE%O.legend.noColumns==0){if(aF){aH.push("</tr>")}aH.push("<tr>");aF=true}if(aN){aJ=aN(aJ,aM)}aH.push('<td class="legendColorBox"><div style="border:1px solid '+O.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+aM.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+aJ+"</td>")}if(aF){aH.push("</tr>")}if(aH.length==0){return}var aL='<table style="font-size:smaller;color:'+O.grid.color+'">'+aH.join("")+"</table>";if(O.legend.container!=null){c(O.legend.container).html(aL)}else{var aI="",aC=O.legend.position,aD=O.legend.margin;if(aD[0]==null){aD=[aD,aD]}if(aC.charAt(0)=="n"){aI+="top:"+(aD[1]+q.top)+"px;"}else{if(aC.charAt(0)=="s"){aI+="bottom:"+(aD[1]+q.bottom)+"px;"}}if(aC.charAt(1)=="e"){aI+="right:"+(aD[0]+q.right)+"px;"}else{if(aC.charAt(1)=="w"){aI+="left:"+(aD[0]+q.left)+"px;"}}var aK=c('<div class="legend">'+aL.replace('style="','style="position:absolute;'+aI+";")+"</div>").appendTo(av);if(O.legend.backgroundOpacity!=0){var aG=O.legend.backgroundColor;if(aG==null){aG=O.grid.backgroundColor;if(aG&&typeof aG=="string"){aG=c.color.parse(aG)}else{aG=c.color.extract(aK,"background-color")}aG.a=1;aG=aG.toString()}var aB=aK.children();c('<div style="position:absolute;width:'+aB.width()+"px;height:"+aB.height()+"px;"+aI+"background-color:"+aG+';"> </div>').prependTo(aK).css("opacity",O.legend.backgroundOpacity)}}}var ab=[],M=null;function K(aI,aG,aD){var aO=O.grid.mouseActiveRadius,a0=aO*aO+1,aY=null,aR=false,aW,aU;for(aW=Q.length-1;aW>=0;--aW){if(!aD(Q[aW])){continue}var aP=Q[aW],aH=aP.xaxis,aF=aP.yaxis,aV=aP.datapoints.points,aT=aP.datapoints.pointsize,aQ=aH.c2p(aI),aN=aF.c2p(aG),aC=aO/aH.scale,aB=aO/aF.scale;if(aH.options.inverseTransform){aC=Number.MAX_VALUE}if(aF.options.inverseTransform){aB=Number.MAX_VALUE}if(aP.lines.show||aP.points.show){for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1];if(aK==null){continue}if(aK-aQ>aC||aK-aQ<-aC||aJ-aN>aB||aJ-aN<-aB){continue}var aM=Math.abs(aH.p2c(aK)-aI),aL=Math.abs(aF.p2c(aJ)-aG),aS=aM*aM+aL*aL;if(aS<a0){a0=aS;aY=[aW,aU/aT]}}}if(aP.bars.show&&!aY){var aE=aP.bars.align=="left"?0:-aP.bars.barWidth/2,aX=aE+aP.bars.barWidth;for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1],aZ=aV[aU+2];if(aK==null){continue}if(Q[aW].bars.horizontal?(aQ<=Math.max(aZ,aK)&&aQ>=Math.min(aZ,aK)&&aN>=aJ+aE&&aN<=aJ+aX):(aQ>=aK+aE&&aQ<=aK+aX&&aN>=Math.min(aZ,aJ)&&aN<=Math.max(aZ,aJ))){aY=[aW,aU/aT]}}}}if(aY){aW=aY[0];aU=aY[1];aT=Q[aW].datapoints.pointsize;return{datapoint:Q[aW].datapoints.points.slice(aU*aT,(aU+1)*aT),dataIndex:aU,series:Q[aW],seriesIndex:aW}}return null}function aa(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return aC.hoverable!=false})}}function l(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return false})}}function R(aB){u("plotclick",aB,function(aC){return aC.clickable!=false})}function u(aC,aB,aD){var aE=y.offset(),aH=aB.pageX-aE.left-q.left,aF=aB.pageY-aE.top-q.top,aJ=C({left:aH,top:aF});aJ.pageX=aB.pageX;aJ.pageY=aB.pageY;var aK=K(aH,aF,aD);if(aK){aK.pageX=parseInt(aK.series.xaxis.p2c(aK.datapoint[0])+aE.left+q.left);aK.pageY=parseInt(aK.series.yaxis.p2c(aK.datapoint[1])+aE.top+q.top)}if(O.grid.autoHighlight){for(var aG=0;aG<ab.length;++aG){var aI=ab[aG];if(aI.auto==aC&&!(aK&&aI.series==aK.series&&aI.point[0]==aK.datapoint[0]&&aI.point[1]==aK.datapoint[1])){T(aI.series,aI.point)}}if(aK){x(aK.series,aK.datapoint,aC)}}av.trigger(aC,[aJ,aK])}function f(){if(!M){M=setTimeout(s,30)}}function s(){M=null;A.save();A.clearRect(0,0,G,I);A.translate(q.left,q.top);var aC,aB;for(aC=0;aC<ab.length;++aC){aB=ab[aC];if(aB.series.bars.show){v(aB.series,aB.point)}else{ay(aB.series,aB.point)}}A.restore();an(ak.drawOverlay,[A])}function x(aD,aB,aF){if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){var aE=aD.datapoints.pointsize;aB=aD.datapoints.points.slice(aE*aB,aE*(aB+1))}var aC=al(aD,aB);if(aC==-1){ab.push({series:aD,point:aB,auto:aF});f()}else{if(!aF){ab[aC].auto=false}}}function T(aD,aB){if(aD==null&&aB==null){ab=[];f()}if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){aB=aD.data[aB]}var aC=al(aD,aB);if(aC!=-1){ab.splice(aC,1);f()}}function al(aD,aE){for(var aB=0;aB<ab.length;++aB){var aC=ab[aB];if(aC.series==aD&&aC.point[0]==aE[0]&&aC.point[1]==aE[1]){return aB}}return -1}function ay(aE,aD){var aC=aD[0],aI=aD[1],aH=aE.xaxis,aG=aE.yaxis;if(aC<aH.min||aC>aH.max||aI<aG.min||aI>aG.max){return}var aF=aE.points.radius+aE.points.lineWidth/2;A.lineWidth=aF;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aB=1.5*aF,aC=aH.p2c(aC),aI=aG.p2c(aI);A.beginPath();if(aE.points.symbol=="circle"){A.arc(aC,aI,aB,0,2*Math.PI,false)}else{aE.points.symbol(A,aC,aI,aB,false)}A.closePath();A.stroke()}function v(aE,aB){A.lineWidth=aE.bars.lineWidth;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aD=c.color.parse(aE.color).scale("a",0.5).toString();var aC=aE.bars.align=="left"?0:-aE.bars.barWidth/2;E(aB[0],aB[1],aB[2]||0,aC,aC+aE.bars.barWidth,0,function(){return aD},aE.xaxis,aE.yaxis,A,aE.bars.horizontal,aE.bars.lineWidth)}function am(aJ,aB,aH,aC){if(typeof aJ=="string"){return aJ}else{var aI=H.createLinearGradient(0,aH,0,aB);for(var aE=0,aD=aJ.colors.length;aE<aD;++aE){var aF=aJ.colors[aE];if(typeof aF!="string"){var aG=c.color.parse(aC);if(aF.brightness!=null){aG=aG.scale("rgb",aF.brightness)}if(aF.opacity!=null){aG.a*=aF.opacity}aF=aG.toString()}aI.addColorStop(aE/(aD-1),aF)}return aI}}}c.plot=function(g,e,d){var f=new b(c(g),e,d,c.plot.plugins);return f};c.plot.version="0.7";c.plot.plugins=[];c.plot.formatDate=function(l,f,h){var o=function(d){d=""+d;return d.length==1?"0"+d:d};var e=[];var p=false,j=false;var n=l.getUTCHours();var k=n<12;if(h==null){h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(f.search(/%p|%P/)!=-1){if(n>12){n=n-12}else{if(n==0){n=12}}}for(var g=0;g<f.length;++g){var m=f.charAt(g);if(p){switch(m){case"h":m=""+n;break;case"H":m=o(n);break;case"M":m=o(l.getUTCMinutes());break;case"S":m=o(l.getUTCSeconds());break;case"d":m=""+l.getUTCDate();break;case"m":m=""+(l.getUTCMonth()+1);break;case"y":m=""+l.getUTCFullYear();break;case"b":m=""+h[l.getUTCMonth()];break;case"p":m=(k)?("am"):("pm");break;case"P":m=(k)?("AM"):("PM");break;case"0":m="";j=true;break}if(m&&j){m=o(m);j=false}e.push(m);if(!j){p=false}}else{if(m=="%"){p=true}else{e.push(m)}}}return e.join("")};function a(e,d){return d*Math.floor(e/d)}})(jQuery);
js/jquery.flot.navigate.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ /*
2
+ Javascript plotting library for jQuery ~ v0.8 beta ~ Copyright (c) 2007-2011 IOLA and Ole Laursen
3
+ http://code.google.com/p/flot/issues/detail?id=643
4
+ Licensed under the MIT License ~ https://raw.github.com/flot/flot/master/LICENSE.txt
5
+ */
6
+ (function(a){a.fn.drag=function(b,c,d){var e=typeof b=="string"?b:"",g=a.isFunction(b)?b:a.isFunction(c)?c:null;if(e.indexOf("drag")!==0)e="drag"+e;d=(b==g?c:d)||{};return g?this.bind(e,d,g):this.trigger(e)};var b=a.event,c=b.special,d=c.drag={defaults:{which:1,distance:0,not:":input",handle:null,relative:false,drop:true,click:false},datakey:"dragdata",livekey:"livedrag",add:function(c){var e=a.data(this,d.datakey),g=c.data||{};e.related+=1;if(!e.live&&c.selector){e.live=true;b.add(this,"draginit."+d.livekey,d.delegate)}a.each(d.defaults,function(a){if(g[a]!==undefined)e[a]=g[a]})},remove:function(){a.data(this,d.datakey).related-=1},setup:function(){if(!a.data(this,d.datakey)){var c=a.extend({related:0},d.defaults);a.data(this,d.datakey,c);b.add(this,"mousedown",d.init,c);this.attachEvent&&this.attachEvent("ondragstart",d.dontstart)}},teardown:function(){if(!a.data(this,d.datakey).related){a.removeData(this,d.datakey);b.remove(this,"mousedown",d.init);b.remove(this,"draginit",d.delegate);d.textselect(true);this.detachEvent&&this.detachEvent("ondragstart",d.dontstart)}},init:function(e){var g=e.data,j;if(!(g.which>0&&e.which!=g.which))if(!a(e.target).is(g.not))if(!(g.handle&&!a(e.target).closest(g.handle,e.currentTarget).length)){g.propagates=1;g.interactions=[d.interaction(this,g)];g.target=e.target;g.pageX=e.pageX;g.pageY=e.pageY;g.dragging=null;j=d.hijack(e,"draginit",g);if(g.propagates){if((j=d.flatten(j))&&j.length){g.interactions=[];a.each(j,function(){g.interactions.push(d.interaction(this,g))})}g.propagates=g.interactions.length;g.drop!==false&&c.drop&&c.drop.handler(e,g);d.textselect(false);b.add(document,"mousemove mouseup",d.handler,g);return false}}},interaction:function(b,c){return{drag:b,callback:new d.callback,droppable:[],offset:a(b)[c.relative?"position":"offset"]()||{top:0,left:0}}},handler:function(a){var e=a.data;switch(a.type){case!e.dragging&&"mousemove":if(Math.pow(a.pageX-e.pageX,2)+Math.pow(a.pageY-e.pageY,2)<Math.pow(e.distance,2))break;a.target=e.target;d.hijack(a,"dragstart",e);if(e.propagates)e.dragging=true;case"mousemove":if(e.dragging){d.hijack(a,"drag",e);if(e.propagates){e.drop!==false&&c.drop&&c.drop.handler(a,e);break}a.type="mouseup"};case"mouseup":b.remove(document,"mousemove mouseup",d.handler);if(e.dragging){e.drop!==false&&c.drop&&c.drop.handler(a,e);d.hijack(a,"dragend",e)}d.textselect(true);if(e.click===false&&e.dragging){jQuery.event.triggered=true;setTimeout(function(){jQuery.event.triggered=false},20);e.dragging=false}break}},delegate:function(c){var e=[],g,h=a.data(this,"events")||{};a.each(h.live||[],function(h,j){if(j.preType.indexOf("drag")===0)if(g=a(c.target).closest(j.selector,c.currentTarget)[0]){b.add(g,j.origType+"."+d.livekey,j.origHandler,j.data);a.inArray(g,e)<0&&e.push(g)}});if(!e.length)return false;return a(e).bind("dragend."+d.livekey,function(){b.remove(this,"."+d.livekey)})},hijack:function(c,e,g,h,j){if(g){var k={event:c.originalEvent,type:c.type},l=e.indexOf("drop")?"drag":"drop",m,n=h||0,o,p;h=!isNaN(h)?h:g.interactions.length;c.type=e;c.originalEvent=null;g.results=[];do if(o=g.interactions[n])if(!(e!=="dragend"&&o.cancelled)){p=d.properties(c,g,o);o.results=[];a(j||o[l]||g.droppable).each(function(h,j){m=(p.target=j)?b.handle.call(j,c,p):null;if(m===false){if(l=="drag"){o.cancelled=true;g.propagates-=1}if(e=="drop")o[l][h]=null}else if(e=="dropinit")o.droppable.push(d.element(m)||j);if(e=="dragstart")o.proxy=a(d.element(m)||o.drag)[0];o.results.push(m);delete c.result;if(e!=="dropinit")return m});g.results[n]=d.flatten(o.results);if(e=="dropinit")o.droppable=d.flatten(o.droppable);e=="dragstart"&&!o.cancelled&&p.update()}while(++n<h);c.type=k.type;c.originalEvent=k.event;return d.flatten(g.results)}},properties:function(a,b,c){var e=c.callback;e.drag=c.drag;e.proxy=c.proxy||c.drag;e.startX=b.pageX;e.startY=b.pageY;e.deltaX=a.pageX-b.pageX;e.deltaY=a.pageY-b.pageY;e.originalX=c.offset.left;e.originalY=c.offset.top;e.offsetX=a.pageX-(b.pageX-e.originalX);e.offsetY=a.pageY-(b.pageY-e.originalY);e.drop=d.flatten((c.drop||[]).slice());e.available=d.flatten((c.droppable||[]).slice());return e},element:function(a){if(a&&(a.jquery||a.nodeType==1))return a},flatten:function(b){return a.map(b,function(b){return b&&b.jquery?a.makeArray(b):b&&b.length?d.flatten(b):b})},textselect:function(b){a(document)[b?"unbind":"bind"]("selectstart",d.dontstart).attr("unselectable",b?"off":"on").css("MozUserSelect",b?"":"none")},dontstart:function(){return false},callback:function(){}};d.callback.prototype={update:function(){c.drop&&this.available.length&&a.each(this.available,function(a){c.drop.locate(this,a)})}};c.draginit=c.dragstart=c.dragend=d})(jQuery);(function(a){function b(b){var c=b||window.event,d=[].slice.call(arguments,1),e=0,f=!0,g=0,h=0;return b=a.event.fix(c),b.type="mousewheel",c.wheelDelta&&(e=c.wheelDelta/120),c.detail&&(e=-c.detail/3),h=e,c.axis!==undefined&&c.axis===c.HORIZONTAL_AXIS&&(h=0,g=-1*e),c.wheelDeltaY!==undefined&&(h=c.wheelDeltaY/120),c.wheelDeltaX!==undefined&&(g=-1*c.wheelDeltaX/120),d.unshift(b,e,g,h),(a.event.dispatch||a.event.handle).apply(this,d)}var c=["DOMMouseScroll","mousewheel"];if(a.event.fixHooks)for(var d=c.length;d;)a.event.fixHooks[c[--d]]=a.event.mouseHooks;a.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=c.length;a;)this.addEventListener(c[--a],b,!1);else this.onmousewheel=b},teardown:function(){if(this.removeEventListener)for(var a=c.length;a;)this.removeEventListener(c[--a],b,!1);else this.onmousewheel=null}},a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);(function(a){function c(b){function m(a,b){b.unbind(a.getOptions().zoom.trigger,c);b.unbind("mousewheel",d);b.unbind("dragstart",i);b.unbind("drag",j);b.unbind("dragend",k);if(h)clearTimeout(h)}function l(a,b){var e=a.getOptions();if(e.zoom.interactive){b[e.zoom.trigger](c);b.mousewheel(d)}if(e.pan.interactive){b.bind("dragstart",{distance:10},i);b.bind("drag",j);b.bind("dragend",k)}}function k(a){if(h){clearTimeout(h);h=null}b.getPlaceholder().css("cursor",e);b.pan({left:f-a.pageX,top:g-a.pageY})}function j(a){var c=b.getOptions().pan.frameRate;if(h||!c)return;h=setTimeout(function(){b.pan({left:f-a.pageX,top:g-a.pageY});f=a.pageX;g=a.pageY;h=null},1/c*1e3)}function i(a){if(a.which!=1)return false;var c=b.getPlaceholder().css("cursor");if(c)e=c;b.getPlaceholder().css("cursor",b.getOptions().pan.cursor);f=a.pageX;g=a.pageY}function d(a,b){c(a,b<0);return false}function c(a,c){var d=b.offset();d.left=a.pageX-d.left;d.top=a.pageY-d.top;if(c)b.zoomOut({center:d});else b.zoom({center:d})}var e="default",f=0,g=0,h=null;b.zoomOut=function(a){if(!a)a={};if(!a.amount)a.amount=b.getOptions().zoom.amount;a.amount=1/a.amount;b.zoom(a)};b.zoom=function(c){if(!c)c={};var d=c.center,e=c.amount||b.getOptions().zoom.amount,f=b.width(),g=b.height();if(!d)d={left:f/2,top:g/2};var h=d.left/f,i=d.top/g,j={x:{min:d.left-h*f/e,max:d.left+(1-h)*f/e},y:{min:d.top-i*g/e,max:d.top+(1-i)*g/e}};a.each(b.getAxes(),function(a,b){var c=b.options,d=j[b.direction].min,e=j[b.direction].max,f=c.zoomRange;if(f===false)return;d=b.c2p(d);e=b.c2p(e);if(d>e){var g=d;d=e;e=g}var h=e-d;if(f&&(f[0]!=null&&h<f[0]||f[1]!=null&&h>f[1]))return;c.min=d;c.max=e});b.setupGrid();b.draw();if(!c.preventEvent)b.getPlaceholder().trigger("plotzoom",[b])};b.pan=function(c){var d={x:+c.left,y:+c.top};if(isNaN(d.x))d.x=0;if(isNaN(d.y))d.y=0;a.each(b.getAxes(),function(a,b){var c=b.options,e,f,g=d[b.direction];e=b.c2p(b.p2c(b.min)+g),f=b.c2p(b.p2c(b.max)+g);var h=c.panRange;if(h===false)return;if(h){if(h[0]!=null&&h[0]>e){g=h[0]-e;e+=g;f+=g}if(h[1]!=null&&h[1]<f){g=h[1]-f;e+=g;f+=g}}c.min=e;c.max=f});b.setupGrid();b.draw();if(!c.preventEvent)b.getPlaceholder().trigger("plotpan",[b])};b.hooks.bindEvents.push(l);b.hooks.shutdown.push(m)}var b={xaxis:{zoomRange:null,panRange:null},zoom:{interactive:false,trigger:"dblclick",amount:1.5},pan:{interactive:false,cursor:"move",frameRate:20}};a.plot.plugins.push({init:c,options:b,name:"navigate",version:"1.3"})})(jQuery)
js/jquery.flot.pie.min.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ /*
2
+ Flot plugin for rendering pie charts ~1.0 ~ Copyright (c) 2007-2011 IOLA and Ole Laursen
3
+ Licensed under the MIT License ~ https://raw.github.com/flot/flot/master/LICENSE.txt
4
+ */
5
+ (function(b){function c(D){var h=null;var L=null;var n=null;var B=null;var p=null;var M=0;var F=true;var o=10;var w=0.95;var A=0;var d=false;var z=false;var j=[];D.hooks.processOptions.push(g);D.hooks.bindEvents.push(e);function g(O,N){if(N.series.pie.show){N.grid.show=false;if(N.series.pie.label.show=="auto"){if(N.legend.show){N.series.pie.label.show=false}else{N.series.pie.label.show=true}}if(N.series.pie.radius=="auto"){if(N.series.pie.label.show){N.series.pie.radius=3/4}else{N.series.pie.radius=1}}if(N.series.pie.tilt>1){N.series.pie.tilt=1}if(N.series.pie.tilt<0){N.series.pie.tilt=0}O.hooks.processDatapoints.push(E);O.hooks.drawOverlay.push(H);O.hooks.draw.push(r)}}function e(P,N){var O=P.getOptions();if(O.series.pie.show&&O.grid.hoverable){N.unbind("mousemove").mousemove(t)}if(O.series.pie.show&&O.grid.clickable){N.unbind("click").click(l)}}function G(O){var P="";function N(S,T){if(!T){T=0}for(var R=0;R<S.length;++R){for(var Q=0;Q<T;Q++){P+="\t"}if(typeof S[R]=="object"){P+=""+R+":\n";N(S[R],T+1)}else{P+=""+R+": "+S[R]+"\n"}}}N(O);alert(P)}function q(P){for(var N=0;N<P.length;++N){var O=parseFloat(P[N].data[0][1]);if(O){M+=O}}}function E(Q,N,O,P){if(!d){d=true;h=Q.getCanvas();L=b(h).parent();a=Q.getOptions();Q.setData(K(Q.getData()))}}function I(){A=L.children().filter(".legend").children().width();n=Math.min(h.width,(h.height/a.series.pie.tilt))/2;p=(h.height/2)+a.series.pie.offset.top;B=(h.width/2);if(a.series.pie.offset.left=="auto"){if(a.legend.position.match("w")){B+=A/2}else{B-=A/2}}else{B+=a.series.pie.offset.left}if(B<n){B=n}else{if(B>h.width-n){B=h.width-n}}}function v(O){for(var N=0;N<O.length;++N){if(typeof(O[N].data)=="number"){O[N].data=[[1,O[N].data]]}else{if(typeof(O[N].data)=="undefined"||typeof(O[N].data[0])=="undefined"){if(typeof(O[N].data)!="undefined"&&typeof(O[N].data.label)!="undefined"){O[N].label=O[N].data.label}O[N].data=[[1,0]]}}}return O}function K(Q){Q=v(Q);q(Q);var P=0;var S=0;var N=a.series.pie.combine.color;var R=[];for(var O=0;O<Q.length;++O){Q[O].data[0][1]=parseFloat(Q[O].data[0][1]);if(!Q[O].data[0][1]){Q[O].data[0][1]=0}if(Q[O].data[0][1]/M<=a.series.pie.combine.threshold){P+=Q[O].data[0][1];S++;if(!N){N=Q[O].color}}else{R.push({data:[[1,Q[O].data[0][1]]],color:Q[O].color,label:Q[O].label,angle:(Q[O].data[0][1]*(Math.PI*2))/M,percent:(Q[O].data[0][1]/M*100)})}}if(S>0){R.push({data:[[1,P]],color:N,label:a.series.pie.combine.label,angle:(P*(Math.PI*2))/M,percent:(P/M*100)})}return R}function r(S,Q){if(!L){return}ctx=Q;I();var T=S.getData();var P=0;while(F&&P<o){F=false;if(P>0){n*=w}P+=1;N();if(a.series.pie.tilt<=0.8){O()}R()}if(P>=o){N();L.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>')}if(S.setSeries&&S.insertLegend){S.setSeries(T);S.insertLegend()}function N(){ctx.clearRect(0,0,h.width,h.height);L.children().filter(".pieLabel, .pieLabelBackground").remove()}function O(){var Z=5;var Y=15;var W=10;var X=0.02;if(a.series.pie.radius>1){var U=a.series.pie.radius}else{var U=n*a.series.pie.radius}if(U>=(h.width/2)-Z||U*a.series.pie.tilt>=(h.height/2)-Y||U<=W){return}ctx.save();ctx.translate(Z,Y);ctx.globalAlpha=X;ctx.fillStyle="#000";ctx.translate(B,p);ctx.scale(1,a.series.pie.tilt);for(var V=1;V<=W;V++){ctx.beginPath();ctx.arc(0,0,U,0,Math.PI*2,false);ctx.fill();U-=V}ctx.restore()}function R(){startAngle=Math.PI*a.series.pie.startAngle;if(a.series.pie.radius>1){var U=a.series.pie.radius}else{var U=n*a.series.pie.radius}ctx.save();ctx.translate(B,p);ctx.scale(1,a.series.pie.tilt);ctx.save();var Y=startAngle;for(var W=0;W<T.length;++W){T[W].startAngle=Y;X(T[W].angle,T[W].color,true)}ctx.restore();ctx.save();ctx.lineWidth=a.series.pie.stroke.width;Y=startAngle;for(var W=0;W<T.length;++W){X(T[W].angle,a.series.pie.stroke.color,false)}ctx.restore();J(ctx);if(a.series.pie.label.show){V()}ctx.restore();function X(ab,Z,aa){if(ab<=0){return}if(aa){ctx.fillStyle=Z}else{ctx.strokeStyle=Z;ctx.lineJoin="round"}ctx.beginPath();if(Math.abs(ab-Math.PI*2)>1e-9){ctx.moveTo(0,0)}else{if(b.browser.msie){ab-=0.0001}}ctx.arc(0,0,U,Y,Y+ab,false);ctx.closePath();Y+=ab;if(aa){ctx.fill()}else{ctx.stroke()}}function V(){var ac=startAngle;if(a.series.pie.label.radius>1){var Z=a.series.pie.label.radius}else{var Z=n*a.series.pie.label.radius}for(var ab=0;ab<T.length;++ab){if(T[ab].percent>=a.series.pie.label.threshold*100){aa(T[ab],ac,ab)}ac+=T[ab].angle}function aa(ap,ai,ag){if(ap.data[0][1]==0){return}var ar=a.legend.labelFormatter,aq,ae=a.series.pie.label.formatter;if(ar){aq=ar(ap.label,ap)}else{aq=ap.label}if(ae){aq=ae(aq,ap)}var aj=((ai+ap.angle)+ai)/2;var ao=B+Math.round(Math.cos(aj)*Z);var am=p+Math.round(Math.sin(aj)*Z)*a.series.pie.tilt;var af='<span class="pieLabel" id="pieLabel'+ag+'" style="position:absolute;top:'+am+"px;left:"+ao+'px;">'+aq+"</span>";L.append(af);var an=L.children("#pieLabel"+ag);var ad=(am-an.height()/2);var ah=(ao-an.width()/2);an.css("top",ad);an.css("left",ah);if(0-ad>0||0-ah>0||h.height-(ad+an.height())<0||h.width-(ah+an.width())<0){F=true}if(a.series.pie.label.background.opacity!=0){var ak=a.series.pie.label.background.color;if(ak==null){ak=ap.color}var al="top:"+ad+"px;left:"+ah+"px;";b('<div class="pieLabelBackground" style="position:absolute;width:'+an.width()+"px;height:"+an.height()+"px;"+al+"background-color:"+ak+';"> </div>').insertBefore(an).css("opacity",a.series.pie.label.background.opacity)}}}}}function J(N){if(a.series.pie.innerRadius>0){N.save();innerRadius=a.series.pie.innerRadius>1?a.series.pie.innerRadius:n*a.series.pie.innerRadius;N.globalCompositeOperation="destination-out";N.beginPath();N.fillStyle=a.series.pie.stroke.color;N.arc(0,0,innerRadius,0,Math.PI*2,false);N.fill();N.closePath();N.restore();N.save();N.beginPath();N.strokeStyle=a.series.pie.stroke.color;N.arc(0,0,innerRadius,0,Math.PI*2,false);N.stroke();N.closePath();N.restore()}}function s(Q,R){for(var S=false,P=-1,N=Q.length,O=N-1;++P<N;O=P){((Q[P][1]<=R[1]&&R[1]<Q[O][1])||(Q[O][1]<=R[1]&&R[1]<Q[P][1]))&&(R[0]<(Q[O][0]-Q[P][0])*(R[1]-Q[P][1])/(Q[O][1]-Q[P][1])+Q[P][0])&&(S=!S)}return S}function u(R,P){var T=D.getData(),O=D.getOptions(),N=O.series.pie.radius>1?O.series.pie.radius:n*O.series.pie.radius;for(var Q=0;Q<T.length;++Q){var S=T[Q];if(S.pie.show){ctx.save();ctx.beginPath();ctx.moveTo(0,0);ctx.arc(0,0,N,S.startAngle,S.startAngle+S.angle,false);ctx.closePath();x=R-B;y=P-p;if(ctx.isPointInPath){if(ctx.isPointInPath(R-B,P-p)){ctx.restore();return{datapoint:[S.percent,S.data],dataIndex:0,series:S,seriesIndex:Q}}}else{p1X=(N*Math.cos(S.startAngle));p1Y=(N*Math.sin(S.startAngle));p2X=(N*Math.cos(S.startAngle+(S.angle/4)));p2Y=(N*Math.sin(S.startAngle+(S.angle/4)));p3X=(N*Math.cos(S.startAngle+(S.angle/2)));p3Y=(N*Math.sin(S.startAngle+(S.angle/2)));p4X=(N*Math.cos(S.startAngle+(S.angle/1.5)));p4Y=(N*Math.sin(S.startAngle+(S.angle/1.5)));p5X=(N*Math.cos(S.startAngle+S.angle));p5Y=(N*Math.sin(S.startAngle+S.angle));arrPoly=[[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];arrPoint=[x,y];if(s(arrPoly,arrPoint)){ctx.restore();return{datapoint:[S.percent,S.data],dataIndex:0,series:S,seriesIndex:Q}}}ctx.restore()}}return null}function t(N){m("plothover",N)}function l(N){m("plotclick",N)}function m(N,T){var O=D.offset(),R=parseInt(T.pageX-O.left),P=parseInt(T.pageY-O.top),V=u(R,P);if(a.grid.autoHighlight){for(var Q=0;Q<j.length;++Q){var S=j[Q];if(S.auto==N&&!(V&&S.series==V.series)){f(S.series)}}}if(V){k(V.series,N)}var U={pageX:T.pageX,pageY:T.pageY};L.trigger(N,[U,V])}function k(O,P){if(typeof O=="number"){O=series[O]}var N=C(O);if(N==-1){j.push({series:O,auto:P});D.triggerRedrawOverlay()}else{if(!P){j[N].auto=false}}}function f(O){if(O==null){j=[];D.triggerRedrawOverlay()}if(typeof O=="number"){O=series[O]}var N=C(O);if(N!=-1){j.splice(N,1);D.triggerRedrawOverlay()}}function C(P){for(var N=0;N<j.length;++N){var O=j[N];if(O.series==P){return N}}return -1}function H(Q,R){var P=Q.getOptions();var N=P.series.pie.radius>1?P.series.pie.radius:n*P.series.pie.radius;R.save();R.translate(B,p);R.scale(1,P.series.pie.tilt);for(i=0;i<j.length;++i){O(j[i].series)}J(R);R.restore();function O(S){if(S.angle<0){return}R.fillStyle="rgba(255, 255, 255, "+P.series.pie.highlight.opacity+")";R.beginPath();if(Math.abs(S.angle-Math.PI*2)>1e-9){R.moveTo(0,0)}R.arc(0,0,N,S.startAngle,S.startAngle+S.angle,false);R.closePath();R.fill()}}}var a={series:{pie:{show:false,radius:"auto",innerRadius:0,startAngle:3/2,tilt:1,offset:{top:0,left:"auto"},stroke:{color:"#FFF",width:1},label:{show:"auto",formatter:function(d,e){return'<div style="font-size:x-small;text-align:center;padding:2px;color:'+e.color+';">'+d+"<br/>"+Math.round(e.percent)+"%</div>"},radius:1,background:{color:null,opacity:0},threshold:0},combine:{threshold:-1,color:null,label:"Other"},highlight:{opacity:0.5}}}};b.plot.plugins.push({init:c,options:a,name:"pie",version:"1.0"})})(jQuery);
js/jquery.qtip.min.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * qTip2 - Pretty powerful tooltips
3
+ * http://craigsworks.com/projects/qtip2/
4
+ *
5
+ * Version: nightly
6
+ * Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
7
+ *
8
+ * Dual licensed under MIT or GPLv2 licenses
9
+ * http://en.wikipedia.org/wiki/MIT_License
10
+ * http://en.wikipedia.org/wiki/GNU_General_Public_License
11
+ *
12
+ * Date: Thu Oct 20 13:41:21.0000000000 2011
13
+ *//*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true *//*global window: false, jQuery: false, console: false */(function(a,b,c){function B(b,g){function w(a){var b=a.precedance==="y",c=n[b?"width":"height"],d=n[b?"height":"width"],e=a.string().indexOf("center")>-1,f=c*(e?.5:1),g=Math.pow,h=Math.round,i,j,k,l=Math.sqrt(g(f,2)+g(d,2)),m=[p/f*l,p/d*l];m[2]=Math.sqrt(g(m[0],2)-g(p,2)),m[3]=Math.sqrt(g(m[1],2)-g(p,2)),i=l+m[2]+m[3]+(e?0:m[0]),j=i/l,k=[h(j*d),h(j*c)];return{height:k[b?0:1],width:k[b?1:0]}}function v(b){var c=k.titlebar&&b.y==="top",d=c?k.titlebar:k.content,e=a.browser.mozilla,f=e?"-moz-":a.browser.webkit?"-webkit-":"",g=b.y+(e?"":"-")+b.x,h=f+(e?"border-radius-"+g:"border-"+g+"-radius");return parseInt(d.css(h),10)||parseInt(l.css(h),10)||0}function u(a,b,c){b=b?b:a[a.precedance];var d=l.hasClass(r),e=k.titlebar&&a.y==="top",f=e?k.titlebar:k.content,g="border-"+b+"-width",h;l.addClass(r),h=parseInt(f.css(g),10),h=(c?h||parseInt(l.css(g),10):h)||0,l.toggleClass(r,d);return h}function t(f,g,h,l){if(k.tip){var n=a.extend({},i.corner),o=h.adjusted,p=b.options.position.adjust.method.split(" "),q=p[0],r=p[1]||p[0],s={left:e,top:e,x:0,y:0},t,u={},v;i.corner.fixed!==d&&(q==="shift"&&n.precedance==="x"&&o.left&&n.y!=="center"?n.precedance=n.precedance==="x"?"y":"x":q==="flip"&&o.left&&(n.x=n.x==="center"?o.left>0?"left":"right":n.x==="left"?"right":"left"),r==="shift"&&n.precedance==="y"&&o.top&&n.x!=="center"?n.precedance=n.precedance==="y"?"x":"y":r==="flip"&&o.top&&(n.y=n.y==="center"?o.top>0?"top":"bottom":n.y==="top"?"bottom":"top"),n.string()!==m.corner&&(m.top!==o.top||m.left!==o.left)&&i.update(n,e)),t=i.position(n,o),t.right!==c&&(t.left=-t.right),t.bottom!==c&&(t.top=-t.bottom),t.user=Math.max(0,j.offset);if(s.left=q==="shift"&&!!o.left)n.x==="center"?u["margin-left"]=s.x=t["margin-left"]-o.left:(v=t.right!==c?[o.left,-t.left]:[-o.left,t.left],(s.x=Math.max(v[0],v[1]))>v[0]&&(h.left-=o.left,s.left=e),u[t.right!==c?"right":"left"]=s.x);if(s.top=r==="shift"&&!!o.top)n.y==="center"?u["margin-top"]=s.y=t["margin-top"]-o.top:(v=t.bottom!==c?[o.top,-t.top]:[-o.top,t.top],(s.y=Math.max(v[0],v[1]))>v[0]&&(h.top-=o.top,s.top=e),u[t.bottom!==c?"bottom":"top"]=s.y);k.tip.css(u).toggle(!(s.x&&s.y||n.x==="center"&&s.y||n.y==="center"&&s.x)),h.left-=t.left.charAt?t.user:q!=="shift"||s.top||!s.left&&!s.top?t.left:0,h.top-=t.top.charAt?t.user:r!=="shift"||s.left||!s.left&&!s.top?t.top:0,m.left=o.left,m.top=o.top,m.corner=n.string()}}var i=this,j=b.options.style.tip,k=b.elements,l=k.tooltip,m={top:0,left:0,corner:""},n={width:j.width,height:j.height},o={},p=j.border||0,q=".qtip-tip",s=!!(a("<canvas />")[0]||{}).getContext;i.mimic=i.corner=f,i.border=p,i.offset=j.offset,i.size=n,b.checks.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){i.init()||i.destroy(),b.reposition()},"^style.tip.(height|width)$":function(){n={width:j.width,height:j.height},i.create(),i.update(),b.reposition()},"^content.title.text|style.(classes|widget)$":function(){k.tip&&i.update()}},a.extend(i,{init:function(){var b=i.detectCorner()&&(s||a.browser.msie);b&&(i.create(),i.update(),l.unbind(q).bind("tooltipmove"+q,t));return b},detectCorner:function(){var a=j.corner,c=b.options.position,f=c.at,g=c.my.string?c.my.string():c.my;if(a===e||g===e&&f===e)return e;a===d?i.corner=new h.Corner(g):a.string||(i.corner=new h.Corner(a),i.corner.fixed=d);return i.corner.string()!=="centercenter"},detectColours:function(){var c,d,e,f=k.tip.css({backgroundColor:"",border:""}),g=i.corner,h=g[g.precedance],m="border-"+h+"-color",p="border"+h.charAt(0)+h.substr(1)+"Color",q=/rgba?\(0, 0, 0(, 0)?\)|transparent/i,s="background-color",t="transparent",u=a(document.body).css("color"),v=b.elements.content.css("color"),w=k.titlebar&&(g.y==="top"||g.y==="center"&&f.position().top+n.height/2+j.offset<k.titlebar.outerHeight(1)),x=w?k.titlebar:k.content;l.addClass(r),o.fill=d=f.css(s),o.border=e=f[0].style[p]||f.css(m)||l.css(m);if(!d||q.test(d))o.fill=x.css(s)||t,q.test(o.fill)&&(o.fill=l.css(s)||d);if(!e||q.test(e)||e===u){o.border=x.css(m)||t;if(q.test(o.border)||o.border===v)o.border=e}a("*",f).add(f).css(s,t).css("border",""),l.removeClass(r)},create:function(){var b=n.width,c=n.height,d;k.tip&&k.tip.remove(),k.tip=a("<div />",{"class":"ui-tooltip-tip"}).css({width:b,height:c}).prependTo(l),s?a("<canvas />").appendTo(k.tip)[0].getContext("2d").save():(d='<vml:shape coordorigin="0,0" style="display:inline-block; position:absolute; behavior:url(#default#VML);"></vml:shape>',k.tip.html(d+d))},update:function(b,c){var g=k.tip,l=g.children(),m=n.width,q=n.height,r="px solid ",t="px dashed transparent",v=j.mimic,x=Math.round,y,z,B,C,D;b||(b=i.corner),v===e?v=b:(v=new h.Corner(v),v.precedance=b.precedance,v.x==="inherit"?v.x=b.x:v.y==="inherit"?v.y=b.y:v.x===v.y&&(v[b.precedance]=b[b.precedance])),y=v.precedance,i.detectColours(),o.border!=="transparent"&&o.border!=="#123456"?(p=u(b,f,d),j.border===0&&p>0&&(o.fill=o.border),i.border=p=j.border!==d?j.border:p):i.border=p=0,B=A(v,m,q),i.size=D=w(b),g.css(D),b.precedance==="y"?C=[x(v.x==="left"?p:v.x==="right"?D.width-m-p:(D.width-m)/2),x(v.y==="top"?D.height-q:0)]:C=[x(v.x==="left"?D.width-m:0),x(v.y==="top"?p:v.y==="bottom"?D.height-q-p:(D.height-q)/2)],s?(l.attr(D),z=l[0].getContext("2d"),z.restore(),z.save(),z.clearRect(0,0,3e3,3e3),z.translate(C[0],C[1]),z.beginPath(),z.moveTo(B[0][0],B[0][1]),z.lineTo(B[1][0],B[1][1]),z.lineTo(B[2][0],B[2][1]),z.closePath(),z.fillStyle=o.fill,z.strokeStyle=o.border,z.lineWidth=p*2,z.lineJoin="miter",z.miterLimit=100,p&&z.stroke(),z.fill()):(B="m"+B[0][0]+","+B[0][1]+" l"+B[1][0]+","+B[1][1]+" "+B[2][0]+","+B[2][1]+" xe",C[2]=p&&/^(r|b)/i.test(b.string())?parseFloat(a.browser.version,10)===8?2:1:0,l.css({antialias:""+(v.string().indexOf("center")>-1),left:C[0]-C[2]*Number(y==="x"),top:C[1]-C[2]*Number(y==="y"),width:m+p,height:q+p}).each(function(b){var c=a(this);c[c.prop?"prop":"attr"]({coordsize:m+p+" "+(q+p),path:B,fillcolor:o.fill,filled:!!b,stroked:!b}).css({display:p||b?"block":"none"}),!b&&c.html()===""&&c.html('<vml:stroke weight="'+p*2+'px" color="'+o.border+'" miterlimit="1000" joinstyle="miter" style="behavior:url(#default#VML); display:inline-block;" />')})),c!==e&&i.position(b)},position:function(b){var c=k.tip,f={},g=Math.max(0,j.offset),h,l,m;if(j.corner===e||!c)return e;b=b||i.corner,h=b.precedance,l=w(b),m=[b.x,b.y],h==="x"&&m.reverse(),a.each(m,function(a,c){var e,i;c==="center"?(e=h==="y"?"left":"top",f[e]="50%",f["margin-"+e]=-Math.round(l[h==="y"?"width":"height"]/2)+g):(e=u(b,c,d),i=v(b),f[c]=a?p?u(b,c):0:g+(i>e?i:0))}),f[b[h]]-=l[h==="x"?"width":"height"],c.css({top:"",bottom:"",left:"",right:"",margin:""}).css(f);return f},destroy:function(){k.tip&&k.tip.remove(),l.unbind(q)}}),i.init()}function A(a,b,c){var d=Math.ceil(b/2),e=Math.ceil(c/2),f={bottomright:[[0,0],[b,c],[b,0]],bottomleft:[[0,0],[b,0],[0,c]],topright:[[0,c],[b,0],[b,c]],topleft:[[0,0],[0,c],[b,c]],topcenter:[[0,c],[d,0],[b,c]],bottomcenter:[[0,0],[b,0],[d,c]],rightcenter:[[0,0],[b,e],[0,c]],leftcenter:[[b,0],[b,c],[0,e]]};f.lefttop=f.bottomright,f.righttop=f.bottomleft,f.leftbottom=f.topright,f.rightbottom=f.topleft;return f[a.string()]}function z(b,c){var i,j,k,l,m,n=a(this),o=a(document.body),p=this===document?o:n,q=n.metadata?n.metadata(c.metadata):f,r=c.metadata.type==="html5"&&q?q[c.metadata.name]:f,s=n.data(c.metadata.name||"qtipopts");try{s=typeof s==="string"?(new Function("return "+s))():s}catch(t){w("Unable to parse HTML5 attribute data: "+s)}l=a.extend(d,{},g.defaults,c,typeof s==="object"?x(s):f,x(r||q)),j=l.position,l.id=b;if("boolean"===typeof l.content.text){k=n.attr(l.content.attr);if(l.content.attr!==e&&k)l.content.text=k;else{w("Unable to locate content for tooltip! Aborting render of tooltip on element: ",n);return e}}j.container===e&&(j.container=o),j.target===e&&(j.target=p),l.show.target===e&&(l.show.target=p),l.show.solo===d&&(l.show.solo=o),l.hide.target===e&&(l.hide.target=p),l.position.viewport===d&&(l.position.viewport=j.container),j.at=new h.Corner(j.at),j.my=new h.Corner(j.my);if(a.data(this,"qtip"))if(l.overwrite)n.qtip("destroy");else if(l.overwrite===e)return e;l.suppress&&(m=a.attr(this,"title"))&&a(this).removeAttr("title").attr(u,m),i=new y(n,l,b,!!k),a.data(this,"qtip",i),n.bind("remove.qtip-"+b,function(){i.destroy()});return i}function y(s,t,w,y){function R(){var c=[t.show.target[0],t.hide.target[0],z.rendered&&G.tooltip[0],t.position.container[0],t.position.viewport[0],b,document];z.rendered?a([]).pushStack(a.grep(c,function(a){return typeof a==="object"})).unbind(F):t.show.target.unbind(F+"-create")}function Q(){function p(a){E.is(":visible")&&z.reposition(a)}function o(a){if(E.hasClass(m))return e;clearTimeout(z.timers.inactive),z.timers.inactive=setTimeout(function(){z.hide(a)},t.hide.inactive)}function l(b){if(E.hasClass(m)||C||D)return e;var d=a(b.relatedTarget||b.target),g=d.closest(n)[0]===E[0],h=d[0]===f.show[0];clearTimeout(z.timers.show),clearTimeout(z.timers.hide);if(c.target==="mouse"&&g||t.hide.fixed&&(/mouse(out|leave|move)/.test(b.type)&&(g||h)))try{b.preventDefault(),b.stopImmediatePropagation()}catch(i){}else t.hide.delay>0?z.timers.hide=setTimeout(function(){z.hide(b)},t.hide.delay):z.hide(b)}function k(a){if(E.hasClass(m))return e;f.show.trigger("qtip-"+w+"-inactive"),clearTimeout(z.timers.show),clearTimeout(z.timers.hide);var b=function(){z.toggle(d,a)};t.show.delay>0?z.timers.show=setTimeout(b,t.show.delay):b()}var c=t.position,f={show:t.show.target,hide:t.hide.target,viewport:a(c.viewport),document:a(document),window:a(b)},h={show:a.trim(""+t.show.event).split(" "),hide:a.trim(""+t.hide.event).split(" ")},j=a.browser.msie&&parseInt(a.browser.version,10)===6;E.bind("mouseenter"+F+" mouseleave"+F,function(a){var b=a.type==="mouseenter";b&&z.focus(a),E.toggleClass(q,b)}),t.hide.fixed&&(f.hide=f.hide.add(E),E.bind("mouseover"+F,function(){E.hasClass(m)||clearTimeout(z.timers.hide)})),/mouse(out|leave)/i.test(t.hide.event)?t.hide.leave==="window"&&f.window.bind("mouseout"+F,function(a){/select|option/.test(a.target)&&!a.relatedTarget&&z.hide(a)}):/mouse(over|enter)/i.test(t.show.event)&&f.hide.bind("mouseleave"+F,function(a){clearTimeout(z.timers.show)}),(""+t.hide.event).indexOf("unfocus")>-1&&f.document.bind("mousedown"+F,function(b){var c=a(b.target),d=!E.hasClass(m)&&E.is(":visible");c[0]!==E[0]&&c.parents(n).length===0&&c.add(s).length>1&&!c.is(":disabled")&&z.hide(b)}),"number"===typeof t.hide.inactive&&(f.show.bind("qtip-"+w+"-inactive",o),a.each(g.inactiveEvents,function(a,b){f.hide.add(G.tooltip).bind(b+F+"-inactive",o)})),a.each(h.hide,function(b,c){var d=a.inArray(c,h.show),e=a(f.hide);d>-1&&e.add(f.show).length===e.length||c==="unfocus"?(f.show.bind(c+F,function(a){E.is(":visible")?l(a):k(a)}),delete h.show[d]):f.hide.bind(c+F,l)}),a.each(h.show,function(a,b){f.show.bind(b+F,k)}),"number"===typeof t.hide.distance&&f.show.add(E).bind("mousemove"+F,function(a){var b=H.origin||{},c=t.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&z.hide(a)}),c.target==="mouse"&&(f.show.bind("mousemove"+F,function(a){i={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),c.adjust.mouse&&(t.hide.event&&E.bind("mouseleave"+F,function(a){(a.relatedTarget||a.target)!==f.show[0]&&z.hide(a)}),f.document.bind("mousemove"+F,function(a){!E.hasClass(m)&&E.is(":visible")&&z.reposition(a||i)}))),(c.adjust.resize||f.viewport.length)&&(a.event.special.resize?f.viewport:f.window).bind("resize"+F,p),(f.viewport.length||j&&E.css("position")==="fixed")&&f.viewport.bind("scroll"+F,p)}function P(b,d){function g(b){function i(c){c&&(delete h[c.src],clearTimeout(z.timers.img[c.src]),a(c).unbind(F)),a.isEmptyObject(h)&&(z.redraw(),d!==e&&z.reposition(H.event),b())}var g,h={};if((g=f.find("img:not([height]):not([width])")).length===0)return i();g.each(function(b,d){if(h[d.src]===c){var e=0,f=3;(function g(){if(d.height||d.width||e>f)return i(d);e+=1,z.timers.img[d.src]=setTimeout(g,700)})(),a(d).bind("error"+F+" load"+F,function(){i(this)}),h[d.src]=d}})}var f=G.content;if(!z.rendered||!b)return e;a.isFunction(b)&&(b=b.call(s,H.event,z)||""),b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),z.rendered<0?E.queue("fx",g):(D=0,g(a.noop));return z}function O(b,c){var d=G.title;if(!z.rendered||!b)return e;a.isFunction(b)&&(b=b.call(s,H.event,z));if(b===e)return K(e);b.jquery&&b.length>0?d.empty().append(b.css({display:"block"})):d.html(b),z.redraw(),c!==e&&z.rendered&&E.is(":visible")&&z.reposition(H.event)}function N(a){var b=G.button,c=G.title;if(!z.rendered)return e;a?(c||M(),L()):b.remove()}function M(){var b=B+"-title";G.titlebar&&K(),G.titlebar=a("<div />",{"class":k+"-titlebar "+(t.style.widget?"ui-widget-header":"")}).append(G.title=a("<div />",{id:b,"class":k+"-title","aria-atomic":d})).insertBefore(G.content).delegate(".ui-state-default","mousedown keydown mouseup keyup mouseout",function(b){a(this).toggleClass("ui-state-active ui-state-focus",b.type.substr(-4)==="down")}).delegate(".ui-state-default","mouseover mouseout",function(b){a(this).toggleClass("ui-state-hover",b.type==="mouseover")}),t.content.title.button?L():z.rendered&&z.redraw()}function L(){var b=t.content.title.button,c=typeof b==="string",d=c?b:"Close tooltip";G.button&&G.button.remove(),b.jquery?G.button=b:G.button=a("<a />",{"class":"ui-state-default "+(t.style.widget?"":k+"-icon"),title:d,"aria-label":d}).prepend(a("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),G.button.appendTo(G.titlebar).attr("role","button").click(function(a){E.hasClass(m)||z.hide(a);return e}),z.redraw()}function K(a){G.title&&(G.titlebar.remove(),G.titlebar=G.title=G.button=f,a!==e&&z.reposition())}function J(){var a=t.style.widget;E.toggleClass(l,a).toggleClass(o,!a),G.content.toggleClass(l+"-content",a),G.titlebar&&G.titlebar.toggleClass(l+"-header",a),G.button&&G.button.toggleClass(k+"-icon",!a)}function I(a){var b=0,c,d=t,e=a.split(".");while(d=d[e[b++]])b<e.length&&(c=d);return[c||t,e.pop()]}var z=this,A=document.body,B=k+"-"+w,C=0,D=0,E=a(),F=".qtip-"+w,G,H;z.id=w,z.rendered=e,z.elements=G={target:s},z.timers={img:{}},z.options=t,z.checks={},z.plugins={},z.cache=H={event:{},target:a(),disabled:e,attr:y},z.checks.builtin={"^id$":function(b,c,f){var h=f===d?g.nextid:f,i=k+"-"+h;h!==e&&h.length>0&&!a("#"+i).length&&(E[0].id=i,G.content[0].id=i+"-content",G.title[0].id=i+"-title")},"^content.text$":function(a,b,c){P(c)},"^content.title.text$":function(a,b,c){if(!c)return K();!G.title&&c&&M(),O(c)},"^content.title.button$":function(a,b,c){N(c)},"^position.(my|at)$":function(a,b,c){"string"===typeof c&&(a[b]=new h.Corner(c))},"^position.container$":function(a,b,c){z.rendered&&E.appendTo(c)},"^show.ready$":function(){z.rendered?z.toggle(d):z.render(1)},"^style.classes$":function(a,b,c){E.attr("class",k+" qtip ui-helper-reset "+c)},"^style.widget|content.title":J,"^events.(render|show|move|hide|focus|blur)$":function(b,c,d){E[(a.isFunction(d)?"":"un")+"bind"]("tooltip"+c,d)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var a=t.position;E.attr("tracking",a.target==="mouse"&&a.adjust.mouse),R(),Q()}},a.extend(z,{render:function(b){if(z.rendered)return z;var c=t.content.text,f=t.content.title.text,g=t.position,i=a.Event("tooltiprender");a.attr(s[0],"aria-describedby",B),E=G.tooltip=a("<div/>",{id:B,"class":k+" qtip ui-helper-reset "+o+" "+t.style.classes+" "+k+"-pos-"+t.position.my.abbreviation(),width:t.style.width||"",height:t.style.height||"",tracking:g.target==="mouse"&&g.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":e,"aria-describedby":B+"-content","aria-hidden":d}).toggleClass(m,H.disabled).data("qtip",z).appendTo(t.position.container).append(G.content=a("<div />",{"class":k+"-content",id:B+"-content","aria-atomic":d})),z.rendered=-1,C=D=1,f&&(M(),a.isFunction(f)||O(f,e)),a.isFunction(c)||P(c,e),z.rendered=d,J(),a.each(t.events,function(b,c){a.isFunction(c)&&E.bind(b==="toggle"?"tooltipshow tooltiphide":"tooltip"+b,c)}),a.each(h,function(){this.initialize==="render"&&this(z)}),Q(),E.queue("fx",function(a){i.originalEvent=H.event,E.trigger(i,[z]),C=D=0,z.redraw(),(t.show.ready||b)&&z.toggle(d,H.event),a()});return z},get:function(a){var b,c;switch(a.toLowerCase()){case"dimensions":b={height:E.outerHeight(),width:E.outerWidth()};break;case"offset":b=h.offset(E,t.position.container);break;default:c=I(a.toLowerCase()),b=c[0][c[1]],b=b.precedance?b.string():b}return b},set:function(b,c){function m(a,b){var c,d,e;for(c in k)for(d in k[c])if(e=(new RegExp(d,"i")).exec(a))b.push(e),k[c][d].apply(z,b)}var g=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,h=/^content\.(title|attr)|style/i,i=e,j=e,k=z.checks,l;"string"===typeof b?(l=b,b={},b[l]=c):b=a.extend(d,{},b),a.each(b,function(c,d){var e=I(c.toLowerCase()),f;f=e[0][e[1]],e[0][e[1]]="object"===typeof d&&d.nodeType?a(d):d,b[c]=[e[0],e[1],d,f],i=g.test(c)||i,j=h.test(c)||j}),x(t),C=D=1,a.each(b,m),C=D=0,E.is(":visible")&&z.rendered&&(i&&z.reposition(t.position.target==="mouse"?f:H.event),j&&z.redraw());return z},toggle:function(b,c){function q(){b?(a.browser.msie&&E[0].style.removeAttribute("filter"),E.css("overflow",""),"string"===typeof h.autofocus&&a(h.autofocus,E).focus(),p=a.Event("tooltipvisible"),p.originalEvent=c?H.event:f,E.trigger(p,[z])):E.css({display:"",visibility:"",opacity:"",left:"",top:""})}if(!z.rendered)if(b)z.render(0);else return z;var g=b?"show":"hide",h=t[g],j=E.is(":visible"),k=!c||t[g].target.length<2||H.target[0]===c.target,l=t.position,m=t.content,o,p;(typeof b).search("boolean|number")&&(b=!j);if(!E.is(":animated")&&j===b&&k)return z;if(c){if(/over|enter/.test(c.type)&&/out|leave/.test(H.event.type)&&c.target===t.show.target[0]&&E.has(c.relatedTarget).length)return z;H.event=a.extend({},c)}p=a.Event("tooltip"+g),p.originalEvent=c?H.event:f,E.trigger(p,[z,90]);if(p.isDefaultPrevented())return z;a.attr(E[0],"aria-hidden",!b),b?(H.origin=a.extend({},i),z.focus(c),a.isFunction(m.text)&&P(m.text,e),a.isFunction(m.title.text)&&O(m.title.text,e),!v&&l.target==="mouse"&&l.adjust.mouse&&(a(document).bind("mousemove.qtip",function(a){i={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),v=d),z.reposition(c),(p.solo=!!h.solo)&&a(n,h.solo).not(E).qtip("hide",p)):(clearTimeout(z.timers.show),delete H.origin,v&&!a(n+'[tracking="true"]:visible',h.solo).not(E).length&&(a(document).unbind("mousemove.qtip"),v=e),z.blur(c)),k&&E.stop(0,1),h.effect===e?(E[g](),q.call(E)):a.isFunction(h.effect)?(h.effect.call(E,z),E.queue("fx",function(a){q(),a()})):E.fadeTo(90,b?1:0,q),b&&h.target.trigger("qtip-"+w+"-inactive");return z},show:function(a){return z.toggle(d,a)},hide:function(a){return z.toggle(e,a)},focus:function(b){if(!z.rendered)return z;var c=a(n),d=parseInt(E[0].style.zIndex,10),e=g.zindex+c.length,f=a.extend({},b),h,i;E.hasClass(p)||(i=a.Event("tooltipfocus"),i.originalEvent=f,E.trigger(i,[z,e]),i.isDefaultPrevented()||(d!==e&&(c.each(function(){this.style.zIndex>d&&(this.style.zIndex=this.style.zIndex-1)}),c.filter("."+p).qtip("blur",f)),E.addClass(p)[0].style.zIndex=e));return z},blur:function(b){var c=a.extend({},b),d;E.removeClass(p),d=a.Event("tooltipblur"),d.originalEvent=c,E.trigger(d,[z]);return z},reposition:function(c,d){if(!z.rendered||C)return z;C=1;var f=t.position.target,g=t.position,j=g.my,l=g.at,m=g.adjust,n=m.method.split(" "),o=E.outerWidth(),p=E.outerHeight(),q=0,r=0,s=a.Event("tooltipmove"),u=E.css("position")==="fixed",v=g.viewport,w={left:0,top:0},x=e,y=z.plugins.tip,B={horizontal:n[0],vertical:n[1]=n[1]||n[0],enabled:v.jquery&&f[0]!==b&&f[0]!==A&&m.method!=="none",left:function(a){var b=B.horizontal==="shift",c=v.offset.left+v.scrollLeft,d=j.x==="left"?o:j.x==="right"?-o:-o/2,e=l.x==="left"?q:l.x==="right"?-q:-q/2,f=y&&y.size?y.size.width||0:0,g=y&&y.corner&&y.corner.precedance==="x"&&!b?f:0,h=c-a+g,i=a+o-v.width-c+g,k=d-(j.precedance==="x"||j.x===j.y?e:0),n=j.x==="center";b?(g=y&&y.corner&&y.corner.precedance==="y"?f:0,k=(j.x==="left"?1:-1)*d-g,w.left+=h>0?h:i>0?-i:0,w.left=Math.max(v.offset.left+(g&&y.corner.x==="center"?y.offset:0),a-k,Math.min(Math.max(v.offset.left+v.width,a+k),w.left))):(h>0&&(j.x!=="left"||i>0)?w.left-=k:i>0&&(j.x!=="right"||h>0)&&(w.left-=n?-k:k),w.left!==a&&n&&(w.left-=m.x),w.left<c&&-w.left>i&&(w.left=a));return w.left-a},top:function(a){var b=B.vertical==="shift",c=v.offset.top+v.scrollTop,d=j.y==="top"?p:j.y==="bottom"?-p:-p/2,e=l.y==="top"?r:l.y==="bottom"?-r:-r/2,f=y&&y.size?y.size.height||0:0,g=y&&y.corner&&y.corner.precedance==="y"&&!b?f:0,h=c-a+g,i=a+p-v.height-c+g,k=d-(j.precedance==="y"||j.x===j.y?e:0),n=j.y==="center";b?(g=y&&y.corner&&y.corner.precedance==="x"?f:0,k=(j.y==="top"?1:-1)*d-g,w.top+=h>0?h:i>0?-i:0,w.top=Math.max(v.offset.top+(g&&y.corner.x==="center"?y.offset:0),a-k,Math.min(Math.max(v.offset.top+v.height,a+k),w.top))):(h>0&&(j.y!=="top"||i>0)?w.top-=k:i>0&&(j.y!=="bottom"||h>0)&&(w.top-=n?-k:k),w.top!==a&&n&&(w.top-=m.y),w.top<0&&-w.top>i&&(w.top=a));return w.top-a}},D;if(a.isArray(f)&&f.length===2)l={x:"left",y:"top"},w={left:f[0],top:f[1]};else if(f==="mouse"&&(c&&c.pageX||H.event.pageX))l={x:"left",y:"top"},c=(c&&(c.type==="resize"||c.type==="scroll")?H.event:c&&c.pageX&&c.type==="mousemove"?c:i&&i.pageX&&(m.mouse||!c||!c.pageX)?{pageX:i.pageX,pageY:i.pageY}:!m.mouse&&H.origin&&H.origin.pageX?H.origin:c)||c||H.event||i||{},w={top:c.pageY,left:c.pageX};else{f==="event"?c&&c.target&&c.type!=="scroll"&&c.type!=="resize"?f=H.target=a(c.target):f=H.target:H.target=a(f),f=a(f).eq(0);if(f.length===0)return z;f[0]===document||f[0]===b?(q=h.iOS?b.innerWidth:f.width(),r=h.iOS?b.innerHeight:f.height(),f[0]===b&&(w={top:!u||h.iOS?(v||f).scrollTop():0,left:!u||h.iOS?(v||f).scrollLeft():0})):f.is("area")&&h.imagemap?w=h.imagemap(f,l,B.enabled?n:e):f[0].namespaceURI==="http://www.w3.org/2000/svg"&&h.svg?w=h.svg(f,l):(q=f.outerWidth(),r=f.outerHeight(),w=h.offset(f,g.container)),w.offset&&(q=w.width,r=w.height,x=w.flipoffset,w=w.offset);if(h.iOS<4.1&&h.iOS>3.1||h.iOS==4.3||!h.iOS&&u)D=a(b),w.left-=D.scrollLeft(),w.top-=D.scrollTop();w.left+=l.x==="right"?q:l.x==="center"?q/2:0,w.top+=l.y==="bottom"?r:l.y==="center"?r/2:0}w.left+=m.x+(j.x==="right"?-o:j.x==="center"?-o/2:0),w.top+=m.y+(j.y==="bottom"?-p:j.y==="center"?-p/2:0),B.enabled?(v={elem:v,height:v[(v[0]===b?"h":"outerH")+"eight"](),width:v[(v[0]===b?"w":"outerW")+"idth"](),scrollLeft:u?0:v.scrollLeft(),scrollTop:u?0:v.scrollTop(),offset:v.offset()||{left:0,top:0}},w.adjusted={left:B.horizontal!=="none"?B.left(w.left):0,top:B.vertical!=="none"?B.top(w.top):0},w.adjusted.left+w.adjusted.top&&E.attr("class",function(a,b){return b.replace(/ui-tooltip-pos-\w+/i,k+"-pos-"+j.abbreviation())}),x&&w.adjusted.left&&(w.left+=x.left),x&&w.adjusted.top&&(w.top+=x.top)):w.adjusted={left:0,top:0},s.originalEvent=a.extend({},c),E.trigger(s,[z,w,v.elem||v]);if(s.isDefaultPrevented())return z;delete w.adjusted,d===e||isNaN(w.left)||isNaN(w.top)||f==="mouse"||!a.isFunction(g.effect)?E.css(w):a.isFunction(g.effect)&&(g.effect.call(E,z,a.extend({},w)),E.queue(function(b){a(this).css({opacity:"",height:""}),a.browser.msie&&this.style.removeAttribute("filter"),b()})),C=0;return z},redraw:function(){if(z.rendered<1||D)return z;var a=t.position.container,b,c,d,e;D=1,t.style.height&&E.css("height",t.style.height),t.style.width?E.css("width",t.style.width):(E.css("width","").addClass(r),c=E.width()+1,d=E.css("max-width")||"",e=E.css("min-width")||"",b=(d+e).indexOf("%")>-1?a.width()/100:0,d=(d.indexOf("%")>-1?b:1)*parseInt(d,10)||c,e=(e.indexOf("%")>-1?b:1)*parseInt(e,10)||0,c=d+e?Math.min(Math.max(c,e),d):c,E.css("width",Math.round(c)).removeClass(r)),D=0;return z},disable:function(b){"boolean"!==typeof b&&(b=!E.hasClass(m)&&!H.disabled),z.rendered?(E.toggleClass(m,b),a.attr(E[0],"aria-disabled",b)):H.disabled=!!b;return z},enable:function(){return z.disable(e)},destroy:function(){var b=s[0],c=a.attr(b,u),d=s.data("qtip");z.rendered&&(E.remove(),a.each(z.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(z.timers.show),clearTimeout(z.timers.hide),R();if(!d||z===d)a.removeData(b,"qtip"),t.suppress&&c&&(a.attr(b,"title",c),s.removeAttr(u)),s.removeAttr("aria-describedby");s.unbind(".qtip-"+w),delete j[z.id];return s}})}function x(b){var c;if(!b||"object"!==typeof b)return e;"object"!==typeof b.metadata&&(b.metadata={type:b.metadata});if("content"in b){if("object"!==typeof b.content||b.content.jquery)b.content={text:b.content};c=b.content.text||e,!a.isFunction(c)&&(!c&&!c.attr||c.length<1||"object"===typeof c&&!c.jquery)&&(b.content.text=e),"title"in b.content&&("object"!==typeof b.content.title&&(b.content.title={text:b.content.title}),c=b.content.title.text||e,!a.isFunction(c)&&(!c&&!c.attr||c.length<1||"object"===typeof c&&!c.jquery)&&(b.content.title.text=e))}"position"in b&&("object"!==typeof b.position&&(b.position={my:b.position,at:b.position})),"show"in b&&("object"!==typeof b.show&&(b.show.jquery?b.show={target:b.show}:b.show={event:b.show})),"hide"in b&&("object"!==typeof b.hide&&(b.hide.jquery?b.hide={target:b.hide}:b.hide={event:b.hide})),"style"in b&&("object"!==typeof b.style&&(b.style={classes:b.style})),a.each(h,function(){this.sanitize&&this.sanitize(b)});return b}function w(){w.history=w.history||[],w.history.push(arguments);if("object"===typeof console){var a=console[console.warn?"warn":"log"],b=Array.prototype.slice.call(arguments),c;typeof arguments[0]==="string"&&(b[0]="qTip2: "+b[0]),c=a.apply?a.apply(console,b):a(b)}}"use strict";var d=!0,e=!1,f=null,g,h,i,j={},k="ui-tooltip",l="ui-widget",m="ui-state-disabled",n="div.qtip."+k,o=k+"-default",p=k+"-focus",q=k+"-hover",r=k+"-fluid",s="-31000px",t="_replacedByqTip",u="oldtitle",v;g=a.fn.qtip=function(b,h,i){var j=(""+b).toLowerCase(),k=f,l=j==="disable"?[d]:a.makeArray(arguments).slice(1),m=l[l.length-1],n=this[0]?a.data(this[0],"qtip"):f;if(!arguments.length&&n||j==="api")return n;if("string"===typeof b){this.each(function(){var b=a.data(this,"qtip");if(!b)return d;m&&m.timeStamp&&(b.cache.event=m);if(j!=="option"&&j!=="options"||!h)b[j]&&b[j].apply(b[j],l);else if(a.isPlainObject(h)||i!==c)b.set(h,i);else{k=b.get(h);return e}});return k!==f?k:this}if("object"===typeof b||!arguments.length){n=x(a.extend(d,{},b));return g.bind.call(this,n,m)}},g.bind=function(b,f){return this.each(function(k){function r(b){function d(){p.render(typeof b==="object"||l.show.ready),m.show.add(m.hide).unbind(o)}if(p.cache.disabled)return e;p.cache.event=a.extend({},b),p.cache.target=b?a(b.target):[c],l.show.delay>0?(clearTimeout(p.timers.show),p.timers.show=setTimeout(d,l.show.delay),n.show!==n.hide&&m.hide.bind(n.hide,function(){clearTimeout(p.timers.show)})):d()}var l,m,n,o,p,q;q=a.isArray(b.id)?b.id[k]:b.id,q=!q||q===e||q.length<1||j[q]?g.nextid++:j[q]=q,o=".qtip-"+q+"-create",p=z.call(this,q,b);if(p===e)return d;l=p.options,a.each(h,function(){this.initialize==="initialize"&&this(p)}),m={show:l.show.target,hide:l.hide.target},n={show:a.trim(""+l.show.event).replace(/ /g,o+" ")+o,hide:a.trim(""+l.hide.event).replace(/ /g,o+" ")+o},/mouse(over|enter)/i.test(n.show)&&!/mouse(out|leave)/i.test(n.hide)&&(n.hide+=" mouseleave"+o),m.show.bind("mousemove"+o,function(a){i={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),m.show.bind(n.show,r),(l.show.ready||l.prerender)&&r(f)})},h=g.plugins={Corner:function(a){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,"center").toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase(),this.precedance=a.charAt(0).search(/^(t|b)/)>-1?"y":"x",this.string=function(){return this.precedance==="y"?this.y+this.x:this.x+this.y},this.abbreviation=function(){var a=this.x.substr(0,1),b=this.y.substr(0,1);return a===b?a:a==="c"||a!=="c"&&b!=="c"?b+a:a+b}},offset:function(a,b){function i(a,b){c.left+=b*a.scrollLeft(),c.top+=b*a.scrollTop()}var c=a.offset(),d=b,e=0,f=document.body,g,h;if(d){do{d.css("position")!=="static"&&(g=d[0]===f?{left:parseInt(d.css("left"),10)||0,top:parseInt(d.css("top"),10)||0}:d.position(),c.left-=g.left+(parseInt(d.css("borderLeftWidth"),10)||0)+(parseInt(d.css("marginLeft"),10)||0),c.top-=g.top+(parseInt(d.css("borderTopWidth"),10)||0),h=d.css("overflow"),(h==="scroll"||h==="auto")&&++e);if(d[0]===f)break}while(d=d.offsetParent());b[0]!==f&&e&&i(b,1)}return c},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,3})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_","."))||e,fn:{attr:function(b,c){if(this.length){var d=this[0],e="title",f=a.data(d,"qtip");if(b===e&&f&&"object"===typeof f&&f.options.suppress){if(arguments.length<2)return a.attr(d,u);f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",c);return this.attr(u,c)}}return a.fn["attr"+t].apply(this,arguments)},clone:function(b){var c=a([]),d="title",e=a.fn["clone"+t].apply(this,arguments);b||e.filter("["+u+"]").attr("title",function(){return a.attr(this,u)}).removeAttr(u);return e},remove:a.ui?f:function(b,c){a(this).each(function(){c||(!b||a.filter(b,[this]).length)&&a("*",this).add(this).each(function(){a(this).triggerHandler("remove")})})}}},a.each(h.fn,function(b,c){if(!c||a.fn[b+t])return d;var e=a.fn[b+t]=a.fn[b];a.fn[b]=function(){return c.apply(this,arguments)||e.apply(this,arguments)}}),g.version="nightly",g.nextid=0,g.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),g.zindex=15e3,g.defaults={prerender:e,id:e,overwrite:d,suppress:d,content:{text:d,attr:"title",title:{text:e,button:e}},position:{my:"top left",at:"bottom right",target:e,container:e,viewport:e,adjust:{x:0,y:0,mouse:d,resize:d,method:"flip flip"},effect:function(b,c,d){a(this).animate(c,{duration:200,queue:e})}},show:{target:e,event:"mouseenter",effect:d,delay:90,solo:e,ready:e,autofocus:e},hide:{target:e,event:"mouseleave",effect:d,delay:0,fixed:e,inactive:e,leave:"window",distance:e},style:{classes:"",widget:e,width:e,height:e},events:{render:f,move:f,show:f,hide:f,toggle:f,visible:f,focus:f,blur:f}},h.tip=function(a){var b=a.plugins.tip;return"object"===typeof b?b:a.plugins.tip=new B(a)},h.tip.initialize="render",h.tip.sanitize=function(a){var b=a.style,c;b&&"tip"in b&&(c=a.style.tip,typeof c!=="object"&&(a.style.tip={corner:c}),/string|boolean/i.test(typeof c.corner)||(c.corner=d),typeof c.width!=="number"&&delete c.width,typeof c.height!=="number"&&delete c.height,typeof c.border!=="number"&&c.border!==d&&delete c.border,typeof c.offset!=="number"&&delete c.offset)},a.extend(d,g.defaults,{style:{tip:{corner:d,mimic:e,width:6,height:6,border:d,offset:0}}}),h.svg=function(b,c){var d=a(document),e=b[0],f={width:0,height:0,offset:{top:1e10,left:1e10}},g,h,i,j,k;if(e.getBBox&&e.parentNode){g=e.getBBox(),h=e.getScreenCTM(),i=e.farthestViewportElement||e;if(!i.createSVGPoint)return f;j=i.createSVGPoint(),j.x=g.x,j.y=g.y,k=j.matrixTransform(h),f.offset.left=k.x,f.offset.top=k.y,j.x+=g.width,j.y+=g.height,k=j.matrixTransform(h),f.width=k.x-f.offset.left,f.height=k.y-f.offset.top,f.offset.left+=d.scrollLeft(),f.offset.top+=d.scrollTop()}return f}})(jQuery,window)
license.txt ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
logo.gif ADDED
Binary file
p3-profiler.php ADDED
@@ -0,0 +1,798 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: P3 (Plugin Performance Profiler)
4
+ Plugin URI: http://support.godaddy.com/godaddy/wordpress-p3-plugin/
5
+ Description: See which plugins are slowing down your site. Create a profile of your WordPress site's plugins' performance by measuring their impact on your site's load time.
6
+ Author: GoDaddy.com
7
+ Version: 1.0
8
+ Author URI: http://www.godaddy.com/
9
+ */
10
+
11
+ /**************************************************************************/
12
+ /** PACKAGE CONSTANTS **/
13
+ /**************************************************************************/
14
+
15
+ // Shortcut for knowing our path
16
+ define( 'P3_PATH', realpath( dirname( __FILE__ ) ) );
17
+
18
+ // Flag file for enabling profile mode
19
+ define( 'P3_FLAG_FILE', P3_PATH . DIRECTORY_SEPARATOR . '.profiling_enabled' );
20
+
21
+ // Directory for profiles
22
+ $uploads_dir = wp_upload_dir();
23
+ define( 'P3_PROFILES_PATH', $uploads_dir['basedir'] . DIRECTORY_SEPARATOR . 'profiles' );
24
+
25
+
26
+ /**************************************************************************/
27
+ /** START PROFILING **/
28
+ /**************************************************************************/
29
+
30
+ // Start profiling. If it's already been started, this line won't do anything
31
+ require_once P3_PATH . '/start-profile.php';
32
+
33
+
34
+ /**************************************************************************/
35
+ /** PLUGIN HOOKS **/
36
+ /**************************************************************************/
37
+
38
+ // Global plugin object
39
+ $p3_profiler_plugin = new P3_Profiler_Plugin();
40
+
41
+ // Admin hooks
42
+ if ( is_admin() ) {
43
+ // Show the 'Profiler' option under the 'Plugins' menu
44
+ add_action( 'admin_menu', array( $p3_profiler_plugin, 'settings_menu' ) );
45
+
46
+ // Ajax actions
47
+ add_action( 'wp_ajax_p3_start_scan', array( $p3_profiler_plugin, 'ajax_start_scan' ) );
48
+ add_action( 'wp_ajax_p3_stop_scan', array( $p3_profiler_plugin, 'ajax_stop_scan' ) );
49
+ add_action( 'wp_ajax_p3_send_results', array( $p3_profiler_plugin, 'ajax_send_results' ) );
50
+
51
+ // Show any notices
52
+ add_action( 'admin_notices', array( $p3_profiler_plugin, 'show_notices' ) );
53
+
54
+ // Early init actions ( processing bulk table actions, loading libraries, etc.)
55
+ add_action( 'admin_head', array( $p3_profiler_plugin, 'early_init' ) );
56
+ }
57
+
58
+ // Remove the admin bar when in profiling mode
59
+ if ( defined( 'WPP_PROFILING_STARTED' ) || isset( $_GET['P3_HIDE_ADMIN_BAR'] ) ) {
60
+ add_action( 'plugins_loaded', array( $p3_profiler_plugin, 'remove_admin_bar' ) );
61
+ }
62
+
63
+ // Install / uninstall hooks
64
+ register_activation_hook( P3_PATH . DIRECTORY_SEPARATOR . 'p3-profiler.php', array( $p3_profiler_plugin, 'activate' ) );
65
+ register_deactivation_hook( P3_PATH . DIRECTORY_SEPARATOR . 'p3-profiler.php', array( $p3_profiler_plugin, 'deactivate' ) );
66
+ register_uninstall_hook( P3_PATH . DIRECTORY_SEPARATOR . 'p3-profiler.php', array( 'P3_Profiler_Plugin', 'uninstall' ) );
67
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
68
+ add_action( 'wpmu_add_blog', array( $p3_profiler_plugin, 'sync_profiles_folder' ) );
69
+ add_action( 'wpmu_delete_blog', array( $p3_profiler_plugin, 'sync_profiles_folder' ) );
70
+ }
71
+
72
+ /**
73
+ * P3 Plugin Performance Profiler Plugin Controller
74
+ *
75
+ * @author GoDaddy.com
76
+ * @version 1.0
77
+ * @package P3_Profiler
78
+ */
79
+ class P3_Profiler_Plugin {
80
+
81
+ /**
82
+ * List table of the profile scans
83
+ * @var P3_Profile_Table
84
+ */
85
+ public $scan_table = null;
86
+
87
+ /**
88
+ * Remove the admin bar from the customer site when profiling is enabled
89
+ * to prevent skewing the numbers, as much as possible. Also prevent ssl
90
+ * warnings by forcing content into ssl mode if the admin is in ssl mode
91
+ * @return void
92
+ */
93
+ public function remove_admin_bar() {
94
+ if ( !is_admin() ) {
95
+ remove_action( 'wp_footer', 'wp_admin_bar_render', 1000 );
96
+ if ( true === force_ssl_admin() ) {
97
+ add_filter( 'site_url', array( $this, '_fix_url' ) );
98
+ add_filter( 'admin_url', array( $this, '_fix_url' ) );
99
+ add_filter( 'post_link', array( $this, '_fix_url' ) );
100
+ add_filter( 'category_link', array( $this, '_fix_url' ) );
101
+ add_filter( 'get_archives_link', array( $this, '_fix_url' ) );
102
+ add_filter( 'tag_link', array( $this, '_fix_url' ) );
103
+ add_filter( 'home_url', array( $this, '_fix_url' ) );
104
+ }
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Replace http with https to avoid SSL warnings in the preview iframe if the admin is in SSL
110
+ * This will strip off any port numbers and will not replace URLs in off-site links
111
+ * @param string $url
112
+ * @return string
113
+ */
114
+ public function _fix_url( $url ) {
115
+ static $host = '';
116
+ if ( empty( $host ) ) {
117
+ $host = preg_replace( '/[:\d+$]/', '', $_SERVER['HTTP_HOST'] );
118
+ }
119
+ return str_ireplace( 'http://' . $host, 'https://' . $host, $url );
120
+ }
121
+
122
+ /**
123
+ * Add the 'Profiler' option under the 'Plugins' menu
124
+ * @return void
125
+ */
126
+ public function settings_menu() {
127
+ if ( function_exists( 'add_submenu_page' ) ) {
128
+ $page = add_submenu_page(
129
+ 'tools.php',
130
+ 'P3 Plugin Profiler',
131
+ 'P3 Plugin Profiler',
132
+ 'manage_options',
133
+ basename( __FILE__ ),
134
+ array( $this, 'dispatcher' )
135
+ );
136
+ add_action( 'load-' . $page, array( $this, 'load_libraries' ) );
137
+ add_action( 'admin_print_scripts-' . $page, array( $this, 'load_scripts' ) );
138
+ add_action( 'admin_print_styles-' . $page, array( $this, 'load_styles' ) );
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Load the necessary resources
144
+ * @uses wp_enqueue_script
145
+ * @uses jquery, jquery-ui, jquery.corners
146
+ * @uses flot, flot.pie
147
+ * @return void
148
+ */
149
+ public function load_libraries() {
150
+
151
+ // Load php libraries libraries
152
+ include_once P3_PATH . '/class.p3-profile-table-sorter.php';
153
+ include_once P3_PATH . '/class.p3-profile-table.php';
154
+ include_once P3_PATH . '/class.p3-profile-reader.php';
155
+ }
156
+
157
+ /**
158
+ * Load javascripts
159
+ * @uses wp_enqueue_script
160
+ * @uses jquery, jquery-ui, jquery.corners
161
+ * @uses flot, flot.pie
162
+ * @return void
163
+ */
164
+ public function load_scripts() {
165
+ wp_enqueue_script( 'jquery' );
166
+ wp_enqueue_script( 'jquery-ui-dialog' );
167
+ wp_enqueue_script( 'jquery-ui-tabs' );
168
+ wp_enqueue_script( 'jquery-ui-progressbar' );
169
+ wp_enqueue_script( 'flot', plugins_url() . '/p3-profiler/js/jquery.flot.min.js', array( 'jquery-ui-core' ) );
170
+ wp_enqueue_script( 'flot.pie', plugins_url() . '/p3-profiler/js/jquery.flot.pie.min.js', array( 'flot' ) );
171
+ wp_enqueue_script( 'flot.navigate', plugins_url() . '/p3-profiler/js/jquery.flot.navigate.js', array( 'flot' ) );
172
+ wp_enqueue_script( 'p3_corners', plugins_url() . '/p3-profiler/js/jquery.corner.js', array( 'jquery-ui-core' ) );
173
+ wp_enqueue_script( 'p3_qtip', plugins_url() . '/p3-profiler/js/jquery.qtip.min.js', array( 'jquery-ui-core' ) );
174
+ }
175
+
176
+ /**
177
+ * Load styles
178
+ * @uses wp_enqueue_style
179
+ * @uses jquery-ui
180
+ * @return void
181
+ */
182
+ public function load_styles() {
183
+ wp_enqueue_style( 'p3_jquery_ui_css', plugins_url() . '/p3-profiler/css/custom-theme/jquery-ui-1.8.16.custom.css' );
184
+ wp_enqueue_style( 'p3_qtip_css', plugins_url() . '/p3-profiler/css/jquery.qtip.min.css' );
185
+ wp_enqueue_style( 'p3_css', plugins_url() . '/p3-profiler/css/p3.css' );
186
+ }
187
+
188
+ /**
189
+ * Load the necessary resources
190
+ * @uses wp_enqueue_script
191
+ * @uses jquery, jquery-ui, jquery.corners
192
+ * @uses flot, flot.pie
193
+ * @return void
194
+ */
195
+ public function early_init() {
196
+
197
+ // Only for our page
198
+ if ( isset( $_REQUEST['page'] ) && basename( __FILE__ ) == $_REQUEST['page'] ) {
199
+ // Load the list table, let it handle any bulk actions
200
+ $this->scan_table = new P3_Profile_Table();
201
+ $this->scan_table->prepare_items();
202
+
203
+ // Usability message
204
+ if ( !defined( 'WPP_PROFILING_STARTED' ) ) {
205
+ $this->add_notice( 'Click "Start Scan" to run a performance scan of your website.' );
206
+ }
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Dispatcher function. All requests enter through here
212
+ * and are routed based upon the p3_action request variable
213
+ * @uses $_REQUEST['p3_action']
214
+ * @return void
215
+ */
216
+ public function dispatcher() {
217
+ $p3_action = '';
218
+ if ( ! empty ( $_REQUEST ['p3_action'] ) ) {
219
+ $p3_action = $_REQUEST ['p3_action'];
220
+ }
221
+ switch ( $p3_action ) {
222
+ case 'list-scans' :
223
+ $this->list_scans();
224
+ break;
225
+ case 'view-scan' :
226
+ $this->view_scan();
227
+ break;
228
+ case 'start-scan' :
229
+ $this->start_scan();
230
+ break;
231
+ case 'fix-flag-file' :
232
+ $this->fix_flag_file();
233
+ break;
234
+ case 'help' :
235
+ $this->show_help();
236
+ break;
237
+ default :
238
+ $this->scan_settings_page();
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Write .profiling_enabled file, uses request_filesystem_credentials, if
244
+ * necessary, to create the file and make it writable
245
+ * @return void
246
+ */
247
+ public function fix_flag_file() {
248
+
249
+ // Don't force a specific file system method
250
+ $method = '';
251
+
252
+ // Define any extra pass-thru fields (none)
253
+ $form_fields = array();
254
+
255
+ // Define the URL to post back to (this one)
256
+ $url = wp_nonce_url( add_query_arg( array( 'p3_action' => 'fix-flag-file' ) ), 'p3-fix-flag-file' );
257
+
258
+ // Ask for credentials, if necessary
259
+ if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $form_fields ) ) ) {
260
+ return true;
261
+ } elseif ( ! WP_Filesystem($creds) ) {
262
+ // The credentials are bad, ask again
263
+ request_filesystem_credentials( $url, $method, true, false, $form_fields );
264
+ return true;
265
+ } else {
266
+ // Once we get here, we should have credentials, do the file system operations
267
+ global $wp_filesystem;
268
+ if ( $wp_filesystem->put_contents( $wp_filesystem->wp_plugins_dir() . '/p3-profiler/.profiling_enabled' , '[]', FS_CHMOD_FILE | 0222) ) {
269
+ include_once P3_PATH . '/templates/template.php';
270
+ } else {
271
+ wp_die( 'Error saving file!' );
272
+ }
273
+ }
274
+ }
275
+
276
+ /**
277
+ * Get a list of pages for the auto-scanner
278
+ * @return array
279
+ */
280
+ public function list_of_pages() {
281
+
282
+ // Start off the scan with the home page
283
+ $pages = array( get_home_url() ); // Home page
284
+
285
+ // Get the default RSS feed
286
+ $pages[] = get_feed_link();
287
+
288
+ // Search for 'e'
289
+ $pages[] = home_url( '?s=e' );
290
+
291
+ // Get the latest 10 posts
292
+ $tmp = preg_split( '/\s+/', wp_get_archives( 'type=postbypost&limit=10&echo=0' ) );
293
+ if ( !empty( $tmp ) ) {
294
+ foreach ( $tmp as $page ) {
295
+ if ( preg_match( "/href='([^']+)'/", $page, $matches ) ) {
296
+ $pages[] = $matches[1];
297
+ }
298
+ }
299
+ }
300
+
301
+ // Fix SSL
302
+ if ( true === force_ssl_admin() ) {
303
+ foreach ( $pages as $k => $v ) {
304
+ $pages[$k] = str_replace( 'http://', 'https://', $v );
305
+ }
306
+ }
307
+
308
+ // Done
309
+ return $pages;
310
+ }
311
+
312
+ /**************************************************************/
313
+ /** AJAX FUNCTIONS **/
314
+ /**************************************************************/
315
+
316
+ /**
317
+ * Start scan
318
+ * @return void
319
+ */
320
+ public function ajax_start_scan() {
321
+
322
+ // Check nonce
323
+ if ( !wp_verify_nonce( $_POST ['p3_nonce'], 'p3_ajax_start_scan' ) ) {
324
+ wp_die( 'Invalid nonce' );
325
+ }
326
+
327
+ // Sanitize the file name
328
+ $filename = sanitize_file_name( basename( $_POST['p3_scan_name'] ) );
329
+
330
+ // Create flag file
331
+ if ( file_exists( P3_FLAG_FILE ) ) {
332
+ $json = json_decode( file_get_contents( P3_FLAG_FILE ) );
333
+ } else {
334
+ $json = array();
335
+ }
336
+
337
+ // Site url
338
+ $site_url = parse_url( get_home_url(), PHP_URL_PATH );
339
+ if ( null === $site_url ) {
340
+ $site_url = '/';
341
+ }
342
+
343
+ // Add the entry ( multisite installs can run more than one concurrent profile )
344
+ $json[] = array(
345
+ 'ip' => $_POST['p3_ip'],
346
+ 'disable_opcode_cache' => ( 'true' == $_POST['p3_disable_opcode_cache'] ),
347
+ 'site_url' => $site_url,
348
+ 'name' => $filename,
349
+ );
350
+
351
+ $flag1 = file_put_contents( P3_FLAG_FILE, json_encode( $json ) );
352
+
353
+ // Kick start the profile file
354
+ if ( !file_exists( P3_PROFILES_PATH . "/$filename.json" ) ) {
355
+ $flag2 = file_put_contents( P3_PROFILES_PATH . "/$filename.json", '' );
356
+ } else {
357
+ $flag2 = true;
358
+ }
359
+
360
+ // Check if either operation failed
361
+ if ( false === $flag1 & $flag2 ) {
362
+ wp_die( 0 );
363
+ } else {
364
+ echo 1;
365
+ die();
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Stop scan
371
+ * @return void
372
+ */
373
+ public function ajax_stop_scan() {
374
+
375
+ // Check nonce
376
+ if ( !wp_verify_nonce( $_POST ['p3_nonce'], 'p3_ajax_stop_scan' ) ) {
377
+ wp_die( 'Invalid nonce' );
378
+ }
379
+
380
+ // If there's no file, return an error
381
+ if ( !file_exists( P3_FLAG_FILE ) ) {
382
+ wp_die( 0 );
383
+ }
384
+
385
+ // Get the file
386
+ $json = json_decode( file_get_contents( P3_FLAG_FILE ) );
387
+
388
+ // Stop all sites who match the current site's URL
389
+ $site_url = parse_url( get_home_url(), PHP_URL_PATH );
390
+ if ( null === $site_url ) {
391
+ $site_url = '/';
392
+ }
393
+ foreach ( (array) $json as $k => $v ) {
394
+ if ( $site_url == $v->site_url ) {
395
+ unset( $json[$k] );
396
+ }
397
+ }
398
+
399
+ // Rewrite the file
400
+ $flag = file_put_contents( P3_FLAG_FILE, json_encode( $json ) );
401
+ if ( !$flag ) {
402
+ wp_die( 0 );
403
+ }
404
+
405
+ // Tell the user what happened
406
+ $this->add_notice( 'Turned off performance scanning.' );
407
+
408
+ // Return the last filename
409
+ if ( property_exists( $v, 'name' ) ) {
410
+ echo $v->name . '.json';
411
+ die();
412
+ } else {
413
+ wp_die( 0 );
414
+ }
415
+ }
416
+
417
+
418
+ /**************************************************************/
419
+ /** EMAIL RESULTS **/
420
+ /**************************************************************/
421
+
422
+ /**
423
+ * Send results ( presumably to admin or support )
424
+ * @return void
425
+ */
426
+ public function ajax_send_results() {
427
+
428
+ // Check nonce
429
+ if ( !wp_verify_nonce( $_POST ['p3_nonce'], 'p3_ajax_send_results' ) ) {
430
+ wp_die( 'Invalid nonce' );
431
+ }
432
+
433
+ // Check fields
434
+ $to = sanitize_email( $_POST['p3_to'] );
435
+ $from = sanitize_email( $_POST['p3_from'] );
436
+ $subject = filter_var(
437
+ $_POST['p3_subject'], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES | FILTER_FLAG_STRIP_HIGH | FILTER_FLAG_STRIP_LOW
438
+ );
439
+ $message = strip_tags( $_POST['p3_message'] );
440
+ $results = strip_tags( $_POST['p3_results'] );
441
+
442
+ // Append the results to the message ( if a messge was specified )
443
+ if ( empty( $message ) ) {
444
+ $message = stripslashes( $results );
445
+ } else {
446
+ $message = stripslashes( $message . "\n\n" .$results );
447
+ }
448
+
449
+ // Check for errors and send message
450
+ if ( !is_email( $to ) || !is_email( $from ) ) {
451
+ echo '0|Invalid e-mail';
452
+ } elseif ( empty( $subject ) ) {
453
+ echo '0|Invalid subject';
454
+ } elseif ( false === wp_mail( $to, $subject, $message, "From: $from" ) ) {
455
+ echo '0|<a href="http://codex.wordpress.org/Function_Reference/wp_mail" target="_blank">wp_mail()</a> function returned false';
456
+ } else {
457
+ echo '1';
458
+ }
459
+ die();
460
+ }
461
+
462
+
463
+ /**************************************************************/
464
+ /** CURRENT PAGE **/
465
+ /**************************************************************/
466
+
467
+ /**
468
+ * Show the settings page.
469
+ * This is where the user can start/stop the scan
470
+ */
471
+ public function scan_settings_page() {
472
+ include_once P3_PATH . '/templates/template.php';
473
+ }
474
+
475
+
476
+ /**************************************************************/
477
+ /** HELP PAGE **/
478
+ /**************************************************************/
479
+
480
+ /**
481
+ * Show the help page.
482
+ */
483
+ public function show_help() {
484
+ include_once P3_PATH . '/templates/template.php';
485
+ }
486
+
487
+
488
+ /**************************************************************/
489
+ /** HISTORY PAGE **/
490
+ /**************************************************************/
491
+
492
+ /**
493
+ * View the results of a scan
494
+ * @uses $_REQUEST['name']
495
+ * @return void
496
+ */
497
+ public function view_scan() {
498
+ include_once P3_PATH . '/templates/template.php';
499
+ }
500
+
501
+ /**
502
+ * Show a list of available scans.
503
+ * Uses WP List table to handle UI and sorting.
504
+ * Uses P3_Profile_Table to handle deleting
505
+ * @uses WP_List_Table
506
+ * @uses jquery
507
+ * @uses P3_Profile_Table
508
+ * @return void
509
+ */
510
+ public function list_scans() {
511
+ include_once P3_PATH . '/templates/template.php';
512
+ }
513
+
514
+ /**
515
+ * Get the latest performance scan
516
+ * @return string|false
517
+ */
518
+ public function get_latest_profile() {
519
+
520
+ // Open the directory
521
+ $dir = opendir( P3_PROFILES_PATH );
522
+ if ( false === $dir ) {
523
+ wp_die( 'Cannot read profiles directory' );
524
+ }
525
+
526
+ // Loop through the files, get the path and the last modified time
527
+ $files = array();
528
+ while ( false !== ( $file = readdir( $dir ) ) ) {
529
+ if ( '.json' == substr( $file, -5 ) ) {
530
+ $files[filemtime( P3_PROFILES_PATH . "/$file" )] = P3_PROFILES_PATH . "/$file";
531
+ }
532
+ }
533
+ closedir( $dir );
534
+
535
+ // If there are no files, return false
536
+ if ( empty( $files ) ) {
537
+ return false;
538
+ }
539
+
540
+ // Sort the files by the last modified time, return the latest
541
+ ksort( $files );
542
+ return array_pop( $files );
543
+ }
544
+
545
+ /**
546
+ * Add a notices
547
+ * @uses transients
548
+ * @param string $notice
549
+ * @return void
550
+ */
551
+ public function add_notice( $notice ) {
552
+
553
+ // Get any notices on the stack
554
+ $notices = get_transient( 'p3_notices' );
555
+ if ( empty( $notices ) ) {
556
+ $notices = array();
557
+ }
558
+
559
+ // Add the notice to the stack
560
+ $notices[] = $notice;
561
+
562
+ // Save the stack
563
+ set_transient( 'p3_notices', $notices );
564
+ }
565
+
566
+ /**
567
+ * Display notices
568
+ * @uses transients
569
+ * @return voide
570
+ */
571
+ public function show_notices() {
572
+
573
+ // Skip notices if we're fixing the flag file
574
+ if ( isset( $_REQUEST['p3_action'] ) && 'fix-flag-file' == $_REQUEST['p3_action'] ) {
575
+ return true;
576
+ }
577
+
578
+ $notices = get_transient( 'p3_notices' );
579
+ if ( !empty( $notices ) ) {
580
+ $notices = array_unique( $notices );
581
+ foreach ( $notices as $notice ) {
582
+ echo '<div class="updated"><p>' . htmlentities( $notice ) . '</p></div>';
583
+ }
584
+ }
585
+ set_transient( 'p3_notices', array() );
586
+ if ( false !== $this->scan_enabled() ) {
587
+ echo '<div class="updated"><p>Performance scanning is enabled.</p></div>';
588
+ }
589
+
590
+ // Check that we can write .profiling_enabled
591
+ if ( isset( $_REQUEST['page'] ) && basename( __FILE__ ) == $_REQUEST['page'] && ( !isset( $_REQUEST['p3_action'] ) || 'fix-flag-file' != $_REQUEST['p3_action'] ) ) {
592
+ if ( !file_exists( P3_FLAG_FILE ) || !is_writable( P3_FLAG_FILE ) ) {
593
+ @touch( P3_FLAG_FILE );
594
+ if ( !file_exists( P3_FLAG_FILE ) || !is_writable( P3_FLAG_FILE ) ) {
595
+ echo '<div class="error"><p>Cannot set profile flag file <input type="button" onclick="location.href=\'' . add_query_arg( array( 'p3_action' => 'fix-flag-file' ) ) . '\';" class="button" value="click here to fix" /></p></div>';
596
+ }
597
+ }
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Activation hook
603
+ * Install the profiler loader in the most optimal place
604
+ * @return void
605
+ */
606
+ public function activate() {
607
+ $sapi = strtolower( php_sapi_name() );
608
+
609
+ // .htaccess for mod_php
610
+ if ( 'apache2handler' == $sapi ) {
611
+ insert_with_markers(
612
+ P3_PATH . '/../../../.htaccess',
613
+ 'p3-profiler',
614
+ array( 'php_value auto_prepend_file "' . P3_PATH . DIRECTORY_SEPARATOR . 'start-profile.php"' )
615
+ );
616
+ }
617
+
618
+ // Always try to create the mu-plugin loader in case either of the above methods fail
619
+
620
+ // mu-plugins doesn't exist
621
+ if ( !file_exists( P3_PATH . '/../../mu-plugins/' ) && is_writable( P3_PATH . '/../../' ) ) {
622
+ $flag = wp_mkdir_p( P3_PATH . '/../../mu-plugins/' );
623
+ }
624
+ if ( file_exists( P3_PATH . '/../../mu-plugins/' ) && is_writable( P3_PATH . '/../../mu-plugins' ) ) {
625
+ file_put_contents(
626
+ P3_PATH . '/../../mu-plugins/p3-profiler.php',
627
+ '<' . "?php // Start profiling\nrequire_once( realpath( dirname( __FILE__ ) ) . '/../plugins/p3-profiler/start-profile.php' ); ?" . '>'
628
+ );
629
+ }
630
+
631
+ // Create the profiles folder
632
+ $this->sync_profiles_folder();
633
+ }
634
+
635
+ /**
636
+ * Make the profiles folder
637
+ * @param string $path
638
+ * @return void
639
+ */
640
+ private function _make_profiles_folder( $path ) {
641
+ wp_mkdir_p( $path );
642
+ if ( !file_exists( "$path/.htaccess" ) ) {
643
+ file_put_contents( $path . DIRECTORY_SEPARATOR . '.htaccess', "Deny from all\n" );
644
+ }
645
+ if ( !file_exists( "$path/index.php" ) ) {
646
+ file_put_contents( $path. DIRECTORY_SEPARATOR . 'index.php', '<' . "?php header( 'Status: 404 Not found' ); ?" . ">\nNot found" );
647
+ }
648
+ }
649
+
650
+ /**
651
+ * Delete the profiles folder
652
+ * @param string $path
653
+ * @return void
654
+ */
655
+ private function _delete_profiles_folder( $path ) {
656
+ $dir = opendir( $path );
657
+ while ( ( $file = readdir( $dir ) ) !== false ) {
658
+ if ( $file != '.' && $file != '..' ) {
659
+ unlink( $path . DIRECTORY_SEPARATOR . $file );
660
+ }
661
+ }
662
+ closedir( $dir );
663
+ rmdir( $path );
664
+ }
665
+
666
+ /**
667
+ * Deactivation hook
668
+ * Uninstall the profiler loader
669
+ * @return void
670
+ */
671
+ public function deactivate() {
672
+
673
+ // Remove any .htaccess modifications
674
+ $file = P3_PATH . '/../../../.htaccess';
675
+ if ( file_exists( $file ) && array() !== extract_from_markers( $file, 'p3-profiler' ) ) {
676
+ insert_with_markers( $file, 'p3-profiler', array( '# removed during uninstall' ) );
677
+ }
678
+
679
+ // Remove mu-plugin
680
+ if ( file_exists( P3_PATH . '/../../mu-plugins/p3-profiler.php' ) ) {
681
+ if ( is_writable( P3_PATH . '/../../mu-plugins/p3-profiler.php' ) ) {
682
+ // Some servers give write permission, but not delete permission. Empty the file out, first, then try to delete it.
683
+ file_put_contents( P3_PATH . '/../../mu-plugins/p3-profiler.php', '' );
684
+ unlink( P3_PATH . '/../../mu-plugins/p3-profiler.php' );
685
+ }
686
+ }
687
+ }
688
+
689
+ /**
690
+ * Sync profiles folder
691
+ * Call whenever a blog is added / removed
692
+ * @return void
693
+ */
694
+ public function sync_profiles_folder() {
695
+
696
+ // Base blog profiles folder
697
+ $uploads_dir = wp_upload_dir();
698
+ $folder = $uploads_dir['basedir'] . DIRECTORY_SEPARATOR . 'profiles';
699
+ $this->_make_profiles_folder( $folder );
700
+
701
+ // Only for multisite
702
+ if ( !function_exists( 'is_multisite' ) || !is_multisite() ) {
703
+ return;
704
+ }
705
+
706
+ // List profiles/<blog id> folders
707
+ $folders = array();
708
+ $dir = opendir( $folder );
709
+ while ( ( $file = readdir( $dir ) ) !== false ) {
710
+ if ( $file != '.' && $file != '..' && is_dir( "$folder/$file" ) && is_numeric( $file ) ) {
711
+ $folders[] = $file;
712
+ }
713
+ }
714
+ closedir( $dir );
715
+
716
+ // List blogs
717
+ $blogs = array();
718
+ $blogs = get_blog_list( 0, 'all' );
719
+ foreach ( $blogs as $blog ) {
720
+ $blogs[] = $blog['blog_id'];
721
+ }
722
+
723
+ // Folders without a blog
724
+ foreach ( array_diff( $folders, $blogs ) as $id ) {
725
+ $this->_delete_profiles_folder( $folder . DIRECTORY_SEPARATOR . $id );
726
+ }
727
+
728
+ // Blogs without a folder
729
+ foreach ( array_diff( $blogs, $folders ) as $id ) {
730
+ switch_to_blog( $id );
731
+ $uploads_dir = wp_upload_dir();
732
+ $folder = $uploads_dir['basedir'] . DIRECTORY_SEPARATOR . 'profiles';
733
+ $this->_make_profiles_folder( $folder );
734
+ restore_current_blog();
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Uninstall hook
740
+ * Remove profile data
741
+ * @return void
742
+ */
743
+ public static function uninstall() {
744
+ // This is a static function so it needs an instance
745
+ // Since I'm myself, I can call my own private methods
746
+ $class = __CLASS__;
747
+ $me = new $class();
748
+
749
+ // Delete the profiles folder
750
+ if ( function_exists( 'is_multisite' ) && is_multisite() ) {
751
+ $blogs = get_blog_list( 0, 'all' );
752
+ foreach ( $blogs as $blog ) {
753
+ switch_to_blog( $blog['blog_id'] );
754
+ $uploads_dir = wp_upload_dir();
755
+ $folder = $uploads_dir['basedir'] . DIRECTORY_SEPARATOR . 'profiles' . DIRECTORY_SEPARATOR;
756
+ $me->_delete_profiles_folder( $folder );
757
+ }
758
+ restore_current_blog();
759
+ } else {
760
+ $me->_delete_profiles_folder( P3_PROFILES_PATH );
761
+ }
762
+ }
763
+
764
+ /**
765
+ * Check to see if a scan is enabled
766
+ * @return array|false
767
+ */
768
+ public function scan_enabled() {
769
+ if ( !file_exists( P3_FLAG_FILE ) ) {
770
+ return false;
771
+ }
772
+ $site_url = parse_url( get_home_url(), PHP_URL_PATH );
773
+ if ( null === $site_url ) {
774
+ $site_url = '/';
775
+ }
776
+ $json = json_decode( file_get_contents( P3_FLAG_FILE ), true );
777
+ foreach ( (array) $json as $v ) {
778
+ if ( $site_url == $v['site_url'] ) {
779
+ return $v;
780
+ }
781
+ }
782
+ return false;
783
+ }
784
+
785
+ /**
786
+ * Convert a filesize ( in bytes ) to a human readable filesize
787
+ * @param int $size
788
+ * @return string
789
+ */
790
+ public function readable_size( $size ) {
791
+ $units = array( 'B', 'KB', 'MB', 'GB', 'TB' );
792
+ $size = max( $size, 0 );
793
+ $pow = floor( ( $size ? log( $size ) : 0 ) / log( 1024 ) );
794
+ $pow = min( $pow, count( $units ) - 1 );
795
+ $size /= pow( 1024, $pow );
796
+ return round( $size, 0 ) . ' ' . $units[$pow];
797
+ }
798
+ }
readme.txt ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === P3 (Plugin Performance Profiler) ===
2
+ Contributors: Godaddy, StarfieldTech
3
+ Tags: debug, debugging, developer, development, performance, plugin, profiler, speed
4
+ Requires at least: 3.3
5
+ Tested up to: 3.3
6
+ Stable tag: 1.0
7
+
8
+ See which plugins are slowing down your site. This plugin creates a performance report for your site.
9
+
10
+ == Description ==
11
+ This plugin creates a profile of your WordPress site's plugins' performance by measuring their impact on your site's load time.  Often times, WordPress sites load slowly because of poorly configured plugins or because there are so many of them. By using the P3 plugin, you can narrow down anything causing slowness on your site.
12
+
13
+ Requires Firefox, Chrome, Opera, Safari, or IE9 or later.
14
+
15
+ == Screenshots ==
16
+
17
+ 1. First, profile your site. The scanner generates some traffic on your site and monitors your site's performance on the server, then shows you the results. With this information, you can decide what action to take.
18
+ 2. After profiling, you'll see a breakdown of relative runtime for each plugin.
19
+ 3. Callouts at the top give you quick information like how much load time (in seconds) is dedicated to plugins and how many database queries your site is running per page.
20
+ 4. The detailed timeline gives you timing information for every plugin, the theme, and the core for every page during the profile. Find out exactly what's happening on slow loading pages.
21
+ 5. The query timeline gives you the number of database queries for every page during the profile. Find out which pages generate the most database queries.
22
+ 6. Keep a history of your performance scans, compare your current performance with your previous performance.
23
+ 7. Full in-app help documentation
24
+ 8. Send a summary of your performance profile via e-mail. If you want to show your developer, site admin, hosting support, or a plugin developer what's going on with your site, this is good way to start the conversation.
25
+
26
+ == Installation ==
27
+ Automatic installation
28
+
29
+ 1. Log into your WordPress admin
30
+ 2. Click __Plugins__
31
+ 3. Click __Add New__
32
+ 4. Search for __P3__
33
+ 5. Click __Install Now__ under "P3 (Plugin Performance Profiler)"
34
+ 6. Activate the plugin
35
+
36
+ Manual installation:
37
+
38
+ 1. Download the plugin
39
+ 2. Extract the contents of the zip file
40
+ 3. Upload the contents of the zip file to the wp-content/plugins/ folder of your WordPress installation
41
+ 4. Then activate the Plugin from Plugins page.
42
+
43
+ == Frequently Asked Questions ==
44
+ No FAQs yet.
45
+
46
+ == Upgrade Notice ==
47
+
48
+ = 1.0 =
49
+ Initial release.
50
+
51
+ == Changelog ==
52
+
53
+ = 1.0 =
54
+ * Automatic site profiling
55
+ * Manual site profiling
56
+ * Profile history
57
+ * Continue a profile session
58
+ * Clear opcode caches (if possible) to improve plugin function detection
59
+ * Limit profiling by IP address (regex pattern)
60
+ * Limit profiling by site URL (for MS compatibility)
61
+ * Rewrite http URLs to https to avoid SSL warnings when using wp-admin over SSL
62
+ * Hide the admin toolbar on the front-end when profiling to prevent extra plugin scripts/styles from loading
63
+ * In-app help / glossary page
64
+ * Activate / deactivate hooks to try different loader methods so the profiler runs as early as possible
65
+ * Uninstall hooks to clean up profiles
66
+ * Hooks add/delete blog to clean up profiles
67
+ * Send profile summary via e-mail
screenshot-1.png ADDED
Binary file
screenshot-2.png ADDED
Binary file
screenshot-3.png ADDED
Binary file
screenshot-4.png ADDED
Binary file
screenshot-5.png ADDED
Binary file
screenshot-6.png ADDED
Binary file
screenshot-7.png ADDED
Binary file
screenshot-8.png ADDED
Binary file
start-profile.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // If profiling hasn't started, start it
4
+ if ( !isset( $GLOBALS['p3_profiler'] ) ) {
5
+ declare( ticks = 1 ); // Capture ever user function call
6
+ include_once realpath( dirname( __FILE__ ) ) . '/class.p3-profiler.php';
7
+ $GLOBALS['p3_profiler'] = new P3_Profiler(); // Go
8
+ }
templates/.htaccess ADDED
@@ -0,0 +1 @@
 
1
+ Deny from all
templates/callouts.php ADDED
@@ -0,0 +1,624 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript">
2
+
3
+ /*****************************************************************/
4
+ /** AUTO SCANNER HELPER OBJECT **/
5
+ /*****************************************************************/
6
+ // This will load all of the pages in the list, then turn off
7
+ // the profile mode and view the results when complete.
8
+ var P3_Scan = {
9
+
10
+ // List of pages to scan
11
+ pages: <?php echo json_encode( $this->list_of_pages() ); ?>,
12
+
13
+ // Current page
14
+ current_page: 0,
15
+
16
+ // Pause flag
17
+ paused: false,
18
+
19
+ // Start
20
+ start: function() {
21
+
22
+ // Form data
23
+ data = {
24
+ 'p3_ip' : jQuery( '#p3-advanced-ip' ).val(),
25
+ 'p3_disable_opcode_cache' : jQuery( '#p3-disable-opcode-cache' ).prop( 'checked' ),
26
+ 'p3_scan_name' : jQuery( "#p3-scan-name" ).val(),
27
+ 'action' : 'p3_start_scan',
28
+ 'p3_nonce' : jQuery( "#p3_nonce" ).val()
29
+ }
30
+
31
+ // Turn on the profiler
32
+ jQuery.post( ajaxurl, data, function( response ) {
33
+ if ( 1 != response ) {
34
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
35
+ } else {
36
+
37
+ // Start scanning pages
38
+ jQuery( "#p3-scan-frame" ).attr( "onload", "P3_Scan.next_page();" );
39
+ jQuery( "#p3-scan-frame" ).attr( "src", P3_Scan.pages[0] );
40
+ P3_Scan.current_page = 0;
41
+ P3_Scan.update_display();
42
+
43
+ }
44
+ });
45
+ },
46
+
47
+ // Pause
48
+ pause: function() {
49
+
50
+ // Turn off the profiler
51
+ data = {
52
+ 'action' : 'p3_stop_scan',
53
+ 'p3_nonce' : '<?php echo wp_create_nonce( 'p3_ajax_stop_scan' ); ?>'
54
+ }
55
+ jQuery.post( ajaxurl, data, function( response ) {
56
+ if ( response.indexOf( '.json' ) < 0 ) {
57
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
58
+ }
59
+
60
+ // Hide the cancel button
61
+ jQuery( "#p3-cancel-scan-buttonset" ).hide();
62
+ jQuery( "#p3-resume-scan-buttonset" ).show();
63
+ jQuery( "#p3-view-results-buttonset" ).hide();
64
+
65
+ // Show the view results button
66
+ jQuery( "#p3-view-incomplete-results-submit" ).attr( "data-scan-name", response );
67
+
68
+ // Pause
69
+ P3_Scan.paused = true;
70
+
71
+ // Update the caption
72
+ jQuery( "#p3-scanning-caption" ).html( "Scanning is paused." ).css( "color", "black" );
73
+ });
74
+ },
75
+
76
+ // Resume
77
+ resume: function() {
78
+
79
+ data = {
80
+ 'p3_ip' : jQuery( '#p3-advanced-ip' ).val(),
81
+ 'p3_disable_opcode_cache' : jQuery( '#p3-disable-opcode-cache' ).prop( 'checked' ),
82
+ 'p3_scan_name' : jQuery( "#p3-scan-name" ).val(),
83
+ 'action' : 'p3_start_scan',
84
+ 'p3_nonce' : jQuery( "#p3_nonce" ).val()
85
+ }
86
+
87
+ // Turn on the profiler
88
+ jQuery.post( ajaxurl, data, function( response ) {
89
+ if ( 1 != response ) {
90
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
91
+ } else {
92
+
93
+ // Show the cancel button
94
+ P3_Scan.paused = false;
95
+ jQuery( "#p3-cancel-scan-buttonset" ).show();
96
+ jQuery( "#p3-resume-scan-buttonset" ).hide();
97
+ jQuery( "#p3-view-results-buttonset" ).hide();
98
+ P3_Scan.update_display();
99
+ P3_Scan.next_page();
100
+ }
101
+ });
102
+ },
103
+
104
+ // Stop
105
+ stop: function() {
106
+
107
+ // Turn off the profiler
108
+ data = {
109
+ 'action' : 'p3_stop_scan',
110
+ 'p3_nonce' : '<?php echo wp_create_nonce( 'p3_ajax_stop_scan' ); ?>'
111
+ }
112
+ jQuery.post( ajaxurl, data, function( response ) {
113
+ if ( response.indexOf( '.json' ) < 0 ) {
114
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
115
+ }
116
+
117
+ // Hide the cancel button
118
+ jQuery( "#p3-cancel-scan-buttonset" ).hide();
119
+ jQuery( "#p3-resume-scan-buttonset" ).hide();
120
+ jQuery( "#p3-view-results-buttonset" ).show();
121
+
122
+ // Show the view results button
123
+ jQuery( "#p3-view-results-submit" ).attr( "data-scan-name", response );
124
+
125
+ // Update the caption
126
+ jQuery( "#p3-scanning-caption" ).html( "Scanning is complete." ).css( "color", "black" );
127
+ });
128
+ },
129
+
130
+ // Update the display
131
+ update_display : function() {
132
+ jQuery( "#p3-scanning-caption" ).html( 'Scanning ' + P3_Scan.pages[P3_Scan.current_page] ).css( "color", "" );
133
+ jQuery( "#p3-progress" ).progressbar( "value", ( P3_Scan.current_page / ( P3_Scan.pages.length - 1 ) ) * 100 );
134
+ },
135
+
136
+ // Look at the next page
137
+ next_page : function() {
138
+
139
+ // Paused?
140
+ if ( P3_Scan.paused ) {
141
+ return true;
142
+ }
143
+
144
+ // Is it time to stop?
145
+ if ( P3_Scan.current_page >= P3_Scan.pages.length - 1 ) {
146
+ P3_Scan.stop();
147
+ return true;
148
+ }
149
+
150
+ // Next page
151
+ jQuery( "#p3-scan-frame" ).attr( "src", P3_Scan.pages[++P3_Scan.current_page] );
152
+
153
+ // Update the display
154
+ P3_Scan.update_display();
155
+ }
156
+ };
157
+
158
+
159
+ // Onload functionality
160
+ jQuery( document ).ready( function( $) {
161
+
162
+ /*****************************************************************/
163
+ /** DIALOGS **/
164
+ /*****************************************************************/
165
+
166
+ // IP settings
167
+ $( "#p3-ip-dialog" ).dialog({
168
+ 'autoOpen' : false,
169
+ 'closeOnEscape' : true,
170
+ 'draggable' : false,
171
+ 'resizable' : false,
172
+ 'modal' : true,
173
+ 'width' : 450,
174
+ 'height' : 255,
175
+ 'title' : "Advanced Settings",
176
+ 'buttons' :
177
+ [
178
+ {
179
+ text: 'OK',
180
+ 'class' : 'button-secondary',
181
+ click: function() {
182
+ $( this ).dialog( "close" );
183
+ }
184
+ },
185
+ {
186
+ text: 'Cancel',
187
+ 'class': 'p3-cancel-button',
188
+ click: function() {
189
+ $( this ).dialog( "close" );
190
+ }
191
+ }
192
+ ]
193
+ });
194
+
195
+ // Iframe scanner
196
+ $( "#p3-scanner-dialog" ).dialog({
197
+ 'autoOpen' : false,
198
+ 'closeOnEscape' : true,
199
+ 'draggable' : false,
200
+ 'resizable' : false,
201
+ 'modal' : true,
202
+ 'width': 800,
203
+ 'height' : 600,
204
+ 'title' : "Performance Scan",
205
+ 'dialogClass' : 'noPadding'
206
+ });
207
+
208
+ // Auto scan or manual scan
209
+ $( "#p3-scan-name-dialog" ).dialog({
210
+ 'autoOpen' : false,
211
+ 'closeOnEscape' : true,
212
+ 'draggable' : false,
213
+ 'resizable' : false,
214
+ 'modal' : true,
215
+ 'width' : 425,
216
+ 'height' : 180,
217
+ 'title' : 'Scan Name'
218
+ // 'dialogClass' : 'noTitle'
219
+ });
220
+
221
+ // Progress dialog
222
+ $( "#p3-progress-dialog" ).dialog({
223
+ 'autoOpen' : false,
224
+ 'closeOnEscape' : false,
225
+ 'draggable' : false,
226
+ 'resizable' : false,
227
+ 'modal' : true,
228
+ 'width' : 450,
229
+ 'height' : 117,
230
+ 'dialogClass' : 'noTitle'
231
+ });
232
+
233
+
234
+
235
+ /*****************************************************************/
236
+ /** LINKS **/
237
+ /*****************************************************************/
238
+
239
+ // Advanced settings link
240
+ $( "#p3-advanced-settings" ).click( function() {
241
+ $( "#p3-ip-dialog" ).dialog( "open" );
242
+ });
243
+
244
+
245
+
246
+ /*****************************************************************/
247
+ /** BUTTONS **/
248
+ /*****************************************************************/
249
+
250
+ // Start scan button
251
+ $( "#p3-start-scan-submit" ).click( function() {
252
+
253
+ // Stay checked to keep the styling
254
+ $( this ).prop( "checked", true );
255
+ $( this ).button( "refresh" );
256
+
257
+ url = $( "#p3-scan-frame" ).attr( "data-defaultsrc" );
258
+ if ( url.indexOf('?') >= 0 || url.indexOf('&') >= 0 ) {
259
+ url += '&P3_HIDE_ADMIN_BAR=1';
260
+ } else if ( url.charAt(url.length - 1) != '/' ) {
261
+ url += '/?P3_HIDE_ADMIN_BAR=1';
262
+ } else {
263
+ url += '?P3_HIDE_ADMIN_BAR=1';
264
+ }
265
+
266
+ $( "#p3-scan-frame" ).attr( "src", url );
267
+ $( "#p3-scanner-dialog" ).dialog( "open" );
268
+ $( "#p3-scan-name-dialog" ).dialog( "open" );
269
+ });
270
+
271
+ // Stop scan button
272
+ $( "#p3-stop-scan-submit" ).click( function() {
273
+
274
+ // Stay checked to keep the styling
275
+ $( this ).prop( "checked", true );
276
+ $( this ).button( "refresh" );
277
+
278
+ // Turn off the profiler
279
+ data = {
280
+ 'action' : 'p3_stop_scan',
281
+ 'p3_nonce' : '<?php echo wp_create_nonce( 'p3_ajax_stop_scan' ); ?>'
282
+ }
283
+ jQuery.post( ajaxurl, data, function( response ) {
284
+ if ( response.indexOf( '.json' ) < 0 ) {
285
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
286
+ }
287
+ location.reload();
288
+ });
289
+ });
290
+
291
+ // Auto scan button
292
+ $( "#p3-auto-scan-submit" ).click( function() {
293
+
294
+ // Stay checked to keep the styling
295
+ $( this ).prop( "checked", true );
296
+ $( this ).button( "refresh" );
297
+
298
+ // Close the "auto or manual" dialog
299
+ $( "#p3-scan-name-dialog" ).dialog( "close" );
300
+
301
+ // Open the progress bar dialog
302
+ $( "#p3-progress-dialog" ).dialog( "open" );
303
+
304
+ // Initialize the progress bar to 0%
305
+ $( "#p3-progress" ).progressbar({
306
+ 'value': 0
307
+ });
308
+
309
+ P3_Scan.start();
310
+ });
311
+
312
+ // Manual scan button
313
+ $( "#p3-manual-scan-submit" ).click( function() {
314
+
315
+ // Stay checked to keep the styling
316
+ $( this ).prop( "checked", true );
317
+ $( this ).button( "refresh" );
318
+
319
+
320
+ // Form data
321
+ data = {
322
+ 'p3_ip' : jQuery( '#p3-advanced-ip' ).val(),
323
+ 'p3_disable_opcode_cache' : jQuery( '#p3-disable-opcode-cache' ).prop( 'checked' ),
324
+ 'p3_scan_name' : jQuery( "#p3-scan-name" ).val(),
325
+ 'action' : 'p3_start_scan',
326
+ 'p3_nonce' : jQuery( "#p3_nonce" ).val()
327
+ }
328
+
329
+ // Turn on the profiler
330
+ jQuery.post( ajaxurl, data, function( response ) {
331
+ if ( 1 != response ) {
332
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
333
+ }
334
+ });
335
+
336
+ $( "#p3-scan-name-dialog" ).dialog( "close" );
337
+ $( "#p3-scan-caption" ).hide();
338
+ $( "#p3-manual-scan-caption" ).show();
339
+ });
340
+
341
+ // Manual scan "I'm done" button
342
+ $( "#p3-manual-scan-done-submit" ).click( function() {
343
+ data = {
344
+ 'action' : 'p3_stop_scan',
345
+ 'p3_nonce' : '<?php echo wp_create_nonce( 'p3_ajax_stop_scan' ); ?>'
346
+ }
347
+ jQuery.post( ajaxurl, data, function( response ) {
348
+ if ( response.indexOf( '.json' ) < 0 ) {
349
+ alert( "There was an error processing your request. Please reload the page and try again. [" + response + "]");
350
+ } else {
351
+ location.href = "<?php echo add_query_arg( array( 'p3_action' => 'view-scan' ) ); ?>&name=" + response;
352
+ }
353
+ })
354
+ $( "#p3-scanner-dialog" ).dialog( "close" );
355
+ });
356
+
357
+ // Manual scan cancel link
358
+ $( "#p3-manual-scan-cancel" ).click( function() {
359
+ P3_Scan.pause();
360
+ $( "#p3-scanner-dialog" ).dialog( "close" );
361
+ });
362
+
363
+ // Cancel scan button
364
+ $( "#p3-cancel-scan-submit" ).click( function() {
365
+
366
+ // Stay checked to keep the styling
367
+ $( this ).prop( "checked", true );
368
+ $( this ).button( "refresh" );
369
+
370
+ P3_Scan.pause();
371
+ });
372
+
373
+ // Resume
374
+ $( "#p3-resume-scan-submit" ).click( function() {
375
+
376
+ // Stay checked to keep the styling
377
+ $( this ).prop( "checked", true );
378
+ $( this ).button( "refresh" );
379
+
380
+ P3_Scan.resume();
381
+ });
382
+
383
+ // View results button
384
+ $( "#p3-view-results-submit" ).click( function() {
385
+
386
+ // Stay checked to keep the styling
387
+ $( this ).prop( "checked", true );
388
+ $( this ).button( "refresh" );
389
+
390
+ // Close the dialogs
391
+ jQuery( "#p3-scanner-dialog" ).dialog( "close" );
392
+ jQuery( "#p3-progress-dialog" ).dialog( "close" );
393
+
394
+ // View the scan
395
+ location.href = "<?php echo add_query_arg( array( 'p3_action' => 'view-scan' ) ); ?>&name=" + $( this ).attr( "data-scan-name" );
396
+ });
397
+ $( "#p3-view-incomplete-results-submit" ).click( function() {
398
+ $( "#p3-view-results-submit" ).trigger( "click" );
399
+ });
400
+
401
+
402
+ /*****************************************************************/
403
+ /** OTHER **/
404
+ /*****************************************************************/
405
+ // Enable / disable buttons based on scan name input
406
+ $( "#p3-scan-name" ).live( "keyup", function() {
407
+ if ( $( this ).val().match(/^[a-zA-Z0-9_\.-]+$/) ) {
408
+ $( "#p3-auto-scan-submit" ).button( "enable" )
409
+ $( "#p3-manual-scan-submit" ).button( "enable" );
410
+ } else {
411
+ $( "#p3-auto-scan-submit" ).button( "disable" );
412
+ $( "#p3-manual-scan-submit" ).button( "disable" );
413
+ }
414
+ });
415
+
416
+ // Callouts
417
+ $( "div.p3-callout-inner-wrapper" )
418
+ .corner( "round 8px" )
419
+ .parent()
420
+ .css( "padding", "4px" )
421
+ .corner( "round 10px" );
422
+
423
+ // Start / stop buttons
424
+ $( "#p3-scan-form-wrapper" ).corner( "round 8px" );
425
+
426
+ // Continue scan
427
+ $( "a.p3-continue-scan" ).click( function() {
428
+ $( "#p3-start-scan-submit" ).trigger( "click" );
429
+ $( "#p3-scan-name" ).val( $( this ).attr( "data-name" ).replace(/\.json$/, '' ) );
430
+ });
431
+ });
432
+ </script>
433
+ <table id="p3-quick-report" cellpadding="0" cellspacing="0">
434
+ <tr>
435
+
436
+ <td>
437
+ <div class="ui-widget-header" id="p3-scan-form-wrapper">
438
+ <?php if ( false !== ( $info = $this->scan_enabled() ) ) { ?>
439
+ <!-- Stop scan button -->
440
+
441
+ <strong>IP:</strong><?php echo htmlentities( $info['ip'] ); ?>
442
+ <div class="p3-big-button"><input type="checkbox" checked="checked" id="p3-stop-scan-submit" />
443
+ <label for="p3-stop-scan-submit">Stop Scan</label></div>
444
+ <?php echo htmlentities( $info['name'] ); ?>
445
+
446
+ <?php } else { ?>
447
+
448
+ <!-- Start scan button -->
449
+ <?php echo wp_nonce_field( 'p3_ajax_start_scan', 'p3_nonce' ); ?>
450
+ <strong>My IP:</strong><?php echo htmlentities( $GLOBALS['p3_profiler']->get_ip() ); ?>
451
+ <div class="p3-big-button"><input type="checkbox" checked="checked" id="p3-start-scan-submit" />
452
+ <label for="p3-start-scan-submit">Start Scan</label></div>
453
+ <a href="javascript:;" id="p3-advanced-settings">Advanced Settings</a>
454
+
455
+ <?php } ?>
456
+ </div>
457
+ </td>
458
+
459
+ <!-- First callout cell -->
460
+ <td class="p3-callout">
461
+ <div class="p3-callout-outer-wrapper qtip-tip" title="Total number of active plugins, including must-use plugins, on your site.">
462
+ <div class="p3-callout-inner-wrapper">
463
+ <div class="p3-callout-caption">Total plugins:</div>
464
+ <div class="p3-callout-data">
465
+ <?php
466
+ // Get the total number of plugins
467
+ $active_plugins = count( get_mu_plugins() );
468
+ foreach ( get_plugins() as $plugin => $junk ) {
469
+ if ( is_plugin_active( $plugin ) ) {
470
+ $active_plugins++;
471
+ }
472
+ }
473
+ echo $active_plugins;
474
+ ?>
475
+ </div>
476
+ <div class="p3-callout-caption">( currently active )</div>
477
+ </div>
478
+ </div>
479
+ </td>
480
+
481
+ <!-- Second callout cell -->
482
+ <td class="p3-callout">
483
+ <div class="p3-callout-outer-wrapper qtip-tip" title="Total number of seconds dedicated to plugin code per visit on your site."
484
+ <?php if ( !empty( $scan ) ) { ?>title="From <?php echo basename( $scan ); ?><?php } ?>">
485
+ <div class="p3-callout-inner-wrapper">
486
+ <div class="p3-callout-caption">Plugin load time</div>
487
+ <div class="p3-callout-data">
488
+ <?php if ( null === $profile ) { ?>
489
+ <span class="p3-faded-grey">n/a</span>
490
+ <?php } else { ?>
491
+ <?php printf( '%.3f', $profile->averages['plugins'] ); ?>
492
+ <?php } ?>
493
+ </div>
494
+ <div class="p3-callout-caption">( sec. per visit )</div>
495
+ </div>
496
+ </div>
497
+ </td>
498
+
499
+ <!-- Third callout cell -->
500
+ <td class="p3-callout">
501
+ <div class="p3-callout-outer-wrapper qtip-tip" title="Percent of load time on your site dedicated to plugin code."
502
+ <?php if ( !empty( $scan ) ) { ?>title="From <?php echo basename( $scan ); ?><?php } ?>">
503
+ <div class="p3-callout-inner-wrapper">
504
+ <div class="p3-callout-caption">Plugin impact</div>
505
+ <div class="p3-callout-data">
506
+ <?php if ( null === $profile ) { ?>
507
+ <span class="p3-faded-grey">n/a</span>
508
+ <?php } else { ?>
509
+ <?php printf( '%.1f%%', $profile->averages['plugin_impact'] ); ?>
510
+ <?php } ?>
511
+ </div>
512
+ <div class="p3-callout-caption">( of page load time )</div>
513
+ </div>
514
+ </div>
515
+ </td>
516
+
517
+ <!-- Fourth callout cell -->
518
+ <td class="p3-callout">
519
+ <div class="p3-callout-outer-wrapper qtip-tip" title="Total number of database queries per visit."
520
+ <?php if ( !empty( $scan ) ) { ?>title="From <?php echo basename( $scan ); ?><?php } ?>">
521
+ <div class="p3-callout-inner-wrapper">
522
+ <div class="p3-callout-caption">MySQL Queries</div>
523
+ <div class="p3-callout-data">
524
+ <?php if ( null === $profile ) { ?>
525
+ <span class="p3-faded-grey">n/a</span>
526
+ <?php } else { ?>
527
+ <?php echo round( $profile->averages['queries'] ); ?>
528
+ <?php } ?>
529
+ </div>
530
+ <div class="p3-callout-caption">Per visit</div>
531
+ </div>
532
+ </div>
533
+ </td>
534
+
535
+ </tr>
536
+ </table>
537
+
538
+ <!-- Dialog for IP settings -->
539
+ <div id="p3-ip-dialog" class="p3-dialog">
540
+ <div>
541
+ IP address or pattern:<br />
542
+ <input type="text" id="p3-advanced-ip" style="width:90%;" size="35"
543
+ value="<?php echo $GLOBALS['p3_profiler']->get_ip(); ?>" title="Enter IP address or regular expression pattern" />
544
+ <br />
545
+ <em class="p3-em">Example: 1.2.3.4 or ( 1.2.3.4|4.5.6.7 )</em>
546
+ </div>
547
+ <br />
548
+ <div>
549
+ <input type="checkbox" id="p3-disable-opcode-cache" checked="checked" />
550
+ <label for="p3-disable-opcode-cache">Attempt to disable opcode caches <em>( recommended )</em></label>
551
+ <br />
552
+ <em class="p3-em">This can increase accuracy in plugin detection, but decrease accuracy in timing</em>
553
+ </div>
554
+ </div>
555
+
556
+ <!-- Dialog for iframe scanner -->
557
+ <div id="p3-scanner-dialog" class="p3-dialog">
558
+ <iframe id="p3-scan-frame" frameborder="0"
559
+ data-defaultsrc="<?php echo ( true === force_ssl_admin() ? str_replace( 'http://', 'https://', home_url() ) : home_url() ); ?>">
560
+ </iframe>
561
+ <div id="p3-scan-caption">
562
+ The scanner will analyze the speed and resource usage of all active plugins on your website.
563
+ It may take several minutes, and this window must remain open for the scan to finish successfully.
564
+ </div>
565
+ <div id="p3-manual-scan-caption" style="display: none;">
566
+ <table>
567
+ <tr>
568
+ <td>
569
+ Click the links and pages of your site, and the scanner will
570
+ analyze the speed and resource usage of all of your active
571
+ plugins.
572
+ </td>
573
+ <td width="220">
574
+ <a href="javascript:;" id="p3-manual-scan-cancel">Cancel</a>
575
+ &nbsp;&nbsp;&nbsp;
576
+ <span class="p3-big-button">
577
+ <input type="checkbox" id="p3-manual-scan-done-submit" checked="checked" />
578
+ <label for="p3-manual-scan-done-submit">I'm Done</label>
579
+ </span>
580
+ </td>
581
+ </tr>
582
+ </table>
583
+ </div>
584
+ </div>
585
+
586
+ <!-- Dialog for choose manual or auto scan -->
587
+ <div id="p3-scan-name-dialog" class="p3-dialog">
588
+ <div style="padding-top: 10px;">Scan name:
589
+ <input type="text" name="p3_scan_name" id="p3-scan-name" title="Enter scan name here"
590
+ value="scan_<?php echo date( 'Y-m-d' ); ?>_<?php echo substr( md5( uniqid() ), -8 );?>" size="35" maxlength="100" />
591
+ </div>
592
+ <div style="padding-top: 10px;"><em class="p3-em">Enter the name of a previous scan to continue scanning</em></div>
593
+ <br />
594
+ <div class="p3-big-button">
595
+ <input type="checkbox" id="p3-auto-scan-submit" checked="checked" /><label for="p3-auto-scan-submit">Auto Scan</label>
596
+ <input type="checkbox" id="p3-manual-scan-submit" checked="checked" /><label for="p3-manual-scan-submit">Manual Scan</label>
597
+ </div>
598
+ </div>
599
+
600
+ <!-- Dialog for progress bar -->
601
+ <div id="p3-progress-dialog" class="p3-dialog">
602
+ <div id="p3-scanning-caption">
603
+ Scanning ...
604
+ </div>
605
+ <div id="p3-progress"></div>
606
+
607
+ <!-- Cancel button -->
608
+ <div class="p3-big-button" id="p3-cancel-scan-buttonset">
609
+ <input type="checkbox" id="p3-cancel-scan-submit" checked="checked" /><label for="p3-cancel-scan-submit">Stop Scan</label>
610
+ </div>
611
+
612
+ <!-- View / resume buttons -->
613
+ <div class="p3-big-button" id="p3-resume-scan-buttonset" style="display: none;">
614
+ <input type="checkbox" id="p3-resume-scan-submit" checked="checked" /><label for="p3-resume-scan-submit">Resume</label>
615
+ <input type="checkbox" id="p3-view-incomplete-results-submit" checked="checked" data-scan-name="" />
616
+ <label for="p3-view-incomplete-results-submit">View Results</label>
617
+ </div>
618
+
619
+ <!-- View results button -->
620
+ <div class="p3-big-button" id="p3-view-results-buttonset" style="display: none;">
621
+ <input type="checkbox" id="p3-view-results-submit" checked="checked" data-scan-name="" />
622
+ <label for="p3-view-results-submit">View Results</label>
623
+ </div>
624
+ </div>
templates/fix-flag-file.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if ( file_exists( P3_FLAG_FILE ) && is_writable( P3_FLAG_FILE ) ) { ?>
2
+ <h3>Fixed!</h3>
3
+ The profiling flag file has been created and is writable.
4
+ <?php } else { ?>
5
+ <h3>Still broken!</h3>
6
+ The profiling flag file needs to exist and be writable.
7
+ <?php } ?>
8
+ <br /><br />
9
+ <code><?php echo realpath( P3_FLAG_FILE ); ?></code>
10
+ <br /><br />
11
+ <input type="button" class="button" onclick="location.href='<?php echo add_query_arg( array( 'p3_action' => 'current-scan' ) ); ?>';" value="Go back" />
templates/help.php ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/javascript">
2
+ // Set up the tabs
3
+ jQuery( document ).ready( function( $) {
4
+ $( "#toggle-glossary" ).click( function() {
5
+ $( "#glossary-terms" ).toggle();
6
+ if ( "Hide Glossary" == $( "#toggle-glossary" ).html() ) {
7
+ $( "#toggle-glossary" ).html( "Show Glossary" );
8
+ } else {
9
+ $( "#toggle-glossary" ).html( "Hide Glossary" );
10
+ }
11
+ });
12
+ $( "#glossary-terms td.term" ).click( function() {
13
+ var definition = $( "div.definition", $( this ) ).html();
14
+ $( "#p3-glossary-term-display" ).html( definition );
15
+ $( "#p3-glossary-table td.term.hover" ).removeClass( "hover" );
16
+ $( this ).addClass( "hover" );
17
+ });
18
+ $( "#p3-glossary-table td.term:first" ).click();
19
+ $( "#p3-hide-glossary" ).click( function() {
20
+ if ( "Hide" == $( this ).html() ) {
21
+ $( "#p3-glossary-table tbody" ).hide();
22
+ $( "#p3-glossary-table tfoot" ).hide();
23
+ $( this ).html( "Show" );
24
+ } else {
25
+ $( "#p3-glossary-table tbody" ).show();
26
+ $( "#p3-glossary-table tfoot" ).show();
27
+ $( this ).html( "Hide" );
28
+ }
29
+ });
30
+ // $( "#p3-hide-glossary" ).trigger( "click" );
31
+ $( "#p3-glossary-container" ).dblclick( function() {
32
+ $( "#p3-hide-glossary" ).trigger( "click" );
33
+ });
34
+
35
+ // Automatically create the table of contents
36
+ var links = [];
37
+ var i = 1;
38
+ $( "h2.p3-help-question:not(:first )" ).each( function() {
39
+ $( this ).before( '<a name="q' + i + '">&nbsp;</a>' );
40
+ links.push( '<li><a href="#q' + i + '">' + $( this ).html() + '</a></li>' );
41
+ i++;
42
+ });
43
+ $( "div.p3-question blockquote:not(:first )" ).each( function() {
44
+ $( this ).after( '<a href="#top">Back to top</a>' );
45
+ });
46
+ $( "#p3-help-toc" ).html( "<ul>" + links.join( "\n" ) + "</ul>" );
47
+
48
+ $( "div.p3-question" ).corner( "round 8px" )
49
+ });
50
+ </script>
51
+
52
+ <div class="p3-question">
53
+ <a name="top">&nbsp;</a>
54
+ <h2 class="p3-help-question">Contents</h2>
55
+ <blockquote>
56
+ <div id="p3-help-toc"></div>
57
+ </blockquote>
58
+ </div>
59
+
60
+
61
+ <div class="p3-question">
62
+ <h2 class="p3-help-question">What does the P3 plugin do?</h2>
63
+ <blockquote>
64
+ This plugin does just what its name says, it creates a profile of your WordPress site's plugins' performance
65
+ by measuring their impact on your site's load time.
66
+ <br /><br />
67
+ Often times, WordPress sites load slowly because of poorly-configured plugins or because there are so many of
68
+ them. This plugin can help you narrow down the cause of your site's slowness.
69
+ </blockquote>
70
+ </div>
71
+
72
+ <div class="p3-question">
73
+ <h2 class="p3-help-question">How do I use this?</h2>
74
+ <blockquote>
75
+ Simply click "Start Scan" to run an automated scan of your site. The scanner generates some traffic on your
76
+ site and monitors your site's performance on the server, then shows you the results. With this information,
77
+ you can decide what action to take.
78
+ </blockquote>
79
+ </div>
80
+
81
+ <div class="p3-question">
82
+ <h2 class="p3-help-question">What do I do with these results?</h2>
83
+ <blockquote>
84
+ If your site loads in an acceptable time (usually &lt; 0.5 seconds), you might consider other explanation for
85
+ sluggish loading. For example, loading large images, large videos, or a lot of content can cause slowness.
86
+ Tools like <a href="http://www.webpagetest.org/" target="_blank">webpagetest.org</a>, <a href="http://getfirebug.com/"
87
+ target="_blank">Firebug</a>, <a href="http://tools.pingdom.com/" target="_blank">Pingdom tools</a>, or
88
+ <a href="http://developer.apple.com/technologies/safari/developer-tools.html" target="_blank">Safari Developer Tools</a>
89
+ or <a href="http://code.google.com/chrome/devtools/docs/overview.html" target="_blank">Chrome Developer Tools</a> can
90
+ show you a connection breakdown of your site's content.
91
+ </blockquote>
92
+ </div>
93
+
94
+ <div class="p3-question">
95
+ <h2 class="p3-help-question">How does this work?</h2>
96
+ <blockquote>
97
+ When you activate the plugin by clicking "Start Scan," it detects visits from your IP address, and actively monitors
98
+ all <a href="http://php.net/functions" target="_blank">php user defined function calls</a> while the server generates
99
+ your WordPress pages. It then records the information in a report file you can view later. When the scan is complete,
100
+ or you click "Stop Scan," the plugin becomes dormant again.
101
+ </blockquote>
102
+ </div>
103
+
104
+ <div class="p3-question">
105
+ <h2 class="p3-help-question">How does my site load the plugin?</h2>
106
+ <blockquote>
107
+ The plugin should be active at the earliest point in the code execution. The plugin can be loaded through an
108
+ auto_prepend_file configuration directive from a .htaccess file or a <a href="http://php.net/manual/en/configuration.file.per-user.php"
109
+ target="_blank">.user.ini</a> file, but be careful. The .user.ini files are cached, so you must remove the entry from your
110
+ .user.ini file before you remove this plugin.
111
+ <br /><br />
112
+ This plugin automatically enables itself in .htaccess if possible, or, if that doesn't succeed, it creates a
113
+ <a href="http://codex.wordpress.org/Must_Use_Plugins" target="_blank">must-use</a> plugin to load before other plugins.
114
+ If neither of those methods work, it runs like a regular plugin.
115
+ <br /><br />
116
+ You are currently using:
117
+ <?php
118
+
119
+ // .htaccess file test
120
+ $htaccess_file = P3_PATH . '/../../../.htaccess';
121
+ $htaccess_content = '';
122
+ if ( file_exists( $htaccess_file ) ) {
123
+ $htaccess_content = extract_from_markers( $htaccess_file, 'p3-profiler' );
124
+ foreach ( $htaccess_content as $k => $v ) {
125
+ if ( '#' == substr( trim( $v ), 0, 1 ) ) {
126
+ unset( $htaccess_content[$k] ); // Get rid of comment lines
127
+ }
128
+ }
129
+ $htaccess_content = implode( "\n", $htaccess_content );
130
+ }
131
+
132
+ // must-use plugin file
133
+ $mu_file = P3_PATH . '/../../mu-plugins/p3-profiler.php';
134
+
135
+ // List php ini files
136
+ $ini_files = array_filter(
137
+ array_merge(
138
+ array( php_ini_loaded_file() ),
139
+ explode( ',', php_ini_scanned_files() )
140
+ )
141
+ );
142
+ ?>
143
+
144
+ <?php /* .htaccess file is there, the profiler content is there, hasn't been commented out, and the auto_prepend_file directive is active */ ?>
145
+ <?php if (
146
+ file_exists( $htaccess_file ) &&
147
+ !empty( $htaccess_content ) &&
148
+ false !== strpos( $htaccess_content, 'start-profile.php' ) &&
149
+ false !== strpos( ini_get( 'auto_prepend_file' ), 'start-profile.php' ) ) { ?>
150
+ <a href="http://php.net/manual/en/configuration.changes.php" target="_blank">.htaccess file</a>
151
+ - <code><?php echo realpath( $htaccess_file ); ?></code>
152
+ <?php /* the auto_prepend_file directive is active */ ?>
153
+ <?php } elseif ( false !== strpos( ini_get( 'auto_prepend_file' ), 'start-profile.php' ) ){ ?>
154
+ <a href="http://www.php.net/manual/en/configuration.file.php" target="_blank">php.ini</a>
155
+ <?php if ( version_compare( phpversion(), '5.3.0' ) >= 0 ) { ?>
156
+ or <a href="http://www.php.net/manual/en/configuration.file.per-user.php" target="_blank">.user.ini</a>
157
+ <?php } ?>
158
+ entry from one of these files:
159
+ <ul>
160
+ <?php foreach ( $ini_files as $file ) { ?>
161
+ <ol><code><?php echo trim( $file ); ?></code></ol>
162
+ <?php } ?>
163
+ </ul>
164
+ <?php /* must-use plugin file is there and not-empty */ ?>
165
+ <?php } elseif ( file_exists( $mu_file ) && filesize( $mu_file ) > 0 ){ ?>
166
+ <a href="http://codex.wordpress.org/Must_Use_Plugins" target="_blank">must-use plugin</a>
167
+ - <code><?php echo realpath( $mu_file ); ?></code>
168
+ <?php /* default, using this plugin file */ ?>
169
+ <?php } else { ?>
170
+ <a href="http://codex.wordpress.org/Plugins" target="_blank">plugin</a>
171
+ - <code><?php echo realpath( P3_PATH . '/p3-profiler.php' ); ?></code>
172
+ <?php } ?>
173
+ </blockquote>
174
+ </div>
175
+
176
+ <div class="p3-question">
177
+ <h2 class="p3-help-question">How accurate are these results?</h2>
178
+ <blockquote>
179
+ The results have an inherent margin of error because of the nature of the tool and its multi-layered design.
180
+ The plugin changes the environment to measure it, and that makes it impossible to get completely accurate results.
181
+ <br /><br />
182
+ It gets really close, though! The "margin of error" on the Advanced Metrics page displays the discrepancy between
183
+ the measured results (the time for your site's PHP code to completely run) and the expected results (sum of the plugins,
184
+ core, theme, profile load times) to show you the plugin's accuracy.
185
+ <br /><br />
186
+ If you want more accurate results, you'll need to resort to a different profiler like <a href="http://xdebug.org/" target="_blank">xdebug</a>,
187
+ but this will not break down results by plugin.
188
+ </blockquote>
189
+ </div>
190
+
191
+ <div class="p3-question">
192
+ <h2 class="p3-help-question">Why are some plugins slow?</h2>
193
+ <blockquote>
194
+ WordPress is a complex ecosystem of plugins and themes, and it lives on a complex ecosystem of software on your web server.
195
+ <br /><br />
196
+ If a plugin runs slowly just once, it's probably an anomaly, a transient hiccup, and you can safely ignore it.
197
+ <br /><br />
198
+ If a plugin shows slowness once on a reguarly basis (e.g. every time you run a scan, once a day, once an hour), a scheduled
199
+ task might be causing it. Plugins that backup your site, monitor your site for changes, contact outside sources (e.g. RSS feeds),
200
+ warm up caches, etc. can exhibit this kind of behavior.
201
+ <br /><br />
202
+ If a plugin shows as fast-slow-fast-slow-fast-slow, it could be caused as the plugin loads its main code, then a follow-up piece
203
+ of code, like a piece of generated JavaScript.
204
+ <br /><br />
205
+ If a plugin is consistently shows slowness, you might want to contact the plugin author or try deactivating the plugin temporarily
206
+ to see if it makes a difference on your site.
207
+ </blockquote>
208
+ </div>
209
+
210
+ <div class="p3-question">
211
+ <h2 class="p3-help-question">How are these results different from YSlow / PageSpeed / Webpagetest.org / Pingdom Tools?</h2>
212
+ <blockquote>
213
+ This plugin measures how your site was generated on the server. Tools like <a href="http://developer.yahoo.com/yslow/"
214
+ target="_blank">YSlow</a>, <a href="https://developers.google.com/pagespeed/" target="_blank">PageSpeed</a>,
215
+ <a href="http://www.webpagetest.org/" target="_blank">Webpagetest.org</a>, and <a href="http://tools.pingdom.com/fpt/"
216
+ target="_blank">Pingdom Tools</a> measure how your site looks to the browser.
217
+ </blockquote>
218
+ </div>
219
+
220
+ <div class="p3-question">
221
+ <h2 class="p3-help-question">What can interfere with testing?</h2>
222
+ <blockquote>
223
+ Opcode caches can interfere with PHP backtraces. Leaving opcode caches turned on will result in timing that more accurately
224
+ reflects your site's real performance, but the function calls to plugins may be "optimized" out of the backtraces and some
225
+ plugins (especially those with only one hook) might not show up. Disabling opcode caches results in slower times, but shows all plugins.
226
+ <br /><br />
227
+ By default, this plugin attempts to clear any opcode caches before it runs. You can change this setting by clicking "Advanced
228
+ Settings" under "Start Scan."
229
+ <br /><br />
230
+ Caching plugins that have an option to disable caches for logged in users will not give you the same performance profile that
231
+ an anonymous users experience. To get around this, you should select a manual scan, then run an incognito browser window, or run
232
+ another browser, and browse your site as a logged out user. When you're finished, click "I'm done," and your scan should show the
233
+ performance of an anonymous user.
234
+ </blockquote>
235
+ </div>
236
+
237
+ <div class="p3-question">
238
+ <h2 class="p3-help-question">How much room do these profiles take up on my server</h2>
239
+ <blockquote>
240
+ <?php
241
+ $total_size = 0;
242
+ $dir = opendir( P3_PROFILES_PATH );
243
+ while ( false !== ( $file = readdir( $dir ) ) ) {
244
+ if ( '.' != $file && '..' != $file && '.json' == substr( $file, -5 ) ) {
245
+ $total_size += filesize( P3_PROFILES_PATH . "/$file" );
246
+ }
247
+ }
248
+ closedir( $dir );
249
+
250
+ ?>
251
+ The scans are stored in <code><?php echo realpath( P3_PROFILES_PATH ); ?></code> and
252
+ take up <?php echo $this->readable_size( $total_size ); ?> of disk space. Each time you
253
+ run a scan, this storage requirement goes up, and each time you delete a scan, it
254
+ goes down.
255
+ </blockquote>
256
+ </div>
257
+
258
+ <div class="p3-question">
259
+ <h2 class="p3-help-question">Is this plugin always running?</h2>
260
+ <blockquote>
261
+ The short answer is no.
262
+ <br /><br />
263
+ The more detailed answer is the loader is always running, but checks very early in the page
264
+ loading process to see if you've enabled profiling mode and if the user's IP address matches
265
+ the IP address the plugin is monitoring. For multisite installations, it also matches the site URL.
266
+ If all these match, the plugin becomes active and profiles. Otherwise, your site loads as normal
267
+ with no other code overhead.
268
+ <br /><br />
269
+ Deactivating the plugin ensures it's not running at all, and does not delete your scans. However,
270
+ uninstalling the plugin does delete your scans.
271
+ </blockquote>
272
+ </div>
273
+
274
+ <div class="p3-question">
275
+ <h2 class="p3-help-question">How can I test specific pages on my site?</h2>
276
+ <blockquote>
277
+ When you start a scan, choose "Manual Scan" and then you can visit specific links on your site that
278
+ you want to profile. If you want to profile the admin section, just click the "X" in the top right
279
+ of the scan window and you'll be returned to your admin section. You can browse as normal, then come
280
+ back to the profile page and click "Stop Scan" when you're ready to view the results.
281
+ <br /><br />
282
+ To scan your site as an anonymous user, select "Manual Mode" as above, but instead of clicking your
283
+ site in the scan window, open a different browser (or an incognito window) and browse your site as a
284
+ logged out user. When you're done, close that browser and return to your admin. Click "I'm done" and
285
+ view your scan results.
286
+ </blockquote>
287
+ </div>
288
+
289
+ <div class="p3-question">
290
+ <h2 class="p3-help-question">My plugins don't seem to cause site slowness. Why is my site still slow?</h2>
291
+ <blockquote>
292
+ Your site can be slow for a number of reasons. Your site could have a lot of traffic, other sites on
293
+ your server could have a lot of traffic, you could be referencing content from other sites that are slow,
294
+ your internet connection could be slow, your server could be out of RAM, your site could be very image
295
+ heavy, your site could require a lot of HTTP requests, etc. In short, a lot of factors can cause slowness
296
+ on your site
297
+ <br /><br />
298
+ Your next stop should be to <a href="http://tools.pingdom.com/" target="_blank">Pingdom Tools</a>,
299
+ <a href="http://webpagetest.org/" target="_blank">Webpage Test</a>, <a href="http://developer.yahoo.com/yslow/"
300
+ target="_blank">YSlow</a>, <a href="https://developers.google.com/pagespeed/" target="_blank">Google PageSpeed</a>,
301
+ and your browser's development tools like <a href="http://getfirebug.com/" target="_blank">Firebug</a> for Firefox,
302
+ <a href="http://code.google.com/chrome/devtools/docs/overview.html" target="_blank">Chrome Developer Tools</a> for
303
+ Chrome, or <a href="http://developer.apple.com/technologies/safari/developer-tools.html" target="_blank">Safari
304
+ Developer Tools</a> for Safari.
305
+ <br /><br />
306
+ After you've tuned your site up as much as possible, if you're still not happy with its performance, you should
307
+ consult your site/server administrator or hosting support.
308
+ </blockquote>
309
+ </div>
310
+
311
+ <div class="p3-question">
312
+ <h2 class="p3-help-question" style="border-bottom-width: 0px !important;">Glossary</h2>
313
+ <blockquote>
314
+ <div>
315
+ <div id="p3-glossary-container">
316
+ <div class="ui-widget-header" id="p3-glossary-header" style="padding: 8px;">
317
+ <strong>Glossary</strong>
318
+ <div style="position: relative; top: 0px; right: 80px; float: right;">
319
+ <a href="javascript:;" id="p3-hide-glossary">Hide</a>
320
+ </div>
321
+ </div>
322
+ <div>
323
+ <table class="p3-results-table" id="p3-glossary-table" cellpadding="0" cellspacing="0" border="0">
324
+ <tbody>
325
+ <tr>
326
+ <td colspan="2" style="border-left-width: 1px !important;">
327
+ <div id="glossary">
328
+ <table width="100%" cellpadding="0" cellspacing="0" border="0" id="glossary-terms">
329
+ <tr>
330
+ <td width="200" class="term"><strong>Total Load Time</strong>
331
+ <div id="total-load-time-definition" style="display: none;" class="definition">
332
+ The length of time the site took to load. This is an observed measurement (start timing when
333
+ the page was requested, stop timing when the page was delivered to the browser, calculate the
334
+ difference). Lower is better.
335
+ </div>
336
+ </td>
337
+ <td width="400" rowspan="12" id="p3-glossary-term-display">&nbsp;</td>
338
+ </tr>
339
+ <tr>
340
+ <td class="term"><strong>Site Load Time</strong>
341
+ <div id="site-load-time-definition" style="display: none;" class="definition">
342
+ The calculated total load time minus the profile overhead. This is closer to your site's
343
+ real-life load time. Lower is better.
344
+ </div>
345
+ </td>
346
+ </tr>
347
+ <tr>
348
+ <td class="term"><strong>Profile Overhead</strong>
349
+ <div id="profile-overhead-definition" style="display: none;" class="definition">
350
+ The load time spent profiling code. Because the profiler slows down your load time, it is
351
+ important to know how much impact the profiler has. However, it doesn't impact your site's
352
+ real-life load time.
353
+ </div>
354
+ </td>
355
+ </tr>
356
+ <tr>
357
+ <td class="term"><strong>Plugin Load Time</strong>
358
+ <div id="plugin-load-time-definition" style="display: none;" class="definition">
359
+ The load time caused by plugins. Because of WordPress' construction, we can trace a
360
+ function call from a plugin through a theme through the core. The profiler prioritizes
361
+ plugin calls first, theme calls second, and core calls last. Lower is better.
362
+ </div>
363
+ </td>
364
+ </tr>
365
+ <tr>
366
+ <td class="term"><strong>Theme Load Time</strong>
367
+ <div id="theme-load-time-definition" style="display: none;" class="definition">
368
+ The load time spent applying the theme. Because of WordPress' construction, we can trace a
369
+ function call from a plugin through a theme through the core. The profiler prioritizes
370
+ plugin calls first, theme calls second, and core calls last. Lower is better.
371
+ </div>
372
+ </td>
373
+ </tr>
374
+ <tr>
375
+ <td class="term"><strong>Core Load Time</strong>
376
+ <div id="core-load-time-definition" style="display: none;" class="definition">
377
+ The load time caused by the WordPress core. Because of WordPress' construction, we can trace
378
+ a function call from a plugin through a theme through the core. The profiler prioritizes
379
+ plugin calls first, theme calls second, and core calls last. This will probably be constant.
380
+ </div>
381
+ </td>
382
+ </tr>
383
+ <tr>
384
+ <td class="term"><strong>Margin of Error</strong>
385
+ <div id="drift-definition" style="display: none;" class="definition">
386
+ This is the difference between the observed runtime (what actually happened) and expected
387
+ runtime (adding the plugin runtime, theme runtime, core runtime, and profiler overhead).
388
+ <br /><br />
389
+ There are several reasons this margin of error can exist. Most likely, the profiler is
390
+ missing microseconds while adding the runtime it observed. Using a network clock to set the
391
+ time (NTP) can also cause minute timing changes.
392
+ <br /><br />
393
+ Ideally, this number should be zero, but there's nothing you can do to change it. It
394
+ will give you an idea of how accurate the other results are.
395
+ </div>
396
+ </td>
397
+ </tr>
398
+ <tr>
399
+ <td class="term"><strong>Observed</strong>
400
+ <div id="observed-definition" style="display: none;" class="definition">
401
+ The time the site took to load. This is an observed measurement (start timing when the
402
+ page was requested, stop timing when the page was delivered to the browser, calculate the
403
+ difference).
404
+ </div>
405
+ </td>
406
+ </tr>
407
+ <tr>
408
+ <td class="term"><strong>Expected</strong>
409
+ <div id="expected-definition" style="display: none;" class="definition">
410
+ The expected site load time calculated by adding plugin load time, core load time, theme
411
+ load time, and profiler overhead.
412
+ </div>
413
+ </td>
414
+ </tr>
415
+ <tr>
416
+ <td class="term"><strong>Plugin Function Calls</strong>
417
+ <div id="plugin-funciton-calls-definition" style="display: none;" class="definition">
418
+ The number of PHP function calls generated by a plugin. Fewer is better.
419
+ </div>
420
+ </td>
421
+ </tr>
422
+ <tr>
423
+ <td class="term"><strong>Memory Usage</strong>
424
+ <div id="memory-usage-definition" style="display: none;" class="definition">
425
+ The amount of RAM usage observed. This is reported by
426
+ <a href="http://php.net/memory_get_peak_usage"
427
+ target="_blank">memory_get_peak_usage()</a>. Lower is better.
428
+ </div>
429
+ </td>
430
+ </tr>
431
+ <tr>
432
+ <td class="term"><strong>MySQL Queries</strong>
433
+ <div id="mysql-queries-definition" style="display: none;" class="definition">
434
+ The number of queries sent to the database. This reported by the WordPress function
435
+ <a href="http://codex.wordpress.org/Function_Reference/get_num_queries"
436
+ target="_new">get_num_queries()</a>. Fewer is better.
437
+ </div>
438
+ </td>
439
+ </tr>
440
+ </table>
441
+ </div>
442
+ </td>
443
+ </tr>
444
+ </tbody>
445
+ </table>
446
+ </div>
447
+ </div>
448
+ </div>
449
+ </blockquote>
450
+ </div>
451
+
452
+ <div class="p3-question">
453
+ <h2 class="p3-help-question">License</h2>
454
+ <blockquote>
455
+ <strong>P3 (Plugin Performance Profiler)</strong>
456
+ <br />
457
+ <?php if (date('Y') > 2011) : ?>
458
+ Copyright &copy; 2011-<?php echo date('Y'); ?> <a href="http://www.godaddy.com/" target="_blank">GoDaddy.com</a>. All rights reserved.
459
+ <?php else : ?>
460
+ Copyright &copy; 2011 <a href="http://www.godaddy.com/" target="_blank">GoDaddy.com</a>. All rights reserved.
461
+ <?php endif; ?>
462
+ <br /><br />
463
+ This program is offered under the terms of the GNU General Public License Version 2 as published by the Free Software Foundation.
464
+ <br /><br />
465
+ This program offered WITHOUT WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
466
+ See the GNU General Public License Version 2 for the specific terms.
467
+ <br /><br />
468
+ A copy of the GNU General Public License has been provided with this program. Alternatively, you may find a copy of the license here:
469
+ <a href="http://www.gnu.org/licenses/gpl-2.0.html" target="_blank">http://www.gnu.org/licenses/gpl-2.0.html</a>.
470
+ </blockquote>
471
+ </div>
templates/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php header( 'Status: 404 Not found' ); ?>
2
+ Not found
templates/list-scans.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form id="scans-filter" method="post">
2
+ <input type="hidden" name="page" value="<?php echo $_REQUEST ['page']?>" />
3
+ <?php echo wp_nonce_field( 'delete_scans', 'p3_nonce' ); ?>
4
+ <?php $this->scan_table->display(); ?>
5
+ </form>
6
+ <script type="text/javascript">
7
+ jQuery(document).ready(function($) {
8
+ $("input:submit", "#scans-filter").click(function(evt) {
9
+ if (0 == $("input:checked", $("#scans-filter")).length) {
10
+ evt.stopPropagation();
11
+ evt.preventDefault();
12
+ } else if (!confirm('Are you sure you want to delete these scans?')) {
13
+ evt.stopPropagation();
14
+ evt.preventDefault();
15
+ } else {
16
+ return true;
17
+ }
18
+ });
19
+ $("a.delete-scan").click(function(evt) {
20
+ if (confirm('Are you sure you want to delete this scan?')) {
21
+
22
+ // De-select the checkboxes
23
+ $("#scans-filter input:checked").prop("checked", false);
24
+
25
+ // Find the parent TR
26
+ $tr = $(this).parents("tr");
27
+
28
+ // Check the checkbox
29
+ $("input:checkbox", $tr).prop("checked", true);
30
+
31
+ // Select the delete action
32
+ $("select:first", "#scans-filter").val("delete");
33
+
34
+ // Submit the form
35
+ $("#scans-filter").submit();
36
+ }
37
+ });
38
+ });
39
+ </script>
templates/template.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $p3_action = '';
3
+ if ( !empty( $_REQUEST['p3_action'] ) ) {
4
+ $p3_action = $_REQUEST['p3_action'];
5
+ }
6
+ $scan = $this->get_latest_profile();
7
+ if ( empty( $p3_action ) || 'current-scan' == $p3_action ) {
8
+ $p3_action = 'current-scan';
9
+ } elseif ( 'view-scan' == $p3_action && !empty( $_REQUEST['name'] ) ) {
10
+ $scan = sanitize_file_name( $_REQUEST['name'] );
11
+ if ( !file_exists( P3_PROFILES_PATH . "/$scan" ) ) {
12
+ wp_die( '<div id="message" class="error"><p>Scan does not exist</p></div>' );
13
+ }
14
+ $scan = P3_PROFILES_PATH . "/$scan";
15
+ }
16
+ $button_current_checked = '';
17
+ $button_history_checked = '';
18
+ $button_help_checked = '';
19
+ if ( 'current-scan' == $p3_action ) {
20
+ $button_current_checked = 'checked="checked"';
21
+ } elseif ( 'help' == $p3_action || 'fix-flag-file' == $p3_action ) {
22
+ $button_help_checked = 'checked="checked"';
23
+ } else {
24
+ $button_history_checked = 'checked="checked"';
25
+ }
26
+ ?>
27
+ <script type="text/javascript">
28
+ jQuery( document ).ready( function( $) {
29
+ $( "#button-current-scan" ).click( function() {
30
+ location.href = "<?php echo add_query_arg( array( 'p3_action' => 'current-scan' ) ); ?>";
31
+ });
32
+ $( "#button-history-scans" ).click( function() {
33
+ location.href = "<?php echo add_query_arg( array( 'p3_action' => 'list-scans' ) ); ?>";
34
+ });
35
+ $( "#button-help" ).click( function() {
36
+ location.href = "<?php echo add_query_arg( array( 'p3_action' => 'help' ) ); ?>";
37
+ })
38
+ $( ".p3-button" ).button();
39
+ $( "#p3-navbar" ).buttonset();
40
+ $( "#p3-navbar" ).corner( "round 8px" );
41
+ $( ".p3-big-button" ).buttonset();
42
+ $( "#p3-results-table tr:even" ).addClass( "even" );
43
+ $( "td div.row-actions-visible" ).hide();
44
+ $( "table.wp-list-table td" ).mouseover( function() {
45
+ $( "div.row-actions-visible", $( this ) ).show();
46
+ }).mouseout( function() {
47
+ $( "div.row-actions-visible", $( this ) ).hide();
48
+ });
49
+ $( ".qtip-tip" ).each( function() {
50
+ $( this ).qtip({
51
+ content: $( this ).attr( "title" ),
52
+ position: {
53
+ my: 'top center',
54
+ at: 'bottom center'
55
+ },
56
+ style: {
57
+ classes: 'ui-tooltip-blue ui-tooltip-shadow'
58
+ }
59
+ });
60
+ });
61
+ });
62
+ </script>
63
+ <div class="wrap">
64
+
65
+ <!-- Header icon / title -->
66
+ <div id="icon-plugins" class="icon32"><br/></div>
67
+ <h2>P3 - Plugin Performance Profiler</h2>
68
+
69
+ <!-- Header navbar -->
70
+ <div class="ui-widget-header" id="p3-navbar">
71
+ <input type="radio" name="p3-nav" id="button-current-scan" <?php echo $button_current_checked; ?> />
72
+ <label for="button-current-scan">Current</label>
73
+ <input type="radio" name="p3-nav" id="button-history-scans" <?php echo $button_history_checked; ?> />
74
+ <label for="button-history-scans">History</label>
75
+ <input type="radio" name="p3-nav" id="button-help" <?php echo $button_help_checked; ?> /><label for="button-help">Help</label>
76
+ </div>
77
+
78
+ <!-- Start / stop button and callouts -->
79
+ <?php
80
+
81
+ // If there's a scan, create a viewer object
82
+ if ( !empty( $scan ) ) {
83
+ try {
84
+ $profile = new P3_Profile_Reader( $scan );
85
+ } catch ( Exception $e ) {
86
+ wp_die( '<div id="message" class="error"><p>Error reading scan</p></div>' );
87
+ }
88
+ } else {
89
+ $profile = null;
90
+ }
91
+
92
+ // Show the callouts bar
93
+ require_once P3_PATH . '/templates/callouts.php';
94
+ ?>
95
+
96
+ <!-- View scan or show a list of scans -->
97
+ <?php if ( ( 'current-scan' == $p3_action && !empty( $scan ) ) || 'view-scan' == $p3_action ) { ?>
98
+ <?php include_once P3_PATH . '/templates/view-scan.php'; ?>
99
+ <?php } elseif ( 'help' == $p3_action ) { ?>
100
+ <?php include_once P3_PATH . '/templates/help.php'; ?>
101
+ <?php } elseif ( 'fix-flag-file' == $p3_action ) { ?>
102
+ <?php include_once P3_PATH . '/templates/fix-flag-file.php'; ?>
103
+ <?php } else { ?>
104
+ <?php include_once P3_PATH . '/templates/list-scans.php'; ?>
105
+ <?php } ?>
106
+
107
+ </div>
108
+
109
+ <div id="p3-copyright">
110
+ <img src="<?php echo plugins_url() . '/p3-profiler/logo.gif'; ?>" alt="GoDaddy.com logo" title="GoDaddy.com logo" />
111
+ <br />
112
+ <?php if (date('Y') > 2011) : ?>
113
+ Copyright &copy; 2011-<?php echo date('Y'); ?> <a href="http://www.godaddy.com/" target="_blank">GoDaddy.com</a>. All rights reserved.
114
+ <?php else : ?>
115
+ Copyright &copy; 2011 <a href="http://www.godaddy.com/" target="_blank">GoDaddy.com</a>. All rights reserved.
116
+ <?php endif; ?>
117
+ </div>
templates/view-scan.php ADDED
@@ -0,0 +1,993 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $url_stats = array();
3
+ $domain = '';
4
+ if ( !empty( $profile ) ) {
5
+ $url_stats = $profile->get_stats_by_url();
6
+ $domain = @parse_url( $profile->report_url, PHP_URL_HOST );
7
+ }
8
+ $pie_chart_id = substr( md5( uniqid() ), -8 );
9
+ $runtime_chart_id = substr( md5( uniqid() ), -8 );
10
+ $query_chart_id = substr( md5( uniqid() ), -8 );
11
+ $component_breakdown_chart_id = substr( md5( uniqid() ), -8 );
12
+ $component_runtime_chart_id = substr( md5( uniqid() ), -8 );
13
+ ?>
14
+ <script type="text/javascript">
15
+
16
+ /**************************************************************/
17
+ /** Init **/
18
+ /**************************************************************/
19
+
20
+ // Raw json data ( used in the charts for tooltip data
21
+ var _data = [];
22
+ <?php if ( !empty( $scan ) && file_exists( $scan ) ) { ?>
23
+ <?php foreach ( file( $scan, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ) as $line ) { ?>
24
+ _data.push(<?php echo $line; ?>);
25
+ <?php } ?>
26
+ <?php } ?>
27
+
28
+ // Set up the tabs
29
+ jQuery( document ).ready( function( $) {
30
+ $( "#p3-tabs" ).tabs();
31
+ $( "#results-table tr:even" ).addClass( "even" );
32
+ $( "#p3-email-sending-dialog" ).dialog({
33
+ 'autoOpen' : false,
34
+ 'closeOnEscape' : false,
35
+ 'draggable' : false,
36
+ 'resizable' : false,
37
+ 'modal' : true,
38
+ 'width' : 325,
39
+ 'height' : 120,
40
+ 'dialogClass' : 'noTitle'
41
+ });
42
+ $( "#p3-email-results-dialog" ).dialog({
43
+ 'autoOpen' : false,
44
+ 'closeOnEscape' : true,
45
+ 'draggable' : false,
46
+ 'resizable' : false,
47
+ 'modal' : true,
48
+ 'width' : 500,
49
+ 'height' : 560,
50
+ 'title' : "Email Report",
51
+ 'buttons' :
52
+ [
53
+ {
54
+ text: 'Send',
55
+ 'class' : 'button-secondary',
56
+ click: function() {
57
+ data = {
58
+ 'p3_to' : jQuery( '#p3-email-results-to' ).val(),
59
+ 'p3_from' : jQuery( '#p3-email-results-from' ).val(),
60
+ 'p3_subject' : jQuery( '#p3-email-results-subject' ).val(),
61
+ 'p3_results' : jQuery( "#p3-email-results-results" ).val(),
62
+ 'p3_message' : jQuery( "#p3-email-results-message" ).val(),
63
+ 'action' : 'p3_send_results',
64
+ 'p3_nonce' : '<?php echo wp_create_nonce( 'p3_ajax_send_results' ); ?>'
65
+ }
66
+
67
+ // Open the "loading" dialog
68
+ $( "#p3-email-sending-success" ).hide();
69
+ $( "#p3-email-sending-error" ).hide();
70
+ $( "#p3-email-sending-loading" ).show();
71
+ $( "#p3-email-sending-close" ).hide();
72
+ $( "#p3-email-sending-dialog" ).dialog( "open" );
73
+
74
+ // Send the data
75
+ jQuery.post( ajaxurl, data, function( response ) {
76
+ if ( "1" == response ) {
77
+ $( "#p3-email-success-recipient" ).html( jQuery( '#p3-email-results-to' ).val() );
78
+ $( "#p3-email-sending-success" ).show();
79
+ $( "#p3-email-sending-error" ).hide();
80
+ $( "#p3-email-sending-loading" ).hide();
81
+ $( "#p3-email-sending-close" ).show();
82
+ } else {
83
+ if ( "-1" == response ) {
84
+ $( "#p3-email-error" ).html( "nonce error" );
85
+ } else if ( "0" == response.charAt( 0 ) ) {
86
+ $( "#p3-email-error" ).html( response.substr( 2 ) );
87
+ } else {
88
+ $( "#p3-email-error" ).html( "unknown error" );
89
+ }
90
+ $( "#p3-email-sending-success" ).hide();
91
+ $( "#p3-email-sending-error" ).show();
92
+ $( "#p3-email-sending-loading" ).hide();
93
+ $( "#p3-email-sending-close" ).show();
94
+ }
95
+ });
96
+ }
97
+ },
98
+ {
99
+ text: 'Cancel',
100
+ 'class': 'p3-cancel-button',
101
+ click: function() {
102
+ $( this ).dialog( "close" );
103
+ }
104
+ }
105
+ ]
106
+ });
107
+ $( "#p3-email-sending-close-submit" ).click( function() {
108
+ $( this ).prop( "checked", true );
109
+ $( this ).button( "refresh" );
110
+ $( "#p3-email-sending-dialog" ).dialog( "close" );
111
+ $( "#p3-email-results-dialog" ).dialog( "close" );
112
+ });
113
+ $( "#p3-email-results" ).click( function() {
114
+ $( "#p3-email-results-dialog" ).dialog( "open" );
115
+ });
116
+ $( "#p3-email-sending-close" ).buttonset();
117
+ });
118
+
119
+
120
+
121
+ /**************************************************************/
122
+ /** Hover function for charts **/
123
+ /**************************************************************/
124
+ var previousPoint = null;
125
+ function showTooltip( x, y, contents ) {
126
+ jQuery( '<div id="p3-tooltip">' + contents + '</div>' ).css(
127
+ {
128
+ position: 'absolute',
129
+ display: 'none',
130
+ top: y + 5,
131
+ left: x + 5,
132
+ border: '1px solid #fdd',
133
+ padding: '2px',
134
+ 'background-color': '#fee',
135
+ opacity: 0.80
136
+ }
137
+ ).appendTo( "body" ).fadeIn( 200 );
138
+ }
139
+
140
+
141
+
142
+ /**************************************************************/
143
+ /** Plugin pie chart **/
144
+ /**************************************************************/
145
+ var data_<?php echo $pie_chart_id; ?> = [
146
+ <?php if ( !empty( $profile ) ){ ?>
147
+ <?php foreach ( $profile->plugin_times as $k => $v ) { ?>
148
+ {
149
+ label: "<?php echo $k; ?>",
150
+ data: <?php echo $v; ?>
151
+ },
152
+ <?php } ?>
153
+ <?php } else { ?>
154
+ { label: 'No plugins', data: 1}
155
+ <?php } ?>
156
+ ];
157
+ jQuery( document ).ready( function( $) {
158
+ $.plot( $(
159
+ "#p3-holder_<?php echo $pie_chart_id; ?>" ),
160
+ data_<?php echo $pie_chart_id; ?>,
161
+ {
162
+ series: {
163
+ pie: {
164
+ show: true,
165
+ combine: {
166
+ threshold: .03 // 3% or less
167
+ }
168
+ }
169
+ },
170
+ grid: {
171
+ hoverable: true,
172
+ clickable: true
173
+ },
174
+ legend: {
175
+ container: $( "#p3-legend_<?php echo $pie_chart_id; ?>" )
176
+ }
177
+ });
178
+
179
+ $( "#p3-holder_<?php echo $pie_chart_id; ?>" ).bind( "plothover", function ( event, pos, item ) {
180
+ if ( item ) {
181
+ $( "#p3-tooltip" ).remove();
182
+ showTooltip( pos.pageX, pos.pageY,
183
+ item.series.label + "<br />" + Math.round( item.series.percent ) + "%<br />" +
184
+ Math.round( item.datapoint[1][0][1] * Math.pow( 10, 4 ) ) / Math.pow( 10, 4 ) + " seconds"
185
+ );
186
+ } else {
187
+ $( "#p3-tooltip" ).remove();
188
+ }
189
+ });
190
+ });
191
+
192
+
193
+
194
+ /**************************************************************/
195
+ /** Runtime line chart data **/
196
+ /**************************************************************/
197
+ var chart_<?php echo $runtime_chart_id; ?> = null;
198
+ var data_<?php echo $runtime_chart_id; ?> = [
199
+ {
200
+ label: "WP Core time",
201
+ data: [
202
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
203
+ [
204
+ <?php echo $k + 1; ?>,
205
+ <?php echo $v['core']; ?>
206
+ ],
207
+ <?php } ?>
208
+ ]
209
+ },
210
+ {
211
+ label: "Theme time",
212
+ data: [
213
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
214
+ [
215
+ <?php echo $k + 1; ?>,
216
+ <?php echo $v['theme']; ?>
217
+ ],
218
+ <?php } ?>
219
+ ]
220
+ },
221
+ {
222
+ label: "Plugin time",
223
+ data: [
224
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
225
+ [
226
+ <?php echo $k + 1; ?>,
227
+ <?php echo $v['plugins']; ?>
228
+ ],
229
+ <?php } ?>
230
+ ]
231
+ }
232
+ ];
233
+ jQuery( document ).ready( function( $) {
234
+ chart_<?php echo $runtime_chart_id; ?> = $.plot( $(
235
+ "#p3-holder_<?php echo $runtime_chart_id; ?>" ),
236
+ data_<?php echo $runtime_chart_id; ?>,
237
+ {
238
+ series: {
239
+ lines: { show: true },
240
+ points: { show: true },
241
+ },
242
+ grid: {
243
+ hoverable: true,
244
+ clickable: true
245
+ },
246
+ legend : {
247
+ container: $( "#p3-legend_<?php echo $runtime_chart_id; ?>" )
248
+ },
249
+ zoom: {
250
+ interactive: true
251
+ },
252
+ pan: {
253
+ interactive: true
254
+ },
255
+ xaxis: {
256
+ show: false
257
+ }
258
+ });
259
+
260
+ // zoom buttons
261
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">-</div>' )
262
+ .appendTo( $( "#p3-holder_<?php echo $runtime_chart_id; ?>" ).parent() ).click( function ( e ) {
263
+ e.preventDefault();
264
+ chart_<?php echo $runtime_chart_id; ?>.zoomOut();
265
+ });
266
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">+</div>' )
267
+ .appendTo( $( "#p3-holder_<?php echo $runtime_chart_id; ?>" ).parent() ).click( function ( e ) {
268
+ e.preventDefault();
269
+ chart_<?php echo $runtime_chart_id; ?>.zoom();
270
+ });
271
+
272
+ $( "#p3-holder_<?php echo $runtime_chart_id; ?>" ).bind( "plothover", function ( event, pos, item ) {
273
+ if ( item ) {
274
+ if ( previousPoint != item.dataIndex ) {
275
+ previousPoint = item.dataIndex;
276
+
277
+ $( "#p3-tooltip" ).remove();
278
+ var x = item.datapoint[0].toFixed( 2 ),
279
+ y = item.datapoint[1].toFixed( 2 );
280
+
281
+ url = _data[item["dataIndex"]]["url"];
282
+
283
+ // Get rid of the domain
284
+ url = url.replace(/http[s]?:\/\/<?php echo $domain; ?>(:\d+)?/, "" );
285
+
286
+ showTooltip( item.pageX, item.pageY,
287
+ item.series.label + "<br />" +
288
+ url + "<br />" +
289
+ y + " seconds" );
290
+ }
291
+ } else {
292
+ $( "#p3-tooltip" ).remove();
293
+ previousPoint = null;
294
+ }
295
+ });
296
+ });
297
+
298
+
299
+
300
+ /**************************************************************/
301
+ /** Query line chart data **/
302
+ /**************************************************************/
303
+ var chart_<?php echo $query_chart_id; ?> = null;
304
+ var data_<?php echo $query_chart_id; ?> = [
305
+ {
306
+ label: "# of Queries",
307
+ data: [
308
+ <?php if ( !empty( $profile ) ){ ?>
309
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
310
+ [
311
+ <?php echo $k + 1; ?>,
312
+ <?php echo $v['queries']; ?>
313
+ ],
314
+ <?php } ?>
315
+ <?php } ?>
316
+ ]
317
+ }
318
+ ];
319
+ jQuery( document ).ready( function( $) {
320
+ chart_<?php echo $query_chart_id; ?> = $.plot( $(
321
+ "#p3-holder_<?php echo $query_chart_id; ?>" ),
322
+ data_<?php echo $query_chart_id; ?>,
323
+ {
324
+ series: {
325
+ lines: { show: true },
326
+ points: { show: true }
327
+ },
328
+ grid: {
329
+ hoverable: true,
330
+ clickable: true
331
+ },
332
+ legend : {
333
+ container: $( "#p3-legend_<?php echo $query_chart_id; ?>" )
334
+ },
335
+ zoom: {
336
+ interactive: true
337
+ },
338
+ pan: {
339
+ interactive: true
340
+ },
341
+ xaxis: {
342
+ show: false
343
+ }
344
+ });
345
+
346
+ // zoom buttons
347
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">-</div>' )
348
+ .appendTo( $( "#p3-holder_<?php echo $query_chart_id; ?>" ).parent() ).click( function ( e ) {
349
+ e.preventDefault();
350
+ chart_<?php echo $query_chart_id; ?>.zoomOut();
351
+ });
352
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">+</div>' )
353
+ .appendTo( $( "#p3-holder_<?php echo $query_chart_id; ?>" ).parent() ).click( function ( e ) {
354
+ e.preventDefault();
355
+ chart_<?php echo $query_chart_id; ?>.zoom();
356
+ });
357
+
358
+ $( "#p3-holder_<?php echo $query_chart_id; ?>" ).bind( "plothover", function ( event, pos, item ) {
359
+ if ( item ) {
360
+ if ( previousPoint != item.dataIndex ) {
361
+ previousPoint = item.dataIndex;
362
+
363
+ $( "#p3-tooltip" ).remove();
364
+ var x = item.datapoint[0].toFixed( 2 ),
365
+ y = item.datapoint[1]; //.toFixed( 2 );
366
+
367
+ url = _data[item["dataIndex"]]["url"];
368
+
369
+ // Get rid of the domain
370
+ url = url.replace(/http[s]?:\/\/<?php echo $domain; ?>(:\d+)?/, "" );
371
+
372
+ qword = ( y == 1 ) ? "query" : "queries";
373
+ showTooltip( item.pageX, item.pageY,
374
+ item.series.label + "<br />" +
375
+ url + "<br />" +
376
+ y + " " + qword );
377
+ }
378
+ } else {
379
+ $( "#p3-tooltip" ).remove();
380
+ previousPoint = null;
381
+ }
382
+ });
383
+ });
384
+
385
+
386
+ /**************************************************************/
387
+ /** Compnent bar chart data **/
388
+ /**************************************************************/
389
+ var chart_<?php echo $component_breakdown_chart_id; ?> = null;
390
+ var data_<?php echo $component_breakdown_chart_id; ?> = [
391
+ {
392
+ label: 'Site Load Time',
393
+ bars: {show: false},
394
+ points: {show: false},
395
+ lines: {show: true, lineWidth: 3},
396
+ shadowSize: 0,
397
+ data: [
398
+ <?php for ( $i = -999 ; $i < 999 + 2; $i++ ) { ?>
399
+ [
400
+ <?php echo $i; ?>,
401
+ <?php echo $profile->averages['site']; ?>
402
+ ],
403
+ <?php } ?>
404
+ ]
405
+ },
406
+ {
407
+ label: 'WP Core Time',
408
+ data: [[0, <?php echo $profile->averages['core']; ?>]]
409
+ },
410
+ {
411
+ label: 'Theme',
412
+ data: [[1, <?php echo $profile->averages['theme']; ?>]]
413
+ },
414
+ <?php $i = 2; $other = 0; ?>
415
+ <?php foreach ( $profile->plugin_times as $k => $v ) { ?>
416
+ {
417
+ label: '<?php echo $k; ?>',
418
+ data: [[
419
+ <?php echo $i++; ?>,
420
+ <?php echo $v; ?>
421
+ ]],
422
+ },
423
+ <?php } ?>
424
+ ];
425
+
426
+ jQuery( document ).ready( function( $) {
427
+ chart_<?php echo $component_breakdown_chart_id; ?> = $.plot( $(
428
+ "#p3-holder_<?php echo $component_breakdown_chart_id; ?>" ),
429
+ data_<?php echo $component_breakdown_chart_id; ?>,
430
+ {
431
+ series: {
432
+ bars: {
433
+ show: true,
434
+ barWidth: 0.9,
435
+ align: 'center'
436
+ },
437
+ stack: false,
438
+ lines: {
439
+ show: false,
440
+ steps: false,
441
+ }
442
+ },
443
+ grid: {
444
+ hoverable: true,
445
+ clickable: true,
446
+ },
447
+ xaxis: {
448
+ show: false,
449
+ ticks: [
450
+ [0, 'Site Load Time'],
451
+ [1, 'WP Core Time'],
452
+ [2, 'Theme'],
453
+ <?php $i = 3; ?>
454
+ <?php foreach ( $profile->plugin_times as $k => $v ) { ?>
455
+ [
456
+ <?php echo $i++ ?>,
457
+ '<?php echo $k; ?>'
458
+ ],
459
+ <?php } ?>
460
+ ],
461
+ min: 0,
462
+ max: <?php echo $i; ?>,
463
+ },
464
+ legend : {
465
+ container: $( "#p3-legend_<?php echo $component_breakdown_chart_id; ?>" )
466
+ },
467
+ zoom: {
468
+ interactive: true
469
+ },
470
+ pan: {
471
+ interactive: true
472
+ }
473
+ });
474
+
475
+ $( "#p3-holder_<?php echo $component_breakdown_chart_id; ?>" ).bind( "plothover", function ( event, pos, item ) {
476
+ if ( item ) {
477
+ $( "#p3-tooltip" ).remove();
478
+ showTooltip( pos.pageX, pos.pageY,
479
+ item.series.label + "<br />" + Math.round( item.datapoint[1] * Math.pow( 10, 4 ) ) / Math.pow( 10, 4 ) + " seconds"
480
+ );
481
+ } else {
482
+ $( "#p3-tooltip" ).remove();
483
+ }
484
+ });
485
+
486
+ // zoom buttons
487
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">-</div>' )
488
+ .appendTo( $( "#p3-holder_<?php echo $component_breakdown_chart_id; ?>" ).parent() ).click( function ( e ) {
489
+ e.preventDefault();
490
+ chart_<?php echo $component_breakdown_chart_id; ?>.zoomOut();
491
+ });
492
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">+</div>' )
493
+ .appendTo( $( "#p3-holder_<?php echo $component_breakdown_chart_id; ?>" ).parent() ).click( function ( e ) {
494
+ e.preventDefault();
495
+ chart_<?php echo $component_breakdown_chart_id; ?>.zoom();
496
+ });
497
+ });
498
+
499
+ /**************************************************************/
500
+ /** Runtime by component line chart data **/
501
+ /**************************************************************/
502
+ var chart_<?php echo $component_runtime_chart_id; ?> = null;
503
+ var data_<?php echo $component_runtime_chart_id; ?> = [
504
+ {
505
+ label: "WP Core Time",
506
+ data: [
507
+ <?php if ( !empty( $profile ) ){ ?>
508
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
509
+ [
510
+ <?php echo $k + 1; ?>,
511
+ <?php echo $v['core']; ?>
512
+ ],
513
+ <?php } ?>
514
+ <?php } ?>
515
+ ]
516
+ },
517
+ {
518
+ label: "Theme",
519
+ data: [
520
+ <?php if ( !empty( $profile ) ){ ?>
521
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
522
+ [
523
+ <?php echo $k + 1; ?>,
524
+ <?php echo $v['theme']; ?>
525
+ ],
526
+ <?php } ?>
527
+ <?php } ?>
528
+ ]
529
+ },
530
+ <?php if ( !empty( $profile ) && !empty( $profile->detected_plugins ) ) { ?>
531
+ <?php foreach ( $profile->detected_plugins as $plugin ) { ?>
532
+ {
533
+ label: "<?php echo $plugin; ?>",
534
+ data: [
535
+ <?php foreach ( array_values( $url_stats ) as $k => $v ) { ?>
536
+ <?php if ( array_key_exists( $plugin, $v['breakdown'] ) ) { ?>
537
+ [
538
+ <?php echo $k + 1; ?>,
539
+ <?php echo $v['breakdown'][$plugin]; ?>
540
+ ],
541
+ <?php } ?>
542
+ <?php } ?>
543
+ ]
544
+ },
545
+ <?php } ?>
546
+ <?php } ?>
547
+ ];
548
+ jQuery( document ).ready( function( $) {
549
+ chart_<?php echo $component_runtime_chart_id; ?> = $.plot(
550
+ $( "#p3-holder_<?php echo $component_runtime_chart_id; ?>" ),
551
+ data_<?php echo $component_runtime_chart_id; ?>,
552
+ {
553
+ series: {
554
+ lines: { show: true },
555
+ points: { show: true }
556
+ },
557
+ grid: {
558
+ hoverable: true,
559
+ clickable: true
560
+ },
561
+ legend : {
562
+ container: $( "#p3-legend_<?php echo $component_runtime_chart_id; ?>" )
563
+ },
564
+ zoom: {
565
+ interactive: true
566
+ },
567
+ pan: {
568
+ interactive: true
569
+ },
570
+ xaxis: {
571
+ show: false
572
+ }
573
+ });
574
+
575
+ $( "#p3-holder_<?php echo $component_runtime_chart_id; ?>" ).bind( "plothover", function ( event, pos, item ) {
576
+ if ( item ) {
577
+ if ( previousPoint != item.dataIndex ) {
578
+ previousPoint = item.dataIndex;
579
+
580
+ $( "#p3-tooltip" ).remove();
581
+ var x = item.datapoint[0].toFixed( 2 ),
582
+ y = item.datapoint[1]; //.toFixed( 2 );
583
+
584
+ url = _data[item["dataIndex"]]["url"];
585
+
586
+ // Get rid of the domain
587
+ url = url.replace(/http[s]?:\/\/<?php echo $domain; ?>(:\d+)?/, "" );
588
+
589
+ showTooltip( item.pageX, item.pageY,
590
+ item.series.label + "<br />" +
591
+ url + "<br />" +
592
+ y + " seconds" );
593
+ }
594
+ } else {
595
+ $( "#p3-tooltip" ).remove();
596
+ previousPoint = null;
597
+ }
598
+ });
599
+
600
+ // zoom buttons
601
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">-</div>' )
602
+ .appendTo( $( "#p3-holder_<?php echo $component_runtime_chart_id; ?>" ).parent() ).click( function ( e ) {
603
+ e.preventDefault();
604
+ chart_<?php echo $component_runtime_chart_id; ?>.zoomOut();
605
+ });
606
+ $( '<div class="button" style="float: left; position: relative; left: 440px; top: -290px;">+</div>' )
607
+ .appendTo( $( "#p3-holder_<?php echo $component_runtime_chart_id; ?>" ).parent() ).click( function ( e ) {
608
+ e.preventDefault();
609
+ chart_<?php echo $component_runtime_chart_id; ?>.zoom();
610
+ });
611
+ });
612
+
613
+ </script>
614
+ <div id="p3-tabs">
615
+ <ul>
616
+ <li><a href="#p3-tabs-1">Runtime By Plugin</a></li>
617
+ <li><a href="#p3-tabs-5">Detailed Breakdown</a></li>
618
+ <li><a href="#p3-tabs-2">Simple Timeline</a></li>
619
+ <li><a href="#p3-tabs-6">Detailed Timeline</a></li>
620
+ <li><a href="#p3-tabs-3">Query Timeline</a></li>
621
+ <li><a href="#p3-tabs-4">Advanced Metrics</a></li>
622
+ </ul>
623
+
624
+ <!-- Plugin bar chart -->
625
+ <div id="p3-tabs-5">
626
+ <h2>Detailed Breakdown</h2>
627
+ <div class="p3-plugin-graph">
628
+ <table>
629
+ <tr>
630
+ <td rowspan="2">
631
+ <div class="p3-y-axis-label">
632
+ <em class="p3-em">Seconds</em>
633
+ </div>
634
+ </td>
635
+ <td rowspan="2">
636
+ <div class="p3-line p3-graph-holder" id="p3-holder_<?php echo $component_breakdown_chart_id; ?>"></div>
637
+ </td>
638
+ <td>
639
+ <h3>Legend</h3>
640
+ </td>
641
+ </tr>
642
+ <tr>
643
+ <td>
644
+ <div class="p3-custom-legend" id="p3-legend_<?php echo $component_breakdown_chart_id; ?>"></div>
645
+ </td>
646
+ </tr>
647
+ <tr>
648
+ <td>&nbsp;</td>
649
+ <td colspan="2">
650
+ <div class="p3-x-axis-label" style="top: -10px;">
651
+ <em class="p3-em">Component</em>
652
+ </div>
653
+ </td>
654
+ </tr>
655
+ </table>
656
+ </div>
657
+ </div>
658
+
659
+ <!-- Plugin pie chart div -->
660
+ <div id="p3-tabs-1">
661
+ <h2>Runtime by Plugin</h2>
662
+ <div class="p3-plugin-graph">
663
+ <table>
664
+ <tr>
665
+ <td rowspan="2">
666
+ <div style="width: 370px;" class="p3-line p3-graph-holder" id="p3-holder_<?php echo $pie_chart_id; ?>"></div>
667
+ </td>
668
+ <td>
669
+ <h3>Legend</h3>
670
+ </td>
671
+ </tr>
672
+ <tr>
673
+ <td>
674
+ <div style="width: 250px;" class="p3-custom-legend" id="p3-legend_<?php echo $pie_chart_id;?>"></div>
675
+ </td>
676
+ </tr>
677
+ </table>
678
+ </div>
679
+ </div>
680
+
681
+ <!-- Runtime line chart div -->
682
+ <div id="p3-tabs-2">
683
+ <h2>Summary Timeline</h2>
684
+ <div class="p3-plugin-graph">
685
+ <table>
686
+ <tr>
687
+ <td rowspan="2">
688
+ <div class="p3-y-axis-label">
689
+ <em class="p3-em">Seconds</em>
690
+ </div>
691
+ </td>
692
+ <td rowspan="2">
693
+ <div class="p3-line p3-graph-holder" id="p3-holder_<?php echo $runtime_chart_id; ?>"></div>
694
+ </td>
695
+ <td>
696
+ <h3>Legend</h3>
697
+ </td>
698
+ </tr>
699
+ <tr>
700
+ <td>
701
+ <div class="p3-custom-legend" id="p3-legend_<?php echo $runtime_chart_id; ?>"></div>
702
+ </td>
703
+ </tr>
704
+ <tr>
705
+ <td>&nbsp;</td>
706
+ <td colspan="2">
707
+ <div class="p3-x-axis-label">
708
+ <!-- <em class="p3-em">Visit</em> -->
709
+ </div>
710
+ </td>
711
+ </tr>
712
+ </table>
713
+ </div>
714
+ </div>
715
+
716
+ <!-- Query line chart div -->
717
+ <div id="p3-tabs-3">
718
+ <h2>Query Timeline</h2>
719
+ <div class="p3-plugin-graph">
720
+ <table>
721
+ <tr>
722
+ <td rowspan="2">
723
+ <div class="p3-y-axis-label">
724
+ <em class="p3-em">Queries</em>
725
+ </div>
726
+ </td>
727
+ <td rowspan="2">
728
+ <div class="p3-line p3-graph-holder" id="p3-holder_<?php echo $query_chart_id; ?>"></div>
729
+ </td>
730
+ <td>
731
+ <h3>Legend</h3>
732
+ </td>
733
+ </tr>
734
+ <tr>
735
+ <td>
736
+ <div class="p3-custom-legend" id="p3-legend_<?php echo $query_chart_id; ?>"></div>
737
+ </td>
738
+ </tr>
739
+ <tr>
740
+ <td>&nbsp;</td>
741
+ <td colspan="2">
742
+ <div class="p3-x-axis-label">
743
+ <!-- <em class="p3-em">Visit</em> -->
744
+ </div>
745
+ </td>
746
+ </tr>
747
+ </table>
748
+ </div>
749
+ </div>
750
+
751
+ <!-- Component runtime chart div -->
752
+ <div id="p3-tabs-6">
753
+ <h2>Detailed Timeline</h2>
754
+ <div class="p3-plugin-graph">
755
+ <table>
756
+ <tr>
757
+ <td rowspan="2">
758
+ <div class="p3-y-axis-label">
759
+ <em class="p3-em">Seconds</em>
760
+ </div>
761
+ </td>
762
+ <td rowspan="2">
763
+ <div class="p3-line p3-graph-holder" id="p3-holder_<?php echo $component_runtime_chart_id; ?>"></div>
764
+ </td>
765
+ <td>
766
+ <h3>Legend</h3>
767
+ </td>
768
+ </tr>
769
+ <tr>
770
+ <td>
771
+ <div class="p3-custom-legend" id="p3-legend_<?php echo $component_runtime_chart_id; ?>"></div>
772
+ </td>
773
+ </tr>
774
+ <tr>
775
+ <td>&nbsp;</td>
776
+ <td colspan="2">
777
+ <div class="p3-x-axis-label">
778
+ <!-- <em class="p3-em">Visit</em> -->
779
+ </div>
780
+ </td>
781
+ </tr>
782
+ </table>
783
+ </div>
784
+ </div>
785
+
786
+ <!-- Advanced data -->
787
+ <div id="p3-tabs-4">
788
+ <div id="p3-metrics-container">
789
+ <div class="ui-widget-header" id="p3-metrics-header" style="padding: 8px;">
790
+ <strong>Advanced Metrics</strong>
791
+ </div>
792
+ <div>
793
+ <table class="p3-results-table" id="p3-results-table" cellpadding="0" cellspacing="0" border="0">
794
+ <tbody>
795
+ <tr class="advanced">
796
+ <td class="qtip-tip" title="The time the site took to load. This is an observed measurement (start
797
+ timing when the page was requested, stop timing when the page was delivered to the browser,
798
+ calculate the difference). Lower is better.">
799
+ <strong>Total Load Time: </strong>
800
+ </td>
801
+ <td>
802
+ <?php printf( '%.4f', $profile->averages['total'] ); ?> seconds <em class="p3-em">avg.</em>
803
+ </td>
804
+ </tr>
805
+ <tr>
806
+ <td class="qtip-tip" title="The calculated total load time minus the profile overhead. This is closer to your
807
+ site's real-life load time. Lower is better.">
808
+ <strong>Site Load Time</small></em></strong>
809
+ </td>
810
+ <td>
811
+ <?php printf( '%.4f', $profile->averages['site'] ); ?> seconds <em class="p3-em">avg.</em>
812
+ </td>
813
+ </tr>
814
+ <tr class="advanced">
815
+ <td class="qtip-tip" title="The load time spent profiling code. Because the profiler slows down your load time,
816
+ it is important to know how much impact the profiler has. However, it doesn't impact your site's
817
+ real-life load time.">
818
+ <strong>Profile Overhead: </strong>
819
+ </td>
820
+ <td>
821
+ <?php printf( '%.4f', $profile->averages['profile'] ); ?> seconds <em class="p3-em">avg.</em>
822
+ </td>
823
+ </tr>
824
+ <tr>
825
+ <td class="qtip-tip" title="The load time caused by plugins. Because of WordPress' construction, we can trace a
826
+ function call from a plugin through a theme through the core. The profiler prioritizes plugin calls
827
+ first, theme calls second, and core calls last. Lower is better.">
828
+ <strong>Plugin Load Time: </strong>
829
+ </td>
830
+ <td>
831
+ <?php printf( '%.4f', $profile->averages['plugins'] ); ?> seconds <em class="p3-em">avg.</em>
832
+ </td>
833
+ </tr>
834
+ <tr>
835
+ <td class="qtip-tip" title="The load time spent applying the theme. Because of WordPress' construction, we can trace
836
+ a function call from a plugin through a theme through the core. The profiler prioritizes plugin calls
837
+ first, theme calls second, and core calls last. Lower is better.">
838
+ <strong>Theme Load Time: </strong>
839
+ </td>
840
+ <td>
841
+ <?php printf( '%.4f', $profile->averages['theme'] ); ?> seconds <em class="p3-em">avg.</em>
842
+ </td>
843
+ </tr>
844
+ <tr>
845
+ <td class="qtip-tip" title="The load time caused by the WordPress core. Because of WordPress' construction, we can
846
+ trace a function call from a plugin through a theme through the core. The profiler prioritizes plugin
847
+ calls first, theme calls second, and core calls last. This will probably be constant.">
848
+ <strong>Core Load Time: </strong>
849
+ </td>
850
+ <td>
851
+ <?php printf( '%.4f', $profile->averages['core'] ); ?> seconds <em class="p3-em">avg.</em>
852
+ </td>
853
+ </tr>
854
+ <tr class="advanced">
855
+ <td class="qtip-tip" title="This is the difference between the observed runtime (what actually happened) and expected
856
+ runtime (adding the plugin runtime, theme runtime, core runtime, and profiler overhead).
857
+ There are several reasons this margin of error can exist. Most likely, the profiler is
858
+ missing microseconds while adding the runtime it observed. Using a network clock to set the
859
+ time (NTP) can also cause minute timing changes.
860
+ Ideally, this number should be zero, but there's nothing you can do to change it. It
861
+ will give you an idea of how accurate the other results are.">
862
+ <strong>Margin of Error: </strong>
863
+ </td>
864
+ <td>
865
+ <?php printf( '%.4f', $profile->averages['drift'] ); ?> seconds <em class="p3-em">avg.</em>
866
+ <br />
867
+ <em class="p3-em">
868
+ (<span class="qtip-tip" title="How long the site took to load. This is an observed measurement (start timing
869
+ when the page was requested, stop timing when the page was delivered to the browser, calculate the
870
+ difference)."><?php printf( '%.4f', $profile->averages['observed'] ); ?> observed<span>,
871
+ <span class="qtip-tip" title="The expected site load time calculated by adding plugin load time, core
872
+ load time, theme load time, and profiler overhead.">
873
+ <?php printf( '%.4f', $profile->averages['expected'] ); ?> expected</span>)
874
+ </em>
875
+ </td>
876
+ </tr>
877
+ <tr class="advanced">
878
+ <td class="qtip-tip" title="The number of visits registered during a profiling session. More visits produce a more
879
+ accurate summary.">
880
+ <strong>Visits: </strong>
881
+ </td>
882
+ <td>
883
+ <?php echo number_format( $profile->visits ); ?>
884
+ </td>
885
+ </tr>
886
+ <tr class="advanced">
887
+ <td class="qtip-tip" title="The number of PHP function calls generated by a plugin. Fewer is better.">
888
+ <strong>Number of Plugin Function Calls: </strong>
889
+ </td>
890
+ <td>
891
+ <?php echo number_format( $profile->averages['plugin_calls'] ); ?> calls <em class="p3-em">avg.</em>
892
+ </td>
893
+ </tr>
894
+ <tr>
895
+ <td class="qtip-tip" title="The amount of RAM usage observed. This is reported by memory_get_peak_usage().
896
+ Lower is better.">
897
+ <strong>Memory Usage: </strong>
898
+ </td>
899
+ <td>
900
+ <?php echo number_format( $profile->averages['memory'] / 1024 / 1024, 2 ); ?> MB <em class="p3-em">avg.</em>
901
+ </td>
902
+ </tr>
903
+ <tr>
904
+ <td class="qtip-tip" title="The count of queries sent to the database. This reported by the WordPress function
905
+ get_num_queries(). Lower is better.">
906
+ <strong>MySQL Queries: </strong>
907
+ </td>
908
+ <td>
909
+ <?php echo round( $profile->averages['queries'] ); ?> queries <em class="p3-em">avg.</em>
910
+ </td>
911
+ </tr>
912
+ </tbody>
913
+ </table>
914
+ </div>
915
+ </div>
916
+ </div>
917
+
918
+ <!-- Email these results -->
919
+ <div class="button" id="p3-email-results" style="width: 155px; padding: 5px;">
920
+ <img src="<?php echo plugins_url(); ?>/p3-profiler/css/icon_mail.gif" height="22" width="22" align="center"
921
+ alt="Email these results" title="Email these results" />
922
+ <a href="javascript:;">Email these results</a>
923
+ </div>
924
+
925
+ <!-- Email results dialog -->
926
+ <div id="p3-email-results-dialog" class="p3-dialog">
927
+ <div>
928
+ From:<br />
929
+ <input type="text" id="p3-email-results-from" style="width:95%;" size="35"
930
+ value="<?php $user = wp_get_current_user(); echo $user->user_email; ?>" title="Enter the e-mail address to send from" />
931
+ </div>
932
+ <br />
933
+ <div>
934
+ Recipient:<br />
935
+ <input type="text" id="p3-email-results-to" style="width:95%;" size="35"
936
+ value="<?php $user = wp_get_current_user(); echo $user->user_email; ?>"
937
+ title="Enter the e-mail address where you would like to send these results" />
938
+ </div>
939
+ <br />
940
+ <div>
941
+ Subject:<br />
942
+ <input type="text" id="p3-email-results-subject" style="width:95%;" size="35"
943
+ value="Performance Profile Results - <?php bloginfo( 'name' ); ?>" title="Enter the e-mail subject" />
944
+ </div>
945
+ <br />
946
+ <div>
947
+ Message: <em class="p3-em">( optional )</em><br />
948
+ <textarea id="p3-email-results-message" style="width: 95%; height: 100px;">Hello,
949
+
950
+ I profiled my WordPress site's performance using the Profile Plugin and I wanted
951
+ to share the results with you. Please take a look at the information below:</textarea>
952
+ </div>
953
+ <br />
954
+ <div>
955
+ Results: <em class="p3-em">( system generated, do not edit )</em><br />
956
+ <textarea disabled="disabled" id="p3-email-results-results" style="width: 95%; height: 120px;"><?php
957
+ echo "WordPress Plugin Profile Report\n";
958
+ echo "===========================================\n";
959
+ echo 'Report date: ' . date( 'D M j, Y', $profile->report_date ) . "\n";
960
+ echo 'Pages browsed: ' . $profile->visits . "\n";
961
+ echo 'Avg. load time: ' . sprintf( '%.4f', $profile->averages['site'] ) . " sec\n";
962
+ echo 'Number of plugins: ' . count( $profile->detected_plugins ) . " \n";
963
+ echo 'Plugin impact: ' . sprintf( '%.2f%%', $profile->averages['plugin_impact'] ) . " % of load time\n";
964
+ echo 'Avg. plugin time: ' . sprintf( '%.4f', $profile->averages['plugins'] ) . " sec\n";
965
+ echo 'Avg. core time: ' . sprintf( '%.4f', $profile->averages['core'] ) . " sec\n";
966
+ echo 'Avg. theme time: ' . sprintf( '%.4f', $profile->averages['theme'] ) . " sec\n";
967
+ echo 'Avg. mem usage: ' . number_format( $profile->averages['memory'] / 1024 / 1024, 2 ) . " MB\n";
968
+ echo 'Avg. plugin calls: ' . number_format( $profile->averages['plugin_calls'] ) . "\n";
969
+ echo 'Avg. db queries : ' . sprintf( '%.2f', $profile->averages['queries'] ) . "\n";
970
+ echo 'Margin of error : ' . sprintf( '%.4f', $profile->averages['drift'] ) . " sec\n";
971
+ echo "\nPlugin list:\n";
972
+ echo "===========================================\n";
973
+ echo implode( "\n", $profile->detected_plugins ) . "\n";
974
+ ?></textarea>
975
+ </div>
976
+ <input type="hidden" id="p3-email-results-scan" value="<?php echo basename( $scan ); ?>" />
977
+ </div>
978
+
979
+ <!-- Email sending dialog -->
980
+ <div id="p3-email-sending-dialog" class="p3-dialog">
981
+ <div id="p3-email-sending-loading">
982
+ <img src="<?php echo get_site_url() . '/wp-admin/images/loading.gif' ?>" height="16" width="16" title="Loading" alt="Loading" />
983
+ </div>
984
+ <div id="p3-email-sending-error">
985
+ There was a problem sending the e-mail: <span id="p3-email-error"></span>
986
+ </div>
987
+ <div id="p3-email-sending-success">
988
+ Your report was sent successfully to <span id="p3-email-success-recipient"></span>
989
+ </div>
990
+ <div id="p3-email-sending-close">
991
+ <input type="checkbox" id="p3-email-sending-close-submit" checked="checked" /><label for="p3-email-sending-close-submit">Done</label>
992
+ </div>
993
+ </div>