Antispam Bee - Version 2.9.0

Version Description

Download this release

Release Info

Developer websupporter
Plugin Icon 128x128 Antispam Bee
Version 2.9.0
Comparing to
See all releases

Code changes from version 2.8.1 to 2.9.0

Files changed (5) hide show
  1. antispam_bee.php +1377 -1302
  2. inc/columns.class.php +64 -79
  3. inc/gui.class.php +215 -185
  4. js/raphael.min.js +3 -10
  5. readme.txt +16 -1
antispam_bee.php CHANGED
@@ -1,16 +1,18 @@
1
  <?php
2
- /*
3
- * Plugin Name: Antispam Bee
4
- * Description: Antispam plugin with a sophisticated toolset for effective day to day comment and trackback spam-fighting. Built with data protection and privacy in mind.
5
- * Author: pluginkollektiv
6
- * Author URI: https://pluginkollektiv.org
7
- * Plugin URI: https://wordpress.org/plugins/antispam-bee/
8
- * Text Domain: antispam-bee
9
- * Domain Path: /lang
10
- * License: GPLv2 or later
11
- * License URI: http://www.gnu.org/licenses/gpl-2.0.html
12
- * Version: 2.8.1
13
- */
 
 
14
 
15
  /*
16
  * Copyright (C) 2009-2015 Sergej Müller
@@ -34,229 +36,251 @@
34
  // Make sure this file is only run from within the WordPress context.
35
  defined( 'ABSPATH' ) || exit;
36
 
37
-
38
  /**
39
- * Antispam_Bee
40
- *
41
- * @since 0.1
42
- * @change 2.4
43
- */
44
-
45
  class Antispam_Bee {
46
 
47
-
48
- // Init
 
 
 
49
  public static $defaults;
 
 
 
 
 
 
50
  private static $_base;
 
 
 
 
 
 
51
  private static $_salt;
 
 
 
 
 
 
 
52
  private static $_reason;
53
- private static $_current_post_id;
54
 
 
 
 
 
 
 
55
 
56
  /**
57
- * "Constructor" of the class
58
- *
59
- * @since 0.1
60
- * @change 2.6.4
61
- */
 
 
 
 
 
 
 
 
62
 
63
- public static function init()
64
- {
65
- // Delete spam reason
66
- add_action(
67
- 'unspam_comment',
68
- array(
69
- __CLASS__,
70
- 'delete_spam_reason_by_comment'
71
- )
72
- );
73
-
74
- // AJAX & Co.
75
- if ( (defined('DOING_AJAX') && DOING_AJAX) or (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) ) {
76
  return;
77
  }
78
 
79
- // Initialization
80
  self::_init_internal_vars();
81
 
82
- // Cronjob
83
- if ( defined('DOING_CRON') ) {
84
  add_action(
85
  'antispam_bee_daily_cronjob',
86
  array(
87
  __CLASS__,
88
- 'start_daily_cronjob'
89
  )
90
  );
91
 
92
- // Admin
93
  } elseif ( is_admin() ) {
94
- // Menu
95
  add_action(
96
  'admin_menu',
97
  array(
98
  __CLASS__,
99
- 'add_sidebar_menu'
100
  )
101
  );
102
 
103
- // Dashboard
104
- if ( self::_current_page('dashboard') ) {
105
  add_action(
106
  'init',
107
  array(
108
  __CLASS__,
109
- 'load_plugin_lang'
110
  )
111
  );
112
  add_filter(
113
  'dashboard_glance_items',
114
  array(
115
  __CLASS__,
116
- 'add_dashboard_count'
117
  )
118
  );
119
  add_action(
120
  'wp_dashboard_setup',
121
  array(
122
  __CLASS__,
123
- 'add_dashboard_chart'
124
  )
125
  );
126
 
127
- // Plugins
128
- } else if ( self::_current_page('plugins') ) {
129
  add_action(
130
  'init',
131
  array(
132
  __CLASS__,
133
- 'load_plugin_lang'
134
  )
135
  );
136
  add_filter(
137
  'plugin_row_meta',
138
  array(
139
  __CLASS__,
140
- 'init_row_meta'
141
  ),
142
  10,
143
  2
144
  );
145
  add_filter(
146
- 'plugin_action_links_' .self::$_base,
147
  array(
148
  __CLASS__,
149
- 'init_action_links'
150
  )
151
  );
152
 
153
- // Options
154
- } else if ( self::_current_page('options') ) {
155
  add_action(
156
  'admin_init',
157
  array(
158
  __CLASS__,
159
- 'load_plugin_lang'
160
  )
161
  );
162
  add_action(
163
  'admin_init',
164
  array(
165
  __CLASS__,
166
- 'init_plugin_sources'
167
  )
168
  );
169
 
170
- } else if ( self::_current_page('admin-post') ) {
171
- require_once( dirname(__FILE__). '/inc/gui.class.php' );
172
 
173
  add_action(
174
  'admin_post_ab_save_changes',
175
  array(
176
  'Antispam_Bee_GUI',
177
- 'save_changes'
178
  )
179
  );
180
 
181
- } else if ( self::_current_page('edit-comments') ) {
182
- if ( ! empty($_GET['comment_status']) && $_GET['comment_status'] === 'spam' && ! self::get_option('no_notice') ) {
183
- // Include file
184
- require_once( dirname(__FILE__). '/inc/columns.class.php' );
 
185
 
186
- // Load textdomain
187
  self::load_plugin_lang();
188
 
189
- // Add plugin columns
190
  add_filter(
191
  'manage_edit-comments_columns',
192
  array(
193
  'Antispam_Bee_Columns',
194
- 'register_plugin_columns'
195
  )
196
  );
197
  add_filter(
198
  'manage_comments_custom_column',
199
  array(
200
  'Antispam_Bee_Columns',
201
- 'print_plugin_column'
202
  ),
203
  10,
204
  2
205
  );
206
  add_filter(
207
- 'admin_print_styles-edit-comments.php',
208
- array(
209
- 'Antispam_Bee_Columns',
210
- 'print_column_styles'
211
- )
212
  );
213
 
214
  add_filter(
215
  'manage_edit-comments_sortable_columns',
216
  array(
217
  'Antispam_Bee_Columns',
218
- 'register_sortable_columns'
219
  )
220
  );
221
  add_action(
222
  'pre_get_posts',
223
  array(
224
  'Antispam_Bee_Columns',
225
- 'set_orderby_query'
226
  )
227
  );
228
  }
229
  }
230
-
231
- // Frontend
232
  } else {
233
  add_action(
234
  'wp',
235
  array(
236
  __CLASS__,
237
- 'populate_post_id'
238
  )
239
  );
240
 
 
 
 
 
 
 
 
 
 
 
 
241
  add_action(
242
  'template_redirect',
243
  array(
244
  __CLASS__,
245
- 'prepare_comment_field'
246
  )
247
  );
248
  add_action(
249
  'init',
250
  array(
251
  __CLASS__,
252
- 'precheck_incoming_request'
253
  )
254
  );
255
  add_action(
256
  'preprocess_comment',
257
  array(
258
  __CLASS__,
259
- 'handle_incoming_request'
260
  ),
261
  1
262
  );
@@ -264,7 +288,7 @@ class Antispam_Bee {
264
  'antispam_bee_count',
265
  array(
266
  __CLASS__,
267
- 'the_spam_count'
268
  )
269
  );
270
  }
@@ -272,22 +296,19 @@ class Antispam_Bee {
272
 
273
 
274
 
275
- /*
276
- * ############################
277
- * ######## INSTALL #########
278
- * ############################
279
- */
280
-
281
- /**
282
- * Action during the activation of the Plugins
283
- *
284
- * @since 0.1
285
- * @change 2.4
286
  */
287
 
288
- public static function activate()
289
- {
290
- // Apply Option
 
 
 
 
291
  add_option(
292
  'antispam_bee',
293
  array(),
@@ -295,184 +316,176 @@ class Antispam_Bee {
295
  'no'
296
  );
297
 
298
- // Activate Cron
299
- if ( self::get_option('cronjob_enable') ) {
300
  self::init_scheduled_hook();
301
  }
302
  }
303
 
304
 
305
  /**
306
- * Action to deactivate the plugin
307
- *
308
- * @since 0.1
309
- * @change 2.4
310
- */
311
-
312
- public static function deactivate()
313
- {
314
  self::clear_scheduled_hook();
315
  }
316
 
317
 
318
  /**
319
- * Action deleting the plugin
320
- *
321
- * @since 2.4
322
- * @change 2.4
323
- */
324
-
325
- public static function uninstall()
326
- {
327
- // Global
328
  global $wpdb;
329
 
330
- // Remove settings
331
- delete_option('antispam_bee');
332
 
333
- // Clean DB
334
- $wpdb->query("OPTIMIZE TABLE `" .$wpdb->options. "`");
 
 
335
  }
336
 
337
 
338
 
339
- /*
340
- * ############################
341
- * ######## INTERNAL ########
342
- * ############################
343
- */
344
-
345
- /**
346
- * Initialization of the internal variables
347
- *
348
- * @since 2.4
349
- * @change 2.7.0
350
  */
351
 
352
- private static function _init_internal_vars()
353
- {
354
- self::$_base = plugin_basename(__FILE__);
 
 
 
 
 
355
 
356
- $salt = defined( 'NONCE_SALT' ) ? NONCE_SALT : ABSPATH;
357
  self::$_salt = substr( sha1( $salt ), 0, 10 );
358
 
359
  self::$defaults = array(
360
  'options' => array(
361
- // General
362
- 'advanced_check' => 1,
363
- 'regexp_check' => 1,
364
- 'spam_ip' => 1,
365
- 'already_commented' => 1,
366
- 'gravatar_check' => 0,
367
- 'time_check' => 0,
368
- 'ignore_pings' => 0,
369
- 'always_allowed' => 0,
370
-
371
- 'dashboard_chart' => 0,
372
- 'dashboard_count' => 0,
373
-
374
- // Filter
375
- 'country_code' => 0,
376
- 'country_black' => '',
377
- 'country_white' => '',
378
-
379
- 'translate_api' => 0,
380
- 'translate_lang' => array(),
381
-
382
- 'bbcode_check' => 1,
383
-
384
- // Advanced
385
- 'flag_spam' => 1,
386
- 'email_notify' => 0,
387
- 'no_notice' => 0,
388
- 'cronjob_enable' => 0,
389
- 'cronjob_interval' => 0,
390
-
391
- 'ignore_filter' => 0,
392
- 'ignore_type' => 0,
393
-
394
- 'reasons_enable' => 0,
395
- 'ignore_reasons' => array(),
396
  ),
397
  'reasons' => array(
398
- 'css' => esc_attr__( 'Honeypot', 'antispam-bee' ),
399
- 'time' => esc_attr__( 'Comment time', 'antispam-bee' ),
400
- 'empty' => esc_attr__( 'Empty Data', 'antispam-bee' ),
401
- 'server' => esc_attr__( 'Fake IP', 'antispam-bee' ),
402
- 'localdb' => esc_attr__( 'Local DB Spam', 'antispam-bee' ),
403
- 'country' => esc_attr__( 'Country Check', 'antispam-bee' ),
404
- 'bbcode' => esc_attr__( 'BBCode', 'antispam-bee' ),
405
- 'lang' => esc_attr__( 'Comment Language', 'antispam-bee' ),
406
- 'regexp' => esc_attr__( 'Regular Expression', 'antispam-bee' ),
407
- )
 
408
  );
409
  }
410
 
411
-
412
  /**
413
- * Check and return an array key
414
- *
415
- * @since 2.4.2
416
- * @change 2.4.2
417
- *
418
- * @param array $array Array with values
419
- * @param string $key Name of the key
420
- * @return mixed Value of the requested key
421
- */
422
-
423
- public static function get_key($array, $key)
424
- {
425
- if ( empty($array) or empty($key) or empty($array[$key]) ) {
426
  return null;
427
  }
428
 
429
- return $array[$key];
430
  }
431
 
432
 
433
  /**
434
- * Localization of the admin pages
435
- *
436
- * @since 0.1
437
- * @change 2.4
438
- *
439
- * @param string $page Mark the page
440
- * @return boolean TRUE on success
441
- */
442
-
443
- private static function _current_page($page)
444
- {
445
- switch ($page) {
446
  case 'dashboard':
447
- return ( empty($GLOBALS['pagenow']) or ( !empty($GLOBALS['pagenow']) && $GLOBALS['pagenow'] == 'index.php' ) );
448
 
449
  case 'options':
450
- return ( !empty($_GET['page']) && $_GET['page'] == 'antispam_bee' );
451
 
452
  case 'plugins':
453
- return ( !empty($GLOBALS['pagenow']) && $GLOBALS['pagenow'] == 'plugins.php' );
454
 
455
  case 'admin-post':
456
- return ( !empty($GLOBALS['pagenow']) && $GLOBALS['pagenow'] == 'admin-post.php' );
457
 
458
  case 'edit-comments':
459
- return ( !empty($GLOBALS['pagenow']) && $GLOBALS['pagenow'] == 'edit-comments.php' );
460
 
461
  default:
462
  return false;
463
  }
 
464
  }
465
 
466
 
467
  /**
468
- * Integration of the localization file
469
- *
470
- * @since 0.1
471
- * @change 2.4
472
- */
473
-
474
- public static function load_plugin_lang()
475
- {
476
  load_plugin_textdomain(
477
  'antispam-bee'
478
  );
@@ -480,16 +493,16 @@ class Antispam_Bee {
480
 
481
 
482
  /**
483
- * Add the link to the settings
484
- *
485
- * @since 1.1
486
- * @change 1.1
487
- */
488
-
489
- public static function init_action_links($data)
490
- {
491
- // Rights?
492
- if ( ! current_user_can('manage_options') ) {
493
  return $data;
494
  }
495
 
@@ -500,32 +513,28 @@ class Antispam_Bee {
500
  '<a href="%s">%s</a>',
501
  add_query_arg(
502
  array(
503
- 'page' => 'antispam_bee'
504
  ),
505
- admin_url('options-general.php')
506
  ),
507
- esc_attr__('Settings', 'antispam-bee')
508
- )
509
  )
510
  );
511
  }
512
 
513
-
514
  /**
515
- * Meta links of the plugin
516
- *
517
- * @since 0.1
518
- * @change 2.6.2
519
- *
520
- * @param array $input Existing links
521
- * @param string $file Current page
522
- * @return array $data Modified links
523
- */
524
-
525
- public static function init_row_meta($input, $file)
526
- {
527
- // Rights
528
- if ( $file != self::$_base ) {
529
  return $input;
530
  }
531
 
@@ -538,38 +547,31 @@ class Antispam_Bee {
538
  );
539
  }
540
 
541
-
542
-
543
- /*
544
- * ############################
545
- * ####### RESOURCES ########
546
- * ############################
547
- */
548
-
549
- /**
550
- * Registration of resources (CSS & JS)
551
- *
552
- * @since 1.6
553
- * @change 2.4.5
554
  */
555
 
556
- public static function init_plugin_sources()
557
- {
558
- // Read information
559
- $plugin = get_plugin_data(__FILE__);
 
 
 
 
560
 
561
- // Integrate JS
562
  wp_register_script(
563
  'ab_script',
564
- plugins_url('js/scripts.min.js', __FILE__),
565
- array('jquery'),
566
  $plugin['Version']
567
  );
568
 
569
- // Integrate CSS
570
  wp_register_style(
571
  'ab_style',
572
- plugins_url('css/styles.min.css', __FILE__),
573
  array( 'dashicons' ),
574
  $plugin['Version']
575
  );
@@ -577,15 +579,12 @@ class Antispam_Bee {
577
 
578
 
579
  /**
580
- * Initialization of the option page
581
- *
582
- * @since 0.1
583
- * @change 2.4.3
584
- */
585
-
586
- public static function add_sidebar_menu()
587
- {
588
- // Create menu
589
  $page = add_options_page(
590
  'Antispam Bee',
591
  'Antispam Bee',
@@ -593,191 +592,165 @@ class Antispam_Bee {
593
  'antispam_bee',
594
  array(
595
  'Antispam_Bee_GUI',
596
- 'options_page'
597
  )
598
  );
599
 
600
- // Integrate JS
601
  add_action(
602
  'admin_print_scripts-' . $page,
603
  array(
604
  __CLASS__,
605
- 'add_options_script'
606
  )
607
  );
608
 
609
- // Integrate CSS
610
  add_action(
611
  'admin_print_styles-' . $page,
612
  array(
613
  __CLASS__,
614
- 'add_options_style'
615
  )
616
  );
617
 
618
- // Load PHP
619
  add_action(
620
- 'load-' .$page,
621
  array(
622
  __CLASS__,
623
- 'init_options_page'
624
  )
625
  );
626
  }
627
 
628
 
629
  /**
630
- * Initialization of JavaScript
631
- *
632
- * @since 1.6
633
- * @change 2.4
634
- */
635
-
636
- public static function add_options_script()
637
- {
638
- wp_enqueue_script('ab_script');
639
  }
640
 
641
 
642
  /**
643
- * Initialization of Stylesheets
644
- *
645
- * @since 1.6
646
- * @change 2.4
647
- */
648
-
649
- public static function add_options_style()
650
- {
651
- wp_enqueue_style('ab_style');
652
  }
653
 
654
 
655
  /**
656
- * Integration of the GUI
657
- *
658
- * @since 2.4
659
- * @change 2.4
660
- */
661
-
662
- public static function init_options_page()
663
- {
664
- require_once( dirname(__FILE__). '/inc/gui.class.php' );
665
  }
666
 
667
 
668
 
669
- /*
670
- * ############################
671
- * ####### DASHBOARD ########
672
- * ############################
673
- */
674
-
675
- /**
676
- * Display the spam counter on the dashboard
677
- *
678
- * @since 0.1
679
- * @change 2.6.5
680
- *
681
- * @param array $items Initial array with dashboard items
682
- * @return array $items Merged array with dashboard items
683
  */
684
 
685
- public static function add_dashboard_count( $items = array() )
686
- {
687
- // Skip
688
- if ( ! current_user_can('manage_options') OR ! self::get_option('dashboard_count') ) {
689
- return $items;
 
 
 
 
 
 
 
690
  }
691
 
692
- // Icon styling
693
- echo '<style>#dashboard_right_now .ab-count:before {content: "\f117"}</style>';
694
 
695
- // Right now item
696
- $items[] = '<span class="ab-count">' . esc_html( sprintf(
697
- __( '%d Blocked', 'antispam-bee' ),
698
- self::_get_spam_count()
699
- )
 
700
  ) . '</span>';
701
 
702
- return $items;
703
  }
704
 
705
-
706
  /**
707
- * Initialize the dashboard chart
708
- *
709
- * @since 1.9
710
- * @change 2.5.6
711
- */
712
- public static function add_dashboard_chart()
713
- {
714
- // Filter
715
  if ( ! current_user_can( 'publish_posts' ) || ! self::get_option( 'dashboard_chart' ) ) {
716
  return;
717
  }
718
 
719
- // Add Widget
720
  wp_add_dashboard_widget(
721
  'ab_widget',
722
  'Antispam Bee',
723
  array(
724
  __CLASS__,
725
- 'show_spam_chart'
726
  )
727
  );
728
 
729
- // Load CSS
730
  add_action(
731
  'admin_head',
732
  array(
733
  __CLASS__,
734
- 'add_dashboard_style'
735
  )
736
  );
737
  }
738
 
739
-
740
  /**
741
- * Print dashboard styles
742
- *
743
- * @since 1.9.0
744
- * @change 2.5.8
745
- */
746
-
747
- public static function add_dashboard_style()
748
- {
749
- // Get plugin data
750
- $plugin = get_plugin_data(__FILE__);
751
 
752
- // Register styles
753
  wp_register_style(
754
  'ab_chart',
755
- plugins_url('css/dashboard.min.css', __FILE__),
756
  array(),
757
  $plugin['Version']
758
  );
759
 
760
- // Embed styles
761
- wp_print_styles('ab_chart');
762
  }
763
 
764
 
765
  /**
766
- * Print dashboard scripts
767
- *
768
- * @since 1.9.0
769
- * @change 2.5.8
770
- */
771
  public static function add_dashboard_script() {
772
- // Get stats
773
- if ( ! self::get_option('daily_stats') ) {
774
  return;
775
  }
776
 
777
- // Get plugin data
778
- $plugin = get_plugin_data(__FILE__);
779
 
780
- // Embed scripts
781
  wp_enqueue_script(
782
  'raphael',
783
  plugins_url( 'js/raphael.min.js', __FILE__ ),
@@ -803,84 +776,67 @@ class Antispam_Bee {
803
  );
804
  }
805
 
806
-
807
  /**
808
- * Print dashboard html
809
- *
810
- * @since 1.9.0
811
- * @change 2.5.8
812
- */
813
-
814
- public static function show_spam_chart()
815
- {
816
- // Get stats
817
- $items = (array)self::get_option('daily_stats');
818
 
819
- // Emty array?
820
- if ( empty($items) ) {
821
  echo sprintf(
822
  '<div id="ab_chart"><p>%s</p></div>',
823
- esc_html__('No data available.', 'antispam-bee')
824
  );
825
 
826
  return;
827
  }
828
 
829
- // Enqueue scripts.
830
  self::add_dashboard_script();
831
 
832
- // Sort stats
833
- ksort($items, SORT_NUMERIC);
834
 
835
- // Start HTML
836
  $html = "<table id=ab_chart_data>\n";
837
 
838
-
839
- // Timestamp table
840
  $html .= "<tfoot><tr>\n";
841
- foreach($items as $date => $count) {
842
- $html .= "<th>" .$date. "</th>\n";
843
  }
844
  $html .= "</tr></tfoot>\n";
845
 
846
- // Counter table
847
  $html .= "<tbody><tr>\n";
848
- foreach($items as $date => $count) {
849
- $html .= "<td>" .(int) $count. "</td>\n";
850
  }
851
  $html .= "</tr></tbody>\n";
852
 
853
-
854
- // HTML end
855
  $html .= "</table>\n";
856
 
857
- // Print html
858
- echo '<div id="ab_chart">' .$html. '</div>';
859
  }
860
 
861
-
862
-
863
- /*
864
- * ############################
865
- * ######## OPTIONS #########
866
- * ############################
867
- */
868
-
869
- /**
870
- * Get all plugin options
871
- *
872
- * @since 2.4
873
- * @change 2.6.1
874
- *
875
- * @return array $options Array with option fields
876
  */
877
 
878
- public static function get_options()
879
- {
880
- if ( ! $options = wp_cache_get('antispam_bee') ) {
 
 
 
 
 
 
 
 
881
  wp_cache_set(
882
  'antispam_bee',
883
- $options = get_option('antispam_bee')
884
  );
885
  }
886
 
@@ -890,78 +846,65 @@ class Antispam_Bee {
890
  );
891
  }
892
 
893
-
894
  /**
895
- * Get single option field
896
- *
897
- * @since 0.1
898
- * @change 2.4.2
899
- *
900
- * @param string $field Field name
901
- * @return mixed Field value
902
- */
903
-
904
- public static function get_option($field)
905
- {
906
- // Get all options
907
  $options = self::get_options();
908
 
909
- return self::get_key($options, $field);
910
  }
911
 
912
 
913
  /**
914
- * Update single option field
915
- *
916
- * @since 0.1
917
- * @change 2.4
918
- *
919
- * @param string $field Field name
920
- * @param mixed Field value
921
- */
922
-
923
- private static function _update_option($field, $value)
924
- {
925
  self::update_options(
926
  array(
927
- $field => $value
928
  )
929
  );
930
  }
931
 
932
 
933
  /**
934
- * Update multiple option fields
935
- *
936
- * @since 0.1
937
- * @change 2.6.1
938
- *
939
- * @param array $data Array with plugin option fields
940
- */
941
-
942
- public static function update_options($data)
943
- {
944
- // Get options
945
- $options = get_option('antispam_bee');
946
 
947
- // Merge new data
948
- if ( is_array($options) ) {
949
  $options = array_merge(
950
  $options,
951
  $data
952
  );
953
- }
954
- else {
955
  $options = $data;
956
  }
957
 
958
- // Update options
959
  update_option(
960
  'antispam_bee',
961
  $options
962
  );
963
 
964
- // Refresh cache
965
  wp_cache_set(
966
  'antispam_bee',
967
  $options
@@ -970,58 +913,47 @@ class Antispam_Bee {
970
 
971
 
972
 
973
- /*
974
- * ############################
975
- * ######## CRONJOBS ########
976
- * ############################
977
- */
978
-
979
- /**
980
- * Execution of the daily cronjobs
981
- *
982
- * @since 0.1
983
- * @change 2.4
984
  */
985
 
986
- public static function start_daily_cronjob()
987
- {
988
- // No Cronjob?
989
- if ( !self::get_option('cronjob_enable') ) {
 
 
 
 
990
  return;
991
  }
992
 
993
- // Update timestamp
994
  self::_update_option(
995
  'cronjob_timestamp',
996
  time()
997
  );
998
 
999
- // Delete spam
1000
  self::_delete_old_spam();
1001
  }
1002
 
1003
 
1004
  /**
1005
- * Delete old spam comments
1006
- *
1007
- * @since 0.1
1008
- * @change 2.4
1009
- */
1010
-
1011
- private static function _delete_old_spam()
1012
- {
1013
- // Number of days
1014
- $days = (int)self::get_option('cronjob_interval');
1015
 
1016
- // No value?
1017
- if ( empty($days) ) {
1018
  return false;
1019
  }
1020
 
1021
- // Global
1022
  global $wpdb;
1023
 
1024
- // Delete comments
1025
  $wpdb->query(
1026
  $wpdb->prepare(
1027
  "DELETE FROM `$wpdb->comments` WHERE `comment_approved` = 'spam' AND SUBDATE(NOW(), %d) > comment_date_gmt",
@@ -1029,21 +961,18 @@ class Antispam_Bee {
1029
  )
1030
  );
1031
 
1032
- // DB optimization
1033
- $wpdb->query("OPTIMIZE TABLE `$wpdb->comments`");
1034
  }
1035
 
1036
 
1037
  /**
1038
- * Initialization of the cronjobs
1039
- *
1040
- * @since 0.1
1041
- * @change 2.4
1042
- */
1043
-
1044
- public static function init_scheduled_hook()
1045
- {
1046
- if ( ! wp_next_scheduled('antispam_bee_daily_cronjob') ) {
1047
  wp_schedule_event(
1048
  time(),
1049
  'daily',
@@ -1054,128 +983,101 @@ class Antispam_Bee {
1054
 
1055
 
1056
  /**
1057
- * Deletion of the cronjobs
1058
- *
1059
- * @since 0.1
1060
- * @change 2.4
1061
- */
1062
-
1063
- public static function clear_scheduled_hook()
1064
- {
1065
- if ( wp_next_scheduled('antispam_bee_daily_cronjob') ) {
1066
- wp_clear_scheduled_hook('antispam_bee_daily_cronjob');
1067
  }
1068
  }
1069
 
1070
 
1071
 
1072
- /*
1073
- * ############################
1074
- * ###### SPAM CHECK ########
1075
- * ############################
1076
- */
1077
-
1078
- /**
1079
- * Check POST values
1080
- *
1081
- * @since 0.1
1082
- * @change 2.6.3
1083
  */
1084
 
1085
- public static function precheck_incoming_request()
1086
- {
1087
- // Skip if not a comment request
1088
- if ( is_feed() OR is_trackback() OR empty($_POST) OR self::_is_mobile() ) {
 
 
 
 
 
1089
  return;
1090
  }
1091
 
1092
- // Request params
1093
- $request_uri = self::get_key($_SERVER, 'REQUEST_URI');
1094
- $request_path = parse_url($request_uri, PHP_URL_PATH);
1095
 
1096
- // Request check
1097
- if ( strpos($request_path, 'wp-comments-post.php') === false ) {
1098
  return;
1099
  }
1100
 
1101
- $post_id = (int) self::get_key( $_POST, 'comment_post_ID' );
1102
- // Form fields
1103
  $hidden_field = self::get_key( $_POST, 'comment' );
1104
  $plugin_field = self::get_key( $_POST, self::get_secret_name_for_post( $post_id ) );
1105
 
1106
- // Hidden field check
1107
- if ( empty($hidden_field) && ! empty($plugin_field) ) {
1108
  $_POST['comment'] = $plugin_field;
1109
  unset( $_POST[ self::get_secret_name_for_post( $post_id ) ] );
1110
  } else {
1111
  $_POST['ab_spam__hidden_field'] = 1;
1112
  }
 
1113
  }
1114
 
1115
 
1116
  /**
1117
- * Check incoming requests for spam
1118
- *
1119
- * @since 0.1
1120
- * @change 2.6.3
1121
- *
1122
- * @param array $comment Untreated comment
1123
- * @return array $comment Treated comment
1124
- */
1125
-
1126
- public static function handle_incoming_request($comment)
1127
- {
1128
- // Add client IP
1129
  $comment['comment_author_IP'] = self::get_client_ip();
1130
 
1131
- // Hook client IP
1132
- add_filter(
1133
- 'pre_comment_user_ip',
1134
- array(
1135
- __CLASS__,
1136
- 'get_client_ip'
1137
- ),
1138
- 1
1139
- );
1140
-
1141
- // Request params
1142
- $request_uri = self::get_key($_SERVER, 'REQUEST_URI');
1143
- $request_path = parse_url($request_uri, PHP_URL_PATH);
1144
 
1145
- // Empty path?
1146
- if ( empty($request_path) ) {
1147
  return self::_handle_spam_request(
1148
  $comment,
1149
  'empty'
1150
  );
1151
  }
1152
 
1153
- // Defaults
1154
  $ping = array(
1155
- 'types' => array('pingback', 'trackback', 'pings'),
1156
- 'allowed' => !self::get_option('ignore_pings')
1157
  );
1158
 
1159
- // Is a comment
1160
- if ( strpos($request_path, 'wp-comments-post.php') !== false && ! empty($_POST) ) {
1161
- // Verify request
1162
- $status = self::_verify_comment_request($comment);
 
1163
 
1164
- // Treat the request as spam
1165
- if ( ! empty($status['reason']) ) {
1166
  return self::_handle_spam_request(
1167
  $comment,
1168
  $status['reason']
1169
  );
1170
  }
 
 
1171
 
1172
- // Is a trackback
1173
- } else if ( in_array(self::get_key($comment, 'comment_type'), $ping['types']) && $ping['allowed'] ) {
1174
- // Verify request
1175
- $status = self::_verify_trackback_request($comment);
1176
-
1177
- // Treat the request as spam
1178
- if ( ! empty($status['reason']) ) {
1179
  return self::_handle_spam_request(
1180
  $comment,
1181
  $status['reason'],
@@ -1189,57 +1091,47 @@ class Antispam_Bee {
1189
 
1190
 
1191
  /**
1192
- * Prepares the replacement of the comment field
1193
- *
1194
- * @since 0.1
1195
- * @change 2.4
1196
- */
1197
-
1198
- public static function prepare_comment_field()
1199
- {
1200
- // Frontend only
1201
- if ( is_feed() or is_trackback() or is_robots() or self::_is_mobile() ) {
1202
  return;
1203
  }
1204
 
1205
- // Only Posts
1206
- if ( !is_singular() && !self::get_option('always_allowed') ) {
1207
  return;
1208
  }
1209
 
1210
- // Fire!
1211
  ob_start(
1212
  array(
1213
  'Antispam_Bee',
1214
- 'replace_comment_field'
1215
  )
1216
  );
1217
  }
1218
 
1219
 
1220
  /**
1221
- * Replaces the comment field
1222
- *
1223
- * @since 2.4
1224
- * @change 2.6.4
1225
- *
1226
- * @param string $data HTML code of the website
1227
- * @return string Treated HTML code
1228
- */
1229
-
1230
- public static function replace_comment_field($data)
1231
- {
1232
- // Empty?
1233
- if ( empty($data) ) {
1234
  return;
1235
  }
1236
 
1237
- // Find the comment textarea
1238
- if ( ! preg_match('#<textarea.+?name=["\']comment["\']#s', $data) ) {
1239
  return $data;
1240
  }
1241
 
1242
- /* Inject HTML */
1243
  return preg_replace_callback(
1244
  '/(?P<all> (?# match the whole textarea tag )
1245
  <textarea (?# the opening of the textarea and some optional attributes )
@@ -1263,7 +1155,7 @@ class Antispam_Bee {
1263
  )/x',
1264
  array( 'Antispam_Bee', 'replace_comment_field_callback' ),
1265
  $data,
1266
- 1
1267
  );
1268
  }
1269
 
@@ -1277,8 +1169,7 @@ class Antispam_Bee {
1277
  * @return string The modified content string.
1278
  */
1279
  public static function replace_comment_field_callback( $matches ) {
1280
- // Build init time field
1281
- if ( self::get_option('time_check') ) {
1282
  $init_time_field = sprintf(
1283
  '<input type="hidden" name="ab_init_time" value="%d" />',
1284
  time()
@@ -1291,7 +1182,7 @@ class Antispam_Bee {
1291
 
1292
  $id_script = '';
1293
  if ( ! empty( $matches['id1'] ) || ! empty( $matches['id2'] ) ) {
1294
- $output .= 'id="' . self::get_secret_id_for_post( self::$_current_post_id ) . '" ';
1295
  $id_script = '<script type="text/javascript">document.getElementById("comment").setAttribute( "id", "' . esc_js( md5( time() ) ) . '" );document.getElementById("' . esc_js( self::get_secret_id_for_post( self::$_current_post_id ) ) . '").setAttribute( "id", "comment" );</script>';
1296
  }
1297
 
@@ -1308,264 +1199,318 @@ class Antispam_Bee {
1308
 
1309
 
1310
  /**
1311
- * Check the trackbacks
1312
- *
1313
- * @since 2.4
1314
- * @change 2.7.0
1315
- *
1316
- * @param array $comment Trackback data
1317
- * @return array Array with suspected reason [optional]
1318
- */
1319
-
1320
- private static function _verify_trackback_request($comment)
1321
- {
1322
- // Comment values
1323
- $ip = self::get_key($comment, 'comment_author_IP');
1324
- $url = self::get_key($comment, 'comment_author_url');
1325
- $body = self::get_key($comment, 'comment_content');
 
 
 
 
 
 
1326
 
1327
- // Empty values?
1328
- if ( empty($url) OR empty($body) ) {
1329
  return array(
1330
- 'reason' => 'empty'
1331
  );
1332
  }
1333
 
1334
- // IP?
1335
- if ( empty($ip) ) {
 
 
 
1336
  return array(
1337
- 'reason' => 'empty'
1338
  );
1339
  }
1340
 
1341
- // Options
1342
  $options = self::get_options();
1343
 
1344
- // BBCode spam
1345
- if ( $options['bbcode_check'] && self::_is_bbcode_spam($body) ) {
1346
  return array(
1347
- 'reason' => 'bbcode'
1348
  );
1349
  }
1350
 
1351
- // IP != Server
1352
- if ( $options['advanced_check'] && self::_is_fake_ip($ip, parse_url($url, PHP_URL_HOST)) ) {
1353
  return array(
1354
- 'reason' => 'server'
1355
  );
1356
  }
1357
 
1358
- // IP in local spam
1359
- if ( $options['spam_ip'] && self::_is_db_spam($ip, $url) ) {
1360
  return array(
1361
- 'reason' => 'localdb'
1362
  );
1363
  }
1364
 
1365
- // Check Country Code
1366
- if ( $options['country_code'] && self::_is_country_spam($ip) ) {
1367
  return array(
1368
- 'reason' => 'country'
1369
  );
1370
  }
1371
 
1372
- // Translate API
1373
- if ( $options['translate_api'] && self::_is_lang_spam($body) ) {
1374
  return array(
1375
- 'reason' => 'lang'
1376
  );
1377
  }
1378
- }
1379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1380
 
1381
  /**
1382
- * Check the comment
1383
- *
1384
- * @since 2.4
1385
- * @change 2.7.0
1386
- *
1387
- * @param array $comment Data of the comment
1388
- * @return array Array with suspected reason [optional]
1389
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1390
 
1391
- private static function _verify_comment_request($comment)
1392
- {
1393
- // Comment values
1394
- $ip = self::get_key($comment, 'comment_author_IP');
1395
- $url = self::get_key($comment, 'comment_author_url');
1396
- $body = self::get_key($comment, 'comment_content');
1397
- $email = self::get_key($comment, 'comment_author_email');
1398
- $author = self::get_key($comment, 'comment_author');
1399
-
1400
- // Empty values?
1401
- if ( empty($body) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1402
  return array(
1403
- 'reason' => 'empty'
1404
  );
1405
  }
1406
 
1407
- // IP?
1408
- if ( empty($ip) ) {
1409
  return array(
1410
- 'reason' => 'empty'
1411
  );
1412
  }
1413
 
1414
- // Empty values?
1415
- if ( get_option('require_name_email') && ( empty($email) OR empty($author) ) ) {
1416
  return array(
1417
- 'reason' => 'empty'
1418
  );
1419
  }
1420
 
1421
- // Options
1422
  $options = self::get_options();
1423
 
1424
- // Already commented?
1425
- if ( $options['already_commented'] && ! empty($email) && self::_is_approved_email($email) ) {
1426
  return;
1427
  }
1428
 
1429
- // Check for a Gravatar
1430
- if ( $options['gravatar_check'] && ! empty($email) && self::_has_valid_gravatar($email) ) {
1431
- return;
1432
  }
1433
 
1434
- // Bot detected
1435
- if ( ! empty($_POST['ab_spam__hidden_field']) ) {
1436
  return array(
1437
- 'reason' => 'css'
1438
  );
1439
  }
 
1440
 
1441
- // Action time
1442
  if ( $options['time_check'] && self::_is_shortest_time() ) {
1443
  return array(
1444
- 'reason' => 'time'
1445
  );
1446
  }
1447
 
1448
- // BBCode spam
1449
- if ( $options['bbcode_check'] && self::_is_bbcode_spam($body) ) {
1450
  return array(
1451
- 'reason' => 'bbcode'
1452
  );
1453
  }
1454
 
1455
- // Extended protection
1456
- if ( $options['advanced_check'] && self::_is_fake_ip($ip) ) {
1457
  return array(
1458
- 'reason' => 'server'
1459
  );
1460
  }
1461
 
1462
- // Regex for spam
1463
  if ( $options['regexp_check'] && self::_is_regexp_spam(
1464
  array(
1465
- 'ip' => $ip,
1466
  'rawurl' => $url,
1467
- 'host' => parse_url($url, PHP_URL_HOST),
1468
- 'body' => $body,
1469
- 'email' => $email,
1470
- 'author' => $author
1471
  )
1472
  ) ) {
1473
  return array(
1474
- 'reason' => 'regexp'
1475
  );
1476
  }
1477
 
1478
- // IP in local spam
1479
- if ( $options['spam_ip'] && self::_is_db_spam($ip, $url, $email) ) {
1480
  return array(
1481
- 'reason' => 'localdb'
1482
  );
1483
  }
1484
 
1485
- // Check Country Code
1486
- if ( $options['country_code'] && self::_is_country_spam($ip) ) {
1487
  return array(
1488
- 'reason' => 'country'
1489
  );
1490
  }
1491
 
1492
- // Translate API
1493
- if ( $options['translate_api'] && self::_is_lang_spam($body) ) {
1494
  return array(
1495
- 'reason' => 'lang'
1496
  );
1497
  }
1498
  }
1499
 
1500
 
1501
  /**
1502
- * Check for a Gravatar image
1503
- *
1504
- * @since 2.6.5
1505
- * @change 2.6.5
1506
- *
1507
- * @param string $email Input email
1508
- * @return boolean Check status (true = Gravatar available)
1509
- */
1510
-
1511
- private static function _has_valid_gravatar($email) {
1512
- $response = wp_safe_remote_get(
1513
- sprintf(
1514
- 'https://www.gravatar.com/avatar/%s?d=404',
1515
- md5( strtolower( trim($email) ) )
1516
- )
1517
- );
1518
 
1519
- if ( is_wp_error($response) ) {
1520
- return null;
1521
- }
1522
 
1523
- if ( wp_remote_retrieve_response_code($response) === 200 ) {
1524
- return true;
1525
- }
1526
 
1527
- return false;
1528
- }
1529
 
1530
 
1531
  /**
1532
- * Check for comment action time
1533
- *
1534
- * @since 2.6.4
1535
- * @change 2.6.4
1536
- *
1537
- * @return boolean TRUE if the action time is less than 5 seconds
1538
- */
1539
-
1540
- private static function _is_shortest_time()
1541
- {
1542
- // Comment init time
1543
- if ( ! $init_time = (int)self::get_key($_POST, 'ab_init_time') ) {
 
1544
  return false;
1545
  }
1546
 
1547
- // Compare time values
1548
- if ( time() - $init_time < apply_filters('ab_action_time_limit', 5) ) {
1549
  return true;
1550
  }
1551
 
1552
  return false;
1553
  }
1554
 
1555
-
1556
  /**
1557
- * Usage of regexp, also custom
1558
- *
1559
- * @since 2.5.2
1560
- * @change 2.5.6
1561
- *
1562
- * @param array $comment Array with commentary data
1563
- * @return boolean TRUE for suspicious comment
1564
- */
 
 
 
 
 
 
 
 
1565
 
1566
- private static function _is_regexp_spam($comment)
1567
- {
1568
- // Fields
 
 
 
 
 
 
 
 
1569
  $fields = array(
1570
  'ip',
1571
  'host',
@@ -1574,15 +1519,14 @@ class Antispam_Bee {
1574
  'author',
1575
  );
1576
 
1577
- // Regexp
1578
  $patterns = array(
1579
  array(
1580
- 'host' => '^(www\.)?\d+\w+\.com$',
1581
- 'body' => '^\w+\s\d+$',
1582
- 'email' => '@gmail.com$',
1583
  ),
1584
  array(
1585
- 'body' => '\<\!.+?mfunc.+?\>',
1586
  ),
1587
  array(
1588
  'author' => 'moncler|north face|vuitton|handbag|burberry|outlet|prada|cialis|viagra|maillot|oakley|ralph lauren|ray ban|iphone|プラダ',
@@ -1594,13 +1538,16 @@ class Antispam_Bee {
1594
  'body' => 'target[t]?ed (visitors|traffic)|viagra|cialis',
1595
  ),
1596
  array(
1597
- 'body' => 'dating|sex|lotto|pharmacy',
1598
- 'email' => '@mail\.ru|@yandex\.',
 
 
 
1599
  ),
1600
  );
1601
 
1602
- // Spammy author
1603
- if ( $quoted_author = preg_quote($comment['author'], '/') ) {
1604
  $patterns[] = array(
1605
  'body' => sprintf(
1606
  '<a.+?>%s<\/a>$',
@@ -1614,52 +1561,44 @@ class Antispam_Bee {
1614
  ),
1615
  );
1616
  $patterns[] = array(
1617
- 'email' => '@gmail.com$',
1618
  'author' => '^[a-z0-9-\.]+\.[a-z]{2,6}$',
1619
- 'host' => sprintf(
1620
  '^%s$',
1621
  $quoted_author
1622
  ),
1623
  );
1624
  }
1625
 
1626
- // Hook
1627
  $patterns = apply_filters(
1628
  'antispam_bee_patterns',
1629
  $patterns
1630
  );
1631
 
1632
- // Empty?
1633
  if ( ! $patterns ) {
1634
  return false;
1635
  }
1636
 
1637
- // Loop expressions
1638
- foreach ($patterns as $pattern) {
1639
  $hits = array();
1640
 
1641
- // Loop fields
1642
- foreach ($pattern as $field => $regexp) {
1643
- // Empty value?
1644
- if ( empty($field) OR !in_array($field, $fields) OR empty($regexp) ) {
1645
  continue;
1646
  }
1647
 
1648
- // Ignore non utf-8 chars
1649
- $comment[$field] = ( function_exists('iconv') ? iconv('utf-8', 'utf-8//TRANSLIT', $comment[$field]) : $comment[$field] );
1650
 
1651
- // Empty value?
1652
- if ( empty($comment[$field]) ) {
1653
  continue;
1654
  }
1655
 
1656
- // Perform regex
1657
- if ( @preg_match('/' .$regexp. '/isu', $comment[$field]) ) {
1658
- $hits[$field] = true;
1659
  }
1660
  }
1661
 
1662
- if ( count($hits) === count($pattern) ) {
1663
  return true;
1664
  }
1665
  }
@@ -1669,23 +1608,21 @@ class Antispam_Bee {
1669
 
1670
 
1671
  /**
1672
- * Review a comment on its existence in the local spam
1673
- *
1674
- * @since 2.0.0
1675
- * @change 2.5.4
1676
- *
1677
- * @param string $ip Comment IP
1678
- * @param string $url Comment URL [optional]
1679
- * @param string $email Comment Email [optional]
1680
- * @return boolean TRUE for suspicious comment
1681
- */
1682
-
1683
- private static function _is_db_spam($ip, $url = '', $email = '')
1684
- {
1685
- // Global
1686
  global $wpdb;
1687
 
1688
- $sql = '
 
1689
  select
1690
  meta_value as ip
1691
  from
@@ -1696,9 +1633,9 @@ class Antispam_Bee {
1696
  AND meta.meta_key = "antispam_bee_iphash"
1697
  AND comments.comment_approved="spam"';
1698
  $hashed_ips = $wpdb->get_col( $sql );
1699
- if( ! empty( $hashed_ips) ) {
1700
  foreach ( $hashed_ips as $hash ) {
1701
- if( wp_check_password($ip, $hash) ) {
1702
  return true;
1703
  }
1704
  }
@@ -1706,53 +1643,51 @@ class Antispam_Bee {
1706
 
1707
  $params = array();
1708
  $filter = array();
1709
- // Match the URL
1710
- if ( ! empty($url) ) {
1711
  $filter[] = '`comment_author_url` = %s';
1712
- $params[] = wp_unslash($url);
1713
  }
1714
 
1715
- // Match the E-mail
1716
- if ( ! empty($email) ) {
1717
  $filter[] = '`comment_author_email` = %s';
1718
- $params[] = wp_unslash($email);
1719
  }
1720
-
1721
- if( empty( $params ) ) {
1722
  return false;
1723
  }
1724
 
1725
- // Perform query
 
 
 
1726
  $result = $wpdb->get_var(
1727
  $wpdb->prepare(
1728
  sprintf(
1729
  "SELECT `comment_ID` FROM `$wpdb->comments` WHERE `comment_approved` = 'spam' AND (%s) LIMIT 1",
1730
- implode(' OR ', $filter)
1731
  ),
1732
  $params
1733
  )
1734
  );
 
 
1735
 
1736
- return !empty($result);
1737
  }
1738
 
1739
 
1740
  /**
1741
- * Check for country spam by (anonymized) IP
1742
- *
1743
- * @since 2.6.9
1744
- * @change 2.6.9
1745
- *
1746
- * @param string $ip IP address
1747
- * @return boolean TRUE if the comment is spam based on country filter
1748
- */
1749
-
1750
- private static function _is_country_spam($ip)
1751
- {
1752
- // Get options
1753
  $options = self::get_options();
1754
 
1755
- // White & Black
1756
  $white = preg_split(
1757
  '/[\s,;]+/',
1758
  $options['country_white'],
@@ -1766,90 +1701,75 @@ class Antispam_Bee {
1766
  PREG_SPLIT_NO_EMPTY
1767
  );
1768
 
1769
- // Empty lists?
1770
- if ( empty($white) && empty($black) ) {
1771
  return false;
1772
  }
1773
 
1774
- // IP 2 Country API
1775
  $response = wp_safe_remote_head(
1776
  esc_url_raw(
1777
  sprintf(
1778
  'https://api.ip2country.info/ip?%s',
1779
- self::_anonymize_ip($ip)
1780
  ),
1781
  'https'
1782
  )
1783
  );
1784
 
1785
- // Error by WP
1786
- if ( is_wp_error($response) ) {
1787
  return false;
1788
  }
1789
 
1790
- // Response code check
1791
- if ( wp_remote_retrieve_response_code($response) !== 200 ) {
1792
  return false;
1793
  }
1794
 
1795
- // Get country code
1796
- $country = (string)wp_remote_retrieve_header($response, 'x-country-code');
1797
 
1798
- // Country code check
1799
- if ( empty($country) OR strlen($country) !== 2 ) {
1800
  return false;
1801
  }
1802
 
1803
- // Dive into blacklist
1804
- if ( ! empty($black) ) {
1805
- return ( in_array($country, $black) );
1806
  }
1807
 
1808
- // Dive into whitelist
1809
- return ( ! in_array($country, $white) );
1810
  }
1811
 
1812
 
1813
  /**
1814
- * Check for BBCode spam
1815
- *
1816
- * @since 2.5.1
1817
- * @change 2.5.1
1818
- *
1819
- * @param string $body Content of a comment
1820
- * @return boolean TRUE for BBCode in content
1821
- */
1822
-
1823
- private static function _is_bbcode_spam($body)
1824
- {
1825
- return (bool) preg_match('/\[url[=\]].*\[\/url\]/is', $body);
1826
  }
1827
 
1828
 
1829
  /**
1830
- * Check for an already approved e-mail address
1831
- *
1832
- * @since 2.0
1833
- * @change 2.5.1
1834
- *
1835
- * @param string $email E-mail address
1836
- * @return boolean TRUE for a found entry
1837
- */
1838
-
1839
- private static function _is_approved_email($email)
1840
- {
1841
- // Global
1842
  global $wpdb;
1843
 
1844
- // Search
1845
  $result = $wpdb->get_var(
1846
  $wpdb->prepare(
1847
  "SELECT `comment_ID` FROM `$wpdb->comments` WHERE `comment_approved` = '1' AND `comment_author_email` = %s LIMIT 1",
1848
- wp_unslash($email)
1849
  )
1850
  );
1851
 
1852
- // Found?
1853
  if ( $result ) {
1854
  return true;
1855
  }
@@ -1859,44 +1779,37 @@ class Antispam_Bee {
1859
 
1860
 
1861
  /**
1862
- * Check for a fake IP
1863
- *
1864
- * @since 2.0
1865
- * @change 2.6.2
1866
- *
1867
- * @param string $ip Client IP
1868
- * @param string $host Client Host [optional]
1869
- * @return boolean TRUE if fake IP
1870
- */
1871
-
1872
- private static function _is_fake_ip($client_ip, $client_host = false)
1873
- {
1874
- // Remote Host
1875
- $host_by_ip = gethostbyaddr($client_ip);
1876
 
1877
- // IPv6
1878
- if ( self::_is_ipv6($client_ip) ) {
1879
- return $client_ip != $host_by_ip;
1880
  }
1881
 
1882
- // IPv4 and Comment
1883
- if ( empty($client_host) ) {
1884
- $ip_by_host = gethostbyname($host_by_ip);
1885
 
1886
  if ( $ip_by_host === $host_by_ip ) {
1887
  return false;
1888
  }
1889
-
1890
- // IPv4 and Trackback
1891
  } else {
1892
  if ( $host_by_ip === $client_ip ) {
1893
  return true;
1894
  }
1895
 
1896
- $ip_by_host = gethostbyname($client_host);
1897
  }
1898
 
1899
- if ( strpos( $client_ip, self::_cut_ip($ip_by_host) ) === false ) {
1900
  return true;
1901
  }
1902
 
@@ -1908,103 +1821,282 @@ class Antispam_Bee {
1908
  *
1909
  * @since 2.0
1910
  * @change 2.6.6
1911
- * @change 2.7.0
1912
  *
1913
  * @param string $comment_content Content of the comment.
1914
  *
1915
- * @return boolean TRUE if it is spam
1916
  */
1917
-
1918
  private static function _is_lang_spam( $comment_content ) {
1919
- // User defined language
1920
  $allowed_lang = (array) self::get_option( 'translate_lang' );
1921
 
1922
- // Make comment text plain
1923
  $comment_text = wp_strip_all_tags( $comment_content );
1924
 
1925
- // Skip if empty values
1926
- if ( empty( $allowed_lang )
1927
- || empty( $comment_text )
1928
- ) {
1929
- return false;
1930
- }
1931
-
1932
- // Trim comment text
1933
- if ( ! $query_text = wp_trim_words( $comment_text, 10, '' ) ) {
1934
  return false;
1935
  }
1936
 
1937
  /**
1938
- * Filter the Google Translate API key to be used.
1939
  *
1940
- * @since 2.7.0
1941
  *
1942
- * @param string $key API key to use.
 
1943
  *
1944
- * @return string Modified API key.
1945
  */
1946
- $key = apply_filters(
1947
- 'ab_google_translate_api_key',
1948
- base64_decode(
1949
- strrev( 'B9GcXFjbjdULkdDUfh1SOlzZ2FzMhF1Mt1kRWVTWoVHR5NVY6lUQ' )
1950
- )
1951
- );
1952
 
1953
- // Start request
1954
- $response = wp_safe_remote_request(
1955
- add_query_arg(
1956
- array(
1957
- 'q' => rawurlencode( $query_text ),
1958
- 'key' => $key,
1959
- ),
1960
- 'https://www.googleapis.com/language/translate/v2/detect'
1961
- )
1962
  );
1963
 
1964
- // Skip on error
1965
  if ( is_wp_error( $response )
1966
- || wp_remote_retrieve_response_code( $response ) !== 200 ) {
1967
  return false;
1968
  }
1969
 
1970
- // Get JSON from content
1971
- if ( ! $json = wp_remote_retrieve_body( $response ) ) {
1972
  return false;
1973
  }
1974
 
1975
- // Decode JSON
1976
- if ( ! $data_array = json_decode( $json, true ) ) {
1977
  return false;
1978
  }
1979
 
1980
- if( ! isset( $data_array['data']['detections'][0][0]['language'] ) ) {
1981
- return false;
1982
- }
1983
- // Get detected language
1984
- if ( ! $detected_lang = $data_array['data']['detections'][0][0]['language'] ) {
1985
- return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1986
  }
1987
 
1988
- return ! in_array( $detected_lang, $allowed_lang, true );
1989
  }
1990
 
1991
  /**
1992
- * Trim IP addresses
1993
- *
1994
- * @since 0.1
1995
- * @change 2.5.1
1996
- *
1997
- * @param string $ip Original IP
1998
- * @param boolean $cut_end Shortening the end?
1999
- * @return string Shortened IP
2000
- */
2001
-
2002
- private static function _cut_ip($ip, $cut_end = true)
2003
- {
2004
- $separator = ( self::_is_ipv4($ip) ? '.' : ':' );
2005
 
2006
  return str_replace(
2007
- ( $cut_end ? strrchr( $ip, $separator) : strstr( $ip, $separator) ),
2008
  '',
2009
  $ip
2010
  );
@@ -2012,37 +2104,33 @@ class Antispam_Bee {
2012
 
2013
 
2014
  /**
2015
- * Anonymize the IP addresses
2016
- *
2017
- * @since 2.5.1
2018
- * @change 2.5.1
2019
- *
2020
- * @param string $ip Original IP
2021
- * @return string Anonymous IP
2022
- */
2023
-
2024
- private static function _anonymize_ip($ip)
2025
- {
2026
- if ( self::_is_ipv4($ip) ) {
2027
- return self::_cut_ip($ip). '.0';
2028
  }
2029
 
2030
- return self::_cut_ip($ip, false). ':0:0:0:0:0:0:0';
2031
  }
2032
 
2033
 
2034
  /**
2035
- * Rotates the IP address
2036
- *
2037
- * @since 2.4.5
2038
- * @change 2.4.5
2039
- *
2040
- * @param string $ip IP address
2041
- * @return string Turned IP address
2042
- */
2043
-
2044
- private static function _reverse_ip($ip)
2045
- {
2046
  return implode(
2047
  '.',
2048
  array_reverse(
@@ -2056,150 +2144,129 @@ class Antispam_Bee {
2056
 
2057
 
2058
  /**
2059
- * Check for an IPv4 address
2060
- *
2061
- * @since 2.4
2062
- * @change 2.6.4
2063
- *
2064
- * @param string $ip IP to validate
2065
- * @return integer TRUE if IPv4
2066
- */
2067
-
2068
- private static function _is_ipv4($ip)
2069
- {
2070
- if ( function_exists('filter_var') ) {
2071
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) !== false;
2072
  } else {
2073
- return preg_match('/^\d{1,3}(\.\d{1,3}){3,3}$/', $ip);
2074
  }
2075
  }
2076
 
2077
 
2078
  /**
2079
- * Check for an IPv6 address
2080
- *
2081
- * @since 2.6.2
2082
- * @change 2.6.4
2083
- *
2084
- * @param string $ip IP to validate
2085
- * @return boolean TRUE if IPv6
2086
- */
2087
-
2088
- private static function _is_ipv6($ip)
2089
- {
2090
- if ( function_exists('filter_var') ) {
2091
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) !== false;
2092
  } else {
2093
- return ! self::_is_ipv4($ip);
2094
  }
2095
  }
2096
 
2097
 
2098
  /**
2099
- * Testing on mobile devices
2100
- *
2101
- * @since 0.1
2102
- * @change 2.4
2103
- *
2104
- * @return boolean TRUE if "wptouch" is active
2105
- */
2106
-
2107
- private static function _is_mobile()
2108
- {
2109
- return strpos(TEMPLATEPATH, 'wptouch');
2110
  }
2111
 
2112
 
2113
 
2114
- /*
2115
- * ############################
2116
- * ##### SPAM-TREATMENT #####
2117
- * ############################
2118
- */
2119
 
2120
  /**
2121
- * Execution of the delete/marking process
2122
- *
2123
- * @since 0.1
2124
- * @change 2.6.0
2125
- *
2126
- * @param array $comment Untreated commentary data
2127
- * @param string $reason Reason for suspicion
2128
- * @param boolean $is_ping Ping (yes or no) [optional]
2129
- * @return array $comment Treated commentary data
2130
- */
 
2131
 
2132
- private static function _handle_spam_request($comment, $reason, $is_ping = false)
2133
- {
2134
- // Options
2135
  $options = self::get_options();
2136
 
2137
- // Settings
2138
- $spam_remove = !$options['flag_spam'];
2139
- $spam_notice = !$options['no_notice'];
2140
 
2141
- // Filter settings
2142
  $ignore_filter = $options['ignore_filter'];
2143
- $ignore_type = $options['ignore_type'];
2144
- $ignore_reason = in_array($reason, (array)$options['ignore_reasons']);
2145
 
2146
- // Remember spam
2147
- self::_update_spam_log($comment);
2148
  self::_update_spam_count();
2149
  self::_update_daily_stats();
2150
 
2151
- // Delete spam
2152
  if ( $spam_remove ) {
2153
  self::_go_in_peace();
2154
  }
2155
 
2156
- // Save IP hash, if comment is spam.
2157
- add_action(
2158
- 'comment_post',
2159
- array(
2160
- __CLASS__,
2161
- 'save_ip_hash',
2162
- ),
2163
- 10,
2164
- 3
2165
- );
2166
-
2167
- // Handle types
2168
- if ( $ignore_filter && (( $ignore_type == 1 && $is_ping ) or ( $ignore_type == 2 && !$is_ping )) ) {
2169
  self::_go_in_peace();
2170
  }
2171
 
2172
- // Spam reason
2173
  if ( $ignore_reason ) {
2174
  self::_go_in_peace();
2175
  }
2176
  self::$_reason = $reason;
2177
 
2178
- // Mark spam
2179
  add_filter(
2180
  'pre_comment_approved',
2181
  array(
2182
  __CLASS__,
2183
- 'return_spam'
2184
  )
2185
  );
2186
 
2187
- // Send e-mail
2188
  add_action(
2189
  'comment_post',
2190
  array(
2191
  __CLASS__,
2192
- 'send_mail_notification'
2193
  )
2194
  );
2195
 
2196
- // Spam reason as comment meta
2197
  if ( $spam_notice ) {
2198
  add_action(
2199
  'comment_post',
2200
  array(
2201
  __CLASS__,
2202
- 'add_spam_reason_to_comment'
2203
  )
2204
  );
2205
  }
@@ -2209,32 +2276,27 @@ class Antispam_Bee {
2209
 
2210
 
2211
  /**
2212
- * Logfile with detected spam
2213
- *
2214
- * @since 2.5.7
2215
- * @change 2.6.1
2216
- *
2217
- * @param array $comment Array with commentary data
2218
- * @return mixed FALSE in case of error
2219
- */
2220
-
2221
- private static function _update_spam_log($comment)
2222
- {
2223
- // Skip logfile?
2224
- if ( ! defined('ANTISPAM_BEE_LOG_FILE') OR ! ANTISPAM_BEE_LOG_FILE OR ! is_writable(ANTISPAM_BEE_LOG_FILE) OR validate_file(ANTISPAM_BEE_LOG_FILE) === 1 ) {
2225
  return false;
2226
  }
2227
 
2228
- // Compose entry
2229
  $entry = sprintf(
2230
  '%s comment for post=%d from host=%s marked as spam%s',
2231
- current_time('mysql'),
2232
  $comment['comment_post_ID'],
2233
  $comment['comment_author_IP'],
2234
  PHP_EOL
2235
  );
2236
 
2237
- // Write
2238
  file_put_contents(
2239
  ANTISPAM_BEE_LOG_FILE,
2240
  $entry,
@@ -2244,52 +2306,51 @@ class Antispam_Bee {
2244
 
2245
 
2246
  /**
2247
- * Sends the 403 header and terminates the connection
2248
- *
2249
- * @since 2.5.6
2250
- * @change 2.5.6
2251
- */
2252
-
2253
- private static function _go_in_peace()
2254
- {
2255
- status_header(403);
2256
- die('Spam deleted.');
2257
  }
2258
 
2259
 
2260
  /**
2261
- * Return real client IP
2262
- *
2263
- * @since 2.6.1
2264
- * @change 2.6.1
2265
- *
2266
- * @return mixed $ip Client IP
2267
- */
2268
-
2269
- public static function get_client_ip()
2270
- {
2271
- if ( isset($_SERVER['HTTP_CLIENT_IP']) ) {
2272
- $ip = $_SERVER['HTTP_CLIENT_IP'];
2273
- } else if ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) ) {
2274
- $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
2275
- } else if ( isset($_SERVER['HTTP_X_FORWARDED']) ) {
2276
- $ip = $_SERVER['HTTP_X_FORWARDED'];
2277
- } else if ( isset($_SERVER['HTTP_FORWARDED_FOR']) ) {
2278
- $ip = $_SERVER['HTTP_FORWARDED_FOR'];
2279
- } else if ( isset($_SERVER['HTTP_FORWARDED']) ) {
2280
- $ip = $_SERVER['HTTP_FORWARDED'];
2281
- } else if ( isset($_SERVER['REMOTE_ADDR']) ) {
2282
- $ip = $_SERVER['REMOTE_ADDR'];
2283
  } else {
2284
  return '';
2285
  }
 
2286
 
2287
- if ( strpos($ip, ',') !== false ) {
2288
- $ips = explode(',', $ip);
2289
- $ip = trim(@$ips[0]);
2290
  }
2291
 
2292
- if ( function_exists('filter_var') ) {
2293
  return filter_var(
2294
  $ip,
2295
  FILTER_VALIDATE_IP
@@ -2303,18 +2364,15 @@ class Antispam_Bee {
2303
  );
2304
  }
2305
 
2306
-
2307
  /**
2308
- * Add spam reason as comment data
2309
- *
2310
- * @since 2.6.0
2311
- * @change 2.6.0
2312
- *
2313
- * @param integer $comment_id Comment ID
2314
- */
2315
-
2316
- public static function add_spam_reason_to_comment( $comment_id )
2317
- {
2318
  add_comment_meta(
2319
  $comment_id,
2320
  'antispam_bee_reason',
@@ -2322,9 +2380,13 @@ class Antispam_Bee {
2322
  );
2323
  }
2324
 
2325
- public static function save_ip_hash( $comment_id, $approved, $comment_data )
2326
- {
2327
- $hashed_ip = self::hash_ip( $comment_data['comment_author_IP'] );
 
 
 
 
2328
  add_comment_meta(
2329
  $comment_id,
2330
  'antispam_bee_iphash',
@@ -2332,23 +2394,27 @@ class Antispam_Bee {
2332
  );
2333
  }
2334
 
2335
- public static function hash_ip($ip)
2336
- {
 
 
 
 
 
 
2337
  return wp_hash_password( $ip );
2338
  }
2339
 
2340
 
2341
  /**
2342
- * Delete spam reason as comment data
2343
- *
2344
- * @since 2.6.0
2345
- * @change 2.6.0
2346
- *
2347
- * @param integer $comment_id Comment ID
2348
- */
2349
-
2350
- public static function delete_spam_reason_by_comment( $comment_id )
2351
- {
2352
  delete_comment_meta(
2353
  $comment_id,
2354
  'antispam_bee_reason'
@@ -2359,8 +2425,6 @@ class Antispam_Bee {
2359
  * Get the current post ID.
2360
  *
2361
  * @since 2.7.1
2362
- *
2363
- * @param integer $comment_id Comment ID
2364
  */
2365
  public static function populate_post_id() {
2366
 
@@ -2371,132 +2435,126 @@ class Antispam_Bee {
2371
 
2372
 
2373
  /**
2374
- *Send notification via e-mail
2375
- *
2376
- * @since 0.1
2377
- * @change 2.5.7
2378
- *
2379
- * @hook string antispam_bee_notification_subject Custom subject for notification mails
2380
- *
2381
- * @param intval $id ID des Kommentars
2382
- * @return intval $id ID des Kommentars
2383
- */
2384
-
2385
- public static function send_mail_notification($id)
2386
- {
2387
- // Options
2388
  $options = self::get_options();
2389
 
2390
- // No notification?
2391
- if ( !$options['email_notify'] ) {
2392
  return $id;
2393
  }
2394
 
2395
- // Comment
2396
- $comment = get_comment($id, ARRAY_A);
2397
 
2398
- // No values?
2399
- if ( empty($comment) ) {
2400
  return $id;
2401
  }
2402
 
2403
- // Parent Post
2404
- if ( ! $post = get_post($comment['comment_post_ID']) ) {
2405
  return $id;
2406
  }
2407
 
2408
- // Load the language
2409
  self::load_plugin_lang();
2410
 
2411
- // Subject
2412
  $subject = sprintf(
2413
  '[%s] %s',
2414
  stripslashes_deep(
2415
  html_entity_decode(
2416
- get_bloginfo('name'),
2417
  ENT_QUOTES
2418
  )
2419
  ),
2420
- esc_html__('Comment marked as spam', 'antispam-bee')
2421
  );
2422
 
2423
- // Content
2424
- if ( !$content = strip_tags(stripslashes($comment['comment_content'])) ) {
 
2425
  $content = sprintf(
2426
  '-- %s --',
2427
- esc_html__('Content removed by Antispam Bee', 'antispam-bee')
2428
  );
2429
  }
2430
 
2431
- // Body
2432
  $body = sprintf(
2433
  "%s \"%s\"\r\n\r\n",
2434
- esc_html__('New spam comment on your post', 'antispam-bee'),
2435
- strip_tags($post->post_title)
2436
- ).sprintf(
2437
  "%s: %s\r\n",
2438
- esc_html__('Author', 'antispam-bee'),
2439
- ( empty($comment['comment_author']) ? '' : strip_tags($comment['comment_author']) )
2440
- ).sprintf(
2441
  "URL: %s\r\n",
2442
- // empty check exists
2443
- esc_url($comment['comment_author_url'])
2444
- ).sprintf(
2445
  "%s: %s\r\n",
2446
- esc_html__('Type', 'antispam-bee'),
2447
- esc_html__( ( empty($comment['comment_type']) ? 'Comment' : 'Trackback' ), 'antispam-bee' )
2448
- ).sprintf(
2449
  "Whois: http://whois.arin.net/rest/ip/%s\r\n",
2450
  $comment['comment_author_IP']
2451
- ).sprintf(
2452
  "%s: %s\r\n\r\n",
2453
- esc_html__('Spam Reason', 'antispam-bee'),
2454
- esc_html__(self::$defaults['reasons'][self::$_reason], 'antispam-bee')
2455
- ).sprintf(
2456
  "%s\r\n\r\n\r\n",
2457
  $content
2458
- ).(
2459
  EMPTY_TRASH_DAYS ? (
2460
  sprintf(
2461
  "%s: %s\r\n",
2462
- esc_html__('Trash it', 'antispam-bee'),
2463
- admin_url('comment.php?action=trash&c=' .$id)
2464
  )
2465
  ) : (
2466
  sprintf(
2467
  "%s: %s\r\n",
2468
- esc_html__('Delete it', 'antispam-bee'),
2469
- admin_url('comment.php?action=delete&c=' .$id)
2470
  )
2471
  )
2472
- ).sprintf(
2473
- "%s: %s\r\n",
2474
- esc_html__('Approve it', 'antispam-bee'),
2475
- admin_url('comment.php?action=approve&c=' .$id)
2476
- ).sprintf(
2477
  "%s: %s\r\n\r\n",
2478
- esc_html__('Spam list', 'antispam-bee'),
2479
- admin_url('edit-comments.php?comment_status=spam')
2480
- ).sprintf(
2481
  "%s\r\n%s\r\n",
2482
- esc_html__('Notify message by Antispam Bee', 'antispam-bee'),
2483
- esc_html__('http://antispambee.com', 'antispam-bee')
2484
  );
2485
 
2486
- // Send
2487
  wp_mail(
2488
  /**
2489
- * Filters the recipients of the spam notification
 
2490
  * @param array The recipients array.
2491
- **/
2492
  apply_filters(
2493
  'antispam_bee_notification_recipients',
2494
- array( get_bloginfo('admin_email') )
2495
  ),
2496
  /**
2497
- * Filters the subject of the spam notification
 
2498
  * @param string $subject subject line.
2499
- **/
2500
  apply_filters(
2501
  'antispam_bee_notification_subject',
2502
  $subject
@@ -2509,121 +2567,116 @@ class Antispam_Bee {
2509
 
2510
 
2511
 
2512
- /*
2513
- * ############################
2514
- * ####### STATISTICS #######
2515
- * ############################
2516
- */
2517
-
2518
- /**
2519
- * Return the number of spam comments
2520
- *
2521
- * @since 0.1
2522
- * @change 2.4
2523
- *
2524
- * @param intval $count Number of spam comments
2525
  */
2526
 
2527
- private static function _get_spam_count()
2528
- {
2529
- // Init
2530
- $count = self::get_option('spam_count');
 
 
 
 
 
2531
 
2532
- // Fire
2533
- return ( get_locale() == 'de_DE' ? number_format($count, 0, '', '.') : number_format_i18n($count) );
2534
  }
2535
 
2536
 
2537
  /**
2538
- * Output the number of spam comments
2539
- *
2540
- * @since 0.1
2541
- * @change 2.4
2542
- */
2543
-
2544
- public static function the_spam_count()
2545
- {
2546
  echo esc_html( self::_get_spam_count() );
2547
  }
2548
 
2549
 
2550
  /**
2551
- * Update the number of spam comments
2552
- *
2553
- * @since 0.1
2554
- * @change 2.6.1
2555
- */
2556
-
2557
- private static function _update_spam_count()
2558
- {
2559
- // Skip if not enabled
2560
- if ( ! self::get_option('dashboard_count') ) {
2561
  return;
2562
  }
2563
 
2564
  self::_update_option(
2565
  'spam_count',
2566
- intval( self::get_option('spam_count') + 1 )
2567
  );
2568
  }
2569
 
2570
-
2571
  /**
2572
- * Update statistics
2573
- *
2574
- * @since 1.9
2575
- * @change 2.6.1
2576
- */
2577
-
2578
- private static function _update_daily_stats()
2579
- {
2580
- // Skip if not enabled
2581
- if ( ! self::get_option('dashboard_chart') ) {
2582
  return;
2583
  }
2584
 
2585
- // Init
2586
- $stats = (array)self::get_option('daily_stats');
2587
- $today = (int)strtotime('today');
2588
 
2589
- // Count up
2590
- if ( array_key_exists($today, $stats) ) {
2591
- $stats[$today] ++;
2592
  } else {
2593
- $stats[$today] = 1;
2594
  }
2595
 
2596
- // Sort
2597
- krsort($stats, SORT_NUMERIC);
2598
 
2599
- // Save
2600
  self::_update_option(
2601
  'daily_stats',
2602
- array_slice($stats, 0, 31, true)
2603
  );
2604
  }
2605
 
2606
  /**
2607
  * Returns the secret of a post used in the textarea name attribute.
2608
  *
2609
- * @param int $post_id
2610
  *
2611
  * @return string
2612
  */
2613
  public static function get_secret_name_for_post( $post_id ) {
2614
 
2615
- $secret = substr( sha1( md5( self::$_salt . (int) $post_id ) ), 0, 10 );
 
 
 
 
2616
 
2617
  /**
2618
  * Filters the secret for a post, which is used in the textarea name attribute.
2619
  *
2620
  * @param string $secret The secret.
2621
  * @param int $post_id The post ID.
 
2622
  */
2623
  return apply_filters(
2624
  'ab_get_secret_name_for_post',
2625
  $secret,
2626
- (int) $post_id
 
2627
  );
2628
 
2629
  }
@@ -2631,24 +2684,30 @@ class Antispam_Bee {
2631
  /**
2632
  * Returns the secret of a post used in the textarea id attribute.
2633
  *
2634
- * @param int $post_id
2635
  *
2636
  * @return string
2637
  */
2638
  public static function get_secret_id_for_post( $post_id ) {
2639
 
2640
- $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
 
 
 
 
2641
 
2642
  /**
2643
  * Filters the secret for a post, which is used in the textarea id attribute.
2644
  *
2645
  * @param string $secret The secret.
2646
  * @param int $post_id The post ID.
 
2647
  */
2648
  return apply_filters(
2649
  'ab_get_secret_id_for_post',
2650
  $secret,
2651
- (int) $post_id
 
2652
  );
2653
  }
2654
 
@@ -2663,41 +2722,57 @@ class Antispam_Bee {
2663
 
2664
  return 'spam';
2665
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2666
  }
2667
 
2668
 
2669
- // Fire
2670
  add_action(
2671
  'plugins_loaded',
2672
  array(
2673
  'Antispam_Bee',
2674
- 'init'
2675
  )
2676
  );
2677
 
2678
- // Activation
2679
  register_activation_hook(
2680
  __FILE__,
2681
  array(
2682
  'Antispam_Bee',
2683
- 'activate'
2684
  )
2685
  );
2686
 
2687
- // Deactivation
2688
  register_deactivation_hook(
2689
  __FILE__,
2690
  array(
2691
  'Antispam_Bee',
2692
- 'deactivate'
2693
  )
2694
  );
2695
 
2696
- // Uninstall
2697
  register_uninstall_hook(
2698
  __FILE__,
2699
  array(
2700
  'Antispam_Bee',
2701
- 'uninstall'
2702
  )
2703
  );
1
  <?php
2
+ /**
3
+ * Plugin Name: Antispam Bee
4
+ * Description: Antispam plugin with a sophisticated toolset for effective day to day comment and trackback spam-fighting. Built with data protection and privacy in mind.
5
+ * Author: pluginkollektiv
6
+ * Author URI: https://pluginkollektiv.org
7
+ * Plugin URI: https://wordpress.org/plugins/antispam-bee/
8
+ * Text Domain: antispam-bee
9
+ * Domain Path: /lang
10
+ * License: GPLv2 or later
11
+ * License URI: http://www.gnu.org/licenses/gpl-2.0.html
12
+ * Version: 2.9.0
13
+ *
14
+ * @package Antispam Bee
15
+ **/
16
 
17
  /*
18
  * Copyright (C) 2009-2015 Sergej Müller
36
  // Make sure this file is only run from within the WordPress context.
37
  defined( 'ABSPATH' ) || exit;
38
 
 
39
  /**
40
+ * Antispam_Bee
41
+ *
42
+ * @since 0.1
43
+ * @change 2.4
44
+ */
 
45
  class Antispam_Bee {
46
 
47
+ /**
48
+ * The option defaults.
49
+ *
50
+ * @var array
51
+ */
52
  public static $defaults;
53
+
54
+ /**
55
+ * The base.
56
+ *
57
+ * @var string
58
+ */
59
  private static $_base;
60
+
61
+ /**
62
+ * The salt.
63
+ *
64
+ * @var string
65
+ */
66
  private static $_salt;
67
+
68
+
69
+ /**
70
+ * The spam reason.
71
+ *
72
+ * @var string
73
+ */
74
  private static $_reason;
 
75
 
76
+ /**
77
+ * The current Post ID.
78
+ *
79
+ * @var int
80
+ */
81
+ private static $_current_post_id;
82
 
83
  /**
84
+ * "Constructor" of the class
85
+ *
86
+ * @since 0.1
87
+ * @change 2.6.4
88
+ */
89
+ public static function init() {
90
+ add_action(
91
+ 'unspam_comment',
92
+ array(
93
+ __CLASS__,
94
+ 'delete_spam_reason_by_comment',
95
+ )
96
+ );
97
 
98
+ if ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
99
  return;
100
  }
101
 
 
102
  self::_init_internal_vars();
103
 
104
+ if ( defined( 'DOING_CRON' ) ) {
 
105
  add_action(
106
  'antispam_bee_daily_cronjob',
107
  array(
108
  __CLASS__,
109
+ 'start_daily_cronjob',
110
  )
111
  );
112
 
 
113
  } elseif ( is_admin() ) {
 
114
  add_action(
115
  'admin_menu',
116
  array(
117
  __CLASS__,
118
+ 'add_sidebar_menu',
119
  )
120
  );
121
 
122
+ if ( self::_current_page( 'dashboard' ) ) {
 
123
  add_action(
124
  'init',
125
  array(
126
  __CLASS__,
127
+ 'load_plugin_lang',
128
  )
129
  );
130
  add_filter(
131
  'dashboard_glance_items',
132
  array(
133
  __CLASS__,
134
+ 'add_dashboard_count',
135
  )
136
  );
137
  add_action(
138
  'wp_dashboard_setup',
139
  array(
140
  __CLASS__,
141
+ 'add_dashboard_chart',
142
  )
143
  );
144
 
145
+ } elseif ( self::_current_page( 'plugins' ) ) {
 
146
  add_action(
147
  'init',
148
  array(
149
  __CLASS__,
150
+ 'load_plugin_lang',
151
  )
152
  );
153
  add_filter(
154
  'plugin_row_meta',
155
  array(
156
  __CLASS__,
157
+ 'init_row_meta',
158
  ),
159
  10,
160
  2
161
  );
162
  add_filter(
163
+ 'plugin_action_links_' . self::$_base,
164
  array(
165
  __CLASS__,
166
+ 'init_action_links',
167
  )
168
  );
169
 
170
+ } elseif ( self::_current_page( 'options' ) ) {
 
171
  add_action(
172
  'admin_init',
173
  array(
174
  __CLASS__,
175
+ 'load_plugin_lang',
176
  )
177
  );
178
  add_action(
179
  'admin_init',
180
  array(
181
  __CLASS__,
182
+ 'init_plugin_sources',
183
  )
184
  );
185
 
186
+ } elseif ( self::_current_page( 'admin-post' ) ) {
187
+ require_once dirname( __FILE__ ) . '/inc/gui.class.php';
188
 
189
  add_action(
190
  'admin_post_ab_save_changes',
191
  array(
192
  'Antispam_Bee_GUI',
193
+ 'save_changes',
194
  )
195
  );
196
 
197
+ } elseif ( self::_current_page( 'edit-comments' ) ) {
198
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
199
+ if ( ! empty( $_GET['comment_status'] ) && 'spam' === $_GET['comment_status'] && ! self::get_option( 'no_notice' ) ) {
200
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
201
+ require_once dirname( __FILE__ ) . '/inc/columns.class.php';
202
 
 
203
  self::load_plugin_lang();
204
 
 
205
  add_filter(
206
  'manage_edit-comments_columns',
207
  array(
208
  'Antispam_Bee_Columns',
209
+ 'register_plugin_columns',
210
  )
211
  );
212
  add_filter(
213
  'manage_comments_custom_column',
214
  array(
215
  'Antispam_Bee_Columns',
216
+ 'print_plugin_column',
217
  ),
218
  10,
219
  2
220
  );
221
  add_filter(
222
+ 'admin_print_styles-edit-comments.php',
223
+ array(
224
+ 'Antispam_Bee_Columns',
225
+ 'print_column_styles',
226
+ )
227
  );
228
 
229
  add_filter(
230
  'manage_edit-comments_sortable_columns',
231
  array(
232
  'Antispam_Bee_Columns',
233
+ 'register_sortable_columns',
234
  )
235
  );
236
  add_action(
237
  'pre_get_posts',
238
  array(
239
  'Antispam_Bee_Columns',
240
+ 'set_orderby_query',
241
  )
242
  );
243
  }
244
  }
 
 
245
  } else {
246
  add_action(
247
  'wp',
248
  array(
249
  __CLASS__,
250
+ 'populate_post_id',
251
  )
252
  );
253
 
254
+ // Save IP hash, if comment is spam.
255
+ add_action(
256
+ 'comment_post',
257
+ array(
258
+ __CLASS__,
259
+ 'save_ip_hash',
260
+ ),
261
+ 10,
262
+ 1
263
+ );
264
+
265
  add_action(
266
  'template_redirect',
267
  array(
268
  __CLASS__,
269
+ 'prepare_comment_field',
270
  )
271
  );
272
  add_action(
273
  'init',
274
  array(
275
  __CLASS__,
276
+ 'precheck_incoming_request',
277
  )
278
  );
279
  add_action(
280
  'preprocess_comment',
281
  array(
282
  __CLASS__,
283
+ 'handle_incoming_request',
284
  ),
285
  1
286
  );
288
  'antispam_bee_count',
289
  array(
290
  __CLASS__,
291
+ 'the_spam_count',
292
  )
293
  );
294
  }
296
 
297
 
298
 
299
+ /*
300
+ * ############################
301
+ * ######## INSTALL #########
302
+ * ############################
 
 
 
 
 
 
 
303
  */
304
 
305
+ /**
306
+ * Action during the activation of the Plugins
307
+ *
308
+ * @since 0.1
309
+ * @change 2.4
310
+ */
311
+ public static function activate() {
312
  add_option(
313
  'antispam_bee',
314
  array(),
316
  'no'
317
  );
318
 
319
+ if ( self::get_option( 'cronjob_enable' ) ) {
 
320
  self::init_scheduled_hook();
321
  }
322
  }
323
 
324
 
325
  /**
326
+ * Action to deactivate the plugin
327
+ *
328
+ * @since 0.1
329
+ * @change 2.4
330
+ */
331
+ public static function deactivate() {
 
 
332
  self::clear_scheduled_hook();
333
  }
334
 
335
 
336
  /**
337
+ * Action deleting the plugin
338
+ *
339
+ * @since 2.4
340
+ * @change 2.4
341
+ */
342
+ public static function uninstall() {
343
+ if ( ! self::get_option( 'delete_data_on_uninstall' ) ) {
344
+ return;
345
+ }
346
  global $wpdb;
347
 
348
+ delete_option( 'antispam_bee' );
349
+ $wpdb->query( 'OPTIMIZE TABLE `' . $wpdb->options . '`' );
350
 
351
+ //phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
352
+ $sql = 'delete from `' . $wpdb->commentmeta . '` where `meta_key` IN ("antispam_bee_iphash", "antispam_bee_reason")';
353
+ $wpdb->query( $sql );
354
+ //phpcs:enable WordPress.WP.PreparedSQL.NotPrepared
355
  }
356
 
357
 
358
 
359
+ /*
360
+ * ############################
361
+ * ######## INTERNAL ########
362
+ * ############################
 
 
 
 
 
 
 
363
  */
364
 
365
+ /**
366
+ * Initialization of the internal variables
367
+ *
368
+ * @since 2.4
369
+ * @change 2.7.0
370
+ */
371
+ private static function _init_internal_vars() {
372
+ self::$_base = plugin_basename( __FILE__ );
373
 
374
+ $salt = defined( 'NONCE_SALT' ) ? NONCE_SALT : ABSPATH;
375
  self::$_salt = substr( sha1( $salt ), 0, 10 );
376
 
377
  self::$defaults = array(
378
  'options' => array(
379
+ 'advanced_check' => 1,
380
+ 'regexp_check' => 1,
381
+ 'spam_ip' => 1,
382
+ 'already_commented' => 1,
383
+ 'gravatar_check' => 0,
384
+ 'time_check' => 0,
385
+ 'ignore_pings' => 0,
386
+ 'always_allowed' => 0,
387
+
388
+ 'dashboard_chart' => 0,
389
+ 'dashboard_count' => 0,
390
+
391
+ 'country_code' => 0,
392
+ 'country_black' => '',
393
+ 'country_white' => '',
394
+
395
+ 'translate_api' => 0,
396
+ 'translate_lang' => array(),
397
+
398
+ 'bbcode_check' => 1,
399
+
400
+ 'flag_spam' => 1,
401
+ 'email_notify' => 0,
402
+ 'no_notice' => 0,
403
+ 'cronjob_enable' => 0,
404
+ 'cronjob_interval' => 0,
405
+
406
+ 'ignore_filter' => 0,
407
+ 'ignore_type' => 0,
408
+
409
+ 'reasons_enable' => 0,
410
+ 'ignore_reasons' => array(),
411
+
412
+ 'delete_data_on_uninstall' => 1,
 
413
  ),
414
  'reasons' => array(
415
+ 'css' => esc_attr__( 'Honeypot', 'antispam-bee' ),
416
+ 'time' => esc_attr__( 'Comment time', 'antispam-bee' ),
417
+ 'empty' => esc_attr__( 'Empty Data', 'antispam-bee' ),
418
+ 'server' => esc_attr__( 'Fake IP', 'antispam-bee' ),
419
+ 'localdb' => esc_attr__( 'Local DB Spam', 'antispam-bee' ),
420
+ 'country' => esc_attr__( 'Country Check', 'antispam-bee' ),
421
+ 'bbcode' => esc_attr__( 'BBCode', 'antispam-bee' ),
422
+ 'lang' => esc_attr__( 'Comment Language', 'antispam-bee' ),
423
+ 'regexp' => esc_attr__( 'Regular Expression', 'antispam-bee' ),
424
+ 'title_is_name' => esc_attr__( 'Identical Post title and blog title', 'antispam-bee' ),
425
+ ),
426
  );
427
  }
428
 
 
429
  /**
430
+ * Check and return an array key
431
+ *
432
+ * @since 2.4.2
433
+ * @change 2.4.2
434
+ *
435
+ * @param array $array Array with values.
436
+ * @param string $key Name of the key.
437
+ * @return mixed Value of the requested key.
438
+ */
439
+ public static function get_key( $array, $key ) {
440
+ if ( empty( $array ) || empty( $key ) || empty( $array[ $key ] ) ) {
 
 
441
  return null;
442
  }
443
 
444
+ return $array[ $key ];
445
  }
446
 
447
 
448
  /**
449
+ * Localization of the admin pages
450
+ *
451
+ * @since 0.1
452
+ * @change 2.4
453
+ *
454
+ * @param string $page Mark the page.
455
+ * @return boolean True on success.
456
+ */
457
+ private static function _current_page( $page ) {
458
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
459
+ switch ( $page ) {
 
460
  case 'dashboard':
461
+ return ( empty( $GLOBALS['pagenow'] ) || ( ! empty( $GLOBALS['pagenow'] ) && 'index.php' === $GLOBALS['pagenow'] ) );
462
 
463
  case 'options':
464
+ return ( ! empty( $_GET['page'] ) && 'antispam_bee' === $_GET['page'] );
465
 
466
  case 'plugins':
467
+ return ( ! empty( $GLOBALS['pagenow'] ) && 'plugins.php' === $GLOBALS['pagenow'] );
468
 
469
  case 'admin-post':
470
+ return ( ! empty( $GLOBALS['pagenow'] ) && 'admin-post.php' === $GLOBALS['pagenow'] );
471
 
472
  case 'edit-comments':
473
+ return ( ! empty( $GLOBALS['pagenow'] ) && 'edit-comments.php' === $GLOBALS['pagenow'] );
474
 
475
  default:
476
  return false;
477
  }
478
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
479
  }
480
 
481
 
482
  /**
483
+ * Integration of the localization file
484
+ *
485
+ * @since 0.1
486
+ * @change 2.4
487
+ */
488
+ public static function load_plugin_lang() {
 
 
489
  load_plugin_textdomain(
490
  'antispam-bee'
491
  );
493
 
494
 
495
  /**
496
+ * Add the link to the settings
497
+ *
498
+ * @since 1.1
499
+ * @change 1.1
500
+ *
501
+ * @param array $data The action link array.
502
+ * @return array $data The action link array.
503
+ */
504
+ public static function init_action_links( $data ) {
505
+ if ( ! current_user_can( 'manage_options' ) ) {
506
  return $data;
507
  }
508
 
513
  '<a href="%s">%s</a>',
514
  add_query_arg(
515
  array(
516
+ 'page' => 'antispam_bee',
517
  ),
518
+ admin_url( 'options-general.php' )
519
  ),
520
+ esc_attr__( 'Settings', 'antispam-bee' )
521
+ ),
522
  )
523
  );
524
  }
525
 
 
526
  /**
527
+ * Meta links of the plugin
528
+ *
529
+ * @since 0.1
530
+ * @change 2.6.2
531
+ *
532
+ * @param array $input Existing links.
533
+ * @param string $file Current page.
534
+ * @return array $data Modified links.
535
+ */
536
+ public static function init_row_meta( $input, $file ) {
537
+ if ( $file !== self::$_base ) {
 
 
 
538
  return $input;
539
  }
540
 
547
  );
548
  }
549
 
550
+ /*
551
+ * ############################
552
+ * ####### RESOURCES ########
553
+ * ############################
 
 
 
 
 
 
 
 
 
554
  */
555
 
556
+ /**
557
+ * Registration of resources (CSS & JS)
558
+ *
559
+ * @since 1.6
560
+ * @change 2.4.5
561
+ */
562
+ public static function init_plugin_sources() {
563
+ $plugin = get_plugin_data( __FILE__ );
564
 
 
565
  wp_register_script(
566
  'ab_script',
567
+ plugins_url( 'js/scripts.min.js', __FILE__ ),
568
+ array( 'jquery' ),
569
  $plugin['Version']
570
  );
571
 
 
572
  wp_register_style(
573
  'ab_style',
574
+ plugins_url( 'css/styles.min.css', __FILE__ ),
575
  array( 'dashicons' ),
576
  $plugin['Version']
577
  );
579
 
580
 
581
  /**
582
+ * Initialization of the option page
583
+ *
584
+ * @since 0.1
585
+ * @change 2.4.3
586
+ */
587
+ public static function add_sidebar_menu() {
 
 
 
588
  $page = add_options_page(
589
  'Antispam Bee',
590
  'Antispam Bee',
592
  'antispam_bee',
593
  array(
594
  'Antispam_Bee_GUI',
595
+ 'options_page',
596
  )
597
  );
598
 
 
599
  add_action(
600
  'admin_print_scripts-' . $page,
601
  array(
602
  __CLASS__,
603
+ 'add_options_script',
604
  )
605
  );
606
 
 
607
  add_action(
608
  'admin_print_styles-' . $page,
609
  array(
610
  __CLASS__,
611
+ 'add_options_style',
612
  )
613
  );
614
 
 
615
  add_action(
616
+ 'load-' . $page,
617
  array(
618
  __CLASS__,
619
+ 'init_options_page',
620
  )
621
  );
622
  }
623
 
624
 
625
  /**
626
+ * Initialization of JavaScript
627
+ *
628
+ * @since 1.6
629
+ * @change 2.4
630
+ */
631
+ public static function add_options_script() {
632
+ wp_enqueue_script( 'ab_script' );
 
 
633
  }
634
 
635
 
636
  /**
637
+ * Initialization of Stylesheets
638
+ *
639
+ * @since 1.6
640
+ * @change 2.4
641
+ */
642
+ public static function add_options_style() {
643
+ wp_enqueue_style( 'ab_style' );
 
 
644
  }
645
 
646
 
647
  /**
648
+ * Integration of the GUI
649
+ *
650
+ * @since 2.4
651
+ * @change 2.4
652
+ */
653
+ public static function init_options_page() {
654
+ require_once dirname( __FILE__ ) . '/inc/gui.class.php';
 
 
655
  }
656
 
657
 
658
 
659
+ /*
660
+ * ############################
661
+ * ####### DASHBOARD ########
662
+ * ############################
 
 
 
 
 
 
 
 
 
 
663
  */
664
 
665
+ /**
666
+ * Display the spam counter on the dashboard
667
+ *
668
+ * @since 0.1
669
+ * @change 2.6.5
670
+ *
671
+ * @param array $items Initial array with dashboard items.
672
+ * @return array $items Merged array with dashboard items.
673
+ */
674
+ public static function add_dashboard_count( $items = array() ) {
675
+ if ( ! current_user_can( 'manage_options' ) || ! self::get_option( 'dashboard_count' ) ) {
676
+ return $items;
677
  }
678
 
679
+ echo '<style>#dashboard_right_now .ab-count:before {content: "\f117"}</style>';
 
680
 
681
+ $items[] = '<span class="ab-count">' . esc_html(
682
+ sprintf(
683
+ // translators: The number of spam comments Antispam Bee blocked so far.
684
+ __( '%d Blocked', 'antispam-bee' ),
685
+ self::_get_spam_count()
686
+ )
687
  ) . '</span>';
688
 
689
+ return $items;
690
  }
691
 
 
692
  /**
693
+ * Initialize the dashboard chart
694
+ *
695
+ * @since 1.9
696
+ * @change 2.5.6
697
+ */
698
+ public static function add_dashboard_chart() {
 
 
699
  if ( ! current_user_can( 'publish_posts' ) || ! self::get_option( 'dashboard_chart' ) ) {
700
  return;
701
  }
702
 
 
703
  wp_add_dashboard_widget(
704
  'ab_widget',
705
  'Antispam Bee',
706
  array(
707
  __CLASS__,
708
+ 'show_spam_chart',
709
  )
710
  );
711
 
 
712
  add_action(
713
  'admin_head',
714
  array(
715
  __CLASS__,
716
+ 'add_dashboard_style',
717
  )
718
  );
719
  }
720
 
 
721
  /**
722
+ * Print dashboard styles
723
+ *
724
+ * @since 1.9.0
725
+ * @change 2.5.8
726
+ */
727
+ public static function add_dashboard_style() {
728
+ $plugin = get_plugin_data( __FILE__ );
 
 
 
729
 
 
730
  wp_register_style(
731
  'ab_chart',
732
+ plugins_url( 'css/dashboard.min.css', __FILE__ ),
733
  array(),
734
  $plugin['Version']
735
  );
736
 
737
+ wp_print_styles( 'ab_chart' );
 
738
  }
739
 
740
 
741
  /**
742
+ * Print dashboard scripts
743
+ *
744
+ * @since 1.9.0
745
+ * @change 2.5.8
746
+ */
747
  public static function add_dashboard_script() {
748
+ if ( ! self::get_option( 'daily_stats' ) ) {
 
749
  return;
750
  }
751
 
752
+ $plugin = get_plugin_data( __FILE__ );
 
753
 
 
754
  wp_enqueue_script(
755
  'raphael',
756
  plugins_url( 'js/raphael.min.js', __FILE__ ),
776
  );
777
  }
778
 
 
779
  /**
780
+ * Print dashboard html
781
+ *
782
+ * @since 1.9.0
783
+ * @change 2.5.8
784
+ */
785
+ public static function show_spam_chart() {
786
+ $items = (array) self::get_option( 'daily_stats' );
 
 
 
787
 
788
+ if ( empty( $items ) ) {
 
789
  echo sprintf(
790
  '<div id="ab_chart"><p>%s</p></div>',
791
+ esc_html__( 'No data available.', 'antispam-bee' )
792
  );
793
 
794
  return;
795
  }
796
 
 
797
  self::add_dashboard_script();
798
 
799
+ ksort( $items, SORT_NUMERIC );
 
800
 
 
801
  $html = "<table id=ab_chart_data>\n";
802
 
 
 
803
  $html .= "<tfoot><tr>\n";
804
+ foreach ( $items as $date => $count ) {
805
+ $html .= '<th>' . $date . "</th>\n";
806
  }
807
  $html .= "</tr></tfoot>\n";
808
 
 
809
  $html .= "<tbody><tr>\n";
810
+ foreach ( $items as $date => $count ) {
811
+ $html .= '<td>' . (int) $count . "</td>\n";
812
  }
813
  $html .= "</tr></tbody>\n";
814
 
 
 
815
  $html .= "</table>\n";
816
 
817
+ echo wp_kses_post( '<div id="ab_chart">' . $html . '</div>' );
 
818
  }
819
 
820
+ /*
821
+ * ############################
822
+ * ######## OPTIONS #########
823
+ * ############################
 
 
 
 
 
 
 
 
 
 
 
824
  */
825
 
826
+ /**
827
+ * Get all plugin options
828
+ *
829
+ * @since 2.4
830
+ * @change 2.6.1
831
+ *
832
+ * @return array $options Array with option fields.
833
+ */
834
+ public static function get_options() {
835
+ $options = wp_cache_get( 'antispam_bee' );
836
+ if ( ! $options ) {
837
  wp_cache_set(
838
  'antispam_bee',
839
+ $options = get_option( 'antispam_bee' )
840
  );
841
  }
842
 
846
  );
847
  }
848
 
 
849
  /**
850
+ * Get single option field
851
+ *
852
+ * @since 0.1
853
+ * @change 2.4.2
854
+ *
855
+ * @param string $field Field name.
856
+ * @return mixed Field value.
857
+ */
858
+ public static function get_option( $field ) {
 
 
 
859
  $options = self::get_options();
860
 
861
+ return self::get_key( $options, $field );
862
  }
863
 
864
 
865
  /**
866
+ * Update single option field
867
+ *
868
+ * @since 0.1
869
+ * @change 2.4
870
+ *
871
+ * @param string $field Field name.
872
+ * @param mixed $value The Field value.
873
+ */
874
+ private static function _update_option( $field, $value ) {
 
 
875
  self::update_options(
876
  array(
877
+ $field => $value,
878
  )
879
  );
880
  }
881
 
882
 
883
  /**
884
+ * Update multiple option fields
885
+ *
886
+ * @since 0.1
887
+ * @change 2.6.1
888
+ *
889
+ * @param array $data Array with plugin option fields.
890
+ */
891
+ public static function update_options( $data ) {
892
+ $options = get_option( 'antispam_bee' );
 
 
 
893
 
894
+ if ( is_array( $options ) ) {
 
895
  $options = array_merge(
896
  $options,
897
  $data
898
  );
899
+ } else {
 
900
  $options = $data;
901
  }
902
 
 
903
  update_option(
904
  'antispam_bee',
905
  $options
906
  );
907
 
 
908
  wp_cache_set(
909
  'antispam_bee',
910
  $options
913
 
914
 
915
 
916
+ /*
917
+ * ############################
918
+ * ######## CRONJOBS ########
919
+ * ############################
 
 
 
 
 
 
 
920
  */
921
 
922
+ /**
923
+ * Execution of the daily cronjobs
924
+ *
925
+ * @since 0.1
926
+ * @change 2.4
927
+ */
928
+ public static function start_daily_cronjob() {
929
+ if ( ! self::get_option( 'cronjob_enable' ) ) {
930
  return;
931
  }
932
 
 
933
  self::_update_option(
934
  'cronjob_timestamp',
935
  time()
936
  );
937
 
 
938
  self::_delete_old_spam();
939
  }
940
 
941
 
942
  /**
943
+ * Delete old spam comments
944
+ *
945
+ * @since 0.1
946
+ * @change 2.4
947
+ */
948
+ private static function _delete_old_spam() {
949
+ $days = (int) self::get_option( 'cronjob_interval' );
 
 
 
950
 
951
+ if ( empty( $days ) ) {
 
952
  return false;
953
  }
954
 
 
955
  global $wpdb;
956
 
 
957
  $wpdb->query(
958
  $wpdb->prepare(
959
  "DELETE FROM `$wpdb->comments` WHERE `comment_approved` = 'spam' AND SUBDATE(NOW(), %d) > comment_date_gmt",
961
  )
962
  );
963
 
964
+ $wpdb->query( "OPTIMIZE TABLE `$wpdb->comments`" );
 
965
  }
966
 
967
 
968
  /**
969
+ * Initialization of the cronjobs
970
+ *
971
+ * @since 0.1
972
+ * @change 2.4
973
+ */
974
+ public static function init_scheduled_hook() {
975
+ if ( ! wp_next_scheduled( 'antispam_bee_daily_cronjob' ) ) {
 
 
976
  wp_schedule_event(
977
  time(),
978
  'daily',
983
 
984
 
985
  /**
986
+ * Deletion of the cronjobs
987
+ *
988
+ * @since 0.1
989
+ * @change 2.4
990
+ */
991
+ public static function clear_scheduled_hook() {
992
+ if ( wp_next_scheduled( 'antispam_bee_daily_cronjob' ) ) {
993
+ wp_clear_scheduled_hook( 'antispam_bee_daily_cronjob' );
 
 
994
  }
995
  }
996
 
997
 
998
 
999
+ /*
1000
+ * ############################
1001
+ * ###### SPAM CHECK ########
1002
+ * ############################
 
 
 
 
 
 
 
1003
  */
1004
 
1005
+ /**
1006
+ * Check POST values
1007
+ *
1008
+ * @since 0.1
1009
+ * @change 2.6.3
1010
+ */
1011
+ public static function precheck_incoming_request() {
1012
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
1013
+ if ( is_feed() || is_trackback() || empty( $_POST ) || self::_is_mobile() ) {
1014
  return;
1015
  }
1016
 
1017
+ $request_uri = self::get_key( $_SERVER, 'REQUEST_URI' );
1018
+ $request_path = self::parse_url( $request_uri, 'path' );
 
1019
 
1020
+ if ( strpos( $request_path, 'wp-comments-post.php' ) === false ) {
 
1021
  return;
1022
  }
1023
 
1024
+ $post_id = (int) self::get_key( $_POST, 'comment_post_ID' );
 
1025
  $hidden_field = self::get_key( $_POST, 'comment' );
1026
  $plugin_field = self::get_key( $_POST, self::get_secret_name_for_post( $post_id ) );
1027
 
1028
+ if ( empty( $hidden_field ) && ! empty( $plugin_field ) ) {
 
1029
  $_POST['comment'] = $plugin_field;
1030
  unset( $_POST[ self::get_secret_name_for_post( $post_id ) ] );
1031
  } else {
1032
  $_POST['ab_spam__hidden_field'] = 1;
1033
  }
1034
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
1035
  }
1036
 
1037
 
1038
  /**
1039
+ * Check incoming requests for spam
1040
+ *
1041
+ * @since 0.1
1042
+ * @change 2.6.3
1043
+ *
1044
+ * @param array $comment Untreated comment.
1045
+ * @return array $comment Treated comment.
1046
+ */
1047
+ public static function handle_incoming_request( $comment ) {
 
 
 
1048
  $comment['comment_author_IP'] = self::get_client_ip();
1049
 
1050
+ $request_uri = self::get_key( $_SERVER, 'REQUEST_URI' );
1051
+ $request_path = self::parse_url( $request_uri, 'path' );
 
 
 
 
 
 
 
 
 
 
 
1052
 
1053
+ if ( empty( $request_path ) ) {
 
1054
  return self::_handle_spam_request(
1055
  $comment,
1056
  'empty'
1057
  );
1058
  }
1059
 
 
1060
  $ping = array(
1061
+ 'types' => array( 'pingback', 'trackback', 'pings' ),
1062
+ 'allowed' => ! self::get_option( 'ignore_pings' ),
1063
  );
1064
 
1065
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
1066
+ // Everybody can post.
1067
+ if ( strpos( $request_path, 'wp-comments-post.php' ) !== false && ! empty( $_POST ) ) {
1068
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
1069
+ $status = self::_verify_comment_request( $comment );
1070
 
1071
+ if ( ! empty( $status['reason'] ) ) {
 
1072
  return self::_handle_spam_request(
1073
  $comment,
1074
  $status['reason']
1075
  );
1076
  }
1077
+ } elseif ( in_array( self::get_key( $comment, 'comment_type' ), $ping['types'], true ) && $ping['allowed'] ) {
1078
+ $status = self::_verify_trackback_request( $comment );
1079
 
1080
+ if ( ! empty( $status['reason'] ) ) {
 
 
 
 
 
 
1081
  return self::_handle_spam_request(
1082
  $comment,
1083
  $status['reason'],
1091
 
1092
 
1093
  /**
1094
+ * Prepares the replacement of the comment field
1095
+ *
1096
+ * @since 0.1
1097
+ * @change 2.4
1098
+ */
1099
+ public static function prepare_comment_field() {
1100
+ if ( is_feed() || is_trackback() || is_robots() || self::_is_mobile() ) {
 
 
 
1101
  return;
1102
  }
1103
 
1104
+ if ( ! is_singular() && ! self::get_option( 'always_allowed' ) ) {
 
1105
  return;
1106
  }
1107
 
 
1108
  ob_start(
1109
  array(
1110
  'Antispam_Bee',
1111
+ 'replace_comment_field',
1112
  )
1113
  );
1114
  }
1115
 
1116
 
1117
  /**
1118
+ * Replaces the comment field
1119
+ *
1120
+ * @since 2.4
1121
+ * @change 2.6.4
1122
+ *
1123
+ * @param string $data HTML code of the website.
1124
+ * @return string Treated HTML code.
1125
+ */
1126
+ public static function replace_comment_field( $data ) {
1127
+ if ( empty( $data ) ) {
 
 
 
1128
  return;
1129
  }
1130
 
1131
+ if ( ! preg_match( '#<textarea.+?name=["\']comment["\']#s', $data ) ) {
 
1132
  return $data;
1133
  }
1134
 
 
1135
  return preg_replace_callback(
1136
  '/(?P<all> (?# match the whole textarea tag )
1137
  <textarea (?# the opening of the textarea and some optional attributes )
1155
  )/x',
1156
  array( 'Antispam_Bee', 'replace_comment_field_callback' ),
1157
  $data,
1158
+ -1
1159
  );
1160
  }
1161
 
1169
  * @return string The modified content string.
1170
  */
1171
  public static function replace_comment_field_callback( $matches ) {
1172
+ if ( self::get_option( 'time_check' ) ) {
 
1173
  $init_time_field = sprintf(
1174
  '<input type="hidden" name="ab_init_time" value="%d" />',
1175
  time()
1182
 
1183
  $id_script = '';
1184
  if ( ! empty( $matches['id1'] ) || ! empty( $matches['id2'] ) ) {
1185
+ $output .= 'id="' . self::get_secret_id_for_post( self::$_current_post_id ) . '" ';
1186
  $id_script = '<script type="text/javascript">document.getElementById("comment").setAttribute( "id", "' . esc_js( md5( time() ) ) . '" );document.getElementById("' . esc_js( self::get_secret_id_for_post( self::$_current_post_id ) ) . '").setAttribute( "id", "comment" );</script>';
1187
  }
1188
 
1199
 
1200
 
1201
  /**
1202
+ * Check the trackbacks
1203
+ *
1204
+ * @since 2.4
1205
+ * @change 2.7.0
1206
+ *
1207
+ * @param array $comment Trackback data.
1208
+ * @return array Array with suspected reason.
1209
+ */
1210
+ private static function _verify_trackback_request( $comment ) {
1211
+ $ip = self::get_key( $comment, 'comment_author_IP' );
1212
+ $url = self::get_key( $comment, 'comment_author_url' );
1213
+ $body = self::get_key( $comment, 'comment_content' );
1214
+ $post_id = self::get_key( $comment, 'comment_post_ID' );
1215
+ $type = self::get_key( $comment, 'comment_type' );
1216
+ $blog_name = self::get_key( $comment, 'comment_author' );
1217
+
1218
+ if ( empty( $url ) || empty( $body ) ) {
1219
+ return array(
1220
+ 'reason' => 'empty',
1221
+ );
1222
+ }
1223
 
1224
+ if ( empty( $ip ) ) {
 
1225
  return array(
1226
+ 'reason' => 'empty',
1227
  );
1228
  }
1229
 
1230
+ if ( 'pingback' === $type && self::_pingback_from_myself( $url, $post_id ) ) {
1231
+ return;
1232
+ }
1233
+
1234
+ if ( self::is_trackback_post_title_blog_name_spam( $body, $blog_name ) ) {
1235
  return array(
1236
+ 'reason' => 'title_is_name',
1237
  );
1238
  }
1239
 
 
1240
  $options = self::get_options();
1241
 
1242
+ if ( $options['bbcode_check'] && self::_is_bbcode_spam( $body ) ) {
 
1243
  return array(
1244
+ 'reason' => 'bbcode',
1245
  );
1246
  }
1247
 
1248
+ if ( $options['advanced_check'] && self::_is_fake_ip( $ip, self::parse_url( $url, 'host' ) ) ) {
 
1249
  return array(
1250
+ 'reason' => 'server',
1251
  );
1252
  }
1253
 
1254
+ if ( $options['spam_ip'] && self::_is_db_spam( $ip, $url ) ) {
 
1255
  return array(
1256
+ 'reason' => 'localdb',
1257
  );
1258
  }
1259
 
1260
+ if ( $options['country_code'] && self::_is_country_spam( $ip ) ) {
 
1261
  return array(
1262
+ 'reason' => 'country',
1263
  );
1264
  }
1265
 
1266
+ if ( $options['translate_api'] && self::_is_lang_spam( $body ) ) {
 
1267
  return array(
1268
+ 'reason' => 'lang',
1269
  );
1270
  }
 
1271
 
1272
+ if ( $options['regexp_check'] && self::_is_regexp_spam(
1273
+ array(
1274
+ 'ip' => $ip,
1275
+ 'rawurl' => $url,
1276
+ 'host' => self::parse_url( $url, 'host' ),
1277
+ 'body' => $body,
1278
+ 'email' => '',
1279
+ 'author' => '',
1280
+ )
1281
+ ) ) {
1282
+ return array(
1283
+ 'reason' => 'regexp',
1284
+ );
1285
+ }
1286
+ }
1287
 
1288
  /**
1289
+ * Check, if I pinged myself.
1290
+ *
1291
+ * @since 2.8.2
1292
+ *
1293
+ * @param string $url The URL from where the ping came.
1294
+ * @param int $target_post_id The post ID which has been pinged.
1295
+ *
1296
+ * @return bool
1297
+ */
1298
+ private static function _pingback_from_myself( $url, $target_post_id ) {
1299
+
1300
+ if ( 0 !== strpos( $url, home_url() ) ) {
1301
+ return false;
1302
+ }
1303
+
1304
+ $original_post_id = (int) url_to_postid( $url );
1305
+ if ( ! $original_post_id ) {
1306
+ return false;
1307
+ }
1308
+
1309
+ $post = get_post( $original_post_id );
1310
+ if ( ! $post ) {
1311
+ return false;
1312
+ }
1313
 
1314
+ $urls = wp_extract_urls( $post->post_content );
1315
+ $url_to_find = get_permalink( $target_post_id );
1316
+ if ( ! $url_to_find ) {
1317
+ return false;
1318
+ }
1319
+ foreach ( $urls as $url ) {
1320
+ if ( strpos( $url, $url_to_find ) === 0 ) {
1321
+ return true;
1322
+ }
1323
+ }
1324
+ return false;
1325
+ }
1326
+
1327
+ /**
1328
+ * Check the comment
1329
+ *
1330
+ * @since 2.4
1331
+ * @change 2.7.0
1332
+ *
1333
+ * @param array $comment Data of the comment.
1334
+ * @return array|void Array with suspected reason
1335
+ */
1336
+ private static function _verify_comment_request( $comment ) {
1337
+ $ip = self::get_key( $comment, 'comment_author_IP' );
1338
+ $url = self::get_key( $comment, 'comment_author_url' );
1339
+ $body = self::get_key( $comment, 'comment_content' );
1340
+ $email = self::get_key( $comment, 'comment_author_email' );
1341
+ $author = self::get_key( $comment, 'comment_author' );
1342
+
1343
+ if ( empty( $body ) ) {
1344
  return array(
1345
+ 'reason' => 'empty',
1346
  );
1347
  }
1348
 
1349
+ if ( empty( $ip ) ) {
 
1350
  return array(
1351
+ 'reason' => 'empty',
1352
  );
1353
  }
1354
 
1355
+ if ( get_option( 'require_name_email' ) && ( empty( $email ) || empty( $author ) ) ) {
 
1356
  return array(
1357
+ 'reason' => 'empty',
1358
  );
1359
  }
1360
 
 
1361
  $options = self::get_options();
1362
 
1363
+ if ( $options['already_commented'] && ! empty( $email ) && self::_is_approved_email( $email ) ) {
 
1364
  return;
1365
  }
1366
 
1367
+ if ( $options['gravatar_check'] && ! empty( $email ) && 1 === (int) get_option( 'show_avatars', 0 ) && self::_has_valid_gravatar( $email ) ) {
1368
+ return;
 
1369
  }
1370
 
1371
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
1372
+ if ( ! empty( $_POST['ab_spam__hidden_field'] ) ) {
1373
  return array(
1374
+ 'reason' => 'css',
1375
  );
1376
  }
1377
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
1378
 
 
1379
  if ( $options['time_check'] && self::_is_shortest_time() ) {
1380
  return array(
1381
+ 'reason' => 'time',
1382
  );
1383
  }
1384
 
1385
+ if ( $options['bbcode_check'] && self::_is_bbcode_spam( $body ) ) {
 
1386
  return array(
1387
+ 'reason' => 'bbcode',
1388
  );
1389
  }
1390
 
1391
+ if ( $options['advanced_check'] && self::_is_fake_ip( $ip ) ) {
 
1392
  return array(
1393
+ 'reason' => 'server',
1394
  );
1395
  }
1396
 
 
1397
  if ( $options['regexp_check'] && self::_is_regexp_spam(
1398
  array(
1399
+ 'ip' => $ip,
1400
  'rawurl' => $url,
1401
+ 'host' => self::parse_url( $url, 'host' ),
1402
+ 'body' => $body,
1403
+ 'email' => $email,
1404
+ 'author' => $author,
1405
  )
1406
  ) ) {
1407
  return array(
1408
+ 'reason' => 'regexp',
1409
  );
1410
  }
1411
 
1412
+ if ( $options['spam_ip'] && self::_is_db_spam( $ip, $url, $email ) ) {
 
1413
  return array(
1414
+ 'reason' => 'localdb',
1415
  );
1416
  }
1417
 
1418
+ if ( $options['country_code'] && self::_is_country_spam( $ip ) ) {
 
1419
  return array(
1420
+ 'reason' => 'country',
1421
  );
1422
  }
1423
 
1424
+ if ( $options['translate_api'] && self::_is_lang_spam( $body ) ) {
 
1425
  return array(
1426
+ 'reason' => 'lang',
1427
  );
1428
  }
1429
  }
1430
 
1431
 
1432
  /**
1433
+ * Check for a Gravatar image
1434
+ *
1435
+ * @since 2.6.5
1436
+ * @change 2.6.5
1437
+ *
1438
+ * @param string $email Input email.
1439
+ * @return boolean Check status (true = Gravatar available).
1440
+ */
1441
+ private static function _has_valid_gravatar( $email ) {
1442
+ $response = wp_safe_remote_get(
1443
+ sprintf(
1444
+ 'https://www.gravatar.com/avatar/%s?d=404',
1445
+ md5( strtolower( trim( $email ) ) )
1446
+ )
1447
+ );
 
1448
 
1449
+ if ( is_wp_error( $response ) ) {
1450
+ return null;
1451
+ }
1452
 
1453
+ if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
1454
+ return true;
1455
+ }
1456
 
1457
+ return false;
1458
+ }
1459
 
1460
 
1461
  /**
1462
+ * Check for comment action time
1463
+ *
1464
+ * @since 2.6.4
1465
+ * @change 2.6.4
1466
+ *
1467
+ * @return boolean TRUE if the action time is less than 5 seconds
1468
+ */
1469
+ private static function _is_shortest_time() {
1470
+ // phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification
1471
+ // Everybody can Post.
1472
+ $init_time = (int) self::get_key( $_POST, 'ab_init_time' );
1473
+ // phpcs:enable WordPress.CSRF.NonceVerification.NoNonceVerification
1474
+ if ( 0 === $init_time ) {
1475
  return false;
1476
  }
1477
 
1478
+ if ( time() - $init_time < apply_filters( 'ab_action_time_limit', 5 ) ) {
 
1479
  return true;
1480
  }
1481
 
1482
  return false;
1483
  }
1484
 
 
1485
  /**
1486
+ * Check if the blog name and the title of the blog post from which the trackback originates are equal.
1487
+ *
1488
+ * @since 2.6.4
1489
+ *
1490
+ * @param string $body The comment body.
1491
+ * @param string $blog_name The name of the blog.
1492
+ *
1493
+ * @return bool
1494
+ */
1495
+ private static function is_trackback_post_title_blog_name_spam( $body, $blog_name ) {
1496
+ preg_match( '/<strong>(.*)<\/strong>\\n\\n/', $body, $matches );
1497
+ if ( ! isset( $matches[1] ) ) {
1498
+ return false;
1499
+ }
1500
+ return trim( $matches[1] ) === trim( $blog_name );
1501
+ }
1502
 
1503
+
1504
+ /**
1505
+ * Usage of regexp, also custom
1506
+ *
1507
+ * @since 2.5.2
1508
+ * @change 2.5.6
1509
+ *
1510
+ * @param array $comment Array with commentary data.
1511
+ * @return boolean True for suspicious comment.
1512
+ */
1513
+ private static function _is_regexp_spam( $comment ) {
1514
  $fields = array(
1515
  'ip',
1516
  'host',
1519
  'author',
1520
  );
1521
 
 
1522
  $patterns = array(
1523
  array(
1524
+ 'host' => '^(www\.)?\d+\w+\.com$',
1525
+ 'body' => '^\w+\s\d+$',
1526
+ 'email' => '@gmail.com$',
1527
  ),
1528
  array(
1529
+ 'body' => '\<\!.+?mfunc.+?\>',
1530
  ),
1531
  array(
1532
  'author' => 'moncler|north face|vuitton|handbag|burberry|outlet|prada|cialis|viagra|maillot|oakley|ralph lauren|ray ban|iphone|プラダ',
1538
  'body' => 'target[t]?ed (visitors|traffic)|viagra|cialis',
1539
  ),
1540
  array(
1541
+ 'body' => 'purchase amazing|buy amazing',
1542
+ ),
1543
+ array(
1544
+ 'body' => 'dating|sex|lotto|pharmacy',
1545
+ 'email' => '@mail\.ru|@yandex\.',
1546
  ),
1547
  );
1548
 
1549
+ $quoted_author = preg_quote( $comment['author'], '/' );
1550
+ if ( $quoted_author ) {
1551
  $patterns[] = array(
1552
  'body' => sprintf(
1553
  '<a.+?>%s<\/a>$',
1561
  ),
1562
  );
1563
  $patterns[] = array(
1564
+ 'email' => '@gmail.com$',
1565
  'author' => '^[a-z0-9-\.]+\.[a-z]{2,6}$',
1566
+ 'host' => sprintf(
1567
  '^%s$',
1568
  $quoted_author
1569
  ),
1570
  );
1571
  }
1572
 
 
1573
  $patterns = apply_filters(
1574
  'antispam_bee_patterns',
1575
  $patterns
1576
  );
1577
 
 
1578
  if ( ! $patterns ) {
1579
  return false;
1580
  }
1581
 
1582
+ foreach ( $patterns as $pattern ) {
 
1583
  $hits = array();
1584
 
1585
+ foreach ( $pattern as $field => $regexp ) {
1586
+ if ( empty( $field ) || ! in_array( $field, $fields, true ) || empty( $regexp ) ) {
 
 
1587
  continue;
1588
  }
1589
 
1590
+ $comment[ $field ] = ( function_exists( 'iconv' ) ? iconv( 'utf-8', 'utf-8//TRANSLIT', $comment[ $field ] ) : $comment[ $field ] );
 
1591
 
1592
+ if ( empty( $comment[ $field ] ) ) {
 
1593
  continue;
1594
  }
1595
 
1596
+ if ( preg_match( '/' . $regexp . '/isu', $comment[ $field ] ) ) {
1597
+ $hits[ $field ] = true;
 
1598
  }
1599
  }
1600
 
1601
+ if ( count( $hits ) === count( $pattern ) ) {
1602
  return true;
1603
  }
1604
  }
1608
 
1609
 
1610
  /**
1611
+ * Review a comment on its existence in the local spam
1612
+ *
1613
+ * @since 2.0.0
1614
+ * @change 2.5.4
1615
+ *
1616
+ * @param string $ip Comment IP.
1617
+ * @param string $url Comment URL (optional).
1618
+ * @param string $email Comment Email (optional).
1619
+ * @return boolean True for suspicious comment.
1620
+ */
1621
+ private static function _is_db_spam( $ip, $url = '', $email = '' ) {
 
 
 
1622
  global $wpdb;
1623
 
1624
+ // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
1625
+ $sql = '
1626
  select
1627
  meta_value as ip
1628
  from
1633
  AND meta.meta_key = "antispam_bee_iphash"
1634
  AND comments.comment_approved="spam"';
1635
  $hashed_ips = $wpdb->get_col( $sql );
1636
+ if ( ! empty( $hashed_ips ) ) {
1637
  foreach ( $hashed_ips as $hash ) {
1638
+ if ( wp_check_password( $ip, $hash ) ) {
1639
  return true;
1640
  }
1641
  }
1643
 
1644
  $params = array();
1645
  $filter = array();
1646
+ if ( ! empty( $url ) ) {
 
1647
  $filter[] = '`comment_author_url` = %s';
1648
+ $params[] = wp_unslash( $url );
1649
  }
1650
 
1651
+ if ( ! empty( $email ) ) {
 
1652
  $filter[] = '`comment_author_email` = %s';
1653
+ $params[] = wp_unslash( $email );
1654
  }
1655
+ if ( empty( $params ) ) {
 
1656
  return false;
1657
  }
1658
 
1659
+ // phpcs:disable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
1660
+ // ToDo: Have a closer look on this SQL Query.
1661
+ $filter_sql = implode( ' OR ', $filter );
1662
+
1663
  $result = $wpdb->get_var(
1664
  $wpdb->prepare(
1665
  sprintf(
1666
  "SELECT `comment_ID` FROM `$wpdb->comments` WHERE `comment_approved` = 'spam' AND (%s) LIMIT 1",
1667
+ $filter_sql
1668
  ),
1669
  $params
1670
  )
1671
  );
1672
+ // phpcs:enable WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
1673
+ // phpcs:enable WordPress.WP.PreparedSQL.NotPrepared
1674
 
1675
+ return ! empty( $result );
1676
  }
1677
 
1678
 
1679
  /**
1680
+ * Check for country spam by (anonymized) IP
1681
+ *
1682
+ * @since 2.6.9
1683
+ * @change 2.6.9
1684
+ *
1685
+ * @param string $ip IP address.
1686
+ * @return boolean True if the comment is spam based on country filter.
1687
+ */
1688
+ private static function _is_country_spam( $ip ) {
 
 
 
1689
  $options = self::get_options();
1690
 
 
1691
  $white = preg_split(
1692
  '/[\s,;]+/',
1693
  $options['country_white'],
1701
  PREG_SPLIT_NO_EMPTY
1702
  );
1703
 
1704
+ if ( empty( $white ) && empty( $black ) ) {
 
1705
  return false;
1706
  }
1707
 
 
1708
  $response = wp_safe_remote_head(
1709
  esc_url_raw(
1710
  sprintf(
1711
  'https://api.ip2country.info/ip?%s',
1712
+ self::_anonymize_ip( $ip )
1713
  ),
1714
  'https'
1715
  )
1716
  );
1717
 
1718
+ if ( is_wp_error( $response ) ) {
 
1719
  return false;
1720
  }
1721
 
1722
+ if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
 
1723
  return false;
1724
  }
1725
 
1726
+ $country = (string) wp_remote_retrieve_header( $response, 'x-country-code' );
 
1727
 
1728
+ if ( empty( $country ) || strlen( $country ) !== 2 ) {
 
1729
  return false;
1730
  }
1731
 
1732
+ if ( ! empty( $black ) ) {
1733
+ return ( in_array( $country, $black, true ) );
 
1734
  }
1735
 
1736
+ return ( ! in_array( $country, $white, true ) );
 
1737
  }
1738
 
1739
 
1740
  /**
1741
+ * Check for BBCode spam
1742
+ *
1743
+ * @since 2.5.1
1744
+ * @change 2.5.1
1745
+ *
1746
+ * @param string $body Content of a comment.
1747
+ * @return boolean True for BBCode in content
1748
+ */
1749
+ private static function _is_bbcode_spam( $body ) {
1750
+ return (bool) preg_match( '/\[url[=\]].*\[\/url\]/is', $body );
 
 
1751
  }
1752
 
1753
 
1754
  /**
1755
+ * Check for an already approved e-mail address
1756
+ *
1757
+ * @since 2.0
1758
+ * @change 2.5.1
1759
+ *
1760
+ * @param string $email E-mail address.
1761
+ * @return boolean True for a found entry.
1762
+ */
1763
+ private static function _is_approved_email( $email ) {
 
 
 
1764
  global $wpdb;
1765
 
 
1766
  $result = $wpdb->get_var(
1767
  $wpdb->prepare(
1768
  "SELECT `comment_ID` FROM `$wpdb->comments` WHERE `comment_approved` = '1' AND `comment_author_email` = %s LIMIT 1",
1769
+ wp_unslash( $email )
1770
  )
1771
  );
1772
 
 
1773
  if ( $result ) {
1774
  return true;
1775
  }
1779
 
1780
 
1781
  /**
1782
+ * Check for a fake IP
1783
+ *
1784
+ * @since 2.0
1785
+ * @change 2.6.2
1786
+ *
1787
+ * @param string $client_ip Client IP.
1788
+ * @param string $client_host Client Host (optional).
1789
+ * @return boolean True if fake IP.
1790
+ */
1791
+ private static function _is_fake_ip( $client_ip, $client_host = '' ) {
1792
+ $host_by_ip = gethostbyaddr( $client_ip );
 
 
 
1793
 
1794
+ if ( self::_is_ipv6( $client_ip ) ) {
1795
+ return $client_ip !== $host_by_ip;
 
1796
  }
1797
 
1798
+ if ( empty( $client_host ) ) {
1799
+ $ip_by_host = gethostbyname( $host_by_ip );
 
1800
 
1801
  if ( $ip_by_host === $host_by_ip ) {
1802
  return false;
1803
  }
 
 
1804
  } else {
1805
  if ( $host_by_ip === $client_ip ) {
1806
  return true;
1807
  }
1808
 
1809
+ $ip_by_host = gethostbyname( $client_host );
1810
  }
1811
 
1812
+ if ( strpos( $client_ip, self::_cut_ip( $ip_by_host ) ) === false ) {
1813
  return true;
1814
  }
1815
 
1821
  *
1822
  * @since 2.0
1823
  * @change 2.6.6
1824
+ * @change 2.8.2
1825
  *
1826
  * @param string $comment_content Content of the comment.
1827
  *
1828
+ * @return boolean TRUE if it is spam.
1829
  */
 
1830
  private static function _is_lang_spam( $comment_content ) {
 
1831
  $allowed_lang = (array) self::get_option( 'translate_lang' );
1832
 
 
1833
  $comment_text = wp_strip_all_tags( $comment_content );
1834
 
1835
+ if ( empty( $allowed_lang ) || empty( $comment_text ) ) {
 
 
 
 
 
 
 
 
1836
  return false;
1837
  }
1838
 
1839
  /**
1840
+ * Filters the detected language. With this filter, other detection methods can skip in and detect the language.
1841
  *
1842
+ * @since 2.8.2
1843
  *
1844
+ * @param null $detected_lang The detected language.
1845
+ * @param string $comment_text The text, to detect the language.
1846
  *
1847
+ * @return null|string The detected language or null.
1848
  */
1849
+ $detected_lang = apply_filters( 'antispam_bee_detected_lang', null, $comment_text );
1850
+ if ( null !== $detected_lang ) {
1851
+ return ! in_array( $detected_lang, $allowed_lang, true );
1852
+ }
 
 
1853
 
1854
+ if ( mb_strlen( $comment_text ) < 10 ) {
1855
+ return false;
1856
+ }
1857
+
1858
+ $response = wp_safe_remote_post(
1859
+ 'https://api.pluginkollektiv.org/language/v1/',
1860
+ array( 'body' => wp_json_encode( array( 'body' => $comment_text ) ) )
 
 
1861
  );
1862
 
 
1863
  if ( is_wp_error( $response )
1864
+ || wp_remote_retrieve_response_code( $response ) !== 200 ) {
1865
  return false;
1866
  }
1867
 
1868
+ $detected_lang = wp_remote_retrieve_body( $response );
1869
+ if ( ! $detected_lang ) {
1870
  return false;
1871
  }
1872
 
1873
+ $detected_lang = json_decode( $detected_lang );
1874
+ if ( ! $detected_lang || ! isset( $detected_lang->code ) ) {
1875
  return false;
1876
  }
1877
 
1878
+ return ! in_array( self::_map_lang_code( $detected_lang->code ), $allowed_lang, true );
1879
+ }
1880
+
1881
+ /**
1882
+ * Map franc language codes
1883
+ *
1884
+ * @since 2.9.0
1885
+ *
1886
+ * @param string $franc_code The franc code, received from the service.
1887
+ *
1888
+ * @return string Mapped ISO code
1889
+ */
1890
+ private static function _map_lang_code( $franc_code ) {
1891
+ $codes = array(
1892
+ 'zha' => 'za',
1893
+ 'zho' => 'zh',
1894
+ 'zul' => 'zu',
1895
+ 'yid' => 'yi',
1896
+ 'yor' => 'yo',
1897
+ 'xho' => 'xh',
1898
+ 'wln' => 'wa',
1899
+ 'wol' => 'wo',
1900
+ 'ven' => 've',
1901
+ 'vie' => 'vi',
1902
+ 'vol' => 'vo',
1903
+ 'uig' => 'ug',
1904
+ 'ukr' => 'uk',
1905
+ 'urd' => 'ur',
1906
+ 'uzb' => 'uz',
1907
+ 'tah' => 'ty',
1908
+ 'tam' => 'ta',
1909
+ 'tat' => 'tt',
1910
+ 'tel' => 'te',
1911
+ 'tgk' => 'tg',
1912
+ 'tgl' => 'tl',
1913
+ 'tha' => 'th',
1914
+ 'tir' => 'ti',
1915
+ 'ton' => 'to',
1916
+ 'tsn' => 'tn',
1917
+ 'tso' => 'ts',
1918
+ 'tuk' => 'tk',
1919
+ 'tur' => 'tr',
1920
+ 'twi' => 'tw',
1921
+ 'sag' => 'sg',
1922
+ 'san' => 'sa',
1923
+ 'sin' => 'si',
1924
+ 'slk' => 'sk',
1925
+ 'slv' => 'sl',
1926
+ 'sme' => 'se',
1927
+ 'smo' => 'sm',
1928
+ 'sna' => 'sn',
1929
+ 'snd' => 'sd',
1930
+ 'som' => 'so',
1931
+ 'sot' => 'st',
1932
+ 'spa' => 'es',
1933
+ 'sqi' => 'sq',
1934
+ 'srd' => 'sc',
1935
+ 'srp' => 'sr',
1936
+ 'ssw' => 'ss',
1937
+ 'sun' => 'su',
1938
+ 'swa' => 'sw',
1939
+ 'swe' => 'sv',
1940
+ 'roh' => 'rm',
1941
+ 'ron' => 'ro',
1942
+ 'run' => 'rn',
1943
+ 'rus' => 'ru',
1944
+ 'que' => 'qu',
1945
+ 'pan' => 'pa',
1946
+ 'pli' => 'pi',
1947
+ 'pol' => 'pl',
1948
+ 'por' => 'pt',
1949
+ 'pus' => 'ps',
1950
+ 'oci' => 'oc',
1951
+ 'oji' => 'oj',
1952
+ 'ori' => 'or',
1953
+ 'orm' => 'om',
1954
+ 'oss' => 'os',
1955
+ 'nau' => 'na',
1956
+ 'nav' => 'nv',
1957
+ 'nbl' => 'nr',
1958
+ 'nde' => 'nd',
1959
+ 'ndo' => 'ng',
1960
+ 'nep' => 'ne',
1961
+ 'nld' => 'nl',
1962
+ 'nno' => 'nn',
1963
+ 'nob' => 'nb',
1964
+ 'nor' => 'no',
1965
+ 'nya' => 'ny',
1966
+ 'mah' => 'mh',
1967
+ 'mal' => 'ml',
1968
+ 'mar' => 'mr',
1969
+ 'mkd' => 'mk',
1970
+ 'mlg' => 'mg',
1971
+ 'mlt' => 'mt',
1972
+ 'mon' => 'mn',
1973
+ 'mri' => 'mi',
1974
+ 'msa' => 'ms',
1975
+ 'mya' => 'my',
1976
+ 'lao' => 'lo',
1977
+ 'lat' => 'la',
1978
+ 'lav' => 'lv',
1979
+ 'lim' => 'li',
1980
+ 'lin' => 'ln',
1981
+ 'lit' => 'lt',
1982
+ 'ltz' => 'lb',
1983
+ 'lub' => 'lu',
1984
+ 'lug' => 'lg',
1985
+ 'kal' => 'kl',
1986
+ 'kan' => 'kn',
1987
+ 'kas' => 'ks',
1988
+ 'kat' => 'ka',
1989
+ 'kau' => 'kr',
1990
+ 'kaz' => 'kk',
1991
+ 'khm' => 'km',
1992
+ 'kik' => 'ki',
1993
+ 'kin' => 'rw',
1994
+ 'kir' => 'ky',
1995
+ 'kom' => 'kv',
1996
+ 'kon' => 'kg',
1997
+ 'kor' => 'ko',
1998
+ 'kua' => 'kj',
1999
+ 'kur' => 'ku',
2000
+ 'jav' => 'jv',
2001
+ 'jpn' => 'ja',
2002
+ 'ibo' => 'ig',
2003
+ 'ido' => 'io',
2004
+ 'iii' => 'ii',
2005
+ 'iku' => 'iu',
2006
+ 'ile' => 'ie',
2007
+ 'ina' => 'ia',
2008
+ 'ind' => 'id',
2009
+ 'ipk' => 'ik',
2010
+ 'isl' => 'is',
2011
+ 'ita' => 'it',
2012
+ 'hat' => 'ht',
2013
+ 'hau' => 'ha',
2014
+ 'hbs' => 'sh',
2015
+ 'heb' => 'he',
2016
+ 'her' => 'hz',
2017
+ 'hin' => 'hi',
2018
+ 'hmo' => 'ho',
2019
+ 'hrv' => 'hr',
2020
+ 'hun' => 'hu',
2021
+ 'hye' => 'hy',
2022
+ 'gla' => 'gd',
2023
+ 'gle' => 'ga',
2024
+ 'glg' => 'gl',
2025
+ 'glv' => 'gv',
2026
+ 'grn' => 'gn',
2027
+ 'guj' => 'gu',
2028
+ 'fao' => 'fo',
2029
+ 'fas' => 'fa',
2030
+ 'fij' => 'fj',
2031
+ 'fin' => 'fi',
2032
+ 'fra' => 'fr',
2033
+ 'fry' => 'fy',
2034
+ 'ful' => 'ff',
2035
+ 'ell' => 'el',
2036
+ 'eng' => 'en',
2037
+ 'epo' => 'eo',
2038
+ 'est' => 'et',
2039
+ 'eus' => 'eu',
2040
+ 'ewe' => 'ee',
2041
+ 'dan' => 'da',
2042
+ 'deu' => 'de',
2043
+ 'div' => 'dv',
2044
+ 'dzo' => 'dz',
2045
+ 'cat' => 'ca',
2046
+ 'ces' => 'cs',
2047
+ 'cha' => 'ch',
2048
+ 'che' => 'ce',
2049
+ 'chu' => 'cu',
2050
+ 'chv' => 'cv',
2051
+ 'cor' => 'kw',
2052
+ 'cos' => 'co',
2053
+ 'cre' => 'cr',
2054
+ 'cym' => 'cy',
2055
+ 'bak' => 'ba',
2056
+ 'bam' => 'bm',
2057
+ 'bel' => 'be',
2058
+ 'ben' => 'bn',
2059
+ 'bis' => 'bi',
2060
+ 'bod' => 'bo',
2061
+ 'bos' => 'bs',
2062
+ 'bre' => 'br',
2063
+ 'bul' => 'bg',
2064
+ 'aar' => 'aa',
2065
+ 'abk' => 'ab',
2066
+ 'afr' => 'af',
2067
+ 'aka' => 'ak',
2068
+ 'amh' => 'am',
2069
+ 'ara' => 'ar',
2070
+ 'arg' => 'an',
2071
+ 'asm' => 'as',
2072
+ 'ava' => 'av',
2073
+ 'ave' => 'ae',
2074
+ 'aym' => 'ay',
2075
+ 'aze' => 'az',
2076
+ );
2077
+
2078
+ if ( array_key_exists( $franc_code, $codes ) ) {
2079
+ return $codes[ $franc_code ];
2080
  }
2081
 
2082
+ return $franc_code;
2083
  }
2084
 
2085
  /**
2086
+ * Trim IP addresses
2087
+ *
2088
+ * @since 0.1
2089
+ * @change 2.5.1
2090
+ *
2091
+ * @param string $ip Original IP.
2092
+ * @param boolean $cut_end Shortening the end.
2093
+ * @return string Shortened IP.
2094
+ */
2095
+ private static function _cut_ip( $ip, $cut_end = true ) {
2096
+ $separator = ( self::_is_ipv4( $ip ) ? '.' : ':' );
 
 
2097
 
2098
  return str_replace(
2099
+ ( $cut_end ? strrchr( $ip, $separator ) : strstr( $ip, $separator ) ),
2100
  '',
2101
  $ip
2102
  );
2104
 
2105
 
2106
  /**
2107
+ * Anonymize the IP addresses
2108
+ *
2109
+ * @since 2.5.1
2110
+ * @change 2.5.1
2111
+ *
2112
+ * @param string $ip Original IP.
2113
+ * @return string Anonymous IP.
2114
+ */
2115
+ private static function _anonymize_ip( $ip ) {
2116
+ if ( self::_is_ipv4( $ip ) ) {
2117
+ return self::_cut_ip( $ip ) . '.0';
 
 
2118
  }
2119
 
2120
+ return self::_cut_ip( $ip, false ) . ':0:0:0:0:0:0:0';
2121
  }
2122
 
2123
 
2124
  /**
2125
+ * Rotates the IP address
2126
+ *
2127
+ * @since 2.4.5
2128
+ * @change 2.4.5
2129
+ *
2130
+ * @param string $ip IP address.
2131
+ * @return string Turned IP address.
2132
+ */
2133
+ private static function _reverse_ip( $ip ) {
 
 
2134
  return implode(
2135
  '.',
2136
  array_reverse(
2144
 
2145
 
2146
  /**
2147
+ * Check for an IPv4 address
2148
+ *
2149
+ * @since 2.4
2150
+ * @change 2.6.4
2151
+ *
2152
+ * @param string $ip IP to validate.
2153
+ * @return integer TRUE if IPv4.
2154
+ */
2155
+ private static function _is_ipv4( $ip ) {
2156
+ if ( function_exists( 'filter_var' ) ) {
 
 
2157
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) !== false;
2158
  } else {
2159
+ return preg_match( '/^\d{1,3}(\.\d{1,3}){3,3}$/', $ip );
2160
  }
2161
  }
2162
 
2163
 
2164
  /**
2165
+ * Check for an IPv6 address
2166
+ *
2167
+ * @since 2.6.2
2168
+ * @change 2.6.4
2169
+ *
2170
+ * @param string $ip IP to validate.
2171
+ * @return boolean TRUE if IPv6.
2172
+ */
2173
+ private static function _is_ipv6( $ip ) {
2174
+ if ( function_exists( 'filter_var' ) ) {
 
 
2175
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) !== false;
2176
  } else {
2177
+ return ! self::_is_ipv4( $ip );
2178
  }
2179
  }
2180
 
2181
 
2182
  /**
2183
+ * Testing on mobile devices
2184
+ *
2185
+ * @since 0.1
2186
+ * @change 2.4
2187
+ *
2188
+ * @return boolean TRUE if "wptouch" is active
2189
+ */
2190
+ private static function _is_mobile() {
2191
+ return strpos( get_template_directory(), 'wptouch' );
 
 
2192
  }
2193
 
2194
 
2195
 
2196
+ /*
2197
+ * ############################
2198
+ * ##### SPAM-TREATMENT #####
2199
+ * ############################
2200
+ */
2201
 
2202
  /**
2203
+ * Execution of the delete/marking process
2204
+ *
2205
+ * @since 0.1
2206
+ * @change 2.6.0
2207
+ *
2208
+ * @param array $comment Untreated commentary data.
2209
+ * @param string $reason Reason for suspicion.
2210
+ * @param boolean $is_ping Ping (optional).
2211
+ * @return array $comment Treated commentary data.
2212
+ */
2213
+ private static function _handle_spam_request( $comment, $reason, $is_ping = false ) {
2214
 
 
 
 
2215
  $options = self::get_options();
2216
 
2217
+ $spam_remove = ! $options['flag_spam'];
2218
+ $spam_notice = ! $options['no_notice'];
 
2219
 
2220
+ // Filter settings.
2221
  $ignore_filter = $options['ignore_filter'];
2222
+ $ignore_type = $options['ignore_type'];
2223
+ $ignore_reason = in_array( $reason, (array) $options['ignore_reasons'], true );
2224
 
2225
+ // Remember spam.
2226
+ self::_update_spam_log( $comment );
2227
  self::_update_spam_count();
2228
  self::_update_daily_stats();
2229
 
2230
+ // Delete spam.
2231
  if ( $spam_remove ) {
2232
  self::_go_in_peace();
2233
  }
2234
 
2235
+ if ( $ignore_filter && ( ( 1 === (int) $ignore_type && $is_ping ) || ( 2 === (int) $ignore_type && ! $is_ping ) ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
2236
  self::_go_in_peace();
2237
  }
2238
 
2239
+ // Spam reason.
2240
  if ( $ignore_reason ) {
2241
  self::_go_in_peace();
2242
  }
2243
  self::$_reason = $reason;
2244
 
2245
+ // Mark spam.
2246
  add_filter(
2247
  'pre_comment_approved',
2248
  array(
2249
  __CLASS__,
2250
+ 'return_spam',
2251
  )
2252
  );
2253
 
2254
+ // Send e-mail.
2255
  add_action(
2256
  'comment_post',
2257
  array(
2258
  __CLASS__,
2259
+ 'send_mail_notification',
2260
  )
2261
  );
2262
 
2263
+ // Spam reason as comment meta.
2264
  if ( $spam_notice ) {
2265
  add_action(
2266
  'comment_post',
2267
  array(
2268
  __CLASS__,
2269
+ 'add_spam_reason_to_comment',
2270
  )
2271
  );
2272
  }
2276
 
2277
 
2278
  /**
2279
+ * Logfile with detected spam
2280
+ *
2281
+ * @since 2.5.7
2282
+ * @change 2.6.1
2283
+ *
2284
+ * @param array $comment Array with commentary data.
2285
+ * @return mixed FALSE in case of error
2286
+ */
2287
+ private static function _update_spam_log( $comment ) {
2288
+ if ( ! defined( 'ANTISPAM_BEE_LOG_FILE' ) || ! ANTISPAM_BEE_LOG_FILE || ! is_writable( ANTISPAM_BEE_LOG_FILE ) || validate_file( ANTISPAM_BEE_LOG_FILE ) === 1 ) {
 
 
 
2289
  return false;
2290
  }
2291
 
 
2292
  $entry = sprintf(
2293
  '%s comment for post=%d from host=%s marked as spam%s',
2294
+ current_time( 'mysql' ),
2295
  $comment['comment_post_ID'],
2296
  $comment['comment_author_IP'],
2297
  PHP_EOL
2298
  );
2299
 
 
2300
  file_put_contents(
2301
  ANTISPAM_BEE_LOG_FILE,
2302
  $entry,
2306
 
2307
 
2308
  /**
2309
+ * Sends the 403 header and terminates the connection
2310
+ *
2311
+ * @since 2.5.6
2312
+ * @change 2.5.6
2313
+ */
2314
+ private static function _go_in_peace() {
2315
+ status_header( 403 );
2316
+ die( 'Spam deleted.' );
 
 
2317
  }
2318
 
2319
 
2320
  /**
2321
+ * Return real client IP
2322
+ *
2323
+ * @since 2.6.1
2324
+ * @change 2.6.1
2325
+ *
2326
+ * @return mixed $ip Client IP
2327
+ */
2328
+ public static function get_client_ip() {
2329
+ // phpcs:disable WordPress.VIP.ValidatedSanitizedInput.InputNotSanitized
2330
+ // Sanitization of $ip takes place further down.
2331
+ if ( isset( $_SERVER['HTTP_CLIENT_IP'] ) ) {
2332
+ $ip = wp_unslash( $_SERVER['HTTP_CLIENT_IP'] );
2333
+ } elseif ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
2334
+ $ip = wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] );
2335
+ } elseif ( isset( $_SERVER['HTTP_X_FORWARDED'] ) ) {
2336
+ $ip = wp_unslash( $_SERVER['HTTP_X_FORWARDED'] );
2337
+ } elseif ( isset( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
2338
+ $ip = wp_unslash( $_SERVER['HTTP_FORWARDED_FOR'] );
2339
+ } elseif ( isset( $_SERVER['HTTP_FORWARDED'] ) ) {
2340
+ $ip = wp_unslash( $_SERVER['HTTP_FORWARDED'] );
2341
+ } elseif ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
2342
+ $ip = wp_unslash( $_SERVER['REMOTE_ADDR'] );
2343
  } else {
2344
  return '';
2345
  }
2346
+ // phpcs:enable WordPress.VIP.ValidatedSanitizedInput.InputNotSanitized
2347
 
2348
+ if ( strpos( $ip, ',' ) !== false ) {
2349
+ $ips = explode( ',', $ip );
2350
+ $ip = trim( $ips[0] );
2351
  }
2352
 
2353
+ if ( function_exists( 'filter_var' ) ) {
2354
  return filter_var(
2355
  $ip,
2356
  FILTER_VALIDATE_IP
2364
  );
2365
  }
2366
 
 
2367
  /**
2368
+ * Add spam reason as comment data
2369
+ *
2370
+ * @since 2.6.0
2371
+ * @change 2.6.0
2372
+ *
2373
+ * @param integer $comment_id Comment ID.
2374
+ */
2375
+ public static function add_spam_reason_to_comment( $comment_id ) {
 
 
2376
  add_comment_meta(
2377
  $comment_id,
2378
  'antispam_bee_reason',
2380
  );
2381
  }
2382
 
2383
+ /**
2384
+ * Saves the IP address.
2385
+ *
2386
+ * @param int $comment_id The ID of the comment.
2387
+ */
2388
+ public static function save_ip_hash( $comment_id ) {
2389
+ $hashed_ip = self::hash_ip( self::get_client_ip() );
2390
  add_comment_meta(
2391
  $comment_id,
2392
  'antispam_bee_iphash',
2394
  );
2395
  }
2396
 
2397
+ /**
2398
+ * Hashes an IP address
2399
+ *
2400
+ * @param string $ip The IP address to hash.
2401
+ *
2402
+ * @return string
2403
+ */
2404
+ public static function hash_ip( $ip ) {
2405
  return wp_hash_password( $ip );
2406
  }
2407
 
2408
 
2409
  /**
2410
+ * Delete spam reason as comment data
2411
+ *
2412
+ * @since 2.6.0
2413
+ * @change 2.6.0
2414
+ *
2415
+ * @param integer $comment_id Comment ID.
2416
+ */
2417
+ public static function delete_spam_reason_by_comment( $comment_id ) {
 
 
2418
  delete_comment_meta(
2419
  $comment_id,
2420
  'antispam_bee_reason'
2425
  * Get the current post ID.
2426
  *
2427
  * @since 2.7.1
 
 
2428
  */
2429
  public static function populate_post_id() {
2430
 
2435
 
2436
 
2437
  /**
2438
+ * Send notification via e-mail
2439
+ *
2440
+ * @since 0.1
2441
+ * @change 2.5.7
2442
+ *
2443
+ * @hook string antispam_bee_notification_subject Custom subject for notification mails
2444
+ *
2445
+ * @param int $id ID of the comment.
2446
+ * @return int $id ID of the comment.
2447
+ */
2448
+ public static function send_mail_notification( $id ) {
 
 
 
2449
  $options = self::get_options();
2450
 
2451
+ if ( ! $options['email_notify'] ) {
 
2452
  return $id;
2453
  }
2454
 
2455
+ $comment = get_comment( $id, ARRAY_A );
 
2456
 
2457
+ if ( empty( $comment ) ) {
 
2458
  return $id;
2459
  }
2460
 
2461
+ $post = get_post( $comment['comment_post_ID'] );
2462
+ if ( ! $post ) {
2463
  return $id;
2464
  }
2465
 
 
2466
  self::load_plugin_lang();
2467
 
 
2468
  $subject = sprintf(
2469
  '[%s] %s',
2470
  stripslashes_deep(
2471
  html_entity_decode(
2472
+ get_bloginfo( 'name' ),
2473
  ENT_QUOTES
2474
  )
2475
  ),
2476
+ esc_html__( 'Comment marked as spam', 'antispam-bee' )
2477
  );
2478
 
2479
+ // Content.
2480
+ $content = strip_tags( stripslashes( $comment['comment_content'] ) );
2481
+ if ( ! $content ) {
2482
  $content = sprintf(
2483
  '-- %s --',
2484
+ esc_html__( 'Content removed by Antispam Bee', 'antispam-bee' )
2485
  );
2486
  }
2487
 
2488
+ // Body.
2489
  $body = sprintf(
2490
  "%s \"%s\"\r\n\r\n",
2491
+ esc_html__( 'New spam comment on your post', 'antispam-bee' ),
2492
+ strip_tags( $post->post_title )
2493
+ ) . sprintf(
2494
  "%s: %s\r\n",
2495
+ esc_html__( 'Author', 'antispam-bee' ),
2496
+ ( empty( $comment['comment_author'] ) ? '' : strip_tags( $comment['comment_author'] ) )
2497
+ ) . sprintf(
2498
  "URL: %s\r\n",
2499
+ // empty check exists.
2500
+ esc_url( $comment['comment_author_url'] )
2501
+ ) . sprintf(
2502
  "%s: %s\r\n",
2503
+ esc_html__( 'Type', 'antispam-bee' ),
2504
+ esc_html( ( empty( $comment['comment_type'] ) ? __( 'Comment', 'antispam-bee' ) : __( 'Trackback', 'antispam-bee' ) ) )
2505
+ ) . sprintf(
2506
  "Whois: http://whois.arin.net/rest/ip/%s\r\n",
2507
  $comment['comment_author_IP']
2508
+ ) . sprintf(
2509
  "%s: %s\r\n\r\n",
2510
+ esc_html__( 'Spam Reason', 'antispam-bee' ),
2511
+ esc_html( self::$defaults['reasons'][ self::$_reason ] )
2512
+ ) . sprintf(
2513
  "%s\r\n\r\n\r\n",
2514
  $content
2515
+ ) . (
2516
  EMPTY_TRASH_DAYS ? (
2517
  sprintf(
2518
  "%s: %s\r\n",
2519
+ esc_html__( 'Trash it', 'antispam-bee' ),
2520
+ admin_url( 'comment.php?action=trash&c=' . $id )
2521
  )
2522
  ) : (
2523
  sprintf(
2524
  "%s: %s\r\n",
2525
+ esc_html__( 'Delete it', 'antispam-bee' ),
2526
+ admin_url( 'comment.php?action=delete&c=' . $id )
2527
  )
2528
  )
2529
+ ) . sprintf(
2530
+ "%s: %s\r\n",
2531
+ esc_html__( 'Approve it', 'antispam-bee' ),
2532
+ admin_url( 'comment.php?action=approve&c=' . $id )
2533
+ ) . sprintf(
2534
  "%s: %s\r\n\r\n",
2535
+ esc_html__( 'Spam list', 'antispam-bee' ),
2536
+ admin_url( 'edit-comments.php?comment_status=spam' )
2537
+ ) . sprintf(
2538
  "%s\r\n%s\r\n",
2539
+ esc_html__( 'Notify message by Antispam Bee', 'antispam-bee' ),
2540
+ esc_html__( 'http://antispambee.com', 'antispam-bee' )
2541
  );
2542
 
 
2543
  wp_mail(
2544
  /**
2545
+ * Filters the recipients of the spam notification.
2546
+ *
2547
  * @param array The recipients array.
2548
+ */
2549
  apply_filters(
2550
  'antispam_bee_notification_recipients',
2551
+ array( get_bloginfo( 'admin_email' ) )
2552
  ),
2553
  /**
2554
+ * Filters the subject of the spam notification.
2555
+ *
2556
  * @param string $subject subject line.
2557
+ */
2558
  apply_filters(
2559
  'antispam_bee_notification_subject',
2560
  $subject
2567
 
2568
 
2569
 
2570
+ /*
2571
+ * ############################
2572
+ * ####### STATISTICS #######
2573
+ * ############################
 
 
 
 
 
 
 
 
 
2574
  */
2575
 
2576
+ /**
2577
+ * Return the number of spam comments
2578
+ *
2579
+ * @since 0.1
2580
+ * @change 2.4
2581
+ */
2582
+ private static function _get_spam_count() {
2583
+ // Init.
2584
+ $count = self::get_option( 'spam_count' );
2585
 
2586
+ // Fire.
2587
+ return ( get_locale() === 'de_DE' ? number_format( $count, 0, '', '.' ) : number_format_i18n( $count ) );
2588
  }
2589
 
2590
 
2591
  /**
2592
+ * Output the number of spam comments
2593
+ *
2594
+ * @since 0.1
2595
+ * @change 2.4
2596
+ */
2597
+ public static function the_spam_count() {
 
 
2598
  echo esc_html( self::_get_spam_count() );
2599
  }
2600
 
2601
 
2602
  /**
2603
+ * Update the number of spam comments
2604
+ *
2605
+ * @since 0.1
2606
+ * @change 2.6.1
2607
+ */
2608
+ private static function _update_spam_count() {
2609
+ // Skip if not enabled.
2610
+ if ( ! self::get_option( 'dashboard_count' ) ) {
 
 
2611
  return;
2612
  }
2613
 
2614
  self::_update_option(
2615
  'spam_count',
2616
+ intval( self::get_option( 'spam_count' ) + 1 )
2617
  );
2618
  }
2619
 
 
2620
  /**
2621
+ * Update statistics
2622
+ *
2623
+ * @since 1.9
2624
+ * @change 2.6.1
2625
+ */
2626
+ private static function _update_daily_stats() {
2627
+ // Skip if not enabled.
2628
+ if ( ! self::get_option( 'dashboard_chart' ) ) {
 
 
2629
  return;
2630
  }
2631
 
2632
+ // Init.
2633
+ $stats = (array) self::get_option( 'daily_stats' );
2634
+ $today = (int) strtotime( 'today' );
2635
 
2636
+ // Count up.
2637
+ if ( array_key_exists( $today, $stats ) ) {
2638
+ $stats[ $today ] ++;
2639
  } else {
2640
+ $stats[ $today ] = 1;
2641
  }
2642
 
2643
+ // Sort.
2644
+ krsort( $stats, SORT_NUMERIC );
2645
 
2646
+ // Save.
2647
  self::_update_option(
2648
  'daily_stats',
2649
+ array_slice( $stats, 0, 31, true )
2650
  );
2651
  }
2652
 
2653
  /**
2654
  * Returns the secret of a post used in the textarea name attribute.
2655
  *
2656
+ * @param int $post_id The Post ID.
2657
  *
2658
  * @return string
2659
  */
2660
  public static function get_secret_name_for_post( $post_id ) {
2661
 
2662
+ if ( self::get_option( 'always_allowed' ) ) {
2663
+ $secret = substr( sha1( md5( 'comment-id' . self::$_salt ) ), 0, 10 );
2664
+ } else {
2665
+ $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
2666
+ }
2667
 
2668
  /**
2669
  * Filters the secret for a post, which is used in the textarea name attribute.
2670
  *
2671
  * @param string $secret The secret.
2672
  * @param int $post_id The post ID.
2673
+ * @param bool $always_allowed Whether the comment form is used outside of the single post view or not.
2674
  */
2675
  return apply_filters(
2676
  'ab_get_secret_name_for_post',
2677
  $secret,
2678
+ (int) $post_id,
2679
+ (bool) self::get_option( 'always_allowed' )
2680
  );
2681
 
2682
  }
2684
  /**
2685
  * Returns the secret of a post used in the textarea id attribute.
2686
  *
2687
+ * @param int $post_id The post ID.
2688
  *
2689
  * @return string
2690
  */
2691
  public static function get_secret_id_for_post( $post_id ) {
2692
 
2693
+ if ( self::get_option( 'always_allowed' ) ) {
2694
+ $secret = substr( sha1( md5( 'comment-id' . self::$_salt ) ), 0, 10 );
2695
+ } else {
2696
+ $secret = substr( sha1( md5( 'comment-id' . self::$_salt . (int) $post_id ) ), 0, 10 );
2697
+ }
2698
 
2699
  /**
2700
  * Filters the secret for a post, which is used in the textarea id attribute.
2701
  *
2702
  * @param string $secret The secret.
2703
  * @param int $post_id The post ID.
2704
+ * @param bool $always_allowed Whether the comment form is used outside of the single post view or not.
2705
  */
2706
  return apply_filters(
2707
  'ab_get_secret_id_for_post',
2708
  $secret,
2709
+ (int) $post_id,
2710
+ (bool) self::get_option( 'always_allowed' )
2711
  );
2712
  }
2713
 
2722
 
2723
  return 'spam';
2724
  }
2725
+
2726
+ /**
2727
+ * A wrapper around wp_parse_url().
2728
+ *
2729
+ * @since 2.8.2
2730
+ *
2731
+ * @param string $url The URL to parse.
2732
+ * @param string $component The component to get back.
2733
+ *
2734
+ * @return string
2735
+ */
2736
+ private static function parse_url( $url, $component = 'host' ) {
2737
+
2738
+ $parts = wp_parse_url( $url );
2739
+ return ( is_array( $parts ) && isset( $parts[ $component ] ) ) ? $parts[ $component ] : '';
2740
+ }
2741
  }
2742
 
2743
 
2744
+ // Fire.
2745
  add_action(
2746
  'plugins_loaded',
2747
  array(
2748
  'Antispam_Bee',
2749
+ 'init',
2750
  )
2751
  );
2752
 
2753
+ // Activation.
2754
  register_activation_hook(
2755
  __FILE__,
2756
  array(
2757
  'Antispam_Bee',
2758
+ 'activate',
2759
  )
2760
  );
2761
 
2762
+ // Deactivation.
2763
  register_deactivation_hook(
2764
  __FILE__,
2765
  array(
2766
  'Antispam_Bee',
2767
+ 'deactivate',
2768
  )
2769
  );
2770
 
2771
+ // Uninstall.
2772
  register_uninstall_hook(
2773
  __FILE__,
2774
  array(
2775
  'Antispam_Bee',
2776
+ 'uninstall',
2777
  )
2778
  );
inc/columns.class.php CHANGED
@@ -1,120 +1,105 @@
1
  <?php
2
- // Make sure this file is only run from within the WordPress context.
3
- defined( 'ABSPATH' ) || exit;
4
-
 
 
5
 
6
- // Antispam_Bee_Columns
7
 
 
 
 
8
  final class Antispam_Bee_Columns {
9
 
10
-
11
  /**
12
- * Register plugin columns on comments screen
13
- *
14
- * @since 2.6.0
15
- * @change 2.6.0
16
- *
17
- * @param array $columns Array with existing columns
18
- * @return array Array with extended columns
19
- */
20
-
21
- public static function register_plugin_columns($columns)
22
- {
23
  return array_merge(
24
  $columns,
25
  array(
26
- 'antispam_bee_reason' => esc_html__('Spam Reason', 'antispam-bee')
27
  )
28
  );
29
  }
30
 
31
-
32
  /**
33
- * Display plugin column values on comments screen
34
- *
35
- * @since 2.6.0
36
- * @change 2.6.0
37
- *
38
- * @param string $column Currently selected column
39
- * @param integer $comment_id Comment ID
40
- */
41
-
42
- public static function print_plugin_column($column, $comment_id)
43
- {
44
- // Only Antispam Bee column
45
- if ( $column !== 'antispam_bee_reason' ) {
46
  return;
47
  }
48
 
49
- // Init data
50
- $spam_reason = get_comment_meta($comment_id, $column, true);
51
  $spam_reasons = Antispam_Bee::$defaults['reasons'];
52
 
53
- // Empty values?
54
- if ( empty($spam_reason) OR empty($spam_reasons[$spam_reason]) ) {
55
  return;
56
  }
57
 
58
- // Escape & Print
59
- echo esc_html( $spam_reasons[$spam_reason] );
60
  }
61
 
62
-
63
  /**
64
- * Register plugin sortable columns on comments screen
65
- *
66
- * @since 2.6.3
67
- * @change 2.6.3
68
- *
69
- * @param array $columns Registered columns
70
- * @return array $columns Columns with AB field
71
- */
72
-
73
- public static function register_sortable_columns($columns)
74
- {
75
  $columns['antispam_bee_reason'] = 'antispam_bee_reason';
76
 
77
  return $columns;
78
  }
79
 
80
-
81
  /**
82
- * Adjust orderby query
83
- *
84
- * @since 2.6.3
85
- * @change 2.6.3
86
- *
87
- * @param object $query Current WordPress query
88
- */
89
-
90
- public static function set_orderby_query($query)
91
- {
92
- // Order by value
93
- $orderby = $query->get('orderby');
94
-
95
- // Skip if not our case
96
- if ( empty($orderby) OR $orderby !== 'antispam_bee_reason' ) {
97
  return;
98
  }
99
 
100
- // Set orderby values
101
  $query->set( 'meta_key', 'antispam_bee_reason' );
102
  $query->set( 'orderby', 'meta_value' );
103
  }
104
 
105
-
106
  /**
107
- * Print CSS for the plugin column
108
- *
109
- * @since 2.6.1
110
- * @change 2.6.1
111
- */
112
-
113
  public static function print_column_styles() { ?>
114
- <style>
115
- .column-antispam_bee_reason {
116
- width: 10%;
117
- }
118
- </style>
119
- <?php }
 
120
  }
1
  <?php
2
+ /**
3
+ * The Columns Class.
4
+ *
5
+ * @package Antispam Bee
6
+ */
7
 
8
+ defined( 'ABSPATH' ) || exit;
9
 
10
+ /**
11
+ * Class Antispam_Bee_Columns
12
+ */
13
  final class Antispam_Bee_Columns {
14
 
 
15
  /**
16
+ * Register plugin columns on comments screen.
17
+ *
18
+ * @since 2.6.0
19
+ * @change 2.6.0
20
+ *
21
+ * @param array $columns Array with existing columns.
22
+ * @return array Array with extended columns.
23
+ */
24
+ public static function register_plugin_columns( $columns ) {
 
 
25
  return array_merge(
26
  $columns,
27
  array(
28
+ 'antispam_bee_reason' => esc_html__( 'Spam Reason', 'antispam-bee' ),
29
  )
30
  );
31
  }
32
 
 
33
  /**
34
+ * Display plugin column values on comments screen
35
+ *
36
+ * @since 2.6.0
37
+ * @change 2.6.0
38
+ *
39
+ * @param string $column Currently selected column.
40
+ * @param integer $comment_id Comment ID.
41
+ */
42
+ public static function print_plugin_column( $column, $comment_id ) {
43
+ if ( 'antispam_bee_reason' !== $column ) {
 
 
 
44
  return;
45
  }
46
 
47
+ $spam_reason = get_comment_meta( $comment_id, $column, true );
 
48
  $spam_reasons = Antispam_Bee::$defaults['reasons'];
49
 
50
+ if ( empty( $spam_reason ) || empty( $spam_reasons[ $spam_reason ] ) ) {
 
51
  return;
52
  }
53
 
54
+ echo esc_html( $spam_reasons[ $spam_reason ] );
 
55
  }
56
 
 
57
  /**
58
+ * Register plugin sortable columns on comments screen
59
+ *
60
+ * @since 2.6.3
61
+ * @change 2.6.3
62
+ *
63
+ * @param array $columns Registered columns.
64
+ * @return array $columns Columns with AB field.
65
+ */
66
+ public static function register_sortable_columns( $columns ) {
 
 
67
  $columns['antispam_bee_reason'] = 'antispam_bee_reason';
68
 
69
  return $columns;
70
  }
71
 
 
72
  /**
73
+ * Adjust orderby query
74
+ *
75
+ * @since 2.6.3
76
+ * @change 2.6.3
77
+ *
78
+ * @param \WP_Query $query Current WordPress query.
79
+ */
80
+ public static function set_orderby_query( $query ) {
81
+ $orderby = $query->get( 'orderby' );
82
+
83
+ if ( empty( $orderby ) || 'antispam_bee_reason' !== $orderby ) {
 
 
 
 
84
  return;
85
  }
86
 
 
87
  $query->set( 'meta_key', 'antispam_bee_reason' );
88
  $query->set( 'orderby', 'meta_value' );
89
  }
90
 
 
91
  /**
92
+ * Print CSS for the plugin column
93
+ *
94
+ * @since 2.6.1
95
+ * @change 2.6.1
96
+ */
 
97
  public static function print_column_styles() { ?>
98
+ <style>
99
+ .column-antispam_bee_reason {
100
+ width: 10%;
101
+ }
102
+ </style>
103
+ <?php
104
+ }
105
  }
inc/gui.class.php CHANGED
@@ -1,42 +1,38 @@
1
  <?php
2
- // Make sure this file is only run from within the WordPress context.
3
- defined( 'ABSPATH' ) || exit;
 
 
 
4
 
 
5
 
6
  /**
7
- * Antispam_Bee_GUI
8
- *
9
- * @since 2.4
10
- */
11
-
12
  class Antispam_Bee_GUI extends Antispam_Bee {
13
 
14
-
15
  /**
16
- * Save the GUI
17
- *
18
- * @since 0.1
19
- * @change 2.7.0
20
- */
21
-
22
- public static function save_changes()
23
- {
24
- // No POST?
25
- if ( empty($_POST) ) {
26
- wp_die(esc_html__('Cheatin&#8217; uh?', 'antispam-bee'));
27
  }
28
 
29
- // Capability check
30
- if ( ! current_user_can('manage_options') ) {
31
- wp_die(esc_html__('Cheatin&#8217; uh?', 'antispam-bee'));
32
  }
33
 
34
- // Check referer
35
- check_admin_referer('_antispam_bee__settings_nonce');
36
 
37
- // Determine options
38
- $selected_languages_raw = wp_unslash( self::get_key($_POST, 'ab_translate_lang' ) );
39
- if(!is_array($selected_languages_raw)) {
40
  $selected_languages_raw = array();
41
  }
42
  $selected_languages = array();
@@ -49,98 +45,92 @@ class Antispam_Bee_GUI extends Antispam_Bee {
49
  $selected_languages[] = $value;
50
  }
51
  $options = array(
52
- 'flag_spam' => (int)(!empty($_POST['ab_flag_spam'])),
53
- 'email_notify' => (int)(!empty($_POST['ab_email_notify'])),
54
- 'cronjob_enable' => (int)(!empty($_POST['ab_cronjob_enable'])),
55
- 'cronjob_interval' => (int)self::get_key($_POST, 'ab_cronjob_interval'),
56
-
57
- 'no_notice' => (int)(!empty($_POST['ab_no_notice'])),
58
-
59
- 'dashboard_count' => (int)(!empty($_POST['ab_dashboard_count'])),
60
- 'dashboard_chart' => (int)(!empty($_POST['ab_dashboard_chart'])),
61
- 'advanced_check' => (int)(!empty($_POST['ab_advanced_check'])),
62
- 'regexp_check' => (int)(!empty($_POST['ab_regexp_check'])),
63
- 'spam_ip' => (int)(!empty($_POST['ab_spam_ip'])),
64
- 'already_commented' => (int)(!empty($_POST['ab_already_commented'])),
65
- 'time_check' => (int)(!empty($_POST['ab_time_check'])),
66
- 'always_allowed' => (int)(!empty($_POST['ab_always_allowed'])),
67
-
68
- 'ignore_pings' => (int)(!empty($_POST['ab_ignore_pings'])),
69
- 'ignore_filter' => (int)(!empty($_POST['ab_ignore_filter'])),
70
- 'ignore_type' => (int)self::get_key($_POST, 'ab_ignore_type'),
71
-
72
- 'reasons_enable' => (int)(!empty($_POST['ab_reasons_enable'])),
73
- 'ignore_reasons' => (array)self::get_key($_POST, 'ab_ignore_reasons'),
74
-
75
- 'bbcode_check' => (int)(!empty($_POST['ab_bbcode_check'])),
76
- 'gravatar_check' => (int)(!empty($_POST['ab_gravatar_check'])),
77
- 'country_code' => (int)(!empty($_POST['ab_country_code'])),
78
- 'country_black' => sanitize_text_field( wp_unslash( self::get_key( $_POST, 'ab_country_black' ) ) ),
79
- 'country_white' => sanitize_text_field( wp_unslash( self::get_key( $_POST, 'ab_country_white' ) ) ),
80
-
81
- 'translate_api' => (int)(!empty($_POST['ab_translate_api'])),
82
- 'translate_lang' => $selected_languages,
 
 
 
83
  );
84
 
85
- foreach( $options['ignore_reasons'] as $key => $val ) {
86
  if ( ! isset( self::$defaults['reasons'][ $val ] ) ) {
87
  unset( $options['ignore_reasons'][ $key ] );
88
  }
89
  }
90
 
91
- // No number of days indicated?
92
- if ( empty($options['cronjob_interval']) ) {
93
  $options['cronjob_enable'] = 0;
94
  }
95
 
96
- // Translate API
97
- if ( empty($options['translate_lang']) ) {
98
  $options['translate_api'] = 0;
99
  }
100
 
101
- // List of spam reasons
102
- if ( empty($options['reasons_enable']) ) {
103
  $options['ignore_reasons'] = array();
104
  }
105
 
106
- // Blacklist clean
107
- if ( !empty($options['country_black']) ) {
108
  $options['country_black'] = preg_replace(
109
  '/[^A-Z ,;]/',
110
  '',
111
- strtoupper($options['country_black'])
112
  );
113
  }
114
 
115
- // Whitelist clean
116
- if ( !empty($options['country_white']) ) {
117
  $options['country_white'] = preg_replace(
118
  '/[^A-Z ,;]/',
119
  '',
120
- strtoupper($options['country_white'])
121
  );
122
  }
123
 
124
- // Empty lists?
125
- if ( empty($options['country_black']) && empty($options['country_white']) ) {
126
  $options['country_code'] = 0;
127
  }
128
 
129
- // Stop Cron?
130
- if ( $options['cronjob_enable'] && !self::get_option('cronjob_enable') ) {
131
  self::init_scheduled_hook();
132
- } else if ( !$options['cronjob_enable'] && self::get_option('cronjob_enable') ) {
133
  self::clear_scheduled_hook();
134
  }
135
 
136
- // Save options
137
- self::update_options($options);
138
 
139
- // Redirect
140
  wp_safe_redirect(
141
  add_query_arg(
142
  array(
143
- 'updated' => 'true'
144
  ),
145
  wp_get_referer()
146
  )
@@ -149,30 +139,22 @@ class Antispam_Bee_GUI extends Antispam_Bee {
149
  die();
150
  }
151
 
152
-
153
  /**
154
- * Generation of a selectbox
155
- *
156
- * @since 2.4.5
157
- * @change 2.4.5
158
- *
159
- * @param string $name Name of the Selectbox
160
- * @param array $data Array with values
161
- * @param string $selected Selected value
162
- * @return string $html Generated HTML
163
- */
164
-
165
- private static function _build_select($name, $data, $selected)
166
- {
167
- // Start HTML
168
- $html = '<select name="' .$name. '">';
169
-
170
- // Loop options
171
- foreach( $data as $k => $v) {
172
- $html .= '<option value="' .esc_attr($k). '" ' .selected($selected, $k, false). '>' .esc_html( $v ). '</option>';
173
  }
174
-
175
- // Close HTML
176
  $html .= '</select>';
177
 
178
  return $html;
@@ -180,24 +162,23 @@ class Antispam_Bee_GUI extends Antispam_Bee {
180
 
181
 
182
  /**
183
- * Display the GUI
184
- *
185
- * @since 0.1
186
- * @change 2.7.0
187
- */
188
-
189
  public static function options_page() { ?>
190
  <div class="wrap" id="ab_main">
191
  <h2>
192
  Antispam Bee
193
  </h2>
194
 
195
- <form action="<?php echo admin_url('admin-post.php') ?>" method="post">
196
  <input type="hidden" name="action" value="ab_save_changes" />
197
 
198
- <?php wp_nonce_field('_antispam_bee__settings_nonce') ?>
199
 
200
- <?php $options = self::get_options() ?>
201
  <div class="ab-wrap">
202
  <!--[if lt IE 9]>
203
  <p class="browsehappy">
@@ -215,33 +196,41 @@ class Antispam_Bee_GUI extends Antispam_Bee {
215
 
216
  <ul>
217
  <li>
218
- <input type="checkbox" name="ab_already_commented" id="ab_already_commented" value="1" <?php checked($options['already_commented'], 1) ?> />
219
  <label for="ab_already_commented">
220
  <?php esc_html_e( 'Trust approved commenters', 'antispam-bee' ); ?>
221
  <span><?php esc_html_e( 'No review of already commented users', 'antispam-bee' ); ?></span>
222
  </label>
223
  </li>
224
 
 
225
  <li>
226
- <input type="checkbox" name="ab_gravatar_check" id="ab_gravatar_check" value="1" <?php checked($options['gravatar_check'], 1) ?> />
227
  <label for="ab_gravatar_check">
228
  <?php esc_html_e( 'Trust commenters with a Gravatar', 'antispam-bee' ); ?>
229
- <span><?php $link1 = sprintf(
 
 
230
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
231
- esc_url( __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation#trust-commenters-with-a-gravatar', 'antispam-bee' ),
232
- 'https' )
 
 
 
 
 
 
 
 
233
  );
234
- printf(
235
- /* translators: 1: opening <a> tag with link to documentation. 2: closing </a> tag */
236
- esc_html__( 'Check if commenter has a Gravatar image. Please note the %1$sprivacy notice%2$s for this option.', 'antispam-bee' ),
237
- $link1,
238
- '</a>'
239
- ); ?></span>
240
  </label>
241
  </li>
 
242
 
243
  <li>
244
- <input type="checkbox" name="ab_time_check" id="ab_time_check" value="1" <?php checked($options['time_check'], 1) ?> />
245
  <label for="ab_time_check">
246
  <?php esc_html_e( 'Consider the comment time', 'antispam-bee' ); ?>
247
  <span><?php esc_html_e( 'Not recommended when using page caching', 'antispam-bee' ); ?></span>
@@ -249,7 +238,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
249
  </li>
250
 
251
  <li>
252
- <input type="checkbox" name="ab_bbcode_check" id="ab_bbcode_check" value="1" <?php checked($options['bbcode_check'], 1) ?> />
253
  <label for="ab_bbcode_check">
254
  <?php esc_html_e( 'BBCode is spam', 'antispam-bee' ); ?>
255
  <span><?php esc_html_e( 'Review the comment contents for BBCode links', 'antispam-bee' ); ?></span>
@@ -257,7 +246,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
257
  </li>
258
 
259
  <li>
260
- <input type="checkbox" name="ab_advanced_check" id="ab_advanced_check" value="1" <?php checked($options['advanced_check'], 1) ?> />
261
  <label for="ab_advanced_check">
262
  <?php esc_html_e( 'Validate the ip address of commenters', 'antispam-bee' ); ?>
263
  <span><?php esc_html_e( 'Validation of the IP address used', 'antispam-bee' ); ?></span>
@@ -265,7 +254,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
265
  </li>
266
 
267
  <li>
268
- <input type="checkbox" name="ab_regexp_check" id="ab_regexp_check" value="1" <?php checked($options['regexp_check'], 1) ?> />
269
  <label for="ab_regexp_check">
270
  <?php esc_html_e( 'Use regular expressions', 'antispam-bee' ); ?>
271
  <span><?php esc_html_e( 'Predefined and custom patterns by plugin hook', 'antispam-bee' ); ?></span>
@@ -273,7 +262,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
273
  </li>
274
 
275
  <li>
276
- <input type="checkbox" name="ab_spam_ip" id="ab_spam_ip" value="1" <?php checked($options['spam_ip'], 1) ?> />
277
  <label for="ab_spam_ip">
278
  <?php esc_html_e( 'Look in the local spam database', 'antispam-bee' ); ?>
279
  <span><?php esc_html_e( 'Check for spam data on your own blog', 'antispam-bee' ); ?></span>
@@ -281,85 +270,108 @@ class Antispam_Bee_GUI extends Antispam_Bee {
281
  </li>
282
 
283
  <li>
284
- <input type="checkbox" name="ab_country_code" id="ab_country_code" value="1" <?php checked($options['country_code'], 1) ?> />
285
  <label for="ab_country_code">
286
- <?php esc_html_e('Block or allow comments from specific countries', 'antispam-bee') ?>
287
- <span><?php $link1 = sprintf(
 
 
288
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
289
- esc_url( __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation#block-comments-from-specific-countries', 'antispam-bee' ),
290
- 'https' )
 
 
 
 
 
 
 
291
  );
292
- printf(
293
- /* translators: 1: opening <a> tag with link to documentation. 2: closing </a> tag. */
294
- esc_html__( 'Filtering the requests depending on country. Please note the %1$sprivacy notice%2$s for this option.', 'antispam-bee' ),
295
- $link1, '</a>'
296
- ); ?></span>
297
  </label>
298
 
299
  <ul>
300
- <?php $iso_codes_link = sprintf(
 
301
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
302
- esc_url( __( 'https://www.iso.org/iso/country_names_and_code_elements', 'antispam-bee' ),
303
- 'https' )
304
- ); ?>
 
 
 
305
  <li>
306
- <textarea name="ab_country_black" id="ab_country_black" class="ab-medium-field code" placeholder="<?php esc_attr_e( 'e.g. BF, SG, YE', 'antispam-bee' ); ?>"><?php echo esc_attr($options['country_black']); ?></textarea>
307
  <label for="ab_country_black">
308
- <span><?php
 
309
  printf(
310
  /* translators: 1: opening <a> tag with link to ISO codes reference. 2: closing </a> tag. */
311
  esc_html__( 'Blacklist %1$sISO Codes%2$s for this option.', 'antispam-bee' ),
312
- $iso_codes_link,
313
- '</a>' );
314
- ?></span>
 
 
315
  </label>
316
  </li>
317
  <li>
318
- <textarea name="ab_country_white" id="ab_country_white" class="ab-medium-field code" placeholder="<?php esc_attr_e( 'e.g. BF, SG, YE', 'antispam-bee' ); ?>"><?php echo esc_attr($options['country_white']); ?></textarea>
319
  <label for="ab_country_white">
320
- <span><?php
 
321
  printf(
322
  /* translators: 1: opening <a> tag with link to ISO codes reference. 2: closing </a> tag. */
323
  esc_html__( 'Whitelist %1$sISO Codes%2$s for this option.', 'antispam-bee' ),
324
- $iso_codes_link,
325
- '</a>' );
326
- ?></span>
 
 
327
  </label>
328
  </li>
329
  </ul>
330
  </li>
331
 
332
  <li>
333
- <input type="checkbox" name="ab_translate_api" id="ab_translate_api" value="1" <?php checked($options['translate_api'], 1) ?> />
334
  <label for="ab_translate_api">
335
- <?php esc_html_e( 'Allow comments only in certain language', 'antispam-bee' ) ?>
336
- <span><?php
 
337
  $link1 = sprintf(
338
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
339
- esc_url( __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation#allow-comments-only-in-certain-language', 'antispam-bee' ),
340
- 'https' )
 
 
341
  );
342
 
343
  printf(
344
  /* translators: 1: opening <a> tag with link to documentation. 2: closing </a> tag. */
345
  esc_html__( 'Detect and approve only the specified language. Please note the %1$sprivacy notice%2$s for this option.', 'antispam-bee' ),
346
- $link1,
347
- '</a>' );
348
- ?></span>
 
 
349
  </label>
350
 
351
  <ul>
352
  <li>
353
  <select multiple name="ab_translate_lang[]">
354
  <?php
355
- $lang = self::get_allowed_translate_languages();
356
  $selected_languages = (array) $options['translate_lang'];
357
- foreach( $lang as $k => $v ) { ?>
 
358
  <option <?php echo in_array( $k, $selected_languages, true ) ? 'selected="selected"' : ''; ?> value="<?php echo esc_attr( $k ); ?>"><?php echo esc_html( $v ); ?></option>
 
359
  <?php } ?>
360
  </select>
361
  <label for="ab_translate_lang">
362
- <?php esc_html_e('Language', 'antispam-bee') ?>
363
  </label>
364
  </li>
365
  </ul>
@@ -367,7 +379,6 @@ class Antispam_Bee_GUI extends Antispam_Bee {
367
  </ul>
368
  </div>
369
 
370
-
371
  <div class="ab-column ab-join">
372
  <h3 class="icon advanced">
373
  <?php esc_html_e( 'Advanced', 'antispam-bee' ); ?>
@@ -378,7 +389,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
378
 
379
  <ul>
380
  <li>
381
- <input type="checkbox" name="ab_flag_spam" id="ab_flag_spam" value="1" <?php checked($options['flag_spam'], 1) ?> />
382
  <label for="ab_flag_spam">
383
  <?php esc_html_e( 'Mark as spam, do not delete', 'antispam-bee' ); ?>
384
  <span><?php esc_html_e( 'Keep the spam in my blog.', 'antispam-bee' ); ?></span>
@@ -386,7 +397,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
386
  </li>
387
 
388
  <li class="ab_flag_spam_child">
389
- <input type="checkbox" name="ab_email_notify" id="ab_email_notify" value="1" <?php checked($options['email_notify'], 1) ?> />
390
  <label for="ab_email_notify">
391
  <?php esc_html_e( 'Spam-Notification by email', 'antispam-bee' ); ?>
392
  <span><?php esc_html_e( 'Notify admins by e-mail about incoming spam', 'antispam-bee' ); ?></span>
@@ -394,7 +405,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
394
  </li>
395
 
396
  <li class="ab_flag_spam_child">
397
- <input type="checkbox" name="ab_no_notice" id="ab_no_notice" value="1" <?php checked($options['no_notice'], 1) ?> />
398
  <label for="ab_no_notice">
399
  <?php esc_html_e( 'Do not save the spam reason', 'antispam-bee' ); ?>
400
  <span><?php esc_html_e( 'Spam reason as a table column in the spam overview', 'antispam-bee' ); ?></span>
@@ -402,36 +413,45 @@ class Antispam_Bee_GUI extends Antispam_Bee {
402
  </li>
403
 
404
  <li class="ab_flag_spam_child">
405
- <input type="checkbox" name="ab_cronjob_enable" id="ab_cronjob_enable" value="1" <?php checked($options['cronjob_enable'], 1) ?> />
406
  <label for="ab_cronjob_enable">
407
- <?php echo sprintf(
 
 
408
  esc_html__( 'Delete existing spam after %s days', 'antispam-bee' ),
409
- '<input type="number" min="0" name="ab_cronjob_interval" value="' .esc_attr($options['cronjob_interval']). '" class="ab-mini-field" />'
410
- ) ?>
411
- <span><?php esc_html_e( 'Cleaning up the database from old entries', 'antispam-bee' ) ?></span>
 
412
  </label>
413
  </li>
414
 
415
  <li class="ab_flag_spam_child">
416
- <input type="checkbox" name="ab_ignore_filter" id="ab_ignore_filter" value="1" <?php checked($options['ignore_filter'], 1) ?> />
417
  <label for="ab_ignore_filter">
418
- <?php echo sprintf(
 
 
 
 
419
  esc_html__( 'Limit approval to %s', 'antispam-bee' ),
420
  self::_build_select(
421
  'ab_ignore_type',
422
  array(
423
  1 => esc_attr__( 'Comments', 'antispam-bee' ),
424
- 2 => esc_attr__( 'Pings', 'antispam-bee' )
425
  ),
426
  $options['ignore_type']
427
  )
428
- ); ?>
 
 
429
  <span><?php esc_html_e( 'Other types of spam will be deleted immediately', 'antispam-bee' ); ?></span>
430
  </label>
431
  </li>
432
 
433
  <li class="ab_flag_spam_child">
434
- <input type="checkbox" name="ab_reasons_enable" id="ab_reasons_enable" value="1" <?php checked($options['reasons_enable'], 1) ?> />
435
  <label for="ab_reasons_enable">
436
  <?php esc_html_e( 'Delete comments by spam reasons', 'antispam-bee' ); ?>
437
  <span><?php esc_html_e( 'For multiple selections press Ctrl/CMD', 'antispam-bee' ); ?></span>
@@ -441,7 +461,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
441
  <li>
442
  <select name="ab_ignore_reasons[]" id="ab_ignore_reasons" size="2" multiple>
443
  <?php foreach ( self::$defaults['reasons'] as $k => $v ) { ?>
444
- <option <?php selected(in_array($k, $options['ignore_reasons']), true); ?> value="<?php echo $k ?>"><?php esc_html_e($v, 'antispam-bee') ?></option>
445
  <?php } ?>
446
  </select>
447
  <label for="ab_ignore_reasons">
@@ -450,7 +470,16 @@ class Antispam_Bee_GUI extends Antispam_Bee {
450
  </li>
451
  </ul>
452
  </li>
 
 
 
 
 
 
 
 
453
  </ul>
 
454
  </div>
455
 
456
 
@@ -464,7 +493,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
464
 
465
  <ul>
466
  <li>
467
- <input type="checkbox" name="ab_dashboard_chart" id="ab_dashboard_chart" value="1" <?php checked($options['dashboard_chart'], 1) ?> />
468
  <label for="ab_dashboard_chart">
469
  <?php esc_html_e( 'Generate statistics as a dashboard widget', 'antispam-bee' ); ?>
470
  <span><?php esc_html_e( 'Daily updates of spam detection rate', 'antispam-bee' ); ?></span>
@@ -472,7 +501,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
472
  </li>
473
 
474
  <li>
475
- <input type="checkbox" name="ab_dashboard_count" id="ab_dashboard_count" value="1" <?php checked($options['dashboard_count'], 1) ?> />
476
  <label for="ab_dashboard_count">
477
  <?php esc_html_e( 'Spam counter on the dashboard', 'antispam-bee' ); ?>
478
  <span><?php esc_html_e( 'Amount of identified spam comments', 'antispam-bee' ); ?></span>
@@ -480,7 +509,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
480
  </li>
481
 
482
  <li>
483
- <input type="checkbox" name="ab_ignore_pings" id="ab_ignore_pings" value="1" <?php checked($options['ignore_pings'], 1) ?> />
484
  <label for="ab_ignore_pings">
485
  <?php esc_html_e( 'Do not check trackbacks / pingbacks', 'antispam-bee' ); ?>
486
  <span><?php esc_html_e( 'No spam check for link notifications', 'antispam-bee' ); ?></span>
@@ -488,7 +517,7 @@ class Antispam_Bee_GUI extends Antispam_Bee {
488
  </li>
489
 
490
  <li>
491
- <input type="checkbox" name="ab_always_allowed" id="ab_always_allowed" value="1" <?php checked($options['always_allowed'], 1) ?> />
492
  <label for="ab_always_allowed">
493
  <?php esc_html_e( 'Comment form used outside of posts', 'antispam-bee' ); ?>
494
  <span><?php esc_html_e( 'Check for comment forms on archive pages', 'antispam-bee' ); ?></span>
@@ -502,13 +531,13 @@ class Antispam_Bee_GUI extends Antispam_Bee {
502
  <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Donate', 'antispam-bee' ); ?></a>
503
  </p>
504
  <p>
505
- <a href="<?php esc_html_e( 'https://wordpress.org/plugins/antispam-bee/faq/', 'antispam-bee' ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'FAQ', 'antispam-bee' ); ?></a>
506
  </p>
507
  <p>
508
- <a href="https://github.com/pluginkollektiv/antispam-bee/wiki/" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Manual', 'antispam-bee' ); ?></a>
509
  </p>
510
  <p>
511
- <a href="https://wordpress.org/support/plugin/antispam-bee" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Support', 'antispam-bee' ); ?></a>
512
  </p>
513
 
514
  <input type="submit" class="button button-primary" value="<?php esc_html_e( 'Save Changes', 'antispam-bee' ); ?>" />
@@ -516,7 +545,8 @@ class Antispam_Bee_GUI extends Antispam_Bee {
516
  </div>
517
  </form>
518
  </div>
519
- <?php }
 
520
 
521
  /**
522
  * Get the languages, which are selectable to restrict the comment language to.
1
  <?php
2
+ /**
3
+ * The Antispam Bee GUI
4
+ *
5
+ * @package Antispam Bee
6
+ */
7
 
8
+ defined( 'ABSPATH' ) || exit;
9
 
10
  /**
11
+ * Antispam_Bee_GUI
12
+ *
13
+ * @since 2.4
14
+ */
 
15
  class Antispam_Bee_GUI extends Antispam_Bee {
16
 
 
17
  /**
18
+ * Save the GUI
19
+ *
20
+ * @since 0.1
21
+ * @change 2.7.0
22
+ */
23
+ public static function save_changes() {
24
+ if ( empty( $_POST ) ) {
25
+ wp_die( esc_html__( 'Cheatin&#8217; uh?', 'antispam-bee' ) );
 
 
 
26
  }
27
 
28
+ if ( ! current_user_can( 'manage_options' ) ) {
29
+ wp_die( esc_html__( 'Cheatin&#8217; uh?', 'antispam-bee' ) );
 
30
  }
31
 
32
+ check_admin_referer( '_antispam_bee__settings_nonce' );
 
33
 
34
+ $selected_languages_raw = wp_unslash( self::get_key( $_POST, 'ab_translate_lang' ) );
35
+ if ( ! is_array( $selected_languages_raw ) ) {
 
36
  $selected_languages_raw = array();
37
  }
38
  $selected_languages = array();
45
  $selected_languages[] = $value;
46
  }
47
  $options = array(
48
+ 'flag_spam' => (int) ( ! empty( $_POST['ab_flag_spam'] ) ),
49
+ 'email_notify' => (int) ( ! empty( $_POST['ab_email_notify'] ) ),
50
+ 'cronjob_enable' => (int) ( ! empty( $_POST['ab_cronjob_enable'] ) ),
51
+ 'cronjob_interval' => (int) self::get_key( $_POST, 'ab_cronjob_interval' ),
52
+
53
+ 'no_notice' => (int) ( ! empty( $_POST['ab_no_notice'] ) ),
54
+
55
+ 'dashboard_count' => (int) ( ! empty( $_POST['ab_dashboard_count'] ) ),
56
+ 'dashboard_chart' => (int) ( ! empty( $_POST['ab_dashboard_chart'] ) ),
57
+ 'advanced_check' => (int) ( ! empty( $_POST['ab_advanced_check'] ) ),
58
+ 'regexp_check' => (int) ( ! empty( $_POST['ab_regexp_check'] ) ),
59
+ 'spam_ip' => (int) ( ! empty( $_POST['ab_spam_ip'] ) ),
60
+ 'already_commented' => (int) ( ! empty( $_POST['ab_already_commented'] ) ),
61
+ 'time_check' => (int) ( ! empty( $_POST['ab_time_check'] ) ),
62
+ 'always_allowed' => (int) ( ! empty( $_POST['ab_always_allowed'] ) ),
63
+
64
+ 'ignore_pings' => (int) ( ! empty( $_POST['ab_ignore_pings'] ) ),
65
+ 'ignore_filter' => (int) ( ! empty( $_POST['ab_ignore_filter'] ) ),
66
+ 'ignore_type' => (int) self::get_key( $_POST, 'ab_ignore_type' ),
67
+
68
+ 'reasons_enable' => (int) ( ! empty( $_POST['ab_reasons_enable'] ) ),
69
+ 'ignore_reasons' => (array) self::get_key( $_POST, 'ab_ignore_reasons' ),
70
+
71
+ 'bbcode_check' => (int) ( ! empty( $_POST['ab_bbcode_check'] ) ),
72
+ 'gravatar_check' => (int) ( ! empty( $_POST['ab_gravatar_check'] ) ),
73
+ 'country_code' => (int) ( ! empty( $_POST['ab_country_code'] ) ),
74
+ 'country_black' => sanitize_text_field( wp_unslash( self::get_key( $_POST, 'ab_country_black' ) ) ),
75
+ 'country_white' => sanitize_text_field( wp_unslash( self::get_key( $_POST, 'ab_country_white' ) ) ),
76
+
77
+ 'translate_api' => (int) ( ! empty( $_POST['ab_translate_api'] ) ),
78
+ 'translate_lang' => $selected_languages,
79
+
80
+ 'delete_data_on_uninstall' => (int) ( ! empty( $_POST['delete_data_on_uninstall'] ) ),
81
+
82
  );
83
 
84
+ foreach ( $options['ignore_reasons'] as $key => $val ) {
85
  if ( ! isset( self::$defaults['reasons'][ $val ] ) ) {
86
  unset( $options['ignore_reasons'][ $key ] );
87
  }
88
  }
89
 
90
+ if ( empty( $options['cronjob_interval'] ) ) {
 
91
  $options['cronjob_enable'] = 0;
92
  }
93
 
94
+ if ( empty( $options['translate_lang'] ) ) {
 
95
  $options['translate_api'] = 0;
96
  }
97
 
98
+ if ( empty( $options['reasons_enable'] ) ) {
 
99
  $options['ignore_reasons'] = array();
100
  }
101
 
102
+ if ( ! empty( $options['country_black'] ) ) {
 
103
  $options['country_black'] = preg_replace(
104
  '/[^A-Z ,;]/',
105
  '',
106
+ strtoupper( $options['country_black'] )
107
  );
108
  }
109
 
110
+ if ( ! empty( $options['country_white'] ) ) {
 
111
  $options['country_white'] = preg_replace(
112
  '/[^A-Z ,;]/',
113
  '',
114
+ strtoupper( $options['country_white'] )
115
  );
116
  }
117
 
118
+ if ( empty( $options['country_black'] ) && empty( $options['country_white'] ) ) {
 
119
  $options['country_code'] = 0;
120
  }
121
 
122
+ if ( $options['cronjob_enable'] && ! self::get_option( 'cronjob_enable' ) ) {
 
123
  self::init_scheduled_hook();
124
+ } elseif ( ! $options['cronjob_enable'] && self::get_option( 'cronjob_enable' ) ) {
125
  self::clear_scheduled_hook();
126
  }
127
 
128
+ self::update_options( $options );
 
129
 
 
130
  wp_safe_redirect(
131
  add_query_arg(
132
  array(
133
+ 'updated' => 'true',
134
  ),
135
  wp_get_referer()
136
  )
139
  die();
140
  }
141
 
 
142
  /**
143
+ * Generation of a selectbox
144
+ *
145
+ * @since 2.4.5
146
+ * @change 2.4.5
147
+ *
148
+ * @param string $name Name of the Selectbox.
149
+ * @param array $data Array with values.
150
+ * @param string $selected Selected value.
151
+ * @return string $html Generated HTML.
152
+ */
153
+ private static function _build_select( $name, $data, $selected ) {
154
+ $html = '<select name="' . esc_attr( $name ) . '">';
155
+ foreach ( $data as $k => $v ) {
156
+ $html .= '<option value="' . esc_attr( $k ) . '" ' . selected( $selected, $k, false ) . '>' . esc_html( $v ) . '</option>';
 
 
 
 
 
157
  }
 
 
158
  $html .= '</select>';
159
 
160
  return $html;
162
 
163
 
164
  /**
165
+ * Display the GUI
166
+ *
167
+ * @since 0.1
168
+ * @change 2.7.0
169
+ */
 
170
  public static function options_page() { ?>
171
  <div class="wrap" id="ab_main">
172
  <h2>
173
  Antispam Bee
174
  </h2>
175
 
176
+ <form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post">
177
  <input type="hidden" name="action" value="ab_save_changes" />
178
 
179
+ <?php wp_nonce_field( '_antispam_bee__settings_nonce' ); ?>
180
 
181
+ <?php $options = self::get_options(); ?>
182
  <div class="ab-wrap">
183
  <!--[if lt IE 9]>
184
  <p class="browsehappy">
196
 
197
  <ul>
198
  <li>
199
+ <input type="checkbox" name="ab_already_commented" id="ab_already_commented" value="1" <?php checked( $options['already_commented'], 1 ); ?> />
200
  <label for="ab_already_commented">
201
  <?php esc_html_e( 'Trust approved commenters', 'antispam-bee' ); ?>
202
  <span><?php esc_html_e( 'No review of already commented users', 'antispam-bee' ); ?></span>
203
  </label>
204
  </li>
205
 
206
+ <?php if ( 1 === (int) get_option( 'show_avatars', 0 ) ) : ?>
207
  <li>
208
+ <input type="checkbox" name="ab_gravatar_check" id="ab_gravatar_check" value="1" <?php checked( $options['gravatar_check'], 1 ); ?> />
209
  <label for="ab_gravatar_check">
210
  <?php esc_html_e( 'Trust commenters with a Gravatar', 'antispam-bee' ); ?>
211
+ <span>
212
+ <?php
213
+ $link1 = sprintf(
214
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
215
+ esc_url(
216
+ __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation#trust-commenters-with-a-gravatar', 'antispam-bee' ),
217
+ 'https'
218
+ )
219
+ );
220
+ printf(
221
+ /* translators: 1: opening <a> tag with link to documentation. 2: closing </a> tag */
222
+ esc_html__( 'Check if commenter has a Gravatar image. Please note the %1$sprivacy notice%2$s for this option.', 'antispam-bee' ),
223
+ wp_kses_post( $link1 ),
224
+ '</a>'
225
  );
226
+ ?>
227
+ </span>
 
 
 
 
228
  </label>
229
  </li>
230
+ <?php endif; ?>
231
 
232
  <li>
233
+ <input type="checkbox" name="ab_time_check" id="ab_time_check" value="1" <?php checked( $options['time_check'], 1 ); ?> />
234
  <label for="ab_time_check">
235
  <?php esc_html_e( 'Consider the comment time', 'antispam-bee' ); ?>
236
  <span><?php esc_html_e( 'Not recommended when using page caching', 'antispam-bee' ); ?></span>
238
  </li>
239
 
240
  <li>
241
+ <input type="checkbox" name="ab_bbcode_check" id="ab_bbcode_check" value="1" <?php checked( $options['bbcode_check'], 1 ); ?> />
242
  <label for="ab_bbcode_check">
243
  <?php esc_html_e( 'BBCode is spam', 'antispam-bee' ); ?>
244
  <span><?php esc_html_e( 'Review the comment contents for BBCode links', 'antispam-bee' ); ?></span>
246
  </li>
247
 
248
  <li>
249
+ <input type="checkbox" name="ab_advanced_check" id="ab_advanced_check" value="1" <?php checked( $options['advanced_check'], 1 ); ?> />
250
  <label for="ab_advanced_check">
251
  <?php esc_html_e( 'Validate the ip address of commenters', 'antispam-bee' ); ?>
252
  <span><?php esc_html_e( 'Validation of the IP address used', 'antispam-bee' ); ?></span>
254
  </li>
255
 
256
  <li>
257
+ <input type="checkbox" name="ab_regexp_check" id="ab_regexp_check" value="1" <?php checked( $options['regexp_check'], 1 ); ?> />
258
  <label for="ab_regexp_check">
259
  <?php esc_html_e( 'Use regular expressions', 'antispam-bee' ); ?>
260
  <span><?php esc_html_e( 'Predefined and custom patterns by plugin hook', 'antispam-bee' ); ?></span>
262
  </li>
263
 
264
  <li>
265
+ <input type="checkbox" name="ab_spam_ip" id="ab_spam_ip" value="1" <?php checked( $options['spam_ip'], 1 ); ?> />
266
  <label for="ab_spam_ip">
267
  <?php esc_html_e( 'Look in the local spam database', 'antispam-bee' ); ?>
268
  <span><?php esc_html_e( 'Check for spam data on your own blog', 'antispam-bee' ); ?></span>
270
  </li>
271
 
272
  <li>
273
+ <input type="checkbox" name="ab_country_code" id="ab_country_code" value="1" <?php checked( $options['country_code'], 1 ); ?> />
274
  <label for="ab_country_code">
275
+ <?php esc_html_e( 'Block or allow comments from specific countries', 'antispam-bee' ); ?>
276
+ <span>
277
+ <?php
278
+ $link1 = sprintf(
279
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
280
+ esc_url(
281
+ __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation#block-comments-from-specific-countries', 'antispam-bee' ),
282
+ 'https'
283
+ )
284
+ );
285
+ printf(
286
+ /* translators: 1: opening <a> tag with link to documentation. 2: closing </a> tag. */
287
+ esc_html__( 'Filtering the requests depending on country. Please note the %1$sprivacy notice%2$s for this option.', 'antispam-bee' ),
288
+ wp_kses_post( $link1 ), '</a>'
289
  );
290
+ ?>
291
+ </span>
 
 
 
292
  </label>
293
 
294
  <ul>
295
+ <?php
296
+ $iso_codes_link = sprintf(
297
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
298
+ esc_url(
299
+ __( 'https://www.iso.org/iso/country_names_and_code_elements', 'antispam-bee' ),
300
+ 'https'
301
+ )
302
+ );
303
+ ?>
304
  <li>
305
+ <textarea name="ab_country_black" id="ab_country_black" class="ab-medium-field code" placeholder="<?php esc_attr_e( 'e.g. BF, SG, YE', 'antispam-bee' ); ?>"><?php echo esc_attr( $options['country_black'] ); ?></textarea>
306
  <label for="ab_country_black">
307
+ <span>
308
+ <?php
309
  printf(
310
  /* translators: 1: opening <a> tag with link to ISO codes reference. 2: closing </a> tag. */
311
  esc_html__( 'Blacklist %1$sISO Codes%2$s for this option.', 'antispam-bee' ),
312
+ wp_kses_post( $iso_codes_link ),
313
+ '</a>'
314
+ );
315
+ ?>
316
+ </span>
317
  </label>
318
  </li>
319
  <li>
320
+ <textarea name="ab_country_white" id="ab_country_white" class="ab-medium-field code" placeholder="<?php esc_attr_e( 'e.g. BF, SG, YE', 'antispam-bee' ); ?>"><?php echo esc_attr( $options['country_white'] ); ?></textarea>
321
  <label for="ab_country_white">
322
+ <span>
323
+ <?php
324
  printf(
325
  /* translators: 1: opening <a> tag with link to ISO codes reference. 2: closing </a> tag. */
326
  esc_html__( 'Whitelist %1$sISO Codes%2$s for this option.', 'antispam-bee' ),
327
+ wp_kses_post( $iso_codes_link ),
328
+ '</a>'
329
+ );
330
+ ?>
331
+ </span>
332
  </label>
333
  </li>
334
  </ul>
335
  </li>
336
 
337
  <li>
338
+ <input type="checkbox" name="ab_translate_api" id="ab_translate_api" value="1" <?php checked( $options['translate_api'], 1 ); ?> />
339
  <label for="ab_translate_api">
340
+ <?php esc_html_e( 'Allow comments only in certain language', 'antispam-bee' ); ?>
341
+ <span>
342
+ <?php
343
  $link1 = sprintf(
344
  '<a href="%s" target="_blank" rel="noopener noreferrer">',
345
+ esc_url(
346
+ __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/en-Documentation#allow-comments-only-in-certain-language', 'antispam-bee' ),
347
+ 'https'
348
+ )
349
  );
350
 
351
  printf(
352
  /* translators: 1: opening <a> tag with link to documentation. 2: closing </a> tag. */
353
  esc_html__( 'Detect and approve only the specified language. Please note the %1$sprivacy notice%2$s for this option.', 'antispam-bee' ),
354
+ wp_kses_post( $link1 ),
355
+ '</a>'
356
+ );
357
+ ?>
358
+ </span>
359
  </label>
360
 
361
  <ul>
362
  <li>
363
  <select multiple name="ab_translate_lang[]">
364
  <?php
365
+ $lang = self::get_allowed_translate_languages();
366
  $selected_languages = (array) $options['translate_lang'];
367
+ foreach ( $lang as $k => $v ) {
368
+ ?>
369
  <option <?php echo in_array( $k, $selected_languages, true ) ? 'selected="selected"' : ''; ?> value="<?php echo esc_attr( $k ); ?>"><?php echo esc_html( $v ); ?></option>
370
+
371
  <?php } ?>
372
  </select>
373
  <label for="ab_translate_lang">
374
+ <?php esc_html_e( 'Language', 'antispam-bee' ); ?>
375
  </label>
376
  </li>
377
  </ul>
379
  </ul>
380
  </div>
381
 
 
382
  <div class="ab-column ab-join">
383
  <h3 class="icon advanced">
384
  <?php esc_html_e( 'Advanced', 'antispam-bee' ); ?>
389
 
390
  <ul>
391
  <li>
392
+ <input type="checkbox" name="ab_flag_spam" id="ab_flag_spam" value="1" <?php checked( $options['flag_spam'], 1 ); ?> />
393
  <label for="ab_flag_spam">
394
  <?php esc_html_e( 'Mark as spam, do not delete', 'antispam-bee' ); ?>
395
  <span><?php esc_html_e( 'Keep the spam in my blog.', 'antispam-bee' ); ?></span>
397
  </li>
398
 
399
  <li class="ab_flag_spam_child">
400
+ <input type="checkbox" name="ab_email_notify" id="ab_email_notify" value="1" <?php checked( $options['email_notify'], 1 ); ?> />
401
  <label for="ab_email_notify">
402
  <?php esc_html_e( 'Spam-Notification by email', 'antispam-bee' ); ?>
403
  <span><?php esc_html_e( 'Notify admins by e-mail about incoming spam', 'antispam-bee' ); ?></span>
405
  </li>
406
 
407
  <li class="ab_flag_spam_child">
408
+ <input type="checkbox" name="ab_no_notice" id="ab_no_notice" value="1" <?php checked( $options['no_notice'], 1 ); ?> />
409
  <label for="ab_no_notice">
410
  <?php esc_html_e( 'Do not save the spam reason', 'antispam-bee' ); ?>
411
  <span><?php esc_html_e( 'Spam reason as a table column in the spam overview', 'antispam-bee' ); ?></span>
413
  </li>
414
 
415
  <li class="ab_flag_spam_child">
416
+ <input type="checkbox" name="ab_cronjob_enable" id="ab_cronjob_enable" value="1" <?php checked( $options['cronjob_enable'], 1 ); ?> />
417
  <label for="ab_cronjob_enable">
418
+ <?php
419
+ echo sprintf(
420
+ // translators: $s is an input field containing the number of days.
421
  esc_html__( 'Delete existing spam after %s days', 'antispam-bee' ),
422
+ '<input type="number" min="0" name="ab_cronjob_interval" value="' . esc_attr( $options['cronjob_interval'] ) . '" class="ab-mini-field" />'
423
+ )
424
+ ?>
425
+ <span><?php esc_html_e( 'Cleaning up the database from old entries', 'antispam-bee' ); ?></span>
426
  </label>
427
  </li>
428
 
429
  <li class="ab_flag_spam_child">
430
+ <input type="checkbox" name="ab_ignore_filter" id="ab_ignore_filter" value="1" <?php checked( $options['ignore_filter'], 1 ); ?> />
431
  <label for="ab_ignore_filter">
432
+ <?php
433
+ echo sprintf(
434
+ // phpcs:disable WordPress.XSS.EscapeOutput.OutputNotEscaped
435
+ // Output gets escaped in _build_select()
436
+ // translators: %s is the select field.
437
  esc_html__( 'Limit approval to %s', 'antispam-bee' ),
438
  self::_build_select(
439
  'ab_ignore_type',
440
  array(
441
  1 => esc_attr__( 'Comments', 'antispam-bee' ),
442
+ 2 => esc_attr__( 'Pings', 'antispam-bee' ),
443
  ),
444
  $options['ignore_type']
445
  )
446
+ // phpcs:enable _build_select
447
+ );
448
+ ?>
449
  <span><?php esc_html_e( 'Other types of spam will be deleted immediately', 'antispam-bee' ); ?></span>
450
  </label>
451
  </li>
452
 
453
  <li class="ab_flag_spam_child">
454
+ <input type="checkbox" name="ab_reasons_enable" id="ab_reasons_enable" value="1" <?php checked( $options['reasons_enable'], 1 ); ?> />
455
  <label for="ab_reasons_enable">
456
  <?php esc_html_e( 'Delete comments by spam reasons', 'antispam-bee' ); ?>
457
  <span><?php esc_html_e( 'For multiple selections press Ctrl/CMD', 'antispam-bee' ); ?></span>
461
  <li>
462
  <select name="ab_ignore_reasons[]" id="ab_ignore_reasons" size="2" multiple>
463
  <?php foreach ( self::$defaults['reasons'] as $k => $v ) { ?>
464
+ <option <?php selected( in_array( $k, $options['ignore_reasons'], true ), true ); ?> value="<?php echo esc_attr( $k ); ?>"><?php echo esc_html( $v ); ?></option>
465
  <?php } ?>
466
  </select>
467
  <label for="ab_ignore_reasons">
470
  </li>
471
  </ul>
472
  </li>
473
+
474
+ <li class="delete_data_on_uninstall">
475
+ <input type="checkbox" name="delete_data_on_uninstall" id="delete_data_on_uninstall" value="1" <?php checked( $options['delete_data_on_uninstall'], 1 ); ?> />
476
+ <label for="delete_data_on_uninstall">
477
+ <?php esc_html_e( 'Delete Antispam Bee data when uninstalling', 'antispam-bee' ); ?>
478
+ <span><?php esc_html_e( 'If checked, you will delete all data Antispam Bee creates, when uninstalling the plugin.', 'antispam-bee' ); ?></span>
479
+ </label>
480
+ </li>
481
  </ul>
482
+
483
  </div>
484
 
485
 
493
 
494
  <ul>
495
  <li>
496
+ <input type="checkbox" name="ab_dashboard_chart" id="ab_dashboard_chart" value="1" <?php checked( $options['dashboard_chart'], 1 ); ?> />
497
  <label for="ab_dashboard_chart">
498
  <?php esc_html_e( 'Generate statistics as a dashboard widget', 'antispam-bee' ); ?>
499
  <span><?php esc_html_e( 'Daily updates of spam detection rate', 'antispam-bee' ); ?></span>
501
  </li>
502
 
503
  <li>
504
+ <input type="checkbox" name="ab_dashboard_count" id="ab_dashboard_count" value="1" <?php checked( $options['dashboard_count'], 1 ); ?> />
505
  <label for="ab_dashboard_count">
506
  <?php esc_html_e( 'Spam counter on the dashboard', 'antispam-bee' ); ?>
507
  <span><?php esc_html_e( 'Amount of identified spam comments', 'antispam-bee' ); ?></span>
509
  </li>
510
 
511
  <li>
512
+ <input type="checkbox" name="ab_ignore_pings" id="ab_ignore_pings" value="1" <?php checked( $options['ignore_pings'], 1 ); ?> />
513
  <label for="ab_ignore_pings">
514
  <?php esc_html_e( 'Do not check trackbacks / pingbacks', 'antispam-bee' ); ?>
515
  <span><?php esc_html_e( 'No spam check for link notifications', 'antispam-bee' ); ?></span>
517
  </li>
518
 
519
  <li>
520
+ <input type="checkbox" name="ab_always_allowed" id="ab_always_allowed" value="1" <?php checked( $options['always_allowed'], 1 ); ?> />
521
  <label for="ab_always_allowed">
522
  <?php esc_html_e( 'Comment form used outside of posts', 'antispam-bee' ); ?>
523
  <span><?php esc_html_e( 'Check for comment forms on archive pages', 'antispam-bee' ); ?></span>
531
  <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Donate', 'antispam-bee' ); ?></a>
532
  </p>
533
  <p>
534
+ <a href="<?php echo esc_url( __( 'https://wordpress.org/plugins/antispam-bee/faq/', 'antispam-bee' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'FAQ', 'antispam-bee' ); ?></a>
535
  </p>
536
  <p>
537
+ <a href="<?php echo esc_url( __( 'https://github.com/pluginkollektiv/antispam-bee/wiki/', 'antispam-bee' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Manual', 'antispam-bee' ); ?></a>
538
  </p>
539
  <p>
540
+ <a href="<?php echo esc_url( __( 'https://wordpress.org/support/plugin/antispam-bee', 'antispam-bee' ) ); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Support', 'antispam-bee' ); ?></a>
541
  </p>
542
 
543
  <input type="submit" class="button button-primary" value="<?php esc_html_e( 'Save Changes', 'antispam-bee' ); ?>" />
545
  </div>
546
  </form>
547
  </div>
548
+ <?php
549
+ }
550
 
551
  /**
552
  * Get the languages, which are selectable to restrict the comment language to.
js/raphael.min.js CHANGED
@@ -1,10 +1,3 @@
1
- // ┌────────────────────────────────────────────────────────────────────┐ \\
2
- // │ Raphaël 2.1.0 - JavaScript Vector Library │ \\
3
- // ├────────────────────────────────────────────────────────────────────┤ \\
4
- // │ Copyright © 2008-2012 Dmitry Baranovskiy (http://raphaeljs.com) │ \\
5
- // │ Copyright © 2008-2012 Sencha Labs (http://sencha.com) │ \\
6
- // ├────────────────────────────────────────────────────────────────────┤ \\
7
- // │ Licensed under the MIT (http://raphaeljs.com/license.html) license.│ \\
8
- // └────────────────────────────────────────────────────────────────────┘ \\
9
-
10
- (function(a){var b="0.3.4",c="hasOwnProperty",d=/[\.\/]/,e="*",f=function(){},g=function(a,b){return a-b},h,i,j={n:{}},k=function(a,b){var c=j,d=i,e=Array.prototype.slice.call(arguments,2),f=k.listeners(a),l=0,m=!1,n,o=[],p={},q=[],r=h,s=[];h=a,i=0;for(var t=0,u=f.length;t<u;t++)"zIndex"in f[t]&&(o.push(f[t].zIndex),f[t].zIndex<0&&(p[f[t].zIndex]=f[t]));o.sort(g);while(o[l]<0){n=p[o[l++]],q.push(n.apply(b,e));if(i){i=d;return q}}for(t=0;t<u;t++){n=f[t];if("zIndex"in n)if(n.zIndex==o[l]){q.push(n.apply(b,e));if(i)break;do{l++,n=p[o[l]],n&&q.push(n.apply(b,e));if(i)break}while(n)}else p[n.zIndex]=n;else{q.push(n.apply(b,e));if(i)break}}i=d,h=r;return q.length?q:null};k.listeners=function(a){var b=a.split(d),c=j,f,g,h,i,k,l,m,n,o=[c],p=[];for(i=0,k=b.length;i<k;i++){n=[];for(l=0,m=o.length;l<m;l++){c=o[l].n,g=[c[b[i]],c[e]],h=2;while(h--)f=g[h],f&&(n.push(f),p=p.concat(f.f||[]))}o=n}return p},k.on=function(a,b){var c=a.split(d),e=j;for(var g=0,h=c.length;g<h;g++)e=e.n,!e[c[g]]&&(e[c[g]]={n:{}}),e=e[c[g]];e.f=e.f||[];for(g=0,h=e.f.length;g<h;g++)if(e.f[g]==b)return f;e.f.push(b);return function(a){+a==+a&&(b.zIndex=+a)}},k.stop=function(){i=1},k.nt=function(a){if(a)return(new RegExp("(?:\\.|\\/|^)"+a+"(?:\\.|\\/|$)")).test(h);return h},k.off=k.unbind=function(a,b){var f=a.split(d),g,h,i,k,l,m,n,o=[j];for(k=0,l=f.length;k<l;k++)for(m=0;m<o.length;m+=i.length-2){i=[m,1],g=o[m].n;if(f[k]!=e)g[f[k]]&&i.push(g[f[k]]);else for(h in g)g[c](h)&&i.push(g[h]);o.splice.apply(o,i)}for(k=0,l=o.length;k<l;k++){g=o[k];while(g.n){if(b){if(g.f){for(m=0,n=g.f.length;m<n;m++)if(g.f[m]==b){g.f.splice(m,1);break}!g.f.length&&delete g.f}for(h in g.n)if(g.n[c](h)&&g.n[h].f){var p=g.n[h].f;for(m=0,n=p.length;m<n;m++)if(p[m]==b){p.splice(m,1);break}!p.length&&delete g.n[h].f}}else{delete g.f;for(h in g.n)g.n[c](h)&&g.n[h].f&&delete g.n[h].f}g=g.n}}},k.once=function(a,b){var c=function(){var d=b.apply(this,arguments);k.unbind(a,c);return d};return k.on(a,c)},k.version=b,k.toString=function(){return"You are running Eve "+b},typeof module!="undefined"&&module.exports?module.exports=k:typeof define!="undefined"?define("eve",[],function(){return k}):a.eve=k})(this),function(){function cF(a){for(var b=0;b<cy.length;b++)cy[b].el.paper==a&&cy.splice(b--,1)}function cE(b,d,e,f,h,i){e=Q(e);var j,k,l,m=[],o,p,q,t=b.ms,u={},v={},w={};if(f)for(y=0,z=cy.length;y<z;y++){var x=cy[y];if(x.el.id==d.id&&x.anim==b){x.percent!=e?(cy.splice(y,1),l=1):k=x,d.attr(x.totalOrigin);break}}else f=+v;for(var y=0,z=b.percents.length;y<z;y++){if(b.percents[y]==e||b.percents[y]>f*b.top){e=b.percents[y],p=b.percents[y-1]||0,t=t/b.top*(e-p),o=b.percents[y+1],j=b.anim[e];break}f&&d.attr(b.anim[b.percents[y]])}if(!!j){if(!k){for(var A in j)if(j[g](A))if(U[g](A)||d.paper.customAttributes[g](A)){u[A]=d.attr(A),u[A]==null&&(u[A]=T[A]),v[A]=j[A];switch(U[A]){case C:w[A]=(v[A]-u[A])/t;break;case"colour":u[A]=a.getRGB(u[A]);var B=a.getRGB(v[A]);w[A]={r:(B.r-u[A].r)/t,g:(B.g-u[A].g)/t,b:(B.b-u[A].b)/t};break;case"path":var D=bR(u[A],v[A]),E=D[1];u[A]=D[0],w[A]=[];for(y=0,z=u[A].length;y<z;y++){w[A][y]=[0];for(var F=1,G=u[A][y].length;F<G;F++)w[A][y][F]=(E[y][F]-u[A][y][F])/t}break;case"transform":var H=d._,I=ca(H[A],v[A]);if(I){u[A]=I.from,v[A]=I.to,w[A]=[],w[A].real=!0;for(y=0,z=u[A].length;y<z;y++){w[A][y]=[u[A][y][0]];for(F=1,G=u[A][y].length;F<G;F++)w[A][y][F]=(v[A][y][F]-u[A][y][F])/t}}else{var J=d.matrix||new cb,K={_:{transform:H.transform},getBBox:function(){return d.getBBox(1)}};u[A]=[J.a,J.b,J.c,J.d,J.e,J.f],b$(K,v[A]),v[A]=K._.transform,w[A]=[(K.matrix.a-J.a)/t,(K.matrix.b-J.b)/t,(K.matrix.c-J.c)/t,(K.matrix.d-J.d)/t,(K.matrix.e-J.e)/t,(K.matrix.f-J.f)/t]}break;case"csv":var L=r(j[A])[s](c),M=r(u[A])[s](c);if(A=="clip-rect"){u[A]=M,w[A]=[],y=M.length;while(y--)w[A][y]=(L[y]-u[A][y])/t}v[A]=L;break;default:L=[][n](j[A]),M=[][n](u[A]),w[A]=[],y=d.paper.customAttributes[A].length;while(y--)w[A][y]=((L[y]||0)-(M[y]||0))/t}}var O=j.easing,P=a.easing_formulas[O];if(!P){P=r(O).match(N);if(P&&P.length==5){var R=P;P=function(a){return cC(a,+R[1],+R[2],+R[3],+R[4],t)}}else P=bf}q=j.start||b.start||+(new Date),x={anim:b,percent:e,timestamp:q,start:q+(b.del||0),status:0,initstatus:f||0,stop:!1,ms:t,easing:P,from:u,diff:w,to:v,el:d,callback:j.callback,prev:p,next:o,repeat:i||b.times,origin:d.attr(),totalOrigin:h},cy.push(x);if(f&&!k&&!l){x.stop=!0,x.start=new Date-t*f;if(cy.length==1)return cA()}l&&(x.start=new Date-x.ms*f),cy.length==1&&cz(cA)}else k.initstatus=f,k.start=new Date-k.ms*f;eve("raphael.anim.start."+d.id,d,b)}}function cD(a,b){var c=[],d={};this.ms=b,this.times=1;if(a){for(var e in a)a[g](e)&&(d[Q(e)]=a[e],c.push(Q(e)));c.sort(bd)}this.anim=d,this.top=c[c.length-1],this.percents=c}function cC(a,b,c,d,e,f){function o(a,b){var c,d,e,f,j,k;for(e=a,k=0;k<8;k++){f=m(e)-a;if(z(f)<b)return e;j=(3*i*e+2*h)*e+g;if(z(j)<1e-6)break;e=e-f/j}c=0,d=1,e=a;if(e<c)return c;if(e>d)return d;while(c<d){f=m(e);if(z(f-a)<b)return e;a>f?c=e:d=e,e=(d-c)/2+c}return e}function n(a,b){var c=o(a,b);return((l*c+k)*c+j)*c}function m(a){return((i*a+h)*a+g)*a}var g=3*b,h=3*(d-b)-g,i=1-g-h,j=3*c,k=3*(e-c)-j,l=1-j-k;return n(a,1/(200*f))}function cq(){return this.x+q+this.y+q+this.width+" × "+this.height}function cp(){return this.x+q+this.y}function cb(a,b,c,d,e,f){a!=null?(this.a=+a,this.b=+b,this.c=+c,this.d=+d,this.e=+e,this.f=+f):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function bH(b,c,d){b=a._path2curve(b),c=a._path2curve(c);var e,f,g,h,i,j,k,l,m,n,o=d?0:[];for(var p=0,q=b.length;p<q;p++){var r=b[p];if(r[0]=="M")e=i=r[1],f=j=r[2];else{r[0]=="C"?(m=[e,f].concat(r.slice(1)),e=m[6],f=m[7]):(m=[e,f,e,f,i,j,i,j],e=i,f=j);for(var s=0,t=c.length;s<t;s++){var u=c[s];if(u[0]=="M")g=k=u[1],h=l=u[2];else{u[0]=="C"?(n=[g,h].concat(u.slice(1)),g=n[6],h=n[7]):(n=[g,h,g,h,k,l,k,l],g=k,h=l);var v=bG(m,n,d);if(d)o+=v;else{for(var w=0,x=v.length;w<x;w++)v[w].segment1=p,v[w].segment2=s,v[w].bez1=m,v[w].bez2=n;o=o.concat(v)}}}}}return o}function bG(b,c,d){var e=a.bezierBBox(b),f=a.bezierBBox(c);if(!a.isBBoxIntersect(e,f))return d?0:[];var g=bB.apply(0,b),h=bB.apply(0,c),i=~~(g/5),j=~~(h/5),k=[],l=[],m={},n=d?0:[];for(var o=0;o<i+1;o++){var p=a.findDotsAtSegment.apply(a,b.concat(o/i));k.push({x:p.x,y:p.y,t:o/i})}for(o=0;o<j+1;o++)p=a.findDotsAtSegment.apply(a,c.concat(o/j)),l.push({x:p.x,y:p.y,t:o/j});for(o=0;o<i;o++)for(var q=0;q<j;q++){var r=k[o],s=k[o+1],t=l[q],u=l[q+1],v=z(s.x-r.x)<.001?"y":"x",w=z(u.x-t.x)<.001?"y":"x",x=bD(r.x,r.y,s.x,s.y,t.x,t.y,u.x,u.y);if(x){if(m[x.x.toFixed(4)]==x.y.toFixed(4))continue;m[x.x.toFixed(4)]=x.y.toFixed(4);var y=r.t+z((x[v]-r[v])/(s[v]-r[v]))*(s.t-r.t),A=t.t+z((x[w]-t[w])/(u[w]-t[w]))*(u.t-t.t);y>=0&&y<=1&&A>=0&&A<=1&&(d?n++:n.push({x:x.x,y:x.y,t1:y,t2:A}))}}return n}function bF(a,b){return bG(a,b,1)}function bE(a,b){return bG(a,b)}function bD(a,b,c,d,e,f,g,h){if(!(x(a,c)<y(e,g)||y(a,c)>x(e,g)||x(b,d)<y(f,h)||y(b,d)>x(f,h))){var i=(a*d-b*c)*(e-g)-(a-c)*(e*h-f*g),j=(a*d-b*c)*(f-h)-(b-d)*(e*h-f*g),k=(a-c)*(f-h)-(b-d)*(e-g);if(!k)return;var l=i/k,m=j/k,n=+l.toFixed(2),o=+m.toFixed(2);if(n<+y(a,c).toFixed(2)||n>+x(a,c).toFixed(2)||n<+y(e,g).toFixed(2)||n>+x(e,g).toFixed(2)||o<+y(b,d).toFixed(2)||o>+x(b,d).toFixed(2)||o<+y(f,h).toFixed(2)||o>+x(f,h).toFixed(2))return;return{x:l,y:m}}}function bC(a,b,c,d,e,f,g,h,i){if(!(i<0||bB(a,b,c,d,e,f,g,h)<i)){var j=1,k=j/2,l=j-k,m,n=.01;m=bB(a,b,c,d,e,f,g,h,l);while(z(m-i)>n)k/=2,l+=(m<i?1:-1)*k,m=bB(a,b,c,d,e,f,g,h,l);return l}}function bB(a,b,c,d,e,f,g,h,i){i==null&&(i=1),i=i>1?1:i<0?0:i;var j=i/2,k=12,l=[-0.1252,.1252,-0.3678,.3678,-0.5873,.5873,-0.7699,.7699,-0.9041,.9041,-0.9816,.9816],m=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],n=0;for(var o=0;o<k;o++){var p=j*l[o]+j,q=bA(p,a,c,e,g),r=bA(p,b,d,f,h),s=q*q+r*r;n+=m[o]*w.sqrt(s)}return j*n}function bA(a,b,c,d,e){var f=-3*b+9*c-9*d+3*e,g=a*f+6*b-12*c+6*d;return a*g-3*b+3*c}function by(a,b){var c=[];for(var d=0,e=a.length;e-2*!b>d;d+=2){var f=[{x:+a[d-2],y:+a[d-1]},{x:+a[d],y:+a[d+1]},{x:+a[d+2],y:+a[d+3]},{x:+a[d+4],y:+a[d+5]}];b?d?e-4==d?f[3]={x:+a[0],y:+a[1]}:e-2==d&&(f[2]={x:+a[0],y:+a[1]},f[3]={x:+a[2],y:+a[3]}):f[0]={x:+a[e-2],y:+a[e-1]}:e-4==d?f[3]=f[2]:d||(f[0]={x:+a[d],y:+a[d+1]}),c.push(["C",(-f[0].x+6*f[1].x+f[2].x)/6,(-f[0].y+6*f[1].y+f[2].y)/6,(f[1].x+6*f[2].x-f[3].x)/6,(f[1].y+6*f[2].y-f[3].y)/6,f[2].x,f[2].y])}return c}function bx(){return this.hex}function bv(a,b,c){function d(){var e=Array.prototype.slice.call(arguments,0),f=e.join("␀"),h=d.cache=d.cache||{},i=d.count=d.count||[];if(h[g](f)){bu(i,f);return c?c(h[f]):h[f]}i.length>=1e3&&delete h[i.shift()],i.push(f),h[f]=a[m](b,e);return c?c(h[f]):h[f]}return d}function bu(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return a.push(a.splice(c,1)[0])}function bm(a){if(Object(a)!==a)return a;var b=new a.constructor;for(var c in a)a[g](c)&&(b[c]=bm(a[c]));return b}function a(c){if(a.is(c,"function"))return b?c():eve.on("raphael.DOMload",c);if(a.is(c,E))return a._engine.create[m](a,c.splice(0,3+a.is(c[0],C))).add(c);var d=Array.prototype.slice.call(arguments,0);if(a.is(d[d.length-1],"function")){var e=d.pop();return b?e.call(a._engine.create[m](a,d)):eve.on("raphael.DOMload",function(){e.call(a._engine.create[m](a,d))})}return a._engine.create[m](a,arguments)}a.version="2.1.0",a.eve=eve;var b,c=/[, ]+/,d={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},e=/\{(\d+)\}/g,f="prototype",g="hasOwnProperty",h={doc:document,win:window},i={was:Object.prototype[g].call(h.win,"Raphael"),is:h.win.Raphael},j=function(){this.ca=this.customAttributes={}},k,l="appendChild",m="apply",n="concat",o="createTouch"in h.doc,p="",q=" ",r=String,s="split",t="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[s](q),u={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},v=r.prototype.toLowerCase,w=Math,x=w.max,y=w.min,z=w.abs,A=w.pow,B=w.PI,C="number",D="string",E="array",F="toString",G="fill",H=Object.prototype.toString,I={},J="push",K=a._ISURL=/^url\(['"]?([^\)]+?)['"]?\)$/i,L=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,M={NaN:1,Infinity:1,"-Infinity":1},N=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,O=w.round,P="setAttribute",Q=parseFloat,R=parseInt,S=r.prototype.toUpperCase,T=a._availableAttrs={"arrow-end":"none","arrow-start":"none",blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/","letter-spacing":0,opacity:1,path:"M0,0",r:0,rx:0,ry:0,src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",transform:"",width:0,x:0,y:0},U=a._availableAnimAttrs={blur:C,"clip-rect":"csv",cx:C,cy:C,fill:"colour","fill-opacity":C,"font-size":C,height:C,opacity:C,path:"path",r:C,rx:C,ry:C,stroke:"colour","stroke-opacity":C,"stroke-width":C,transform:"transform",width:C,x:C,y:C},V=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,W=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,X={hs:1,rg:1},Y=/,?([achlmqrstvxz]),?/gi,Z=/([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,$=/([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/ig,_=/(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/ig,ba=a._radial_gradient=/^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,bb={},bc=function(a,b){return a.key-b.key},bd=function(a,b){return Q(a)-Q(b)},be=function(){},bf=function(a){return a},bg=a._rectPath=function(a,b,c,d,e){if(e)return[["M",a+e,b],["l",c-e*2,0],["a",e,e,0,0,1,e,e],["l",0,d-e*2],["a",e,e,0,0,1,-e,e],["l",e*2-c,0],["a",e,e,0,0,1,-e,-e],["l",0,e*2-d],["a",e,e,0,0,1,e,-e],["z"]];return[["M",a,b],["l",c,0],["l",0,d],["l",-c,0],["z"]]},bh=function(a,b,c,d){d==null&&(d=c);return[["M",a,b],["m",0,-d],["a",c,d,0,1,1,0,2*d],["a",c,d,0,1,1,0,-2*d],["z"]]},bi=a._getPath={path:function(a){return a.attr("path")},circle:function(a){var b=a.attrs;return bh(b.cx,b.cy,b.r)},ellipse:function(a){var b=a.attrs;return bh(b.cx,b.cy,b.rx,b.ry)},rect:function(a){var b=a.attrs;return bg(b.x,b.y,b.width,b.height,b.r)},image:function(a){var b=a.attrs;return bg(b.x,b.y,b.width,b.height)},text:function(a){var b=a._getBBox();return bg(b.x,b.y,b.width,b.height)}},bj=a.mapPath=function(a,b){if(!b)return a;var c,d,e,f,g,h,i;a=bR(a);for(e=0,g=a.length;e<g;e++){i=a[e];for(f=1,h=i.length;f<h;f+=2)c=b.x(i[f],i[f+1]),d=b.y(i[f],i[f+1]),i[f]=c,i[f+1]=d}return a};a._g=h,a.type=h.win.SVGAngle||h.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML";if(a.type=="VML"){var bk=h.doc.createElement("div"),bl;bk.innerHTML='<v:shape adj="1"/>',bl=bk.firstChild,bl.style.behavior="url(#default#VML)";if(!bl||typeof bl.adj!="object")return a.type=p;bk=null}a.svg=!(a.vml=a.type=="VML"),a._Paper=j,a.fn=k=j.prototype=a.prototype,a._id=0,a._oid=0,a.is=function(a,b){b=v.call(b);if(b=="finite")return!M[g](+a);if(b=="array")return a instanceof Array;return b=="null"&&a===null||b==typeof a&&a!==null||b=="object"&&a===Object(a)||b=="array"&&Array.isArray&&Array.isArray(a)||H.call(a).slice(8,-1).toLowerCase()==b},a.angle=function(b,c,d,e,f,g){if(f==null){var h=b-d,i=c-e;if(!h&&!i)return 0;return(180+w.atan2(-i,-h)*180/B+360)%360}return a.angle(b,c,f,g)-a.angle(d,e,f,g)},a.rad=function(a){return a%360*B/180},a.deg=function(a){return a*180/B%360},a.snapTo=function(b,c,d){d=a.is(d,"finite")?d:10;if(a.is(b,E)){var e=b.length;while(e--)if(z(b[e]-c)<=d)return b[e]}else{b=+b;var f=c%b;if(f<d)return c-f;if(f>b-d)return c-f+b}return c};var bn=a.createUUID=function(a,b){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(a,b).toUpperCase()}}(/[xy]/g,function(a){var b=w.random()*16|0,c=a=="x"?b:b&3|8;return c.toString(16)});a.setWindow=function(b){eve("raphael.setWindow",a,h.win,b),h.win=b,h.doc=h.win.document,a._engine.initWin&&a._engine.initWin(h.win)};var bo=function(b){if(a.vml){var c=/^\s+|\s+$/g,d;try{var e=new ActiveXObject("htmlfile");e.write("<body>"),e.close(),d=e.body}catch(f){d=createPopup().document.body}var g=d.createTextRange();bo=bv(function(a){try{d.style.color=r(a).replace(c,p);var b=g.queryCommandValue("ForeColor");b=(b&255)<<16|b&65280|(b&16711680)>>>16;return"#"+("000000"+b.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=h.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",h.doc.body.appendChild(i),bo=bv(function(a){i.style.color=a;return h.doc.defaultView.getComputedStyle(i,p).getPropertyValue("color")})}return bo(b)},bp=function(){return"hsb("+[this.h,this.s,this.b]+")"},bq=function(){return"hsl("+[this.h,this.s,this.l]+")"},br=function(){return this.hex},bs=function(b,c,d){c==null&&a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b&&(d=b.b,c=b.g,b=b.r);if(c==null&&a.is(b,D)){var e=a.getRGB(b);b=e.r,c=e.g,d=e.b}if(b>1||c>1||d>1)b/=255,c/=255,d/=255;return[b,c,d]},bt=function(b,c,d,e){b*=255,c*=255,d*=255;var f={r:b,g:c,b:d,hex:a.rgb(b,c,d),toString:br};a.is(e,"finite")&&(f.opacity=e);return f};a.color=function(b){var c;a.is(b,"object")&&"h"in b&&"s"in b&&"b"in b?(c=a.hsb2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):a.is(b,"object")&&"h"in b&&"s"in b&&"l"in b?(c=a.hsl2rgb(b),b.r=c.r,b.g=c.g,b.b=c.b,b.hex=c.hex):(a.is(b,"string")&&(b=a.getRGB(b)),a.is(b,"object")&&"r"in b&&"g"in b&&"b"in b?(c=a.rgb2hsl(b),b.h=c.h,b.s=c.s,b.l=c.l,c=a.rgb2hsb(b),b.v=c.b):(b={hex:"none"},b.r=b.g=b.b=b.h=b.s=b.v=b.l=-1)),b.toString=br;return b},a.hsb2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"b"in a&&(c=a.b,b=a.s,a=a.h,d=a.o),a*=360;var e,f,g,h,i;a=a%360/60,i=c*b,h=i*(1-z(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.hsl2rgb=function(a,b,c,d){this.is(a,"object")&&"h"in a&&"s"in a&&"l"in a&&(c=a.l,b=a.s,a=a.h);if(a>1||b>1||c>1)a/=360,b/=100,c/=100;a*=360;var e,f,g,h,i;a=a%360/60,i=2*b*(c<.5?c:1-c),h=i*(1-z(a%2-1)),e=f=g=c-i/2,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a];return bt(e,f,g,d)},a.rgb2hsb=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g;f=x(a,b,c),g=f-y(a,b,c),d=g==0?null:f==a?(b-c)/g:f==b?(c-a)/g+2:(a-b)/g+4,d=(d+360)%6*60/360,e=g==0?0:g/f;return{h:d,s:e,b:f,toString:bp}},a.rgb2hsl=function(a,b,c){c=bs(a,b,c),a=c[0],b=c[1],c=c[2];var d,e,f,g,h,i;g=x(a,b,c),h=y(a,b,c),i=g-h,d=i==0?null:g==a?(b-c)/i:g==b?(c-a)/i+2:(a-b)/i+4,d=(d+360)%6*60/360,f=(g+h)/2,e=i==0?0:f<.5?i/(2*f):i/(2-2*f);return{h:d,s:e,l:f,toString:bq}},a._path2string=function(){return this.join(",").replace(Y,"$1")};var bw=a._preload=function(a,b){var c=h.doc.createElement("img");c.style.cssText="position:absolute;left:-9999em;top:-9999em",c.onload=function(){b.call(this),this.onload=null,h.doc.body.removeChild(this)},c.onerror=function(){h.doc.body.removeChild(this)},h.doc.body.appendChild(c),c.src=a};a.getRGB=bv(function(b){if(!b||!!((b=r(b)).indexOf("-")+1))return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx};if(b=="none")return{r:-1,g:-1,b:-1,hex:"none",toString:bx};!X[g](b.toLowerCase().substring(0,2))&&b.charAt()!="#"&&(b=bo(b));var c,d,e,f,h,i,j,k=b.match(L);if(k){k[2]&&(f=R(k[2].substring(5),16),e=R(k[2].substring(3,5),16),d=R(k[2].substring(1,3),16)),k[3]&&(f=R((i=k[3].charAt(3))+i,16),e=R((i=k[3].charAt(2))+i,16),d=R((i=k[3].charAt(1))+i,16)),k[4]&&(j=k[4][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),k[1].toLowerCase().slice(0,4)=="rgba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100));if(k[5]){j=k[5][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsba"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsb2rgb(d,e,f,h)}if(k[6]){j=k[6][s](W),d=Q(j[0]),j[0].slice(-1)=="%"&&(d*=2.55),e=Q(j[1]),j[1].slice(-1)=="%"&&(e*=2.55),f=Q(j[2]),j[2].slice(-1)=="%"&&(f*=2.55),(j[0].slice(-3)=="deg"||j[0].slice(-1)=="°")&&(d/=360),k[1].toLowerCase().slice(0,4)=="hsla"&&(h=Q(j[3])),j[3]&&j[3].slice(-1)=="%"&&(h/=100);return a.hsl2rgb(d,e,f,h)}k={r:d,g:e,b:f,toString:bx},k.hex="#"+(16777216|f|e<<8|d<<16).toString(16).slice(1),a.is(h,"finite")&&(k.opacity=h);return k}return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:bx}},a),a.hsb=bv(function(b,c,d){return a.hsb2rgb(b,c,d).hex}),a.hsl=bv(function(b,c,d){return a.hsl2rgb(b,c,d).hex}),a.rgb=bv(function(a,b,c){return"#"+(16777216|c|b<<8|a<<16).toString(16).slice(1)}),a.getColor=function(a){var b=this.getColor.start=this.getColor.start||{h:0,s:1,b:a||.75},c=this.hsb2rgb(b.h,b.s,b.b);b.h+=.075,b.h>1&&(b.h=0,b.s-=.2,b.s<=0&&(this.getColor.start={h:0,s:1,b:b.b}));return c.hex},a.getColor.reset=function(){delete this.start},a.parsePathString=function(b){if(!b)return null;var c=bz(b);if(c.arr)return bJ(c.arr);var d={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},e=[];a.is(b,E)&&a.is(b[0],E)&&(e=bJ(b)),e.length||r(b).replace(Z,function(a,b,c){var f=[],g=b.toLowerCase();c.replace(_,function(a,b){b&&f.push(+b)}),g=="m"&&f.length>2&&(e.push([b][n](f.splice(0,2))),g="l",b=b=="m"?"l":"L");if(g=="r")e.push([b][n](f));else while(f.length>=d[g]){e.push([b][n](f.splice(0,d[g])));if(!d[g])break}}),e.toString=a._path2string,c.arr=bJ(e);return e},a.parseTransformString=bv(function(b){if(!b)return null;var c={r:3,s:4,t:2,m:6},d=[];a.is(b,E)&&a.is(b[0],E)&&(d=bJ(b)),d.length||r(b).replace($,function(a,b,c){var e=[],f=v.call(b);c.replace(_,function(a,b){b&&e.push(+b)}),d.push([b][n](e))}),d.toString=a._path2string;return d});var bz=function(a){var b=bz.ps=bz.ps||{};b[a]?b[a].sleep=100:b[a]={sleep:100},setTimeout(function(){for(var c in b)b[g](c)&&c!=a&&(b[c].sleep--,!b[c].sleep&&delete b[c])});return b[a]};a.findDotsAtSegment=function(a,b,c,d,e,f,g,h,i){var j=1-i,k=A(j,3),l=A(j,2),m=i*i,n=m*i,o=k*a+l*3*i*c+j*3*i*i*e+n*g,p=k*b+l*3*i*d+j*3*i*i*f+n*h,q=a+2*i*(c-a)+m*(e-2*c+a),r=b+2*i*(d-b)+m*(f-2*d+b),s=c+2*i*(e-c)+m*(g-2*e+c),t=d+2*i*(f-d)+m*(h-2*f+d),u=j*a+i*c,v=j*b+i*d,x=j*e+i*g,y=j*f+i*h,z=90-w.atan2(q-s,r-t)*180/B;(q>s||r<t)&&(z+=180);return{x:o,y:p,m:{x:q,y:r},n:{x:s,y:t},start:{x:u,y:v},end:{x:x,y:y},alpha:z}},a.bezierBBox=function(b,c,d,e,f,g,h,i){a.is(b,"array")||(b=[b,c,d,e,f,g,h,i]);var j=bQ.apply(null,b);return{x:j.min.x,y:j.min.y,x2:j.max.x,y2:j.max.y,width:j.max.x-j.min.x,height:j.max.y-j.min.y}},a.isPointInsideBBox=function(a,b,c){return b>=a.x&&b<=a.x2&&c>=a.y&&c<=a.y2},a.isBBoxIntersect=function(b,c){var d=a.isPointInsideBBox;return d(c,b.x,b.y)||d(c,b.x2,b.y)||d(c,b.x,b.y2)||d(c,b.x2,b.y2)||d(b,c.x,c.y)||d(b,c.x2,c.y)||d(b,c.x,c.y2)||d(b,c.x2,c.y2)||(b.x<c.x2&&b.x>c.x||c.x<b.x2&&c.x>b.x)&&(b.y<c.y2&&b.y>c.y||c.y<b.y2&&c.y>b.y)},a.pathIntersection=function(a,b){return bH(a,b)},a.pathIntersectionNumber=function(a,b){return bH(a,b,1)},a.isPointInsidePath=function(b,c,d){var e=a.pathBBox(b);return a.isPointInsideBBox(e,c,d)&&bH(b,[["M",c,d],["H",e.x2+10]],1)%2==1},a._removedFactory=function(a){return function(){eve("raphael.log",null,"Raphaël: you are calling to method “"+a+"” of removed object",a)}};var bI=a.pathBBox=function(a){var b=bz(a);if(b.bbox)return b.bbox;if(!a)return{x:0,y:0,width:0,height:0,x2:0,y2:0};a=bR(a);var c=0,d=0,e=[],f=[],g;for(var h=0,i=a.length;h<i;h++){g=a[h];if(g[0]=="M")c=g[1],d=g[2],e.push(c),f.push(d);else{var j=bQ(c,d,g[1],g[2],g[3],g[4],g[5],g[6]);e=e[n](j.min.x,j.max.x),f=f[n](j.min.y,j.max.y),c=g[5],d=g[6]}}var k=y[m](0,e),l=y[m](0,f),o=x[m](0,e),p=x[m](0,f),q={x:k,y:l,x2:o,y2:p,width:o-k,height:p-l};b.bbox=bm(q);return q},bJ=function(b){var c=bm(b);c.toString=a._path2string;return c},bK=a._pathToRelative=function(b){var c=bz(b);if(c.rel)return bJ(c.rel);if(!a.is(b,E)||!a.is(b&&b[0],E))b=a.parsePathString(b);var d=[],e=0,f=0,g=0,h=0,i=0;b[0][0]=="M"&&(e=b[0][1],f=b[0][2],g=e,h=f,i++,d.push(["M",e,f]));for(var j=i,k=b.length;j<k;j++){var l=d[j]=[],m=b[j];if(m[0]!=v.call(m[0])){l[0]=v.call(m[0]);switch(l[0]){case"a":l[1]=m[1],l[2]=m[2],l[3]=m[3],l[4]=m[4],l[5]=m[5],l[6]=+(m[6]-e).toFixed(3),l[7]=+(m[7]-f).toFixed(3);break;case"v":l[1]=+(m[1]-f).toFixed(3);break;case"m":g=m[1],h=m[2];default:for(var n=1,o=m.length;n<o;n++)l[n]=+(m[n]-(n%2?e:f)).toFixed(3)}}else{l=d[j]=[],m[0]=="m"&&(g=m[1]+e,h=m[2]+f);for(var p=0,q=m.length;p<q;p++)d[j][p]=m[p]}var r=d[j].length;switch(d[j][0]){case"z":e=g,f=h;break;case"h":e+=+d[j][r-1];break;case"v":f+=+d[j][r-1];break;default:e+=+d[j][r-2],f+=+d[j][r-1]}}d.toString=a._path2string,c.rel=bJ(d);return d},bL=a._pathToAbsolute=function(b){var c=bz(b);if(c.abs)return bJ(c.abs);if(!a.is(b,E)||!a.is(b&&b[0],E))b=a.parsePathString(b);if(!b||!b.length)return[["M",0,0]];var d=[],e=0,f=0,g=0,h=0,i=0;b[0][0]=="M"&&(e=+b[0][1],f=+b[0][2],g=e,h=f,i++,d[0]=["M",e,f]);var j=b.length==3&&b[0][0]=="M"&&b[1][0].toUpperCase()=="R"&&b[2][0].toUpperCase()=="Z";for(var k,l,m=i,o=b.length;m<o;m++){d.push(k=[]),l=b[m];if(l[0]!=S.call(l[0])){k[0]=S.call(l[0]);switch(k[0]){case"A":k[1]=l[1],k[2]=l[2],k[3]=l[3],k[4]=l[4],k[5]=l[5],k[6]=+(l[6]+e),k[7]=+(l[7]+f);break;case"V":k[1]=+l[1]+f;break;case"H":k[1]=+l[1]+e;break;case"R":var p=[e,f][n](l.slice(1));for(var q=2,r=p.length;q<r;q++)p[q]=+p[q]+e,p[++q]=+p[q]+f;d.pop(),d=d[n](by(p,j));break;case"M":g=+l[1]+e,h=+l[2]+f;default:for(q=1,r=l.length;q<r;q++)k[q]=+l[q]+(q%2?e:f)}}else if(l[0]=="R")p=[e,f][n](l.slice(1)),d.pop(),d=d[n](by(p,j)),k=["R"][n](l.slice(-2));else for(var s=0,t=l.length;s<t;s++)k[s]=l[s];switch(k[0]){case"Z":e=g,f=h;break;case"H":e=k[1];break;case"V":f=k[1];break;case"M":g=k[k.length-2],h=k[k.length-1];default:e=k[k.length-2],f=k[k.length-1]}}d.toString=a._path2string,c.abs=bJ(d);return d},bM=function(a,b,c,d){return[a,b,c,d,c,d]},bN=function(a,b,c,d,e,f){var g=1/3,h=2/3;return[g*a+h*c,g*b+h*d,g*e+h*c,g*f+h*d,e,f]},bO=function(a,b,c,d,e,f,g,h,i,j){var k=B*120/180,l=B/180*(+e||0),m=[],o,p=bv(function(a,b,c){var d=a*w.cos(c)-b*w.sin(c),e=a*w.sin(c)+b*w.cos(c);return{x:d,y:e}});if(!j){o=p(a,b,-l),a=o.x,b=o.y,o=p(h,i,-l),h=o.x,i=o.y;var q=w.cos(B/180*e),r=w.sin(B/180*e),t=(a-h)/2,u=(b-i)/2,v=t*t/(c*c)+u*u/(d*d);v>1&&(v=w.sqrt(v),c=v*c,d=v*d);var x=c*c,y=d*d,A=(f==g?-1:1)*w.sqrt(z((x*y-x*u*u-y*t*t)/(x*u*u+y*t*t))),C=A*c*u/d+(a+h)/2,D=A*-d*t/c+(b+i)/2,E=w.asin(((b-D)/d).toFixed(9)),F=w.asin(((i-D)/d).toFixed(9));E=a<C?B-E:E,F=h<C?B-F:F,E<0&&(E=B*2+E),F<0&&(F=B*2+F),g&&E>F&&(E=E-B*2),!g&&F>E&&(F=F-B*2)}else E=j[0],F=j[1],C=j[2],D=j[3];var G=F-E;if(z(G)>k){var H=F,I=h,J=i;F=E+k*(g&&F>E?1:-1),h=C+c*w.cos(F),i=D+d*w.sin(F),m=bO(h,i,c,d,e,0,g,I,J,[F,H,C,D])}G=F-E;var K=w.cos(E),L=w.sin(E),M=w.cos(F),N=w.sin(F),O=w.tan(G/4),P=4/3*c*O,Q=4/3*d*O,R=[a,b],S=[a+P*L,b-Q*K],T=[h+P*N,i-Q*M],U=[h,i];S[0]=2*R[0]-S[0],S[1]=2*R[1]-S[1];if(j)return[S,T,U][n](m);m=[S,T,U][n](m).join()[s](",");var V=[];for(var W=0,X=m.length;W<X;W++)V[W]=W%2?p(m[W-1],m[W],l).y:p(m[W],m[W+1],l).x;return V},bP=function(a,b,c,d,e,f,g,h,i){var j=1-i;return{x:A(j,3)*a+A(j,2)*3*i*c+j*3*i*i*e+A(i,3)*g,y:A(j,3)*b+A(j,2)*3*i*d+j*3*i*i*f+A(i,3)*h}},bQ=bv(function(a,b,c,d,e,f,g,h){var i=e-2*c+a-(g-2*e+c),j=2*(c-a)-2*(e-c),k=a-c,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,o=[b,h],p=[a,g],q;z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y)),i=f-2*d+b-(h-2*f+d),j=2*(d-b)-2*(f-d),k=b-d,l=(-j+w.sqrt(j*j-4*i*k))/2/i,n=(-j-w.sqrt(j*j-4*i*k))/2/i,z(l)>"1e12"&&(l=.5),z(n)>"1e12"&&(n=.5),l>0&&l<1&&(q=bP(a,b,c,d,e,f,g,h,l),p.push(q.x),o.push(q.y)),n>0&&n<1&&(q=bP(a,b,c,d,e,f,g,h,n),p.push(q.x),o.push(q.y));return{min:{x:y[m](0,p),y:y[m](0,o)},max:{x:x[m](0,p),y:x[m](0,o)}}}),bR=a._path2curve=bv(function(a,b){var c=!b&&bz(a);if(!b&&c.curve)return bJ(c.curve);var d=bL(a),e=b&&bL(b),f={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},g={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},h=function(a,b){var c,d;if(!a)return["C",b.x,b.y,b.x,b.y,b.x,b.y];!(a[0]in{T:1,Q:1})&&(b.qx=b.qy=null);switch(a[0]){case"M":b.X=a[1],b.Y=a[2];break;case"A":a=["C"][n](bO[m](0,[b.x,b.y][n](a.slice(1))));break;case"S":c=b.x+(b.x-(b.bx||b.x)),d=b.y+(b.y-(b.by||b.y)),a=["C",c,d][n](a.slice(1));break;case"T":b.qx=b.x+(b.x-(b.qx||b.x)),b.qy=b.y+(b.y-(b.qy||b.y)),a=["C"][n](bN(b.x,b.y,b.qx,b.qy,a[1],a[2]));break;case"Q":b.qx=a[1],b.qy=a[2],a=["C"][n](bN(b.x,b.y,a[1],a[2],a[3],a[4]));break;case"L":a=["C"][n](bM(b.x,b.y,a[1],a[2]));break;case"H":a=["C"][n](bM(b.x,b.y,a[1],b.y));break;case"V":a=["C"][n](bM(b.x,b.y,b.x,a[1]));break;case"Z":a=["C"][n](bM(b.x,b.y,b.X,b.Y))}return a},i=function(a,b){if(a[b].length>7){a[b].shift();var c=a[b];while(c.length)a.splice(b++,0,["C"][n](c.splice(0,6)));a.splice(b,1),l=x(d.length,e&&e.length||0)}},j=function(a,b,c,f,g){a&&b&&a[g][0]=="M"&&b[g][0]!="M"&&(b.splice(g,0,["M",f.x,f.y]),c.bx=0,c.by=0,c.x=a[g][1],c.y=a[g][2],l=x(d.length,e&&e.length||0))};for(var k=0,l=x(d.length,e&&e.length||0);k<l;k++){d[k]=h(d[k],f),i(d,k),e&&(e[k]=h(e[k],g)),e&&i(e,k),j(d,e,f,g,k),j(e,d,g,f,k);var o=d[k],p=e&&e[k],q=o.length,r=e&&p.length;f.x=o[q-2],f.y=o[q-1],f.bx=Q(o[q-4])||f.x,f.by=Q(o[q-3])||f.y,g.bx=e&&(Q(p[r-4])||g.x),g.by=e&&(Q(p[r-3])||g.y),g.x=e&&p[r-2],g.y=e&&p[r-1]}e||(c.curve=bJ(d));return e?[d,e]:d},null,bJ),bS=a._parseDots=bv(function(b){var c=[];for(var d=0,e=b.length;d<e;d++){var f={},g=b[d].match(/^([^:]*):?([\d\.]*)/);f.color=a.getRGB(g[1]);if(f.color.error)return null;f.color=f.color.hex,g[2]&&(f.offset=g[2]+"%"),c.push(f)}for(d=1,e=c.length-1;d<e;d++)if(!c[d].offset){var h=Q(c[d-1].offset||0),i=0;for(var j=d+1;j<e;j++)if(c[j].offset){i=c[j].offset;break}i||(i=100,j=e),i=Q(i);var k=(i-h)/(j-d+1);for(;d<j;d++)h+=k,c[d].offset=h+"%"}return c}),bT=a._tear=function(a,b){a==b.top&&(b.top=a.prev),a==b.bottom&&(b.bottom=a.next),a.next&&(a.next.prev=a.prev),a.prev&&(a.prev.next=a.next)},bU=a._tofront=function(a,b){b.top!==a&&(bT(a,b),a.next=null,a.prev=b.top,b.top.next=a,b.top=a)},bV=a._toback=function(a,b){b.bottom!==a&&(bT(a,b),a.next=b.bottom,a.prev=null,b.bottom.prev=a,b.bottom=a)},bW=a._insertafter=function(a,b,c){bT(a,c),b==c.top&&(c.top=a),b.next&&(b.next.prev=a),a.next=b.next,a.prev=b,b.next=a},bX=a._insertbefore=function(a,b,c){bT(a,c),b==c.bottom&&(c.bottom=a),b.prev&&(b.prev.next=a),a.prev=b.prev,b.prev=a,a.next=b},bY=a.toMatrix=function(a,b){var c=bI(a),d={_:{transform:p},getBBox:function(){return c}};b$(d,b);return d.matrix},bZ=a.transformPath=function(a,b){return bj(a,bY(a,b))},b$=a._extractTransform=function(b,c){if(c==null)return b._.transform;c=r(c).replace(/\.{3}|\u2026/g,b._.transform||p);var d=a.parseTransformString(c),e=0,f=0,g=0,h=1,i=1,j=b._,k=new cb;j.transform=d||[];if(d)for(var l=0,m=d.length;l<m;l++){var n=d[l],o=n.length,q=r(n[0]).toLowerCase(),s=n[0]!=q,t=s?k.invert():0,u,v,w,x,y;q=="t"&&o==3?s?(u=t.x(0,0),v=t.y(0,0),w=t.x(n[1],n[2]),x=t.y(n[1],n[2]),k.translate(w-u,x-v)):k.translate(n[1],n[2]):q=="r"?o==2?(y=y||b.getBBox(1),k.rotate(n[1],y.x+y.width/2,y.y+y.height/2),e+=n[1]):o==4&&(s?(w=t.x(n[2],n[3]),x=t.y(n[2],n[3]),k.rotate(n[1],w,x)):k.rotate(n[1],n[2],n[3]),e+=n[1]):q=="s"?o==2||o==3?(y=y||b.getBBox(1),k.scale(n[1],n[o-1],y.x+y.width/2,y.y+y.height/2),h*=n[1],i*=n[o-1]):o==5&&(s?(w=t.x(n[3],n[4]),x=t.y(n[3],n[4]),k.scale(n[1],n[2],w,x)):k.scale(n[1],n[2],n[3],n[4]),h*=n[1],i*=n[2]):q=="m"&&o==7&&k.add(n[1],n[2],n[3],n[4],n[5],n[6]),j.dirtyT=1,b.matrix=k}b.matrix=k,j.sx=h,j.sy=i,j.deg=e,j.dx=f=k.e,j.dy=g=k.f,h==1&&i==1&&!e&&j.bbox?(j.bbox.x+=+f,j.bbox.y+=+g):j.dirtyT=1},b_=function(a){var b=a[0];switch(b.toLowerCase()){case"t":return[b,0,0];case"m":return[b,1,0,0,1,0,0];case"r":return a.length==4?[b,0,a[2],a[3]]:[b,0];case"s":return a.length==5?[b,1,1,a[3],a[4]]:a.length==3?[b,1,1]:[b,1]}},ca=a._equaliseTransform=function(b,c){c=r(c).replace(/\.{3}|\u2026/g,b),b=a.parseTransformString(b)||[],c=a.parseTransformString(c)||[];var d=x(b.length,c.length),e=[],f=[],g=0,h,i,j,k;for(;g<d;g++){j=b[g]||b_(c[g]),k=c[g]||b_(j);if(j[0]!=k[0]||j[0].toLowerCase()=="r"&&(j[2]!=k[2]||j[3]!=k[3])||j[0].toLowerCase()=="s"&&(j[3]!=k[3]||j[4]!=k[4]))return;e[g]=[],f[g]=[];for(h=0,i=x(j.length,k.length);h<i;h++)h in j&&(e[g][h]=j[h]),h in k&&(f[g][h]=k[h])}return{from:e,to:f}};a._getContainer=function(b,c,d,e){var f;f=e==null&&!a.is(b,"object")?h.doc.getElementById(b):b;if(f!=null){if(f.tagName)return c==null?{container:f,width:f.style.pixelWidth||f.offsetWidth,height:f.style.pixelHeight||f.offsetHeight}:{container:f,width:c,height:d};return{container:1,x:b,y:c,width:d,height:e}}},a.pathToRelative=bK,a._engine={},a.path2curve=bR,a.matrix=function(a,b,c,d,e,f){return new cb(a,b,c,d,e,f)},function(b){function d(a){var b=w.sqrt(c(a));a[0]&&(a[0]/=b),a[1]&&(a[1]/=b)}function c(a){return a[0]*a[0]+a[1]*a[1]}b.add=function(a,b,c,d,e,f){var g=[[],[],[]],h=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],i=[[a,c,e],[b,d,f],[0,0,1]],j,k,l,m;a&&a instanceof cb&&(i=[[a.a,a.c,a.e],[a.b,a.d,a.f],[0,0,1]]);for(j=0;j<3;j++)for(k=0;k<3;k++){m=0;for(l=0;l<3;l++)m+=h[j][l]*i[l][k];g[j][k]=m}this.a=g[0][0],this.b=g[1][0],this.c=g[0][1],this.d=g[1][1],this.e=g[0][2],this.f=g[1][2]},b.invert=function(){var a=this,b=a.a*a.d-a.b*a.c;return new cb(a.d/b,-a.b/b,-a.c/b,a.a/b,(a.c*a.f-a.d*a.e)/b,(a.b*a.e-a.a*a.f)/b)},b.clone=function(){return new cb(this.a,this.b,this.c,this.d,this.e,this.f)},b.translate=function(a,b){this.add(1,0,0,1,a,b)},b.scale=function(a,b,c,d){b==null&&(b=a),(c||d)&&this.add(1,0,0,1,c,d),this.add(a,0,0,b,0,0),(c||d)&&this.add(1,0,0,1,-c,-d)},b.rotate=function(b,c,d){b=a.rad(b),c=c||0,d=d||0;var e=+w.cos(b).toFixed(9),f=+w.sin(b).toFixed(9);this.add(e,f,-f,e,c,d),this.add(1,0,0,1,-c,-d)},b.x=function(a,b){return a*this.a+b*this.c+this.e},b.y=function(a,b){return a*this.b+b*this.d+this.f},b.get=function(a){return+this[r.fromCharCode(97+a)].toFixed(4)},b.toString=function(){return a.svg?"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")":[this.get(0),this.get(2),this.get(1),this.get(3),0,0].join()},b.toFilter=function(){return"progid:DXImageTransform.Microsoft.Matrix(M11="+this.get(0)+", M12="+this.get(2)+", M21="+this.get(1)+", M22="+this.get(3)+", Dx="+this.get(4)+", Dy="+this.get(5)+", sizingmethod='auto expand')"},b.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},b.split=function(){var b={};b.dx=this.e,b.dy=this.f;var e=[[this.a,this.c],[this.b,this.d]];b.scalex=w.sqrt(c(e[0])),d(e[0]),b.shear=e[0][0]*e[1][0]+e[0][1]*e[1][1],e[1]=[e[1][0]-e[0][0]*b.shear,e[1][1]-e[0][1]*b.shear],b.scaley=w.sqrt(c(e[1])),d(e[1]),b.shear/=b.scaley;var f=-e[0][1],g=e[1][1];g<0?(b.rotate=a.deg(w.acos(g)),f<0&&(b.rotate=360-b.rotate)):b.rotate=a.deg(w.asin(f)),b.isSimple=!+b.shear.toFixed(9)&&(b.scalex.toFixed(9)==b.scaley.toFixed(9)||!b.rotate),b.isSuperSimple=!+b.shear.toFixed(9)&&b.scalex.toFixed(9)==b.scaley.toFixed(9)&&!b.rotate,b.noRotation=!+b.shear.toFixed(9)&&!b.rotate;return b},b.toTransformString=function(a){var b=a||this[s]();if(b.isSimple){b.scalex=+b.scalex.toFixed(4),b.scaley=+b.scaley.toFixed(4),b.rotate=+b.rotate.toFixed(4);return(b.dx||b.dy?"t"+[b.dx,b.dy]:p)+(b.scalex!=1||b.scaley!=1?"s"+[b.scalex,b.scaley,0,0]:p)+(b.rotate?"r"+[b.rotate,0,0]:p)}return"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]}}(cb.prototype);var cc=navigator.userAgent.match(/Version\/(.*?)\s/)||navigator.userAgent.match(/Chrome\/(\d+)/);navigator.vendor=="Apple Computer, Inc."&&(cc&&cc[1]<4||navigator.platform.slice(0,2)=="iP")||navigator.vendor=="Google Inc."&&cc&&cc[1]<8?k.safari=function(){var a=this.rect(-99,-99,this.width+99,this.height+99).attr({stroke:"none"});setTimeout(function(){a.remove()})}:k.safari=be;var cd=function(){this.returnValue=!1},ce=function(){return this.originalEvent.preventDefault()},cf=function(){this.cancelBubble=!0},cg=function(){return this.originalEvent.stopPropagation()},ch=function(){if(h.doc.addEventListener)return function(a,b,c,d){var e=o&&u[b]?u[b]:b,f=function(e){var f=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,i=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft,j=e.clientX+i,k=e.clientY+f;if(o&&u[g](b))for(var l=0,m=e.targetTouches&&e.targetTouches.length;l<m;l++)if(e.targetTouches[l].target==a){var n=e;e=e.targetTouches[l],e.originalEvent=n,e.preventDefault=ce,e.stopPropagation=cg;break}return c.call(d,e,j,k)};a.addEventListener(e,f,!1);return function(){a.removeEventListener(e,f,!1);return!0}};if(h.doc.attachEvent)return function(a,b,c,d){var e=function(a){a=a||h.win.event;var b=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,e=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft,f=a.clientX+e,g=a.clientY+b;a.preventDefault=a.preventDefault||cd,a.stopPropagation=a.stopPropagation||cf;return c.call(d,a,f,g)};a.attachEvent("on"+b,e);var f=function(){a.detachEvent("on"+b,e);return!0};return f}}(),ci=[],cj=function(a){var b=a.clientX,c=a.clientY,d=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,e=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft,f,g=ci.length;while(g--){f=ci[g];if(o){var i=a.touches.length,j;while(i--){j=a.touches[i];if(j.identifier==f.el._drag.id){b=j.clientX,c=j.clientY,(a.originalEvent?a.originalEvent:a).preventDefault();break}}}else a.preventDefault();var k=f.el.node,l,m=k.nextSibling,n=k.parentNode,p=k.style.display;h.win.opera&&n.removeChild(k),k.style.display="none",l=f.el.paper.getElementByPoint(b,c),k.style.display=p,h.win.opera&&(m?n.insertBefore(k,m):n.appendChild(k)),l&&eve("raphael.drag.over."+f.el.id,f.el,l),b+=e,c+=d,eve("raphael.drag.move."+f.el.id,f.move_scope||f.el,b-f.el._drag.x,c-f.el._drag.y,b,c,a)}},ck=function(b){a.unmousemove(cj).unmouseup(ck);var c=ci.length,d;while(c--)d=ci[c],d.el._drag={},eve("raphael.drag.end."+d.el.id,d.end_scope||d.start_scope||d.move_scope||d.el,b);ci=[]},cl=a.el={};for(var cm=t.length;cm--;)(function(b){a[b]=cl[b]=function(c,d){a.is(c,"function")&&(this.events=this.events||[],this.events.push({name:b,f:c,unbind:ch(this.shape||this.node||h.doc,b,c,d||this)}));return this},a["un"+b]=cl["un"+b]=function(a){var c=this.events||[],d=c.length;while(d--)if(c[d].name==b&&c[d].f==a){c[d].unbind(),c.splice(d,1),!c.length&&delete this.events;return this}return this}})(t[cm]);cl.data=function(b,c){var d=bb[this.id]=bb[this.id]||{};if(arguments.length==1){if(a.is(b,"object")){for(var e in b)b[g](e)&&this.data(e,b[e]);return this}eve("raphael.data.get."+this.id,this,d[b],b);return d[b]}d[b]=c,eve("raphael.data.set."+this.id,this,c,b);return this},cl.removeData=function(a){a==null?bb[this.id]={}:bb[this.id]&&delete bb[this.id][a];return this},cl.hover=function(a,b,c,d){return this.mouseover(a,c).mouseout(b,d||c)},cl.unhover=function(a,b){return this.unmouseover(a).unmouseout(b)};var cn=[];cl.drag=function(b,c,d,e,f,g){function i(i){(i.originalEvent||i).preventDefault();var j=h.doc.documentElement.scrollTop||h.doc.body.scrollTop,k=h.doc.documentElement.scrollLeft||h.doc.body.scrollLeft;this._drag.x=i.clientX+k,this._drag.y=i.clientY+j,this._drag.id=i.identifier,!ci.length&&a.mousemove(cj).mouseup(ck),ci.push({el:this,move_scope:e,start_scope:f,end_scope:g}),c&&eve.on("raphael.drag.start."+this.id,c),b&&eve.on("raphael.drag.move."+this.id,b),d&&eve.on("raphael.drag.end."+this.id,d),eve("raphael.drag.start."+this.id,f||e||this,i.clientX+k,i.clientY+j,i)}this._drag={},cn.push({el:this,start:i}),this.mousedown(i);return this},cl.onDragOver=function(a){a?eve.on("raphael.drag.over."+this.id,a):eve.unbind("raphael.drag.over."+this.id)},cl.undrag=function(){var b=cn.length;while(b--)cn[b].el==this&&(this.unmousedown(cn[b].start),cn.splice(b,1),eve.unbind("raphael.drag.*."+this.id));!cn.length&&a.unmousemove(cj).unmouseup(ck)},k.circle=function(b,c,d){var e=a._engine.circle(this,b||0,c||0,d||0);this.__set__&&this.__set__.push(e);return e},k.rect=function(b,c,d,e,f){var g=a._engine.rect(this,b||0,c||0,d||0,e||0,f||0);this.__set__&&this.__set__.push(g);return g},k.ellipse=function(b,c,d,e){var f=a._engine.ellipse(this,b||0,c||0,d||0,e||0);this.__set__&&this.__set__.push(f);return f},k.path=function(b){b&&!a.is(b,D)&&!a.is(b[0],E)&&(b+=p);var c=a._engine.path(a.format[m](a,arguments),this);this.__set__&&this.__set__.push(c);return c},k.image=function(b,c,d,e,f){var g=a._engine.image(this,b||"about:blank",c||0,d||0,e||0,f||0);this.__set__&&this.__set__.push(g);return g},k.text=function(b,c,d){var e=a._engine.text(this,b||0,c||0,r(d));this.__set__&&this.__set__.push(e);return e},k.set=function(b){!a.is(b,"array")&&(b=Array.prototype.splice.call(arguments,0,arguments.length));var c=new cG(b);this.__set__&&this.__set__.push(c);return c},k.setStart=function(a){this.__set__=a||this.set()},k.setFinish=function(a){var b=this.__set__;delete this.__set__;return b},k.setSize=function(b,c){return a._engine.setSize.call(this,b,c)},k.setViewBox=function(b,c,d,e,f){return a._engine.setViewBox.call(this,b,c,d,e,f)},k.top=k.bottom=null,k.raphael=a;var co=function(a){var b=a.getBoundingClientRect(),c=a.ownerDocument,d=c.body,e=c.documentElement,f=e.clientTop||d.clientTop||0,g=e.clientLeft||d.clientLeft||0,i=b.top+(h.win.pageYOffset||e.scrollTop||d.scrollTop)-f,j=b.left+(h.win.pageXOffset||e.scrollLeft||d.scrollLeft)-g;return{y:i,x:j}};k.getElementByPoint=function(a,b){var c=this,d=c.canvas,e=h.doc.elementFromPoint(a,b);if(h.win.opera&&e.tagName=="svg"){var f=co(d),g=d.createSVGRect();g.x=a-f.x,g.y=b-f.y,g.width=g.height=1;var i=d.getIntersectionList(g,null);i.length&&(e=i[i.length-1])}if(!e)return null;while(e.parentNode&&e!=d.parentNode&&!e.raphael)e=e.parentNode;e==c.canvas.parentNode&&(e=d),e=e&&e.raphael?c.getById(e.raphaelid):null;return e},k.getById=function(a){var b=this.bottom;while(b){if(b.id==a)return b;b=b.next}return null},k.forEach=function(a,b){var c=this.bottom;while(c){if(a.call(b,c)===!1)return this;c=c.next}return this},k.getElementsByPoint=function(a,b){var c=this.set();this.forEach(function(d){d.isPointInside(a,b)&&c.push(d)});return c},cl.isPointInside=function(b,c){var d=this.realPath=this.realPath||bi[this.type](this);return a.isPointInsidePath(d,b,c)},cl.getBBox=function(a){if(this.removed)return{};var b=this._;if(a){if(b.dirty||!b.bboxwt)this.realPath=bi[this.type](this),b.bboxwt=bI(this.realPath),b.bboxwt.toString=cq,b.dirty=0;return b.bboxwt}if(b.dirty||b.dirtyT||!b.bbox){if(b.dirty||!this.realPath)b.bboxwt=0,this.realPath=bi[this.type](this);b.bbox=bI(bj(this.realPath,this.matrix)),b.bbox.toString=cq,b.dirty=b.dirtyT=0}return b.bbox},cl.clone=function(){if(this.removed)return null;var a=this.paper[this.type]().attr(this.attr());this.__set__&&this.__set__.push(a);return a},cl.glow=function(a){if(this.type=="text")return null;a=a||{};var b={width:(a.width||10)+(+this.attr("stroke-width")||1),fill:a.fill||!1,opacity:a.opacity||.5,offsetx:a.offsetx||0,offsety:a.offsety||0,color:a.color||"#000"},c=b.width/2,d=this.paper,e=d.set(),f=this.realPath||bi[this.type](this);f=this.matrix?bj(f,this.matrix):f;for(var g=1;g<c+1;g++)e.push(d.path(f).attr({stroke:b.color,fill:b.fill?b.color:"none","stroke-linejoin":"round","stroke-linecap":"round","stroke-width":+(b.width/c*g).toFixed(3),opacity:+(b.opacity/c).toFixed(3)}));return e.insertBefore(this).translate(b.offsetx,b.offsety)};var cr={},cs=function(b,c,d,e,f,g,h,i,j){return j==null?bB(b,c,d,e,f,g,h,i):a.findDotsAtSegment(b,c,d,e,f,g,h,i,bC(b,c,d,e,f,g,h,i,j))},ct=function(b,c){return function(d,e,f){d=bR(d);var g,h,i,j,k="",l={},m,n=0;for(var o=0,p=d.length;o<p;o++){i=d[o];if(i[0]=="M")g=+i[1],h=+i[2];else{j=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6]);if(n+j>e){if(c&&!l.start){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n),k+=["C"+m.start.x,m.start.y,m.m.x,m.m.y,m.x,m.y];if(f)return k;l.start=k,k=["M"+m.x,m.y+"C"+m.n.x,m.n.y,m.end.x,m.end.y,i[5],i[6]].join(),n+=j,g=+i[5],h=+i[6];continue}if(!b&&!c){m=cs(g,h,i[1],i[2],i[3],i[4],i[5],i[6],e-n);return{x:m.x,y:m.y,alpha:m.alpha}}}n+=j,g=+i[5],h=+i[6]}k+=i.shift()+i}l.end=k,m=b?n:c?l:a.findDotsAtSegment(g,h,i[0],i[1],i[2],i[3],i[4],i[5],1),m.alpha&&(m={x:m.x,y:m.y,alpha:m.alpha});return m}},cu=ct(1),cv=ct(),cw=ct(0,1);a.getTotalLength=cu,a.getPointAtLength=cv,a.getSubpath=function(a,b,c){if(this.getTotalLength(a)-c<1e-6)return cw(a,b).end;var d=cw(a,c,1);return b?cw(d,b).end:d},cl.getTotalLength=function(){if(this.type=="path"){if(this.node.getTotalLength)return this.node.getTotalLength();return cu(this.attrs.path)}},cl.getPointAtLength=function(a){if(this.type=="path")return cv(this.attrs.path,a)},cl.getSubpath=function(b,c){if(this.type=="path")return a.getSubpath(this.attrs.path,b,c)};var cx=a.easing_formulas={linear:function(a){return a},"<":function(a){return A(a,1.7)},">":function(a){return A(a,.48)},"<>":function(a){var b=.48-a/1.04,c=w.sqrt(.1734+b*b),d=c-b,e=A(z(d),1/3)*(d<0?-1:1),f=-c-b,g=A(z(f),1/3)*(f<0?-1:1),h=e+g+.5;return(1-h)*3*h*h+h*h*h},backIn:function(a){var b=1.70158;return a*a*((b+1)*a-b)},backOut:function(a){a=a-1;var b=1.70158;return a*a*((b+1)*a+b)+1},elastic:function(a){if(a==!!a)return a;return A(2,-10*a)*w.sin((a-.075)*2*B/.3)+1},bounce:function(a){var b=7.5625,c=2.75,d;a<1/c?d=b*a*a:a<2/c?(a-=1.5/c,d=b*a*a+.75):a<2.5/c?(a-=2.25/c,d=b*a*a+.9375):(a-=2.625/c,d=b*a*a+.984375);return d}};cx.easeIn=cx["ease-in"]=cx["<"],cx.easeOut=cx["ease-out"]=cx[">"],cx.easeInOut=cx["ease-in-out"]=cx["<>"],cx["back-in"]=cx.backIn,cx["back-out"]=cx.backOut;var cy=[],cz=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,16)},cA=function(){var b=+(new Date),c=0;for(;c<cy.length;c++){var d=cy[c];if(d.el.removed||d.paused)continue;var e=b-d.start,f=d.ms,h=d.easing,i=d.from,j=d.diff,k=d.to,l=d.t,m=d.el,o={},p,r={},s;d.initstatus?(e=(d.initstatus*d.anim.top-d.prev)/(d.percent-d.prev)*f,d.status=d.initstatus,delete d.initstatus,d.stop&&cy.splice(c--,1)):d.status=(d.prev+(d.percent-d.prev)*(e/f))/d.anim.top;if(e<0)continue;if(e<f){var t=h(e/f);for(var u in i)if(i[g](u)){switch(U[u]){case C:p=+i[u]+t*f*j[u];break;case"colour":p="rgb("+[cB(O(i[u].r+t*f*j[u].r)),cB(O(i[u].g+t*f*j[u].g)),cB(O(i[u].b+t*f*j[u].b))].join(",")+")";break;case"path":p=[];for(var v=0,w=i[u].length;v<w;v++){p[v]=[i[u][v][0]];for(var x=1,y=i[u][v].length;x<y;x++)p[v][x]=+i[u][v][x]+t*f*j[u][v][x];p[v]=p[v].join(q)}p=p.join(q);break;case"transform":if(j[u].real){p=[];for(v=0,w=i[u].length;v<w;v++){p[v]=[i[u][v][0]];for(x=1,y=i[u][v].length;x<y;x++)p[v][x]=i[u][v][x]+t*f*j[u][v][x]}}else{var z=function(a){return+i[u][a]+t*f*j[u][a]};p=[["m",z(0),z(1),z(2),z(3),z(4),z(5)]]}break;case"csv":if(u=="clip-rect"){p=[],v=4;while(v--)p[v]=+i[u][v]+t*f*j[u][v]}break;default:var A=[][n](i[u]);p=[],v=m.paper.customAttributes[u].length;while(v--)p[v]=+A[v]+t*f*j[u][v]}o[u]=p}m.attr(o),function(a,b,c){setTimeout(function(){eve("raphael.anim.frame."+a,b,c)})}(m.id,m,d.anim)}else{(function(b,c,d){setTimeout(function(){eve("raphael.anim.frame."+c.id,c,d),eve("raphael.anim.finish."+c.id,c,d),a.is(b,"function")&&b.call(c)})})(d.callback,m,d.anim),m.attr(k),cy.splice(c--,1);if(d.repeat>1&&!d.next){for(s in k)k[g](s)&&(r[s]=d.totalOrigin[s]);d.el.attr(r),cE(d.anim,d.el,d.anim.percents[0],null,d.totalOrigin,d.repeat-1)}d.next&&!d.stop&&cE(d.anim,d.el,d.next,null,d.totalOrigin,d.repeat)}}a.svg&&m&&m.paper&&m.paper.safari(),cy.length&&cz(cA)},cB=function(a){return a>255?255:a<0?0:a};cl.animateWith=function(b,c,d,e,f,g){var h=this;if(h.removed){g&&g.call(h);return h}var i=d instanceof cD?d:a.animation(d,e,f,g),j,k;cE(i,h,i.percents[0],null,h.attr());for(var l=0,m=cy.length;l<m;l++)if(cy[l].anim==c&&cy[l].el==b){cy[m-1].start=cy[l].start;break}return h},cl.onAnimation=function(a){a?eve.on("raphael.anim.frame."+this.id,a):eve.unbind("raphael.anim.frame."+this.id);return this},cD.prototype.delay=function(a){var b=new cD(this.anim,this.ms);b.times=this.times,b.del=+a||0;return b},cD.prototype.repeat=function(a){var b=new cD(this.anim,this.ms);b.del=this.del,b.times=w.floor(x(a,0))||1;return b},a.animation=function(b,c,d,e){if(b instanceof cD)return b;if(a.is(d,"function")||!d)e=e||d||null,d=null;b=Object(b),c=+c||0;var f={},h,i;for(i in b)b[g](i)&&Q(i)!=i&&Q(i)+"%"!=i&&(h=!0,f[i]=b[i]);if(!h)return new cD(b,c);d&&(f.easing=d),e&&(f.callback=e);return new cD({100:f},c)},cl.animate=function(b,c,d,e){var f=this;if(f.removed){e&&e.call(f);return f}var g=b instanceof cD?b:a.animation(b,c,d,e);cE(g,f,g.percents[0],null,f.attr());return f},cl.setTime=function(a,b){a&&b!=null&&this.status(a,y(b,a.ms)/a.ms);return this},cl.status=function(a,b){var c=[],d=0,e,f;if(b!=null){cE(a,this,-1,y(b,1));return this}e=cy.length;for(;d<e;d++){f=cy[d];if(f.el.id==this.id&&(!a||f.anim==a)){if(a)return f.status;c.push({anim:f.anim,status:f.status})}}if(a)return 0;return c},cl.pause=function(a){for(var b=0;b<cy.length;b++)cy[b].el.id==this.id&&(!a||cy[b].anim==a)&&eve("raphael.anim.pause."+this.id,this,cy[b].anim)!==!1&&(cy[b].paused=!0);return this},cl.resume=function(a){for(var b=0;b<cy.length;b++)if(cy[b].el.id==this.id&&(!a||cy[b].anim==a)){var c=cy[b];eve("raphael.anim.resume."+this.id,this,c.anim)!==!1&&(delete c.paused,this.status(c.anim,c.status))}return this},cl.stop=function(a){for(var b=0;b<cy.length;b++)cy[b].el.id==this.id&&(!a||cy[b].anim==a)&&eve("raphael.anim.stop."+this.id,this,cy[b].anim)!==!1&&cy.splice(b--,1);return this},eve.on("raphael.remove",cF),eve.on("raphael.clear",cF),cl.toString=function(){return"Raphaël’s object"};var cG=function(a){this.items=[],this.length=0,this.type="set";if(a)for(var b=0,c=a.length;b<c;b++)a[b]&&(a[b].constructor==cl.constructor||a[b].constructor==cG)&&(this[this.items.length]=this.items[this.items.length]=a[b],this.length++)},cH=cG.prototype;cH.push=function(){var a,b;for(var c=0,d=arguments.length;c<d;c++)a=arguments[c],a&&(a.constructor==cl.constructor||a.constructor==cG)&&(b=this.items.length,this[b]=this.items[b]=a,this.length++);return this},cH.pop=function(){this.length&&delete this[this.length--];return this.items.pop()},cH.forEach=function(a,b){for(var c=0,d=this.items.length;c<d;c++)if(a.call(b,this.items[c],c)===!1)return this;return this};for(var cI in cl)cl[g](cI)&&(cH[cI]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a][m](c,b)})}}(cI));cH.attr=function(b,c){if(b&&a.is(b,E)&&a.is(b[0],"object"))for(var d=0,e=b.length;d<e;d++)this.items[d].attr(b[d]);else for(var f=0,g=this.items.length;f<g;f++)this.items[f].attr(b,c);return this},cH.clear=function(){while(this.length)this.pop()},cH.splice=function(a,b,c){a=a<0?x(this.length+a,0):a,b=x(0,y(this.length-a,b));var d=[],e=[],f=[],g;for(g=2;g<arguments.length;g++)f.push(arguments[g]);for(g=0;g<b;g++)e.push(this[a+g]);for(;g<this.length-a;g++)d.push(this[a+g]);var h=f.length;for(g=0;g<h+d.length;g++)this.items[a+g]=this[a+g]=g<h?f[g]:d[g-h];g=this.items.length=this.length-=b-h;while(this[g])delete this[g++];return new cG(e)},cH.exclude=function(a){for(var b=0,c=this.length;b<c;b++)if(this[b]==a){this.splice(b,1);return!0}},cH.animate=function(b,c,d,e){(a.is(d,"function")||!d)&&(e=d||null);var f=this.items.length,g=f,h,i=this,j;if(!f)return this;e&&(j=function(){!--f&&e.call(i)}),d=a.is(d,D)?d:j;var k=a.animation(b,c,d,j);h=this.items[--g].animate(k);while(g--)this.items[g]&&!this.items[g].removed&&this.items[g].animateWith(h,k,k);return this},cH.insertAfter=function(a){var b=this.items.length;while(b--)this.items[b].insertAfter(a);return this},cH.getBBox=function(){var a=[],b=[],c=[],d=[];for(var e=this.items.length;e--;)if(!this.items[e].removed){var f=this.items[e].getBBox();a.push(f.x),b.push(f.y),c.push(f.x+f.width),d.push(f.y+f.height)}a=y[m](0,a),b=y[m](0,b),c=x[m](0,c),d=x[m](0,d);return{x:a,y:b,x2:c,y2:d,width:c-a,height:d-b}},cH.clone=function(a){a=new cG;for(var b=0,c=this.items.length;b<c;b++)a.push(this.items[b].clone());return a},cH.toString=function(){return"Raphaël‘s set"},a.registerFont=function(a){if(!a.face)return a;this.fonts=this.fonts||{};var b={w:a.w,face:{},glyphs:{}},c=a.face["font-family"];for(var d in a.face)a.face[g](d)&&(b.face[d]=a.face[d]);this.fonts[c]?this.fonts[c].push(b):this.fonts[c]=[b];if(!a.svg){b.face["units-per-em"]=R(a.face["units-per-em"],10);for(var e in a.glyphs)if(a.glyphs[g](e)){var f=a.glyphs[e];b.glyphs[e]={w:f.w,k:{},d:f.d&&"M"+f.d.replace(/[mlcxtrv]/g,function(a){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}[a]||"M"})+"z"};if(f.k)for(var h in f.k)f[g](h)&&(b.glyphs[e].k[h]=f.k[h])}}return a},k.getFont=function(b,c,d,e){e=e||"normal",d=d||"normal",c=+c||{normal:400,bold:700,lighter:300,bolder:800}[c]||400;if(!!a.fonts){var f=a.fonts[b];if(!f){var h=new RegExp("(^|\\s)"+b.replace(/[^\w\d\s+!~.:_-]/g,p)+"(\\s|$)","i");for(var i in a.fonts)if(a.fonts[g](i)&&h.test(i)){f=a.fonts[i];break}}var j;if(f)for(var k=0,l=f.length;k<l;k++){j=f[k];if(j.face["font-weight"]==c&&(j.face["font-style"]==d||!j.face["font-style"])&&j.face["font-stretch"]==e)break}return j}},k.print=function(b,d,e,f,g,h,i){h=h||"middle",i=x(y(i||0,1),-1);var j=r(e)[s](p),k=0,l=0,m=p,n;a.is(f,e)&&(f=this.getFont(f));if(f){n=(g||16)/f.face["units-per-em"];var o=f.face.bbox[s](c),q=+o[0],t=o[3]-o[1],u=0,v=+o[1]+(h=="baseline"?t+ +f.face.descent:t/2);for(var w=0,z=j.length;w<z;w++){if(j[w]=="\n")k=0,B=0,l=0,u+=t;else{var A=l&&f.glyphs[j[w-1]]||{},B=f.glyphs[j[w]];k+=l?(A.w||f.w)+(A.k&&A.k[j[w]]||0)+f.w*i:0,l=1}B&&B.d&&(m+=a.transformPath(B.d,["t",k*n,u*n,"s",n,n,q,v,"t",(b-q)/n,(d-v)/n]))}}return this.path(m).attr({fill:"#000",stroke:"none"})},k.add=function(b){if(a.is(b,"array")){var c=this.set(),e=0,f=b.length,h;for(;e<f;e++)h=b[e]||{},d[g](h.type)&&c.push(this[h.type]().attr(h))}return c},a.format=function(b,c){var d=a.is(c,E)?[0][n](c):arguments;b&&a.is(b,D)&&d.length-1&&(b=b.replace(e,function(a,b){return d[++b]==null?p:d[b]}));return b||p},a.fullfill=function(){var a=/\{([^\}]+)\}/g,b=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,c=function(a,c,d){var e=d;c.replace(b,function(a,b,c,d,f){b=b||d,e&&(b in e&&(e=e[b]),typeof e=="function"&&f&&(e=e()))}),e=(e==null||e==d?a:e)+"";return e};return function(b,d){return String(b).replace(a,function(a,b){return c(a,b,d)})}}(),a.ninja=function(){i.was?h.win.Raphael=i.is:delete Raphael;return a},a.st=cH,function(b,c,d){function e(){/in/.test(b.readyState)?setTimeout(e,9):a.eve("raphael.DOMload")}b.readyState==null&&b.addEventListener&&(b.addEventListener(c,d=function(){b.removeEventListener(c,d,!1),b.readyState="complete"},!1),b.readyState="loading"),e()}(document,"DOMContentLoaded"),i.was?h.win.Raphael=a:Raphael=a,eve.on("raphael.DOMload",function(){b=!0})}(),window.Raphael.svg&&function(a){var b="hasOwnProperty",c=String,d=parseFloat,e=parseInt,f=Math,g=f.max,h=f.abs,i=f.pow,j=/[, ]+/,k=a.eve,l="",m=" ",n="http://www.w3.org/1999/xlink",o={block:"M5,0 0,2.5 5,5z",classic:"M5,0 0,2.5 5,5 3.5,3 3.5,2z",diamond:"M2.5,0 5,2.5 2.5,5 0,2.5z",open:"M6,1 1,3.5 6,6",oval:"M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"},p={};a.toString=function(){return"Your browser supports SVG.\nYou are running Raphaël "+this.version};var q=function(d,e){if(e){typeof d=="string"&&(d=q(d));for(var f in e)e[b](f)&&(f.substring(0,6)=="xlink:"?d.setAttributeNS(n,f.substring(6),c(e[f])):d.setAttribute(f,c(e[f])))}else d=a._g.doc.createElementNS("http://www.w3.org/2000/svg",d),d.style&&(d.style.webkitTapHighlightColor="rgba(0,0,0,0)");return d},r=function(b,e){var j="linear",k=b.id+e,m=.5,n=.5,o=b.node,p=b.paper,r=o.style,s=a._g.doc.getElementById(k);if(!s){e=c(e).replace(a._radial_gradient,function(a,b,c){j="radial";if(b&&c){m=d(b),n=d(c);var e=(n>.5)*2-1;i(m-.5,2)+i(n-.5,2)>.25&&(n=f.sqrt(.25-i(m-.5,2))*e+.5)&&n!=.5&&(n=n.toFixed(5)-1e-5*e)}return l}),e=e.split(/\s*\-\s*/);if(j=="linear"){var t=e.shift();t=-d(t);if(isNaN(t))return null;var u=[0,0,f.cos(a.rad(t)),f.sin(a.rad(t))],v=1/(g(h(u[2]),h(u[3]))||1);u[2]*=v,u[3]*=v,u[2]<0&&(u[0]=-u[2],u[2]=0),u[3]<0&&(u[1]=-u[3],u[3]=0)}var w=a._parseDots(e);if(!w)return null;k=k.replace(/[\(\)\s,\xb0#]/g,"_"),b.gradient&&k!=b.gradient.id&&(p.defs.removeChild(b.gradient),delete b.gradient);if(!b.gradient){s=q(j+"Gradient",{id:k}),b.gradient=s,q(s,j=="radial"?{fx:m,fy:n}:{x1:u[0],y1:u[1],x2:u[2],y2:u[3],gradientTransform:b.matrix.invert()}),p.defs.appendChild(s);for(var x=0,y=w.length;x<y;x++)s.appendChild(q("stop",{offset:w[x].offset?w[x].offset:x?"100%":"0%","stop-color":w[x].color||"#fff"}))}}q(o,{fill:"url(#"+k+")",opacity:1,"fill-opacity":1}),r.fill=l,r.opacity=1,r.fillOpacity=1;return 1},s=function(a){var b=a.getBBox(1);q(a.pattern,{patternTransform:a.matrix.invert()+" translate("+b.x+","+b.y+")"})},t=function(d,e,f){if(d.type=="path"){var g=c(e).toLowerCase().split("-"),h=d.paper,i=f?"end":"start",j=d.node,k=d.attrs,m=k["stroke-width"],n=g.length,r="classic",s,t,u,v,w,x=3,y=3,z=5;while(n--)switch(g[n]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":r=g[n];break;case"wide":y=5;break;case"narrow":y=2;break;case"long":x=5;break;case"short":x=2}r=="open"?(x+=2,y+=2,z+=2,u=1,v=f?4:1,w={fill:"none",stroke:k.stroke}):(v=u=x/2,w={fill:k.stroke,stroke:"none"}),d._.arrows?f?(d._.arrows.endPath&&p[d._.arrows.endPath]--,d._.arrows.endMarker&&p[d._.arrows.endMarker]--):(d._.arrows.startPath&&p[d._.arrows.startPath]--,d._.arrows.startMarker&&p[d._.arrows.startMarker]--):d._.arrows={};if(r!="none"){var A="raphael-marker-"+r,B="raphael-marker-"+i+r+x+y;a._g.doc.getElementById(A)?p[A]++:(h.defs.appendChild(q(q("path"),{"stroke-linecap":"round",d:o[r],id:A})),p[A]=1);var C=a._g.doc.getElementById(B),D;C?(p[B]++,D=C.getElementsByTagName("use")[0]):(C=q(q("marker"),{id:B,markerHeight:y,markerWidth:x,orient:"auto",refX:v,refY:y/2}),D=q(q("use"),{"xlink:href":"#"+A,transform:(f?"rotate(180 "+x/2+" "+y/2+") ":l)+"scale("+x/z+","+y/z+")","stroke-width":(1/((x/z+y/z)/2)).toFixed(4)}),C.appendChild(D),h.defs.appendChild(C),p[B]=1),q(D,w);var F=u*(r!="diamond"&&r!="oval");f?(s=d._.arrows.startdx*m||0,t=a.getTotalLength(k.path)-F*m):(s=F*m,t=a.getTotalLength(k.path)-(d._.arrows.enddx*m||0)),w={},w["marker-"+i]="url(#"+B+")";if(t||s)w.d=Raphael.getSubpath(k.path,s,t);q(j,w),d._.arrows[i+"Path"]=A,d._.arrows[i+"Marker"]=B,d._.arrows[i+"dx"]=F,d._.arrows[i+"Type"]=r,d._.arrows[i+"String"]=e}else f?(s=d._.arrows.startdx*m||0,t=a.getTotalLength(k.path)-s):(s=0,t=a.getTotalLength(k.path)-(d._.arrows.enddx*m||0)),d._.arrows[i+"Path"]&&q(j,{d:Raphael.getSubpath(k.path,s,t)}),delete d._.arrows[i+"Path"],delete d._.arrows[i+"Marker"],delete d._.arrows[i+"dx"],delete d._.arrows[i+"Type"],delete d._.arrows[i+"String"];for(w in p)if(p[b](w)&&!p[w]){var G=a._g.doc.getElementById(w);G&&G.parentNode.removeChild(G)}}},u={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},v=function(a,b,d){b=u[c(b).toLowerCase()];if(b){var e=a.attrs["stroke-width"]||"1",f={round:e,square:e,butt:0}[a.attrs["stroke-linecap"]||d["stroke-linecap"]]||0,g=[],h=b.length;while(h--)g[h]=b[h]*e+(h%2?1:-1)*f;q(a.node,{"stroke-dasharray":g.join(",")})}},w=function(d,f){var i=d.node,k=d.attrs,m=i.style.visibility;i.style.visibility="hidden";for(var o in f)if(f[b](o)){if(!a._availableAttrs[b](o))continue;var p=f[o];k[o]=p;switch(o){case"blur":d.blur(p);break;case"href":case"title":case"target":var u=i.parentNode;if(u.tagName.toLowerCase()!="a"){var w=q("a");u.insertBefore(w,i),w.appendChild(i),u=w}o=="target"?u.setAttributeNS(n,"show",p=="blank"?"new":p):u.setAttributeNS(n,o,p);break;case"cursor":i.style.cursor=p;break;case"transform":d.transform(p);break;case"arrow-start":t(d,p);break;case"arrow-end":t(d,p,1);break;case"clip-rect":var x=c(p).split(j);if(x.length==4){d.clip&&d.clip.parentNode.parentNode.removeChild(d.clip.parentNode);var z=q("clipPath"),A=q("rect");z.id=a.createUUID(),q(A,{x:x[0],y:x[1],width:x[2],height:x[3]}),z.appendChild(A),d.paper.defs.appendChild(z),q(i,{"clip-path":"url(#"+z.id+")"}),d.clip=A}if(!p){var B=i.getAttribute("clip-path");if(B){var C=a._g.doc.getElementById(B.replace(/(^url\(#|\)$)/g,l));C&&C.parentNode.removeChild(C),q(i,{"clip-path":l}),delete d.clip}}break;case"path":d.type=="path"&&(q(i,{d:p?k.path=a._pathToAbsolute(p):"M0,0"}),d._.dirty=1,d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1)));break;case"width":i.setAttribute(o,p),d._.dirty=1;if(k.fx)o="x",p=k.x;else break;case"x":k.fx&&(p=-k.x-(k.width||0));case"rx":if(o=="rx"&&d.type=="rect")break;case"cx":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"height":i.setAttribute(o,p),d._.dirty=1;if(k.fy)o="y",p=k.y;else break;case"y":k.fy&&(p=-k.y-(k.height||0));case"ry":if(o=="ry"&&d.type=="rect")break;case"cy":i.setAttribute(o,p),d.pattern&&s(d),d._.dirty=1;break;case"r":d.type=="rect"?q(i,{rx:p,ry:p}):i.setAttribute(o,p),d._.dirty=1;break;case"src":d.type=="image"&&i.setAttributeNS(n,"href",p);break;case"stroke-width":if(d._.sx!=1||d._.sy!=1)p/=g(h(d._.sx),h(d._.sy))||1;d.paper._vbSize&&(p*=d.paper._vbSize),i.setAttribute(o,p),k["stroke-dasharray"]&&v(d,k["stroke-dasharray"],f),d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"stroke-dasharray":v(d,p,f);break;case"fill":var D=c(p).match(a._ISURL);if(D){z=q("pattern");var F=q("image");z.id=a.createUUID(),q(z,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1}),q(F,{x:0,y:0,"xlink:href":D[1]}),z.appendChild(F),function(b){a._preload(D[1],function(){var a=this.offsetWidth,c=this.offsetHeight;q(b,{width:a,height:c}),q(F,{width:a,height:c}),d.paper.safari()})}(z),d.paper.defs.appendChild(z),q(i,{fill:"url(#"+z.id+")"}),d.pattern=z,d.pattern&&s(d);break}var G=a.getRGB(p);if(!G.error)delete f.gradient,delete k.gradient,!a.is(k.opacity,"undefined")&&a.is(f.opacity,"undefined")&&q(i,{opacity:k.opacity}),!a.is(k["fill-opacity"],"undefined")&&a.is(f["fill-opacity"],"undefined")&&q(i,{"fill-opacity":k["fill-opacity"]});else if((d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p)){if("opacity"in k||"fill-opacity"in k){var H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l));if(H){var I=H.getElementsByTagName("stop");q(I[I.length-1],{"stop-opacity":("opacity"in k?k.opacity:1)*("fill-opacity"in k?k["fill-opacity"]:1)})}}k.gradient=p,k.fill="none";break}G[b]("opacity")&&q(i,{"fill-opacity":G.opacity>1?G.opacity/100:G.opacity});case"stroke":G=a.getRGB(p),i.setAttribute(o,G.hex),o=="stroke"&&G[b]("opacity")&&q(i,{"stroke-opacity":G.opacity>1?G.opacity/100:G.opacity}),o=="stroke"&&d._.arrows&&("startString"in d._.arrows&&t(d,d._.arrows.startString),"endString"in d._.arrows&&t(d,d._.arrows.endString,1));break;case"gradient":(d.type=="circle"||d.type=="ellipse"||c(p).charAt()!="r")&&r(d,p);break;case"opacity":k.gradient&&!k[b]("stroke-opacity")&&q(i,{"stroke-opacity":p>1?p/100:p});case"fill-opacity":if(k.gradient){H=a._g.doc.getElementById(i.getAttribute("fill").replace(/^url\(#|\)$/g,l)),H&&(I=H.getElementsByTagName("stop"),q(I[I.length-1],{"stop-opacity":p}));break};default:o=="font-size"&&(p=e(p,10)+"px");var J=o.replace(/(\-.)/g,function(a){return a.substring(1).toUpperCase()});i.style[J]=p,d._.dirty=1,i.setAttribute(o,p)}}y(d,f),i.style.visibility=m},x=1.2,y=function(d,f){if(d.type=="text"&&!!(f[b]("text")||f[b]("font")||f[b]("font-size")||f[b]("x")||f[b]("y"))){var g=d.attrs,h=d.node,i=h.firstChild?e(a._g.doc.defaultView.getComputedStyle(h.firstChild,l).getPropertyValue("font-size"),10):10;if(f[b]("text")){g.text=f.text;while(h.firstChild)h.removeChild(h.firstChild);var j=c(f.text).split("\n"),k=[],m;for(var n=0,o=j.length;n<o;n++)m=q("tspan"),n&&q(m,{dy:i*x,x:g.x}),m.appendChild(a._g.doc.createTextNode(j[n])),h.appendChild(m),k[n]=m}else{k=h.getElementsByTagName("tspan");for(n=0,o=k.length;n<o;n++)n?q(k[n],{dy:i*x,x:g.x}):q(k[0],{dy:0})}q(h,{x:g.x,y:g.y}),d._.dirty=1;var p=d._getBBox(),r=g.y-(p.y+p.height/2);r&&a.is(r,"finite")&&q(k[0],{dy:r})}},z=function(b,c){var d=0,e=0;this[0]=this.node=b,b.raphael=!0,this.id=a._oid++,b.raphaelid=this.id,this.matrix=a.matrix(),this.realPath=null,this.paper=c,this.attrs=this.attrs||{},this._={transform:[],sx:1,sy:1,deg:0,dx:0,dy:0,dirty:1},!c.bottom&&(c.bottom=this),this.prev=c.top,c.top&&(c.top.next=this),c.top=this,this.next=null},A=a.el;z.prototype=A,A.constructor=z,a._engine.path=function(a,b){var c=q("path");b.canvas&&b.canvas.appendChild(c);var d=new z(c,b);d.type="path",w(d,{fill:"none",stroke:"#000",path:a});return d},A.rotate=function(a,b,e){if(this.removed)return this;a=c(a).split(j),a.length-1&&(b=d(a[1]),e=d(a[2])),a=d(a[0]),e==null&&(b=e);if(b==null||e==null){var f=this.getBBox(1);b=f.x+f.width/2,e=f.y+f.height/2}this.transform(this._.transform.concat([["r",a,b,e]]));return this},A.scale=function(a,b,e,f){if(this.removed)return this;a=c(a).split(j),a.length-1&&(b=d(a[1]),e=d(a[2]),f=d(a[3])),a=d(a[0]),b==null&&(b=a),f==null&&(e=f);if(e==null||f==null)var g=this.getBBox(1);e=e==null?g.x+g.width/2:e,f=f==null?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,b,e,f]]));return this},A.translate=function(a,b){if(this.removed)return this;a=c(a).split(j),a.length-1&&(b=d(a[1])),a=d(a[0])||0,b=+b||0,this.transform(this._.transform.concat([["t",a,b]]));return this},A.transform=function(c){var d=this._;if(c==null)return d.transform;a._extractTransform(this,c),this.clip&&q(this.clip,{transform:this.matrix.invert()}),this.pattern&&s(this),this.node&&q(this.node,{transform:this.matrix});if(d.sx!=1||d.sy!=1){var e=this.attrs[b]("stroke-width")?this.attrs["stroke-width"]:1;this.attr({"stroke-width":e})}return this},A.hide=function(){!this.removed&&this.paper.safari(this.node.style.display="none");return this},A.show=function(){!this.removed&&this.paper.safari(this.node.style.display="");return this},A.remove=function(){if(!this.removed&&!!this.node.parentNode){var b=this.paper;b.__set__&&b.__set__.exclude(this),k.unbind("raphael.*.*."+this.id),this.gradient&&b.defs.removeChild(this.gradient),a._tear(this,b),this.node.parentNode.tagName.toLowerCase()=="a"?this.node.parentNode.parentNode.removeChild(this.node.parentNode):this.node.parentNode.removeChild(this.node);for(var c in this)this[c]=typeof this[c]=="function"?a._removedFactory(c):null;this.removed=!0}},A._getBBox=function(){if(this.node.style.display=="none"){this.show();var a=!0}var b={};try{b=this.node.getBBox()}catch(c){}finally{b=b||{}}a&&this.hide();return b},A.attr=function(c,d){if(this.removed)return this;if(c==null){var e={};for(var f in this.attrs)this.attrs[b](f)&&(e[f]=this.attrs[f]);e.gradient&&e.fill=="none"&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform;return e}if(d==null&&a.is(c,"string")){if(c=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;if(c=="transform")return this._.transform;var g=c.split(j),h={};for(var i=0,l=g.length;i<l;i++)c=g[i],c in this.attrs?h[c]=this.attrs[c]:a.is(this.paper.customAttributes[c],"function")?h[c]=this.paper.customAttributes[c].def:h[c]=a._availableAttrs[c];return l-1?h:h[g[0]]}if(d==null&&a.is(c,"array")){h={};for(i=0,l=c.length;i<l;i++)h[c[i]]=this.attr(c[i]);return h}if(d!=null){var m={};m[c]=d}else c!=null&&a.is(c,"object")&&(m=c);for(var n in m)k("raphael.attr."+n+"."+this.id,this,m[n]);for(n in this.paper.customAttributes)if(this.paper.customAttributes[b](n)&&m[b](n)&&a.is(this.paper.customAttributes[n],"function")){var o=this.paper.customAttributes[n].apply(this,[].concat(m[n]));this.attrs[n]=m[n];for(var p in o)o[b](p)&&(m[p]=o[p])}w(this,m);return this},A.toFront=function(){if(this.removed)return this;this.node.parentNode.tagName.toLowerCase()=="a"?this.node.parentNode.parentNode.appendChild(this.node.parentNode):this.node.parentNode.appendChild(this.node);var b=this.paper;b.top!=this&&a._tofront(this,b);return this},A.toBack=function(){if(this.removed)return this;var b=this.node.parentNode;b.tagName.toLowerCase()=="a"?b.parentNode.insertBefore(this.node.parentNode,this.node.parentNode.parentNode.firstChild):b.firstChild!=this.node&&b.insertBefore(this.node,this.node.parentNode.firstChild),a._toback(this,this.paper);var c=this.paper;return this},A.insertAfter=function(b){if(this.removed)return this;var c=b.node||b[b.length-1].node;c.nextSibling?c.parentNode.insertBefore(this.node,c.nextSibling):c.parentNode.appendChild(this.node),a._insertafter(this,b,this.paper);return this},A.insertBefore=function(b){if(this.removed)return this;var c=b.node||b[0].node;c.parentNode.insertBefore(this.node,c),a._insertbefore(this,b,this.paper);return this},A.blur=function(b){var c=this;if(+b!==0){var d=q("filter"),e=q("feGaussianBlur");c.attrs.blur=b,d.id=a.createUUID(),q(e,{stdDeviation:+b||1.5}),d.appendChild(e),c.paper.defs.appendChild(d),c._blur=d,q(c.node,{filter:"url(#"+d.id+")"})}else c._blur&&(c._blur.parentNode.removeChild(c._blur),delete c._blur,delete c.attrs.blur),c.node.removeAttribute("filter")},a._engine.circle=function(a,b,c,d){var e=q("circle");a.canvas&&a.canvas.appendChild(e);var f=new z(e,a);f.attrs={cx:b,cy:c,r:d,fill:"none",stroke:"#000"},f.type="circle",q(e,f.attrs);return f},a._engine.rect=function(a,b,c,d,e,f){var g=q("rect");a.canvas&&a.canvas.appendChild(g);var h=new z(g,a);h.attrs={x:b,y:c,width:d,height:e,r:f||0,rx:f||0,ry:f||0,fill:"none",stroke:"#000"},h.type="rect",q(g,h.attrs);return h},a._engine.ellipse=function(a,b,c,d,e){var f=q("ellipse");a.canvas&&a.canvas.appendChild(f);var g=new z(f,a);g.attrs={cx:b,cy:c,rx:d,ry:e,fill:"none",stroke:"#000"},g.type="ellipse",q(f,g.attrs);return g},a._engine.image=function(a,b,c,d,e,f){var g=q("image");q(g,{x:c,y:d,width:e,height:f,preserveAspectRatio:"none"}),g.setAttributeNS(n,"href",b),a.canvas&&a.canvas.appendChild(g);var h=new z(g,a);h.attrs={x:c,y:d,width:e,height:f,src:b},h.type="image";return h},a._engine.text=function(b,c,d,e){var f=q("text");b.canvas&&b.canvas.appendChild(f);var g=new z(f,b);g.attrs={x:c,y:d,"text-anchor":"middle",text:e,font:a._availableAttrs.font,stroke:"none",fill:"#000"},g.type="text",w(g,g.attrs);return g},a._engine.setSize=function(a,b){this.width=a||this.width,this.height=b||this.height,this.canvas.setAttribute("width",this.width),this.canvas.setAttribute("height",this.height),this._viewBox&&this.setViewBox.apply(this,this._viewBox);return this},a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b&&b.container,d=b.x,e=b.y,f=b.width,g=b.height;if(!c)throw new Error("SVG container not found.");var h=q("svg"),i="overflow:hidden;",j;d=d||0,e=e||0,f=f||512,g=g||342,q(h,{height:g,version:1.1,width:f,xmlns:"http://www.w3.org/2000/svg"}),c==1?(h.style.cssText=i+"position:absolute;left:"+d+"px;top:"+e+"px",a._g.doc.body.appendChild(h),j=1):(h.style.cssText=i+"position:relative",c.firstChild?c.insertBefore(h,c.firstChild):c.appendChild(h)),c=new a._Paper,c.width=f,c.height=g,c.canvas=h,c.clear(),c._left=c._top=0,j&&(c.renderfix=function(){}),c.renderfix();return c},a._engine.setViewBox=function(a,b,c,d,e){k("raphael.setViewBox",this,this._viewBox,[a,b,c,d,e]);var f=g(c/this.width,d/this.height),h=this.top,i=e?"meet":"xMinYMin",j,l;a==null?(this._vbSize&&(f=1),delete this._vbSize,j="0 0 "+this.width+m+this.height):(this._vbSize=f,j=a+m+b+m+c+m+d),q(this.canvas,{viewBox:j,preserveAspectRatio:i});while(f&&h)l="stroke-width"in h.attrs?h.attrs["stroke-width"]:1,h.attr({"stroke-width":l}),h._.dirty=1,h._.dirtyT=1,h=h.prev;this._viewBox=[a,b,c,d,!!e];return this},a.prototype.renderfix=function(){var a=this.canvas,b=a.style,c;try{c=a.getScreenCTM()||a.createSVGMatrix()}catch(d){c=a.createSVGMatrix()}var e=-c.e%1,f=-c.f%1;if(e||f)e&&(this._left=(this._left+e)%1,b.left=this._left+"px"),f&&(this._top=(this._top+f)%1,b.top=this._top+"px")},a.prototype.clear=function(){a.eve("raphael.clear",this);var b=this.canvas;while(b.firstChild)b.removeChild(b.firstChild);this.bottom=this.top=null,(this.desc=q("desc")).appendChild(a._g.doc.createTextNode("Created with Raphaël "+a.version)),b.appendChild(this.desc),b.appendChild(this.defs=q("defs"))},a.prototype.remove=function(){k("raphael.remove",this),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null};var B=a.st;for(var C in A)A[b](C)&&!B[b](C)&&(B[C]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(C))}(window.Raphael),window.Raphael.vml&&function(a){var b="hasOwnProperty",c=String,d=parseFloat,e=Math,f=e.round,g=e.max,h=e.min,i=e.abs,j="fill",k=/[, ]+/,l=a.eve,m=" progid:DXImageTransform.Microsoft",n=" ",o="",p={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},q=/([clmz]),?([^clmz]*)/gi,r=/ progid:\S+Blur\([^\)]+\)/g,s=/-?[^,\s-]+/g,t="position:absolute;left:0;top:0;width:1px;height:1px",u=21600,v={path:1,rect:1,image:1},w={circle:1,ellipse:1},x=function(b){var d=/[ahqstv]/ig,e=a._pathToAbsolute;c(b).match(d)&&(e=a._path2curve),d=/[clmz]/g;if(e==a._pathToAbsolute&&!c(b).match(d)){var g=c(b).replace(q,function(a,b,c){var d=[],e=b.toLowerCase()=="m",g=p[b];c.replace(s,function(a){e&&d.length==2&&(g+=d+p[b=="m"?"l":"L"],d=[]),d.push(f(a*u))});return g+d});return g}var h=e(b),i,j;g=[];for(var k=0,l=h.length;k<l;k++){i=h[k],j=h[k][0].toLowerCase(),j=="z"&&(j="x");for(var m=1,r=i.length;m<r;m++)j+=f(i[m]*u)+(m!=r-1?",":o);g.push(j)}return g.join(n)},y=function(b,c,d){var e=a.matrix();e.rotate(-b,.5,.5);return{dx:e.x(c,d),dy:e.y(c,d)}},z=function(a,b,c,d,e,f){var g=a._,h=a.matrix,k=g.fillpos,l=a.node,m=l.style,o=1,p="",q,r=u/b,s=u/c;m.visibility="hidden";if(!!b&&!!c){l.coordsize=i(r)+n+i(s),m.rotation=f*(b*c<0?-1:1);if(f){var t=y(f,d,e);d=t.dx,e=t.dy}b<0&&(p+="x"),c<0&&(p+=" y")&&(o=-1),m.flip=p,l.coordorigin=d*-r+n+e*-s;if(k||g.fillsize){var v=l.getElementsByTagName(j);v=v&&v[0],l.removeChild(v),k&&(t=y(f,h.x(k[0],k[1]),h.y(k[0],k[1])),v.position=t.dx*o+n+t.dy*o),g.fillsize&&(v.size=g.fillsize[0]*i(b)+n+g.fillsize[1]*i(c)),l.appendChild(v)}m.visibility="visible"}};a.toString=function(){return"Your browser doesn’t support SVG. Falling down to VML.\nYou are running Raphaël "+this.version};var A=function(a,b,d){var e=c(b).toLowerCase().split("-"),f=d?"end":"start",g=e.length,h="classic",i="medium",j="medium";while(g--)switch(e[g]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":h=e[g];break;case"wide":case"narrow":j=e[g];break;case"long":case"short":i=e[g]}var k=a.node.getElementsByTagName("stroke")[0];k[f+"arrow"]=h,k[f+"arrowlength"]=i,k[f+"arrowwidth"]=j},B=function(e,i){e.attrs=e.attrs||{};var l=e.node,m=e.attrs,p=l.style,q,r=v[e.type]&&(i.x!=m.x||i.y!=m.y||i.width!=m.width||i.height!=m.height||i.cx!=m.cx||i.cy!=m.cy||i.rx!=m.rx||i.ry!=m.ry||i.r!=m.r),s=w[e.type]&&(m.cx!=i.cx||m.cy!=i.cy||m.r!=i.r||m.rx!=i.rx||m.ry!=i.ry),t=e;for(var y in i)i[b](y)&&(m[y]=i[y]);r&&(m.path=a._getPath[e.type](e),e._.dirty=1),i.href&&(l.href=i.href),i.title&&(l.title=i.title),i.target&&(l.target=i.target),i.cursor&&(p.cursor=i.cursor),"blur"in i&&e.blur(i.blur);if(i.path&&e.type=="path"||r)l.path=x(~c(m.path).toLowerCase().indexOf("r")?a._pathToAbsolute(m.path):m.path),e.type=="image"&&(e._.fillpos=[m.x,m.y],e._.fillsize=[m.width,m.height],z(e,1,1,0,0,0));"transform"in i&&e.transform(i.transform);if(s){var B=+m.cx,D=+m.cy,E=+m.rx||+m.r||0,G=+m.ry||+m.r||0;l.path=a.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x",f((B-E)*u),f((D-G)*u),f((B+E)*u),f((D+G)*u),f(B*u))}if("clip-rect"in i){var H=c(i["clip-rect"]).split(k);if(H.length==4){H[2]=+H[2]+ +H[0],H[3]=+H[3]+ +H[1];var I=l.clipRect||a._g.doc.createElement("div"),J=I.style;J.clip=a.format("rect({1}px {2}px {3}px {0}px)",H),l.clipRect||(J.position="absolute",J.top=0,J.left=0,J.width=e.paper.width+"px",J.height=e.paper.height+"px",l.parentNode.insertBefore(I,l),I.appendChild(l),l.clipRect=I)}i["clip-rect"]||l.clipRect&&(l.clipRect.style.clip="auto")}if(e.textpath){var K=e.textpath.style;i.font&&(K.font=i.font),i["font-family"]&&(K.fontFamily='"'+i["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g,o)+'"'),i["font-size"]&&(K.fontSize=i["font-size"]),i["font-weight"]&&(K.fontWeight=i["font-weight"]),i["font-style"]&&(K.fontStyle=i["font-style"])}"arrow-start"in i&&A(t,i["arrow-start"]),"arrow-end"in i&&A(t,i["arrow-end"],1);if(i.opacity!=null||i["stroke-width"]!=null||i.fill!=null||i.src!=null||i.stroke!=null||i["stroke-width"]!=null||i["stroke-opacity"]!=null||i["fill-opacity"]!=null||i["stroke-dasharray"]!=null||i["stroke-miterlimit"]!=null||i["stroke-linejoin"]!=null||i["stroke-linecap"]!=null){var L=l.getElementsByTagName(j),M=!1;L=L&&L[0],!L&&(M=L=F(j)),e.type=="image"&&i.src&&(L.src=i.src),i.fill&&(L.on=!0);if(L.on==null||i.fill=="none"||i.fill===null)L.on=!1;if(L.on&&i.fill){var N=c(i.fill).match(a._ISURL);if(N){L.parentNode==l&&l.removeChild(L),L.rotate=!0,L.src=N[1],L.type="tile";var O=e.getBBox(1);L.position=O.x+n+O.y,e._.fillpos=[O.x,O.y],a._preload(N[1],function(){e._.fillsize=[this.offsetWidth,this.offsetHeight]})}else L.color=a.getRGB(i.fill).hex,L.src=o,L.type="solid",a.getRGB(i.fill).error&&(t.type in{circle:1,ellipse:1}||c(i.fill).charAt()!="r")&&C(t,i.fill,L)&&(m.fill="none",m.gradient=i.fill,L.rotate=!1)}if("fill-opacity"in i||"opacity"in i){var P=((+m["fill-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+a.getRGB(i.fill).o+1||2)-1);P=h(g(P,0),1),L.opacity=P,L.src&&(L.color="none")}l.appendChild(L);var Q=l.getElementsByTagName("stroke")&&l.getElementsByTagName("stroke")[0],T=!1;!Q&&(T=Q=F("stroke"));if(i.stroke&&i.stroke!="none"||i["stroke-width"]||i["stroke-opacity"]!=null||i["stroke-dasharray"]||i["stroke-miterlimit"]||i["stroke-linejoin"]||i["stroke-linecap"])Q.on=!0;(i.stroke=="none"||i.stroke===null||Q.on==null||i.stroke==0||i["stroke-width"]==0)&&(Q.on=!1);var U=a.getRGB(i.stroke);Q.on&&i.stroke&&(Q.color=U.hex),P=((+m["stroke-opacity"]+1||2)-1)*((+m.opacity+1||2)-1)*((+U.o+1||2)-1);var V=(d(i["stroke-width"])||1)*.75;P=h(g(P,0),1),i["stroke-width"]==null&&(V=m["stroke-width"]),i["stroke-width"]&&(Q.weight=V),V&&V<1&&(P*=V)&&(Q.weight=1),Q.opacity=P,i["stroke-linejoin"]&&(Q.joinstyle=i["stroke-linejoin"]||"miter"),Q.miterlimit=i["stroke-miterlimit"]||8,i["stroke-linecap"]&&(Q.endcap=i["stroke-linecap"]=="butt"?"flat":i["stroke-linecap"]=="square"?"square":"round");if(i["stroke-dasharray"]){var W={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};Q.dashstyle=W[b](i["stroke-dasharray"])?W[i["stroke-dasharray"]]:o}T&&l.appendChild(Q)}if(t.type=="text"){t.paper.canvas.style.display=o;var X=t.paper.span,Y=100,Z=m.font&&m.font.match(/\d+(?:\.\d*)?(?=px)/);p=X.style,m.font&&(p.font=m.font),m["font-family"]&&(p.fontFamily=m["font-family"]),m["font-weight"]&&(p.fontWeight=m["font-weight"]),m["font-style"]&&(p.fontStyle=m["font-style"]),Z=d(m["font-size"]||Z&&Z[0])||10,p.fontSize=Z*Y+"px",t.textpath.string&&(X.innerHTML=c(t.textpath.string).replace(/</g,"&#60;").replace(/&/g,"&#38;").replace(/\n/g,"<br>"));var $=X.getBoundingClientRect();t.W=m.w=($.right-$.left)/Y,t.H=m.h=($.bottom-$.top)/Y,t.X=m.x,t.Y=m.y+t.H/2,("x"in i||"y"in i)&&(t.path.v=a.format("m{0},{1}l{2},{1}",f(m.x*u),f(m.y*u),f(m.x*u)+1));var _=["x","y","text","font","font-family","font-weight","font-style","font-size"];for(var ba=0,bb=_.length;ba<bb;ba++)if(_[ba]in i){t._.dirty=1;break}switch(m["text-anchor"]){case"start":t.textpath.style["v-text-align"]="left",t.bbx=t.W/2;break;case"end":t.textpath.style["v-text-align"]="right",t.bbx=-t.W/2;break;default:t.textpath.style["v-text-align"]="center",t.bbx=0}t.textpath.style["v-text-kern"]=!0}},C=function(b,f,g){b.attrs=b.attrs||{};var h=b.attrs,i=Math.pow,j,k,l="linear",m=".5 .5";b.attrs.gradient=f,f=c(f).replace(a._radial_gradient,function(a,b,c){l="radial",b&&c&&(b=d(b),c=d(c),i(b-.5,2)+i(c-.5,2)>.25&&(c=e.sqrt(.25-i(b-.5,2))*((c>.5)*2-1)+.5),m=b+n+c);return o}),f=f.split(/\s*\-\s*/);if(l=="linear"){var p=f.shift();p=-d(p);if(isNaN(p))return null}var q=a._parseDots(f);if(!q)return null;b=b.shape||b.node;if(q.length){b.removeChild(g),g.on=!0,g.method="none",g.color=q[0].color,g.color2=q[q.length-1].color;var r=[];for(var s=0,t=q.length;s<t;s++)q[s].offset&&r.push(q[s].offset+n+q[s].color);g.colors=r.length?r.join():"0% "+g.color,l=="radial"?(g.type="gradientTitle",g.focus="100%",g.focussize="0 0",g.focusposition=m,g.angle=0):(g.type="gradient",g.angle=(270-p)%360),b.appendChild(g)}return 1},D=function(b,c){this[0]=this.node=b,b.raphael=!0,this.id=a._oid++,b.raphaelid=this.id,this.X=0,this.Y=0,this.attrs={},this.paper=c,this.matrix=a.matrix(),this._={transform:[],sx:1,sy:1,dx:0,dy:0,deg:0,dirty:1,dirtyT:1},!c.bottom&&(c.bottom=this),this.prev=c.top,c.top&&(c.top.next=this),c.top=this,this.next=null},E=a.el;D.prototype=E,E.constructor=D,E.transform=function(b){if(b==null)return this._.transform;var d=this.paper._viewBoxShift,e=d?"s"+[d.scale,d.scale]+"-1-1t"+[d.dx,d.dy]:o,f;d&&(f=b=c(b).replace(/\.{3}|\u2026/g,this._.transform||o)),a._extractTransform(this,e+b);var g=this.matrix.clone(),h=this.skew,i=this.node,j,k=~c(this.attrs.fill).indexOf("-"),l=!c(this.attrs.fill).indexOf("url(");g.translate(-0.5,-0.5);if(l||k||this.type=="image"){h.matrix="1 0 0 1",h.offset="0 0",j=g.split();if(k&&j.noRotation||!j.isSimple){i.style.filter=g.toFilter();var m=this.getBBox(),p=this.getBBox(1),q=m.x-p.x,r=m.y-p.y;i.coordorigin=q*-u+n+r*-u,z(this,1,1,q,r,0)}else i.style.filter=o,z(this,j.scalex,j.scaley,j.dx,j.dy,j.rotate)}else i.style.filter=o,h.matrix=c(g),h.offset=g.offset();f&&(this._.transform=f);return this},E.rotate=function(a,b,e){if(this.removed)return this;if(a!=null){a=c(a).split(k),a.length-1&&(b=d(a[1]),e=d(a[2])),a=d(a[0]),e==null&&(b=e);if(b==null||e==null){var f=this.getBBox(1);b=f.x+f.width/2,e=f.y+f.height/2}this._.dirtyT=1,this.transform(this._.transform.concat([["r",a,b,e]]));return this}},E.translate=function(a,b){if(this.removed)return this;a=c(a).split(k),a.length-1&&(b=d(a[1])),a=d(a[0])||0,b=+b||0,this._.bbox&&(this._.bbox.x+=a,this._.bbox.y+=b),this.transform(this._.transform.concat([["t",a,b]]));return this},E.scale=function(a,b,e,f){if(this.removed)return this;a=c(a).split(k),a.length-1&&(b=d(a[1]),e=d(a[2]),f=d(a[3]),isNaN(e)&&(e=null),isNaN(f)&&(f=null)),a=d(a[0]),b==null&&(b=a),f==null&&(e=f);if(e==null||f==null)var g=this.getBBox(1);e=e==null?g.x+g.width/2:e,f=f==null?g.y+g.height/2:f,this.transform(this._.transform.concat([["s",a,b,e,f]])),this._.dirtyT=1;return this},E.hide=function(){!this.removed&&(this.node.style.display="none");return this},E.show=function(){!this.removed&&(this.node.style.display=o);return this},E._getBBox=function(){if(this.removed)return{};return{x:this.X+(this.bbx||0)-this.W/2,y:this.Y-this.H,width:this.W,height:this.H}},E.remove=function(){if(!this.removed&&!!this.node.parentNode){this.paper.__set__&&this.paper.__set__.exclude(this),a.eve.unbind("raphael.*.*."+this.id),a._tear(this,this.paper),this.node.parentNode.removeChild(this.node),this.shape&&this.shape.parentNode.removeChild(this.shape);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;this.removed=!0}},E.attr=function(c,d){if(this.removed)return this;if(c==null){var e={};for(var f in this.attrs)this.attrs[b](f)&&(e[f]=this.attrs[f]);e.gradient&&e.fill=="none"&&(e.fill=e.gradient)&&delete e.gradient,e.transform=this._.transform;return e}if(d==null&&a.is(c,"string")){if(c==j&&this.attrs.fill=="none"&&this.attrs.gradient)return this.attrs.gradient;var g=c.split(k),h={};for(var i=0,m=g.length;i<m;i++)c=g[i],c in this.attrs?h[c]=this.attrs[c]:a.is(this.paper.customAttributes[c],"function")?h[c]=this.paper.customAttributes[c].def:h[c]=a._availableAttrs[c];return m-1?h:h[g[0]]}if(this.attrs&&d==null&&a.is(c,"array")){h={};for(i=0,m=c.length;i<m;i++)h[c[i]]=this.attr(c[i]);return h}var n;d!=null&&(n={},n[c]=d),d==null&&a.is(c,"object")&&(n=c);for(var o in n)l("raphael.attr."+o+"."+this.id,this,n[o]);if(n){for(o in this.paper.customAttributes)if(this.paper.customAttributes[b](o)&&n[b](o)&&a.is(this.paper.customAttributes[o],"function")){var p=this.paper.customAttributes[o].apply(this,[].concat(n[o]));this.attrs[o]=n[o];for(var q in p)p[b](q)&&(n[q]=p[q])}n.text&&this.type=="text"&&(this.textpath.string=n.text),B(this,n)}return this},E.toFront=function(){!this.removed&&this.node.parentNode.appendChild(this.node),this.paper&&this.paper.top!=this&&a._tofront(this,this.paper);return this},E.toBack=function(){if(this.removed)return this;this.node.parentNode.firstChild!=this.node&&(this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild),a._toback(this,this.paper));return this},E.insertAfter=function(b){if(this.removed)return this;b.constructor==a.st.constructor&&(b=b[b.length-1]),b.node.nextSibling?b.node.parentNode.insertBefore(this.node,b.node.nextSibling):b.node.parentNode.appendChild(this.node),a._insertafter(this,b,this.paper);return this},E.insertBefore=function(b){if(this.removed)return this;b.constructor==a.st.constructor&&(b=b[0]),b.node.parentNode.insertBefore(this.node,b.node),a._insertbefore(this,b,this.paper);return this},E.blur=function(b){var c=this.node.runtimeStyle,d=c.filter;d=d.replace(r,o),+b!==0?(this.attrs.blur=b,c.filter=d+n+m+".Blur(pixelradius="+(+b||1.5)+")",c.margin=a.format("-{0}px 0 0 -{0}px",f(+b||1.5))):(c.filter=d,c.margin=0,delete this.attrs.blur)},a._engine.path=function(a,b){var c=F("shape");c.style.cssText=t,c.coordsize=u+n+u,c.coordorigin=b.coordorigin;var d=new D(c,b),e={fill:"none",stroke:"#000"};a&&(e.path=a),d.type="path",d.path=[],d.Path=o,B(d,e),b.canvas.appendChild(c);var f=F("skew");f.on=!0,c.appendChild(f),d.skew=f,d.transform(o);return d},a._engine.rect=function(b,c,d,e,f,g){var h=a._rectPath(c,d,e,f,g),i=b.path(h),j=i.attrs;i.X=j.x=c,i.Y=j.y=d,i.W=j.width=e,i.H=j.height=f,j.r=g,j.path=h,i.type="rect";return i},a._engine.ellipse=function(a,b,c,d,e){var f=a.path(),g=f.attrs;f.X=b-d,f.Y=c-e,f.W=d*2,f.H=e*2,f.type="ellipse",B(f,{cx:b,cy:c,rx:d,ry:e});return f},a._engine.circle=function(a,b,c,d){var e=a.path(),f=e.attrs;e.X=b-d,e.Y=c-d,e.W=e.H=d*2,e.type="circle",B(e,{cx:b,cy:c,r:d});return e},a._engine.image=function(b,c,d,e,f,g){var h=a._rectPath(d,e,f,g),i=b.path(h).attr({stroke:"none"}),k=i.attrs,l=i.node,m=l.getElementsByTagName(j)[0];k.src=c,i.X=k.x=d,i.Y=k.y=e,i.W=k.width=f,i.H=k.height=g,k.path=h,i.type="image",m.parentNode==l&&l.removeChild(m),m.rotate=!0,m.src=c,m.type="tile",i._.fillpos=[d,e],i._.fillsize=[f,g],l.appendChild(m),z(i,1,1,0,0,0);return i},a._engine.text=function(b,d,e,g){var h=F("shape"),i=F("path"),j=F("textpath");d=d||0,e=e||0,g=g||"",i.v=a.format("m{0},{1}l{2},{1}",f(d*u),f(e*u),f(d*u)+1),i.textpathok=!0,j.string=c(g),j.on=!0,h.style.cssText=t,h.coordsize=u+n+u,h.coordorigin="0 0";var k=new D(h,b),l={fill:"#000",stroke:"none",font:a._availableAttrs.font,text:g};k.shape=h,k.path=i,k.textpath=j,k.type="text",k.attrs.text=c(g),k.attrs.x=d,k.attrs.y=e,k.attrs.w=1,k.attrs.h=1,B(k,l),h.appendChild(j),h.appendChild(i),b.canvas.appendChild(h);var m=F("skew");m.on=!0,h.appendChild(m),k.skew=m,k.transform(o);return k},a._engine.setSize=function(b,c){var d=this.canvas.style;this.width=b,this.height=c,b==+b&&(b+="px"),c==+c&&(c+="px"),d.width=b,d.height=c,d.clip="rect(0 "+b+" "+c+" 0)",this._viewBox&&a._engine.setViewBox.apply(this,this._viewBox);return this},a._engine.setViewBox=function(b,c,d,e,f){a.eve("raphael.setViewBox",this,this._viewBox,[b,c,d,e,f]);var h=this.width,i=this.height,j=1/g(d/h,e/i),k,l;f&&(k=i/e,l=h/d,d*k<h&&(b-=(h-d*k)/2/k),e*l<i&&(c-=(i-e*l)/2/l)),this._viewBox=[b,c,d,e,!!f],this._viewBoxShift={dx:-b,dy:-c,scale:j},this.forEach(function(a){a.transform("...")});return this};var F;a._engine.initWin=function(a){var b=a.document;b.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{!b.namespaces.rvml&&b.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),F=function(a){return b.createElement("<rvml:"+a+' class="rvml">')}}catch(c){F=function(a){return b.createElement("<"+a+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},a._engine.initWin(a._g.win),a._engine.create=function(){var b=a._getContainer.apply(0,arguments),c=b.container,d=b.height,e,f=b.width,g=b.x,h=b.y;if(!c)throw new Error("VML container not found.");var i=new a._Paper,j=i.canvas=a._g.doc.createElement("div"),k=j.style;g=g||0,h=h||0,f=f||512,d=d||342,i.width=f,i.height=d,f==+f&&(f+="px"),d==+d&&(d+="px"),i.coordsize=u*1e3+n+u*1e3,i.coordorigin="0 0",i.span=a._g.doc.createElement("span"),i.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",j.appendChild(i.span),k.cssText=a.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",f,d),c==1?(a._g.doc.body.appendChild(j),k.left=g+"px",k.top=h+"px",k.position="absolute"):c.firstChild?c.insertBefore(j,c.firstChild):c.appendChild(j),i.renderfix=function(){};return i},a.prototype.clear=function(){a.eve("raphael.clear",this),this.canvas.innerHTML=o,this.span=a._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},a.prototype.remove=function(){a.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var b in this)this[b]=typeof this[b]=="function"?a._removedFactory(b):null;return!0};var G=a.st;for(var H in E)E[b](H)&&!G[b](H)&&(G[H]=function(a){return function(){var b=arguments;return this.forEach(function(c){c[a].apply(c,b)})}}(H))}(window.Raphael)
1
+ !function t(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.Raphael=r():e.Raphael=r()}(this,function(){return function(t){function e(i){if(r[i])return r[i].exports;var n=r[i]={exports:{},id:i,loaded:!1};return t[i].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){var i,n;i=[r(1),r(3),r(4)],n=function(t){return t}.apply(e,i),!(void 0!==n&&(t.exports=n))},function(t,e,r){var i,n;i=[r(2)],n=function(t){function e(r){if(e.is(r,"function"))return w?r():t.on("raphael.DOMload",r);if(e.is(r,Q))return e._engine.create[z](e,r.splice(0,3+e.is(r[0],$))).add(r);var i=Array.prototype.slice.call(arguments,0);if(e.is(i[i.length-1],"function")){var n=i.pop();return w?n.call(e._engine.create[z](e,i)):t.on("raphael.DOMload",function(){n.call(e._engine.create[z](e,i))})}return e._engine.create[z](e,arguments)}function r(t){if("function"==typeof t||Object(t)!==t)return t;var e=new t.constructor;for(var i in t)t[A](i)&&(e[i]=r(t[i]));return e}function i(t,e){for(var r=0,i=t.length;r<i;r++)if(t[r]===e)return t.push(t.splice(r,1)[0])}function n(t,e,r){function n(){var a=Array.prototype.slice.call(arguments,0),s=a.join("␀"),o=n.cache=n.cache||{},l=n.count=n.count||[];return o[A](s)?(i(l,s),r?r(o[s]):o[s]):(l.length>=1e3&&delete o[l.shift()],l.push(s),o[s]=t[z](e,a),r?r(o[s]):o[s])}return n}function a(){return this.hex}function s(t,e){for(var r=[],i=0,n=t.length;n-2*!e>i;i+=2){var a=[{x:+t[i-2],y:+t[i-1]},{x:+t[i],y:+t[i+1]},{x:+t[i+2],y:+t[i+3]},{x:+t[i+4],y:+t[i+5]}];e?i?n-4==i?a[3]={x:+t[0],y:+t[1]}:n-2==i&&(a[2]={x:+t[0],y:+t[1]},a[3]={x:+t[2],y:+t[3]}):a[0]={x:+t[n-2],y:+t[n-1]}:n-4==i?a[3]=a[2]:i||(a[0]={x:+t[i],y:+t[i+1]}),r.push(["C",(-a[0].x+6*a[1].x+a[2].x)/6,(-a[0].y+6*a[1].y+a[2].y)/6,(a[1].x+6*a[2].x-a[3].x)/6,(a[1].y+6*a[2].y-a[3].y)/6,a[2].x,a[2].y])}return r}function o(t,e,r,i,n){var a=-3*e+9*r-9*i+3*n,s=t*a+6*e-12*r+6*i;return t*s-3*e+3*r}function l(t,e,r,i,n,a,s,l,h){null==h&&(h=1),h=h>1?1:h<0?0:h;for(var u=h/2,c=12,f=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],p=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],d=0,g=0;g<c;g++){var v=u*f[g]+u,x=o(v,t,r,n,s),y=o(v,e,i,a,l),m=x*x+y*y;d+=p[g]*Y.sqrt(m)}return u*d}function h(t,e,r,i,n,a,s,o,h){if(!(h<0||l(t,e,r,i,n,a,s,o)<h)){var u=1,c=u/2,f=u-c,p,d=.01;for(p=l(t,e,r,i,n,a,s,o,f);H(p-h)>d;)c/=2,f+=(p<h?1:-1)*c,p=l(t,e,r,i,n,a,s,o,f);return f}}function u(t,e,r,i,n,a,s,o){if(!(W(t,r)<G(n,s)||G(t,r)>W(n,s)||W(e,i)<G(a,o)||G(e,i)>W(a,o))){var l=(t*i-e*r)*(n-s)-(t-r)*(n*o-a*s),h=(t*i-e*r)*(a-o)-(e-i)*(n*o-a*s),u=(t-r)*(a-o)-(e-i)*(n-s);if(u){var c=l/u,f=h/u,p=+c.toFixed(2),d=+f.toFixed(2);if(!(p<+G(t,r).toFixed(2)||p>+W(t,r).toFixed(2)||p<+G(n,s).toFixed(2)||p>+W(n,s).toFixed(2)||d<+G(e,i).toFixed(2)||d>+W(e,i).toFixed(2)||d<+G(a,o).toFixed(2)||d>+W(a,o).toFixed(2)))return{x:c,y:f}}}}function c(t,e){return p(t,e)}function f(t,e){return p(t,e,1)}function p(t,r,i){var n=e.bezierBBox(t),a=e.bezierBBox(r);if(!e.isBBoxIntersect(n,a))return i?0:[];for(var s=l.apply(0,t),o=l.apply(0,r),h=W(~~(s/5),1),c=W(~~(o/5),1),f=[],p=[],d={},g=i?0:[],v=0;v<h+1;v++){var x=e.findDotsAtSegment.apply(e,t.concat(v/h));f.push({x:x.x,y:x.y,t:v/h})}for(v=0;v<c+1;v++)x=e.findDotsAtSegment.apply(e,r.concat(v/c)),p.push({x:x.x,y:x.y,t:v/c});for(v=0;v<h;v++)for(var y=0;y<c;y++){var m=f[v],b=f[v+1],_=p[y],w=p[y+1],k=H(b.x-m.x)<.001?"y":"x",B=H(w.x-_.x)<.001?"y":"x",C=u(m.x,m.y,b.x,b.y,_.x,_.y,w.x,w.y);if(C){if(d[C.x.toFixed(4)]==C.y.toFixed(4))continue;d[C.x.toFixed(4)]=C.y.toFixed(4);var S=m.t+H((C[k]-m[k])/(b[k]-m[k]))*(b.t-m.t),A=_.t+H((C[B]-_[B])/(w[B]-_[B]))*(w.t-_.t);S>=0&&S<=1.001&&A>=0&&A<=1.001&&(i?g++:g.push({x:C.x,y:C.y,t1:G(S,1),t2:G(A,1)}))}}return g}function d(t,r,i){t=e._path2curve(t),r=e._path2curve(r);for(var n,a,s,o,l,h,u,c,f,d,g=i?0:[],v=0,x=t.length;v<x;v++){var y=t[v];if("M"==y[0])n=l=y[1],a=h=y[2];else{"C"==y[0]?(f=[n,a].concat(y.slice(1)),n=f[6],a=f[7]):(f=[n,a,n,a,l,h,l,h],n=l,a=h);for(var m=0,b=r.length;m<b;m++){var _=r[m];if("M"==_[0])s=u=_[1],o=c=_[2];else{"C"==_[0]?(d=[s,o].concat(_.slice(1)),s=d[6],o=d[7]):(d=[s,o,s,o,u,c,u,c],s=u,o=c);var w=p(f,d,i);if(i)g+=w;else{for(var k=0,B=w.length;k<B;k++)w[k].segment1=v,w[k].segment2=m,w[k].bez1=f,w[k].bez2=d;g=g.concat(w)}}}}}return g}function g(t,e,r,i,n,a){null!=t?(this.a=+t,this.b=+e,this.c=+r,this.d=+i,this.e=+n,this.f=+a):(this.a=1,this.b=0,this.c=0,this.d=1,this.e=0,this.f=0)}function v(){return this.x+j+this.y}function x(){return this.x+j+this.y+j+this.width+" × "+this.height}function y(t,e,r,i,n,a){function s(t){return((c*t+u)*t+h)*t}function o(t,e){var r=l(t,e);return((d*r+p)*r+f)*r}function l(t,e){var r,i,n,a,o,l;for(n=t,l=0;l<8;l++){if(a=s(n)-t,H(a)<e)return n;if(o=(3*c*n+2*u)*n+h,H(o)<1e-6)break;n-=a/o}if(r=0,i=1,n=t,n<r)return r;if(n>i)return i;for(;r<i;){if(a=s(n),H(a-t)<e)return n;t>a?r=n:i=n,n=(i-r)/2+r}return n}var h=3*e,u=3*(i-e)-h,c=1-h-u,f=3*r,p=3*(n-r)-f,d=1-f-p;return o(t,1/(200*a))}function m(t,e){var r=[],i={};if(this.ms=e,this.times=1,t){for(var n in t)t[A](n)&&(i[ht(n)]=t[n],r.push(ht(n)));r.sort(Bt)}this.anim=i,this.top=r[r.length-1],this.percents=r}function b(r,i,n,a,s,o){n=ht(n);var l,h,u,c=[],f,p,d,v=r.ms,x={},m={},b={};if(a)for(w=0,B=Ee.length;w<B;w++){var _=Ee[w];if(_.el.id==i.id&&_.anim==r){_.percent!=n?(Ee.splice(w,1),u=1):h=_,i.attr(_.totalOrigin);break}}else a=+m;for(var w=0,B=r.percents.length;w<B;w++){if(r.percents[w]==n||r.percents[w]>a*r.top){n=r.percents[w],p=r.percents[w-1]||0,v=v/r.top*(n-p),f=r.percents[w+1],l=r.anim[n];break}a&&i.attr(r.anim[r.percents[w]])}if(l){if(h)h.initstatus=a,h.start=new Date-h.ms*a;else{for(var C in l)if(l[A](C)&&(pt[A](C)||i.paper.customAttributes[A](C)))switch(x[C]=i.attr(C),null==x[C]&&(x[C]=ft[C]),m[C]=l[C],pt[C]){case $:b[C]=(m[C]-x[C])/v;break;case"colour":x[C]=e.getRGB(x[C]);var S=e.getRGB(m[C]);b[C]={r:(S.r-x[C].r)/v,g:(S.g-x[C].g)/v,b:(S.b-x[C].b)/v};break;case"path":var T=Qt(x[C],m[C]),E=T[1];for(x[C]=T[0],b[C]=[],w=0,B=x[C].length;w<B;w++){b[C][w]=[0];for(var M=1,N=x[C][w].length;M<N;M++)b[C][w][M]=(E[w][M]-x[C][w][M])/v}break;case"transform":var L=i._,z=le(L[C],m[C]);if(z)for(x[C]=z.from,m[C]=z.to,b[C]=[],b[C].real=!0,w=0,B=x[C].length;w<B;w++)for(b[C][w]=[x[C][w][0]],M=1,N=x[C][w].length;M<N;M++)b[C][w][M]=(m[C][w][M]-x[C][w][M])/v;else{var F=i.matrix||new g,R={_:{transform:L.transform},getBBox:function(){return i.getBBox(1)}};x[C]=[F.a,F.b,F.c,F.d,F.e,F.f],se(R,m[C]),m[C]=R._.transform,b[C]=[(R.matrix.a-F.a)/v,(R.matrix.b-F.b)/v,(R.matrix.c-F.c)/v,(R.matrix.d-F.d)/v,(R.matrix.e-F.e)/v,(R.matrix.f-F.f)/v]}break;case"csv":var j=I(l[C])[q](k),D=I(x[C])[q](k);if("clip-rect"==C)for(x[C]=D,b[C]=[],w=D.length;w--;)b[C][w]=(j[w]-x[C][w])/v;m[C]=j;break;default:for(j=[][P](l[C]),D=[][P](x[C]),b[C]=[],w=i.paper.customAttributes[C].length;w--;)b[C][w]=((j[w]||0)-(D[w]||0))/v}var V=l.easing,O=e.easing_formulas[V];if(!O)if(O=I(V).match(st),O&&5==O.length){var Y=O;O=function(t){return y(t,+Y[1],+Y[2],+Y[3],+Y[4],v)}}else O=St;if(d=l.start||r.start||+new Date,_={anim:r,percent:n,timestamp:d,start:d+(r.del||0),status:0,initstatus:a||0,stop:!1,ms:v,easing:O,from:x,diff:b,to:m,el:i,callback:l.callback,prev:p,next:f,repeat:o||r.times,origin:i.attr(),totalOrigin:s},Ee.push(_),a&&!h&&!u&&(_.stop=!0,_.start=new Date-v*a,1==Ee.length))return Ne();u&&(_.start=new Date-_.ms*a),1==Ee.length&&Me(Ne)}t("raphael.anim.start."+i.id,i,r)}}function _(t){for(var e=0;e<Ee.length;e++)Ee[e].el.paper==t&&Ee.splice(e--,1)}e.version="2.2.0",e.eve=t;var w,k=/[, ]+/,B={circle:1,rect:1,path:1,ellipse:1,text:1,image:1},C=/\{(\d+)\}/g,S="prototype",A="hasOwnProperty",T={doc:document,win:window},E={was:Object.prototype[A].call(T.win,"Raphael"),is:T.win.Raphael},M=function(){this.ca=this.customAttributes={}},N,L="appendChild",z="apply",P="concat",F="ontouchstart"in T.win||T.win.DocumentTouch&&T.doc instanceof DocumentTouch,R="",j=" ",I=String,q="split",D="click dblclick mousedown mousemove mouseout mouseover mouseup touchstart touchmove touchend touchcancel"[q](j),V={mousedown:"touchstart",mousemove:"touchmove",mouseup:"touchend"},O=I.prototype.toLowerCase,Y=Math,W=Y.max,G=Y.min,H=Y.abs,X=Y.pow,U=Y.PI,$="number",Z="string",Q="array",J="toString",K="fill",tt=Object.prototype.toString,et={},rt="push",it=e._ISURL=/^url\(['"]?(.+?)['"]?\)$/i,nt=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i,at={NaN:1,Infinity:1,"-Infinity":1},st=/^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/,ot=Y.round,lt="setAttribute",ht=parseFloat,ut=parseInt,ct=I.prototype.toUpperCase,ft=e._availableAttrs={"arrow-end":"none","arrow-start":"none",blur:0,"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/","letter-spacing":0,opacity:1,path:"M0,0",r:0,rx:0,ry:0,src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",transform:"",width:0,x:0,y:0,"class":""},pt=e._availableAnimAttrs={blur:$,"clip-rect":"csv",cx:$,cy:$,fill:"colour","fill-opacity":$,"font-size":$,height:$,opacity:$,path:"path",r:$,rx:$,ry:$,stroke:"colour","stroke-opacity":$,"stroke-width":$,transform:"transform",width:$,x:$,y:$},dt=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]/g,gt=/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/,vt={hs:1,rg:1},xt=/,?([achlmqrstvxz]),?/gi,yt=/([achlmrqstvz])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/gi,mt=/([rstm])[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029,]*((-?\d*\.?\d*(?:e[\-+]?\d+)?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*)+)/gi,bt=/(-?\d*\.?\d*(?:e[\-+]?\d+)?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,?[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/gi,_t=e._radial_gradient=/^r(?:\(([^,]+?)[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*([^\)]+?)\))?/,wt={},kt=function(t,e){return t.key-e.key},Bt=function(t,e){return ht(t)-ht(e)},Ct=function(){},St=function(t){return t},At=e._rectPath=function(t,e,r,i,n){return n?[["M",t+n,e],["l",r-2*n,0],["a",n,n,0,0,1,n,n],["l",0,i-2*n],["a",n,n,0,0,1,-n,n],["l",2*n-r,0],["a",n,n,0,0,1,-n,-n],["l",0,2*n-i],["a",n,n,0,0,1,n,-n],["z"]]:[["M",t,e],["l",r,0],["l",0,i],["l",-r,0],["z"]]},Tt=function(t,e,r,i){return null==i&&(i=r),[["M",t,e],["m",0,-i],["a",r,i,0,1,1,0,2*i],["a",r,i,0,1,1,0,-2*i],["z"]]},Et=e._getPath={path:function(t){return t.attr("path")},circle:function(t){var e=t.attrs;return Tt(e.cx,e.cy,e.r)},ellipse:function(t){var e=t.attrs;return Tt(e.cx,e.cy,e.rx,e.ry)},rect:function(t){var e=t.attrs;return At(e.x,e.y,e.width,e.height,e.r)},image:function(t){var e=t.attrs;return At(e.x,e.y,e.width,e.height)},text:function(t){var e=t._getBBox();return At(e.x,e.y,e.width,e.height)},set:function(t){var e=t._getBBox();return At(e.x,e.y,e.width,e.height)}},Mt=e.mapPath=function(t,e){if(!e)return t;var r,i,n,a,s,o,l;for(t=Qt(t),n=0,s=t.length;n<s;n++)for(l=t[n],a=1,o=l.length;a<o;a+=2)r=e.x(l[a],l[a+1]),i=e.y(l[a],l[a+1]),l[a]=r,l[a+1]=i;return t};if(e._g=T,e.type=T.win.SVGAngle||T.doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML","VML"==e.type){var Nt=T.doc.createElement("div"),Lt;if(Nt.innerHTML='<v:shape adj="1"/>',Lt=Nt.firstChild,Lt.style.behavior="url(#default#VML)",!Lt||"object"!=typeof Lt.adj)return e.type=R;Nt=null}e.svg=!(e.vml="VML"==e.type),e._Paper=M,e.fn=N=M.prototype=e.prototype,e._id=0,e.is=function(t,e){return e=O.call(e),"finite"==e?!at[A](+t):"array"==e?t instanceof Array:"null"==e&&null===t||e==typeof t&&null!==t||"object"==e&&t===Object(t)||"array"==e&&Array.isArray&&Array.isArray(t)||tt.call(t).slice(8,-1).toLowerCase()==e},e.angle=function(t,r,i,n,a,s){if(null==a){var o=t-i,l=r-n;return o||l?(180+180*Y.atan2(-l,-o)/U+360)%360:0}return e.angle(t,r,a,s)-e.angle(i,n,a,s)},e.rad=function(t){return t%360*U/180},e.deg=function(t){return Math.round(180*t/U%360*1e3)/1e3},e.snapTo=function(t,r,i){if(i=e.is(i,"finite")?i:10,e.is(t,Q)){for(var n=t.length;n--;)if(H(t[n]-r)<=i)return t[n]}else{t=+t;var a=r%t;if(a<i)return r-a;if(a>t-i)return r-a+t}return r};var zt=e.createUUID=function(t,e){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(t,e).toUpperCase()}}(/[xy]/g,function(t){var e=16*Y.random()|0,r="x"==t?e:3&e|8;return r.toString(16)});e.setWindow=function(r){t("raphael.setWindow",e,T.win,r),T.win=r,T.doc=T.win.document,e._engine.initWin&&e._engine.initWin(T.win)};var Pt=function(t){if(e.vml){var r=/^\s+|\s+$/g,i;try{var a=new ActiveXObject("htmlfile");a.write("<body>"),a.close(),i=a.body}catch(s){i=createPopup().document.body}var o=i.createTextRange();Pt=n(function(t){try{i.style.color=I(t).replace(r,R);var e=o.queryCommandValue("ForeColor");return e=(255&e)<<16|65280&e|(16711680&e)>>>16,"#"+("000000"+e.toString(16)).slice(-6)}catch(n){return"none"}})}else{var l=T.doc.createElement("i");l.title="Raphaël Colour Picker",l.style.display="none",T.doc.body.appendChild(l),Pt=n(function(t){return l.style.color=t,T.doc.defaultView.getComputedStyle(l,R).getPropertyValue("color")})}return Pt(t)},Ft=function(){return"hsb("+[this.h,this.s,this.b]+")"},Rt=function(){return"hsl("+[this.h,this.s,this.l]+")"},jt=function(){return this.hex},It=function(t,r,i){if(null==r&&e.is(t,"object")&&"r"in t&&"g"in t&&"b"in t&&(i=t.b,r=t.g,t=t.r),null==r&&e.is(t,Z)){var n=e.getRGB(t);t=n.r,r=n.g,i=n.b}return(t>1||r>1||i>1)&&(t/=255,r/=255,i/=255),[t,r,i]},qt=function(t,r,i,n){t*=255,r*=255,i*=255;var a={r:t,g:r,b:i,hex:e.rgb(t,r,i),toString:jt};return e.is(n,"finite")&&(a.opacity=n),a};e.color=function(t){var r;return e.is(t,"object")&&"h"in t&&"s"in t&&"b"in t?(r=e.hsb2rgb(t),t.r=r.r,t.g=r.g,t.b=r.b,t.hex=r.hex):e.is(t,"object")&&"h"in t&&"s"in t&&"l"in t?(r=e.hsl2rgb(t),t.r=r.r,t.g=r.g,t.b=r.b,t.hex=r.hex):(e.is(t,"string")&&(t=e.getRGB(t)),e.is(t,"object")&&"r"in t&&"g"in t&&"b"in t?(r=e.rgb2hsl(t),t.h=r.h,t.s=r.s,t.l=r.l,r=e.rgb2hsb(t),t.v=r.b):(t={hex:"none"},t.r=t.g=t.b=t.h=t.s=t.v=t.l=-1)),t.toString=jt,t},e.hsb2rgb=function(t,e,r,i){this.is(t,"object")&&"h"in t&&"s"in t&&"b"in t&&(r=t.b,e=t.s,i=t.o,t=t.h),t*=360;var n,a,s,o,l;return t=t%360/60,l=r*e,o=l*(1-H(t%2-1)),n=a=s=r-l,t=~~t,n+=[l,o,0,0,o,l][t],a+=[o,l,l,o,0,0][t],s+=[0,0,o,l,l,o][t],qt(n,a,s,i)},e.hsl2rgb=function(t,e,r,i){this.is(t,"object")&&"h"in t&&"s"in t&&"l"in t&&(r=t.l,e=t.s,t=t.h),(t>1||e>1||r>1)&&(t/=360,e/=100,r/=100),t*=360;var n,a,s,o,l;return t=t%360/60,l=2*e*(r<.5?r:1-r),o=l*(1-H(t%2-1)),n=a=s=r-l/2,t=~~t,n+=[l,o,0,0,o,l][t],a+=[o,l,l,o,0,0][t],s+=[0,0,o,l,l,o][t],qt(n,a,s,i)},e.rgb2hsb=function(t,e,r){r=It(t,e,r),t=r[0],e=r[1],r=r[2];var i,n,a,s;return a=W(t,e,r),s=a-G(t,e,r),i=0==s?null:a==t?(e-r)/s:a==e?(r-t)/s+2:(t-e)/s+4,i=(i+360)%6*60/360,n=0==s?0:s/a,{h:i,s:n,b:a,toString:Ft}},e.rgb2hsl=function(t,e,r){r=It(t,e,r),t=r[0],e=r[1],r=r[2];var i,n,a,s,o,l;return s=W(t,e,r),o=G(t,e,r),l=s-o,i=0==l?null:s==t?(e-r)/l:s==e?(r-t)/l+2:(t-e)/l+4,i=(i+360)%6*60/360,a=(s+o)/2,n=0==l?0:a<.5?l/(2*a):l/(2-2*a),{h:i,s:n,l:a,toString:Rt}},e._path2string=function(){return this.join(",").replace(xt,"$1")};var Dt=e._preload=function(t,e){var r=T.doc.createElement("img");r.style.cssText="position:absolute;left:-9999em;top:-9999em",r.onload=function(){e.call(this),this.onload=null,T.doc.body.removeChild(this)},r.onerror=function(){T.doc.body.removeChild(this)},T.doc.body.appendChild(r),r.src=t};e.getRGB=n(function(t){if(!t||(t=I(t)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:a};if("none"==t)return{r:-1,g:-1,b:-1,hex:"none",toString:a};!(vt[A](t.toLowerCase().substring(0,2))||"#"==t.charAt())&&(t=Pt(t));var r,i,n,s,o,l,h,u=t.match(nt);return u?(u[2]&&(s=ut(u[2].substring(5),16),n=ut(u[2].substring(3,5),16),i=ut(u[2].substring(1,3),16)),u[3]&&(s=ut((l=u[3].charAt(3))+l,16),n=ut((l=u[3].charAt(2))+l,16),i=ut((l=u[3].charAt(1))+l,16)),u[4]&&(h=u[4][q](gt),i=ht(h[0]),"%"==h[0].slice(-1)&&(i*=2.55),n=ht(h[1]),"%"==h[1].slice(-1)&&(n*=2.55),s=ht(h[2]),"%"==h[2].slice(-1)&&(s*=2.55),"rgba"==u[1].toLowerCase().slice(0,4)&&(o=ht(h[3])),h[3]&&"%"==h[3].slice(-1)&&(o/=100)),u[5]?(h=u[5][q](gt),i=ht(h[0]),"%"==h[0].slice(-1)&&(i*=2.55),n=ht(h[1]),"%"==h[1].slice(-1)&&(n*=2.55),s=ht(h[2]),"%"==h[2].slice(-1)&&(s*=2.55),("deg"==h[0].slice(-3)||"°"==h[0].slice(-1))&&(i/=360),"hsba"==u[1].toLowerCase().slice(0,4)&&(o=ht(h[3])),h[3]&&"%"==h[3].slice(-1)&&(o/=100),e.hsb2rgb(i,n,s,o)):u[6]?(h=u[6][q](gt),i=ht(h[0]),"%"==h[0].slice(-1)&&(i*=2.55),n=ht(h[1]),"%"==h[1].slice(-1)&&(n*=2.55),s=ht(h[2]),"%"==h[2].slice(-1)&&(s*=2.55),("deg"==h[0].slice(-3)||"°"==h[0].slice(-1))&&(i/=360),"hsla"==u[1].toLowerCase().slice(0,4)&&(o=ht(h[3])),h[3]&&"%"==h[3].slice(-1)&&(o/=100),e.hsl2rgb(i,n,s,o)):(u={r:i,g:n,b:s,toString:a},u.hex="#"+(16777216|s|n<<8|i<<16).toString(16).slice(1),e.is(o,"finite")&&(u.opacity=o),u)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:a}},e),e.hsb=n(function(t,r,i){return e.hsb2rgb(t,r,i).hex}),e.hsl=n(function(t,r,i){return e.hsl2rgb(t,r,i).hex}),e.rgb=n(function(t,e,r){function i(t){return t+.5|0}return"#"+(16777216|i(r)|i(e)<<8|i(t)<<16).toString(16).slice(1)}),e.getColor=function(t){var e=this.getColor.start=this.getColor.start||{h:0,s:1,b:t||.75},r=this.hsb2rgb(e.h,e.s,e.b);return e.h+=.075,e.h>1&&(e.h=0,e.s-=.2,e.s<=0&&(this.getColor.start={h:0,s:1,b:e.b})),r.hex},e.getColor.reset=function(){delete this.start},e.parsePathString=function(t){if(!t)return null;var r=Vt(t);if(r.arr)return Yt(r.arr);var i={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},n=[];return e.is(t,Q)&&e.is(t[0],Q)&&(n=Yt(t)),n.length||I(t).replace(yt,function(t,e,r){var a=[],s=e.toLowerCase();if(r.replace(bt,function(t,e){e&&a.push(+e)}),"m"==s&&a.length>2&&(n.push([e][P](a.splice(0,2))),s="l",e="m"==e?"l":"L"),"r"==s)n.push([e][P](a));else for(;a.length>=i[s]&&(n.push([e][P](a.splice(0,i[s]))),i[s]););}),n.toString=e._path2string,r.arr=Yt(n),n},e.parseTransformString=n(function(t){if(!t)return null;var r={r:3,s:4,t:2,m:6},i=[];return e.is(t,Q)&&e.is(t[0],Q)&&(i=Yt(t)),i.length||I(t).replace(mt,function(t,e,r){var n=[],a=O.call(e);r.replace(bt,function(t,e){e&&n.push(+e)}),i.push([e][P](n))}),i.toString=e._path2string,i});var Vt=function(t){var e=Vt.ps=Vt.ps||{};return e[t]?e[t].sleep=100:e[t]={sleep:100},setTimeout(function(){for(var r in e)e[A](r)&&r!=t&&(e[r].sleep--,!e[r].sleep&&delete e[r])}),e[t]};e.findDotsAtSegment=function(t,e,r,i,n,a,s,o,l){var h=1-l,u=X(h,3),c=X(h,2),f=l*l,p=f*l,d=u*t+3*c*l*r+3*h*l*l*n+p*s,g=u*e+3*c*l*i+3*h*l*l*a+p*o,v=t+2*l*(r-t)+f*(n-2*r+t),x=e+2*l*(i-e)+f*(a-2*i+e),y=r+2*l*(n-r)+f*(s-2*n+r),m=i+2*l*(a-i)+f*(o-2*a+i),b=h*t+l*r,_=h*e+l*i,w=h*n+l*s,k=h*a+l*o,B=90-180*Y.atan2(v-y,x-m)/U;return(v>y||x<m)&&(B+=180),{x:d,y:g,m:{x:v,y:x},n:{x:y,y:m},start:{x:b,y:_},end:{x:w,y:k},alpha:B}},e.bezierBBox=function(t,r,i,n,a,s,o,l){e.is(t,"array")||(t=[t,r,i,n,a,s,o,l]);var h=Zt.apply(null,t);return{x:h.min.x,y:h.min.y,x2:h.max.x,y2:h.max.y,width:h.max.x-h.min.x,height:h.max.y-h.min.y}},e.isPointInsideBBox=function(t,e,r){return e>=t.x&&e<=t.x2&&r>=t.y&&r<=t.y2},e.isBBoxIntersect=function(t,r){var i=e.isPointInsideBBox;return i(r,t.x,t.y)||i(r,t.x2,t.y)||i(r,t.x,t.y2)||i(r,t.x2,t.y2)||i(t,r.x,r.y)||i(t,r.x2,r.y)||i(t,r.x,r.y2)||i(t,r.x2,r.y2)||(t.x<r.x2&&t.x>r.x||r.x<t.x2&&r.x>t.x)&&(t.y<r.y2&&t.y>r.y||r.y<t.y2&&r.y>t.y)},e.pathIntersection=function(t,e){return d(t,e)},e.pathIntersectionNumber=function(t,e){return d(t,e,1)},e.isPointInsidePath=function(t,r,i){var n=e.pathBBox(t);return e.isPointInsideBBox(n,r,i)&&d(t,[["M",r,i],["H",n.x2+10]],1)%2==1},e._removedFactory=function(e){return function(){t("raphael.log",null,"Raphaël: you are calling to method “"+e+"” of removed object",e)}};var Ot=e.pathBBox=function(t){var e=Vt(t);if(e.bbox)return r(e.bbox);if(!t)return{x:0,y:0,width:0,height:0,x2:0,y2:0};t=Qt(t);for(var i=0,n=0,a=[],s=[],o,l=0,h=t.length;l<h;l++)if(o=t[l],"M"==o[0])i=o[1],n=o[2],a.push(i),s.push(n);else{var u=Zt(i,n,o[1],o[2],o[3],o[4],o[5],o[6]);a=a[P](u.min.x,u.max.x),s=s[P](u.min.y,u.max.y),i=o[5],n=o[6]}var c=G[z](0,a),f=G[z](0,s),p=W[z](0,a),d=W[z](0,s),g=p-c,v=d-f,x={x:c,y:f,x2:p,y2:d,width:g,height:v,cx:c+g/2,cy:f+v/2};return e.bbox=r(x),x},Yt=function(t){var i=r(t);return i.toString=e._path2string,i},Wt=e._pathToRelative=function(t){var r=Vt(t);if(r.rel)return Yt(r.rel);e.is(t,Q)&&e.is(t&&t[0],Q)||(t=e.parsePathString(t));var i=[],n=0,a=0,s=0,o=0,l=0;"M"==t[0][0]&&(n=t[0][1],a=t[0][2],s=n,o=a,l++,i.push(["M",n,a]));for(var h=l,u=t.length;h<u;h++){var c=i[h]=[],f=t[h];if(f[0]!=O.call(f[0]))switch(c[0]=O.call(f[0]),c[0]){case"a":c[1]=f[1],c[2]=f[2],c[3]=f[3],c[4]=f[4],c[5]=f[5],c[6]=+(f[6]-n).toFixed(3),c[7]=+(f[7]-a).toFixed(3);break;case"v":c[1]=+(f[1]-a).toFixed(3);break;case"m":s=f[1],o=f[2];default:for(var p=1,d=f.length;p<d;p++)c[p]=+(f[p]-(p%2?n:a)).toFixed(3)}else{c=i[h]=[],"m"==f[0]&&(s=f[1]+n,o=f[2]+a);for(var g=0,v=f.length;g<v;g++)i[h][g]=f[g]}var x=i[h].length;switch(i[h][0]){case"z":n=s,a=o;break;case"h":n+=+i[h][x-1];break;case"v":a+=+i[h][x-1];break;default:n+=+i[h][x-2],a+=+i[h][x-1]}}return i.toString=e._path2string,r.rel=Yt(i),i},Gt=e._pathToAbsolute=function(t){var r=Vt(t);if(r.abs)return Yt(r.abs);if(e.is(t,Q)&&e.is(t&&t[0],Q)||(t=e.parsePathString(t)),!t||!t.length)return[["M",0,0]];var i=[],n=0,a=0,o=0,l=0,h=0;"M"==t[0][0]&&(n=+t[0][1],a=+t[0][2],o=n,l=a,h++,i[0]=["M",n,a]);for(var u=3==t.length&&"M"==t[0][0]&&"R"==t[1][0].toUpperCase()&&"Z"==t[2][0].toUpperCase(),c,f,p=h,d=t.length;p<d;p++){if(i.push(c=[]),f=t[p],f[0]!=ct.call(f[0]))switch(c[0]=ct.call(f[0]),c[0]){case"A":c[1]=f[1],c[2]=f[2],c[3]=f[3],c[4]=f[4],c[5]=f[5],c[6]=+(f[6]+n),c[7]=+(f[7]+a);break;case"V":c[1]=+f[1]+a;break;case"H":c[1]=+f[1]+n;break;case"R":for(var g=[n,a][P](f.slice(1)),v=2,x=g.length;v<x;v++)g[v]=+g[v]+n,g[++v]=+g[v]+a;i.pop(),i=i[P](s(g,u));break;case"M":o=+f[1]+n,l=+f[2]+a;default:for(v=1,x=f.length;v<x;v++)c[v]=+f[v]+(v%2?n:a)}else if("R"==f[0])g=[n,a][P](f.slice(1)),i.pop(),i=i[P](s(g,u)),c=["R"][P](f.slice(-2));else for(var y=0,m=f.length;y<m;y++)c[y]=f[y];switch(c[0]){case"Z":n=o,a=l;break;case"H":n=c[1];break;case"V":a=c[1];break;case"M":o=c[c.length-2],l=c[c.length-1];default:n=c[c.length-2],a=c[c.length-1]}}return i.toString=e._path2string,r.abs=Yt(i),i},Ht=function(t,e,r,i){return[t,e,r,i,r,i]},Xt=function(t,e,r,i,n,a){var s=1/3,o=2/3;return[s*t+o*r,s*e+o*i,s*n+o*r,s*a+o*i,n,a]},Ut=function(t,e,r,i,a,s,o,l,h,u){var c=120*U/180,f=U/180*(+a||0),p=[],d,g=n(function(t,e,r){var i=t*Y.cos(r)-e*Y.sin(r),n=t*Y.sin(r)+e*Y.cos(r);return{x:i,y:n}});if(u)S=u[0],A=u[1],B=u[2],C=u[3];else{d=g(t,e,-f),t=d.x,e=d.y,d=g(l,h,-f),l=d.x,h=d.y;var v=Y.cos(U/180*a),x=Y.sin(U/180*a),y=(t-l)/2,m=(e-h)/2,b=y*y/(r*r)+m*m/(i*i);b>1&&(b=Y.sqrt(b),r=b*r,i=b*i);var _=r*r,w=i*i,k=(s==o?-1:1)*Y.sqrt(H((_*w-_*m*m-w*y*y)/(_*m*m+w*y*y))),B=k*r*m/i+(t+l)/2,C=k*-i*y/r+(e+h)/2,S=Y.asin(((e-C)/i).toFixed(9)),A=Y.asin(((h-C)/i).toFixed(9));S=t<B?U-S:S,A=l<B?U-A:A,S<0&&(S=2*U+S),A<0&&(A=2*U+A),o&&S>A&&(S-=2*U),!o&&A>S&&(A-=2*U)}var T=A-S;if(H(T)>c){var E=A,M=l,N=h;A=S+c*(o&&A>S?1:-1),l=B+r*Y.cos(A),h=C+i*Y.sin(A),p=Ut(l,h,r,i,a,0,o,M,N,[A,E,B,C])}T=A-S;var L=Y.cos(S),z=Y.sin(S),F=Y.cos(A),R=Y.sin(A),j=Y.tan(T/4),I=4/3*r*j,D=4/3*i*j,V=[t,e],O=[t+I*z,e-D*L],W=[l+I*R,h-D*F],G=[l,h];if(O[0]=2*V[0]-O[0],O[1]=2*V[1]-O[1],u)return[O,W,G][P](p);p=[O,W,G][P](p).join()[q](",");for(var X=[],$=0,Z=p.length;$<Z;$++)X[$]=$%2?g(p[$-1],p[$],f).y:g(p[$],p[$+1],f).x;return X},$t=function(t,e,r,i,n,a,s,o,l){var h=1-l;return{x:X(h,3)*t+3*X(h,2)*l*r+3*h*l*l*n+X(l,3)*s,y:X(h,3)*e+3*X(h,2)*l*i+3*h*l*l*a+X(l,3)*o}},Zt=n(function(t,e,r,i,n,a,s,o){var l=n-2*r+t-(s-2*n+r),h=2*(r-t)-2*(n-r),u=t-r,c=(-h+Y.sqrt(h*h-4*l*u))/2/l,f=(-h-Y.sqrt(h*h-4*l*u))/2/l,p=[e,o],d=[t,s],g;return H(c)>"1e12"&&(c=.5),H(f)>"1e12"&&(f=.5),c>0&&c<1&&(g=$t(t,e,r,i,n,a,s,o,c),d.push(g.x),p.push(g.y)),f>0&&f<1&&(g=$t(t,e,r,i,n,a,s,o,f),d.push(g.x),p.push(g.y)),l=a-2*i+e-(o-2*a+i),h=2*(i-e)-2*(a-i),u=e-i,c=(-h+Y.sqrt(h*h-4*l*u))/2/l,f=(-h-Y.sqrt(h*h-4*l*u))/2/l,H(c)>"1e12"&&(c=.5),H(f)>"1e12"&&(f=.5),c>0&&c<1&&(g=$t(t,e,r,i,n,a,s,o,c),d.push(g.x),p.push(g.y)),f>0&&f<1&&(g=$t(t,e,r,i,n,a,s,o,f),d.push(g.x),p.push(g.y)),{min:{x:G[z](0,d),y:G[z](0,p)},max:{x:W[z](0,d),y:W[z](0,p)}}}),Qt=e._path2curve=n(function(t,e){var r=!e&&Vt(t);if(!e&&r.curve)return Yt(r.curve);for(var i=Gt(t),n=e&&Gt(e),a={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},s={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},o=(function(t,e,r){var i,n,a={T:1,Q:1};if(!t)return["C",e.x,e.y,e.x,e.y,e.x,e.y];switch(!(t[0]in a)&&(e.qx=e.qy=null),t[0]){case"M":e.X=t[1],e.Y=t[2];break;case"A":t=["C"][P](Ut[z](0,[e.x,e.y][P](t.slice(1))));break;case"S":"C"==r||"S"==r?(i=2*e.x-e.bx,n=2*e.y-e.by):(i=e.x,n=e.y),t=["C",i,n][P](t.slice(1));break;case"T":"Q"==r||"T"==r?(e.qx=2*e.x-e.qx,e.qy=2*e.y-e.qy):(e.qx=e.x,e.qy=e.y),t=["C"][P](Xt(e.x,e.y,e.qx,e.qy,t[1],t[2]));break;case"Q":e.qx=t[1],e.qy=t[2],t=["C"][P](Xt(e.x,e.y,t[1],t[2],t[3],t[4]));break;case"L":t=["C"][P](Ht(e.x,e.y,t[1],t[2]));break;case"H":t=["C"][P](Ht(e.x,e.y,t[1],e.y));break;case"V":t=["C"][P](Ht(e.x,e.y,e.x,t[1]));break;case"Z":t=["C"][P](Ht(e.x,e.y,e.X,e.Y))}return t}),l=function(t,e){if(t[e].length>7){t[e].shift();for(var r=t[e];r.length;)u[e]="A",n&&(c[e]="A"),t.splice(e++,0,["C"][P](r.splice(0,6)));t.splice(e,1),g=W(i.length,n&&n.length||0)}},h=function(t,e,r,a,s){t&&e&&"M"==t[s][0]&&"M"!=e[s][0]&&(e.splice(s,0,["M",a.x,a.y]),r.bx=0,r.by=0,r.x=t[s][1],r.y=t[s][2],g=W(i.length,n&&n.length||0))},u=[],c=[],f="",p="",d=0,g=W(i.length,n&&n.length||0);d<g;d++){i[d]&&(f=i[d][0]),"C"!=f&&(u[d]=f,d&&(p=u[d-1])),i[d]=o(i[d],a,p),"A"!=u[d]&&"C"==f&&(u[d]="C"),l(i,d),n&&(n[d]&&(f=n[d][0]),"C"!=f&&(c[d]=f,d&&(p=c[d-1])),n[d]=o(n[d],s,p),"A"!=c[d]&&"C"==f&&(c[d]="C"),l(n,d)),h(i,n,a,s,d),h(n,i,s,a,d);var v=i[d],x=n&&n[d],y=v.length,m=n&&x.length;a.x=v[y-2],a.y=v[y-1],a.bx=ht(v[y-4])||a.x,a.by=ht(v[y-3])||a.y,s.bx=n&&(ht(x[m-4])||s.x),s.by=n&&(ht(x[m-3])||s.y),s.x=n&&x[m-2],s.y=n&&x[m-1]}return n||(r.curve=Yt(i)),n?[i,n]:i},null,Yt),Jt=e._parseDots=n(function(t){for(var r=[],i=0,n=t.length;i<n;i++){var a={},s=t[i].match(/^([^:]*):?([\d\.]*)/);if(a.color=e.getRGB(s[1]),a.color.error)return null;a.opacity=a.color.opacity,a.color=a.color.hex,s[2]&&(a.offset=s[2]+"%"),r.push(a)}for(i=1,n=r.length-1;i<n;i++)if(!r[i].offset){for(var o=ht(r[i-1].offset||0),l=0,h=i+1;h<n;h++)if(r[h].offset){l=r[h].offset;break}l||(l=100,h=n),l=ht(l);for(var u=(l-o)/(h-i+1);i<h;i++)o+=u,r[i].offset=o+"%"}return r}),Kt=e._tear=function(t,e){t==e.top&&(e.top=t.prev),t==e.bottom&&(e.bottom=t.next),t.next&&(t.next.prev=t.prev),t.prev&&(t.prev.next=t.next)},te=e._tofront=function(t,e){e.top!==t&&(Kt(t,e),t.next=null,t.prev=e.top,e.top.next=t,e.top=t)},ee=e._toback=function(t,e){e.bottom!==t&&(Kt(t,e),t.next=e.bottom,t.prev=null,e.bottom.prev=t,e.bottom=t)},re=e._insertafter=function(t,e,r){Kt(t,r),e==r.top&&(r.top=t),e.next&&(e.next.prev=t),t.next=e.next,t.prev=e,e.next=t},ie=e._insertbefore=function(t,e,r){Kt(t,r),e==r.bottom&&(r.bottom=t),e.prev&&(e.prev.next=t),t.prev=e.prev,e.prev=t,t.next=e},ne=e.toMatrix=function(t,e){var r=Ot(t),i={_:{transform:R},getBBox:function(){return r}};return se(i,e),i.matrix},ae=e.transformPath=function(t,e){return Mt(t,ne(t,e))},se=e._extractTransform=function(t,r){if(null==r)return t._.transform;r=I(r).replace(/\.{3}|\u2026/g,t._.transform||R);var i=e.parseTransformString(r),n=0,a=0,s=0,o=1,l=1,h=t._,u=new g;if(h.transform=i||[],i)for(var c=0,f=i.length;c<f;c++){var p=i[c],d=p.length,v=I(p[0]).toLowerCase(),x=p[0]!=v,y=x?u.invert():0,m,b,_,w,k;"t"==v&&3==d?x?(m=y.x(0,0),b=y.y(0,0),_=y.x(p[1],p[2]),w=y.y(p[1],p[2]),u.translate(_-m,w-b)):u.translate(p[1],p[2]):"r"==v?2==d?(k=k||t.getBBox(1),u.rotate(p[1],k.x+k.width/2,k.y+k.height/2),n+=p[1]):4==d&&(x?(_=y.x(p[2],p[3]),w=y.y(p[2],p[3]),u.rotate(p[1],_,w)):u.rotate(p[1],p[2],p[3]),n+=p[1]):"s"==v?2==d||3==d?(k=k||t.getBBox(1),u.scale(p[1],p[d-1],k.x+k.width/2,k.y+k.height/2),o*=p[1],l*=p[d-1]):5==d&&(x?(_=y.x(p[3],p[4]),w=y.y(p[3],p[4]),u.scale(p[1],p[2],_,w)):u.scale(p[1],p[2],p[3],p[4]),o*=p[1],l*=p[2]):"m"==v&&7==d&&u.add(p[1],p[2],p[3],p[4],p[5],p[6]),h.dirtyT=1,t.matrix=u}t.matrix=u,h.sx=o,h.sy=l,h.deg=n,h.dx=a=u.e,h.dy=s=u.f,1==o&&1==l&&!n&&h.bbox?(h.bbox.x+=+a,h.bbox.y+=+s):h.dirtyT=1},oe=function(t){var e=t[0];switch(e.toLowerCase()){case"t":return[e,0,0];case"m":return[e,1,0,0,1,0,0];case"r":return 4==t.length?[e,0,t[2],t[3]]:[e,0];case"s":return 5==t.length?[e,1,1,t[3],t[4]]:3==t.length?[e,1,1]:[e,1]}},le=e._equaliseTransform=function(t,r){r=I(r).replace(/\.{3}|\u2026/g,t),t=e.parseTransformString(t)||[],r=e.parseTransformString(r)||[];for(var i=W(t.length,r.length),n=[],a=[],s=0,o,l,h,u;s<i;s++){if(h=t[s]||oe(r[s]),u=r[s]||oe(h),h[0]!=u[0]||"r"==h[0].toLowerCase()&&(h[2]!=u[2]||h[3]!=u[3])||"s"==h[0].toLowerCase()&&(h[3]!=u[3]||h[4]!=u[4]))return;for(n[s]=[],a[s]=[],o=0,l=W(h.length,u.length);o<l;o++)o in h&&(n[s][o]=h[o]),o in u&&(a[s][o]=u[o])}return{from:n,to:a}};e._getContainer=function(t,r,i,n){var a;if(a=null!=n||e.is(t,"object")?t:T.doc.getElementById(t),null!=a)return a.tagName?null==r?{container:a,width:a.style.pixelWidth||a.offsetWidth,height:a.style.pixelHeight||a.offsetHeight}:{container:a,width:r,height:i}:{container:1,x:t,y:r,width:i,height:n}},e.pathToRelative=Wt,e._engine={},e.path2curve=Qt,e.matrix=function(t,e,r,i,n,a){return new g(t,e,r,i,n,a)},function(t){function r(t){return t[0]*t[0]+t[1]*t[1]}function i(t){var e=Y.sqrt(r(t));t[0]&&(t[0]/=e),t[1]&&(t[1]/=e)}t.add=function(t,e,r,i,n,a){var s=[[],[],[]],o=[[this.a,this.c,this.e],[this.b,this.d,this.f],[0,0,1]],l=[[t,r,n],[e,i,a],[0,0,1]],h,u,c,f;for(t&&t instanceof g&&(l=[[t.a,t.c,t.e],[t.b,t.d,t.f],[0,0,1]]),h=0;h<3;h++)for(u=0;u<3;u++){for(f=0,c=0;c<3;c++)f+=o[h][c]*l[c][u];s[h][u]=f}this.a=s[0][0],this.b=s[1][0],this.c=s[0][1],this.d=s[1][1],this.e=s[0][2],this.f=s[1][2]},t.invert=function(){var t=this,e=t.a*t.d-t.b*t.c;return new g(t.d/e,-t.b/e,-t.c/e,t.a/e,(t.c*t.f-t.d*t.e)/e,(t.b*t.e-t.a*t.f)/e)},t.clone=function(){return new g(this.a,this.b,this.c,this.d,this.e,this.f)},t.translate=function(t,e){
2
+ this.add(1,0,0,1,t,e)},t.scale=function(t,e,r,i){null==e&&(e=t),(r||i)&&this.add(1,0,0,1,r,i),this.add(t,0,0,e,0,0),(r||i)&&this.add(1,0,0,1,-r,-i)},t.rotate=function(t,r,i){t=e.rad(t),r=r||0,i=i||0;var n=+Y.cos(t).toFixed(9),a=+Y.sin(t).toFixed(9);this.add(n,a,-a,n,r,i),this.add(1,0,0,1,-r,-i)},t.x=function(t,e){return t*this.a+e*this.c+this.e},t.y=function(t,e){return t*this.b+e*this.d+this.f},t.get=function(t){return+this[I.fromCharCode(97+t)].toFixed(4)},t.toString=function(){return e.svg?"matrix("+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)].join()+")":[this.get(0),this.get(2),this.get(1),this.get(3),0,0].join()},t.toFilter=function(){return"progid:DXImageTransform.Microsoft.Matrix(M11="+this.get(0)+", M12="+this.get(2)+", M21="+this.get(1)+", M22="+this.get(3)+", Dx="+this.get(4)+", Dy="+this.get(5)+", sizingmethod='auto expand')"},t.offset=function(){return[this.e.toFixed(4),this.f.toFixed(4)]},t.split=function(){var t={};t.dx=this.e,t.dy=this.f;var n=[[this.a,this.c],[this.b,this.d]];t.scalex=Y.sqrt(r(n[0])),i(n[0]),t.shear=n[0][0]*n[1][0]+n[0][1]*n[1][1],n[1]=[n[1][0]-n[0][0]*t.shear,n[1][1]-n[0][1]*t.shear],t.scaley=Y.sqrt(r(n[1])),i(n[1]),t.shear/=t.scaley;var a=-n[0][1],s=n[1][1];return s<0?(t.rotate=e.deg(Y.acos(s)),a<0&&(t.rotate=360-t.rotate)):t.rotate=e.deg(Y.asin(a)),t.isSimple=!(+t.shear.toFixed(9)||t.scalex.toFixed(9)!=t.scaley.toFixed(9)&&t.rotate),t.isSuperSimple=!+t.shear.toFixed(9)&&t.scalex.toFixed(9)==t.scaley.toFixed(9)&&!t.rotate,t.noRotation=!+t.shear.toFixed(9)&&!t.rotate,t},t.toTransformString=function(t){var e=t||this[q]();return e.isSimple?(e.scalex=+e.scalex.toFixed(4),e.scaley=+e.scaley.toFixed(4),e.rotate=+e.rotate.toFixed(4),(e.dx||e.dy?"t"+[e.dx,e.dy]:R)+(1!=e.scalex||1!=e.scaley?"s"+[e.scalex,e.scaley,0,0]:R)+(e.rotate?"r"+[e.rotate,0,0]:R)):"m"+[this.get(0),this.get(1),this.get(2),this.get(3),this.get(4),this.get(5)]}}(g.prototype);for(var he=function(){this.returnValue=!1},ue=function(){return this.originalEvent.preventDefault()},ce=function(){this.cancelBubble=!0},fe=function(){return this.originalEvent.stopPropagation()},pe=function(t){var e=T.doc.documentElement.scrollTop||T.doc.body.scrollTop,r=T.doc.documentElement.scrollLeft||T.doc.body.scrollLeft;return{x:t.clientX+r,y:t.clientY+e}},de=function(){return T.doc.addEventListener?function(t,e,r,i){var n=function(t){var e=pe(t);return r.call(i,t,e.x,e.y)};if(t.addEventListener(e,n,!1),F&&V[e]){var a=function(e){for(var n=pe(e),a=e,s=0,o=e.targetTouches&&e.targetTouches.length;s<o;s++)if(e.targetTouches[s].target==t){e=e.targetTouches[s],e.originalEvent=a,e.preventDefault=ue,e.stopPropagation=fe;break}return r.call(i,e,n.x,n.y)};t.addEventListener(V[e],a,!1)}return function(){return t.removeEventListener(e,n,!1),F&&V[e]&&t.removeEventListener(V[e],a,!1),!0}}:T.doc.attachEvent?function(t,e,r,i){var n=function(t){t=t||T.win.event;var e=T.doc.documentElement.scrollTop||T.doc.body.scrollTop,n=T.doc.documentElement.scrollLeft||T.doc.body.scrollLeft,a=t.clientX+n,s=t.clientY+e;return t.preventDefault=t.preventDefault||he,t.stopPropagation=t.stopPropagation||ce,r.call(i,t,a,s)};t.attachEvent("on"+e,n);var a=function(){return t.detachEvent("on"+e,n),!0};return a}:void 0}(),ge=[],ve=function(e){for(var r=e.clientX,i=e.clientY,n=T.doc.documentElement.scrollTop||T.doc.body.scrollTop,a=T.doc.documentElement.scrollLeft||T.doc.body.scrollLeft,s,o=ge.length;o--;){if(s=ge[o],F&&e.touches){for(var l=e.touches.length,h;l--;)if(h=e.touches[l],h.identifier==s.el._drag.id){r=h.clientX,i=h.clientY,(e.originalEvent?e.originalEvent:e).preventDefault();break}}else e.preventDefault();var u=s.el.node,c,f=u.nextSibling,p=u.parentNode,d=u.style.display;T.win.opera&&p.removeChild(u),u.style.display="none",c=s.el.paper.getElementByPoint(r,i),u.style.display=d,T.win.opera&&(f?p.insertBefore(u,f):p.appendChild(u)),c&&t("raphael.drag.over."+s.el.id,s.el,c),r+=a,i+=n,t("raphael.drag.move."+s.el.id,s.move_scope||s.el,r-s.el._drag.x,i-s.el._drag.y,r,i,e)}},xe=function(r){e.unmousemove(ve).unmouseup(xe);for(var i=ge.length,n;i--;)n=ge[i],n.el._drag={},t("raphael.drag.end."+n.el.id,n.end_scope||n.start_scope||n.move_scope||n.el,r);ge=[]},ye=e.el={},me=D.length;me--;)!function(t){e[t]=ye[t]=function(r,i){return e.is(r,"function")&&(this.events=this.events||[],this.events.push({name:t,f:r,unbind:de(this.shape||this.node||T.doc,t,r,i||this)})),this},e["un"+t]=ye["un"+t]=function(r){for(var i=this.events||[],n=i.length;n--;)i[n].name!=t||!e.is(r,"undefined")&&i[n].f!=r||(i[n].unbind(),i.splice(n,1),!i.length&&delete this.events);return this}}(D[me]);ye.data=function(r,i){var n=wt[this.id]=wt[this.id]||{};if(0==arguments.length)return n;if(1==arguments.length){if(e.is(r,"object")){for(var a in r)r[A](a)&&this.data(a,r[a]);return this}return t("raphael.data.get."+this.id,this,n[r],r),n[r]}return n[r]=i,t("raphael.data.set."+this.id,this,i,r),this},ye.removeData=function(t){return null==t?wt[this.id]={}:wt[this.id]&&delete wt[this.id][t],this},ye.getData=function(){return r(wt[this.id]||{})},ye.hover=function(t,e,r,i){return this.mouseover(t,r).mouseout(e,i||r)},ye.unhover=function(t,e){return this.unmouseover(t).unmouseout(e)};var be=[];ye.drag=function(r,i,n,a,s,o){function l(l){(l.originalEvent||l).preventDefault();var h=l.clientX,u=l.clientY,c=T.doc.documentElement.scrollTop||T.doc.body.scrollTop,f=T.doc.documentElement.scrollLeft||T.doc.body.scrollLeft;if(this._drag.id=l.identifier,F&&l.touches)for(var p=l.touches.length,d;p--;)if(d=l.touches[p],this._drag.id=d.identifier,d.identifier==this._drag.id){h=d.clientX,u=d.clientY;break}this._drag.x=h+f,this._drag.y=u+c,!ge.length&&e.mousemove(ve).mouseup(xe),ge.push({el:this,move_scope:a,start_scope:s,end_scope:o}),i&&t.on("raphael.drag.start."+this.id,i),r&&t.on("raphael.drag.move."+this.id,r),n&&t.on("raphael.drag.end."+this.id,n),t("raphael.drag.start."+this.id,s||a||this,l.clientX+f,l.clientY+c,l)}return this._drag={},be.push({el:this,start:l}),this.mousedown(l),this},ye.onDragOver=function(e){e?t.on("raphael.drag.over."+this.id,e):t.unbind("raphael.drag.over."+this.id)},ye.undrag=function(){for(var r=be.length;r--;)be[r].el==this&&(this.unmousedown(be[r].start),be.splice(r,1),t.unbind("raphael.drag.*."+this.id));!be.length&&e.unmousemove(ve).unmouseup(xe),ge=[]},N.circle=function(t,r,i){var n=e._engine.circle(this,t||0,r||0,i||0);return this.__set__&&this.__set__.push(n),n},N.rect=function(t,r,i,n,a){var s=e._engine.rect(this,t||0,r||0,i||0,n||0,a||0);return this.__set__&&this.__set__.push(s),s},N.ellipse=function(t,r,i,n){var a=e._engine.ellipse(this,t||0,r||0,i||0,n||0);return this.__set__&&this.__set__.push(a),a},N.path=function(t){t&&!e.is(t,Z)&&!e.is(t[0],Q)&&(t+=R);var r=e._engine.path(e.format[z](e,arguments),this);return this.__set__&&this.__set__.push(r),r},N.image=function(t,r,i,n,a){var s=e._engine.image(this,t||"about:blank",r||0,i||0,n||0,a||0);return this.__set__&&this.__set__.push(s),s},N.text=function(t,r,i){var n=e._engine.text(this,t||0,r||0,I(i));return this.__set__&&this.__set__.push(n),n},N.set=function(t){!e.is(t,"array")&&(t=Array.prototype.splice.call(arguments,0,arguments.length));var r=new ze(t);return this.__set__&&this.__set__.push(r),r.paper=this,r.type="set",r},N.setStart=function(t){this.__set__=t||this.set()},N.setFinish=function(t){var e=this.__set__;return delete this.__set__,e},N.getSize=function(){var t=this.canvas.parentNode;return{width:t.offsetWidth,height:t.offsetHeight}},N.setSize=function(t,r){return e._engine.setSize.call(this,t,r)},N.setViewBox=function(t,r,i,n,a){return e._engine.setViewBox.call(this,t,r,i,n,a)},N.top=N.bottom=null,N.raphael=e;var _e=function(t){var e=t.getBoundingClientRect(),r=t.ownerDocument,i=r.body,n=r.documentElement,a=n.clientTop||i.clientTop||0,s=n.clientLeft||i.clientLeft||0,o=e.top+(T.win.pageYOffset||n.scrollTop||i.scrollTop)-a,l=e.left+(T.win.pageXOffset||n.scrollLeft||i.scrollLeft)-s;return{y:o,x:l}};N.getElementByPoint=function(t,e){var r=this,i=r.canvas,n=T.doc.elementFromPoint(t,e);if(T.win.opera&&"svg"==n.tagName){var a=_e(i),s=i.createSVGRect();s.x=t-a.x,s.y=e-a.y,s.width=s.height=1;var o=i.getIntersectionList(s,null);o.length&&(n=o[o.length-1])}if(!n)return null;for(;n.parentNode&&n!=i.parentNode&&!n.raphael;)n=n.parentNode;return n==r.canvas.parentNode&&(n=i),n=n&&n.raphael?r.getById(n.raphaelid):null},N.getElementsByBBox=function(t){var r=this.set();return this.forEach(function(i){e.isBBoxIntersect(i.getBBox(),t)&&r.push(i)}),r},N.getById=function(t){for(var e=this.bottom;e;){if(e.id==t)return e;e=e.next}return null},N.forEach=function(t,e){for(var r=this.bottom;r;){if(t.call(e,r)===!1)return this;r=r.next}return this},N.getElementsByPoint=function(t,e){var r=this.set();return this.forEach(function(i){i.isPointInside(t,e)&&r.push(i)}),r},ye.isPointInside=function(t,r){var i=this.realPath=Et[this.type](this);return this.attr("transform")&&this.attr("transform").length&&(i=e.transformPath(i,this.attr("transform"))),e.isPointInsidePath(i,t,r)},ye.getBBox=function(t){if(this.removed)return{};var e=this._;return t?(!e.dirty&&e.bboxwt||(this.realPath=Et[this.type](this),e.bboxwt=Ot(this.realPath),e.bboxwt.toString=x,e.dirty=0),e.bboxwt):((e.dirty||e.dirtyT||!e.bbox)&&(!e.dirty&&this.realPath||(e.bboxwt=0,this.realPath=Et[this.type](this)),e.bbox=Ot(Mt(this.realPath,this.matrix)),e.bbox.toString=x,e.dirty=e.dirtyT=0),e.bbox)},ye.clone=function(){if(this.removed)return null;var t=this.paper[this.type]().attr(this.attr());return this.__set__&&this.__set__.push(t),t},ye.glow=function(t){if("text"==this.type)return null;t=t||{};var e={width:(t.width||10)+(+this.attr("stroke-width")||1),fill:t.fill||!1,opacity:null==t.opacity?.5:t.opacity,offsetx:t.offsetx||0,offsety:t.offsety||0,color:t.color||"#000"},r=e.width/2,i=this.paper,n=i.set(),a=this.realPath||Et[this.type](this);a=this.matrix?Mt(a,this.matrix):a;for(var s=1;s<r+1;s++)n.push(i.path(a).attr({stroke:e.color,fill:e.fill?e.color:"none","stroke-linejoin":"round","stroke-linecap":"round","stroke-width":+(e.width/r*s).toFixed(3),opacity:+(e.opacity/r).toFixed(3)}));return n.insertBefore(this).translate(e.offsetx,e.offsety)};var we={},ke=function(t,r,i,n,a,s,o,u,c){return null==c?l(t,r,i,n,a,s,o,u):e.findDotsAtSegment(t,r,i,n,a,s,o,u,h(t,r,i,n,a,s,o,u,c))},Be=function(t,r){return function(i,n,a){i=Qt(i);for(var s,o,l,h,u="",c={},f,p=0,d=0,g=i.length;d<g;d++){if(l=i[d],"M"==l[0])s=+l[1],o=+l[2];else{if(h=ke(s,o,l[1],l[2],l[3],l[4],l[5],l[6]),p+h>n){if(r&&!c.start){if(f=ke(s,o,l[1],l[2],l[3],l[4],l[5],l[6],n-p),u+=["C"+f.start.x,f.start.y,f.m.x,f.m.y,f.x,f.y],a)return u;c.start=u,u=["M"+f.x,f.y+"C"+f.n.x,f.n.y,f.end.x,f.end.y,l[5],l[6]].join(),p+=h,s=+l[5],o=+l[6];continue}if(!t&&!r)return f=ke(s,o,l[1],l[2],l[3],l[4],l[5],l[6],n-p),{x:f.x,y:f.y,alpha:f.alpha}}p+=h,s=+l[5],o=+l[6]}u+=l.shift()+l}return c.end=u,f=t?p:r?c:e.findDotsAtSegment(s,o,l[0],l[1],l[2],l[3],l[4],l[5],1),f.alpha&&(f={x:f.x,y:f.y,alpha:f.alpha}),f}},Ce=Be(1),Se=Be(),Ae=Be(0,1);e.getTotalLength=Ce,e.getPointAtLength=Se,e.getSubpath=function(t,e,r){if(this.getTotalLength(t)-r<1e-6)return Ae(t,e).end;var i=Ae(t,r,1);return e?Ae(i,e).end:i},ye.getTotalLength=function(){var t=this.getPath();if(t)return this.node.getTotalLength?this.node.getTotalLength():Ce(t)},ye.getPointAtLength=function(t){var e=this.getPath();if(e)return Se(e,t)},ye.getPath=function(){var t,r=e._getPath[this.type];if("text"!=this.type&&"set"!=this.type)return r&&(t=r(this)),t},ye.getSubpath=function(t,r){var i=this.getPath();if(i)return e.getSubpath(i,t,r)};var Te=e.easing_formulas={linear:function(t){return t},"<":function(t){return X(t,1.7)},">":function(t){return X(t,.48)},"<>":function(t){var e=.48-t/1.04,r=Y.sqrt(.1734+e*e),i=r-e,n=X(H(i),1/3)*(i<0?-1:1),a=-r-e,s=X(H(a),1/3)*(a<0?-1:1),o=n+s+.5;return 3*(1-o)*o*o+o*o*o},backIn:function(t){var e=1.70158;return t*t*((e+1)*t-e)},backOut:function(t){t-=1;var e=1.70158;return t*t*((e+1)*t+e)+1},elastic:function(t){return t==!!t?t:X(2,-10*t)*Y.sin((t-.075)*(2*U)/.3)+1},bounce:function(t){var e=7.5625,r=2.75,i;return t<1/r?i=e*t*t:t<2/r?(t-=1.5/r,i=e*t*t+.75):t<2.5/r?(t-=2.25/r,i=e*t*t+.9375):(t-=2.625/r,i=e*t*t+.984375),i}};Te.easeIn=Te["ease-in"]=Te["<"],Te.easeOut=Te["ease-out"]=Te[">"],Te.easeInOut=Te["ease-in-out"]=Te["<>"],Te["back-in"]=Te.backIn,Te["back-out"]=Te.backOut;var Ee=[],Me=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){setTimeout(t,16)},Ne=function(){for(var r=+new Date,i=0;i<Ee.length;i++){var n=Ee[i];if(!n.el.removed&&!n.paused){var a=r-n.start,s=n.ms,o=n.easing,l=n.from,h=n.diff,u=n.to,c=n.t,f=n.el,p={},d,g={},v;if(n.initstatus?(a=(n.initstatus*n.anim.top-n.prev)/(n.percent-n.prev)*s,n.status=n.initstatus,delete n.initstatus,n.stop&&Ee.splice(i--,1)):n.status=(n.prev+(n.percent-n.prev)*(a/s))/n.anim.top,!(a<0))if(a<s){var x=o(a/s);for(var y in l)if(l[A](y)){switch(pt[y]){case $:d=+l[y]+x*s*h[y];break;case"colour":d="rgb("+[Le(ot(l[y].r+x*s*h[y].r)),Le(ot(l[y].g+x*s*h[y].g)),Le(ot(l[y].b+x*s*h[y].b))].join(",")+")";break;case"path":d=[];for(var m=0,_=l[y].length;m<_;m++){d[m]=[l[y][m][0]];for(var w=1,k=l[y][m].length;w<k;w++)d[m][w]=+l[y][m][w]+x*s*h[y][m][w];d[m]=d[m].join(j)}d=d.join(j);break;case"transform":if(h[y].real)for(d=[],m=0,_=l[y].length;m<_;m++)for(d[m]=[l[y][m][0]],w=1,k=l[y][m].length;w<k;w++)d[m][w]=l[y][m][w]+x*s*h[y][m][w];else{var B=function(t){return+l[y][t]+x*s*h[y][t]};d=[["m",B(0),B(1),B(2),B(3),B(4),B(5)]]}break;case"csv":if("clip-rect"==y)for(d=[],m=4;m--;)d[m]=+l[y][m]+x*s*h[y][m];break;default:var C=[][P](l[y]);for(d=[],m=f.paper.customAttributes[y].length;m--;)d[m]=+C[m]+x*s*h[y][m]}p[y]=d}f.attr(p),function(e,r,i){setTimeout(function(){t("raphael.anim.frame."+e,r,i)})}(f.id,f,n.anim)}else{if(function(r,i,n){setTimeout(function(){t("raphael.anim.frame."+i.id,i,n),t("raphael.anim.finish."+i.id,i,n),e.is(r,"function")&&r.call(i)})}(n.callback,f,n.anim),f.attr(u),Ee.splice(i--,1),n.repeat>1&&!n.next){for(v in u)u[A](v)&&(g[v]=n.totalOrigin[v]);n.el.attr(g),b(n.anim,n.el,n.anim.percents[0],null,n.totalOrigin,n.repeat-1)}n.next&&!n.stop&&b(n.anim,n.el,n.next,null,n.totalOrigin,n.repeat)}}}Ee.length&&Me(Ne)},Le=function(t){return t>255?255:t<0?0:t};ye.animateWith=function(t,r,i,n,a,s){var o=this;if(o.removed)return s&&s.call(o),o;var l=i instanceof m?i:e.animation(i,n,a,s),h,u;b(l,o,l.percents[0],null,o.attr());for(var c=0,f=Ee.length;c<f;c++)if(Ee[c].anim==r&&Ee[c].el==t){Ee[f-1].start=Ee[c].start;break}return o},ye.onAnimation=function(e){return e?t.on("raphael.anim.frame."+this.id,e):t.unbind("raphael.anim.frame."+this.id),this},m.prototype.delay=function(t){var e=new m(this.anim,this.ms);return e.times=this.times,e.del=+t||0,e},m.prototype.repeat=function(t){var e=new m(this.anim,this.ms);return e.del=this.del,e.times=Y.floor(W(t,0))||1,e},e.animation=function(t,r,i,n){if(t instanceof m)return t;!e.is(i,"function")&&i||(n=n||i||null,i=null),t=Object(t),r=+r||0;var a={},s,o;for(o in t)t[A](o)&&ht(o)!=o&&ht(o)+"%"!=o&&(s=!0,a[o]=t[o]);if(s)return i&&(a.easing=i),n&&(a.callback=n),new m({100:a},r);if(n){var l=0;for(var h in t){var u=ut(h);t[A](h)&&u>l&&(l=u)}l+="%",!t[l].callback&&(t[l].callback=n)}return new m(t,r)},ye.animate=function(t,r,i,n){var a=this;if(a.removed)return n&&n.call(a),a;var s=t instanceof m?t:e.animation(t,r,i,n);return b(s,a,s.percents[0],null,a.attr()),a},ye.setTime=function(t,e){return t&&null!=e&&this.status(t,G(e,t.ms)/t.ms),this},ye.status=function(t,e){var r=[],i=0,n,a;if(null!=e)return b(t,this,-1,G(e,1)),this;for(n=Ee.length;i<n;i++)if(a=Ee[i],a.el.id==this.id&&(!t||a.anim==t)){if(t)return a.status;r.push({anim:a.anim,status:a.status})}return t?0:r},ye.pause=function(e){for(var r=0;r<Ee.length;r++)Ee[r].el.id!=this.id||e&&Ee[r].anim!=e||t("raphael.anim.pause."+this.id,this,Ee[r].anim)!==!1&&(Ee[r].paused=!0);return this},ye.resume=function(e){for(var r=0;r<Ee.length;r++)if(Ee[r].el.id==this.id&&(!e||Ee[r].anim==e)){var i=Ee[r];t("raphael.anim.resume."+this.id,this,i.anim)!==!1&&(delete i.paused,this.status(i.anim,i.status))}return this},ye.stop=function(e){for(var r=0;r<Ee.length;r++)Ee[r].el.id!=this.id||e&&Ee[r].anim!=e||t("raphael.anim.stop."+this.id,this,Ee[r].anim)!==!1&&Ee.splice(r--,1);return this},t.on("raphael.remove",_),t.on("raphael.clear",_),ye.toString=function(){return"Raphaël’s object"};var ze=function(t){if(this.items=[],this.length=0,this.type="set",t)for(var e=0,r=t.length;e<r;e++)!t[e]||t[e].constructor!=ye.constructor&&t[e].constructor!=ze||(this[this.items.length]=this.items[this.items.length]=t[e],this.length++)},Pe=ze.prototype;Pe.push=function(){for(var t,e,r=0,i=arguments.length;r<i;r++)t=arguments[r],!t||t.constructor!=ye.constructor&&t.constructor!=ze||(e=this.items.length,this[e]=this.items[e]=t,this.length++);return this},Pe.pop=function(){return this.length&&delete this[this.length--],this.items.pop()},Pe.forEach=function(t,e){for(var r=0,i=this.items.length;r<i;r++)if(t.call(e,this.items[r],r)===!1)return this;return this};for(var Fe in ye)ye[A](Fe)&&(Pe[Fe]=function(t){return function(){var e=arguments;return this.forEach(function(r){r[t][z](r,e)})}}(Fe));return Pe.attr=function(t,r){if(t&&e.is(t,Q)&&e.is(t[0],"object"))for(var i=0,n=t.length;i<n;i++)this.items[i].attr(t[i]);else for(var a=0,s=this.items.length;a<s;a++)this.items[a].attr(t,r);return this},Pe.clear=function(){for(;this.length;)this.pop()},Pe.splice=function(t,e,r){t=t<0?W(this.length+t,0):t,e=W(0,G(this.length-t,e));var i=[],n=[],a=[],s;for(s=2;s<arguments.length;s++)a.push(arguments[s]);for(s=0;s<e;s++)n.push(this[t+s]);for(;s<this.length-t;s++)i.push(this[t+s]);var o=a.length;for(s=0;s<o+i.length;s++)this.items[t+s]=this[t+s]=s<o?a[s]:i[s-o];for(s=this.items.length=this.length-=e-o;this[s];)delete this[s++];return new ze(n)},Pe.exclude=function(t){for(var e=0,r=this.length;e<r;e++)if(this[e]==t)return this.splice(e,1),!0},Pe.animate=function(t,r,i,n){(e.is(i,"function")||!i)&&(n=i||null);var a=this.items.length,s=a,o,l=this,h;if(!a)return this;n&&(h=function(){!--a&&n.call(l)}),i=e.is(i,Z)?i:h;var u=e.animation(t,r,i,h);for(o=this.items[--s].animate(u);s--;)this.items[s]&&!this.items[s].removed&&this.items[s].animateWith(o,u,u),this.items[s]&&!this.items[s].removed||a--;return this},Pe.insertAfter=function(t){for(var e=this.items.length;e--;)this.items[e].insertAfter(t);return this},Pe.getBBox=function(){for(var t=[],e=[],r=[],i=[],n=this.items.length;n--;)if(!this.items[n].removed){var a=this.items[n].getBBox();t.push(a.x),e.push(a.y),r.push(a.x+a.width),i.push(a.y+a.height)}return t=G[z](0,t),e=G[z](0,e),r=W[z](0,r),i=W[z](0,i),{x:t,y:e,x2:r,y2:i,width:r-t,height:i-e}},Pe.clone=function(t){t=this.paper.set();for(var e=0,r=this.items.length;e<r;e++)t.push(this.items[e].clone());return t},Pe.toString=function(){return"Raphaël‘s set"},Pe.glow=function(t){var e=this.paper.set();return this.forEach(function(r,i){var n=r.glow(t);null!=n&&n.forEach(function(t,r){e.push(t)})}),e},Pe.isPointInside=function(t,e){var r=!1;return this.forEach(function(i){if(i.isPointInside(t,e))return r=!0,!1}),r},e.registerFont=function(t){if(!t.face)return t;this.fonts=this.fonts||{};var e={w:t.w,face:{},glyphs:{}},r=t.face["font-family"];for(var i in t.face)t.face[A](i)&&(e.face[i]=t.face[i]);if(this.fonts[r]?this.fonts[r].push(e):this.fonts[r]=[e],!t.svg){e.face["units-per-em"]=ut(t.face["units-per-em"],10);for(var n in t.glyphs)if(t.glyphs[A](n)){var a=t.glyphs[n];if(e.glyphs[n]={w:a.w,k:{},d:a.d&&"M"+a.d.replace(/[mlcxtrv]/g,function(t){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}[t]||"M"})+"z"},a.k)for(var s in a.k)a[A](s)&&(e.glyphs[n].k[s]=a.k[s])}}return t},N.getFont=function(t,r,i,n){if(n=n||"normal",i=i||"normal",r=+r||{normal:400,bold:700,lighter:300,bolder:800}[r]||400,e.fonts){var a=e.fonts[t];if(!a){var s=new RegExp("(^|\\s)"+t.replace(/[^\w\d\s+!~.:_-]/g,R)+"(\\s|$)","i");for(var o in e.fonts)if(e.fonts[A](o)&&s.test(o)){a=e.fonts[o];break}}var l;if(a)for(var h=0,u=a.length;h<u&&(l=a[h],l.face["font-weight"]!=r||l.face["font-style"]!=i&&l.face["font-style"]||l.face["font-stretch"]!=n);h++);return l}},N.print=function(t,r,i,n,a,s,o,l){s=s||"middle",o=W(G(o||0,1),-1),l=W(G(l||1,3),1);var h=I(i)[q](R),u=0,c=0,f=R,p;if(e.is(n,"string")&&(n=this.getFont(n)),n){p=(a||16)/n.face["units-per-em"];for(var d=n.face.bbox[q](k),g=+d[0],v=d[3]-d[1],x=0,y=+d[1]+("baseline"==s?v+ +n.face.descent:v/2),m=0,b=h.length;m<b;m++){if("\n"==h[m])u=0,w=0,c=0,x+=v*l;else{var _=c&&n.glyphs[h[m-1]]||{},w=n.glyphs[h[m]];u+=c?(_.w||n.w)+(_.k&&_.k[h[m]]||0)+n.w*o:0,c=1}w&&w.d&&(f+=e.transformPath(w.d,["t",u*p,x*p,"s",p,p,g,y,"t",(t-g)/p,(r-y)/p]))}}return this.path(f).attr({fill:"#000",stroke:"none"})},N.add=function(t){if(e.is(t,"array"))for(var r=this.set(),i=0,n=t.length,a;i<n;i++)a=t[i]||{},B[A](a.type)&&r.push(this[a.type]().attr(a));return r},e.format=function(t,r){var i=e.is(r,Q)?[0][P](r):arguments;return t&&e.is(t,Z)&&i.length-1&&(t=t.replace(C,function(t,e){return null==i[++e]?R:i[e]})),t||R},e.fullfill=function(){var t=/\{([^\}]+)\}/g,e=/(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g,r=function(t,r,i){var n=i;return r.replace(e,function(t,e,r,i,a){e=e||i,n&&(e in n&&(n=n[e]),"function"==typeof n&&a&&(n=n()))}),n=(null==n||n==i?t:n)+""};return function(e,i){return String(e).replace(t,function(t,e){return r(t,e,i)})}}(),e.ninja=function(){if(E.was)T.win.Raphael=E.is;else{window.Raphael=void 0;try{delete window.Raphael}catch(t){}}return e},e.st=Pe,t.on("raphael.DOMload",function(){w=!0}),function(t,r,i){function n(){/in/.test(t.readyState)?setTimeout(n,9):e.eve("raphael.DOMload")}null==t.readyState&&t.addEventListener&&(t.addEventListener(r,i=function(){t.removeEventListener(r,i,!1),t.readyState="complete"},!1),t.readyState="loading"),n()}(document,"DOMContentLoaded"),e}.apply(e,i),!(void 0!==n&&(t.exports=n))},function(t,e,r){var i,n;!function(r){var a="0.5.0",s="hasOwnProperty",o=/[\.\/]/,l=/\s*,\s*/,h="*",u=function(){},c=function(t,e){return t-e},f,p,d={n:{}},g=function(){for(var t=0,e=this.length;t<e;t++)if("undefined"!=typeof this[t])return this[t]},v=function(){for(var t=this.length;--t;)if("undefined"!=typeof this[t])return this[t]},x=Object.prototype.toString,y=String,m=Array.isArray||function(t){return t instanceof Array||"[object Array]"==x.call(t)};eve=function(t,e){var r=d,i=p,n=Array.prototype.slice.call(arguments,2),a=eve.listeners(t),s=0,o=!1,l,h=[],u={},x=[],y=f,m=[];x.firstDefined=g,x.lastDefined=v,f=t,p=0;for(var b=0,_=a.length;b<_;b++)"zIndex"in a[b]&&(h.push(a[b].zIndex),a[b].zIndex<0&&(u[a[b].zIndex]=a[b]));for(h.sort(c);h[s]<0;)if(l=u[h[s++]],x.push(l.apply(e,n)),p)return p=i,x;for(b=0;b<_;b++)if(l=a[b],"zIndex"in l)if(l.zIndex==h[s]){if(x.push(l.apply(e,n)),p)break;do if(s++,l=u[h[s]],l&&x.push(l.apply(e,n)),p)break;while(l)}else u[l.zIndex]=l;else if(x.push(l.apply(e,n)),p)break;return p=i,f=y,x},eve._events=d,eve.listeners=function(t){var e=m(t)?t:t.split(o),r=d,i,n,a,s,l,u,c,f,p=[r],g=[];for(s=0,l=e.length;s<l;s++){for(f=[],u=0,c=p.length;u<c;u++)for(r=p[u].n,n=[r[e[s]],r[h]],a=2;a--;)i=n[a],i&&(f.push(i),g=g.concat(i.f||[]));p=f}return g},eve.separator=function(t){t?(t=y(t).replace(/(?=[\.\^\]\[\-])/g,"\\"),t="["+t+"]",o=new RegExp(t)):o=/[\.\/]/},eve.on=function(t,e){if("function"!=typeof e)return function(){};for(var r=m(t)?m(t[0])?t:[t]:y(t).split(l),i=0,n=r.length;i<n;i++)!function(t){for(var r=m(t)?t:y(t).split(o),i=d,n,a=0,s=r.length;a<s;a++)i=i.n,i=i.hasOwnProperty(r[a])&&i[r[a]]||(i[r[a]]={n:{}});for(i.f=i.f||[],a=0,s=i.f.length;a<s;a++)if(i.f[a]==e){n=!0;break}!n&&i.f.push(e)}(r[i]);return function(t){+t==+t&&(e.zIndex=+t)}},eve.f=function(t){var e=[].slice.call(arguments,1);return function(){eve.apply(null,[t,null].concat(e).concat([].slice.call(arguments,0)))}},eve.stop=function(){p=1},eve.nt=function(t){var e=m(f)?f.join("."):f;return t?new RegExp("(?:\\.|\\/|^)"+t+"(?:\\.|\\/|$)").test(e):e},eve.nts=function(){return m(f)?f:f.split(o)},eve.off=eve.unbind=function(t,e){if(!t)return void(eve._events=d={n:{}});var r=m(t)?m(t[0])?t:[t]:y(t).split(l);if(r.length>1)for(var i=0,n=r.length;i<n;i++)eve.off(r[i],e);else{r=m(t)?t:y(t).split(o);var a,u,c,i,n,f,p,g=[d];for(i=0,n=r.length;i<n;i++)for(f=0;f<g.length;f+=c.length-2){if(c=[f,1],a=g[f].n,r[i]!=h)a[r[i]]&&c.push(a[r[i]]);else for(u in a)a[s](u)&&c.push(a[u]);g.splice.apply(g,c)}for(i=0,n=g.length;i<n;i++)for(a=g[i];a.n;){if(e){if(a.f){for(f=0,p=a.f.length;f<p;f++)if(a.f[f]==e){a.f.splice(f,1);break}!a.f.length&&delete a.f}for(u in a.n)if(a.n[s](u)&&a.n[u].f){var v=a.n[u].f;for(f=0,p=v.length;f<p;f++)if(v[f]==e){v.splice(f,1);break}!v.length&&delete a.n[u].f}}else{delete a.f;for(u in a.n)a.n[s](u)&&a.n[u].f&&delete a.n[u].f}a=a.n}}},eve.once=function(t,e){var r=function(){return eve.off(t,r),e.apply(this,arguments)};return eve.on(t,r)},eve.version=a,eve.toString=function(){return"You are running Eve "+a},"undefined"!=typeof t&&t.exports?t.exports=eve:(i=[],n=function(){return eve}.apply(e,i),!(void 0!==n&&(t.exports=n)))}(this)},function(t,e,r){var i,n;i=[r(1)],n=function(t){if(!t||t.svg){var e="hasOwnProperty",r=String,i=parseFloat,n=parseInt,a=Math,s=a.max,o=a.abs,l=a.pow,h=/[, ]+/,u=t.eve,c="",f=" ",p="http://www.w3.org/1999/xlink",d={block:"M5,0 0,2.5 5,5z",classic:"M5,0 0,2.5 5,5 3.5,3 3.5,2z",diamond:"M2.5,0 5,2.5 2.5,5 0,2.5z",open:"M6,1 1,3.5 6,6",oval:"M2.5,0A2.5,2.5,0,0,1,2.5,5 2.5,2.5,0,0,1,2.5,0z"},g={};t.toString=function(){return"Your browser supports SVG.\nYou are running Raphaël "+this.version};var v=function(i,n){if(n){"string"==typeof i&&(i=v(i));for(var a in n)n[e](a)&&("xlink:"==a.substring(0,6)?i.setAttributeNS(p,a.substring(6),r(n[a])):i.setAttribute(a,r(n[a])))}else i=t._g.doc.createElementNS("http://www.w3.org/2000/svg",i),i.style&&(i.style.webkitTapHighlightColor="rgba(0,0,0,0)");return i},x=function(e,n){var h="linear",u=e.id+n,f=.5,p=.5,d=e.node,g=e.paper,x=d.style,y=t._g.doc.getElementById(u);if(!y){if(n=r(n).replace(t._radial_gradient,function(t,e,r){if(h="radial",e&&r){f=i(e),p=i(r);var n=2*(p>.5)-1;l(f-.5,2)+l(p-.5,2)>.25&&(p=a.sqrt(.25-l(f-.5,2))*n+.5)&&.5!=p&&(p=p.toFixed(5)-1e-5*n)}return c}),n=n.split(/\s*\-\s*/),"linear"==h){var b=n.shift();if(b=-i(b),isNaN(b))return null;var _=[0,0,a.cos(t.rad(b)),a.sin(t.rad(b))],w=1/(s(o(_[2]),o(_[3]))||1);_[2]*=w,_[3]*=w,_[2]<0&&(_[0]=-_[2],_[2]=0),_[3]<0&&(_[1]=-_[3],_[3]=0)}var k=t._parseDots(n);if(!k)return null;if(u=u.replace(/[\(\)\s,\xb0#]/g,"_"),e.gradient&&u!=e.gradient.id&&(g.defs.removeChild(e.gradient),delete e.gradient),!e.gradient){y=v(h+"Gradient",{id:u}),e.gradient=y,v(y,"radial"==h?{fx:f,fy:p}:{x1:_[0],y1:_[1],x2:_[2],y2:_[3],gradientTransform:e.matrix.invert()}),g.defs.appendChild(y);for(var B=0,C=k.length;B<C;B++)y.appendChild(v("stop",{offset:k[B].offset?k[B].offset:B?"100%":"0%","stop-color":k[B].color||"#fff","stop-opacity":isFinite(k[B].opacity)?k[B].opacity:1}))}}return v(d,{fill:m(u),opacity:1,"fill-opacity":1}),x.fill=c,x.opacity=1,x.fillOpacity=1,1},y=function(){var t=document.documentMode;return t&&(9===t||10===t)},m=function(t){if(y())return"url('#"+t+"')";var e=document.location,r=e.protocol+"//"+e.host+e.pathname+e.search;return"url('"+r+"#"+t+"')"},b=function(t){var e=t.getBBox(1);v(t.pattern,{patternTransform:t.matrix.invert()+" translate("+e.x+","+e.y+")"})},_=function(i,n,a){if("path"==i.type){for(var s=r(n).toLowerCase().split("-"),o=i.paper,l=a?"end":"start",h=i.node,u=i.attrs,f=u["stroke-width"],p=s.length,x="classic",y,m,b,_,w,k=3,B=3,C=5;p--;)switch(s[p]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":x=s[p];break;case"wide":B=5;break;case"narrow":B=2;break;case"long":k=5;break;case"short":k=2}if("open"==x?(k+=2,B+=2,C+=2,b=1,_=a?4:1,w={fill:"none",stroke:u.stroke}):(_=b=k/2,w={fill:u.stroke,stroke:"none"}),i._.arrows?a?(i._.arrows.endPath&&g[i._.arrows.endPath]--,i._.arrows.endMarker&&g[i._.arrows.endMarker]--):(i._.arrows.startPath&&g[i._.arrows.startPath]--,i._.arrows.startMarker&&g[i._.arrows.startMarker]--):i._.arrows={},"none"!=x){var S="raphael-marker-"+x,A="raphael-marker-"+l+x+k+B+"-obj"+i.id;t._g.doc.getElementById(S)?g[S]++:(o.defs.appendChild(v(v("path"),{"stroke-linecap":"round",d:d[x],id:S})),g[S]=1);var T=t._g.doc.getElementById(A),E;T?(g[A]++,E=T.getElementsByTagName("use")[0]):(T=v(v("marker"),{id:A,markerHeight:B,markerWidth:k,orient:"auto",refX:_,refY:B/2}),E=v(v("use"),{"xlink:href":"#"+S,transform:(a?"rotate(180 "+k/2+" "+B/2+") ":c)+"scale("+k/C+","+B/C+")","stroke-width":(1/((k/C+B/C)/2)).toFixed(4)}),T.appendChild(E),o.defs.appendChild(T),g[A]=1),v(E,w);var M=b*("diamond"!=x&&"oval"!=x);a?(y=i._.arrows.startdx*f||0,m=t.getTotalLength(u.path)-M*f):(y=M*f,m=t.getTotalLength(u.path)-(i._.arrows.enddx*f||0)),w={},w["marker-"+l]="url(#"+A+")",(m||y)&&(w.d=t.getSubpath(u.path,y,m)),v(h,w),i._.arrows[l+"Path"]=S,i._.arrows[l+"Marker"]=A,i._.arrows[l+"dx"]=M,i._.arrows[l+"Type"]=x,i._.arrows[l+"String"]=n}else a?(y=i._.arrows.startdx*f||0,m=t.getTotalLength(u.path)-y):(y=0,m=t.getTotalLength(u.path)-(i._.arrows.enddx*f||0)),i._.arrows[l+"Path"]&&v(h,{d:t.getSubpath(u.path,y,m)}),delete i._.arrows[l+"Path"],delete i._.arrows[l+"Marker"],delete i._.arrows[l+"dx"],delete i._.arrows[l+"Type"],delete i._.arrows[l+"String"];for(w in g)if(g[e](w)&&!g[w]){var N=t._g.doc.getElementById(w);N&&N.parentNode.removeChild(N)}}},w={"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},k=function(t,e,i){if(e=w[r(e).toLowerCase()]){for(var n=t.attrs["stroke-width"]||"1",a={round:n,square:n,butt:0}[t.attrs["stroke-linecap"]||i["stroke-linecap"]]||0,s=[],o=e.length;o--;)s[o]=e[o]*n+(o%2?1:-1)*a;v(t.node,{"stroke-dasharray":s.join(",")})}else v(t.node,{"stroke-dasharray":"none"})},B=function(i,a){var l=i.node,u=i.attrs,f=l.style.visibility;l.style.visibility="hidden";for(var d in a)if(a[e](d)){if(!t._availableAttrs[e](d))continue;var g=a[d];switch(u[d]=g,d){case"blur":i.blur(g);break;case"title":var y=l.getElementsByTagName("title");if(y.length&&(y=y[0]))y.firstChild.nodeValue=g;else{y=v("title");var m=t._g.doc.createTextNode(g);y.appendChild(m),l.appendChild(y)}break;case"href":case"target":var w=l.parentNode;if("a"!=w.tagName.toLowerCase()){var B=v("a");w.insertBefore(B,l),B.appendChild(l),w=B}"target"==d?w.setAttributeNS(p,"show","blank"==g?"new":g):w.setAttributeNS(p,d,g);break;case"cursor":l.style.cursor=g;break;case"transform":i.transform(g);break;case"arrow-start":_(i,g);break;case"arrow-end":_(i,g,1);break;case"clip-rect":var C=r(g).split(h);if(4==C.length){i.clip&&i.clip.parentNode.parentNode.removeChild(i.clip.parentNode);var A=v("clipPath"),T=v("rect");A.id=t.createUUID(),v(T,{x:C[0],y:C[1],width:C[2],height:C[3]}),A.appendChild(T),i.paper.defs.appendChild(A),v(l,{"clip-path":"url(#"+A.id+")"}),i.clip=T}if(!g){var E=l.getAttribute("clip-path");if(E){var M=t._g.doc.getElementById(E.replace(/(^url\(#|\)$)/g,c));M&&M.parentNode.removeChild(M),v(l,{"clip-path":c}),delete i.clip}}break;case"path":"path"==i.type&&(v(l,{d:g?u.path=t._pathToAbsolute(g):"M0,0"}),i._.dirty=1,i._.arrows&&("startString"in i._.arrows&&_(i,i._.arrows.startString),"endString"in i._.arrows&&_(i,i._.arrows.endString,1)));break;case"width":if(l.setAttribute(d,g),i._.dirty=1,!u.fx)break;d="x",g=u.x;case"x":u.fx&&(g=-u.x-(u.width||0));case"rx":if("rx"==d&&"rect"==i.type)break;case"cx":l.setAttribute(d,g),i.pattern&&b(i),i._.dirty=1;break;case"height":if(l.setAttribute(d,g),i._.dirty=1,!u.fy)break;d="y",g=u.y;case"y":u.fy&&(g=-u.y-(u.height||0));case"ry":if("ry"==d&&"rect"==i.type)break;case"cy":l.setAttribute(d,g),i.pattern&&b(i),i._.dirty=1;break;case"r":"rect"==i.type?v(l,{rx:g,ry:g}):l.setAttribute(d,g),i._.dirty=1;break;case"src":"image"==i.type&&l.setAttributeNS(p,"href",g);break;case"stroke-width":1==i._.sx&&1==i._.sy||(g/=s(o(i._.sx),o(i._.sy))||1),l.setAttribute(d,g),u["stroke-dasharray"]&&k(i,u["stroke-dasharray"],a),
3
+ i._.arrows&&("startString"in i._.arrows&&_(i,i._.arrows.startString),"endString"in i._.arrows&&_(i,i._.arrows.endString,1));break;case"stroke-dasharray":k(i,g,a);break;case"fill":var N=r(g).match(t._ISURL);if(N){A=v("pattern");var L=v("image");A.id=t.createUUID(),v(A,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1}),v(L,{x:0,y:0,"xlink:href":N[1]}),A.appendChild(L),function(e){t._preload(N[1],function(){var t=this.offsetWidth,r=this.offsetHeight;v(e,{width:t,height:r}),v(L,{width:t,height:r})})}(A),i.paper.defs.appendChild(A),v(l,{fill:"url(#"+A.id+")"}),i.pattern=A,i.pattern&&b(i);break}var z=t.getRGB(g);if(z.error){if(("circle"==i.type||"ellipse"==i.type||"r"!=r(g).charAt())&&x(i,g)){if("opacity"in u||"fill-opacity"in u){var P=t._g.doc.getElementById(l.getAttribute("fill").replace(/^url\(#|\)$/g,c));if(P){var F=P.getElementsByTagName("stop");v(F[F.length-1],{"stop-opacity":("opacity"in u?u.opacity:1)*("fill-opacity"in u?u["fill-opacity"]:1)})}}u.gradient=g,u.fill="none";break}}else delete a.gradient,delete u.gradient,!t.is(u.opacity,"undefined")&&t.is(a.opacity,"undefined")&&v(l,{opacity:u.opacity}),!t.is(u["fill-opacity"],"undefined")&&t.is(a["fill-opacity"],"undefined")&&v(l,{"fill-opacity":u["fill-opacity"]});z[e]("opacity")&&v(l,{"fill-opacity":z.opacity>1?z.opacity/100:z.opacity});case"stroke":z=t.getRGB(g),l.setAttribute(d,z.hex),"stroke"==d&&z[e]("opacity")&&v(l,{"stroke-opacity":z.opacity>1?z.opacity/100:z.opacity}),"stroke"==d&&i._.arrows&&("startString"in i._.arrows&&_(i,i._.arrows.startString),"endString"in i._.arrows&&_(i,i._.arrows.endString,1));break;case"gradient":("circle"==i.type||"ellipse"==i.type||"r"!=r(g).charAt())&&x(i,g);break;case"opacity":u.gradient&&!u[e]("stroke-opacity")&&v(l,{"stroke-opacity":g>1?g/100:g});case"fill-opacity":if(u.gradient){P=t._g.doc.getElementById(l.getAttribute("fill").replace(/^url\(#|\)$/g,c)),P&&(F=P.getElementsByTagName("stop"),v(F[F.length-1],{"stop-opacity":g}));break}default:"font-size"==d&&(g=n(g,10)+"px");var R=d.replace(/(\-.)/g,function(t){return t.substring(1).toUpperCase()});l.style[R]=g,i._.dirty=1,l.setAttribute(d,g)}}S(i,a),l.style.visibility=f},C=1.2,S=function(i,a){if("text"==i.type&&(a[e]("text")||a[e]("font")||a[e]("font-size")||a[e]("x")||a[e]("y"))){var s=i.attrs,o=i.node,l=o.firstChild?n(t._g.doc.defaultView.getComputedStyle(o.firstChild,c).getPropertyValue("font-size"),10):10;if(a[e]("text")){for(s.text=a.text;o.firstChild;)o.removeChild(o.firstChild);for(var h=r(a.text).split("\n"),u=[],f,p=0,d=h.length;p<d;p++)f=v("tspan"),p&&v(f,{dy:l*C,x:s.x}),f.appendChild(t._g.doc.createTextNode(h[p])),o.appendChild(f),u[p]=f}else for(u=o.getElementsByTagName("tspan"),p=0,d=u.length;p<d;p++)p?v(u[p],{dy:l*C,x:s.x}):v(u[0],{dy:0});v(o,{x:s.x,y:s.y}),i._.dirty=1;var g=i._getBBox(),x=s.y-(g.y+g.height/2);x&&t.is(x,"finite")&&v(u[0],{dy:x})}},A=function(t){return t.parentNode&&"a"===t.parentNode.tagName.toLowerCase()?t.parentNode:t},T=function(e,r){function i(){return("0000"+(Math.random()*Math.pow(36,5)<<0).toString(36)).slice(-5)}var n=0,a=0;this[0]=this.node=e,e.raphael=!0,this.id=i(),e.raphaelid=this.id,this.matrix=t.matrix(),this.realPath=null,this.paper=r,this.attrs=this.attrs||{},this._={transform:[],sx:1,sy:1,deg:0,dx:0,dy:0,dirty:1},!r.bottom&&(r.bottom=this),this.prev=r.top,r.top&&(r.top.next=this),r.top=this,this.next=null},E=t.el;T.prototype=E,E.constructor=T,t._engine.path=function(t,e){var r=v("path");e.canvas&&e.canvas.appendChild(r);var i=new T(r,e);return i.type="path",B(i,{fill:"none",stroke:"#000",path:t}),i},E.rotate=function(t,e,n){if(this.removed)return this;if(t=r(t).split(h),t.length-1&&(e=i(t[1]),n=i(t[2])),t=i(t[0]),null==n&&(e=n),null==e||null==n){var a=this.getBBox(1);e=a.x+a.width/2,n=a.y+a.height/2}return this.transform(this._.transform.concat([["r",t,e,n]])),this},E.scale=function(t,e,n,a){if(this.removed)return this;if(t=r(t).split(h),t.length-1&&(e=i(t[1]),n=i(t[2]),a=i(t[3])),t=i(t[0]),null==e&&(e=t),null==a&&(n=a),null==n||null==a)var s=this.getBBox(1);return n=null==n?s.x+s.width/2:n,a=null==a?s.y+s.height/2:a,this.transform(this._.transform.concat([["s",t,e,n,a]])),this},E.translate=function(t,e){return this.removed?this:(t=r(t).split(h),t.length-1&&(e=i(t[1])),t=i(t[0])||0,e=+e||0,this.transform(this._.transform.concat([["t",t,e]])),this)},E.transform=function(r){var i=this._;if(null==r)return i.transform;if(t._extractTransform(this,r),this.clip&&v(this.clip,{transform:this.matrix.invert()}),this.pattern&&b(this),this.node&&v(this.node,{transform:this.matrix}),1!=i.sx||1!=i.sy){var n=this.attrs[e]("stroke-width")?this.attrs["stroke-width"]:1;this.attr({"stroke-width":n})}return this},E.hide=function(){return this.removed||(this.node.style.display="none"),this},E.show=function(){return this.removed||(this.node.style.display=""),this},E.remove=function(){var e=A(this.node);if(!this.removed&&e.parentNode){var r=this.paper;r.__set__&&r.__set__.exclude(this),u.unbind("raphael.*.*."+this.id),this.gradient&&r.defs.removeChild(this.gradient),t._tear(this,r),e.parentNode.removeChild(e),this.removeData();for(var i in this)this[i]="function"==typeof this[i]?t._removedFactory(i):null;this.removed=!0}},E._getBBox=function(){if("none"==this.node.style.display){this.show();var t=!0}var e=!1,r;this.paper.canvas.parentElement?r=this.paper.canvas.parentElement.style:this.paper.canvas.parentNode&&(r=this.paper.canvas.parentNode.style),r&&"none"==r.display&&(e=!0,r.display="");var i={};try{i=this.node.getBBox()}catch(n){i={x:this.node.clientLeft,y:this.node.clientTop,width:this.node.clientWidth,height:this.node.clientHeight}}finally{i=i||{},e&&(r.display="none")}return t&&this.hide(),i},E.attr=function(r,i){if(this.removed)return this;if(null==r){var n={};for(var a in this.attrs)this.attrs[e](a)&&(n[a]=this.attrs[a]);return n.gradient&&"none"==n.fill&&(n.fill=n.gradient)&&delete n.gradient,n.transform=this._.transform,n}if(null==i&&t.is(r,"string")){if("fill"==r&&"none"==this.attrs.fill&&this.attrs.gradient)return this.attrs.gradient;if("transform"==r)return this._.transform;for(var s=r.split(h),o={},l=0,c=s.length;l<c;l++)r=s[l],r in this.attrs?o[r]=this.attrs[r]:t.is(this.paper.customAttributes[r],"function")?o[r]=this.paper.customAttributes[r].def:o[r]=t._availableAttrs[r];return c-1?o:o[s[0]]}if(null==i&&t.is(r,"array")){for(o={},l=0,c=r.length;l<c;l++)o[r[l]]=this.attr(r[l]);return o}if(null!=i){var f={};f[r]=i}else null!=r&&t.is(r,"object")&&(f=r);for(var p in f)u("raphael.attr."+p+"."+this.id,this,f[p]);for(p in this.paper.customAttributes)if(this.paper.customAttributes[e](p)&&f[e](p)&&t.is(this.paper.customAttributes[p],"function")){var d=this.paper.customAttributes[p].apply(this,[].concat(f[p]));this.attrs[p]=f[p];for(var g in d)d[e](g)&&(f[g]=d[g])}return B(this,f),this},E.toFront=function(){if(this.removed)return this;var e=A(this.node);e.parentNode.appendChild(e);var r=this.paper;return r.top!=this&&t._tofront(this,r),this},E.toBack=function(){if(this.removed)return this;var e=A(this.node),r=e.parentNode;r.insertBefore(e,r.firstChild),t._toback(this,this.paper);var i=this.paper;return this},E.insertAfter=function(e){if(this.removed||!e)return this;var r=A(this.node),i=A(e.node||e[e.length-1].node);return i.nextSibling?i.parentNode.insertBefore(r,i.nextSibling):i.parentNode.appendChild(r),t._insertafter(this,e,this.paper),this},E.insertBefore=function(e){if(this.removed||!e)return this;var r=A(this.node),i=A(e.node||e[0].node);return i.parentNode.insertBefore(r,i),t._insertbefore(this,e,this.paper),this},E.blur=function(e){var r=this;if(0!==+e){var i=v("filter"),n=v("feGaussianBlur");r.attrs.blur=e,i.id=t.createUUID(),v(n,{stdDeviation:+e||1.5}),i.appendChild(n),r.paper.defs.appendChild(i),r._blur=i,v(r.node,{filter:"url(#"+i.id+")"})}else r._blur&&(r._blur.parentNode.removeChild(r._blur),delete r._blur,delete r.attrs.blur),r.node.removeAttribute("filter");return r},t._engine.circle=function(t,e,r,i){var n=v("circle");t.canvas&&t.canvas.appendChild(n);var a=new T(n,t);return a.attrs={cx:e,cy:r,r:i,fill:"none",stroke:"#000"},a.type="circle",v(n,a.attrs),a},t._engine.rect=function(t,e,r,i,n,a){var s=v("rect");t.canvas&&t.canvas.appendChild(s);var o=new T(s,t);return o.attrs={x:e,y:r,width:i,height:n,rx:a||0,ry:a||0,fill:"none",stroke:"#000"},o.type="rect",v(s,o.attrs),o},t._engine.ellipse=function(t,e,r,i,n){var a=v("ellipse");t.canvas&&t.canvas.appendChild(a);var s=new T(a,t);return s.attrs={cx:e,cy:r,rx:i,ry:n,fill:"none",stroke:"#000"},s.type="ellipse",v(a,s.attrs),s},t._engine.image=function(t,e,r,i,n,a){var s=v("image");v(s,{x:r,y:i,width:n,height:a,preserveAspectRatio:"none"}),s.setAttributeNS(p,"href",e),t.canvas&&t.canvas.appendChild(s);var o=new T(s,t);return o.attrs={x:r,y:i,width:n,height:a,src:e},o.type="image",o},t._engine.text=function(e,r,i,n){var a=v("text");e.canvas&&e.canvas.appendChild(a);var s=new T(a,e);return s.attrs={x:r,y:i,"text-anchor":"middle",text:n,"font-family":t._availableAttrs["font-family"],"font-size":t._availableAttrs["font-size"],stroke:"none",fill:"#000"},s.type="text",B(s,s.attrs),s},t._engine.setSize=function(t,e){return this.width=t||this.width,this.height=e||this.height,this.canvas.setAttribute("width",this.width),this.canvas.setAttribute("height",this.height),this._viewBox&&this.setViewBox.apply(this,this._viewBox),this},t._engine.create=function(){var e=t._getContainer.apply(0,arguments),r=e&&e.container,i=e.x,n=e.y,a=e.width,s=e.height;if(!r)throw new Error("SVG container not found.");var o=v("svg"),l="overflow:hidden;",h;return i=i||0,n=n||0,a=a||512,s=s||342,v(o,{height:s,version:1.1,width:a,xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}),1==r?(o.style.cssText=l+"position:absolute;left:"+i+"px;top:"+n+"px",t._g.doc.body.appendChild(o),h=1):(o.style.cssText=l+"position:relative",r.firstChild?r.insertBefore(o,r.firstChild):r.appendChild(o)),r=new t._Paper,r.width=a,r.height=s,r.canvas=o,r.clear(),r._left=r._top=0,h&&(r.renderfix=function(){}),r.renderfix(),r},t._engine.setViewBox=function(t,e,r,i,n){u("raphael.setViewBox",this,this._viewBox,[t,e,r,i,n]);var a=this.getSize(),o=s(r/a.width,i/a.height),l=this.top,h=n?"xMidYMid meet":"xMinYMin",c,p;for(null==t?(this._vbSize&&(o=1),delete this._vbSize,c="0 0 "+this.width+f+this.height):(this._vbSize=o,c=t+f+e+f+r+f+i),v(this.canvas,{viewBox:c,preserveAspectRatio:h});o&&l;)p="stroke-width"in l.attrs?l.attrs["stroke-width"]:1,l.attr({"stroke-width":p}),l._.dirty=1,l._.dirtyT=1,l=l.prev;return this._viewBox=[t,e,r,i,!!n],this},t.prototype.renderfix=function(){var t=this.canvas,e=t.style,r;try{r=t.getScreenCTM()||t.createSVGMatrix()}catch(i){r=t.createSVGMatrix()}var n=-r.e%1,a=-r.f%1;(n||a)&&(n&&(this._left=(this._left+n)%1,e.left=this._left+"px"),a&&(this._top=(this._top+a)%1,e.top=this._top+"px"))},t.prototype.clear=function(){t.eve("raphael.clear",this);for(var e=this.canvas;e.firstChild;)e.removeChild(e.firstChild);this.bottom=this.top=null,(this.desc=v("desc")).appendChild(t._g.doc.createTextNode("Created with Raphaël "+t.version)),e.appendChild(this.desc),e.appendChild(this.defs=v("defs"))},t.prototype.remove=function(){u("raphael.remove",this),this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var e in this)this[e]="function"==typeof this[e]?t._removedFactory(e):null};var M=t.st;for(var N in E)E[e](N)&&!M[e](N)&&(M[N]=function(t){return function(){var e=arguments;return this.forEach(function(r){r[t].apply(r,e)})}}(N))}}.apply(e,i),!(void 0!==n&&(t.exports=n))},function(t,e,r){var i,n;i=[r(1)],n=function(t){if(!t||t.vml){var e="hasOwnProperty",r=String,i=parseFloat,n=Math,a=n.round,s=n.max,o=n.min,l=n.abs,h="fill",u=/[, ]+/,c=t.eve,f=" progid:DXImageTransform.Microsoft",p=" ",d="",g={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},v=/([clmz]),?([^clmz]*)/gi,x=/ progid:\S+Blur\([^\)]+\)/g,y=/-?[^,\s-]+/g,m="position:absolute;left:0;top:0;width:1px;height:1px;behavior:url(#default#VML)",b=21600,_={path:1,rect:1,image:1},w={circle:1,ellipse:1},k=function(e){var i=/[ahqstv]/gi,n=t._pathToAbsolute;if(r(e).match(i)&&(n=t._path2curve),i=/[clmz]/g,n==t._pathToAbsolute&&!r(e).match(i)){var s=r(e).replace(v,function(t,e,r){var i=[],n="m"==e.toLowerCase(),s=g[e];return r.replace(y,function(t){n&&2==i.length&&(s+=i+g["m"==e?"l":"L"],i=[]),i.push(a(t*b))}),s+i});return s}var o=n(e),l,h;s=[];for(var u=0,c=o.length;u<c;u++){l=o[u],h=o[u][0].toLowerCase(),"z"==h&&(h="x");for(var f=1,x=l.length;f<x;f++)h+=a(l[f]*b)+(f!=x-1?",":d);s.push(h)}return s.join(p)},B=function(e,r,i){var n=t.matrix();return n.rotate(-e,.5,.5),{dx:n.x(r,i),dy:n.y(r,i)}},C=function(t,e,r,i,n,a){var s=t._,o=t.matrix,u=s.fillpos,c=t.node,f=c.style,d=1,g="",v,x=b/e,y=b/r;if(f.visibility="hidden",e&&r){if(c.coordsize=l(x)+p+l(y),f.rotation=a*(e*r<0?-1:1),a){var m=B(a,i,n);i=m.dx,n=m.dy}if(e<0&&(g+="x"),r<0&&(g+=" y")&&(d=-1),f.flip=g,c.coordorigin=i*-x+p+n*-y,u||s.fillsize){var _=c.getElementsByTagName(h);_=_&&_[0],c.removeChild(_),u&&(m=B(a,o.x(u[0],u[1]),o.y(u[0],u[1])),_.position=m.dx*d+p+m.dy*d),s.fillsize&&(_.size=s.fillsize[0]*l(e)+p+s.fillsize[1]*l(r)),c.appendChild(_)}f.visibility="visible"}};t.toString=function(){return"Your browser doesn’t support SVG. Falling down to VML.\nYou are running Raphaël "+this.version};var S=function(t,e,i){for(var n=r(e).toLowerCase().split("-"),a=i?"end":"start",s=n.length,o="classic",l="medium",h="medium";s--;)switch(n[s]){case"block":case"classic":case"oval":case"diamond":case"open":case"none":o=n[s];break;case"wide":case"narrow":h=n[s];break;case"long":case"short":l=n[s]}var u=t.node.getElementsByTagName("stroke")[0];u[a+"arrow"]=o,u[a+"arrowlength"]=l,u[a+"arrowwidth"]=h},A=function(n,l){n.attrs=n.attrs||{};var c=n.node,f=n.attrs,g=c.style,v,x=_[n.type]&&(l.x!=f.x||l.y!=f.y||l.width!=f.width||l.height!=f.height||l.cx!=f.cx||l.cy!=f.cy||l.rx!=f.rx||l.ry!=f.ry||l.r!=f.r),y=w[n.type]&&(f.cx!=l.cx||f.cy!=l.cy||f.r!=l.r||f.rx!=l.rx||f.ry!=l.ry),m=n;for(var B in l)l[e](B)&&(f[B]=l[B]);if(x&&(f.path=t._getPath[n.type](n),n._.dirty=1),l.href&&(c.href=l.href),l.title&&(c.title=l.title),l.target&&(c.target=l.target),l.cursor&&(g.cursor=l.cursor),"blur"in l&&n.blur(l.blur),(l.path&&"path"==n.type||x)&&(c.path=k(~r(f.path).toLowerCase().indexOf("r")?t._pathToAbsolute(f.path):f.path),n._.dirty=1,"image"==n.type&&(n._.fillpos=[f.x,f.y],n._.fillsize=[f.width,f.height],C(n,1,1,0,0,0))),"transform"in l&&n.transform(l.transform),y){var A=+f.cx,E=+f.cy,M=+f.rx||+f.r||0,L=+f.ry||+f.r||0;c.path=t.format("ar{0},{1},{2},{3},{4},{1},{4},{1}x",a((A-M)*b),a((E-L)*b),a((A+M)*b),a((E+L)*b),a(A*b)),n._.dirty=1}if("clip-rect"in l){var z=r(l["clip-rect"]).split(u);if(4==z.length){z[2]=+z[2]+ +z[0],z[3]=+z[3]+ +z[1];var P=c.clipRect||t._g.doc.createElement("div"),F=P.style;F.clip=t.format("rect({1}px {2}px {3}px {0}px)",z),c.clipRect||(F.position="absolute",F.top=0,F.left=0,F.width=n.paper.width+"px",F.height=n.paper.height+"px",c.parentNode.insertBefore(P,c),P.appendChild(c),c.clipRect=P)}l["clip-rect"]||c.clipRect&&(c.clipRect.style.clip="auto")}if(n.textpath){var R=n.textpath.style;l.font&&(R.font=l.font),l["font-family"]&&(R.fontFamily='"'+l["font-family"].split(",")[0].replace(/^['"]+|['"]+$/g,d)+'"'),l["font-size"]&&(R.fontSize=l["font-size"]),l["font-weight"]&&(R.fontWeight=l["font-weight"]),l["font-style"]&&(R.fontStyle=l["font-style"])}if("arrow-start"in l&&S(m,l["arrow-start"]),"arrow-end"in l&&S(m,l["arrow-end"],1),null!=l.opacity||null!=l.fill||null!=l.src||null!=l.stroke||null!=l["stroke-width"]||null!=l["stroke-opacity"]||null!=l["fill-opacity"]||null!=l["stroke-dasharray"]||null!=l["stroke-miterlimit"]||null!=l["stroke-linejoin"]||null!=l["stroke-linecap"]){var j=c.getElementsByTagName(h),I=!1;if(j=j&&j[0],!j&&(I=j=N(h)),"image"==n.type&&l.src&&(j.src=l.src),l.fill&&(j.on=!0),null!=j.on&&"none"!=l.fill&&null!==l.fill||(j.on=!1),j.on&&l.fill){var q=r(l.fill).match(t._ISURL);if(q){j.parentNode==c&&c.removeChild(j),j.rotate=!0,j.src=q[1],j.type="tile";var D=n.getBBox(1);j.position=D.x+p+D.y,n._.fillpos=[D.x,D.y],t._preload(q[1],function(){n._.fillsize=[this.offsetWidth,this.offsetHeight]})}else j.color=t.getRGB(l.fill).hex,j.src=d,j.type="solid",t.getRGB(l.fill).error&&(m.type in{circle:1,ellipse:1}||"r"!=r(l.fill).charAt())&&T(m,l.fill,j)&&(f.fill="none",f.gradient=l.fill,j.rotate=!1)}if("fill-opacity"in l||"opacity"in l){var V=((+f["fill-opacity"]+1||2)-1)*((+f.opacity+1||2)-1)*((+t.getRGB(l.fill).o+1||2)-1);V=o(s(V,0),1),j.opacity=V,j.src&&(j.color="none")}c.appendChild(j);var O=c.getElementsByTagName("stroke")&&c.getElementsByTagName("stroke")[0],Y=!1;!O&&(Y=O=N("stroke")),(l.stroke&&"none"!=l.stroke||l["stroke-width"]||null!=l["stroke-opacity"]||l["stroke-dasharray"]||l["stroke-miterlimit"]||l["stroke-linejoin"]||l["stroke-linecap"])&&(O.on=!0),("none"==l.stroke||null===l.stroke||null==O.on||0==l.stroke||0==l["stroke-width"])&&(O.on=!1);var W=t.getRGB(l.stroke);O.on&&l.stroke&&(O.color=W.hex),V=((+f["stroke-opacity"]+1||2)-1)*((+f.opacity+1||2)-1)*((+W.o+1||2)-1);var G=.75*(i(l["stroke-width"])||1);if(V=o(s(V,0),1),null==l["stroke-width"]&&(G=f["stroke-width"]),l["stroke-width"]&&(O.weight=G),G&&G<1&&(V*=G)&&(O.weight=1),O.opacity=V,l["stroke-linejoin"]&&(O.joinstyle=l["stroke-linejoin"]||"miter"),O.miterlimit=l["stroke-miterlimit"]||8,l["stroke-linecap"]&&(O.endcap="butt"==l["stroke-linecap"]?"flat":"square"==l["stroke-linecap"]?"square":"round"),"stroke-dasharray"in l){var H={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};O.dashstyle=H[e](l["stroke-dasharray"])?H[l["stroke-dasharray"]]:d}Y&&c.appendChild(O)}if("text"==m.type){m.paper.canvas.style.display=d;var X=m.paper.span,U=100,$=f.font&&f.font.match(/\d+(?:\.\d*)?(?=px)/);g=X.style,f.font&&(g.font=f.font),f["font-family"]&&(g.fontFamily=f["font-family"]),f["font-weight"]&&(g.fontWeight=f["font-weight"]),f["font-style"]&&(g.fontStyle=f["font-style"]),$=i(f["font-size"]||$&&$[0])||10,g.fontSize=$*U+"px",m.textpath.string&&(X.innerHTML=r(m.textpath.string).replace(/</g,"&#60;").replace(/&/g,"&#38;").replace(/\n/g,"<br>"));var Z=X.getBoundingClientRect();m.W=f.w=(Z.right-Z.left)/U,m.H=f.h=(Z.bottom-Z.top)/U,m.X=f.x,m.Y=f.y+m.H/2,("x"in l||"y"in l)&&(m.path.v=t.format("m{0},{1}l{2},{1}",a(f.x*b),a(f.y*b),a(f.x*b)+1));for(var Q=["x","y","text","font","font-family","font-weight","font-style","font-size"],J=0,K=Q.length;J<K;J++)if(Q[J]in l){m._.dirty=1;break}switch(f["text-anchor"]){case"start":m.textpath.style["v-text-align"]="left",m.bbx=m.W/2;break;case"end":m.textpath.style["v-text-align"]="right",m.bbx=-m.W/2;break;default:m.textpath.style["v-text-align"]="center",m.bbx=0}m.textpath.style["v-text-kern"]=!0}},T=function(e,a,s){e.attrs=e.attrs||{};var o=e.attrs,l=Math.pow,h,u,c="linear",f=".5 .5";if(e.attrs.gradient=a,a=r(a).replace(t._radial_gradient,function(t,e,r){return c="radial",e&&r&&(e=i(e),r=i(r),l(e-.5,2)+l(r-.5,2)>.25&&(r=n.sqrt(.25-l(e-.5,2))*(2*(r>.5)-1)+.5),f=e+p+r),d}),a=a.split(/\s*\-\s*/),"linear"==c){var g=a.shift();if(g=-i(g),isNaN(g))return null}var v=t._parseDots(a);if(!v)return null;if(e=e.shape||e.node,v.length){e.removeChild(s),s.on=!0,s.method="none",s.color=v[0].color,s.color2=v[v.length-1].color;for(var x=[],y=0,m=v.length;y<m;y++)v[y].offset&&x.push(v[y].offset+p+v[y].color);s.colors=x.length?x.join():"0% "+s.color,"radial"==c?(s.type="gradientTitle",s.focus="100%",s.focussize="0 0",s.focusposition=f,s.angle=0):(s.type="gradient",s.angle=(270-g)%360),e.appendChild(s)}return 1},E=function(e,r){this[0]=this.node=e,e.raphael=!0,this.id=t._oid++,e.raphaelid=this.id,this.X=0,this.Y=0,this.attrs={},this.paper=r,this.matrix=t.matrix(),this._={transform:[],sx:1,sy:1,dx:0,dy:0,deg:0,dirty:1,dirtyT:1},!r.bottom&&(r.bottom=this),this.prev=r.top,r.top&&(r.top.next=this),r.top=this,this.next=null},M=t.el;E.prototype=M,M.constructor=E,M.transform=function(e){if(null==e)return this._.transform;var i=this.paper._viewBoxShift,n=i?"s"+[i.scale,i.scale]+"-1-1t"+[i.dx,i.dy]:d,a;i&&(a=e=r(e).replace(/\.{3}|\u2026/g,this._.transform||d)),t._extractTransform(this,n+e);var s=this.matrix.clone(),o=this.skew,l=this.node,h,u=~r(this.attrs.fill).indexOf("-"),c=!r(this.attrs.fill).indexOf("url(");if(s.translate(1,1),c||u||"image"==this.type)if(o.matrix="1 0 0 1",o.offset="0 0",h=s.split(),u&&h.noRotation||!h.isSimple){l.style.filter=s.toFilter();var f=this.getBBox(),g=this.getBBox(1),v=f.x-g.x,x=f.y-g.y;l.coordorigin=v*-b+p+x*-b,C(this,1,1,v,x,0)}else l.style.filter=d,C(this,h.scalex,h.scaley,h.dx,h.dy,h.rotate);else l.style.filter=d,o.matrix=r(s),o.offset=s.offset();return null!==a&&(this._.transform=a,t._extractTransform(this,a)),this},M.rotate=function(t,e,n){if(this.removed)return this;if(null!=t){if(t=r(t).split(u),t.length-1&&(e=i(t[1]),n=i(t[2])),t=i(t[0]),null==n&&(e=n),null==e||null==n){var a=this.getBBox(1);e=a.x+a.width/2,n=a.y+a.height/2}return this._.dirtyT=1,this.transform(this._.transform.concat([["r",t,e,n]])),this}},M.translate=function(t,e){return this.removed?this:(t=r(t).split(u),t.length-1&&(e=i(t[1])),t=i(t[0])||0,e=+e||0,this._.bbox&&(this._.bbox.x+=t,this._.bbox.y+=e),this.transform(this._.transform.concat([["t",t,e]])),this)},M.scale=function(t,e,n,a){if(this.removed)return this;if(t=r(t).split(u),t.length-1&&(e=i(t[1]),n=i(t[2]),a=i(t[3]),isNaN(n)&&(n=null),isNaN(a)&&(a=null)),t=i(t[0]),null==e&&(e=t),null==a&&(n=a),null==n||null==a)var s=this.getBBox(1);return n=null==n?s.x+s.width/2:n,a=null==a?s.y+s.height/2:a,this.transform(this._.transform.concat([["s",t,e,n,a]])),this._.dirtyT=1,this},M.hide=function(){return!this.removed&&(this.node.style.display="none"),this},M.show=function(){return!this.removed&&(this.node.style.display=d),this},M.auxGetBBox=t.el.getBBox,M.getBBox=function(){var t=this.auxGetBBox();if(this.paper&&this.paper._viewBoxShift){var e={},r=1/this.paper._viewBoxShift.scale;return e.x=t.x-this.paper._viewBoxShift.dx,e.x*=r,e.y=t.y-this.paper._viewBoxShift.dy,e.y*=r,e.width=t.width*r,e.height=t.height*r,e.x2=e.x+e.width,e.y2=e.y+e.height,e}return t},M._getBBox=function(){return this.removed?{}:{x:this.X+(this.bbx||0)-this.W/2,y:this.Y-this.H,width:this.W,height:this.H}},M.remove=function(){if(!this.removed&&this.node.parentNode){this.paper.__set__&&this.paper.__set__.exclude(this),t.eve.unbind("raphael.*.*."+this.id),t._tear(this,this.paper),this.node.parentNode.removeChild(this.node),this.shape&&this.shape.parentNode.removeChild(this.shape);for(var e in this)this[e]="function"==typeof this[e]?t._removedFactory(e):null;this.removed=!0}},M.attr=function(r,i){if(this.removed)return this;if(null==r){var n={};for(var a in this.attrs)this.attrs[e](a)&&(n[a]=this.attrs[a]);return n.gradient&&"none"==n.fill&&(n.fill=n.gradient)&&delete n.gradient,n.transform=this._.transform,n}if(null==i&&t.is(r,"string")){if(r==h&&"none"==this.attrs.fill&&this.attrs.gradient)return this.attrs.gradient;for(var s=r.split(u),o={},l=0,f=s.length;l<f;l++)r=s[l],r in this.attrs?o[r]=this.attrs[r]:t.is(this.paper.customAttributes[r],"function")?o[r]=this.paper.customAttributes[r].def:o[r]=t._availableAttrs[r];return f-1?o:o[s[0]]}if(this.attrs&&null==i&&t.is(r,"array")){for(o={},l=0,f=r.length;l<f;l++)o[r[l]]=this.attr(r[l]);return o}var p;null!=i&&(p={},p[r]=i),null==i&&t.is(r,"object")&&(p=r);for(var d in p)c("raphael.attr."+d+"."+this.id,this,p[d]);if(p){for(d in this.paper.customAttributes)if(this.paper.customAttributes[e](d)&&p[e](d)&&t.is(this.paper.customAttributes[d],"function")){var g=this.paper.customAttributes[d].apply(this,[].concat(p[d]));this.attrs[d]=p[d];for(var v in g)g[e](v)&&(p[v]=g[v])}p.text&&"text"==this.type&&(this.textpath.string=p.text),A(this,p)}return this},M.toFront=function(){return!this.removed&&this.node.parentNode.appendChild(this.node),this.paper&&this.paper.top!=this&&t._tofront(this,this.paper),this},M.toBack=function(){return this.removed?this:(this.node.parentNode.firstChild!=this.node&&(this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild),t._toback(this,this.paper)),this)},M.insertAfter=function(e){return this.removed?this:(e.constructor==t.st.constructor&&(e=e[e.length-1]),e.node.nextSibling?e.node.parentNode.insertBefore(this.node,e.node.nextSibling):e.node.parentNode.appendChild(this.node),t._insertafter(this,e,this.paper),this)},M.insertBefore=function(e){return this.removed?this:(e.constructor==t.st.constructor&&(e=e[0]),e.node.parentNode.insertBefore(this.node,e.node),t._insertbefore(this,e,this.paper),this)},M.blur=function(e){var r=this.node.runtimeStyle,i=r.filter;return i=i.replace(x,d),0!==+e?(this.attrs.blur=e,r.filter=i+p+f+".Blur(pixelradius="+(+e||1.5)+")",r.margin=t.format("-{0}px 0 0 -{0}px",a(+e||1.5))):(r.filter=i,r.margin=0,delete this.attrs.blur),this},t._engine.path=function(t,e){var r=N("shape");r.style.cssText=m,r.coordsize=b+p+b,r.coordorigin=e.coordorigin;var i=new E(r,e),n={fill:"none",stroke:"#000"};t&&(n.path=t),i.type="path",i.path=[],i.Path=d,A(i,n),e.canvas&&e.canvas.appendChild(r);var a=N("skew");return a.on=!0,r.appendChild(a),i.skew=a,i.transform(d),i},t._engine.rect=function(e,r,i,n,a,s){var o=t._rectPath(r,i,n,a,s),l=e.path(o),h=l.attrs;return l.X=h.x=r,l.Y=h.y=i,l.W=h.width=n,l.H=h.height=a,h.r=s,h.path=o,l.type="rect",l},t._engine.ellipse=function(t,e,r,i,n){var a=t.path(),s=a.attrs;return a.X=e-i,a.Y=r-n,a.W=2*i,a.H=2*n,a.type="ellipse",A(a,{cx:e,cy:r,rx:i,ry:n}),a},t._engine.circle=function(t,e,r,i){var n=t.path(),a=n.attrs;return n.X=e-i,n.Y=r-i,n.W=n.H=2*i,n.type="circle",A(n,{cx:e,cy:r,r:i}),n},t._engine.image=function(e,r,i,n,a,s){var o=t._rectPath(i,n,a,s),l=e.path(o).attr({stroke:"none"}),u=l.attrs,c=l.node,f=c.getElementsByTagName(h)[0];return u.src=r,l.X=u.x=i,l.Y=u.y=n,l.W=u.width=a,l.H=u.height=s,u.path=o,l.type="image",f.parentNode==c&&c.removeChild(f),f.rotate=!0,f.src=r,f.type="tile",l._.fillpos=[i,n],l._.fillsize=[a,s],c.appendChild(f),C(l,1,1,0,0,0),l},t._engine.text=function(e,i,n,s){var o=N("shape"),l=N("path"),h=N("textpath");i=i||0,n=n||0,s=s||"",l.v=t.format("m{0},{1}l{2},{1}",a(i*b),a(n*b),a(i*b)+1),l.textpathok=!0,h.string=r(s),h.on=!0,o.style.cssText=m,o.coordsize=b+p+b,o.coordorigin="0 0";var u=new E(o,e),c={fill:"#000",stroke:"none",font:t._availableAttrs.font,text:s};u.shape=o,u.path=l,u.textpath=h,u.type="text",u.attrs.text=r(s),u.attrs.x=i,u.attrs.y=n,u.attrs.w=1,u.attrs.h=1,A(u,c),o.appendChild(h),o.appendChild(l),e.canvas.appendChild(o);var f=N("skew");return f.on=!0,o.appendChild(f),u.skew=f,u.transform(d),u},t._engine.setSize=function(e,r){var i=this.canvas.style;return this.width=e,this.height=r,e==+e&&(e+="px"),r==+r&&(r+="px"),i.width=e,i.height=r,i.clip="rect(0 "+e+" "+r+" 0)",this._viewBox&&t._engine.setViewBox.apply(this,this._viewBox),this},t._engine.setViewBox=function(e,r,i,n,a){t.eve("raphael.setViewBox",this,this._viewBox,[e,r,i,n,a]);var s=this.getSize(),o=s.width,l=s.height,h,u;return a&&(h=l/n,u=o/i,i*h<o&&(e-=(o-i*h)/2/h),n*u<l&&(r-=(l-n*u)/2/u)),this._viewBox=[e,r,i,n,!!a],this._viewBoxShift={dx:-e,dy:-r,scale:s},this.forEach(function(t){t.transform("...")}),this};var N;t._engine.initWin=function(t){var e=t.document;e.styleSheets.length<31?e.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)"):e.styleSheets[0].addRule(".rvml","behavior:url(#default#VML)");try{!e.namespaces.rvml&&e.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),N=function(t){return e.createElement("<rvml:"+t+' class="rvml">')}}catch(r){N=function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},t._engine.initWin(t._g.win),t._engine.create=function(){var e=t._getContainer.apply(0,arguments),r=e.container,i=e.height,n,a=e.width,s=e.x,o=e.y;if(!r)throw new Error("VML container not found.");var l=new t._Paper,h=l.canvas=t._g.doc.createElement("div"),u=h.style;return s=s||0,o=o||0,a=a||512,i=i||342,l.width=a,l.height=i,a==+a&&(a+="px"),i==+i&&(i+="px"),l.coordsize=1e3*b+p+1e3*b,l.coordorigin="0 0",l.span=t._g.doc.createElement("span"),l.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",h.appendChild(l.span),u.cssText=t.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",a,i),1==r?(t._g.doc.body.appendChild(h),u.left=s+"px",u.top=o+"px",u.position="absolute"):r.firstChild?r.insertBefore(h,r.firstChild):r.appendChild(h),l.renderfix=function(){},l},t.prototype.clear=function(){t.eve("raphael.clear",this),this.canvas.innerHTML=d,this.span=t._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},t.prototype.remove=function(){t.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var e in this)this[e]="function"==typeof this[e]?t._removedFactory(e):null;return!0};var L=t.st;for(var z in M)M[e](z)&&!L[e](z)&&(L[z]=function(t){return function(){var e=arguments;return this.forEach(function(r){r[t].apply(r,e)})}}(z))}}.apply(e,i),!(void 0!==n&&(t.exports=n))}])});
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -4,7 +4,7 @@
4
  * Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW
5
  * Requires at least: 4.5
6
  * Tested up to: 4.9
7
- * Stable tag: 2.8.1
8
  * License: GPLv2 or later
9
  * License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -86,6 +86,21 @@ A complete documentation is available in the [GitHub repository Wiki](https://gi
86
 
87
  ## Changelog ##
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  ### 2.8.1 ###
90
  * PHP 5.3 compatibility
91
  * Bugfix where a spam trackback produced a fatal error
4
  * Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TD4AMD2D8EMZW
5
  * Requires at least: 4.5
6
  * Tested up to: 4.9
7
+ * Stable tag: 2.9.0
8
  * License: GPLv2 or later
9
  * License URI: https://www.gnu.org/licenses/gpl-2.0.html
10
 
86
 
87
  ## Changelog ##
88
 
89
+ ### 2.9.0 ###
90
+ * Introduction of coding standards.
91
+ * Switch to franc language detection API for the language check.
92
+ * Do not longer overwrite the IP address WordPress saves with the comment by using `pre_comment_user_ip`.
93
+ * Do not show "Trust commenters with a Gravatar" if the "Show Gravatar" option is not set.
94
+ * Skip the checks, when I ping myself.
95
+ * Fixes some wrong usages of the translation functions.
96
+ * Use the regular expressions check also for trackbacks.
97
+ * Add option to delete Antispam Bee related data when plugin gets deleted via the admin interface.
98
+ * Save a hashed + salted IP for every comment
99
+ * New check for incoming Trackbacks.
100
+ * Introduction of behat tests.
101
+ * Updates the used JavaScript library for the statistics widget.
102
+ * Bugfix in the "Comment form used outside of posts" option.
103
+
104
  ### 2.8.1 ###
105
  * PHP 5.3 compatibility
106
  * Bugfix where a spam trackback produced a fatal error