WordPress Zero Spam - Version 5.0.0

Version Description

Download this release

Release Info

Developer bmarshall511
Plugin Icon 128x128 WordPress Zero Spam
Version 5.0.0
Comparing to
See all releases

Code changes from version 4.10.2 to 5.0.0

Files changed (78) hide show
  1. assets/css/Chart.min.css +0 -1
  2. assets/css/admin-blocked-ips.css +0 -56
  3. assets/css/admin-dashboard.css +0 -144
  4. assets/css/admin-tables.css +0 -62
  5. assets/css/admin.css +133 -62
  6. assets/css/debug.css +0 -25
  7. assets/css/jquery-jvectormap-2.0.5.css +0 -135
  8. assets/img/icon-yellow.svg +0 -25
  9. assets/img/icon.svg +0 -23
  10. assets/js/Chart.bundle.min.js +0 -7
  11. assets/js/admin-blocked-ips.js +0 -15
  12. assets/js/admin-tables.js +0 -22
  13. assets/js/admin.js +59 -0
  14. assets/js/jquery-jvectormap-2.0.5.min.js +0 -1
  15. assets/js/jquery-jvectormap-world-mill.js +0 -1
  16. assets/js/wpzerospam.js +0 -58
  17. classes/class-wpzerospam-blacklisted-table.php +0 -305
  18. classes/class-wpzerospam-blocked-ip-table.php +0 -260
  19. classes/class-wpzerospam-comments.php +0 -16
  20. classes/class-wpzerospam-log-table.php +0 -371
  21. classes/class-wpzerospam-security.php +0 -36
  22. classes/class-wpzerospam.php +0 -977
  23. composer.json +0 -36
  24. core/admin/class-admin.php +148 -0
  25. core/admin/class-dashboard.php +233 -0
  26. core/admin/class-settings.php +264 -0
  27. core/admin/tables/class-blockedtable.php +307 -0
  28. core/admin/tables/class-logtable.php +327 -0
  29. core/class-access.php +181 -0
  30. core/class-settings.php +191 -0
  31. core/class-user.php +61 -0
  32. core/class-utilities.php +111 -0
  33. inc/admin.php +0 -849
  34. inc/helpers.php +0 -600
  35. inc/install.php +0 -86
  36. inc/locations.php +0 -2152
  37. inc/scripts.php +0 -160
  38. inc/uninstall.php +0 -50
  39. inc/updates.php +0 -48
  40. inc/utilities.php +0 -309
  41. includes/class-autoloader.php +121 -0
  42. includes/class-db.php +280 -0
  43. includes/class-plugin.php +181 -0
  44. includes/templates/admin-block-ip.php +55 -0
  45. includes/templates/admin-callout.php +73 -0
  46. includes/templates/admin-modal-details.php +111 -0
  47. integrations/buddypress/buddypress.php +0 -48
  48. integrations/buddypress/js/buddypress.js +0 -4
  49. integrations/comments/comments.php +0 -379
  50. integrations/comments/js/comments.js +0 -4
  51. integrations/contact-form-7/contact-form-7.php +0 -198
  52. integrations/contact-form-7/js/cf7.js +0 -4
  53. integrations/fluentform/fluentform.php +0 -58
  54. integrations/fluentform/js/fluentform.js +0 -4
  55. integrations/formidable/formidable.php +0 -74
  56. integrations/registrations/js/registrations.js +0 -4
  57. integrations/registrations/registrations.php +0 -200
  58. integrations/wpforms/js/wpforms.js +0 -4
  59. integrations/wpforms/wpforms.php +0 -54
  60. languages/zero-spam-fr_FR.pot +0 -685
  61. languages/zero-spam-it_IT.pot +0 -875
  62. modules/class-botscout.php +139 -0
  63. modules/class-google.php +115 -0
  64. modules/class-ipstack.php +194 -0
  65. modules/class-stopforumspam.php +344 -0
  66. modules/class-zerospam.php +40 -0
  67. modules/comments/class-comments.php +163 -0
  68. modules/contactform7/class-contactform7.php +152 -0
  69. modules/registration/class-registration.php +159 -0
  70. readme.txt +20 -310
  71. templates/callout.php +0 -51
  72. templates/countries-pie-chart.php +0 -70
  73. templates/ip-list.php +0 -74
  74. templates/map.php +0 -114
  75. templates/regions-pie-chart.php +0 -85
  76. templates/spam-line-chart.php +0 -61
  77. uninstall.php +44 -0
  78. wordpress-zero-spam.php +50 -109
assets/css/Chart.min.css DELETED
@@ -1 +0,0 @@
1
- @keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}
 
assets/css/admin-blocked-ips.css DELETED
@@ -1,56 +0,0 @@
1
- .wpzerospam-add-ip-container {
2
- align-items: center;
3
- display: flex;
4
- flex-wrap: wrap;
5
- }
6
-
7
- .wpzerospam-add-ip-container-highlight {
8
- animation: wpzerospam-highlight 2s;
9
- animation-fill-mode: forwards;
10
- }
11
-
12
- @keyframes wpzerospam-highlight {
13
- 0% {
14
- background: #fff;
15
- }
16
-
17
- 100% {
18
- background: #FFF4DC;
19
- }
20
- }
21
-
22
-
23
- .wpzerospam-add-ip-container h2,
24
- .wpzerospam-add-ip-field,
25
- .wpzerospam-add-ip-submit {
26
- margin-left: 10px;
27
- margin-right: 10px;
28
- }
29
-
30
- .wpzerospam-add-ip-container h2 {
31
- margin-top: 0;
32
- width: calc(100% - 20px);
33
- }
34
-
35
- .wpzerospam-add-ip-container label {
36
- display: block;
37
- font-weight: bold;
38
- }
39
-
40
- .wpzerospam-add-ip-field {
41
- width: calc(100% / 5 - 20px);
42
- }
43
-
44
- .wpzerospam-add-ip-field input,
45
- .wpzerospam-add-ip-field select {
46
- width: 100%;
47
- }
48
-
49
- #wpzerospam-add-ip-field-reason {
50
- flex-grow: 1;
51
- }
52
-
53
- #wpzerospam-add-ip-field-submit {
54
- margin-top: 10px;
55
- width: auto;
56
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/admin-dashboard.css DELETED
@@ -1,144 +0,0 @@
1
- .wpzerospam-blocked-ip-option {
2
- margin-bottom: 0.3rem;
3
- }
4
-
5
- .wpzerospam-setting-header,
6
- .wpzerospam-blocked-ip-option {
7
- display: flex;
8
- }
9
-
10
- .wpzerospam-setting-header div,
11
- .wpzerospam-input {
12
- width: calc(100% / 3);
13
- }
14
-
15
- .wpzerospam-setting-header div {
16
- margin-bottom: 0.3rem;
17
- }
18
-
19
- .wpzerospam-setting-header label {
20
- font-weight: bold;
21
- }
22
-
23
- .wpzerospam-setting-header small {
24
- color: #666;
25
- font-style: italic;
26
- display: block;
27
- }
28
-
29
-
30
- /**
31
- * Info boxes
32
- */
33
- .wpzerospam-boxes {
34
- display: flex;
35
- flex-wrap: wrap;
36
- margin-left: -8px;
37
- margin-right: -8px;
38
- }
39
-
40
- .wpzerospam-box {
41
- background: #fff;
42
- border: 1px solid #ccd0d4;
43
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
44
- margin-bottom: 16px;
45
- margin-left: 8px;
46
- margin-right: 8px;
47
- overflow: hidden;
48
- width: calc(100% - 18px);
49
- }
50
-
51
- .wpzerospam-box h3 {
52
- border-bottom: 1px solid #ccd0d4;
53
- font-size: 14px;
54
- line-height: 1.4;
55
- margin: 0;
56
- }
57
-
58
- .wpzerospam-box .inside,
59
- .wpzerospam-box h3 {
60
- padding: 12px;
61
- }
62
-
63
- @media (min-width: 1024px) {
64
- .wpzerospam-box {
65
- width: calc(100% / 2 - 18px);
66
- }
67
-
68
- .wpzerospam-box-map {
69
- width: calc(100% - 18px);
70
- }
71
- }
72
-
73
- @media (min-width: 1200px) {
74
- .wpzerospam-box {
75
- width: calc(100% / 3 - 18px);
76
- }
77
-
78
- .wpzerospam-box-line-chart {
79
- width: calc(100% - 18px);
80
- }
81
-
82
- .wpzerospam-box-map {
83
- width: calc((100% / 3) * 2 - 18px);
84
- }
85
-
86
- .wpzerospam-box-countries-pie {
87
- width: calc(100% / 2 - 18px);
88
- }
89
- }
90
-
91
-
92
-
93
- /**
94
- * List of IPs
95
- */
96
- .wpzerospam-list {
97
- margin: 0;
98
- padding: 0;
99
- list-style: none;
100
- }
101
-
102
- .wpzerospam-list-item {
103
- display: flex;
104
- }
105
-
106
- .wpzerospam-list-cell-icon,
107
- .wpzerospam-list-cell-ip,
108
- .wpzerospam-list-cell-count,
109
- .wpzerospam-list-cell-action {
110
- flex-grow: 0;
111
- flex-shrink: 0;
112
- }
113
-
114
- .wpzerospam-list-cell-icon {
115
- margin-right: 5px;
116
- width: 16px;
117
- }
118
-
119
- .wpzerospam-list-cell-ip {
120
- width: 120px;
121
- }
122
-
123
- .wpzerospam-list-cell-country {
124
- flex-grow: 1;
125
- font-size: 0.8rem;
126
- }
127
-
128
- .wpzerospam-list-cell-count,
129
- .wpzerospam-list-cell-action {
130
- text-align: right;
131
- }
132
-
133
- .wpzerospam-list-cell-count {
134
- width: 50px;
135
- }
136
-
137
- .wpzerospam-list-cell-action {
138
- margin-left: 5px;
139
- width: 80px;
140
- }
141
-
142
- .wpzerospam-list-cell-na {
143
- color: #ccd0d4;
144
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/admin-tables.css DELETED
@@ -1,62 +0,0 @@
1
- .wpzerospam-details-modal {
2
- background: rgba(0, 0, 0, 0.8);
3
- cursor: pointer;
4
- display: none;
5
- height: 100%;
6
- left: 0;
7
- position: fixed;
8
- top: 0;
9
- width: 100%;
10
- z-index: 9;
11
- }
12
-
13
- .wpzerospam-details-modal.is-active {
14
- display: block;
15
- }
16
-
17
- .wpzerospam-details-modal-inner {
18
- background-color: #fff;
19
- border: 1px solid #ccd0d4;
20
- border-radius: 5px;
21
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
22
- cursor: default;
23
- display: flex;
24
- flex-wrap: wrap;
25
- left: 50%;
26
- overflow: auto;
27
- max-height: 500px;
28
- max-width: 800px;
29
- padding: 30px;
30
- position: fixed;
31
- top: 50%;
32
- transform: translate(-50%, -50%);
33
- z-index: 99;
34
- width: 95%
35
- }
36
-
37
- .wpzerospam-details-item {
38
- display: flex;
39
- padding: 5px;
40
- width: 100%;
41
- }
42
-
43
- .wpzerospam-details-item-unknown {
44
- order: 9999999;
45
- }
46
-
47
- .wpzerospam-details-label {
48
- flex-shrink: 0;
49
- font-weight: bold;
50
- width: 150px;
51
- }
52
-
53
- .wpzerospam-details-data {
54
- flex-grow: 1;
55
- }
56
-
57
- .wpzerospam-small {
58
- color: #666;
59
- display: inline-block;
60
- font-size: 11px;
61
- line-height: 1;
62
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/admin.css CHANGED
@@ -1,89 +1,160 @@
1
- .wpzerospam-callout {
2
- align-items: center;
3
- background: #fff;
4
- border: 1px solid #ccd0d4;
5
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
6
- display: flex;
7
- flex-wrap: wrap;
8
- margin-bottom: 10px;
9
- padding: 20px;
10
  }
11
 
12
- .wpzerospam-callout h2 {
13
- font-size: 18px;
14
- margin-bottom: 10px;
15
  }
16
 
17
- .wpzerospam-callout p {
18
- font-size: 14px;
19
- margin-top: 0;
20
  }
21
 
22
- .wpzerospam-callout-actions {
23
- display: flex;
24
- flex-wrap: wrap;
25
- width: 100%;
26
  }
27
 
28
- .wpzerospam-callout-actions a.button {
29
- display: block;
30
- margin-bottom: 4px;
31
- margin-left: 2px;
32
- margin-right: 2px;
33
- text-align: center;
34
- width: calc(100% - 4px);
35
  }
36
 
37
- .wpzerospam-callout-actions a:last-child {
38
- margin-bottom: 0;
39
  }
40
 
41
- @media (min-width: 960px) {
42
- .wpzerospam-callout {
43
- flex-wrap: nowrap;
44
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- .wpzerospam-callout-actions {
47
- display: flex;
48
- flex-wrap: wrap;
49
- margin-left: 50px;
50
- width: auto;
51
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- .wpzerospam-callout-actions a.button {
54
- width: calc(50% - 4px);
55
- }
56
  }
57
 
58
- .wpzerospam-country-flag {
59
- vertical-align: text-bottom;
 
60
  }
61
 
62
- .wpzerospam-blocked::before,
63
- .wpzerospam-warning::before {
64
- background-position: center;
65
- background-repeat: no-repeat;
66
- background-size: contain;
67
- content: '';
68
- display: inline-block;
69
- height: 16px;
70
- margin-right: 3px;
71
- vertical-align: top;
72
- width: 16px;
73
  }
74
 
75
- .wpzerospam-blocked {
76
- color: #ca4a1f;
 
 
 
77
  }
78
 
79
- .wpzerospam-blocked::before {
80
- background-image: url('../img/icon.svg');
 
 
81
  }
82
 
83
- .wpzerospam-warning {
84
- color: #fcb214;
85
  }
86
 
87
- .wpzerospam-warning::before {
88
- background-image: url('../img/icon-yellow.svg');
 
 
89
  }
1
+ .zerospam-callout,
2
+ .zerospam-modal {
3
+ background: #fff;
4
+ border: 1px solid #ccd0d4;
5
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
 
 
 
 
6
  }
7
 
8
+ .zerospam-callout-col,
9
+ .zerospam-modal {
10
+ padding: 30px;
11
  }
12
 
13
+ .zerospam-callout {
14
+ display: flex;
15
+ margin: 20px 0;
16
  }
17
 
18
+ .zerospam-callout h2,
19
+ .zerospam-callout h3 {
20
+ margin-top: 0;
 
21
  }
22
 
23
+ .zerospam-callout p:last-child {
24
+ margin-bottom: 0;
 
 
 
 
 
25
  }
26
 
27
+ .zerospam-callout-col {
28
+ width: 100%;
29
  }
30
 
31
+ .zerospam-modal {
32
+ left: 50%;
33
+ max-width: 600px;
34
+ opacity: 0;
35
+ position: fixed;
36
+ max-height: calc(90vh - 60px);
37
+ overflow: auto;
38
+ top: 50%;
39
+ transform: translate(-50%, -50%);
40
+ transition: all 0.5s;
41
+ visibility: hidden;
42
+ width: 100%;
43
+ z-index: 1;
44
+ }
45
+
46
+ .zerospam-modal.is-active {
47
+ opacity: 1;
48
+ visibility: visible;
49
+ }
50
+
51
+ .zerospam-close-modal {
52
+ background: transparent;
53
+ border: 0;
54
+ cursor: pointer;
55
+ display: block;
56
+ height: 16px;
57
+ padding: 0;
58
+ position: absolute;
59
+ right: 15px;
60
+ top: 15px;
61
+ width: 15px;
62
+ }
63
+
64
+ .zerospam-close-modal::before,
65
+ .zerospam-close-modal::after {
66
+ background: #23282d;
67
+ content: '';
68
+ display: block;
69
+ height: 16px;
70
+ left: 6px;
71
+ position: absolute;
72
+ top: 0;
73
+ width: 3px;
74
+ }
75
+
76
+ .zerospam-close-modal::before {
77
+ transform: rotate(45deg);
78
+ }
79
+
80
+ .zerospam-close-modal::after {
81
+ transform: rotate(-45deg);
82
+ }
83
+
84
+ .zerospam-modal-details {
85
+ align-items: center;
86
+ display: flex;;
87
+ flex-wrap: wrap;
88
+ }
89
 
90
+ .zerospam-modal-title,
91
+ .zerospam-modal-subtitle {
92
+ width: 50%;
93
+ }
94
+
95
+ .zerospam-modal-headline,
96
+ .zerospam-modal-list {
97
+ width: 100%;
98
+ }
99
+
100
+ .zerospam-modal-list li {
101
+ display: flex;
102
+ justify-content: space-between;
103
+ padding: 5px;
104
+ }
105
+
106
+ .zerospam-modal-list li strong,
107
+ .zerospam-modal-list li span {
108
+ width: 50%;
109
+ }
110
+
111
+ .zerospam-modal-list li:nth-child(odd) {
112
+ background: #f9f9f9;
113
+ }
114
+
115
+ .zerospam-modal-subtitle {
116
+ text-align: right;
117
+ }
118
+
119
+ .zerospam-tab {
120
+ display: none;
121
+ }
122
 
123
+ .zerospam-tab.is-active {
124
+ display: block;
 
125
  }
126
 
127
+ .zerospam-modal form {
128
+ display: flex;
129
+ flex-wrap: wrap;
130
  }
131
 
132
+ .zerospam-modal form label {
133
+ display: flex;
134
+ flex-wrap: wrap;
135
+ margin-bottom: 10px;
136
+ width: 100%;
 
 
 
 
 
 
137
  }
138
 
139
+ .zerospam-modal form label[for="blocked-ip"],
140
+ .zerospam-modal form label[for="blocked-type"],
141
+ .zerospam-modal form label[for="blocked-start-date"],
142
+ .zerospam-modal form label[for="blocked-end-date"] {
143
+ width: 50%;
144
  }
145
 
146
+ .zerospam-modal input[type="text"],
147
+ .zerospam-modal input[type="datetime-local"],
148
+ .zerospam-modal select {
149
+ width: 100%;
150
  }
151
 
152
+ .zerospam-modal input[type="submit"] {
153
+ margin-top: 16px;
154
  }
155
 
156
+ @media (min-width: 768px) {
157
+ .zerospam-callout-col {
158
+ width: 50%;
159
+ }
160
  }
assets/css/debug.css DELETED
@@ -1,25 +0,0 @@
1
- .wpzerospam-debug-overlay {
2
- background: rgba(255, 255, 255, 0.9);
3
- bottom: 0;
4
- font-size: 13px;
5
- left: 0;
6
- max-height: 95%;
7
- max-width: 800px;
8
- overflow: auto;
9
- padding: 2em;
10
- position: fixed;
11
- width: 100%;
12
- }
13
-
14
- .wpzerospam-debug-item {
15
- display: flex;
16
- }
17
-
18
- .wpzerospam-debug-item-label {
19
- flex-shrink: 0;
20
- width: 16em;
21
- }
22
-
23
- .wpzerospam-debug-item-value {
24
- flex-grow: 1;
25
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/jquery-jvectormap-2.0.5.css DELETED
@@ -1,135 +0,0 @@
1
- svg {
2
- touch-action: none;
3
- }
4
-
5
- .jvectormap-container {
6
- width: 100%;
7
- height: 100%;
8
- position: relative;
9
- overflow: hidden;
10
- touch-action: none;
11
- }
12
-
13
- .jvectormap-tip {
14
- position: absolute;
15
- display: none;
16
- border: solid 1px #CDCDCD;
17
- border-radius: 3px;
18
- background: #292929;
19
- color: white;
20
- font-family: sans-serif, Verdana;
21
- font-size: smaller;
22
- padding: 3px;
23
- }
24
-
25
- .jvectormap-zoomin, .jvectormap-zoomout, .jvectormap-goback {
26
- position: absolute;
27
- left: 10px;
28
- border-radius: 3px;
29
- background: #292929;
30
- padding: 3px;
31
- color: white;
32
- cursor: pointer;
33
- line-height: 10px;
34
- text-align: center;
35
- box-sizing: content-box;
36
- }
37
-
38
- .jvectormap-zoomin, .jvectormap-zoomout {
39
- width: 10px;
40
- height: 10px;
41
- }
42
-
43
- .jvectormap-zoomin {
44
- top: 10px;
45
- }
46
-
47
- .jvectormap-zoomout {
48
- top: 30px;
49
- }
50
-
51
- .jvectormap-goback {
52
- bottom: 10px;
53
- z-index: 1000;
54
- padding: 6px;
55
- }
56
-
57
- .jvectormap-spinner {
58
- position: absolute;
59
- left: 0;
60
- top: 0;
61
- right: 0;
62
- bottom: 0;
63
- background: center no-repeat url(data:image/gif;base64,R0lGODlhIAAgAPMAAP///wAAAMbGxoSEhLa2tpqamjY2NlZWVtjY2OTk5Ly8vB4eHgQEBAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ/V/nmOM82XiHRLYKhKP1oZmADdEAAAh+QQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY/CZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB+A4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6+Ho7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq+B6QDtuetcaBPnW6+O7wDHpIiK9SaVK5GgV543tzjgGcghAgAh+QQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK++G+w48edZPK+M6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE+G+cD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm+FNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk+aV+oJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0/VNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc+XiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30/iI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE/jiuL04RGEBgwWhShRgQExHBAAh+QQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR+ipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq+E71SRQeyqUToLA7VxF0JDyIQh/MVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY+Yip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd+MFCN6HAAIKgNggY0KtEBAAh+QQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1+vsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d+jYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg+ygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0+bm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h+Kr0SJ8MFihpNbx+4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX+BP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA==);
64
- }
65
-
66
- .jvectormap-legend-title {
67
- font-weight: bold;
68
- font-size: 14px;
69
- text-align: center;
70
- }
71
-
72
- .jvectormap-legend-cnt {
73
- position: absolute;
74
- }
75
-
76
- .jvectormap-legend-cnt-h {
77
- bottom: 0;
78
- right: 0;
79
- }
80
-
81
- .jvectormap-legend-cnt-v {
82
- top: 0;
83
- right: 0;
84
- }
85
-
86
- .jvectormap-legend {
87
- background: black;
88
- color: white;
89
- border-radius: 3px;
90
- }
91
-
92
- .jvectormap-legend-cnt-h .jvectormap-legend {
93
- float: left;
94
- margin: 0 10px 10px 0;
95
- padding: 3px 3px 1px 3px;
96
- }
97
-
98
- .jvectormap-legend-cnt-h .jvectormap-legend .jvectormap-legend-tick {
99
- float: left;
100
- }
101
-
102
- .jvectormap-legend-cnt-v .jvectormap-legend {
103
- margin: 10px 10px 0 0;
104
- padding: 3px;
105
- }
106
-
107
- .jvectormap-legend-cnt-h .jvectormap-legend-tick {
108
- width: 40px;
109
- }
110
-
111
- .jvectormap-legend-cnt-h .jvectormap-legend-tick-sample {
112
- height: 15px;
113
- }
114
-
115
- .jvectormap-legend-cnt-v .jvectormap-legend-tick-sample {
116
- height: 20px;
117
- width: 20px;
118
- display: inline-block;
119
- vertical-align: middle;
120
- }
121
-
122
- .jvectormap-legend-tick-text {
123
- font-size: 12px;
124
- }
125
-
126
- .jvectormap-legend-cnt-h .jvectormap-legend-tick-text {
127
- text-align: center;
128
- }
129
-
130
- .jvectormap-legend-cnt-v .jvectormap-legend-tick-text {
131
- display: inline-block;
132
- vertical-align: middle;
133
- line-height: 20px;
134
- padding-left: 3px;
135
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/img/icon-yellow.svg DELETED
@@ -1,25 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="512px" height="478px" viewBox="0 0 512 478" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
- <!-- Generator: Sketch 58 (84663) - https://sketch.com -->
4
- <title>icon</title>
5
- <desc>Created with Sketch.</desc>
6
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
7
- <g id="icon" fill-rule="nonzero">
8
- <g id="under-construction" transform="translate(256.000000, 239.000000) scale(-1, 1) translate(-256.000000, -239.000000) ">
9
- <path d="M241,0 L96,0 C87.716,0 81,6.716 81,15 L81,127 C81,135.284 87.716,142 96,142 L241,142 C249.284,142 256,135.284 256,127 L256,15 C256,6.716 249.284,0 241,0 Z" id="Path" fill="#FCB214"></path>
10
- <path d="M176,112 L15,112 C6.716,112 0,118.716 0,127 L0,239 C0,247.284 6.716,254 15,254 L176,254 C184.284,254 191,247.284 191,239 L191,127 C191,118.716 184.284,112 176,112 Z" id="Path" fill="#FFD06C"></path>
11
- <path d="M351,127 L351,239 C351,247.28 344.28,254 336,254 L176,254 L176,112 L336,112 C344.28,112 351,118.72 351,127 Z" id="Path" fill="#D5A02C"></path>
12
- <path d="M351,127 L351,239 C351,247.28 344.28,254 336,254 L256,254 L256,112 L336,112 C344.28,112 351,118.72 351,127 Z" id="Path" fill="#FFC64B"></path>
13
- <path d="M512,127 L512,239 C512,247.28 505.28,254 497,254 L336,254 L336,112 L497,112 C505.28,112 512,118.72 512,127 Z" id="Path" fill="#895D00"></path>
14
- <path d="M111,239 L111,351 C111,359.28 104.28,366 96,366 L15,366 C6.72,366 0,359.28 0,351 L0,239 L111,239 Z" id="Path" fill="#FFC64B"></path>
15
- <path d="M271,239 L271,351 C271,359.28 264.28,366 256,366 L96,366 L96,239 L271,239 Z" id="Path" fill="#FCB214"></path>
16
- <path d="M431,239 L431,351 C431,359.28 424.28,366 416,366 L256,366 L256,239 L431,239 Z" id="Path" fill="#C08200"></path>
17
- <path d="M512,239 L512,351 C512,359.28 505.28,366 497,366 L416,366 L416,239 L512,239 Z" id="Path" fill="#FFC64B"></path>
18
- <path d="M191,351 L191,463 C191,471.28 184.28,478 176,478 L15,478 C6.72,478 0,471.28 0,463 L0,351 L191,351 Z" id="Path" fill="#FFDA8C"></path>
19
- <path d="M351,351 L351,463 C351,471.28 344.28,478 336,478 L176,478 L176,351 L351,351 Z" id="Path" fill="#D5A02C"></path>
20
- <path d="M351,351 L351,463 C351,471.28 344.28,478 336,478 L256,478 L256,351 L351,351 Z" id="Path" fill="#FFC64B"></path>
21
- <path d="M512,351 L512,463 C512,471.28 505.28,478 497,478 L336,478 L336,351 L512,351 Z" id="Path" fill="#895D00"></path>
22
- </g>
23
- </g>
24
- </g>
25
- </svg>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/img/icon.svg DELETED
@@ -1,23 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <svg width="512px" height="478px" viewBox="0 0 512 478" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3
- <!-- Generator: Sketch 58 (84663) - https://sketch.com -->
4
- <title>under-construction</title>
5
- <desc>Created with Sketch.</desc>
6
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
7
- <g id="under-construction" transform="translate(256.000000, 239.000000) scale(-1, 1) translate(-256.000000, -239.000000) " fill-rule="nonzero">
8
- <path d="M241,0 L96,0 C87.716,0 81,6.716 81,15 L81,127 C81,135.284 87.716,142 96,142 L241,142 C249.284,142 256,135.284 256,127 L256,15 C256,6.716 249.284,0 241,0 Z" id="Path" fill="#BE0000"></path>
9
- <path d="M176,112 L15,112 C6.716,112 0,118.716 0,127 L0,239 C0,247.284 6.716,254 15,254 L176,254 C184.284,254 191,247.284 191,239 L191,127 C191,118.716 184.284,112 176,112 Z" id="Path" fill="#FF7038"></path>
10
- <path d="M351,127 L351,239 C351,247.28 344.28,254 336,254 L176,254 L176,112 L336,112 C344.28,112 351,118.72 351,127 Z" id="Path" fill="#FF2929"></path>
11
- <path d="M351,127 L351,239 C351,247.28 344.28,254 336,254 L256,254 L256,112 L336,112 C344.28,112 351,118.72 351,127 Z" id="Path" fill="#BE0000"></path>
12
- <path d="M512,127 L512,239 C512,247.28 505.28,254 497,254 L336,254 L336,112 L497,112 C505.28,112 512,118.72 512,127 Z" id="Path" fill="#63000D"></path>
13
- <path d="M111,239 L111,351 C111,359.28 104.28,366 96,366 L15,366 C6.72,366 0,359.28 0,351 L0,239 L111,239 Z" id="Path" fill="#FF2929"></path>
14
- <path d="M271,239 L271,351 C271,359.28 264.28,366 256,366 L96,366 L96,239 L271,239 Z" id="Path" fill="#BE0000"></path>
15
- <path d="M431,239 L431,351 C431,359.28 424.28,366 416,366 L256,366 L256,239 L431,239 Z" id="Path" fill="#840012"></path>
16
- <path d="M512,239 L512,351 C512,359.28 505.28,366 497,366 L416,366 L416,239 L512,239 Z" id="Path" fill="#BE0000"></path>
17
- <path d="M191,351 L191,463 C191,471.28 184.28,478 176,478 L15,478 C6.72,478 0,471.28 0,463 L0,351 L191,351 Z" id="Path" fill="#FFA17C"></path>
18
- <path d="M351,351 L351,463 C351,471.28 344.28,478 336,478 L176,478 L176,351 L351,351 Z" id="Path" fill="#FF2929"></path>
19
- <path d="M351,351 L351,463 C351,471.28 344.28,478 336,478 L256,478 L256,351 L351,351 Z" id="Path" fill="#BE0000"></path>
20
- <path d="M512,351 L512,463 C512,471.28 505.28,478 497,478 L336,478 L336,351 L512,351 Z" id="Path" fill="#63000D"></path>
21
- </g>
22
- </g>
23
- </svg>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/Chart.bundle.min.js DELETED
@@ -1,7 +0,0 @@
1
- /*!
2
- * Chart.js v2.9.3
3
- * https://www.chartjs.org
4
- * (c) 2019 Chart.js Contributors
5
- * Released under the MIT License
6
- */
7
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).Chart=e()}(this,(function(){"use strict";"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self&&self;function t(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function e(t,e){return t(e={exports:{}},e.exports),e.exports}var n={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},i=e((function(t){var e={};for(var i in n)n.hasOwnProperty(i)&&(e[n[i]]=i);var a=t.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var r in a)if(a.hasOwnProperty(r)){if(!("channels"in a[r]))throw new Error("missing channels property: "+r);if(!("labels"in a[r]))throw new Error("missing channel labels property: "+r);if(a[r].labels.length!==a[r].channels)throw new Error("channel and label counts mismatch: "+r);var o=a[r].channels,s=a[r].labels;delete a[r].channels,delete a[r].labels,Object.defineProperty(a[r],"channels",{value:o}),Object.defineProperty(a[r],"labels",{value:s})}a.rgb.hsl=function(t){var e,n,i=t[0]/255,a=t[1]/255,r=t[2]/255,o=Math.min(i,a,r),s=Math.max(i,a,r),l=s-o;return s===o?e=0:i===s?e=(a-r)/l:a===s?e=2+(r-i)/l:r===s&&(e=4+(i-a)/l),(e=Math.min(60*e,360))<0&&(e+=360),n=(o+s)/2,[e,100*(s===o?0:n<=.5?l/(s+o):l/(2-s-o)),100*n]},a.rgb.hsv=function(t){var e,n,i,a,r,o=t[0]/255,s=t[1]/255,l=t[2]/255,u=Math.max(o,s,l),d=u-Math.min(o,s,l),h=function(t){return(u-t)/6/d+.5};return 0===d?a=r=0:(r=d/u,e=h(o),n=h(s),i=h(l),o===u?a=i-n:s===u?a=1/3+e-i:l===u&&(a=2/3+n-e),a<0?a+=1:a>1&&(a-=1)),[360*a,100*r,100*u]},a.rgb.hwb=function(t){var e=t[0],n=t[1],i=t[2];return[a.rgb.hsl(t)[0],100*(1/255*Math.min(e,Math.min(n,i))),100*(i=1-1/255*Math.max(e,Math.max(n,i)))]},a.rgb.cmyk=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255;return[100*((1-n-(e=Math.min(1-n,1-i,1-a)))/(1-e)||0),100*((1-i-e)/(1-e)||0),100*((1-a-e)/(1-e)||0),100*e]},a.rgb.keyword=function(t){var i=e[t];if(i)return i;var a,r,o,s=1/0;for(var l in n)if(n.hasOwnProperty(l)){var u=n[l],d=(r=t,o=u,Math.pow(r[0]-o[0],2)+Math.pow(r[1]-o[1],2)+Math.pow(r[2]-o[2],2));d<s&&(s=d,a=l)}return a},a.keyword.rgb=function(t){return n[t]},a.rgb.xyz=function(t){var e=t[0]/255,n=t[1]/255,i=t[2]/255;return[100*(.4124*(e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92)+.3576*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)+.1805*(i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92)),100*(.2126*e+.7152*n+.0722*i),100*(.0193*e+.1192*n+.9505*i)]},a.rgb.lab=function(t){var e=a.rgb.xyz(t),n=e[0],i=e[1],r=e[2];return i/=100,r/=108.883,n=(n/=95.047)>.008856?Math.pow(n,1/3):7.787*n+16/116,[116*(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116)-16,500*(n-i),200*(i-(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116))]},a.hsl.rgb=function(t){var e,n,i,a,r,o=t[0]/360,s=t[1]/100,l=t[2]/100;if(0===s)return[r=255*l,r,r];e=2*l-(n=l<.5?l*(1+s):l+s-l*s),a=[0,0,0];for(var u=0;u<3;u++)(i=o+1/3*-(u-1))<0&&i++,i>1&&i--,r=6*i<1?e+6*(n-e)*i:2*i<1?n:3*i<2?e+(n-e)*(2/3-i)*6:e,a[u]=255*r;return a},a.hsl.hsv=function(t){var e=t[0],n=t[1]/100,i=t[2]/100,a=n,r=Math.max(i,.01);return n*=(i*=2)<=1?i:2-i,a*=r<=1?r:2-r,[e,100*(0===i?2*a/(r+a):2*n/(i+n)),100*((i+n)/2)]},a.hsv.rgb=function(t){var e=t[0]/60,n=t[1]/100,i=t[2]/100,a=Math.floor(e)%6,r=e-Math.floor(e),o=255*i*(1-n),s=255*i*(1-n*r),l=255*i*(1-n*(1-r));switch(i*=255,a){case 0:return[i,l,o];case 1:return[s,i,o];case 2:return[o,i,l];case 3:return[o,s,i];case 4:return[l,o,i];case 5:return[i,o,s]}},a.hsv.hsl=function(t){var e,n,i,a=t[0],r=t[1]/100,o=t[2]/100,s=Math.max(o,.01);return i=(2-r)*o,n=r*s,[a,100*(n=(n/=(e=(2-r)*s)<=1?e:2-e)||0),100*(i/=2)]},a.hwb.rgb=function(t){var e,n,i,a,r,o,s,l=t[0]/360,u=t[1]/100,d=t[2]/100,h=u+d;switch(h>1&&(u/=h,d/=h),i=6*l-(e=Math.floor(6*l)),0!=(1&e)&&(i=1-i),a=u+i*((n=1-d)-u),e){default:case 6:case 0:r=n,o=a,s=u;break;case 1:r=a,o=n,s=u;break;case 2:r=u,o=n,s=a;break;case 3:r=u,o=a,s=n;break;case 4:r=a,o=u,s=n;break;case 5:r=n,o=u,s=a}return[255*r,255*o,255*s]},a.cmyk.rgb=function(t){var e=t[0]/100,n=t[1]/100,i=t[2]/100,a=t[3]/100;return[255*(1-Math.min(1,e*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a)),255*(1-Math.min(1,i*(1-a)+a))]},a.xyz.rgb=function(t){var e,n,i,a=t[0]/100,r=t[1]/100,o=t[2]/100;return n=-.9689*a+1.8758*r+.0415*o,i=.0557*a+-.204*r+1.057*o,e=(e=3.2406*a+-1.5372*r+-.4986*o)>.0031308?1.055*Math.pow(e,1/2.4)-.055:12.92*e,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,i=i>.0031308?1.055*Math.pow(i,1/2.4)-.055:12.92*i,[255*(e=Math.min(Math.max(0,e),1)),255*(n=Math.min(Math.max(0,n),1)),255*(i=Math.min(Math.max(0,i),1))]},a.xyz.lab=function(t){var e=t[0],n=t[1],i=t[2];return n/=100,i/=108.883,e=(e/=95.047)>.008856?Math.pow(e,1/3):7.787*e+16/116,[116*(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116)-16,500*(e-n),200*(n-(i=i>.008856?Math.pow(i,1/3):7.787*i+16/116))]},a.lab.xyz=function(t){var e,n,i,a=t[0];e=t[1]/500+(n=(a+16)/116),i=n-t[2]/200;var r=Math.pow(n,3),o=Math.pow(e,3),s=Math.pow(i,3);return n=r>.008856?r:(n-16/116)/7.787,e=o>.008856?o:(e-16/116)/7.787,i=s>.008856?s:(i-16/116)/7.787,[e*=95.047,n*=100,i*=108.883]},a.lab.lch=function(t){var e,n=t[0],i=t[1],a=t[2];return(e=360*Math.atan2(a,i)/2/Math.PI)<0&&(e+=360),[n,Math.sqrt(i*i+a*a),e]},a.lch.lab=function(t){var e,n=t[0],i=t[1];return e=t[2]/360*2*Math.PI,[n,i*Math.cos(e),i*Math.sin(e)]},a.rgb.ansi16=function(t){var e=t[0],n=t[1],i=t[2],r=1 in arguments?arguments[1]:a.rgb.hsv(t)[2];if(0===(r=Math.round(r/50)))return 30;var o=30+(Math.round(i/255)<<2|Math.round(n/255)<<1|Math.round(e/255));return 2===r&&(o+=60),o},a.hsv.ansi16=function(t){return a.rgb.ansi16(a.hsv.rgb(t),t[2])},a.rgb.ansi256=function(t){var e=t[0],n=t[1],i=t[2];return e===n&&n===i?e<8?16:e>248?231:Math.round((e-8)/247*24)+232:16+36*Math.round(e/255*5)+6*Math.round(n/255*5)+Math.round(i/255*5)},a.ansi16.rgb=function(t){var e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),[e=e/10.5*255,e,e];var n=.5*(1+~~(t>50));return[(1&e)*n*255,(e>>1&1)*n*255,(e>>2&1)*n*255]},a.ansi256.rgb=function(t){if(t>=232){var e=10*(t-232)+8;return[e,e,e]}var n;return t-=16,[Math.floor(t/36)/5*255,Math.floor((n=t%36)/6)/5*255,n%6/5*255]},a.rgb.hex=function(t){var e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},a.hex.rgb=function(t){var e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];var n=e[0];3===e[0].length&&(n=n.split("").map((function(t){return t+t})).join(""));var i=parseInt(n,16);return[i>>16&255,i>>8&255,255&i]},a.rgb.hcg=function(t){var e,n=t[0]/255,i=t[1]/255,a=t[2]/255,r=Math.max(Math.max(n,i),a),o=Math.min(Math.min(n,i),a),s=r-o;return e=s<=0?0:r===n?(i-a)/s%6:r===i?2+(a-n)/s:4+(n-i)/s+4,e/=6,[360*(e%=1),100*s,100*(s<1?o/(1-s):0)]},a.hsl.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=1,a=0;return(i=n<.5?2*e*n:2*e*(1-n))<1&&(a=(n-.5*i)/(1-i)),[t[0],100*i,100*a]},a.hsv.hcg=function(t){var e=t[1]/100,n=t[2]/100,i=e*n,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.hcg.rgb=function(t){var e=t[0]/360,n=t[1]/100,i=t[2]/100;if(0===n)return[255*i,255*i,255*i];var a,r=[0,0,0],o=e%1*6,s=o%1,l=1-s;switch(Math.floor(o)){case 0:r[0]=1,r[1]=s,r[2]=0;break;case 1:r[0]=l,r[1]=1,r[2]=0;break;case 2:r[0]=0,r[1]=1,r[2]=s;break;case 3:r[0]=0,r[1]=l,r[2]=1;break;case 4:r[0]=s,r[1]=0,r[2]=1;break;default:r[0]=1,r[1]=0,r[2]=l}return a=(1-n)*i,[255*(n*r[0]+a),255*(n*r[1]+a),255*(n*r[2]+a)]},a.hcg.hsv=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e),i=0;return n>0&&(i=e/n),[t[0],100*i,100*n]},a.hcg.hsl=function(t){var e=t[1]/100,n=t[2]/100*(1-e)+.5*e,i=0;return n>0&&n<.5?i=e/(2*n):n>=.5&&n<1&&(i=e/(2*(1-n))),[t[0],100*i,100*n]},a.hcg.hwb=function(t){var e=t[1]/100,n=e+t[2]/100*(1-e);return[t[0],100*(n-e),100*(1-n)]},a.hwb.hcg=function(t){var e=t[1]/100,n=1-t[2]/100,i=n-e,a=0;return i<1&&(a=(n-i)/(1-i)),[t[0],100*i,100*a]},a.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},a.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},a.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},a.gray.hsl=a.gray.hsv=function(t){return[0,0,t[0]]},a.gray.hwb=function(t){return[0,100,t[0]]},a.gray.cmyk=function(t){return[0,0,0,t[0]]},a.gray.lab=function(t){return[t[0],0,0]},a.gray.hex=function(t){var e=255&Math.round(t[0]/100*255),n=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(n.length)+n},a.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]}}));i.rgb,i.hsl,i.hsv,i.hwb,i.cmyk,i.xyz,i.lab,i.lch,i.hex,i.keyword,i.ansi16,i.ansi256,i.hcg,i.apple,i.gray;function a(t){var e=function(){for(var t={},e=Object.keys(i),n=e.length,a=0;a<n;a++)t[e[a]]={distance:-1,parent:null};return t}(),n=[t];for(e[t].distance=0;n.length;)for(var a=n.pop(),r=Object.keys(i[a]),o=r.length,s=0;s<o;s++){var l=r[s],u=e[l];-1===u.distance&&(u.distance=e[a].distance+1,u.parent=a,n.unshift(l))}return e}function r(t,e){return function(n){return e(t(n))}}function o(t,e){for(var n=[e[t].parent,t],a=i[e[t].parent][t],o=e[t].parent;e[o].parent;)n.unshift(e[o].parent),a=r(i[e[o].parent][o],a),o=e[o].parent;return a.conversion=n,a}var s={};Object.keys(i).forEach((function(t){s[t]={},Object.defineProperty(s[t],"channels",{value:i[t].channels}),Object.defineProperty(s[t],"labels",{value:i[t].labels});var e=function(t){for(var e=a(t),n={},i=Object.keys(e),r=i.length,s=0;s<r;s++){var l=i[s];null!==e[l].parent&&(n[l]=o(l,e))}return n}(t);Object.keys(e).forEach((function(n){var i=e[n];s[t][n]=function(t){var e=function(e){if(null==e)return e;arguments.length>1&&(e=Array.prototype.slice.call(arguments));var n=t(e);if("object"==typeof n)for(var i=n.length,a=0;a<i;a++)n[a]=Math.round(n[a]);return n};return"conversion"in t&&(e.conversion=t.conversion),e}(i),s[t][n].raw=function(t){var e=function(e){return null==e?e:(arguments.length>1&&(e=Array.prototype.slice.call(arguments)),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(i)}))}));var l=s,u={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},d={getRgba:h,getHsla:c,getRgb:function(t){var e=h(t);return e&&e.slice(0,3)},getHsl:function(t){var e=c(t);return e&&e.slice(0,3)},getHwb:f,getAlpha:function(t){var e=h(t);if(e)return e[3];if(e=c(t))return e[3];if(e=f(t))return e[3]},hexString:function(t,e){e=void 0!==e&&3===t.length?e:t[3];return"#"+b(t[0])+b(t[1])+b(t[2])+(e>=0&&e<1?b(Math.round(255*e)):"")},rgbString:function(t,e){if(e<1||t[3]&&t[3]<1)return g(t,e);return"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},rgbaString:g,percentString:function(t,e){if(e<1||t[3]&&t[3]<1)return m(t,e);var n=Math.round(t[0]/255*100),i=Math.round(t[1]/255*100),a=Math.round(t[2]/255*100);return"rgb("+n+"%, "+i+"%, "+a+"%)"},percentaString:m,hslString:function(t,e){if(e<1||t[3]&&t[3]<1)return p(t,e);return"hsl("+t[0]+", "+t[1]+"%, "+t[2]+"%)"},hslaString:p,hwbString:function(t,e){void 0===e&&(e=void 0!==t[3]?t[3]:1);return"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+(void 0!==e&&1!==e?", "+e:"")+")"},keyword:function(t){return y[t.slice(0,3)]}};function h(t){if(t){var e=[0,0,0],n=1,i=t.match(/^#([a-fA-F0-9]{3,4})$/i),a="";if(i){a=(i=i[1])[3];for(var r=0;r<e.length;r++)e[r]=parseInt(i[r]+i[r],16);a&&(n=Math.round(parseInt(a+a,16)/255*100)/100)}else if(i=t.match(/^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i)){a=i[2],i=i[1];for(r=0;r<e.length;r++)e[r]=parseInt(i.slice(2*r,2*r+2),16);a&&(n=Math.round(parseInt(a,16)/255*100)/100)}else if(i=t.match(/^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=parseInt(i[r+1]);n=parseFloat(i[4])}else if(i=t.match(/^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i)){for(r=0;r<e.length;r++)e[r]=Math.round(2.55*parseFloat(i[r+1]));n=parseFloat(i[4])}else if(i=t.match(/(\w+)/)){if("transparent"==i[1])return[0,0,0,0];if(!(e=u[i[1]]))return}for(r=0;r<e.length;r++)e[r]=v(e[r],0,255);return n=n||0==n?v(n,0,1):1,e[3]=n,e}}function c(t){if(t){var e=t.match(/^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[v(parseInt(e[1]),0,360),v(parseFloat(e[2]),0,100),v(parseFloat(e[3]),0,100),v(isNaN(n)?1:n,0,1)]}}}function f(t){if(t){var e=t.match(/^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/);if(e){var n=parseFloat(e[4]);return[v(parseInt(e[1]),0,360),v(parseFloat(e[2]),0,100),v(parseFloat(e[3]),0,100),v(isNaN(n)?1:n,0,1)]}}}function g(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+e+")"}function m(t,e){return"rgba("+Math.round(t[0]/255*100)+"%, "+Math.round(t[1]/255*100)+"%, "+Math.round(t[2]/255*100)+"%, "+(e||t[3]||1)+")"}function p(t,e){return void 0===e&&(e=void 0!==t[3]?t[3]:1),"hsla("+t[0]+", "+t[1]+"%, "+t[2]+"%, "+e+")"}function v(t,e,n){return Math.min(Math.max(e,t),n)}function b(t){var e=t.toString(16).toUpperCase();return e.length<2?"0"+e:e}var y={};for(var x in u)y[u[x]]=x;var _=function(t){return t instanceof _?t:this instanceof _?(this.valid=!1,this.values={rgb:[0,0,0],hsl:[0,0,0],hsv:[0,0,0],hwb:[0,0,0],cmyk:[0,0,0,0],alpha:1},void("string"==typeof t?(e=d.getRgba(t))?this.setValues("rgb",e):(e=d.getHsla(t))?this.setValues("hsl",e):(e=d.getHwb(t))&&this.setValues("hwb",e):"object"==typeof t&&(void 0!==(e=t).r||void 0!==e.red?this.setValues("rgb",e):void 0!==e.l||void 0!==e.lightness?this.setValues("hsl",e):void 0!==e.v||void 0!==e.value?this.setValues("hsv",e):void 0!==e.w||void 0!==e.whiteness?this.setValues("hwb",e):void 0===e.c&&void 0===e.cyan||this.setValues("cmyk",e)))):new _(t);var e};_.prototype={isValid:function(){return this.valid},rgb:function(){return this.setSpace("rgb",arguments)},hsl:function(){return this.setSpace("hsl",arguments)},hsv:function(){return this.setSpace("hsv",arguments)},hwb:function(){return this.setSpace("hwb",arguments)},cmyk:function(){return this.setSpace("cmyk",arguments)},rgbArray:function(){return this.values.rgb},hslArray:function(){return this.values.hsl},hsvArray:function(){return this.values.hsv},hwbArray:function(){var t=this.values;return 1!==t.alpha?t.hwb.concat([t.alpha]):t.hwb},cmykArray:function(){return this.values.cmyk},rgbaArray:function(){var t=this.values;return t.rgb.concat([t.alpha])},hslaArray:function(){var t=this.values;return t.hsl.concat([t.alpha])},alpha:function(t){return void 0===t?this.values.alpha:(this.setValues("alpha",t),this)},red:function(t){return this.setChannel("rgb",0,t)},green:function(t){return this.setChannel("rgb",1,t)},blue:function(t){return this.setChannel("rgb",2,t)},hue:function(t){return t&&(t=(t%=360)<0?360+t:t),this.setChannel("hsl",0,t)},saturation:function(t){return this.setChannel("hsl",1,t)},lightness:function(t){return this.setChannel("hsl",2,t)},saturationv:function(t){return this.setChannel("hsv",1,t)},whiteness:function(t){return this.setChannel("hwb",1,t)},blackness:function(t){return this.setChannel("hwb",2,t)},value:function(t){return this.setChannel("hsv",2,t)},cyan:function(t){return this.setChannel("cmyk",0,t)},magenta:function(t){return this.setChannel("cmyk",1,t)},yellow:function(t){return this.setChannel("cmyk",2,t)},black:function(t){return this.setChannel("cmyk",3,t)},hexString:function(){return d.hexString(this.values.rgb)},rgbString:function(){return d.rgbString(this.values.rgb,this.values.alpha)},rgbaString:function(){return d.rgbaString(this.values.rgb,this.values.alpha)},percentString:function(){return d.percentString(this.values.rgb,this.values.alpha)},hslString:function(){return d.hslString(this.values.hsl,this.values.alpha)},hslaString:function(){return d.hslaString(this.values.hsl,this.values.alpha)},hwbString:function(){return d.hwbString(this.values.hwb,this.values.alpha)},keyword:function(){return d.keyword(this.values.rgb,this.values.alpha)},rgbNumber:function(){var t=this.values.rgb;return t[0]<<16|t[1]<<8|t[2]},luminosity:function(){for(var t=this.values.rgb,e=[],n=0;n<t.length;n++){var i=t[n]/255;e[n]=i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4)}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast:function(t){var e=this.luminosity(),n=t.luminosity();return e>n?(e+.05)/(n+.05):(n+.05)/(e+.05)},level:function(t){var e=this.contrast(t);return e>=7.1?"AAA":e>=4.5?"AA":""},dark:function(){var t=this.values.rgb;return(299*t[0]+587*t[1]+114*t[2])/1e3<128},light:function(){return!this.dark()},negate:function(){for(var t=[],e=0;e<3;e++)t[e]=255-this.values.rgb[e];return this.setValues("rgb",t),this},lighten:function(t){var e=this.values.hsl;return e[2]+=e[2]*t,this.setValues("hsl",e),this},darken:function(t){var e=this.values.hsl;return e[2]-=e[2]*t,this.setValues("hsl",e),this},saturate:function(t){var e=this.values.hsl;return e[1]+=e[1]*t,this.setValues("hsl",e),this},desaturate:function(t){var e=this.values.hsl;return e[1]-=e[1]*t,this.setValues("hsl",e),this},whiten:function(t){var e=this.values.hwb;return e[1]+=e[1]*t,this.setValues("hwb",e),this},blacken:function(t){var e=this.values.hwb;return e[2]+=e[2]*t,this.setValues("hwb",e),this},greyscale:function(){var t=this.values.rgb,e=.3*t[0]+.59*t[1]+.11*t[2];return this.setValues("rgb",[e,e,e]),this},clearer:function(t){var e=this.values.alpha;return this.setValues("alpha",e-e*t),this},opaquer:function(t){var e=this.values.alpha;return this.setValues("alpha",e+e*t),this},rotate:function(t){var e=this.values.hsl,n=(e[0]+t)%360;return e[0]=n<0?360+n:n,this.setValues("hsl",e),this},mix:function(t,e){var n=t,i=void 0===e?.5:e,a=2*i-1,r=this.alpha()-n.alpha(),o=((a*r==-1?a:(a+r)/(1+a*r))+1)/2,s=1-o;return this.rgb(o*this.red()+s*n.red(),o*this.green()+s*n.green(),o*this.blue()+s*n.blue()).alpha(this.alpha()*i+n.alpha()*(1-i))},toJSON:function(){return this.rgb()},clone:function(){var t,e,n=new _,i=this.values,a=n.values;for(var r in i)i.hasOwnProperty(r)&&(t=i[r],"[object Array]"===(e={}.toString.call(t))?a[r]=t.slice(0):"[object Number]"===e?a[r]=t:console.error("unexpected color value:",t));return n}},_.prototype.spaces={rgb:["red","green","blue"],hsl:["hue","saturation","lightness"],hsv:["hue","saturation","value"],hwb:["hue","whiteness","blackness"],cmyk:["cyan","magenta","yellow","black"]},_.prototype.maxes={rgb:[255,255,255],hsl:[360,100,100],hsv:[360,100,100],hwb:[360,100,100],cmyk:[100,100,100,100]},_.prototype.getValues=function(t){for(var e=this.values,n={},i=0;i<t.length;i++)n[t.charAt(i)]=e[t][i];return 1!==e.alpha&&(n.a=e.alpha),n},_.prototype.setValues=function(t,e){var n,i,a=this.values,r=this.spaces,o=this.maxes,s=1;if(this.valid=!0,"alpha"===t)s=e;else if(e.length)a[t]=e.slice(0,t.length),s=e[t.length];else if(void 0!==e[t.charAt(0)]){for(n=0;n<t.length;n++)a[t][n]=e[t.charAt(n)];s=e.a}else if(void 0!==e[r[t][0]]){var u=r[t];for(n=0;n<t.length;n++)a[t][n]=e[u[n]];s=e.alpha}if(a.alpha=Math.max(0,Math.min(1,void 0===s?a.alpha:s)),"alpha"===t)return!1;for(n=0;n<t.length;n++)i=Math.max(0,Math.min(o[t][n],a[t][n])),a[t][n]=Math.round(i);for(var d in r)d!==t&&(a[d]=l[t][d](a[t]));return!0},_.prototype.setSpace=function(t,e){var n=e[0];return void 0===n?this.getValues(t):("number"==typeof n&&(n=Array.prototype.slice.call(e)),this.setValues(t,n),this)},_.prototype.setChannel=function(t,e,n){var i=this.values[t];return void 0===n?i[e]:n===i[e]?this:(i[e]=n,this.setValues(t,i),this)},"undefined"!=typeof window&&(window.Color=_);var w,k=_,M={noop:function(){},uid:(w=0,function(){return w++}),isNullOrUndef:function(t){return null==t},isArray:function(t){if(Array.isArray&&Array.isArray(t))return!0;var e=Object.prototype.toString.call(t);return"[object"===e.substr(0,7)&&"Array]"===e.substr(-6)},isObject:function(t){return null!==t&&"[object Object]"===Object.prototype.toString.call(t)},isFinite:function(t){return("number"==typeof t||t instanceof Number)&&isFinite(t)},valueOrDefault:function(t,e){return void 0===t?e:t},valueAtIndexOrDefault:function(t,e,n){return M.valueOrDefault(M.isArray(t)?t[e]:t,n)},callback:function(t,e,n){if(t&&"function"==typeof t.call)return t.apply(n,e)},each:function(t,e,n,i){var a,r,o;if(M.isArray(t))if(r=t.length,i)for(a=r-1;a>=0;a--)e.call(n,t[a],a);else for(a=0;a<r;a++)e.call(n,t[a],a);else if(M.isObject(t))for(r=(o=Object.keys(t)).length,a=0;a<r;a++)e.call(n,t[o[a]],o[a])},arrayEquals:function(t,e){var n,i,a,r;if(!t||!e||t.length!==e.length)return!1;for(n=0,i=t.length;n<i;++n)if(a=t[n],r=e[n],a instanceof Array&&r instanceof Array){if(!M.arrayEquals(a,r))return!1}else if(a!==r)return!1;return!0},clone:function(t){if(M.isArray(t))return t.map(M.clone);if(M.isObject(t)){for(var e={},n=Object.keys(t),i=n.length,a=0;a<i;++a)e[n[a]]=M.clone(t[n[a]]);return e}return t},_merger:function(t,e,n,i){var a=e[t],r=n[t];M.isObject(a)&&M.isObject(r)?M.merge(a,r,i):e[t]=M.clone(r)},_mergerIf:function(t,e,n){var i=e[t],a=n[t];M.isObject(i)&&M.isObject(a)?M.mergeIf(i,a):e.hasOwnProperty(t)||(e[t]=M.clone(a))},merge:function(t,e,n){var i,a,r,o,s,l=M.isArray(e)?e:[e],u=l.length;if(!M.isObject(t))return t;for(i=(n=n||{}).merger||M._merger,a=0;a<u;++a)if(e=l[a],M.isObject(e))for(s=0,o=(r=Object.keys(e)).length;s<o;++s)i(r[s],t,e,n);return t},mergeIf:function(t,e){return M.merge(t,e,{merger:M._mergerIf})},extend:Object.assign||function(t){return M.merge(t,[].slice.call(arguments,1),{merger:function(t,e,n){e[t]=n[t]}})},inherits:function(t){var e=this,n=t&&t.hasOwnProperty("constructor")?t.constructor:function(){return e.apply(this,arguments)},i=function(){this.constructor=n};return i.prototype=e.prototype,n.prototype=new i,n.extend=M.inherits,t&&M.extend(n.prototype,t),n.__super__=e.prototype,n},_deprecated:function(t,e,n,i){void 0!==e&&console.warn(t+': "'+n+'" is deprecated. Please use "'+i+'" instead')}},S=M;M.callCallback=M.callback,M.indexOf=function(t,e,n){return Array.prototype.indexOf.call(t,e,n)},M.getValueOrDefault=M.valueOrDefault,M.getValueAtIndexOrDefault=M.valueAtIndexOrDefault;var D={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return-t*(t-2)},easeInOutQuad:function(t){return(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1)},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return(t-=1)*t*t+1},easeInOutCubic:function(t){return(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2)},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return-((t-=1)*t*t*t-1)},easeInOutQuart:function(t){return(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2)},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return(t-=1)*t*t*t*t+1},easeInOutQuint:function(t){return(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2)},easeInSine:function(t){return 1-Math.cos(t*(Math.PI/2))},easeOutSine:function(t){return Math.sin(t*(Math.PI/2))},easeInOutSine:function(t){return-.5*(Math.cos(Math.PI*t)-1)},easeInExpo:function(t){return 0===t?0:Math.pow(2,10*(t-1))},easeOutExpo:function(t){return 1===t?1:1-Math.pow(2,-10*t)},easeInOutExpo:function(t){return 0===t?0:1===t?1:(t/=.5)<1?.5*Math.pow(2,10*(t-1)):.5*(2-Math.pow(2,-10*--t))},easeInCirc:function(t){return t>=1?t:-(Math.sqrt(1-t*t)-1)},easeOutCirc:function(t){return Math.sqrt(1-(t-=1)*t)},easeInOutCirc:function(t){return(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1)},easeInElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),-i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n))},easeOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:1===t?1:(n||(n=.3),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),i*Math.pow(2,-10*t)*Math.sin((t-e)*(2*Math.PI)/n)+1)},easeInOutElastic:function(t){var e=1.70158,n=0,i=1;return 0===t?0:2==(t/=.5)?1:(n||(n=.45),i<1?(i=1,e=n/4):e=n/(2*Math.PI)*Math.asin(1/i),t<1?i*Math.pow(2,10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*-.5:i*Math.pow(2,-10*(t-=1))*Math.sin((t-e)*(2*Math.PI)/n)*.5+1)},easeInBack:function(t){var e=1.70158;return t*t*((e+1)*t-e)},easeOutBack:function(t){var e=1.70158;return(t-=1)*t*((e+1)*t+e)+1},easeInOutBack:function(t){var e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:function(t){return 1-D.easeOutBounce(1-t)},easeOutBounce:function(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:function(t){return t<.5?.5*D.easeInBounce(2*t):.5*D.easeOutBounce(2*t-1)+.5}},C={effects:D};S.easingEffects=D;var P=Math.PI,T=P/180,O=2*P,A=P/2,F=P/4,I=2*P/3,L={clear:function(t){t.ctx.clearRect(0,0,t.width,t.height)},roundedRect:function(t,e,n,i,a,r){if(r){var o=Math.min(r,a/2,i/2),s=e+o,l=n+o,u=e+i-o,d=n+a-o;t.moveTo(e,l),s<u&&l<d?(t.arc(s,l,o,-P,-A),t.arc(u,l,o,-A,0),t.arc(u,d,o,0,A),t.arc(s,d,o,A,P)):s<u?(t.moveTo(s,n),t.arc(u,l,o,-A,A),t.arc(s,l,o,A,P+A)):l<d?(t.arc(s,l,o,-P,0),t.arc(s,d,o,0,P)):t.arc(s,l,o,-P,P),t.closePath(),t.moveTo(e,n)}else t.rect(e,n,i,a)},drawPoint:function(t,e,n,i,a,r){var o,s,l,u,d,h=(r||0)*T;if(e&&"object"==typeof e&&("[object HTMLImageElement]"===(o=e.toString())||"[object HTMLCanvasElement]"===o))return t.save(),t.translate(i,a),t.rotate(h),t.drawImage(e,-e.width/2,-e.height/2,e.width,e.height),void t.restore();if(!(isNaN(n)||n<=0)){switch(t.beginPath(),e){default:t.arc(i,a,n,0,O),t.closePath();break;case"triangle":t.moveTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=I,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),h+=I,t.lineTo(i+Math.sin(h)*n,a-Math.cos(h)*n),t.closePath();break;case"rectRounded":u=n-(d=.516*n),s=Math.cos(h+F)*u,l=Math.sin(h+F)*u,t.arc(i-s,a-l,d,h-P,h-A),t.arc(i+l,a-s,d,h-A,h),t.arc(i+s,a+l,d,h,h+A),t.arc(i-l,a+s,d,h+A,h+P),t.closePath();break;case"rect":if(!r){u=Math.SQRT1_2*n,t.rect(i-u,a-u,2*u,2*u);break}h+=F;case"rectRot":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+l,a-s),t.lineTo(i+s,a+l),t.lineTo(i-l,a+s),t.closePath();break;case"crossRot":h+=F;case"cross":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"star":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s),h+=F,s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l),t.moveTo(i+l,a-s),t.lineTo(i-l,a+s);break;case"line":s=Math.cos(h)*n,l=Math.sin(h)*n,t.moveTo(i-s,a-l),t.lineTo(i+s,a+l);break;case"dash":t.moveTo(i,a),t.lineTo(i+Math.cos(h)*n,a+Math.sin(h)*n)}t.fill(),t.stroke()}},_isPointInArea:function(t,e){return t.x>e.left-1e-6&&t.x<e.right+1e-6&&t.y>e.top-1e-6&&t.y<e.bottom+1e-6},clipArea:function(t,e){t.save(),t.beginPath(),t.rect(e.left,e.top,e.right-e.left,e.bottom-e.top),t.clip()},unclipArea:function(t){t.restore()},lineTo:function(t,e,n,i){var a=n.steppedLine;if(a){if("middle"===a){var r=(e.x+n.x)/2;t.lineTo(r,i?n.y:e.y),t.lineTo(r,i?e.y:n.y)}else"after"===a&&!i||"after"!==a&&i?t.lineTo(e.x,n.y):t.lineTo(n.x,e.y);t.lineTo(n.x,n.y)}else n.tension?t.bezierCurveTo(i?e.controlPointPreviousX:e.controlPointNextX,i?e.controlPointPreviousY:e.controlPointNextY,i?n.controlPointNextX:n.controlPointPreviousX,i?n.controlPointNextY:n.controlPointPreviousY,n.x,n.y):t.lineTo(n.x,n.y)}},R=L;S.clear=L.clear,S.drawRoundedRectangle=function(t){t.beginPath(),L.roundedRect.apply(L,arguments)};var N={_set:function(t,e){return S.merge(this[t]||(this[t]={}),e)}};N._set("global",{defaultColor:"rgba(0,0,0,0.1)",defaultFontColor:"#666",defaultFontFamily:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",defaultFontSize:12,defaultFontStyle:"normal",defaultLineHeight:1.2,showLines:!0});var W=N,Y=S.valueOrDefault;var z={toLineHeight:function(t,e){var n=(""+t).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);if(!n||"normal"===n[1])return 1.2*e;switch(t=+n[2],n[3]){case"px":return t;case"%":t/=100}return e*t},toPadding:function(t){var e,n,i,a;return S.isObject(t)?(e=+t.top||0,n=+t.right||0,i=+t.bottom||0,a=+t.left||0):e=n=i=a=+t||0,{top:e,right:n,bottom:i,left:a,height:e+i,width:a+n}},_parseFont:function(t){var e=W.global,n=Y(t.fontSize,e.defaultFontSize),i={family:Y(t.fontFamily,e.defaultFontFamily),lineHeight:S.options.toLineHeight(Y(t.lineHeight,e.defaultLineHeight),n),size:n,style:Y(t.fontStyle,e.defaultFontStyle),weight:null,string:""};return i.string=function(t){return!t||S.isNullOrUndef(t.size)||S.isNullOrUndef(t.family)?null:(t.style?t.style+" ":"")+(t.weight?t.weight+" ":"")+t.size+"px "+t.family}(i),i},resolve:function(t,e,n,i){var a,r,o,s=!0;for(a=0,r=t.length;a<r;++a)if(void 0!==(o=t[a])&&(void 0!==e&&"function"==typeof o&&(o=o(e),s=!1),void 0!==n&&S.isArray(o)&&(o=o[n],s=!1),void 0!==o))return i&&!s&&(i.cacheable=!1),o}},E={_factorize:function(t){var e,n=[],i=Math.sqrt(t);for(e=1;e<i;e++)t%e==0&&(n.push(e),n.push(t/e));return i===(0|i)&&n.push(i),n.sort((function(t,e){return t-e})).pop(),n},log10:Math.log10||function(t){var e=Math.log(t)*Math.LOG10E,n=Math.round(e);return t===Math.pow(10,n)?n:e}},V=E;S.log10=E.log10;var H=S,B=C,j=R,U=z,G=V,q={getRtlAdapter:function(t,e,n){return t?function(t,e){return{x:function(n){return t+t+e-n},setWidth:function(t){e=t},textAlign:function(t){return"center"===t?t:"right"===t?"left":"right"},xPlus:function(t,e){return t-e},leftForLtr:function(t,e){return t-e}}}(e,n):{x:function(t){return t},setWidth:function(t){},textAlign:function(t){return t},xPlus:function(t,e){return t+e},leftForLtr:function(t,e){return t}}},overrideTextDirection:function(t,e){var n,i;"ltr"!==e&&"rtl"!==e||(i=[(n=t.canvas.style).getPropertyValue("direction"),n.getPropertyPriority("direction")],n.setProperty("direction",e,"important"),t.prevTextDirection=i)},restoreTextDirection:function(t){var e=t.prevTextDirection;void 0!==e&&(delete t.prevTextDirection,t.canvas.style.setProperty("direction",e[0],e[1]))}};H.easing=B,H.canvas=j,H.options=U,H.math=G,H.rtl=q;var Z=function(t){H.extend(this,t),this.initialize.apply(this,arguments)};H.extend(Z.prototype,{_type:void 0,initialize:function(){this.hidden=!1},pivot:function(){var t=this;return t._view||(t._view=H.extend({},t._model)),t._start={},t},transition:function(t){var e=this,n=e._model,i=e._start,a=e._view;return n&&1!==t?(a||(a=e._view={}),i||(i=e._start={}),function(t,e,n,i){var a,r,o,s,l,u,d,h,c,f=Object.keys(n);for(a=0,r=f.length;a<r;++a)if(u=n[o=f[a]],e.hasOwnProperty(o)||(e[o]=u),(s=e[o])!==u&&"_"!==o[0]){if(t.hasOwnProperty(o)||(t[o]=s),(d=typeof u)===typeof(l=t[o]))if("string"===d){if((h=k(l)).valid&&(c=k(u)).valid){e[o]=c.mix(h,i).rgbString();continue}}else if(H.isFinite(l)&&H.isFinite(u)){e[o]=l+(u-l)*i;continue}e[o]=u}}(i,a,n,t),e):(e._view=H.extend({},n),e._start=null,e)},tooltipPosition:function(){return{x:this._model.x,y:this._model.y}},hasValue:function(){return H.isNumber(this._model.x)&&H.isNumber(this._model.y)}}),Z.extend=H.inherits;var $=Z,X=$.extend({chart:null,currentStep:0,numSteps:60,easing:"",render:null,onAnimationProgress:null,onAnimationComplete:null}),K=X;Object.defineProperty(X.prototype,"animationObject",{get:function(){return this}}),Object.defineProperty(X.prototype,"chartInstance",{get:function(){return this.chart},set:function(t){this.chart=t}}),W._set("global",{animation:{duration:1e3,easing:"easeOutQuart",onProgress:H.noop,onComplete:H.noop}});var J={animations:[],request:null,addAnimation:function(t,e,n,i){var a,r,o=this.animations;for(e.chart=t,e.startTime=Date.now(),e.duration=n,i||(t.animating=!0),a=0,r=o.length;a<r;++a)if(o[a].chart===t)return void(o[a]=e);o.push(e),1===o.length&&this.requestAnimationFrame()},cancelAnimation:function(t){var e=H.findIndex(this.animations,(function(e){return e.chart===t}));-1!==e&&(this.animations.splice(e,1),t.animating=!1)},requestAnimationFrame:function(){var t=this;null===t.request&&(t.request=H.requestAnimFrame.call(window,(function(){t.request=null,t.startDigest()})))},startDigest:function(){this.advance(),this.animations.length>0&&this.requestAnimationFrame()},advance:function(){for(var t,e,n,i,a=this.animations,r=0;r<a.length;)e=(t=a[r]).chart,n=t.numSteps,i=Math.floor((Date.now()-t.startTime)/t.duration*n)+1,t.currentStep=Math.min(i,n),H.callback(t.render,[e,t],e),H.callback(t.onAnimationProgress,[t],e),t.currentStep>=n?(H.callback(t.onAnimationComplete,[t],e),e.animating=!1,a.splice(r,1)):++r}},Q=H.options.resolve,tt=["push","pop","shift","splice","unshift"];function et(t,e){var n=t._chartjs;if(n){var i=n.listeners,a=i.indexOf(e);-1!==a&&i.splice(a,1),i.length>0||(tt.forEach((function(e){delete t[e]})),delete t._chartjs)}}var nt=function(t,e){this.initialize(t,e)};H.extend(nt.prototype,{datasetElementType:null,dataElementType:null,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth"],_dataElementOptions:["backgroundColor","borderColor","borderWidth","pointStyle"],initialize:function(t,e){var n=this;n.chart=t,n.index=e,n.linkScales(),n.addElements(),n._type=n.getMeta().type},updateIndex:function(t){this.index=t},linkScales:function(){var t=this.getMeta(),e=this.chart,n=e.scales,i=this.getDataset(),a=e.options.scales;null!==t.xAxisID&&t.xAxisID in n&&!i.xAxisID||(t.xAxisID=i.xAxisID||a.xAxes[0].id),null!==t.yAxisID&&t.yAxisID in n&&!i.yAxisID||(t.yAxisID=i.yAxisID||a.yAxes[0].id)},getDataset:function(){return this.chart.data.datasets[this.index]},getMeta:function(){return this.chart.getDatasetMeta(this.index)},getScaleForId:function(t){return this.chart.scales[t]},_getValueScaleId:function(){return this.getMeta().yAxisID},_getIndexScaleId:function(){return this.getMeta().xAxisID},_getValueScale:function(){return this.getScaleForId(this._getValueScaleId())},_getIndexScale:function(){return this.getScaleForId(this._getIndexScaleId())},reset:function(){this._update(!0)},destroy:function(){this._data&&et(this._data,this)},createMetaDataset:function(){var t=this.datasetElementType;return t&&new t({_chart:this.chart,_datasetIndex:this.index})},createMetaData:function(t){var e=this.dataElementType;return e&&new e({_chart:this.chart,_datasetIndex:this.index,_index:t})},addElements:function(){var t,e,n=this.getMeta(),i=this.getDataset().data||[],a=n.data;for(t=0,e=i.length;t<e;++t)a[t]=a[t]||this.createMetaData(t);n.dataset=n.dataset||this.createMetaDataset()},addElementAndReset:function(t){var e=this.createMetaData(t);this.getMeta().data.splice(t,0,e),this.updateElement(e,t,!0)},buildOrUpdateElements:function(){var t,e,n=this,i=n.getDataset(),a=i.data||(i.data=[]);n._data!==a&&(n._data&&et(n._data,n),a&&Object.isExtensible(a)&&(e=n,(t=a)._chartjs?t._chartjs.listeners.push(e):(Object.defineProperty(t,"_chartjs",{configurable:!0,enumerable:!1,value:{listeners:[e]}}),tt.forEach((function(e){var n="onData"+e.charAt(0).toUpperCase()+e.slice(1),i=t[e];Object.defineProperty(t,e,{configurable:!0,enumerable:!1,value:function(){var e=Array.prototype.slice.call(arguments),a=i.apply(this,e);return H.each(t._chartjs.listeners,(function(t){"function"==typeof t[n]&&t[n].apply(t,e)})),a}})})))),n._data=a),n.resyncElements()},_configure:function(){this._config=H.merge({},[this.chart.options.datasets[this._type],this.getDataset()],{merger:function(t,e,n){"_meta"!==t&&"data"!==t&&H._merger(t,e,n)}})},_update:function(t){this._configure(),this._cachedDataOpts=null,this.update(t)},update:H.noop,transition:function(t){for(var e=this.getMeta(),n=e.data||[],i=n.length,a=0;a<i;++a)n[a].transition(t);e.dataset&&e.dataset.transition(t)},draw:function(){var t=this.getMeta(),e=t.data||[],n=e.length,i=0;for(t.dataset&&t.dataset.draw();i<n;++i)e[i].draw()},getStyle:function(t){var e,n=this.getMeta(),i=n.dataset;return this._configure(),i&&void 0===t?e=this._resolveDatasetElementOptions(i||{}):(t=t||0,e=this._resolveDataElementOptions(n.data[t]||{},t)),!1!==e.fill&&null!==e.fill||(e.backgroundColor=e.borderColor),e},_resolveDatasetElementOptions:function(t,e){var n,i,a,r,o=this,s=o.chart,l=o._config,u=t.custom||{},d=s.options.elements[o.datasetElementType.prototype._type]||{},h=o._datasetElementOptions,c={},f={chart:s,dataset:o.getDataset(),datasetIndex:o.index,hover:e};for(n=0,i=h.length;n<i;++n)a=h[n],r=e?"hover"+a.charAt(0).toUpperCase()+a.slice(1):a,c[a]=Q([u[r],l[r],d[r]],f);return c},_resolveDataElementOptions:function(t,e){var n=this,i=t&&t.custom,a=n._cachedDataOpts;if(a&&!i)return a;var r,o,s,l,u=n.chart,d=n._config,h=u.options.elements[n.dataElementType.prototype._type]||{},c=n._dataElementOptions,f={},g={chart:u,dataIndex:e,dataset:n.getDataset(),datasetIndex:n.index},m={cacheable:!i};if(i=i||{},H.isArray(c))for(o=0,s=c.length;o<s;++o)f[l=c[o]]=Q([i[l],d[l],h[l]],g,e,m);else for(o=0,s=(r=Object.keys(c)).length;o<s;++o)f[l=r[o]]=Q([i[l],d[c[l]],d[l],h[l]],g,e,m);return m.cacheable&&(n._cachedDataOpts=Object.freeze(f)),f},removeHoverStyle:function(t){H.merge(t._model,t.$previousStyle||{}),delete t.$previousStyle},setHoverStyle:function(t){var e=this.chart.data.datasets[t._datasetIndex],n=t._index,i=t.custom||{},a=t._model,r=H.getHoverColor;t.$previousStyle={backgroundColor:a.backgroundColor,borderColor:a.borderColor,borderWidth:a.borderWidth},a.backgroundColor=Q([i.hoverBackgroundColor,e.hoverBackgroundColor,r(a.backgroundColor)],void 0,n),a.borderColor=Q([i.hoverBorderColor,e.hoverBorderColor,r(a.borderColor)],void 0,n),a.borderWidth=Q([i.hoverBorderWidth,e.hoverBorderWidth,a.borderWidth],void 0,n)},_removeDatasetHoverStyle:function(){var t=this.getMeta().dataset;t&&this.removeHoverStyle(t)},_setDatasetHoverStyle:function(){var t,e,n,i,a,r,o=this.getMeta().dataset,s={};if(o){for(r=o._model,a=this._resolveDatasetElementOptions(o,!0),t=0,e=(i=Object.keys(a)).length;t<e;++t)s[n=i[t]]=r[n],r[n]=a[n];o.$previousStyle=s}},resyncElements:function(){var t=this.getMeta(),e=this.getDataset().data,n=t.data.length,i=e.length;i<n?t.data.splice(i,n-i):i>n&&this.insertElements(n,i-n)},insertElements:function(t,e){for(var n=0;n<e;++n)this.addElementAndReset(t+n)},onDataPush:function(){var t=arguments.length;this.insertElements(this.getDataset().data.length-t,t)},onDataPop:function(){this.getMeta().data.pop()},onDataShift:function(){this.getMeta().data.shift()},onDataSplice:function(t,e){this.getMeta().data.splice(t,e),this.insertElements(t,arguments.length-2)},onDataUnshift:function(){this.insertElements(0,arguments.length)}}),nt.extend=H.inherits;var it=nt,at=2*Math.PI;function rt(t,e){var n=e.startAngle,i=e.endAngle,a=e.pixelMargin,r=a/e.outerRadius,o=e.x,s=e.y;t.beginPath(),t.arc(o,s,e.outerRadius,n-r,i+r),e.innerRadius>a?(r=a/e.innerRadius,t.arc(o,s,e.innerRadius-a,i+r,n-r,!0)):t.arc(o,s,a,i+Math.PI/2,n-Math.PI/2),t.closePath(),t.clip()}function ot(t,e,n){var i="inner"===e.borderAlign;i?(t.lineWidth=2*e.borderWidth,t.lineJoin="round"):(t.lineWidth=e.borderWidth,t.lineJoin="bevel"),n.fullCircles&&function(t,e,n,i){var a,r=n.endAngle;for(i&&(n.endAngle=n.startAngle+at,rt(t,n),n.endAngle=r,n.endAngle===n.startAngle&&n.fullCircles&&(n.endAngle+=at,n.fullCircles--)),t.beginPath(),t.arc(n.x,n.y,n.innerRadius,n.startAngle+at,n.startAngle,!0),a=0;a<n.fullCircles;++a)t.stroke();for(t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.startAngle+at),a=0;a<n.fullCircles;++a)t.stroke()}(t,e,n,i),i&&rt(t,n),t.beginPath(),t.arc(n.x,n.y,e.outerRadius,n.startAngle,n.endAngle),t.arc(n.x,n.y,n.innerRadius,n.endAngle,n.startAngle,!0),t.closePath(),t.stroke()}W._set("global",{elements:{arc:{backgroundColor:W.global.defaultColor,borderColor:"#fff",borderWidth:2,borderAlign:"center"}}});var st=$.extend({_type:"arc",inLabelRange:function(t){var e=this._view;return!!e&&Math.pow(t-e.x,2)<Math.pow(e.radius+e.hoverRadius,2)},inRange:function(t,e){var n=this._view;if(n){for(var i=H.getAngleFromPoint(n,{x:t,y:e}),a=i.angle,r=i.distance,o=n.startAngle,s=n.endAngle;s<o;)s+=at;for(;a>s;)a-=at;for(;a<o;)a+=at;var l=a>=o&&a<=s,u=r>=n.innerRadius&&r<=n.outerRadius;return l&&u}return!1},getCenterPoint:function(){var t=this._view,e=(t.startAngle+t.endAngle)/2,n=(t.innerRadius+t.outerRadius)/2;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},getArea:function(){var t=this._view;return Math.PI*((t.endAngle-t.startAngle)/(2*Math.PI))*(Math.pow(t.outerRadius,2)-Math.pow(t.innerRadius,2))},tooltipPosition:function(){var t=this._view,e=t.startAngle+(t.endAngle-t.startAngle)/2,n=(t.outerRadius-t.innerRadius)/2+t.innerRadius;return{x:t.x+Math.cos(e)*n,y:t.y+Math.sin(e)*n}},draw:function(){var t,e=this._chart.ctx,n=this._view,i="inner"===n.borderAlign?.33:0,a={x:n.x,y:n.y,innerRadius:n.innerRadius,outerRadius:Math.max(n.outerRadius-i,0),pixelMargin:i,startAngle:n.startAngle,endAngle:n.endAngle,fullCircles:Math.floor(n.circumference/at)};if(e.save(),e.fillStyle=n.backgroundColor,e.strokeStyle=n.borderColor,a.fullCircles){for(a.endAngle=a.startAngle+at,e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),t=0;t<a.fullCircles;++t)e.fill();a.endAngle=a.startAngle+n.circumference%at}e.beginPath(),e.arc(a.x,a.y,a.outerRadius,a.startAngle,a.endAngle),e.arc(a.x,a.y,a.innerRadius,a.endAngle,a.startAngle,!0),e.closePath(),e.fill(),n.borderWidth&&ot(e,n,a),e.restore()}}),lt=H.valueOrDefault,ut=W.global.defaultColor;W._set("global",{elements:{line:{tension:.4,backgroundColor:ut,borderWidth:3,borderColor:ut,borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",capBezierPoints:!0,fill:!0}}});var dt=$.extend({_type:"line",draw:function(){var t,e,n,i=this,a=i._view,r=i._chart.ctx,o=a.spanGaps,s=i._children.slice(),l=W.global,u=l.elements.line,d=-1,h=i._loop;if(s.length){if(i._loop){for(t=0;t<s.length;++t)if(e=H.previousItem(s,t),!s[t]._view.skip&&e._view.skip){s=s.slice(t).concat(s.slice(0,t)),h=o;break}h&&s.push(s[0])}for(r.save(),r.lineCap=a.borderCapStyle||u.borderCapStyle,r.setLineDash&&r.setLineDash(a.borderDash||u.borderDash),r.lineDashOffset=lt(a.borderDashOffset,u.borderDashOffset),r.lineJoin=a.borderJoinStyle||u.borderJoinStyle,r.lineWidth=lt(a.borderWidth,u.borderWidth),r.strokeStyle=a.borderColor||l.defaultColor,r.beginPath(),(n=s[0]._view).skip||(r.moveTo(n.x,n.y),d=0),t=1;t<s.length;++t)n=s[t]._view,e=-1===d?H.previousItem(s,t):s[d],n.skip||(d!==t-1&&!o||-1===d?r.moveTo(n.x,n.y):H.canvas.lineTo(r,e._view,n),d=t);h&&r.closePath(),r.stroke(),r.restore()}}}),ht=H.valueOrDefault,ct=W.global.defaultColor;function ft(t){var e=this._view;return!!e&&Math.abs(t-e.x)<e.radius+e.hitRadius}W._set("global",{elements:{point:{radius:3,pointStyle:"circle",backgroundColor:ct,borderColor:ct,borderWidth:1,hitRadius:1,hoverRadius:4,hoverBorderWidth:1}}});var gt=$.extend({_type:"point",inRange:function(t,e){var n=this._view;return!!n&&Math.pow(t-n.x,2)+Math.pow(e-n.y,2)<Math.pow(n.hitRadius+n.radius,2)},inLabelRange:ft,inXRange:ft,inYRange:function(t){var e=this._view;return!!e&&Math.abs(t-e.y)<e.radius+e.hitRadius},getCenterPoint:function(){var t=this._view;return{x:t.x,y:t.y}},getArea:function(){return Math.PI*Math.pow(this._view.radius,2)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y,padding:t.radius+t.borderWidth}},draw:function(t){var e=this._view,n=this._chart.ctx,i=e.pointStyle,a=e.rotation,r=e.radius,o=e.x,s=e.y,l=W.global,u=l.defaultColor;e.skip||(void 0===t||H.canvas._isPointInArea(e,t))&&(n.strokeStyle=e.borderColor||u,n.lineWidth=ht(e.borderWidth,l.elements.point.borderWidth),n.fillStyle=e.backgroundColor||u,H.canvas.drawPoint(n,i,r,o,s,a))}}),mt=W.global.defaultColor;function pt(t){return t&&void 0!==t.width}function vt(t){var e,n,i,a,r;return pt(t)?(r=t.width/2,e=t.x-r,n=t.x+r,i=Math.min(t.y,t.base),a=Math.max(t.y,t.base)):(r=t.height/2,e=Math.min(t.x,t.base),n=Math.max(t.x,t.base),i=t.y-r,a=t.y+r),{left:e,top:i,right:n,bottom:a}}function bt(t,e,n){return t===e?n:t===n?e:t}function yt(t,e,n){var i,a,r,o,s=t.borderWidth,l=function(t){var e=t.borderSkipped,n={};return e?(t.horizontal?t.base>t.x&&(e=bt(e,"left","right")):t.base<t.y&&(e=bt(e,"bottom","top")),n[e]=!0,n):n}(t);return H.isObject(s)?(i=+s.top||0,a=+s.right||0,r=+s.bottom||0,o=+s.left||0):i=a=r=o=+s||0,{t:l.top||i<0?0:i>n?n:i,r:l.right||a<0?0:a>e?e:a,b:l.bottom||r<0?0:r>n?n:r,l:l.left||o<0?0:o>e?e:o}}function xt(t,e,n){var i=null===e,a=null===n,r=!(!t||i&&a)&&vt(t);return r&&(i||e>=r.left&&e<=r.right)&&(a||n>=r.top&&n<=r.bottom)}W._set("global",{elements:{rectangle:{backgroundColor:mt,borderColor:mt,borderSkipped:"bottom",borderWidth:0}}});var _t=$.extend({_type:"rectangle",draw:function(){var t=this._chart.ctx,e=this._view,n=function(t){var e=vt(t),n=e.right-e.left,i=e.bottom-e.top,a=yt(t,n/2,i/2);return{outer:{x:e.left,y:e.top,w:n,h:i},inner:{x:e.left+a.l,y:e.top+a.t,w:n-a.l-a.r,h:i-a.t-a.b}}}(e),i=n.outer,a=n.inner;t.fillStyle=e.backgroundColor,t.fillRect(i.x,i.y,i.w,i.h),i.w===a.w&&i.h===a.h||(t.save(),t.beginPath(),t.rect(i.x,i.y,i.w,i.h),t.clip(),t.fillStyle=e.borderColor,t.rect(a.x,a.y,a.w,a.h),t.fill("evenodd"),t.restore())},height:function(){var t=this._view;return t.base-t.y},inRange:function(t,e){return xt(this._view,t,e)},inLabelRange:function(t,e){var n=this._view;return pt(n)?xt(n,t,null):xt(n,null,e)},inXRange:function(t){return xt(this._view,t,null)},inYRange:function(t){return xt(this._view,null,t)},getCenterPoint:function(){var t,e,n=this._view;return pt(n)?(t=n.x,e=(n.y+n.base)/2):(t=(n.x+n.base)/2,e=n.y),{x:t,y:e}},getArea:function(){var t=this._view;return pt(t)?t.width*Math.abs(t.y-t.base):t.height*Math.abs(t.x-t.base)},tooltipPosition:function(){var t=this._view;return{x:t.x,y:t.y}}}),wt={},kt=st,Mt=dt,St=gt,Dt=_t;wt.Arc=kt,wt.Line=Mt,wt.Point=St,wt.Rectangle=Dt;var Ct=H._deprecated,Pt=H.valueOrDefault;function Tt(t,e,n){var i,a,r=n.barThickness,o=e.stackCount,s=e.pixels[t],l=H.isNullOrUndef(r)?function(t,e){var n,i,a,r,o=t._length;for(a=1,r=e.length;a<r;++a)o=Math.min(o,Math.abs(e[a]-e[a-1]));for(a=0,r=t.getTicks().length;a<r;++a)i=t.getPixelForTick(a),o=a>0?Math.min(o,Math.abs(i-n)):o,n=i;return o}(e.scale,e.pixels):-1;return H.isNullOrUndef(r)?(i=l*n.categoryPercentage,a=n.barPercentage):(i=r*o,a=1),{chunk:i/o,ratio:a,start:s-i/2}}W._set("bar",{hover:{mode:"label"},scales:{xAxes:[{type:"category",offset:!0,gridLines:{offsetGridLines:!0}}],yAxes:[{type:"linear"}]}}),W._set("global",{datasets:{bar:{categoryPercentage:.8,barPercentage:.9}}});var Ot=it.extend({dataElementType:wt.Rectangle,_dataElementOptions:["backgroundColor","borderColor","borderSkipped","borderWidth","barPercentage","barThickness","categoryPercentage","maxBarThickness","minBarLength"],initialize:function(){var t,e,n=this;it.prototype.initialize.apply(n,arguments),(t=n.getMeta()).stack=n.getDataset().stack,t.bar=!0,e=n._getIndexScale().options,Ct("bar chart",e.barPercentage,"scales.[x/y]Axes.barPercentage","dataset.barPercentage"),Ct("bar chart",e.barThickness,"scales.[x/y]Axes.barThickness","dataset.barThickness"),Ct("bar chart",e.categoryPercentage,"scales.[x/y]Axes.categoryPercentage","dataset.categoryPercentage"),Ct("bar chart",n._getValueScale().options.minBarLength,"scales.[x/y]Axes.minBarLength","dataset.minBarLength"),Ct("bar chart",e.maxBarThickness,"scales.[x/y]Axes.maxBarThickness","dataset.maxBarThickness")},update:function(t){var e,n,i=this.getMeta().data;for(this._ruler=this.getRuler(),e=0,n=i.length;e<n;++e)this.updateElement(i[e],e,t)},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=i.getDataset(),o=i._resolveDataElementOptions(t,e);t._xScale=i.getScaleForId(a.xAxisID),t._yScale=i.getScaleForId(a.yAxisID),t._datasetIndex=i.index,t._index=e,t._model={backgroundColor:o.backgroundColor,borderColor:o.borderColor,borderSkipped:o.borderSkipped,borderWidth:o.borderWidth,datasetLabel:r.label,label:i.chart.data.labels[e]},H.isArray(r.data[e])&&(t._model.borderSkipped=null),i._updateElementGeometry(t,e,n,o),t.pivot()},_updateElementGeometry:function(t,e,n,i){var a=this,r=t._model,o=a._getValueScale(),s=o.getBasePixel(),l=o.isHorizontal(),u=a._ruler||a.getRuler(),d=a.calculateBarValuePixels(a.index,e,i),h=a.calculateBarIndexPixels(a.index,e,u,i);r.horizontal=l,r.base=n?s:d.base,r.x=l?n?s:d.head:h.center,r.y=l?h.center:n?s:d.head,r.height=l?h.size:void 0,r.width=l?void 0:h.size},_getStacks:function(t){var e,n,i=this._getIndexScale(),a=i._getMatchingVisibleMetas(this._type),r=i.options.stacked,o=a.length,s=[];for(e=0;e<o&&(n=a[e],(!1===r||-1===s.indexOf(n.stack)||void 0===r&&void 0===n.stack)&&s.push(n.stack),n.index!==t);++e);return s},getStackCount:function(){return this._getStacks().length},getStackIndex:function(t,e){var n=this._getStacks(t),i=void 0!==e?n.indexOf(e):-1;return-1===i?n.length-1:i},getRuler:function(){var t,e,n=this._getIndexScale(),i=[];for(t=0,e=this.getMeta().data.length;t<e;++t)i.push(n.getPixelForValue(null,t,this.index));return{pixels:i,start:n._startPixel,end:n._endPixel,stackCount:this.getStackCount(),scale:n}},calculateBarValuePixels:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._getValueScale(),c=h.isHorizontal(),f=d.data.datasets,g=h._getMatchingVisibleMetas(this._type),m=h._parseValue(f[t].data[e]),p=n.minBarLength,v=h.options.stacked,b=this.getMeta().stack,y=void 0===m.start?0:m.max>=0&&m.min>=0?m.min:m.max,x=void 0===m.start?m.end:m.max>=0&&m.min>=0?m.max-m.min:m.min-m.max,_=g.length;if(v||void 0===v&&void 0!==b)for(i=0;i<_&&(a=g[i]).index!==t;++i)a.stack===b&&(r=void 0===(u=h._parseValue(f[a.index].data[e])).start?u.end:u.min>=0&&u.max>=0?u.max:u.min,(m.min<0&&r<0||m.max>=0&&r>0)&&(y+=r));return o=h.getPixelForValue(y),l=(s=h.getPixelForValue(y+x))-o,void 0!==p&&Math.abs(l)<p&&(l=p,s=x>=0&&!c||x<0&&c?o-p:o+p),{size:l,base:o,head:s,center:s+l/2}},calculateBarIndexPixels:function(t,e,n,i){var a="flex"===i.barThickness?function(t,e,n){var i,a=e.pixels,r=a[t],o=t>0?a[t-1]:null,s=t<a.length-1?a[t+1]:null,l=n.categoryPercentage;return null===o&&(o=r-(null===s?e.end-e.start:s-r)),null===s&&(s=r+r-o),i=r-(r-Math.min(o,s))/2*l,{chunk:Math.abs(s-o)/2*l/e.stackCount,ratio:n.barPercentage,start:i}}(e,n,i):Tt(e,n,i),r=this.getStackIndex(t,this.getMeta().stack),o=a.start+a.chunk*r+a.chunk/2,s=Math.min(Pt(i.maxBarThickness,1/0),a.chunk*a.ratio);return{base:o-s/2,head:o+s/2,center:o,size:s}},draw:function(){var t=this.chart,e=this._getValueScale(),n=this.getMeta().data,i=this.getDataset(),a=n.length,r=0;for(H.canvas.clipArea(t.ctx,t.chartArea);r<a;++r){var o=e._parseValue(i.data[r]);isNaN(o.min)||isNaN(o.max)||n[r].draw()}H.canvas.unclipArea(t.ctx)},_resolveDataElementOptions:function(){var t=this,e=H.extend({},it.prototype._resolveDataElementOptions.apply(t,arguments)),n=t._getIndexScale().options,i=t._getValueScale().options;return e.barPercentage=Pt(n.barPercentage,e.barPercentage),e.barThickness=Pt(n.barThickness,e.barThickness),e.categoryPercentage=Pt(n.categoryPercentage,e.categoryPercentage),e.maxBarThickness=Pt(n.maxBarThickness,e.maxBarThickness),e.minBarLength=Pt(i.minBarLength,e.minBarLength),e}}),At=H.valueOrDefault,Ft=H.options.resolve;W._set("bubble",{hover:{mode:"single"},scales:{xAxes:[{type:"linear",position:"bottom",id:"x-axis-0"}],yAxes:[{type:"linear",position:"left",id:"y-axis-0"}]},tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.datasets[t.datasetIndex].label||"",i=e.datasets[t.datasetIndex].data[t.index];return n+": ("+t.xLabel+", "+t.yLabel+", "+i.r+")"}}}});var It=it.extend({dataElementType:wt.Point,_dataElementOptions:["backgroundColor","borderColor","borderWidth","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth","hoverRadius","hitRadius","pointStyle","rotation"],update:function(t){var e=this,n=e.getMeta().data;H.each(n,(function(n,i){e.updateElement(n,i,t)}))},updateElement:function(t,e,n){var i=this,a=i.getMeta(),r=t.custom||{},o=i.getScaleForId(a.xAxisID),s=i.getScaleForId(a.yAxisID),l=i._resolveDataElementOptions(t,e),u=i.getDataset().data[e],d=i.index,h=n?o.getPixelForDecimal(.5):o.getPixelForValue("object"==typeof u?u:NaN,e,d),c=n?s.getBasePixel():s.getPixelForValue(u,e,d);t._xScale=o,t._yScale=s,t._options=l,t._datasetIndex=d,t._index=e,t._model={backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,hitRadius:l.hitRadius,pointStyle:l.pointStyle,rotation:l.rotation,radius:n?0:l.radius,skip:r.skip||isNaN(h)||isNaN(c),x:h,y:c},t.pivot()},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=At(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=At(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=At(n.hoverBorderWidth,n.borderWidth),e.radius=n.radius+n.hoverRadius},_resolveDataElementOptions:function(t,e){var n=this,i=n.chart,a=n.getDataset(),r=t.custom||{},o=a.data[e]||{},s=it.prototype._resolveDataElementOptions.apply(n,arguments),l={chart:i,dataIndex:e,dataset:a,datasetIndex:n.index};return n._cachedDataOpts===s&&(s=H.extend({},s)),s.radius=Ft([r.radius,o.r,n._config.radius,i.options.elements.point.radius],l,e),s}}),Lt=H.valueOrDefault,Rt=Math.PI,Nt=2*Rt,Wt=Rt/2;W._set("doughnut",{animation:{animateRotate:!0,animateScale:!1},hover:{mode:"single"},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r]&&(a.data[r].hidden=!a.data[r].hidden);o.update()}},cutoutPercentage:50,rotation:-Wt,circumference:Nt,tooltips:{callbacks:{title:function(){return""},label:function(t,e){var n=e.labels[t.index],i=": "+e.datasets[t.datasetIndex].data[t.index];return H.isArray(n)?(n=n.slice())[0]+=i:n+=i,n}}}});var Yt=it.extend({dataElementType:wt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],getRingIndex:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&++e;return e},update:function(t){var e,n,i,a,r=this,o=r.chart,s=o.chartArea,l=o.options,u=1,d=1,h=0,c=0,f=r.getMeta(),g=f.data,m=l.cutoutPercentage/100||0,p=l.circumference,v=r._getRingWeight(r.index);if(p<Nt){var b=l.rotation%Nt,y=(b+=b>=Rt?-Nt:b<-Rt?Nt:0)+p,x=Math.cos(b),_=Math.sin(b),w=Math.cos(y),k=Math.sin(y),M=b<=0&&y>=0||y>=Nt,S=b<=Wt&&y>=Wt||y>=Nt+Wt,D=b<=-Wt&&y>=-Wt||y>=Rt+Wt,C=b===-Rt||y>=Rt?-1:Math.min(x,x*m,w,w*m),P=D?-1:Math.min(_,_*m,k,k*m),T=M?1:Math.max(x,x*m,w,w*m),O=S?1:Math.max(_,_*m,k,k*m);u=(T-C)/2,d=(O-P)/2,h=-(T+C)/2,c=-(O+P)/2}for(i=0,a=g.length;i<a;++i)g[i]._options=r._resolveDataElementOptions(g[i],i);for(o.borderWidth=r.getMaxBorderWidth(),e=(s.right-s.left-o.borderWidth)/u,n=(s.bottom-s.top-o.borderWidth)/d,o.outerRadius=Math.max(Math.min(e,n)/2,0),o.innerRadius=Math.max(o.outerRadius*m,0),o.radiusLength=(o.outerRadius-o.innerRadius)/(r._getVisibleDatasetWeightTotal()||1),o.offsetX=h*o.outerRadius,o.offsetY=c*o.outerRadius,f.total=r.calculateTotal(),r.outerRadius=o.outerRadius-o.radiusLength*r._getRingWeightOffset(r.index),r.innerRadius=Math.max(r.outerRadius-o.radiusLength*v,0),i=0,a=g.length;i<a;++i)r.updateElement(g[i],i,t)},updateElement:function(t,e,n){var i=this,a=i.chart,r=a.chartArea,o=a.options,s=o.animation,l=(r.left+r.right)/2,u=(r.top+r.bottom)/2,d=o.rotation,h=o.rotation,c=i.getDataset(),f=n&&s.animateRotate?0:t.hidden?0:i.calculateCircumference(c.data[e])*(o.circumference/Nt),g=n&&s.animateScale?0:i.innerRadius,m=n&&s.animateScale?0:i.outerRadius,p=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_model:{backgroundColor:p.backgroundColor,borderColor:p.borderColor,borderWidth:p.borderWidth,borderAlign:p.borderAlign,x:l+a.offsetX,y:u+a.offsetY,startAngle:d,endAngle:h,circumference:f,outerRadius:m,innerRadius:g,label:H.valueAtIndexOrDefault(c.label,e,a.data.labels[e])}});var v=t._model;n&&s.animateRotate||(v.startAngle=0===e?o.rotation:i.getMeta().data[e-1]._model.endAngle,v.endAngle=v.startAngle+v.circumference),t.pivot()},calculateTotal:function(){var t,e=this.getDataset(),n=this.getMeta(),i=0;return H.each(n.data,(function(n,a){t=e.data[a],isNaN(t)||n.hidden||(i+=Math.abs(t))})),i},calculateCircumference:function(t){var e=this.getMeta().total;return e>0&&!isNaN(t)?Nt*(Math.abs(t)/e):0},getMaxBorderWidth:function(t){var e,n,i,a,r,o,s,l,u=0,d=this.chart;if(!t)for(e=0,n=d.data.datasets.length;e<n;++e)if(d.isDatasetVisible(e)){t=(i=d.getDatasetMeta(e)).data,e!==this.index&&(r=i.controller);break}if(!t)return 0;for(e=0,n=t.length;e<n;++e)a=t[e],r?(r._configure(),o=r._resolveDataElementOptions(a,e)):o=a._options,"inner"!==o.borderAlign&&(s=o.borderWidth,u=(l=o.hoverBorderWidth)>(u=s>u?s:u)?l:u);return u},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=Lt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Lt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Lt(n.hoverBorderWidth,n.borderWidth)},_getRingWeightOffset:function(t){for(var e=0,n=0;n<t;++n)this.chart.isDatasetVisible(n)&&(e+=this._getRingWeight(n));return e},_getRingWeight:function(t){return Math.max(Lt(this.chart.data.datasets[t].weight,1),0)},_getVisibleDatasetWeightTotal:function(){return this._getRingWeightOffset(this.chart.data.datasets.length)}});W._set("horizontalBar",{hover:{mode:"index",axis:"y"},scales:{xAxes:[{type:"linear",position:"bottom"}],yAxes:[{type:"category",position:"left",offset:!0,gridLines:{offsetGridLines:!0}}]},elements:{rectangle:{borderSkipped:"left"}},tooltips:{mode:"index",axis:"y"}}),W._set("global",{datasets:{horizontalBar:{categoryPercentage:.8,barPercentage:.9}}});var zt=Ot.extend({_getValueScaleId:function(){return this.getMeta().xAxisID},_getIndexScaleId:function(){return this.getMeta().yAxisID}}),Et=H.valueOrDefault,Vt=H.options.resolve,Ht=H.canvas._isPointInArea;function Bt(t,e){var n=t&&t.options.ticks||{},i=n.reverse,a=void 0===n.min?e:0,r=void 0===n.max?e:0;return{start:i?r:a,end:i?a:r}}function jt(t,e,n){var i=n/2,a=Bt(t,i),r=Bt(e,i);return{top:r.end,right:a.end,bottom:r.start,left:a.start}}function Ut(t){var e,n,i,a;return H.isObject(t)?(e=t.top,n=t.right,i=t.bottom,a=t.left):e=n=i=a=t,{top:e,right:n,bottom:i,left:a}}W._set("line",{showLines:!0,spanGaps:!1,hover:{mode:"label"},scales:{xAxes:[{type:"category",id:"x-axis-0"}],yAxes:[{type:"linear",id:"y-axis-0"}]}});var Gt=it.extend({datasetElementType:wt.Line,dataElementType:wt.Point,_datasetElementOptions:["backgroundColor","borderCapStyle","borderColor","borderDash","borderDashOffset","borderJoinStyle","borderWidth","cubicInterpolationMode","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.options,l=i._config,u=i._showLine=Et(l.showLine,s.showLines);for(i._xScale=i.getScaleForId(a.xAxisID),i._yScale=i.getScaleForId(a.yAxisID),u&&(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=i._yScale,r._datasetIndex=i.index,r._children=o,r._model=i._resolveDatasetElementOptions(r),r.pivot()),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(u&&0!==r._model.tension&&i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i,a,r=this,o=r.getMeta(),s=t.custom||{},l=r.getDataset(),u=r.index,d=l.data[e],h=r._xScale,c=r._yScale,f=o.dataset._model,g=r._resolveDataElementOptions(t,e);i=h.getPixelForValue("object"==typeof d?d:NaN,e,u),a=n?c.getBasePixel():r.calculatePointY(d,e,u),t._xScale=h,t._yScale=c,t._options=g,t._datasetIndex=u,t._index=e,t._model={x:i,y:a,skip:s.skip||isNaN(i)||isNaN(a),radius:g.radius,pointStyle:g.pointStyle,rotation:g.rotation,backgroundColor:g.backgroundColor,borderColor:g.borderColor,borderWidth:g.borderWidth,tension:Et(s.tension,f?f.tension:0),steppedLine:!!f&&f.steppedLine,hitRadius:g.hitRadius}},_resolveDatasetElementOptions:function(t){var e=this,n=e._config,i=t.custom||{},a=e.chart.options,r=a.elements.line,o=it.prototype._resolveDatasetElementOptions.apply(e,arguments);return o.spanGaps=Et(n.spanGaps,a.spanGaps),o.tension=Et(n.lineTension,r.tension),o.steppedLine=Vt([i.steppedLine,n.steppedLine,r.stepped]),o.clip=Ut(Et(n.clip,jt(e._xScale,e._yScale,o.borderWidth))),o},calculatePointY:function(t,e,n){var i,a,r,o,s,l,u,d=this.chart,h=this._yScale,c=0,f=0;if(h.options.stacked){for(s=+h.getRightValue(t),u=(l=d._getSortedVisibleDatasetMetas()).length,i=0;i<u&&(r=l[i]).index!==n;++i)a=d.data.datasets[r.index],"line"===r.type&&r.yAxisID===h.id&&((o=+h.getRightValue(a.data[e]))<0?f+=o||0:c+=o||0);return s<0?h.getPixelForValue(f+s):h.getPixelForValue(c+s)}return h.getPixelForValue(t)},updateBezierControlPoints:function(){var t,e,n,i,a=this.chart,r=this.getMeta(),o=r.dataset._model,s=a.chartArea,l=r.data||[];function u(t,e,n){return Math.max(Math.min(t,n),e)}if(o.spanGaps&&(l=l.filter((function(t){return!t._model.skip}))),"monotone"===o.cubicInterpolationMode)H.splineCurveMonotone(l);else for(t=0,e=l.length;t<e;++t)n=l[t]._model,i=H.splineCurve(H.previousItem(l,t)._model,n,H.nextItem(l,t)._model,o.tension),n.controlPointPreviousX=i.previous.x,n.controlPointPreviousY=i.previous.y,n.controlPointNextX=i.next.x,n.controlPointNextY=i.next.y;if(a.options.elements.line.capBezierPoints)for(t=0,e=l.length;t<e;++t)n=l[t]._model,Ht(n,s)&&(t>0&&Ht(l[t-1]._model,s)&&(n.controlPointPreviousX=u(n.controlPointPreviousX,s.left,s.right),n.controlPointPreviousY=u(n.controlPointPreviousY,s.top,s.bottom)),t<l.length-1&&Ht(l[t+1]._model,s)&&(n.controlPointNextX=u(n.controlPointNextX,s.left,s.right),n.controlPointNextY=u(n.controlPointNextY,s.top,s.bottom)))},draw:function(){var t,e=this.chart,n=this.getMeta(),i=n.data||[],a=e.chartArea,r=e.canvas,o=0,s=i.length;for(this._showLine&&(t=n.dataset._model.clip,H.canvas.clipArea(e.ctx,{left:!1===t.left?0:a.left-t.left,right:!1===t.right?r.width:a.right+t.right,top:!1===t.top?0:a.top-t.top,bottom:!1===t.bottom?r.height:a.bottom+t.bottom}),n.dataset.draw(),H.canvas.unclipArea(e.ctx));o<s;++o)i[o].draw(a)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Et(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Et(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Et(n.hoverBorderWidth,n.borderWidth),e.radius=Et(n.hoverRadius,n.radius)}}),qt=H.options.resolve;W._set("polarArea",{scale:{type:"radialLinear",angleLines:{display:!1},gridLines:{circular:!0},pointLabels:{display:!1},ticks:{beginAtZero:!0}},animation:{animateRotate:!0,animateScale:!0},startAngle:-.5*Math.PI,legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data,o=r.datasets,s=r.labels;if(a.setAttribute("class",t.id+"-legend"),o.length)for(e=0,n=o[0].data.length;e<n;++e)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=o[0].backgroundColor[e],s[e]&&i.appendChild(document.createTextNode(s[e]));return a.outerHTML},legend:{labels:{generateLabels:function(t){var e=t.data;return e.labels.length&&e.datasets.length?e.labels.map((function(n,i){var a=t.getDatasetMeta(0),r=a.controller.getStyle(i);return{text:n,fillStyle:r.backgroundColor,strokeStyle:r.borderColor,lineWidth:r.borderWidth,hidden:isNaN(e.datasets[0].data[i])||a.data[i].hidden,index:i}})):[]}},onClick:function(t,e){var n,i,a,r=e.index,o=this.chart;for(n=0,i=(o.data.datasets||[]).length;n<i;++n)(a=o.getDatasetMeta(n)).data[r].hidden=!a.data[r].hidden;o.update()}},tooltips:{callbacks:{title:function(){return""},label:function(t,e){return e.labels[t.index]+": "+t.yLabel}}}});var Zt=it.extend({dataElementType:wt.Arc,linkScales:H.noop,_dataElementOptions:["backgroundColor","borderColor","borderWidth","borderAlign","hoverBackgroundColor","hoverBorderColor","hoverBorderWidth"],_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i,a=this,r=a.getDataset(),o=a.getMeta(),s=a.chart.options.startAngle||0,l=a._starts=[],u=a._angles=[],d=o.data;for(a._updateRadius(),o.count=a.countVisibleElements(),e=0,n=r.data.length;e<n;e++)l[e]=s,i=a._computeAngle(e),u[e]=i,s+=i;for(e=0,n=d.length;e<n;++e)d[e]._options=a._resolveDataElementOptions(d[e],e),a.updateElement(d[e],e,t)},_updateRadius:function(){var t=this,e=t.chart,n=e.chartArea,i=e.options,a=Math.min(n.right-n.left,n.bottom-n.top);e.outerRadius=Math.max(a/2,0),e.innerRadius=Math.max(i.cutoutPercentage?e.outerRadius/100*i.cutoutPercentage:1,0),e.radiusLength=(e.outerRadius-e.innerRadius)/e.getVisibleDatasetCount(),t.outerRadius=e.outerRadius-e.radiusLength*t.index,t.innerRadius=t.outerRadius-e.radiusLength},updateElement:function(t,e,n){var i=this,a=i.chart,r=i.getDataset(),o=a.options,s=o.animation,l=a.scale,u=a.data.labels,d=l.xCenter,h=l.yCenter,c=o.startAngle,f=t.hidden?0:l.getDistanceFromCenterForValue(r.data[e]),g=i._starts[e],m=g+(t.hidden?0:i._angles[e]),p=s.animateScale?0:l.getDistanceFromCenterForValue(r.data[e]),v=t._options||{};H.extend(t,{_datasetIndex:i.index,_index:e,_scale:l,_model:{backgroundColor:v.backgroundColor,borderColor:v.borderColor,borderWidth:v.borderWidth,borderAlign:v.borderAlign,x:d,y:h,innerRadius:0,outerRadius:n?p:f,startAngle:n&&s.animateRotate?c:g,endAngle:n&&s.animateRotate?c:m,label:H.valueAtIndexOrDefault(u,e,u[e])}}),t.pivot()},countVisibleElements:function(){var t=this.getDataset(),e=this.getMeta(),n=0;return H.each(e.data,(function(e,i){isNaN(t.data[i])||e.hidden||n++})),n},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor,a=H.valueOrDefault;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth},e.backgroundColor=a(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=a(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=a(n.hoverBorderWidth,n.borderWidth)},_computeAngle:function(t){var e=this,n=this.getMeta().count,i=e.getDataset(),a=e.getMeta();if(isNaN(i.data[t])||a.data[t].hidden)return 0;var r={chart:e.chart,dataIndex:t,dataset:i,datasetIndex:e.index};return qt([e.chart.options.elements.arc.angle,2*Math.PI/n],r,t)}});W._set("pie",H.clone(W.doughnut)),W._set("pie",{cutoutPercentage:0});var $t=Yt,Xt=H.valueOrDefault;W._set("radar",{spanGaps:!1,scale:{type:"radialLinear"},elements:{line:{fill:"start",tension:0}}});var Kt=it.extend({datasetElementType:wt.Line,dataElementType:wt.Point,linkScales:H.noop,_datasetElementOptions:["backgroundColor","borderWidth","borderColor","borderCapStyle","borderDash","borderDashOffset","borderJoinStyle","fill"],_dataElementOptions:{backgroundColor:"pointBackgroundColor",borderColor:"pointBorderColor",borderWidth:"pointBorderWidth",hitRadius:"pointHitRadius",hoverBackgroundColor:"pointHoverBackgroundColor",hoverBorderColor:"pointHoverBorderColor",hoverBorderWidth:"pointHoverBorderWidth",hoverRadius:"pointHoverRadius",pointStyle:"pointStyle",radius:"pointRadius",rotation:"pointRotation"},_getIndexScaleId:function(){return this.chart.scale.id},_getValueScaleId:function(){return this.chart.scale.id},update:function(t){var e,n,i=this,a=i.getMeta(),r=a.dataset,o=a.data||[],s=i.chart.scale,l=i._config;for(void 0!==l.tension&&void 0===l.lineTension&&(l.lineTension=l.tension),r._scale=s,r._datasetIndex=i.index,r._children=o,r._loop=!0,r._model=i._resolveDatasetElementOptions(r),r.pivot(),e=0,n=o.length;e<n;++e)i.updateElement(o[e],e,t);for(i.updateBezierControlPoints(),e=0,n=o.length;e<n;++e)o[e].pivot()},updateElement:function(t,e,n){var i=this,a=t.custom||{},r=i.getDataset(),o=i.chart.scale,s=o.getPointPositionForValue(e,r.data[e]),l=i._resolveDataElementOptions(t,e),u=i.getMeta().dataset._model,d=n?o.xCenter:s.x,h=n?o.yCenter:s.y;t._scale=o,t._options=l,t._datasetIndex=i.index,t._index=e,t._model={x:d,y:h,skip:a.skip||isNaN(d)||isNaN(h),radius:l.radius,pointStyle:l.pointStyle,rotation:l.rotation,backgroundColor:l.backgroundColor,borderColor:l.borderColor,borderWidth:l.borderWidth,tension:Xt(a.tension,u?u.tension:0),hitRadius:l.hitRadius}},_resolveDatasetElementOptions:function(){var t=this,e=t._config,n=t.chart.options,i=it.prototype._resolveDatasetElementOptions.apply(t,arguments);return i.spanGaps=Xt(e.spanGaps,n.spanGaps),i.tension=Xt(e.lineTension,n.elements.line.tension),i},updateBezierControlPoints:function(){var t,e,n,i,a=this.getMeta(),r=this.chart.chartArea,o=a.data||[];function s(t,e,n){return Math.max(Math.min(t,n),e)}for(a.dataset._model.spanGaps&&(o=o.filter((function(t){return!t._model.skip}))),t=0,e=o.length;t<e;++t)n=o[t]._model,i=H.splineCurve(H.previousItem(o,t,!0)._model,n,H.nextItem(o,t,!0)._model,n.tension),n.controlPointPreviousX=s(i.previous.x,r.left,r.right),n.controlPointPreviousY=s(i.previous.y,r.top,r.bottom),n.controlPointNextX=s(i.next.x,r.left,r.right),n.controlPointNextY=s(i.next.y,r.top,r.bottom)},setHoverStyle:function(t){var e=t._model,n=t._options,i=H.getHoverColor;t.$previousStyle={backgroundColor:e.backgroundColor,borderColor:e.borderColor,borderWidth:e.borderWidth,radius:e.radius},e.backgroundColor=Xt(n.hoverBackgroundColor,i(n.backgroundColor)),e.borderColor=Xt(n.hoverBorderColor,i(n.borderColor)),e.borderWidth=Xt(n.hoverBorderWidth,n.borderWidth),e.radius=Xt(n.hoverRadius,n.radius)}});W._set("scatter",{hover:{mode:"single"},scales:{xAxes:[{id:"x-axis-1",type:"linear",position:"bottom"}],yAxes:[{id:"y-axis-1",type:"linear",position:"left"}]},tooltips:{callbacks:{title:function(){return""},label:function(t){return"("+t.xLabel+", "+t.yLabel+")"}}}}),W._set("global",{datasets:{scatter:{showLine:!1}}});var Jt={bar:Ot,bubble:It,doughnut:Yt,horizontalBar:zt,line:Gt,polarArea:Zt,pie:$t,radar:Kt,scatter:Gt};function Qt(t,e){return t.native?{x:t.x,y:t.y}:H.getRelativePosition(t,e)}function te(t,e){var n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas();for(i=0,r=l.length;i<r;++i)for(a=0,o=(n=l[i].data).length;a<o;++a)(s=n[a])._view.skip||e(s)}function ee(t,e){var n=[];return te(t,(function(t){t.inRange(e.x,e.y)&&n.push(t)})),n}function ne(t,e,n,i){var a=Number.POSITIVE_INFINITY,r=[];return te(t,(function(t){if(!n||t.inRange(e.x,e.y)){var o=t.getCenterPoint(),s=i(e,o);s<a?(r=[t],a=s):s===a&&r.push(t)}})),r}function ie(t){var e=-1!==t.indexOf("x"),n=-1!==t.indexOf("y");return function(t,i){var a=e?Math.abs(t.x-i.x):0,r=n?Math.abs(t.y-i.y):0;return Math.sqrt(Math.pow(a,2)+Math.pow(r,2))}}function ae(t,e,n){var i=Qt(e,t);n.axis=n.axis||"x";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a),o=[];return r.length?(t._getSortedVisibleDatasetMetas().forEach((function(t){var e=t.data[r[0]._index];e&&!e._view.skip&&o.push(e)})),o):[]}var re={modes:{single:function(t,e){var n=Qt(e,t),i=[];return te(t,(function(t){if(t.inRange(n.x,n.y))return i.push(t),i})),i.slice(0,1)},label:ae,index:ae,dataset:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis),r=n.intersect?ee(t,i):ne(t,i,!1,a);return r.length>0&&(r=t.getDatasetMeta(r[0]._datasetIndex).data),r},"x-axis":function(t,e){return ae(t,e,{intersect:!1})},point:function(t,e){return ee(t,Qt(e,t))},nearest:function(t,e,n){var i=Qt(e,t);n.axis=n.axis||"xy";var a=ie(n.axis);return ne(t,i,n.intersect,a)},x:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inXRange(i.x)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a},y:function(t,e,n){var i=Qt(e,t),a=[],r=!1;return te(t,(function(t){t.inYRange(i.y)&&a.push(t),t.inRange(i.x,i.y)&&(r=!0)})),n.intersect&&!r&&(a=[]),a}}},oe=H.extend;function se(t,e){return H.where(t,(function(t){return t.pos===e}))}function le(t,e){return t.sort((function(t,n){var i=e?n:t,a=e?t:n;return i.weight===a.weight?i.index-a.index:i.weight-a.weight}))}function ue(t,e,n,i){return Math.max(t[n],e[n])+Math.max(t[i],e[i])}function de(t,e,n){var i,a,r=n.box,o=t.maxPadding;if(n.size&&(t[n.pos]-=n.size),n.size=n.horizontal?r.height:r.width,t[n.pos]+=n.size,r.getPadding){var s=r.getPadding();o.top=Math.max(o.top,s.top),o.left=Math.max(o.left,s.left),o.bottom=Math.max(o.bottom,s.bottom),o.right=Math.max(o.right,s.right)}if(i=e.outerWidth-ue(o,t,"left","right"),a=e.outerHeight-ue(o,t,"top","bottom"),i!==t.w||a!==t.h)return t.w=i,t.h=a,n.horizontal?i!==t.w:a!==t.h}function he(t,e){var n=e.maxPadding;function i(t){var i={left:0,top:0,right:0,bottom:0};return t.forEach((function(t){i[t]=Math.max(e[t],n[t])})),i}return i(t?["left","right"]:["top","bottom"])}function ce(t,e,n){var i,a,r,o,s,l,u=[];for(i=0,a=t.length;i<a;++i)(o=(r=t[i]).box).update(r.width||e.w,r.height||e.h,he(r.horizontal,e)),de(e,n,r)&&(l=!0,u.length&&(s=!0)),o.fullWidth||u.push(r);return s&&ce(u,e,n)||l}function fe(t,e,n){var i,a,r,o,s=n.padding,l=e.x,u=e.y;for(i=0,a=t.length;i<a;++i)o=(r=t[i]).box,r.horizontal?(o.left=o.fullWidth?s.left:e.left,o.right=o.fullWidth?n.outerWidth-s.right:e.left+e.w,o.top=u,o.bottom=u+o.height,o.width=o.right-o.left,u=o.bottom):(o.left=l,o.right=l+o.width,o.top=e.top,o.bottom=e.top+e.h,o.height=o.bottom-o.top,l=o.right);e.x=l,e.y=u}W._set("global",{layout:{padding:{top:0,right:0,bottom:0,left:0}}});var ge,me={defaults:{},addBox:function(t,e){t.boxes||(t.boxes=[]),e.fullWidth=e.fullWidth||!1,e.position=e.position||"top",e.weight=e.weight||0,e._layers=e._layers||function(){return[{z:0,draw:function(){e.draw.apply(e,arguments)}}]},t.boxes.push(e)},removeBox:function(t,e){var n=t.boxes?t.boxes.indexOf(e):-1;-1!==n&&t.boxes.splice(n,1)},configure:function(t,e,n){for(var i,a=["fullWidth","position","weight"],r=a.length,o=0;o<r;++o)i=a[o],n.hasOwnProperty(i)&&(e[i]=n[i])},update:function(t,e,n){if(t){var i=t.options.layout||{},a=H.options.toPadding(i.padding),r=e-a.width,o=n-a.height,s=function(t){var e=function(t){var e,n,i,a=[];for(e=0,n=(t||[]).length;e<n;++e)i=t[e],a.push({index:e,box:i,pos:i.position,horizontal:i.isHorizontal(),weight:i.weight});return a}(t),n=le(se(e,"left"),!0),i=le(se(e,"right")),a=le(se(e,"top"),!0),r=le(se(e,"bottom"));return{leftAndTop:n.concat(a),rightAndBottom:i.concat(r),chartArea:se(e,"chartArea"),vertical:n.concat(i),horizontal:a.concat(r)}}(t.boxes),l=s.vertical,u=s.horizontal,d=Object.freeze({outerWidth:e,outerHeight:n,padding:a,availableWidth:r,vBoxMaxWidth:r/2/l.length,hBoxMaxHeight:o/2}),h=oe({maxPadding:oe({},a),w:r,h:o,x:a.left,y:a.top},a);!function(t,e){var n,i,a;for(n=0,i=t.length;n<i;++n)(a=t[n]).width=a.horizontal?a.box.fullWidth&&e.availableWidth:e.vBoxMaxWidth,a.height=a.horizontal&&e.hBoxMaxHeight}(l.concat(u),d),ce(l,h,d),ce(u,h,d)&&ce(l,h,d),function(t){var e=t.maxPadding;function n(n){var i=Math.max(e[n]-t[n],0);return t[n]+=i,i}t.y+=n("top"),t.x+=n("left"),n("right"),n("bottom")}(h),fe(s.leftAndTop,h,d),h.x+=h.w,h.y+=h.h,fe(s.rightAndBottom,h,d),t.chartArea={left:h.left,top:h.top,right:h.left+h.w,bottom:h.top+h.h},H.each(s.chartArea,(function(e){var n=e.box;oe(n,t.chartArea),n.update(h.w,h.h)}))}}},pe=(ge=Object.freeze({__proto__:null,default:"@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}"}))&&ge.default||ge,ve="$chartjs",be="chartjs-size-monitor",ye="chartjs-render-monitor",xe="chartjs-render-animation",_e=["animationstart","webkitAnimationStart"],we={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"};function ke(t,e){var n=H.getStyle(t,e),i=n&&n.match(/^(\d+)(\.\d+)?px$/);return i?Number(i[1]):void 0}var Me=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){t=!0}});window.addEventListener("e",null,e)}catch(t){}return t}()&&{passive:!0};function Se(t,e,n){t.addEventListener(e,n,Me)}function De(t,e,n){t.removeEventListener(e,n,Me)}function Ce(t,e,n,i,a){return{type:t,chart:e,native:a||null,x:void 0!==n?n:null,y:void 0!==i?i:null}}function Pe(t){var e=document.createElement("div");return e.className=t||"",e}function Te(t,e,n){var i,a,r,o,s=t[ve]||(t[ve]={}),l=s.resizer=function(t){var e=Pe(be),n=Pe(be+"-expand"),i=Pe(be+"-shrink");n.appendChild(Pe()),i.appendChild(Pe()),e.appendChild(n),e.appendChild(i),e._reset=function(){n.scrollLeft=1e6,n.scrollTop=1e6,i.scrollLeft=1e6,i.scrollTop=1e6};var a=function(){e._reset(),t()};return Se(n,"scroll",a.bind(n,"expand")),Se(i,"scroll",a.bind(i,"shrink")),e}((i=function(){if(s.resizer){var i=n.options.maintainAspectRatio&&t.parentNode,a=i?i.clientWidth:0;e(Ce("resize",n)),i&&i.clientWidth<a&&n.canvas&&e(Ce("resize",n))}},r=!1,o=[],function(){o=Array.prototype.slice.call(arguments),a=a||this,r||(r=!0,H.requestAnimFrame.call(window,(function(){r=!1,i.apply(a,o)})))}));!function(t,e){var n=t[ve]||(t[ve]={}),i=n.renderProxy=function(t){t.animationName===xe&&e()};H.each(_e,(function(e){Se(t,e,i)})),n.reflow=!!t.offsetParent,t.classList.add(ye)}(t,(function(){if(s.resizer){var e=t.parentNode;e&&e!==l.parentNode&&e.insertBefore(l,e.firstChild),l._reset()}}))}function Oe(t){var e=t[ve]||{},n=e.resizer;delete e.resizer,function(t){var e=t[ve]||{},n=e.renderProxy;n&&(H.each(_e,(function(e){De(t,e,n)})),delete e.renderProxy),t.classList.remove(ye)}(t),n&&n.parentNode&&n.parentNode.removeChild(n)}var Ae={disableCSSInjection:!1,_enabled:"undefined"!=typeof window&&"undefined"!=typeof document,_ensureLoaded:function(t){if(!this.disableCSSInjection){var e=t.getRootNode?t.getRootNode():document;!function(t,e){var n=t[ve]||(t[ve]={});if(!n.containsStyles){n.containsStyles=!0,e="/* Chart.js */\n"+e;var i=document.createElement("style");i.setAttribute("type","text/css"),i.appendChild(document.createTextNode(e)),t.appendChild(i)}}(e.host?e:document.head,pe)}},acquireContext:function(t,e){"string"==typeof t?t=document.getElementById(t):t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas);var n=t&&t.getContext&&t.getContext("2d");return n&&n.canvas===t?(this._ensureLoaded(t),function(t,e){var n=t.style,i=t.getAttribute("height"),a=t.getAttribute("width");if(t[ve]={initial:{height:i,width:a,style:{display:n.display,height:n.height,width:n.width}}},n.display=n.display||"block",null===a||""===a){var r=ke(t,"width");void 0!==r&&(t.width=r)}if(null===i||""===i)if(""===t.style.height)t.height=t.width/(e.options.aspectRatio||2);else{var o=ke(t,"height");void 0!==r&&(t.height=o)}}(t,e),n):null},releaseContext:function(t){var e=t.canvas;if(e[ve]){var n=e[ve].initial;["height","width"].forEach((function(t){var i=n[t];H.isNullOrUndef(i)?e.removeAttribute(t):e.setAttribute(t,i)})),H.each(n.style||{},(function(t,n){e.style[n]=t})),e.width=e.width,delete e[ve]}},addEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=n[ve]||(n[ve]={});Se(i,e,(a.proxies||(a.proxies={}))[t.id+"_"+e]=function(e){n(function(t,e){var n=we[t.type]||t.type,i=H.getRelativePosition(t,e);return Ce(n,e,i.x,i.y,t)}(e,t))})}else Te(i,n,t)},removeEventListener:function(t,e,n){var i=t.canvas;if("resize"!==e){var a=((n[ve]||{}).proxies||{})[t.id+"_"+e];a&&De(i,e,a)}else Oe(i)}};H.addEvent=Se,H.removeEvent=De;var Fe=Ae._enabled?Ae:{acquireContext:function(t){return t&&t.canvas&&(t=t.canvas),t&&t.getContext("2d")||null}},Ie=H.extend({initialize:function(){},acquireContext:function(){},releaseContext:function(){},addEventListener:function(){},removeEventListener:function(){}},Fe);W._set("global",{plugins:{}});var Le={_plugins:[],_cacheId:0,register:function(t){var e=this._plugins;[].concat(t).forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),this._cacheId++},unregister:function(t){var e=this._plugins;[].concat(t).forEach((function(t){var n=e.indexOf(t);-1!==n&&e.splice(n,1)})),this._cacheId++},clear:function(){this._plugins=[],this._cacheId++},count:function(){return this._plugins.length},getAll:function(){return this._plugins},notify:function(t,e,n){var i,a,r,o,s,l=this.descriptors(t),u=l.length;for(i=0;i<u;++i)if("function"==typeof(s=(r=(a=l[i]).plugin)[e])&&((o=[t].concat(n||[])).push(a.options),!1===s.apply(r,o)))return!1;return!0},descriptors:function(t){var e=t.$plugins||(t.$plugins={});if(e.id===this._cacheId)return e.descriptors;var n=[],i=[],a=t&&t.config||{},r=a.options&&a.options.plugins||{};return this._plugins.concat(a.plugins||[]).forEach((function(t){if(-1===n.indexOf(t)){var e=t.id,a=r[e];!1!==a&&(!0===a&&(a=H.clone(W.global.plugins[e])),n.push(t),i.push({plugin:t,options:a||{}}))}})),e.descriptors=i,e.id=this._cacheId,i},_invalidate:function(t){delete t.$plugins}},Re={constructors:{},defaults:{},registerScaleType:function(t,e,n){this.constructors[t]=e,this.defaults[t]=H.clone(n)},getScaleConstructor:function(t){return this.constructors.hasOwnProperty(t)?this.constructors[t]:void 0},getScaleDefaults:function(t){return this.defaults.hasOwnProperty(t)?H.merge({},[W.scale,this.defaults[t]]):{}},updateScaleDefaults:function(t,e){this.defaults.hasOwnProperty(t)&&(this.defaults[t]=H.extend(this.defaults[t],e))},addScalesToLayout:function(t){H.each(t.scales,(function(e){e.fullWidth=e.options.fullWidth,e.position=e.options.position,e.weight=e.options.weight,me.addBox(t,e)}))}},Ne=H.valueOrDefault,We=H.rtl.getRtlAdapter;W._set("global",{tooltips:{enabled:!0,custom:null,mode:"nearest",position:"average",intersect:!0,backgroundColor:"rgba(0,0,0,0.8)",titleFontStyle:"bold",titleSpacing:2,titleMarginBottom:6,titleFontColor:"#fff",titleAlign:"left",bodySpacing:2,bodyFontColor:"#fff",bodyAlign:"left",footerFontStyle:"bold",footerSpacing:2,footerMarginTop:6,footerFontColor:"#fff",footerAlign:"left",yPadding:6,xPadding:6,caretPadding:2,caretSize:5,cornerRadius:6,multiKeyBackground:"#fff",displayColors:!0,borderColor:"rgba(0,0,0,0)",borderWidth:0,callbacks:{beforeTitle:H.noop,title:function(t,e){var n="",i=e.labels,a=i?i.length:0;if(t.length>0){var r=t[0];r.label?n=r.label:r.xLabel?n=r.xLabel:a>0&&r.index<a&&(n=i[r.index])}return n},afterTitle:H.noop,beforeBody:H.noop,beforeLabel:H.noop,label:function(t,e){var n=e.datasets[t.datasetIndex].label||"";return n&&(n+=": "),H.isNullOrUndef(t.value)?n+=t.yLabel:n+=t.value,n},labelColor:function(t,e){var n=e.getDatasetMeta(t.datasetIndex).data[t.index]._view;return{borderColor:n.borderColor,backgroundColor:n.backgroundColor}},labelTextColor:function(){return this._options.bodyFontColor},afterLabel:H.noop,afterBody:H.noop,beforeFooter:H.noop,footer:H.noop,afterFooter:H.noop}}});var Ye={average:function(t){if(!t.length)return!1;var e,n,i=0,a=0,r=0;for(e=0,n=t.length;e<n;++e){var o=t[e];if(o&&o.hasValue()){var s=o.tooltipPosition();i+=s.x,a+=s.y,++r}}return{x:i/r,y:a/r}},nearest:function(t,e){var n,i,a,r=e.x,o=e.y,s=Number.POSITIVE_INFINITY;for(n=0,i=t.length;n<i;++n){var l=t[n];if(l&&l.hasValue()){var u=l.getCenterPoint(),d=H.distanceBetweenPoints(e,u);d<s&&(s=d,a=l)}}if(a){var h=a.tooltipPosition();r=h.x,o=h.y}return{x:r,y:o}}};function ze(t,e){return e&&(H.isArray(e)?Array.prototype.push.apply(t,e):t.push(e)),t}function Ee(t){return("string"==typeof t||t instanceof String)&&t.indexOf("\n")>-1?t.split("\n"):t}function Ve(t){var e=W.global;return{xPadding:t.xPadding,yPadding:t.yPadding,xAlign:t.xAlign,yAlign:t.yAlign,rtl:t.rtl,textDirection:t.textDirection,bodyFontColor:t.bodyFontColor,_bodyFontFamily:Ne(t.bodyFontFamily,e.defaultFontFamily),_bodyFontStyle:Ne(t.bodyFontStyle,e.defaultFontStyle),_bodyAlign:t.bodyAlign,bodyFontSize:Ne(t.bodyFontSize,e.defaultFontSize),bodySpacing:t.bodySpacing,titleFontColor:t.titleFontColor,_titleFontFamily:Ne(t.titleFontFamily,e.defaultFontFamily),_titleFontStyle:Ne(t.titleFontStyle,e.defaultFontStyle),titleFontSize:Ne(t.titleFontSize,e.defaultFontSize),_titleAlign:t.titleAlign,titleSpacing:t.titleSpacing,titleMarginBottom:t.titleMarginBottom,footerFontColor:t.footerFontColor,_footerFontFamily:Ne(t.footerFontFamily,e.defaultFontFamily),_footerFontStyle:Ne(t.footerFontStyle,e.defaultFontStyle),footerFontSize:Ne(t.footerFontSize,e.defaultFontSize),_footerAlign:t.footerAlign,footerSpacing:t.footerSpacing,footerMarginTop:t.footerMarginTop,caretSize:t.caretSize,cornerRadius:t.cornerRadius,backgroundColor:t.backgroundColor,opacity:0,legendColorBackground:t.multiKeyBackground,displayColors:t.displayColors,borderColor:t.borderColor,borderWidth:t.borderWidth}}function He(t,e){return"center"===e?t.x+t.width/2:"right"===e?t.x+t.width-t.xPadding:t.x+t.xPadding}function Be(t){return ze([],Ee(t))}var je=$.extend({initialize:function(){this._model=Ve(this._options),this._lastActive=[]},getTitle:function(){var t=this,e=t._options,n=e.callbacks,i=n.beforeTitle.apply(t,arguments),a=n.title.apply(t,arguments),r=n.afterTitle.apply(t,arguments),o=[];return o=ze(o,Ee(i)),o=ze(o,Ee(a)),o=ze(o,Ee(r))},getBeforeBody:function(){return Be(this._options.callbacks.beforeBody.apply(this,arguments))},getBody:function(t,e){var n=this,i=n._options.callbacks,a=[];return H.each(t,(function(t){var r={before:[],lines:[],after:[]};ze(r.before,Ee(i.beforeLabel.call(n,t,e))),ze(r.lines,i.label.call(n,t,e)),ze(r.after,Ee(i.afterLabel.call(n,t,e))),a.push(r)})),a},getAfterBody:function(){return Be(this._options.callbacks.afterBody.apply(this,arguments))},getFooter:function(){var t=this,e=t._options.callbacks,n=e.beforeFooter.apply(t,arguments),i=e.footer.apply(t,arguments),a=e.afterFooter.apply(t,arguments),r=[];return r=ze(r,Ee(n)),r=ze(r,Ee(i)),r=ze(r,Ee(a))},update:function(t){var e,n,i,a,r,o,s,l,u,d,h=this,c=h._options,f=h._model,g=h._model=Ve(c),m=h._active,p=h._data,v={xAlign:f.xAlign,yAlign:f.yAlign},b={x:f.x,y:f.y},y={width:f.width,height:f.height},x={x:f.caretX,y:f.caretY};if(m.length){g.opacity=1;var _=[],w=[];x=Ye[c.position].call(h,m,h._eventPosition);var k=[];for(e=0,n=m.length;e<n;++e)k.push((i=m[e],a=void 0,r=void 0,o=void 0,s=void 0,l=void 0,u=void 0,d=void 0,a=i._xScale,r=i._yScale||i._scale,o=i._index,s=i._datasetIndex,l=i._chart.getDatasetMeta(s).controller,u=l._getIndexScale(),d=l._getValueScale(),{xLabel:a?a.getLabelForIndex(o,s):"",yLabel:r?r.getLabelForIndex(o,s):"",label:u?""+u.getLabelForIndex(o,s):"",value:d?""+d.getLabelForIndex(o,s):"",index:o,datasetIndex:s,x:i._model.x,y:i._model.y}));c.filter&&(k=k.filter((function(t){return c.filter(t,p)}))),c.itemSort&&(k=k.sort((function(t,e){return c.itemSort(t,e,p)}))),H.each(k,(function(t){_.push(c.callbacks.labelColor.call(h,t,h._chart)),w.push(c.callbacks.labelTextColor.call(h,t,h._chart))})),g.title=h.getTitle(k,p),g.beforeBody=h.getBeforeBody(k,p),g.body=h.getBody(k,p),g.afterBody=h.getAfterBody(k,p),g.footer=h.getFooter(k,p),g.x=x.x,g.y=x.y,g.caretPadding=c.caretPadding,g.labelColors=_,g.labelTextColors=w,g.dataPoints=k,y=function(t,e){var n=t._chart.ctx,i=2*e.yPadding,a=0,r=e.body,o=r.reduce((function(t,e){return t+e.before.length+e.lines.length+e.after.length}),0);o+=e.beforeBody.length+e.afterBody.length;var s=e.title.length,l=e.footer.length,u=e.titleFontSize,d=e.bodyFontSize,h=e.footerFontSize;i+=s*u,i+=s?(s-1)*e.titleSpacing:0,i+=s?e.titleMarginBottom:0,i+=o*d,i+=o?(o-1)*e.bodySpacing:0,i+=l?e.footerMarginTop:0,i+=l*h,i+=l?(l-1)*e.footerSpacing:0;var c=0,f=function(t){a=Math.max(a,n.measureText(t).width+c)};return n.font=H.fontString(u,e._titleFontStyle,e._titleFontFamily),H.each(e.title,f),n.font=H.fontString(d,e._bodyFontStyle,e._bodyFontFamily),H.each(e.beforeBody.concat(e.afterBody),f),c=e.displayColors?d+2:0,H.each(r,(function(t){H.each(t.before,f),H.each(t.lines,f),H.each(t.after,f)})),c=0,n.font=H.fontString(h,e._footerFontStyle,e._footerFontFamily),H.each(e.footer,f),{width:a+=2*e.xPadding,height:i}}(this,g),b=function(t,e,n,i){var a=t.x,r=t.y,o=t.caretSize,s=t.caretPadding,l=t.cornerRadius,u=n.xAlign,d=n.yAlign,h=o+s,c=l+s;return"right"===u?a-=e.width:"center"===u&&((a-=e.width/2)+e.width>i.width&&(a=i.width-e.width),a<0&&(a=0)),"top"===d?r+=h:r-="bottom"===d?e.height+h:e.height/2,"center"===d?"left"===u?a+=h:"right"===u&&(a-=h):"left"===u?a-=c:"right"===u&&(a+=c),{x:a,y:r}}(g,y,v=function(t,e){var n,i,a,r,o,s=t._model,l=t._chart,u=t._chart.chartArea,d="center",h="center";s.y<e.height?h="top":s.y>l.height-e.height&&(h="bottom");var c=(u.left+u.right)/2,f=(u.top+u.bottom)/2;"center"===h?(n=function(t){return t<=c},i=function(t){return t>c}):(n=function(t){return t<=e.width/2},i=function(t){return t>=l.width-e.width/2}),a=function(t){return t+e.width+s.caretSize+s.caretPadding>l.width},r=function(t){return t-e.width-s.caretSize-s.caretPadding<0},o=function(t){return t<=f?"top":"bottom"},n(s.x)?(d="left",a(s.x)&&(d="center",h=o(s.y))):i(s.x)&&(d="right",r(s.x)&&(d="center",h=o(s.y)));var g=t._options;return{xAlign:g.xAlign?g.xAlign:d,yAlign:g.yAlign?g.yAlign:h}}(this,y),h._chart)}else g.opacity=0;return g.xAlign=v.xAlign,g.yAlign=v.yAlign,g.x=b.x,g.y=b.y,g.width=y.width,g.height=y.height,g.caretX=x.x,g.caretY=x.y,h._model=g,t&&c.custom&&c.custom.call(h,g),h},drawCaret:function(t,e){var n=this._chart.ctx,i=this._view,a=this.getCaretPosition(t,e,i);n.lineTo(a.x1,a.y1),n.lineTo(a.x2,a.y2),n.lineTo(a.x3,a.y3)},getCaretPosition:function(t,e,n){var i,a,r,o,s,l,u=n.caretSize,d=n.cornerRadius,h=n.xAlign,c=n.yAlign,f=t.x,g=t.y,m=e.width,p=e.height;if("center"===c)s=g+p/2,"left"===h?(a=(i=f)-u,r=i,o=s+u,l=s-u):(a=(i=f+m)+u,r=i,o=s-u,l=s+u);else if("left"===h?(i=(a=f+d+u)-u,r=a+u):"right"===h?(i=(a=f+m-d-u)-u,r=a+u):(i=(a=n.caretX)-u,r=a+u),"top"===c)s=(o=g)-u,l=o;else{s=(o=g+p)+u,l=o;var v=r;r=i,i=v}return{x1:i,x2:a,x3:r,y1:o,y2:s,y3:l}},drawTitle:function(t,e,n){var i,a,r,o=e.title,s=o.length;if(s){var l=We(e.rtl,e.x,e.width);for(t.x=He(e,e._titleAlign),n.textAlign=l.textAlign(e._titleAlign),n.textBaseline="middle",i=e.titleFontSize,a=e.titleSpacing,n.fillStyle=e.titleFontColor,n.font=H.fontString(i,e._titleFontStyle,e._titleFontFamily),r=0;r<s;++r)n.fillText(o[r],l.x(t.x),t.y+i/2),t.y+=i+a,r+1===s&&(t.y+=e.titleMarginBottom-a)}},drawBody:function(t,e,n){var i,a,r,o,s,l,u,d,h=e.bodyFontSize,c=e.bodySpacing,f=e._bodyAlign,g=e.body,m=e.displayColors,p=0,v=m?He(e,"left"):0,b=We(e.rtl,e.x,e.width),y=function(e){n.fillText(e,b.x(t.x+p),t.y+h/2),t.y+=h+c},x=b.textAlign(f);for(n.textAlign=f,n.textBaseline="middle",n.font=H.fontString(h,e._bodyFontStyle,e._bodyFontFamily),t.x=He(e,x),n.fillStyle=e.bodyFontColor,H.each(e.beforeBody,y),p=m&&"right"!==x?"center"===f?h/2+1:h+2:0,s=0,u=g.length;s<u;++s){for(i=g[s],a=e.labelTextColors[s],r=e.labelColors[s],n.fillStyle=a,H.each(i.before,y),l=0,d=(o=i.lines).length;l<d;++l){if(m){var _=b.x(v);n.fillStyle=e.legendColorBackground,n.fillRect(b.leftForLtr(_,h),t.y,h,h),n.lineWidth=1,n.strokeStyle=r.borderColor,n.strokeRect(b.leftForLtr(_,h),t.y,h,h),n.fillStyle=r.backgroundColor,n.fillRect(b.leftForLtr(b.xPlus(_,1),h-2),t.y+1,h-2,h-2),n.fillStyle=a}y(o[l])}H.each(i.after,y)}p=0,H.each(e.afterBody,y),t.y-=c},drawFooter:function(t,e,n){var i,a,r=e.footer,o=r.length;if(o){var s=We(e.rtl,e.x,e.width);for(t.x=He(e,e._footerAlign),t.y+=e.footerMarginTop,n.textAlign=s.textAlign(e._footerAlign),n.textBaseline="middle",i=e.footerFontSize,n.fillStyle=e.footerFontColor,n.font=H.fontString(i,e._footerFontStyle,e._footerFontFamily),a=0;a<o;++a)n.fillText(r[a],s.x(t.x),t.y+i/2),t.y+=i+e.footerSpacing}},drawBackground:function(t,e,n,i){n.fillStyle=e.backgroundColor,n.strokeStyle=e.borderColor,n.lineWidth=e.borderWidth;var a=e.xAlign,r=e.yAlign,o=t.x,s=t.y,l=i.width,u=i.height,d=e.cornerRadius;n.beginPath(),n.moveTo(o+d,s),"top"===r&&this.drawCaret(t,i),n.lineTo(o+l-d,s),n.quadraticCurveTo(o+l,s,o+l,s+d),"center"===r&&"right"===a&&this.drawCaret(t,i),n.lineTo(o+l,s+u-d),n.quadraticCurveTo(o+l,s+u,o+l-d,s+u),"bottom"===r&&this.drawCaret(t,i),n.lineTo(o+d,s+u),n.quadraticCurveTo(o,s+u,o,s+u-d),"center"===r&&"left"===a&&this.drawCaret(t,i),n.lineTo(o,s+d),n.quadraticCurveTo(o,s,o+d,s),n.closePath(),n.fill(),e.borderWidth>0&&n.stroke()},draw:function(){var t=this._chart.ctx,e=this._view;if(0!==e.opacity){var n={width:e.width,height:e.height},i={x:e.x,y:e.y},a=Math.abs(e.opacity<.001)?0:e.opacity,r=e.title.length||e.beforeBody.length||e.body.length||e.afterBody.length||e.footer.length;this._options.enabled&&r&&(t.save(),t.globalAlpha=a,this.drawBackground(i,e,t,n),i.y+=e.yPadding,H.rtl.overrideTextDirection(t,e.textDirection),this.drawTitle(i,e,t),this.drawBody(i,e,t),this.drawFooter(i,e,t),H.rtl.restoreTextDirection(t,e.textDirection),t.restore())}},handleEvent:function(t){var e,n=this,i=n._options;return n._lastActive=n._lastActive||[],"mouseout"===t.type?n._active=[]:(n._active=n._chart.getElementsAtEventForMode(t,i.mode,i),i.reverse&&n._active.reverse()),(e=!H.arrayEquals(n._active,n._lastActive))&&(n._lastActive=n._active,(i.enabled||i.custom)&&(n._eventPosition={x:t.x,y:t.y},n.update(!0),n.pivot())),e}}),Ue=Ye,Ge=je;Ge.positioners=Ue;var qe=H.valueOrDefault;function Ze(){return H.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){if("xAxes"===t||"yAxes"===t){var a,r,o,s=n[t].length;for(e[t]||(e[t]=[]),a=0;a<s;++a)o=n[t][a],r=qe(o.type,"xAxes"===t?"category":"linear"),a>=e[t].length&&e[t].push({}),!e[t][a].type||o.type&&o.type!==e[t][a].type?H.merge(e[t][a],[Re.getScaleDefaults(r),o]):H.merge(e[t][a],o)}else H._merger(t,e,n,i)}})}function $e(){return H.merge({},[].slice.call(arguments),{merger:function(t,e,n,i){var a=e[t]||{},r=n[t];"scales"===t?e[t]=Ze(a,r):"scale"===t?e[t]=H.merge(a,[Re.getScaleDefaults(r.type),r]):H._merger(t,e,n,i)}})}function Xe(t){var e=t.options;H.each(t.scales,(function(e){me.removeBox(t,e)})),e=$e(W.global,W[t.config.type],e),t.options=t.config.options=e,t.ensureScalesHaveIDs(),t.buildOrUpdateScales(),t.tooltip._options=e.tooltips,t.tooltip.initialize()}function Ke(t,e,n){var i,a=function(t){return t.id===i};do{i=e+n++}while(H.findIndex(t,a)>=0);return i}function Je(t){return"top"===t||"bottom"===t}function Qe(t,e){return function(n,i){return n[t]===i[t]?n[e]-i[e]:n[t]-i[t]}}W._set("global",{elements:{},events:["mousemove","mouseout","click","touchstart","touchmove"],hover:{onHover:null,mode:"nearest",intersect:!0,animationDuration:400},onClick:null,maintainAspectRatio:!0,responsive:!0,responsiveAnimationDuration:0});var tn=function(t,e){return this.construct(t,e),this};H.extend(tn.prototype,{construct:function(t,e){var n=this;e=function(t){var e=(t=t||{}).data=t.data||{};return e.datasets=e.datasets||[],e.labels=e.labels||[],t.options=$e(W.global,W[t.type],t.options||{}),t}(e);var i=Ie.acquireContext(t,e),a=i&&i.canvas,r=a&&a.height,o=a&&a.width;n.id=H.uid(),n.ctx=i,n.canvas=a,n.config=e,n.width=o,n.height=r,n.aspectRatio=r?o/r:null,n.options=e.options,n._bufferedRender=!1,n._layers=[],n.chart=n,n.controller=n,tn.instances[n.id]=n,Object.defineProperty(n,"data",{get:function(){return n.config.data},set:function(t){n.config.data=t}}),i&&a?(n.initialize(),n.update()):console.error("Failed to create chart: can't acquire context from the given item")},initialize:function(){var t=this;return Le.notify(t,"beforeInit"),H.retinaScale(t,t.options.devicePixelRatio),t.bindEvents(),t.options.responsive&&t.resize(!0),t.initToolTip(),Le.notify(t,"afterInit"),t},clear:function(){return H.canvas.clear(this),this},stop:function(){return J.cancelAnimation(this),this},resize:function(t){var e=this,n=e.options,i=e.canvas,a=n.maintainAspectRatio&&e.aspectRatio||null,r=Math.max(0,Math.floor(H.getMaximumWidth(i))),o=Math.max(0,Math.floor(a?r/a:H.getMaximumHeight(i)));if((e.width!==r||e.height!==o)&&(i.width=e.width=r,i.height=e.height=o,i.style.width=r+"px",i.style.height=o+"px",H.retinaScale(e,n.devicePixelRatio),!t)){var s={width:r,height:o};Le.notify(e,"resize",[s]),n.onResize&&n.onResize(e,s),e.stop(),e.update({duration:n.responsiveAnimationDuration})}},ensureScalesHaveIDs:function(){var t=this.options,e=t.scales||{},n=t.scale;H.each(e.xAxes,(function(t,n){t.id||(t.id=Ke(e.xAxes,"x-axis-",n))})),H.each(e.yAxes,(function(t,n){t.id||(t.id=Ke(e.yAxes,"y-axis-",n))})),n&&(n.id=n.id||"scale")},buildOrUpdateScales:function(){var t=this,e=t.options,n=t.scales||{},i=[],a=Object.keys(n).reduce((function(t,e){return t[e]=!1,t}),{});e.scales&&(i=i.concat((e.scales.xAxes||[]).map((function(t){return{options:t,dtype:"category",dposition:"bottom"}})),(e.scales.yAxes||[]).map((function(t){return{options:t,dtype:"linear",dposition:"left"}})))),e.scale&&i.push({options:e.scale,dtype:"radialLinear",isDefault:!0,dposition:"chartArea"}),H.each(i,(function(e){var i=e.options,r=i.id,o=qe(i.type,e.dtype);Je(i.position)!==Je(e.dposition)&&(i.position=e.dposition),a[r]=!0;var s=null;if(r in n&&n[r].type===o)(s=n[r]).options=i,s.ctx=t.ctx,s.chart=t;else{var l=Re.getScaleConstructor(o);if(!l)return;s=new l({id:r,type:o,options:i,ctx:t.ctx,chart:t}),n[s.id]=s}s.mergeTicksOptions(),e.isDefault&&(t.scale=s)})),H.each(a,(function(t,e){t||delete n[e]})),t.scales=n,Re.addScalesToLayout(this)},buildOrUpdateControllers:function(){var t,e,n=this,i=[],a=n.data.datasets;for(t=0,e=a.length;t<e;t++){var r=a[t],o=n.getDatasetMeta(t),s=r.type||n.config.type;if(o.type&&o.type!==s&&(n.destroyDatasetMeta(t),o=n.getDatasetMeta(t)),o.type=s,o.order=r.order||0,o.index=t,o.controller)o.controller.updateIndex(t),o.controller.linkScales();else{var l=Jt[o.type];if(void 0===l)throw new Error('"'+o.type+'" is not a chart type.');o.controller=new l(n,t),i.push(o.controller)}}return i},resetElements:function(){var t=this;H.each(t.data.datasets,(function(e,n){t.getDatasetMeta(n).controller.reset()}),t)},reset:function(){this.resetElements(),this.tooltip.initialize()},update:function(t){var e,n,i=this;if(t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]}),Xe(i),Le._invalidate(i),!1!==Le.notify(i,"beforeUpdate")){i.tooltip._data=i.data;var a=i.buildOrUpdateControllers();for(e=0,n=i.data.datasets.length;e<n;e++)i.getDatasetMeta(e).controller.buildOrUpdateElements();i.updateLayout(),i.options.animation&&i.options.animation.duration&&H.each(a,(function(t){t.reset()})),i.updateDatasets(),i.tooltip.initialize(),i.lastActive=[],Le.notify(i,"afterUpdate"),i._layers.sort(Qe("z","_idx")),i._bufferedRender?i._bufferedRequest={duration:t.duration,easing:t.easing,lazy:t.lazy}:i.render(t)}},updateLayout:function(){var t=this;!1!==Le.notify(t,"beforeLayout")&&(me.update(this,this.width,this.height),t._layers=[],H.each(t.boxes,(function(e){e._configure&&e._configure(),t._layers.push.apply(t._layers,e._layers())}),t),t._layers.forEach((function(t,e){t._idx=e})),Le.notify(t,"afterScaleUpdate"),Le.notify(t,"afterLayout"))},updateDatasets:function(){if(!1!==Le.notify(this,"beforeDatasetsUpdate")){for(var t=0,e=this.data.datasets.length;t<e;++t)this.updateDataset(t);Le.notify(this,"afterDatasetsUpdate")}},updateDataset:function(t){var e=this.getDatasetMeta(t),n={meta:e,index:t};!1!==Le.notify(this,"beforeDatasetUpdate",[n])&&(e.controller._update(),Le.notify(this,"afterDatasetUpdate",[n]))},render:function(t){var e=this;t&&"object"==typeof t||(t={duration:t,lazy:arguments[1]});var n=e.options.animation,i=qe(t.duration,n&&n.duration),a=t.lazy;if(!1!==Le.notify(e,"beforeRender")){var r=function(t){Le.notify(e,"afterRender"),H.callback(n&&n.onComplete,[t],e)};if(n&&i){var o=new K({numSteps:i/16.66,easing:t.easing||n.easing,render:function(t,e){var n=H.easing.effects[e.easing],i=e.currentStep,a=i/e.numSteps;t.draw(n(a),a,i)},onAnimationProgress:n.onProgress,onAnimationComplete:r});J.addAnimation(e,o,i,a)}else e.draw(),r(new K({numSteps:0,chart:e}));return e}},draw:function(t){var e,n,i=this;if(i.clear(),H.isNullOrUndef(t)&&(t=1),i.transition(t),!(i.width<=0||i.height<=0)&&!1!==Le.notify(i,"beforeDraw",[t])){for(n=i._layers,e=0;e<n.length&&n[e].z<=0;++e)n[e].draw(i.chartArea);for(i.drawDatasets(t);e<n.length;++e)n[e].draw(i.chartArea);i._drawTooltip(t),Le.notify(i,"afterDraw",[t])}},transition:function(t){for(var e=0,n=(this.data.datasets||[]).length;e<n;++e)this.isDatasetVisible(e)&&this.getDatasetMeta(e).controller.transition(t);this.tooltip.transition(t)},_getSortedDatasetMetas:function(t){var e,n,i=[];for(e=0,n=(this.data.datasets||[]).length;e<n;++e)t&&!this.isDatasetVisible(e)||i.push(this.getDatasetMeta(e));return i.sort(Qe("order","index")),i},_getSortedVisibleDatasetMetas:function(){return this._getSortedDatasetMetas(!0)},drawDatasets:function(t){var e,n;if(!1!==Le.notify(this,"beforeDatasetsDraw",[t])){for(n=(e=this._getSortedVisibleDatasetMetas()).length-1;n>=0;--n)this.drawDataset(e[n],t);Le.notify(this,"afterDatasetsDraw",[t])}},drawDataset:function(t,e){var n={meta:t,index:t.index,easingValue:e};!1!==Le.notify(this,"beforeDatasetDraw",[n])&&(t.controller.draw(e),Le.notify(this,"afterDatasetDraw",[n]))},_drawTooltip:function(t){var e=this.tooltip,n={tooltip:e,easingValue:t};!1!==Le.notify(this,"beforeTooltipDraw",[n])&&(e.draw(),Le.notify(this,"afterTooltipDraw",[n]))},getElementAtEvent:function(t){return re.modes.single(this,t)},getElementsAtEvent:function(t){return re.modes.label(this,t,{intersect:!0})},getElementsAtXAxis:function(t){return re.modes["x-axis"](this,t,{intersect:!0})},getElementsAtEventForMode:function(t,e,n){var i=re.modes[e];return"function"==typeof i?i(this,t,n):[]},getDatasetAtEvent:function(t){return re.modes.dataset(this,t,{intersect:!0})},getDatasetMeta:function(t){var e=this.data.datasets[t];e._meta||(e._meta={});var n=e._meta[this.id];return n||(n=e._meta[this.id]={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e.order||0,index:t}),n},getVisibleDatasetCount:function(){for(var t=0,e=0,n=this.data.datasets.length;e<n;++e)this.isDatasetVisible(e)&&t++;return t},isDatasetVisible:function(t){var e=this.getDatasetMeta(t);return"boolean"==typeof e.hidden?!e.hidden:!this.data.datasets[t].hidden},generateLegend:function(){return this.options.legendCallback(this)},destroyDatasetMeta:function(t){var e=this.id,n=this.data.datasets[t],i=n._meta&&n._meta[e];i&&(i.controller.destroy(),delete n._meta[e])},destroy:function(){var t,e,n=this,i=n.canvas;for(n.stop(),t=0,e=n.data.datasets.length;t<e;++t)n.destroyDatasetMeta(t);i&&(n.unbindEvents(),H.canvas.clear(n),Ie.releaseContext(n.ctx),n.canvas=null,n.ctx=null),Le.notify(n,"destroy"),delete tn.instances[n.id]},toBase64Image:function(){return this.canvas.toDataURL.apply(this.canvas,arguments)},initToolTip:function(){var t=this;t.tooltip=new Ge({_chart:t,_chartInstance:t,_data:t.data,_options:t.options.tooltips},t)},bindEvents:function(){var t=this,e=t._listeners={},n=function(){t.eventHandler.apply(t,arguments)};H.each(t.options.events,(function(i){Ie.addEventListener(t,i,n),e[i]=n})),t.options.responsive&&(n=function(){t.resize()},Ie.addEventListener(t,"resize",n),e.resize=n)},unbindEvents:function(){var t=this,e=t._listeners;e&&(delete t._listeners,H.each(e,(function(e,n){Ie.removeEventListener(t,n,e)})))},updateHoverStyle:function(t,e,n){var i,a,r,o=n?"set":"remove";for(a=0,r=t.length;a<r;++a)(i=t[a])&&this.getDatasetMeta(i._datasetIndex).controller[o+"HoverStyle"](i);"dataset"===e&&this.getDatasetMeta(t[0]._datasetIndex).controller["_"+o+"DatasetHoverStyle"]()},eventHandler:function(t){var e=this,n=e.tooltip;if(!1!==Le.notify(e,"beforeEvent",[t])){e._bufferedRender=!0,e._bufferedRequest=null;var i=e.handleEvent(t);n&&(i=n._start?n.handleEvent(t):i|n.handleEvent(t)),Le.notify(e,"afterEvent",[t]);var a=e._bufferedRequest;return a?e.render(a):i&&!e.animating&&(e.stop(),e.render({duration:e.options.hover.animationDuration,lazy:!0})),e._bufferedRender=!1,e._bufferedRequest=null,e}},handleEvent:function(t){var e,n=this,i=n.options||{},a=i.hover;return n.lastActive=n.lastActive||[],"mouseout"===t.type?n.active=[]:n.active=n.getElementsAtEventForMode(t,a.mode,a),H.callback(i.onHover||i.hover.onHover,[t.native,n.active],n),"mouseup"!==t.type&&"click"!==t.type||i.onClick&&i.onClick.call(n,t.native,n.active),n.lastActive.length&&n.updateHoverStyle(n.lastActive,a.mode,!1),n.active.length&&a.mode&&n.updateHoverStyle(n.active,a.mode,!0),e=!H.arrayEquals(n.active,n.lastActive),n.lastActive=n.active,e}}),tn.instances={};var en=tn;tn.Controller=tn,tn.types={},H.configMerge=$e,H.scaleMerge=Ze;function nn(){throw new Error("This method is not implemented: either no adapter can be found or an incomplete integration was provided.")}function an(t){this.options=t||{}}H.extend(an.prototype,{formats:nn,parse:nn,format:nn,add:nn,diff:nn,startOf:nn,endOf:nn,_create:function(t){return t}}),an.override=function(t){H.extend(an.prototype,t)};var rn={_date:an},on={formatters:{values:function(t){return H.isArray(t)?t:""+t},linear:function(t,e,n){var i=n.length>3?n[2]-n[1]:n[1]-n[0];Math.abs(i)>1&&t!==Math.floor(t)&&(i=t-Math.floor(t));var a=H.log10(Math.abs(i)),r="";if(0!==t)if(Math.max(Math.abs(n[0]),Math.abs(n[n.length-1]))<1e-4){var o=H.log10(Math.abs(t)),s=Math.floor(o)-Math.floor(a);s=Math.max(Math.min(s,20),0),r=t.toExponential(s)}else{var l=-1*Math.floor(a);l=Math.max(Math.min(l,20),0),r=t.toFixed(l)}else r="0";return r},logarithmic:function(t,e,n){var i=t/Math.pow(10,Math.floor(H.log10(t)));return 0===t?"0":1===i||2===i||5===i||0===e||e===n.length-1?t.toExponential():""}}},sn=H.isArray,ln=H.isNullOrUndef,un=H.valueOrDefault,dn=H.valueAtIndexOrDefault;function hn(t,e,n){var i,a=t.getTicks().length,r=Math.min(e,a-1),o=t.getPixelForTick(r),s=t._startPixel,l=t._endPixel;if(!(n&&(i=1===a?Math.max(o-s,l-o):0===e?(t.getPixelForTick(1)-o)/2:(o-t.getPixelForTick(r-1))/2,(o+=r<e?i:-i)<s-1e-6||o>l+1e-6)))return o}function cn(t,e,n,i){var a,r,o,s,l,u,d,h,c,f,g,m,p,v=n.length,b=[],y=[],x=[];for(a=0;a<v;++a){if(s=n[a].label,l=n[a].major?e.major:e.minor,t.font=u=l.string,d=i[u]=i[u]||{data:{},gc:[]},h=l.lineHeight,c=f=0,ln(s)||sn(s)){if(sn(s))for(r=0,o=s.length;r<o;++r)g=s[r],ln(g)||sn(g)||(c=H.measureText(t,d.data,d.gc,c,g),f+=h)}else c=H.measureText(t,d.data,d.gc,c,s),f=h;b.push(c),y.push(f),x.push(h/2)}function _(t){return{width:b[t]||0,height:y[t]||0,offset:x[t]||0}}return function(t,e){H.each(t,(function(t){var n,i=t.gc,a=i.length/2;if(a>e){for(n=0;n<a;++n)delete t.data[i[n]];i.splice(0,a)}}))}(i,v),m=b.indexOf(Math.max.apply(null,b)),p=y.indexOf(Math.max.apply(null,y)),{first:_(0),last:_(v-1),widest:_(m),highest:_(p)}}function fn(t){return t.drawTicks?t.tickMarkLength:0}function gn(t){var e,n;return t.display?(e=H.options._parseFont(t),n=H.options.toPadding(t.padding),e.lineHeight+n.height):0}function mn(t,e){return H.extend(H.options._parseFont({fontFamily:un(e.fontFamily,t.fontFamily),fontSize:un(e.fontSize,t.fontSize),fontStyle:un(e.fontStyle,t.fontStyle),lineHeight:un(e.lineHeight,t.lineHeight)}),{color:H.options.resolve([e.fontColor,t.fontColor,W.global.defaultFontColor])})}function pn(t){var e=mn(t,t.minor);return{minor:e,major:t.major.enabled?mn(t,t.major):e}}function vn(t){var e,n,i,a=[];for(n=0,i=t.length;n<i;++n)void 0!==(e=t[n])._index&&a.push(e);return a}function bn(t,e,n,i){var a,r,o,s,l=un(n,0),u=Math.min(un(i,t.length),t.length),d=0;for(e=Math.ceil(e),i&&(e=(a=i-n)/Math.floor(a/e)),s=l;s<0;)d++,s=Math.round(l+d*e);for(r=Math.max(l,0);r<u;r++)o=t[r],r===s?(o._index=r,d++,s=Math.round(l+d*e)):delete o.label}W._set("scale",{display:!0,position:"left",offset:!1,gridLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,drawBorder:!0,drawOnChartArea:!0,drawTicks:!0,tickMarkLength:10,zeroLineWidth:1,zeroLineColor:"rgba(0,0,0,0.25)",zeroLineBorderDash:[],zeroLineBorderDashOffset:0,offsetGridLines:!1,borderDash:[],borderDashOffset:0},scaleLabel:{display:!1,labelString:"",padding:{top:4,bottom:4}},ticks:{beginAtZero:!1,minRotation:0,maxRotation:50,mirror:!1,padding:0,reverse:!1,display:!0,autoSkip:!0,autoSkipPadding:0,labelOffset:0,callback:on.formatters.values,minor:{},major:{}}});var yn=$.extend({zeroLineIndex:0,getPadding:function(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}},getTicks:function(){return this._ticks},_getLabels:function(){var t=this.chart.data;return this.options.labels||(this.isHorizontal()?t.xLabels:t.yLabels)||t.labels||[]},mergeTicksOptions:function(){},beforeUpdate:function(){H.callback(this.options.beforeUpdate,[this])},update:function(t,e,n){var i,a,r,o,s,l=this,u=l.options.ticks,d=u.sampleSize;if(l.beforeUpdate(),l.maxWidth=t,l.maxHeight=e,l.margins=H.extend({left:0,right:0,top:0,bottom:0},n),l._ticks=null,l.ticks=null,l._labelSizes=null,l._maxLabelLines=0,l.longestLabelWidth=0,l.longestTextCache=l.longestTextCache||{},l._gridLineItems=null,l._labelItems=null,l.beforeSetDimensions(),l.setDimensions(),l.afterSetDimensions(),l.beforeDataLimits(),l.determineDataLimits(),l.afterDataLimits(),l.beforeBuildTicks(),o=l.buildTicks()||[],(!(o=l.afterBuildTicks(o)||o)||!o.length)&&l.ticks)for(o=[],i=0,a=l.ticks.length;i<a;++i)o.push({value:l.ticks[i],major:!1});return l._ticks=o,s=d<o.length,r=l._convertTicksToLabels(s?function(t,e){for(var n=[],i=t.length/e,a=0,r=t.length;a<r;a+=i)n.push(t[Math.floor(a)]);return n}(o,d):o),l._configure(),l.beforeCalculateTickRotation(),l.calculateTickRotation(),l.afterCalculateTickRotation(),l.beforeFit(),l.fit(),l.afterFit(),l._ticksToDraw=u.display&&(u.autoSkip||"auto"===u.source)?l._autoSkip(o):o,s&&(r=l._convertTicksToLabels(l._ticksToDraw)),l.ticks=r,l.afterUpdate(),l.minSize},_configure:function(){var t,e,n=this,i=n.options.ticks.reverse;n.isHorizontal()?(t=n.left,e=n.right):(t=n.top,e=n.bottom,i=!i),n._startPixel=t,n._endPixel=e,n._reversePixels=i,n._length=e-t},afterUpdate:function(){H.callback(this.options.afterUpdate,[this])},beforeSetDimensions:function(){H.callback(this.options.beforeSetDimensions,[this])},setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0},afterSetDimensions:function(){H.callback(this.options.afterSetDimensions,[this])},beforeDataLimits:function(){H.callback(this.options.beforeDataLimits,[this])},determineDataLimits:H.noop,afterDataLimits:function(){H.callback(this.options.afterDataLimits,[this])},beforeBuildTicks:function(){H.callback(this.options.beforeBuildTicks,[this])},buildTicks:H.noop,afterBuildTicks:function(t){var e=this;return sn(t)&&t.length?H.callback(e.options.afterBuildTicks,[e,t]):(e.ticks=H.callback(e.options.afterBuildTicks,[e,e.ticks])||e.ticks,t)},beforeTickToLabelConversion:function(){H.callback(this.options.beforeTickToLabelConversion,[this])},convertTicksToLabels:function(){var t=this.options.ticks;this.ticks=this.ticks.map(t.userCallback||t.callback,this)},afterTickToLabelConversion:function(){H.callback(this.options.afterTickToLabelConversion,[this])},beforeCalculateTickRotation:function(){H.callback(this.options.beforeCalculateTickRotation,[this])},calculateTickRotation:function(){var t,e,n,i,a,r,o,s=this,l=s.options,u=l.ticks,d=s.getTicks().length,h=u.minRotation||0,c=u.maxRotation,f=h;!s._isVisible()||!u.display||h>=c||d<=1||!s.isHorizontal()?s.labelRotation=h:(e=(t=s._getLabelSizes()).widest.width,n=t.highest.height-t.highest.offset,i=Math.min(s.maxWidth,s.chart.width-e),e+6>(a=l.offset?s.maxWidth/d:i/(d-1))&&(a=i/(d-(l.offset?.5:1)),r=s.maxHeight-fn(l.gridLines)-u.padding-gn(l.scaleLabel),o=Math.sqrt(e*e+n*n),f=H.toDegrees(Math.min(Math.asin(Math.min((t.highest.height+6)/a,1)),Math.asin(Math.min(r/o,1))-Math.asin(n/o))),f=Math.max(h,Math.min(c,f))),s.labelRotation=f)},afterCalculateTickRotation:function(){H.callback(this.options.afterCalculateTickRotation,[this])},beforeFit:function(){H.callback(this.options.beforeFit,[this])},fit:function(){var t=this,e=t.minSize={width:0,height:0},n=t.chart,i=t.options,a=i.ticks,r=i.scaleLabel,o=i.gridLines,s=t._isVisible(),l="bottom"===i.position,u=t.isHorizontal();if(u?e.width=t.maxWidth:s&&(e.width=fn(o)+gn(r)),u?s&&(e.height=fn(o)+gn(r)):e.height=t.maxHeight,a.display&&s){var d=pn(a),h=t._getLabelSizes(),c=h.first,f=h.last,g=h.widest,m=h.highest,p=.4*d.minor.lineHeight,v=a.padding;if(u){var b=0!==t.labelRotation,y=H.toRadians(t.labelRotation),x=Math.cos(y),_=Math.sin(y),w=_*g.width+x*(m.height-(b?m.offset:0))+(b?0:p);e.height=Math.min(t.maxHeight,e.height+w+v);var k,M,S=t.getPixelForTick(0)-t.left,D=t.right-t.getPixelForTick(t.getTicks().length-1);b?(k=l?x*c.width+_*c.offset:_*(c.height-c.offset),M=l?_*(f.height-f.offset):x*f.width+_*f.offset):(k=c.width/2,M=f.width/2),t.paddingLeft=Math.max((k-S)*t.width/(t.width-S),0)+3,t.paddingRight=Math.max((M-D)*t.width/(t.width-D),0)+3}else{var C=a.mirror?0:g.width+v+p;e.width=Math.min(t.maxWidth,e.width+C),t.paddingTop=c.height/2,t.paddingBottom=f.height/2}}t.handleMargins(),u?(t.width=t._length=n.width-t.margins.left-t.margins.right,t.height=e.height):(t.width=e.width,t.height=t._length=n.height-t.margins.top-t.margins.bottom)},handleMargins:function(){var t=this;t.margins&&(t.margins.left=Math.max(t.paddingLeft,t.margins.left),t.margins.top=Math.max(t.paddingTop,t.margins.top),t.margins.right=Math.max(t.paddingRight,t.margins.right),t.margins.bottom=Math.max(t.paddingBottom,t.margins.bottom))},afterFit:function(){H.callback(this.options.afterFit,[this])},isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},isFullWidth:function(){return this.options.fullWidth},getRightValue:function(t){if(ln(t))return NaN;if(("number"==typeof t||t instanceof Number)&&!isFinite(t))return NaN;if(t)if(this.isHorizontal()){if(void 0!==t.x)return this.getRightValue(t.x)}else if(void 0!==t.y)return this.getRightValue(t.y);return t},_convertTicksToLabels:function(t){var e,n,i,a=this;for(a.ticks=t.map((function(t){return t.value})),a.beforeTickToLabelConversion(),e=a.convertTicksToLabels(t)||a.ticks,a.afterTickToLabelConversion(),n=0,i=t.length;n<i;++n)t[n].label=e[n];return e},_getLabelSizes:function(){var t=this,e=t._labelSizes;return e||(t._labelSizes=e=cn(t.ctx,pn(t.options.ticks),t.getTicks(),t.longestTextCache),t.longestLabelWidth=e.widest.width),e},_parseValue:function(t){var e,n,i,a;return sn(t)?(e=+this.getRightValue(t[0]),n=+this.getRightValue(t[1]),i=Math.min(e,n),a=Math.max(e,n)):(e=void 0,n=t=+this.getRightValue(t),i=t,a=t),{min:i,max:a,start:e,end:n}},_getScaleLabel:function(t){var e=this._parseValue(t);return void 0!==e.start?"["+e.start+", "+e.end+"]":+this.getRightValue(t)},getLabelForIndex:H.noop,getPixelForValue:H.noop,getValueForPixel:H.noop,getPixelForTick:function(t){var e=this.options.offset,n=this._ticks.length,i=1/Math.max(n-(e?0:1),1);return t<0||t>n-1?null:this.getPixelForDecimal(t*i+(e?i/2:0))},getPixelForDecimal:function(t){return this._reversePixels&&(t=1-t),this._startPixel+t*this._length},getDecimalForPixel:function(t){var e=(t-this._startPixel)/this._length;return this._reversePixels?1-e:e},getBasePixel:function(){return this.getPixelForValue(this.getBaseValue())},getBaseValue:function(){var t=this.min,e=this.max;return this.beginAtZero?0:t<0&&e<0?e:t>0&&e>0?t:0},_autoSkip:function(t){var e,n,i,a,r=this.options.ticks,o=this._length,s=r.maxTicksLimit||o/this._tickSize()+1,l=r.major.enabled?function(t){var e,n,i=[];for(e=0,n=t.length;e<n;e++)t[e].major&&i.push(e);return i}(t):[],u=l.length,d=l[0],h=l[u-1];if(u>s)return function(t,e,n){var i,a,r=0,o=e[0];for(n=Math.ceil(n),i=0;i<t.length;i++)a=t[i],i===o?(a._index=i,o=e[++r*n]):delete a.label}(t,l,u/s),vn(t);if(i=function(t,e,n,i){var a,r,o,s,l=function(t){var e,n,i=t.length;if(i<2)return!1;for(n=t[0],e=1;e<i;++e)if(t[e]-t[e-1]!==n)return!1;return n}(t),u=(e.length-1)/i;if(!l)return Math.max(u,1);for(o=0,s=(a=H.math._factorize(l)).length-1;o<s;o++)if((r=a[o])>u)return r;return Math.max(u,1)}(l,t,0,s),u>0){for(e=0,n=u-1;e<n;e++)bn(t,i,l[e],l[e+1]);return a=u>1?(h-d)/(u-1):null,bn(t,i,H.isNullOrUndef(a)?0:d-a,d),bn(t,i,h,H.isNullOrUndef(a)?t.length:h+a),vn(t)}return bn(t,i),vn(t)},_tickSize:function(){var t=this.options.ticks,e=H.toRadians(this.labelRotation),n=Math.abs(Math.cos(e)),i=Math.abs(Math.sin(e)),a=this._getLabelSizes(),r=t.autoSkipPadding||0,o=a?a.widest.width+r:0,s=a?a.highest.height+r:0;return this.isHorizontal()?s*n>o*i?o/n:s/i:s*i<o*n?s/n:o/i},_isVisible:function(){var t,e,n,i=this.chart,a=this.options.display;if("auto"!==a)return!!a;for(t=0,e=i.data.datasets.length;t<e;++t)if(i.isDatasetVisible(t)&&((n=i.getDatasetMeta(t)).xAxisID===this.id||n.yAxisID===this.id))return!0;return!1},_computeGridLineItems:function(t){var e,n,i,a,r,o,s,l,u,d,h,c,f,g,m,p,v,b=this,y=b.chart,x=b.options,_=x.gridLines,w=x.position,k=_.offsetGridLines,M=b.isHorizontal(),S=b._ticksToDraw,D=S.length+(k?1:0),C=fn(_),P=[],T=_.drawBorder?dn(_.lineWidth,0,0):0,O=T/2,A=H._alignPixel,F=function(t){return A(y,t,T)};for("top"===w?(e=F(b.bottom),s=b.bottom-C,u=e-O,h=F(t.top)+O,f=t.bottom):"bottom"===w?(e=F(b.top),h=t.top,f=F(t.bottom)-O,s=e+O,u=b.top+C):"left"===w?(e=F(b.right),o=b.right-C,l=e-O,d=F(t.left)+O,c=t.right):(e=F(b.left),d=t.left,c=F(t.right)-O,o=e+O,l=b.left+C),n=0;n<D;++n)i=S[n]||{},ln(i.label)&&n<S.length||(n===b.zeroLineIndex&&x.offset===k?(g=_.zeroLineWidth,m=_.zeroLineColor,p=_.zeroLineBorderDash||[],v=_.zeroLineBorderDashOffset||0):(g=dn(_.lineWidth,n,1),m=dn(_.color,n,"rgba(0,0,0,0.1)"),p=_.borderDash||[],v=_.borderDashOffset||0),void 0!==(a=hn(b,i._index||n,k))&&(r=A(y,a,g),M?o=l=d=c=r:s=u=h=f=r,P.push({tx1:o,ty1:s,tx2:l,ty2:u,x1:d,y1:h,x2:c,y2:f,width:g,color:m,borderDash:p,borderDashOffset:v})));return P.ticksLength=D,P.borderValue=e,P},_computeLabelItems:function(){var t,e,n,i,a,r,o,s,l,u,d,h,c=this,f=c.options,g=f.ticks,m=f.position,p=g.mirror,v=c.isHorizontal(),b=c._ticksToDraw,y=pn(g),x=g.padding,_=fn(f.gridLines),w=-H.toRadians(c.labelRotation),k=[];for("top"===m?(r=c.bottom-_-x,o=w?"left":"center"):"bottom"===m?(r=c.top+_+x,o=w?"right":"center"):"left"===m?(a=c.right-(p?0:_)-x,o=p?"left":"right"):(a=c.left+(p?0:_)+x,o=p?"right":"left"),t=0,e=b.length;t<e;++t)i=(n=b[t]).label,ln(i)||(s=c.getPixelForTick(n._index||t)+g.labelOffset,u=(l=n.major?y.major:y.minor).lineHeight,d=sn(i)?i.length:1,v?(a=s,h="top"===m?((w?1:.5)-d)*u:(w?0:.5)*u):(r=s,h=(1-d)*u/2),k.push({x:a,y:r,rotation:w,label:i,font:l,textOffset:h,textAlign:o}));return k},_drawGrid:function(t){var e=this,n=e.options.gridLines;if(n.display){var i,a,r,o,s,l=e.ctx,u=e.chart,d=H._alignPixel,h=n.drawBorder?dn(n.lineWidth,0,0):0,c=e._gridLineItems||(e._gridLineItems=e._computeGridLineItems(t));for(r=0,o=c.length;r<o;++r)i=(s=c[r]).width,a=s.color,i&&a&&(l.save(),l.lineWidth=i,l.strokeStyle=a,l.setLineDash&&(l.setLineDash(s.borderDash),l.lineDashOffset=s.borderDashOffset),l.beginPath(),n.drawTicks&&(l.moveTo(s.tx1,s.ty1),l.lineTo(s.tx2,s.ty2)),n.drawOnChartArea&&(l.moveTo(s.x1,s.y1),l.lineTo(s.x2,s.y2)),l.stroke(),l.restore());if(h){var f,g,m,p,v=h,b=dn(n.lineWidth,c.ticksLength-1,1),y=c.borderValue;e.isHorizontal()?(f=d(u,e.left,v)-v/2,g=d(u,e.right,b)+b/2,m=p=y):(m=d(u,e.top,v)-v/2,p=d(u,e.bottom,b)+b/2,f=g=y),l.lineWidth=h,l.strokeStyle=dn(n.color,0),l.beginPath(),l.moveTo(f,m),l.lineTo(g,p),l.stroke()}}},_drawLabels:function(){var t=this;if(t.options.ticks.display){var e,n,i,a,r,o,s,l,u=t.ctx,d=t._labelItems||(t._labelItems=t._computeLabelItems());for(e=0,i=d.length;e<i;++e){if(o=(r=d[e]).font,u.save(),u.translate(r.x,r.y),u.rotate(r.rotation),u.font=o.string,u.fillStyle=o.color,u.textBaseline="middle",u.textAlign=r.textAlign,s=r.label,l=r.textOffset,sn(s))for(n=0,a=s.length;n<a;++n)u.fillText(""+s[n],0,l),l+=o.lineHeight;else u.fillText(s,0,l);u.restore()}}},_drawTitle:function(){var t=this,e=t.ctx,n=t.options,i=n.scaleLabel;if(i.display){var a,r,o=un(i.fontColor,W.global.defaultFontColor),s=H.options._parseFont(i),l=H.options.toPadding(i.padding),u=s.lineHeight/2,d=n.position,h=0;if(t.isHorizontal())a=t.left+t.width/2,r="bottom"===d?t.bottom-u-l.bottom:t.top+u+l.top;else{var c="left"===d;a=c?t.left+u+l.top:t.right-u-l.top,r=t.top+t.height/2,h=c?-.5*Math.PI:.5*Math.PI}e.save(),e.translate(a,r),e.rotate(h),e.textAlign="center",e.textBaseline="middle",e.fillStyle=o,e.font=s.string,e.fillText(i.labelString,0,0),e.restore()}},draw:function(t){this._isVisible()&&(this._drawGrid(t),this._drawTitle(),this._drawLabels())},_layers:function(){var t=this,e=t.options,n=e.ticks&&e.ticks.z||0,i=e.gridLines&&e.gridLines.z||0;return t._isVisible()&&n!==i&&t.draw===t._draw?[{z:i,draw:function(){t._drawGrid.apply(t,arguments),t._drawTitle.apply(t,arguments)}},{z:n,draw:function(){t._drawLabels.apply(t,arguments)}}]:[{z:n,draw:function(){t.draw.apply(t,arguments)}}]},_getMatchingVisibleMetas:function(t){var e=this,n=e.isHorizontal();return e.chart._getSortedVisibleDatasetMetas().filter((function(i){return(!t||i.type===t)&&(n?i.xAxisID===e.id:i.yAxisID===e.id)}))}});yn.prototype._draw=yn.prototype.draw;var xn=yn,_n=H.isNullOrUndef,wn=xn.extend({determineDataLimits:function(){var t,e=this,n=e._getLabels(),i=e.options.ticks,a=i.min,r=i.max,o=0,s=n.length-1;void 0!==a&&(t=n.indexOf(a))>=0&&(o=t),void 0!==r&&(t=n.indexOf(r))>=0&&(s=t),e.minIndex=o,e.maxIndex=s,e.min=n[o],e.max=n[s]},buildTicks:function(){var t=this._getLabels(),e=this.minIndex,n=this.maxIndex;this.ticks=0===e&&n===t.length-1?t:t.slice(e,n+1)},getLabelForIndex:function(t,e){var n=this.chart;return n.getDatasetMeta(e).controller._getValueScaleId()===this.id?this.getRightValue(n.data.datasets[e].data[t]):this._getLabels()[t]},_configure:function(){var t=this,e=t.options.offset,n=t.ticks;xn.prototype._configure.call(t),t.isHorizontal()||(t._reversePixels=!t._reversePixels),n&&(t._startValue=t.minIndex-(e?.5:0),t._valueRange=Math.max(n.length-(e?0:1),1))},getPixelForValue:function(t,e,n){var i,a,r,o=this;return _n(e)||_n(n)||(t=o.chart.data.datasets[n].data[e]),_n(t)||(i=o.isHorizontal()?t.x:t.y),(void 0!==i||void 0!==t&&isNaN(e))&&(a=o._getLabels(),t=H.valueOrDefault(i,t),e=-1!==(r=a.indexOf(t))?r:e,isNaN(e)&&(e=t)),o.getPixelForDecimal((e-o._startValue)/o._valueRange)},getPixelForTick:function(t){var e=this.ticks;return t<0||t>e.length-1?null:this.getPixelForValue(e[t],t+this.minIndex)},getValueForPixel:function(t){var e=Math.round(this._startValue+this.getDecimalForPixel(t)*this._valueRange);return Math.min(Math.max(e,0),this.ticks.length-1)},getBasePixel:function(){return this.bottom}}),kn={position:"bottom"};wn._defaults=kn;var Mn=H.noop,Sn=H.isNullOrUndef;var Dn=xn.extend({getRightValue:function(t){return"string"==typeof t?+t:xn.prototype.getRightValue.call(this,t)},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;if(e.beginAtZero){var n=H.sign(t.min),i=H.sign(t.max);n<0&&i<0?t.max=0:n>0&&i>0&&(t.min=0)}var a=void 0!==e.min||void 0!==e.suggestedMin,r=void 0!==e.max||void 0!==e.suggestedMax;void 0!==e.min?t.min=e.min:void 0!==e.suggestedMin&&(null===t.min?t.min=e.suggestedMin:t.min=Math.min(t.min,e.suggestedMin)),void 0!==e.max?t.max=e.max:void 0!==e.suggestedMax&&(null===t.max?t.max=e.suggestedMax:t.max=Math.max(t.max,e.suggestedMax)),a!==r&&t.min>=t.max&&(a?t.max=t.min+1:t.min=t.max-1),t.min===t.max&&(t.max++,e.beginAtZero||t.min--)},getTickLimit:function(){var t,e=this.options.ticks,n=e.stepSize,i=e.maxTicksLimit;return n?t=Math.ceil(this.max/n)-Math.floor(this.min/n)+1:(t=this._computeTickLimit(),i=i||11),i&&(t=Math.min(i,t)),t},_computeTickLimit:function(){return Number.POSITIVE_INFINITY},handleDirectionalChanges:Mn,buildTicks:function(){var t=this,e=t.options.ticks,n=t.getTickLimit(),i={maxTicks:n=Math.max(2,n),min:e.min,max:e.max,precision:e.precision,stepSize:H.valueOrDefault(e.fixedStepSize,e.stepSize)},a=t.ticks=function(t,e){var n,i,a,r,o=[],s=t.stepSize,l=s||1,u=t.maxTicks-1,d=t.min,h=t.max,c=t.precision,f=e.min,g=e.max,m=H.niceNum((g-f)/u/l)*l;if(m<1e-14&&Sn(d)&&Sn(h))return[f,g];(r=Math.ceil(g/m)-Math.floor(f/m))>u&&(m=H.niceNum(r*m/u/l)*l),s||Sn(c)?n=Math.pow(10,H._decimalPlaces(m)):(n=Math.pow(10,c),m=Math.ceil(m*n)/n),i=Math.floor(f/m)*m,a=Math.ceil(g/m)*m,s&&(!Sn(d)&&H.almostWhole(d/m,m/1e3)&&(i=d),!Sn(h)&&H.almostWhole(h/m,m/1e3)&&(a=h)),r=(a-i)/m,r=H.almostEquals(r,Math.round(r),m/1e3)?Math.round(r):Math.ceil(r),i=Math.round(i*n)/n,a=Math.round(a*n)/n,o.push(Sn(d)?i:d);for(var p=1;p<r;++p)o.push(Math.round((i+p*m)*n)/n);return o.push(Sn(h)?a:h),o}(i,t);t.handleDirectionalChanges(),t.max=H.max(a),t.min=H.min(a),e.reverse?(a.reverse(),t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max)},convertTicksToLabels:function(){var t=this;t.ticksAsNumbers=t.ticks.slice(),t.zeroLineIndex=t.ticks.indexOf(0),xn.prototype.convertTicksToLabels.call(t)},_configure:function(){var t,e=this,n=e.getTicks(),i=e.min,a=e.max;xn.prototype._configure.call(e),e.options.offset&&n.length&&(i-=t=(a-i)/Math.max(n.length-1,1)/2,a+=t),e._startValue=i,e._endValue=a,e._valueRange=a-i}}),Cn={position:"left",ticks:{callback:on.formatters.linear}};function Pn(t,e,n,i){var a,r,o=t.options,s=function(t,e,n){var i=[n.type,void 0===e&&void 0===n.stack?n.index:"",n.stack].join(".");return void 0===t[i]&&(t[i]={pos:[],neg:[]}),t[i]}(e,o.stacked,n),l=s.pos,u=s.neg,d=i.length;for(a=0;a<d;++a)r=t._parseValue(i[a]),isNaN(r.min)||isNaN(r.max)||n.data[a].hidden||(l[a]=l[a]||0,u[a]=u[a]||0,o.relativePoints?l[a]=100:r.min<0||r.max<0?u[a]+=r.min:l[a]+=r.max)}function Tn(t,e,n){var i,a,r=n.length;for(i=0;i<r;++i)a=t._parseValue(n[i]),isNaN(a.min)||isNaN(a.max)||e.data[i].hidden||(t.min=Math.min(t.min,a.min),t.max=Math.max(t.max,a.max))}var On=Dn.extend({determineDataLimits:function(){var t,e,n,i,a=this,r=a.options,o=a.chart.data.datasets,s=a._getMatchingVisibleMetas(),l=r.stacked,u={},d=s.length;if(a.min=Number.POSITIVE_INFINITY,a.max=Number.NEGATIVE_INFINITY,void 0===l)for(t=0;!l&&t<d;++t)l=void 0!==(e=s[t]).stack;for(t=0;t<d;++t)n=o[(e=s[t]).index].data,l?Pn(a,u,e,n):Tn(a,e,n);H.each(u,(function(t){i=t.pos.concat(t.neg),a.min=Math.min(a.min,H.min(i)),a.max=Math.max(a.max,H.max(i))})),a.min=H.isFinite(a.min)&&!isNaN(a.min)?a.min:0,a.max=H.isFinite(a.max)&&!isNaN(a.max)?a.max:1,a.handleTickRangeOptions()},_computeTickLimit:function(){var t;return this.isHorizontal()?Math.ceil(this.width/40):(t=H.options._parseFont(this.options.ticks),Math.ceil(this.height/t.lineHeight))},handleDirectionalChanges:function(){this.isHorizontal()||this.ticks.reverse()},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForValue:function(t){return this.getPixelForDecimal((+this.getRightValue(t)-this._startValue)/this._valueRange)},getValueForPixel:function(t){return this._startValue+this.getDecimalForPixel(t)*this._valueRange},getPixelForTick:function(t){var e=this.ticksAsNumbers;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])}}),An=Cn;On._defaults=An;var Fn=H.valueOrDefault,In=H.math.log10;var Ln={position:"left",ticks:{callback:on.formatters.logarithmic}};function Rn(t,e){return H.isFinite(t)&&t>=0?t:e}var Nn=xn.extend({determineDataLimits:function(){var t,e,n,i,a,r,o=this,s=o.options,l=o.chart,u=l.data.datasets,d=o.isHorizontal();function h(t){return d?t.xAxisID===o.id:t.yAxisID===o.id}o.min=Number.POSITIVE_INFINITY,o.max=Number.NEGATIVE_INFINITY,o.minNotZero=Number.POSITIVE_INFINITY;var c=s.stacked;if(void 0===c)for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e)&&void 0!==e.stack){c=!0;break}if(s.stacked||c){var f={};for(t=0;t<u.length;t++){var g=[(e=l.getDatasetMeta(t)).type,void 0===s.stacked&&void 0===e.stack?t:"",e.stack].join(".");if(l.isDatasetVisible(t)&&h(e))for(void 0===f[g]&&(f[g]=[]),a=0,r=(i=u[t].data).length;a<r;a++){var m=f[g];n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(m[a]=m[a]||0,m[a]+=n.max)}}H.each(f,(function(t){if(t.length>0){var e=H.min(t),n=H.max(t);o.min=Math.min(o.min,e),o.max=Math.max(o.max,n)}}))}else for(t=0;t<u.length;t++)if(e=l.getDatasetMeta(t),l.isDatasetVisible(t)&&h(e))for(a=0,r=(i=u[t].data).length;a<r;a++)n=o._parseValue(i[a]),isNaN(n.min)||isNaN(n.max)||e.data[a].hidden||n.min<0||n.max<0||(o.min=Math.min(n.min,o.min),o.max=Math.max(n.max,o.max),0!==n.min&&(o.minNotZero=Math.min(n.min,o.minNotZero)));o.min=H.isFinite(o.min)?o.min:null,o.max=H.isFinite(o.max)?o.max:null,o.minNotZero=H.isFinite(o.minNotZero)?o.minNotZero:null,this.handleTickRangeOptions()},handleTickRangeOptions:function(){var t=this,e=t.options.ticks;t.min=Rn(e.min,t.min),t.max=Rn(e.max,t.max),t.min===t.max&&(0!==t.min&&null!==t.min?(t.min=Math.pow(10,Math.floor(In(t.min))-1),t.max=Math.pow(10,Math.floor(In(t.max))+1)):(t.min=1,t.max=10)),null===t.min&&(t.min=Math.pow(10,Math.floor(In(t.max))-1)),null===t.max&&(t.max=0!==t.min?Math.pow(10,Math.floor(In(t.min))+1):10),null===t.minNotZero&&(t.min>0?t.minNotZero=t.min:t.max<1?t.minNotZero=Math.pow(10,Math.floor(In(t.max))):t.minNotZero=1)},buildTicks:function(){var t=this,e=t.options.ticks,n=!t.isHorizontal(),i={min:Rn(e.min),max:Rn(e.max)},a=t.ticks=function(t,e){var n,i,a=[],r=Fn(t.min,Math.pow(10,Math.floor(In(e.min)))),o=Math.floor(In(e.max)),s=Math.ceil(e.max/Math.pow(10,o));0===r?(n=Math.floor(In(e.minNotZero)),i=Math.floor(e.minNotZero/Math.pow(10,n)),a.push(r),r=i*Math.pow(10,n)):(n=Math.floor(In(r)),i=Math.floor(r/Math.pow(10,n)));var l=n<0?Math.pow(10,Math.abs(n)):1;do{a.push(r),10===++i&&(i=1,l=++n>=0?1:l),r=Math.round(i*Math.pow(10,n)*l)/l}while(n<o||n===o&&i<s);var u=Fn(t.max,r);return a.push(u),a}(i,t);t.max=H.max(a),t.min=H.min(a),e.reverse?(n=!n,t.start=t.max,t.end=t.min):(t.start=t.min,t.end=t.max),n&&a.reverse()},convertTicksToLabels:function(){this.tickValues=this.ticks.slice(),xn.prototype.convertTicksToLabels.call(this)},getLabelForIndex:function(t,e){return this._getScaleLabel(this.chart.data.datasets[e].data[t])},getPixelForTick:function(t){var e=this.tickValues;return t<0||t>e.length-1?null:this.getPixelForValue(e[t])},_getFirstTickValue:function(t){var e=Math.floor(In(t));return Math.floor(t/Math.pow(10,e))*Math.pow(10,e)},_configure:function(){var t=this,e=t.min,n=0;xn.prototype._configure.call(t),0===e&&(e=t._getFirstTickValue(t.minNotZero),n=Fn(t.options.ticks.fontSize,W.global.defaultFontSize)/t._length),t._startValue=In(e),t._valueOffset=n,t._valueRange=(In(t.max)-In(e))/(1-n)},getPixelForValue:function(t){var e=this,n=0;return(t=+e.getRightValue(t))>e.min&&t>0&&(n=(In(t)-e._startValue)/e._valueRange+e._valueOffset),e.getPixelForDecimal(n)},getValueForPixel:function(t){var e=this,n=e.getDecimalForPixel(t);return 0===n&&0===e.min?0:Math.pow(10,e._startValue+(n-e._valueOffset)*e._valueRange)}}),Wn=Ln;Nn._defaults=Wn;var Yn=H.valueOrDefault,zn=H.valueAtIndexOrDefault,En=H.options.resolve,Vn={display:!0,animate:!0,position:"chartArea",angleLines:{display:!0,color:"rgba(0,0,0,0.1)",lineWidth:1,borderDash:[],borderDashOffset:0},gridLines:{circular:!1},ticks:{showLabelBackdrop:!0,backdropColor:"rgba(255,255,255,0.75)",backdropPaddingY:2,backdropPaddingX:2,callback:on.formatters.linear},pointLabels:{display:!0,fontSize:10,callback:function(t){return t}}};function Hn(t){var e=t.ticks;return e.display&&t.display?Yn(e.fontSize,W.global.defaultFontSize)+2*e.backdropPaddingY:0}function Bn(t,e,n,i,a){return t===i||t===a?{start:e-n/2,end:e+n/2}:t<i||t>a?{start:e-n,end:e}:{start:e,end:e+n}}function jn(t){return 0===t||180===t?"center":t<180?"left":"right"}function Un(t,e,n,i){var a,r,o=n.y+i/2;if(H.isArray(e))for(a=0,r=e.length;a<r;++a)t.fillText(e[a],n.x,o),o+=i;else t.fillText(e,n.x,o)}function Gn(t,e,n){90===t||270===t?n.y-=e.h/2:(t>270||t<90)&&(n.y-=e.h)}function qn(t){return H.isNumber(t)?t:0}var Zn=Dn.extend({setDimensions:function(){var t=this;t.width=t.maxWidth,t.height=t.maxHeight,t.paddingTop=Hn(t.options)/2,t.xCenter=Math.floor(t.width/2),t.yCenter=Math.floor((t.height-t.paddingTop)/2),t.drawingArea=Math.min(t.height-t.paddingTop,t.width)/2},determineDataLimits:function(){var t=this,e=t.chart,n=Number.POSITIVE_INFINITY,i=Number.NEGATIVE_INFINITY;H.each(e.data.datasets,(function(a,r){if(e.isDatasetVisible(r)){var o=e.getDatasetMeta(r);H.each(a.data,(function(e,a){var r=+t.getRightValue(e);isNaN(r)||o.data[a].hidden||(n=Math.min(r,n),i=Math.max(r,i))}))}})),t.min=n===Number.POSITIVE_INFINITY?0:n,t.max=i===Number.NEGATIVE_INFINITY?0:i,t.handleTickRangeOptions()},_computeTickLimit:function(){return Math.ceil(this.drawingArea/Hn(this.options))},convertTicksToLabels:function(){var t=this;Dn.prototype.convertTicksToLabels.call(t),t.pointLabels=t.chart.data.labels.map((function(){var e=H.callback(t.options.pointLabels.callback,arguments,t);return e||0===e?e:""}))},getLabelForIndex:function(t,e){return+this.getRightValue(this.chart.data.datasets[e].data[t])},fit:function(){var t=this.options;t.display&&t.pointLabels.display?function(t){var e,n,i,a=H.options._parseFont(t.options.pointLabels),r={l:0,r:t.width,t:0,b:t.height-t.paddingTop},o={};t.ctx.font=a.string,t._pointLabelSizes=[];var s,l,u,d=t.chart.data.labels.length;for(e=0;e<d;e++){i=t.getPointPosition(e,t.drawingArea+5),s=t.ctx,l=a.lineHeight,u=t.pointLabels[e],n=H.isArray(u)?{w:H.longestText(s,s.font,u),h:u.length*l}:{w:s.measureText(u).width,h:l},t._pointLabelSizes[e]=n;var h=t.getIndexAngle(e),c=H.toDegrees(h)%360,f=Bn(c,i.x,n.w,0,180),g=Bn(c,i.y,n.h,90,270);f.start<r.l&&(r.l=f.start,o.l=h),f.end>r.r&&(r.r=f.end,o.r=h),g.start<r.t&&(r.t=g.start,o.t=h),g.end>r.b&&(r.b=g.end,o.b=h)}t.setReductions(t.drawingArea,r,o)}(this):this.setCenterPoint(0,0,0,0)},setReductions:function(t,e,n){var i=this,a=e.l/Math.sin(n.l),r=Math.max(e.r-i.width,0)/Math.sin(n.r),o=-e.t/Math.cos(n.t),s=-Math.max(e.b-(i.height-i.paddingTop),0)/Math.cos(n.b);a=qn(a),r=qn(r),o=qn(o),s=qn(s),i.drawingArea=Math.min(Math.floor(t-(a+r)/2),Math.floor(t-(o+s)/2)),i.setCenterPoint(a,r,o,s)},setCenterPoint:function(t,e,n,i){var a=this,r=a.width-e-a.drawingArea,o=t+a.drawingArea,s=n+a.drawingArea,l=a.height-a.paddingTop-i-a.drawingArea;a.xCenter=Math.floor((o+r)/2+a.left),a.yCenter=Math.floor((s+l)/2+a.top+a.paddingTop)},getIndexAngle:function(t){var e=this.chart,n=(t*(360/e.data.labels.length)+((e.options||{}).startAngle||0))%360;return(n<0?n+360:n)*Math.PI*2/360},getDistanceFromCenterForValue:function(t){var e=this;if(H.isNullOrUndef(t))return NaN;var n=e.drawingArea/(e.max-e.min);return e.options.ticks.reverse?(e.max-t)*n:(t-e.min)*n},getPointPosition:function(t,e){var n=this.getIndexAngle(t)-Math.PI/2;return{x:Math.cos(n)*e+this.xCenter,y:Math.sin(n)*e+this.yCenter}},getPointPositionForValue:function(t,e){return this.getPointPosition(t,this.getDistanceFromCenterForValue(e))},getBasePosition:function(t){var e=this.min,n=this.max;return this.getPointPositionForValue(t||0,this.beginAtZero?0:e<0&&n<0?n:e>0&&n>0?e:0)},_drawGrid:function(){var t,e,n,i=this,a=i.ctx,r=i.options,o=r.gridLines,s=r.angleLines,l=Yn(s.lineWidth,o.lineWidth),u=Yn(s.color,o.color);if(r.pointLabels.display&&function(t){var e=t.ctx,n=t.options,i=n.pointLabels,a=Hn(n),r=t.getDistanceFromCenterForValue(n.ticks.reverse?t.min:t.max),o=H.options._parseFont(i);e.save(),e.font=o.string,e.textBaseline="middle";for(var s=t.chart.data.labels.length-1;s>=0;s--){var l=0===s?a/2:0,u=t.getPointPosition(s,r+l+5),d=zn(i.fontColor,s,W.global.defaultFontColor);e.fillStyle=d;var h=t.getIndexAngle(s),c=H.toDegrees(h);e.textAlign=jn(c),Gn(c,t._pointLabelSizes[s],u),Un(e,t.pointLabels[s],u,o.lineHeight)}e.restore()}(i),o.display&&H.each(i.ticks,(function(t,n){0!==n&&(e=i.getDistanceFromCenterForValue(i.ticksAsNumbers[n]),function(t,e,n,i){var a,r=t.ctx,o=e.circular,s=t.chart.data.labels.length,l=zn(e.color,i-1),u=zn(e.lineWidth,i-1);if((o||s)&&l&&u){if(r.save(),r.strokeStyle=l,r.lineWidth=u,r.setLineDash&&(r.setLineDash(e.borderDash||[]),r.lineDashOffset=e.borderDashOffset||0),r.beginPath(),o)r.arc(t.xCenter,t.yCenter,n,0,2*Math.PI);else{a=t.getPointPosition(0,n),r.moveTo(a.x,a.y);for(var d=1;d<s;d++)a=t.getPointPosition(d,n),r.lineTo(a.x,a.y)}r.closePath(),r.stroke(),r.restore()}}(i,o,e,n))})),s.display&&l&&u){for(a.save(),a.lineWidth=l,a.strokeStyle=u,a.setLineDash&&(a.setLineDash(En([s.borderDash,o.borderDash,[]])),a.lineDashOffset=En([s.borderDashOffset,o.borderDashOffset,0])),t=i.chart.data.labels.length-1;t>=0;t--)e=i.getDistanceFromCenterForValue(r.ticks.reverse?i.min:i.max),n=i.getPointPosition(t,e),a.beginPath(),a.moveTo(i.xCenter,i.yCenter),a.lineTo(n.x,n.y),a.stroke();a.restore()}},_drawLabels:function(){var t=this,e=t.ctx,n=t.options.ticks;if(n.display){var i,a,r=t.getIndexAngle(0),o=H.options._parseFont(n),s=Yn(n.fontColor,W.global.defaultFontColor);e.save(),e.font=o.string,e.translate(t.xCenter,t.yCenter),e.rotate(r),e.textAlign="center",e.textBaseline="middle",H.each(t.ticks,(function(r,l){(0!==l||n.reverse)&&(i=t.getDistanceFromCenterForValue(t.ticksAsNumbers[l]),n.showLabelBackdrop&&(a=e.measureText(r).width,e.fillStyle=n.backdropColor,e.fillRect(-a/2-n.backdropPaddingX,-i-o.size/2-n.backdropPaddingY,a+2*n.backdropPaddingX,o.size+2*n.backdropPaddingY)),e.fillStyle=s,e.fillText(r,0,-i))})),e.restore()}},_drawTitle:H.noop}),$n=Vn;Zn._defaults=$n;var Xn=H._deprecated,Kn=H.options.resolve,Jn=H.valueOrDefault,Qn=Number.MIN_SAFE_INTEGER||-9007199254740991,ti=Number.MAX_SAFE_INTEGER||9007199254740991,ei={millisecond:{common:!0,size:1,steps:1e3},second:{common:!0,size:1e3,steps:60},minute:{common:!0,size:6e4,steps:60},hour:{common:!0,size:36e5,steps:24},day:{common:!0,size:864e5,steps:30},week:{common:!1,size:6048e5,steps:4},month:{common:!0,size:2628e6,steps:12},quarter:{common:!1,size:7884e6,steps:4},year:{common:!0,size:3154e7}},ni=Object.keys(ei);function ii(t,e){return t-e}function ai(t){return H.valueOrDefault(t.time.min,t.ticks.min)}function ri(t){return H.valueOrDefault(t.time.max,t.ticks.max)}function oi(t,e,n,i){var a=function(t,e,n){for(var i,a,r,o=0,s=t.length-1;o>=0&&o<=s;){if(a=t[(i=o+s>>1)-1]||null,r=t[i],!a)return{lo:null,hi:r};if(r[e]<n)o=i+1;else{if(!(a[e]>n))return{lo:a,hi:r};s=i-1}}return{lo:r,hi:null}}(t,e,n),r=a.lo?a.hi?a.lo:t[t.length-2]:t[0],o=a.lo?a.hi?a.hi:t[t.length-1]:t[1],s=o[e]-r[e],l=s?(n-r[e])/s:0,u=(o[i]-r[i])*l;return r[i]+u}function si(t,e){var n=t._adapter,i=t.options.time,a=i.parser,r=a||i.format,o=e;return"function"==typeof a&&(o=a(o)),H.isFinite(o)||(o="string"==typeof r?n.parse(o,r):n.parse(o)),null!==o?+o:(a||"function"!=typeof r||(o=r(e),H.isFinite(o)||(o=n.parse(o))),o)}function li(t,e){if(H.isNullOrUndef(e))return null;var n=t.options.time,i=si(t,t.getRightValue(e));return null===i?i:(n.round&&(i=+t._adapter.startOf(i,n.round)),i)}function ui(t,e,n,i){var a,r,o,s=ni.length;for(a=ni.indexOf(t);a<s-1;++a)if(o=(r=ei[ni[a]]).steps?r.steps:ti,r.common&&Math.ceil((n-e)/(o*r.size))<=i)return ni[a];return ni[s-1]}function di(t,e,n){var i,a,r=[],o={},s=e.length;for(i=0;i<s;++i)o[a=e[i]]=i,r.push({value:a,major:!1});return 0!==s&&n?function(t,e,n,i){var a,r,o=t._adapter,s=+o.startOf(e[0].value,i),l=e[e.length-1].value;for(a=s;a<=l;a=+o.add(a,1,i))(r=n[a])>=0&&(e[r].major=!0);return e}(t,r,o,n):r}var hi=xn.extend({initialize:function(){this.mergeTicksOptions(),xn.prototype.initialize.call(this)},update:function(){var t=this,e=t.options,n=e.time||(e.time={}),i=t._adapter=new rn._date(e.adapters.date);return Xn("time scale",n.format,"time.format","time.parser"),Xn("time scale",n.min,"time.min","ticks.min"),Xn("time scale",n.max,"time.max","ticks.max"),H.mergeIf(n.displayFormats,i.formats()),xn.prototype.update.apply(t,arguments)},getRightValue:function(t){return t&&void 0!==t.t&&(t=t.t),xn.prototype.getRightValue.call(this,t)},determineDataLimits:function(){var t,e,n,i,a,r,o,s=this,l=s.chart,u=s._adapter,d=s.options,h=d.time.unit||"day",c=ti,f=Qn,g=[],m=[],p=[],v=s._getLabels();for(t=0,n=v.length;t<n;++t)p.push(li(s,v[t]));for(t=0,n=(l.data.datasets||[]).length;t<n;++t)if(l.isDatasetVisible(t))if(a=l.data.datasets[t].data,H.isObject(a[0]))for(m[t]=[],e=0,i=a.length;e<i;++e)r=li(s,a[e]),g.push(r),m[t][e]=r;else m[t]=p.slice(0),o||(g=g.concat(p),o=!0);else m[t]=[];p.length&&(c=Math.min(c,p[0]),f=Math.max(f,p[p.length-1])),g.length&&(g=n>1?function(t){var e,n,i,a={},r=[];for(e=0,n=t.length;e<n;++e)a[i=t[e]]||(a[i]=!0,r.push(i));return r}(g).sort(ii):g.sort(ii),c=Math.min(c,g[0]),f=Math.max(f,g[g.length-1])),c=li(s,ai(d))||c,f=li(s,ri(d))||f,c=c===ti?+u.startOf(Date.now(),h):c,f=f===Qn?+u.endOf(Date.now(),h)+1:f,s.min=Math.min(c,f),s.max=Math.max(c+1,f),s._table=[],s._timestamps={data:g,datasets:m,labels:p}},buildTicks:function(){var t,e,n,i=this,a=i.min,r=i.max,o=i.options,s=o.ticks,l=o.time,u=i._timestamps,d=[],h=i.getLabelCapacity(a),c=s.source,f=o.distribution;for(u="data"===c||"auto"===c&&"series"===f?u.data:"labels"===c?u.labels:function(t,e,n,i){var a,r=t._adapter,o=t.options,s=o.time,l=s.unit||ui(s.minUnit,e,n,i),u=Kn([s.stepSize,s.unitStepSize,1]),d="week"===l&&s.isoWeekday,h=e,c=[];if(d&&(h=+r.startOf(h,"isoWeek",d)),h=+r.startOf(h,d?"day":l),r.diff(n,e,l)>1e5*u)throw e+" and "+n+" are too far apart with stepSize of "+u+" "+l;for(a=h;a<n;a=+r.add(a,u,l))c.push(a);return a!==n&&"ticks"!==o.bounds||c.push(a),c}(i,a,r,h),"ticks"===o.bounds&&u.length&&(a=u[0],r=u[u.length-1]),a=li(i,ai(o))||a,r=li(i,ri(o))||r,t=0,e=u.length;t<e;++t)(n=u[t])>=a&&n<=r&&d.push(n);return i.min=a,i.max=r,i._unit=l.unit||(s.autoSkip?ui(l.minUnit,i.min,i.max,h):function(t,e,n,i,a){var r,o;for(r=ni.length-1;r>=ni.indexOf(n);r--)if(o=ni[r],ei[o].common&&t._adapter.diff(a,i,o)>=e-1)return o;return ni[n?ni.indexOf(n):0]}(i,d.length,l.minUnit,i.min,i.max)),i._majorUnit=s.major.enabled&&"year"!==i._unit?function(t){for(var e=ni.indexOf(t)+1,n=ni.length;e<n;++e)if(ei[ni[e]].common)return ni[e]}(i._unit):void 0,i._table=function(t,e,n,i){if("linear"===i||!t.length)return[{time:e,pos:0},{time:n,pos:1}];var a,r,o,s,l,u=[],d=[e];for(a=0,r=t.length;a<r;++a)(s=t[a])>e&&s<n&&d.push(s);for(d.push(n),a=0,r=d.length;a<r;++a)l=d[a+1],o=d[a-1],s=d[a],void 0!==o&&void 0!==l&&Math.round((l+o)/2)===s||u.push({time:s,pos:a/(r-1)});return u}(i._timestamps.data,a,r,f),i._offsets=function(t,e,n,i,a){var r,o,s=0,l=0;return a.offset&&e.length&&(r=oi(t,"time",e[0],"pos"),s=1===e.length?1-r:(oi(t,"time",e[1],"pos")-r)/2,o=oi(t,"time",e[e.length-1],"pos"),l=1===e.length?o:(o-oi(t,"time",e[e.length-2],"pos"))/2),{start:s,end:l,factor:1/(s+1+l)}}(i._table,d,0,0,o),s.reverse&&d.reverse(),di(i,d,i._majorUnit)},getLabelForIndex:function(t,e){var n=this,i=n._adapter,a=n.chart.data,r=n.options.time,o=a.labels&&t<a.labels.length?a.labels[t]:"",s=a.datasets[e].data[t];return H.isObject(s)&&(o=n.getRightValue(s)),r.tooltipFormat?i.format(si(n,o),r.tooltipFormat):"string"==typeof o?o:i.format(si(n,o),r.displayFormats.datetime)},tickFormatFunction:function(t,e,n,i){var a=this._adapter,r=this.options,o=r.time.displayFormats,s=o[this._unit],l=this._majorUnit,u=o[l],d=n[e],h=r.ticks,c=l&&u&&d&&d.major,f=a.format(t,i||(c?u:s)),g=c?h.major:h.minor,m=Kn([g.callback,g.userCallback,h.callback,h.userCallback]);return m?m(f,e,n):f},convertTicksToLabels:function(t){var e,n,i=[];for(e=0,n=t.length;e<n;++e)i.push(this.tickFormatFunction(t[e].value,e,t));return i},getPixelForOffset:function(t){var e=this._offsets,n=oi(this._table,"time",t,"pos");return this.getPixelForDecimal((e.start+n)*e.factor)},getPixelForValue:function(t,e,n){var i=null;if(void 0!==e&&void 0!==n&&(i=this._timestamps.datasets[n][e]),null===i&&(i=li(this,t)),null!==i)return this.getPixelForOffset(i)},getPixelForTick:function(t){var e=this.getTicks();return t>=0&&t<e.length?this.getPixelForOffset(e[t].value):null},getValueForPixel:function(t){var e=this._offsets,n=this.getDecimalForPixel(t)/e.factor-e.end,i=oi(this._table,"pos",n,"time");return this._adapter._create(i)},_getLabelSize:function(t){var e=this.options.ticks,n=this.ctx.measureText(t).width,i=H.toRadians(this.isHorizontal()?e.maxRotation:e.minRotation),a=Math.cos(i),r=Math.sin(i),o=Jn(e.fontSize,W.global.defaultFontSize);return{w:n*a+o*r,h:n*r+o*a}},getLabelWidth:function(t){return this._getLabelSize(t).w},getLabelCapacity:function(t){var e=this,n=e.options.time,i=n.displayFormats,a=i[n.unit]||i.millisecond,r=e.tickFormatFunction(t,0,di(e,[t],e._majorUnit),a),o=e._getLabelSize(r),s=Math.floor(e.isHorizontal()?e.width/o.w:e.height/o.h);return e.options.offset&&s--,s>0?s:1}}),ci={position:"bottom",distribution:"linear",bounds:"data",adapters:{},time:{parser:!1,unit:!1,round:!1,displayFormat:!1,isoWeekday:!1,minUnit:"millisecond",displayFormats:{}},ticks:{autoSkip:!1,source:"auto",major:{enabled:!1}}};hi._defaults=ci;var fi={category:wn,linear:On,logarithmic:Nn,radialLinear:Zn,time:hi},gi=e((function(e,n){e.exports=function(){var n,i;function a(){return n.apply(null,arguments)}function r(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function l(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function d(t,e){var n,i=[];for(n=0;n<t.length;++n)i.push(e(t[n],n));return i}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function c(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function f(t,e,n,i){return Ie(t,e,n,i,!0).utc()}function g(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function m(t){if(null==t._isValid){var e=g(t),n=i.call(e.parsedDateParts,(function(t){return null!=t})),a=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(a=a&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return a;t._isValid=a}return t._isValid}function p(t){var e=f(NaN);return null!=t?c(g(e),t):g(e).userInvalidated=!0,e}i=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,i=0;i<n;i++)if(i in e&&t.call(this,e[i],i,e))return!0;return!1};var v=a.momentProperties=[];function b(t,e){var n,i,a;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=g(e)),s(e._locale)||(t._locale=e._locale),v.length>0)for(n=0;n<v.length;n++)s(a=e[i=v[n]])||(t[i]=a);return t}var y=!1;function x(t){b(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===y&&(y=!0,a.updateOffset(this),y=!1)}function _(t){return t instanceof x||null!=t&&null!=t._isAMomentObject}function w(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function k(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=w(e)),n}function M(t,e,n){var i,a=Math.min(t.length,e.length),r=Math.abs(t.length-e.length),o=0;for(i=0;i<a;i++)(n&&t[i]!==e[i]||!n&&k(t[i])!==k(e[i]))&&o++;return o+r}function S(t){!1===a.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function D(t,e){var n=!0;return c((function(){if(null!=a.deprecationHandler&&a.deprecationHandler(null,t),n){for(var i,r=[],o=0;o<arguments.length;o++){if(i="","object"==typeof arguments[o]){for(var s in i+="\n["+o+"] ",arguments[0])i+=s+": "+arguments[0][s]+", ";i=i.slice(0,-2)}else i=arguments[o];r.push(i)}S(t+"\nArguments: "+Array.prototype.slice.call(r).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var C,P={};function T(t,e){null!=a.deprecationHandler&&a.deprecationHandler(t,e),P[t]||(S(e),P[t]=!0)}function O(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function A(t,e){var n,i=c({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(i[n]={},c(i[n],t[n]),c(i[n],e[n])):null!=e[n]?i[n]=e[n]:delete i[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(i[n]=c({},i[n]));return i}function F(t){null!=t&&this.set(t)}a.suppressDeprecationWarnings=!1,a.deprecationHandler=null,C=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var I={};function L(t,e){var n=t.toLowerCase();I[n]=I[n+"s"]=I[e]=t}function R(t){return"string"==typeof t?I[t]||I[t.toLowerCase()]:void 0}function N(t){var e,n,i={};for(n in t)h(t,n)&&(e=R(n))&&(i[e]=t[n]);return i}var W={};function Y(t,e){W[t]=e}function z(t,e,n){var i=""+Math.abs(t),a=e-i.length;return(t>=0?n?"+":"":"-")+Math.pow(10,Math.max(0,a)).toString().substr(1)+i}var E=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,V=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,H={},B={};function j(t,e,n,i){var a=i;"string"==typeof i&&(a=function(){return this[i]()}),t&&(B[t]=a),e&&(B[e[0]]=function(){return z(a.apply(this,arguments),e[1],e[2])}),n&&(B[n]=function(){return this.localeData().ordinal(a.apply(this,arguments),t)})}function U(t,e){return t.isValid()?(e=G(e,t.localeData()),H[e]=H[e]||function(t){var e,n,i,a=t.match(E);for(e=0,n=a.length;e<n;e++)B[a[e]]?a[e]=B[a[e]]:a[e]=(i=a[e]).match(/\[[\s\S]/)?i.replace(/^\[|\]$/g,""):i.replace(/\\/g,"");return function(e){var i,r="";for(i=0;i<n;i++)r+=O(a[i])?a[i].call(e,t):a[i];return r}}(e),H[e](t)):t.localeData().invalidDate()}function G(t,e){var n=5;function i(t){return e.longDateFormat(t)||t}for(V.lastIndex=0;n>=0&&V.test(t);)t=t.replace(V,i),V.lastIndex=0,n-=1;return t}var q=/\d/,Z=/\d\d/,$=/\d{3}/,X=/\d{4}/,K=/[+-]?\d{6}/,J=/\d\d?/,Q=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,it=/[+-]?\d{1,6}/,at=/\d+/,rt=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,lt=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function dt(t,e,n){ut[t]=O(e)?e:function(t,i){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ct(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,i,a){return e||n||i||a}))))}function ct(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var ft={};function gt(t,e){var n,i=e;for("string"==typeof t&&(t=[t]),l(e)&&(i=function(t,n){n[e]=k(t)}),n=0;n<t.length;n++)ft[t[n]]=i}function mt(t,e){gt(t,(function(t,n,i,a){i._w=i._w||{},e(t,i._w,i,a)}))}function pt(t,e,n){null!=e&&h(ft,t)&&ft[t](e,n._a,n,t)}var vt=0,bt=1,yt=2,xt=3,_t=4,wt=5,kt=6,Mt=7,St=8;function Dt(t){return Ct(t)?366:365}function Ct(t){return t%4==0&&t%100!=0||t%400==0}j("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),j(0,["YY",2],0,(function(){return this.year()%100})),j(0,["YYYY",4],0,"year"),j(0,["YYYYY",5],0,"year"),j(0,["YYYYYY",6,!0],0,"year"),L("year","y"),Y("year",1),dt("Y",rt),dt("YY",J,Z),dt("YYYY",nt,X),dt("YYYYY",it,K),dt("YYYYYY",it,K),gt(["YYYYY","YYYYYY"],vt),gt("YYYY",(function(t,e){e[vt]=2===t.length?a.parseTwoDigitYear(t):k(t)})),gt("YY",(function(t,e){e[vt]=a.parseTwoDigitYear(t)})),gt("Y",(function(t,e){e[vt]=parseInt(t,10)})),a.parseTwoDigitYear=function(t){return k(t)+(k(t)>68?1900:2e3)};var Pt,Tt=Ot("FullYear",!0);function Ot(t,e){return function(n){return null!=n?(Ft(this,t,n),a.updateOffset(this,e),this):At(this,t)}}function At(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function Ft(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&Ct(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),It(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function It(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=function(t,e){return(t%e+e)%e}(e,12);return t+=(e-n)/12,1===n?Ct(t)?29:28:31-n%7%2}Pt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},j("M",["MM",2],"Mo",(function(){return this.month()+1})),j("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),j("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),Y("month",8),dt("M",J),dt("MM",J,Z),dt("MMM",(function(t,e){return e.monthsShortRegex(t)})),dt("MMMM",(function(t,e){return e.monthsRegex(t)})),gt(["M","MM"],(function(t,e){e[bt]=k(t)-1})),gt(["MMM","MMMM"],(function(t,e,n,i){var a=n._locale.monthsParse(t,i,n._strict);null!=a?e[bt]=a:g(n).invalidMonth=t}));var Lt=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Rt="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Nt="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function Wt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],i=0;i<12;++i)r=f([2e3,i]),this._shortMonthsParse[i]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[i]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(a=Pt.call(this._shortMonthsParse,o))?a:null:-1!==(a=Pt.call(this._longMonthsParse,o))?a:null:"MMM"===e?-1!==(a=Pt.call(this._shortMonthsParse,o))?a:-1!==(a=Pt.call(this._longMonthsParse,o))?a:null:-1!==(a=Pt.call(this._longMonthsParse,o))?a:-1!==(a=Pt.call(this._shortMonthsParse,o))?a:null}function Yt(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=k(e);else if(!l(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),It(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function zt(t){return null!=t?(Yt(this,t),a.updateOffset(this,!0),this):At(this,"Month")}var Et=lt,Vt=lt;function Ht(){function t(t,e){return e.length-t.length}var e,n,i=[],a=[],r=[];for(e=0;e<12;e++)n=f([2e3,e]),i.push(this.monthsShort(n,"")),a.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(i.sort(t),a.sort(t),r.sort(t),e=0;e<12;e++)i[e]=ct(i[e]),a[e]=ct(a[e]);for(e=0;e<24;e++)r[e]=ct(r[e]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+i.join("|")+")","i")}function Bt(t,e,n,i,a,r,o){var s;return t<100&&t>=0?(s=new Date(t+400,e,n,i,a,r,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,i,a,r,o),s}function jt(t){var e;if(t<100&&t>=0){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Ut(t,e,n){var i=7+e-n;return-(7+jt(t,0,i).getUTCDay()-e)%7+i-1}function Gt(t,e,n,i,a){var r,o,s=1+7*(e-1)+(7+n-i)%7+Ut(t,i,a);return s<=0?o=Dt(r=t-1)+s:s>Dt(t)?(r=t+1,o=s-Dt(t)):(r=t,o=s),{year:r,dayOfYear:o}}function qt(t,e,n){var i,a,r=Ut(t.year(),e,n),o=Math.floor((t.dayOfYear()-r-1)/7)+1;return o<1?i=o+Zt(a=t.year()-1,e,n):o>Zt(t.year(),e,n)?(i=o-Zt(t.year(),e,n),a=t.year()+1):(a=t.year(),i=o),{week:i,year:a}}function Zt(t,e,n){var i=Ut(t,e,n),a=Ut(t+1,e,n);return(Dt(t)-i+a)/7}function $t(t,e){return t.slice(e,7).concat(t.slice(0,e))}j("w",["ww",2],"wo","week"),j("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),Y("week",5),Y("isoWeek",5),dt("w",J),dt("ww",J,Z),dt("W",J),dt("WW",J,Z),mt(["w","ww","W","WW"],(function(t,e,n,i){e[i.substr(0,1)]=k(t)})),j("d",0,"do","day"),j("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),j("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),j("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),j("e",0,0,"weekday"),j("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),Y("day",11),Y("weekday",11),Y("isoWeekday",11),dt("d",J),dt("e",J),dt("E",J),dt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),dt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),dt("dddd",(function(t,e){return e.weekdaysRegex(t)})),mt(["dd","ddd","dddd"],(function(t,e,n,i){var a=n._locale.weekdaysParse(t,i,n._strict);null!=a?e.d=a:g(n).invalidWeekday=t})),mt(["d","e","E"],(function(t,e,n,i){e[i]=k(t)}));var Xt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Kt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Jt="Su_Mo_Tu_We_Th_Fr_Sa".split("_");function Qt(t,e,n){var i,a,r,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],i=0;i<7;++i)r=f([2e3,1]).day(i),this._minWeekdaysParse[i]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[i]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[i]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(a=Pt.call(this._weekdaysParse,o))?a:null:"ddd"===e?-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:null:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:"dddd"===e?-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:"ddd"===e?-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:null:-1!==(a=Pt.call(this._minWeekdaysParse,o))?a:-1!==(a=Pt.call(this._weekdaysParse,o))?a:-1!==(a=Pt.call(this._shortWeekdaysParse,o))?a:null}var te=lt,ee=lt,ne=lt;function ie(){function t(t,e){return e.length-t.length}var e,n,i,a,r,o=[],s=[],l=[],u=[];for(e=0;e<7;e++)n=f([2e3,1]).day(e),i=this.weekdaysMin(n,""),a=this.weekdaysShort(n,""),r=this.weekdays(n,""),o.push(i),s.push(a),l.push(r),u.push(i),u.push(a),u.push(r);for(o.sort(t),s.sort(t),l.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ct(s[e]),l[e]=ct(l[e]),u[e]=ct(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function ae(){return this.hours()%12||12}function re(t,e){j(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function oe(t,e){return e._meridiemParse}j("H",["HH",2],0,"hour"),j("h",["hh",2],0,ae),j("k",["kk",2],0,(function(){return this.hours()||24})),j("hmm",0,0,(function(){return""+ae.apply(this)+z(this.minutes(),2)})),j("hmmss",0,0,(function(){return""+ae.apply(this)+z(this.minutes(),2)+z(this.seconds(),2)})),j("Hmm",0,0,(function(){return""+this.hours()+z(this.minutes(),2)})),j("Hmmss",0,0,(function(){return""+this.hours()+z(this.minutes(),2)+z(this.seconds(),2)})),re("a",!0),re("A",!1),L("hour","h"),Y("hour",13),dt("a",oe),dt("A",oe),dt("H",J),dt("h",J),dt("k",J),dt("HH",J,Z),dt("hh",J,Z),dt("kk",J,Z),dt("hmm",Q),dt("hmmss",tt),dt("Hmm",Q),dt("Hmmss",tt),gt(["H","HH"],xt),gt(["k","kk"],(function(t,e,n){var i=k(t);e[xt]=24===i?0:i})),gt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),gt(["h","hh"],(function(t,e,n){e[xt]=k(t),g(n).bigHour=!0})),gt("hmm",(function(t,e,n){var i=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i)),g(n).bigHour=!0})),gt("hmmss",(function(t,e,n){var i=t.length-4,a=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i,2)),e[wt]=k(t.substr(a)),g(n).bigHour=!0})),gt("Hmm",(function(t,e,n){var i=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i))})),gt("Hmmss",(function(t,e,n){var i=t.length-4,a=t.length-2;e[xt]=k(t.substr(0,i)),e[_t]=k(t.substr(i,2)),e[wt]=k(t.substr(a))}));var se,le=Ot("Hours",!0),ue={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Rt,monthsShort:Nt,week:{dow:0,doy:6},weekdays:Xt,weekdaysMin:Jt,weekdaysShort:Kt,meridiemParse:/[ap]\.?m?\.?/i},de={},he={};function ce(t){return t?t.toLowerCase().replace("_","-"):t}function fe(n){var i=null;if(!de[n]&&e&&e.exports)try{i=se._abbr,t(),ge(i)}catch(t){}return de[n]}function ge(t,e){var n;return t&&((n=s(e)?pe(t):me(t,e))?se=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),se._abbr}function me(t,e){if(null!==e){var n,i=ue;if(e.abbr=t,null!=de[t])T("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),i=de[t]._config;else if(null!=e.parentLocale)if(null!=de[e.parentLocale])i=de[e.parentLocale]._config;else{if(null==(n=fe(e.parentLocale)))return he[e.parentLocale]||(he[e.parentLocale]=[]),he[e.parentLocale].push({name:t,config:e}),null;i=n._config}return de[t]=new F(A(i,e)),he[t]&&he[t].forEach((function(t){me(t.name,t.config)})),ge(t),de[t]}return delete de[t],null}function pe(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return se;if(!r(t)){if(e=fe(t))return e;t=[t]}return function(t){for(var e,n,i,a,r=0;r<t.length;){for(e=(a=ce(t[r]).split("-")).length,n=(n=ce(t[r+1]))?n.split("-"):null;e>0;){if(i=fe(a.slice(0,e).join("-")))return i;if(n&&n.length>=e&&M(a,n,!0)>=e-1)break;e--}r++}return se}(t)}function ve(t){var e,n=t._a;return n&&-2===g(t).overflow&&(e=n[bt]<0||n[bt]>11?bt:n[yt]<1||n[yt]>It(n[vt],n[bt])?yt:n[xt]<0||n[xt]>24||24===n[xt]&&(0!==n[_t]||0!==n[wt]||0!==n[kt])?xt:n[_t]<0||n[_t]>59?_t:n[wt]<0||n[wt]>59?wt:n[kt]<0||n[kt]>999?kt:-1,g(t)._overflowDayOfYear&&(e<vt||e>yt)&&(e=yt),g(t)._overflowWeeks&&-1===e&&(e=Mt),g(t)._overflowWeekday&&-1===e&&(e=St),g(t).overflow=e),t}function be(t,e,n){return null!=t?t:null!=e?e:n}function ye(t){var e,n,i,r,o,s=[];if(!t._d){for(i=function(t){var e=new Date(a.now());return t._useUTC?[e.getUTCFullYear(),e.getUTCMonth(),e.getUTCDate()]:[e.getFullYear(),e.getMonth(),e.getDate()]}(t),t._w&&null==t._a[yt]&&null==t._a[bt]&&function(t){var e,n,i,a,r,o,s,l;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)r=1,o=4,n=be(e.GG,t._a[vt],qt(Le(),1,4).year),i=be(e.W,1),((a=be(e.E,1))<1||a>7)&&(l=!0);else{r=t._locale._week.dow,o=t._locale._week.doy;var u=qt(Le(),r,o);n=be(e.gg,t._a[vt],u.year),i=be(e.w,u.week),null!=e.d?((a=e.d)<0||a>6)&&(l=!0):null!=e.e?(a=e.e+r,(e.e<0||e.e>6)&&(l=!0)):a=r}i<1||i>Zt(n,r,o)?g(t)._overflowWeeks=!0:null!=l?g(t)._overflowWeekday=!0:(s=Gt(n,i,a,r,o),t._a[vt]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=be(t._a[vt],i[vt]),(t._dayOfYear>Dt(o)||0===t._dayOfYear)&&(g(t)._overflowDayOfYear=!0),n=jt(o,0,t._dayOfYear),t._a[bt]=n.getUTCMonth(),t._a[yt]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=i[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[xt]&&0===t._a[_t]&&0===t._a[wt]&&0===t._a[kt]&&(t._nextDay=!0,t._a[xt]=0),t._d=(t._useUTC?jt:Bt).apply(null,s),r=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[xt]=24),t._w&&void 0!==t._w.d&&t._w.d!==r&&(g(t).weekdayMismatch=!0)}}var xe=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,_e=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,we=/Z|[+-]\d\d(?::?\d\d)?/,ke=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Me=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Se=/^\/?Date\((\-?\d+)/i;function De(t){var e,n,i,a,r,o,s=t._i,l=xe.exec(s)||_e.exec(s);if(l){for(g(t).iso=!0,e=0,n=ke.length;e<n;e++)if(ke[e][1].exec(l[1])){a=ke[e][0],i=!1!==ke[e][2];break}if(null==a)return void(t._isValid=!1);if(l[3]){for(e=0,n=Me.length;e<n;e++)if(Me[e][1].exec(l[3])){r=(l[2]||" ")+Me[e][0];break}if(null==r)return void(t._isValid=!1)}if(!i&&null!=r)return void(t._isValid=!1);if(l[4]){if(!we.exec(l[4]))return void(t._isValid=!1);o="Z"}t._f=a+(r||"")+(o||""),Ae(t)}else t._isValid=!1}var Ce=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;function Pe(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}var Te={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Oe(t){var e,n,i,a,r,o,s,l=Ce.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(l){var u=(e=l[4],n=l[3],i=l[2],a=l[5],r=l[6],o=l[7],s=[Pe(e),Nt.indexOf(n),parseInt(i,10),parseInt(a,10),parseInt(r,10)],o&&s.push(parseInt(o,10)),s);if(!function(t,e,n){return!t||Kt.indexOf(t)===new Date(e[0],e[1],e[2]).getDay()||(g(n).weekdayMismatch=!0,n._isValid=!1,!1)}(l[1],u,t))return;t._a=u,t._tzm=function(t,e,n){if(t)return Te[t];if(e)return 0;var i=parseInt(n,10),a=i%100;return(i-a)/100*60+a}(l[8],l[9],l[10]),t._d=jt.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),g(t).rfc2822=!0}else t._isValid=!1}function Ae(t){if(t._f!==a.ISO_8601)if(t._f!==a.RFC_2822){t._a=[],g(t).empty=!0;var e,n,i,r,o,s=""+t._i,l=s.length,u=0;for(i=G(t._f,t._locale).match(E)||[],e=0;e<i.length;e++)r=i[e],(n=(s.match(ht(r,t))||[])[0])&&((o=s.substr(0,s.indexOf(n))).length>0&&g(t).unusedInput.push(o),s=s.slice(s.indexOf(n)+n.length),u+=n.length),B[r]?(n?g(t).empty=!1:g(t).unusedTokens.push(r),pt(r,n,t)):t._strict&&!n&&g(t).unusedTokens.push(r);g(t).charsLeftOver=l-u,s.length>0&&g(t).unusedInput.push(s),t._a[xt]<=12&&!0===g(t).bigHour&&t._a[xt]>0&&(g(t).bigHour=void 0),g(t).parsedDateParts=t._a.slice(0),g(t).meridiem=t._meridiem,t._a[xt]=function(t,e,n){var i;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):null!=t.isPM?((i=t.isPM(n))&&e<12&&(e+=12),i||12!==e||(e=0),e):e}(t._locale,t._a[xt],t._meridiem),ye(t),ve(t)}else Oe(t);else De(t)}function Fe(t){var e=t._i,n=t._f;return t._locale=t._locale||pe(t._l),null===e||void 0===n&&""===e?p({nullInput:!0}):("string"==typeof e&&(t._i=e=t._locale.preparse(e)),_(e)?new x(ve(e)):(u(e)?t._d=e:r(n)?function(t){var e,n,i,a,r;if(0===t._f.length)return g(t).invalidFormat=!0,void(t._d=new Date(NaN));for(a=0;a<t._f.length;a++)r=0,e=b({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[a],Ae(e),m(e)&&(r+=g(e).charsLeftOver,r+=10*g(e).unusedTokens.length,g(e).score=r,(null==i||r<i)&&(i=r,n=e));c(t,n||e)}(t):n?Ae(t):function(t){var e=t._i;s(e)?t._d=new Date(a.now()):u(e)?t._d=new Date(e.valueOf()):"string"==typeof e?function(t){var e=Se.exec(t._i);null===e?(De(t),!1===t._isValid&&(delete t._isValid,Oe(t),!1===t._isValid&&(delete t._isValid,a.createFromInputFallback(t)))):t._d=new Date(+e[1])}(t):r(e)?(t._a=d(e.slice(0),(function(t){return parseInt(t,10)})),ye(t)):o(e)?function(t){if(!t._d){var e=N(t._i);t._a=d([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),ye(t)}}(t):l(e)?t._d=new Date(e):a.createFromInputFallback(t)}(t),m(t)||(t._d=null),t))}function Ie(t,e,n,i,a){var s,l={};return!0!==n&&!1!==n||(i=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||r(t)&&0===t.length)&&(t=void 0),l._isAMomentObject=!0,l._useUTC=l._isUTC=a,l._l=n,l._i=t,l._f=e,l._strict=i,(s=new x(ve(Fe(l))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function Le(t,e,n,i){return Ie(t,e,n,i,!1)}a.createFromInputFallback=D("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),a.ISO_8601=function(){},a.RFC_2822=function(){};var Re=D("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=Le.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:p()})),Ne=D("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=Le.apply(null,arguments);return this.isValid()&&t.isValid()?t>this?this:t:p()}));function We(t,e){var n,i;if(1===e.length&&r(e[0])&&(e=e[0]),!e.length)return Le();for(n=e[0],i=1;i<e.length;++i)e[i].isValid()&&!e[i][t](n)||(n=e[i]);return n}var Ye=["year","quarter","month","week","day","hour","minute","second","millisecond"];function ze(t){var e=N(t),n=e.year||0,i=e.quarter||0,a=e.month||0,r=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,l=e.minute||0,u=e.second||0,d=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===Pt.call(Ye,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,i=0;i<Ye.length;++i)if(t[Ye[i]]){if(n)return!1;parseFloat(t[Ye[i]])!==k(t[Ye[i]])&&(n=!0)}return!0}(e),this._milliseconds=+d+1e3*u+6e4*l+1e3*s*60*60,this._days=+o+7*r,this._months=+a+3*i+12*n,this._data={},this._locale=pe(),this._bubble()}function Ee(t){return t instanceof ze}function Ve(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function He(t,e){j(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+z(~~(t/60),2)+e+z(~~t%60,2)}))}He("Z",":"),He("ZZ",""),dt("Z",st),dt("ZZ",st),gt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=je(st,t)}));var Be=/([\+\-]|\d\d)/gi;function je(t,e){var n=(e||"").match(t);if(null===n)return null;var i=((n[n.length-1]||[])+"").match(Be)||["-",0,0],a=60*i[1]+k(i[2]);return 0===a?0:"+"===i[0]?a:-a}function Ue(t,e){var n,i;return e._isUTC?(n=e.clone(),i=(_(t)||u(t)?t.valueOf():Le(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+i),a.updateOffset(n,!1),n):Le(t).local()}function Ge(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function qe(){return!!this.isValid()&&this._isUTC&&0===this._offset}a.updateOffset=function(){};var Ze=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,$e=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Xe(t,e){var n,i,a,r,o,s,u=t,d=null;return Ee(t)?u={ms:t._milliseconds,d:t._days,M:t._months}:l(t)?(u={},e?u[e]=t:u.milliseconds=t):(d=Ze.exec(t))?(n="-"===d[1]?-1:1,u={y:0,d:k(d[yt])*n,h:k(d[xt])*n,m:k(d[_t])*n,s:k(d[wt])*n,ms:k(Ve(1e3*d[kt]))*n}):(d=$e.exec(t))?(n="-"===d[1]?-1:1,u={y:Ke(d[2],n),M:Ke(d[3],n),w:Ke(d[4],n),d:Ke(d[5],n),h:Ke(d[6],n),m:Ke(d[7],n),s:Ke(d[8],n)}):null==u?u={}:"object"==typeof u&&("from"in u||"to"in u)&&(r=Le(u.from),o=Le(u.to),a=r.isValid()&&o.isValid()?(o=Ue(o,r),r.isBefore(o)?s=Je(r,o):((s=Je(o,r)).milliseconds=-s.milliseconds,s.months=-s.months),s):{milliseconds:0,months:0},(u={}).ms=a.milliseconds,u.M=a.months),i=new ze(u),Ee(t)&&h(t,"_locale")&&(i._locale=t._locale),i}function Ke(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function Je(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function Qe(t,e){return function(n,i){var a;return null===i||isNaN(+i)||(T(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),a=n,n=i,i=a),tn(this,Xe(n="string"==typeof n?+n:n,i),t),this}}function tn(t,e,n,i){var r=e._milliseconds,o=Ve(e._days),s=Ve(e._months);t.isValid()&&(i=null==i||i,s&&Yt(t,At(t,"Month")+s*n),o&&Ft(t,"Date",At(t,"Date")+o*n),r&&t._d.setTime(t._d.valueOf()+r*n),i&&a.updateOffset(t,o||s))}Xe.fn=ze.prototype,Xe.invalid=function(){return Xe(NaN)};var en=Qe(1,"add"),nn=Qe(-1,"subtract");function an(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),i=t.clone().add(n,"months");return-(n+(e-i<0?(e-i)/(i-t.clone().add(n-1,"months")):(e-i)/(t.clone().add(n+1,"months")-i)))||0}function rn(t){var e;return void 0===t?this._locale._abbr:(null!=(e=pe(t))&&(this._locale=e),this)}a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var on=D("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function sn(){return this._locale}var ln=1e3,un=60*ln,dn=60*un,hn=3506328*dn;function cn(t,e){return(t%e+e)%e}function fn(t,e,n){return t<100&&t>=0?new Date(t+400,e,n)-hn:new Date(t,e,n).valueOf()}function gn(t,e,n){return t<100&&t>=0?Date.UTC(t+400,e,n)-hn:Date.UTC(t,e,n)}function mn(t,e){j(0,[t,t.length],0,e)}function pn(t,e,n,i,a){var r;return null==t?qt(this,i,a).year:(e>(r=Zt(t,i,a))&&(e=r),vn.call(this,t,e,n,i,a))}function vn(t,e,n,i,a){var r=Gt(t,e,n,i,a),o=jt(r.year,0,r.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}j(0,["gg",2],0,(function(){return this.weekYear()%100})),j(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),mn("gggg","weekYear"),mn("ggggg","weekYear"),mn("GGGG","isoWeekYear"),mn("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),Y("weekYear",1),Y("isoWeekYear",1),dt("G",rt),dt("g",rt),dt("GG",J,Z),dt("gg",J,Z),dt("GGGG",nt,X),dt("gggg",nt,X),dt("GGGGG",it,K),dt("ggggg",it,K),mt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,i){e[i.substr(0,2)]=k(t)})),mt(["gg","GG"],(function(t,e,n,i){e[i]=a.parseTwoDigitYear(t)})),j("Q",0,"Qo","quarter"),L("quarter","Q"),Y("quarter",7),dt("Q",q),gt("Q",(function(t,e){e[bt]=3*(k(t)-1)})),j("D",["DD",2],"Do","date"),L("date","D"),Y("date",9),dt("D",J),dt("DD",J,Z),dt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),gt(["D","DD"],yt),gt("Do",(function(t,e){e[yt]=k(t.match(J)[0])}));var bn=Ot("Date",!0);j("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),Y("dayOfYear",4),dt("DDD",et),dt("DDDD",$),gt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=k(t)})),j("m",["mm",2],0,"minute"),L("minute","m"),Y("minute",14),dt("m",J),dt("mm",J,Z),gt(["m","mm"],_t);var yn=Ot("Minutes",!1);j("s",["ss",2],0,"second"),L("second","s"),Y("second",15),dt("s",J),dt("ss",J,Z),gt(["s","ss"],wt);var xn,_n=Ot("Seconds",!1);for(j("S",0,0,(function(){return~~(this.millisecond()/100)})),j(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),j(0,["SSS",3],0,"millisecond"),j(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),j(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),j(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),j(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),j(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),j(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),Y("millisecond",16),dt("S",et,q),dt("SS",et,Z),dt("SSS",et,$),xn="SSSS";xn.length<=9;xn+="S")dt(xn,at);function wn(t,e){e[kt]=k(1e3*("0."+t))}for(xn="S";xn.length<=9;xn+="S")gt(xn,wn);var kn=Ot("Milliseconds",!1);j("z",0,0,"zoneAbbr"),j("zz",0,0,"zoneName");var Mn=x.prototype;function Sn(t){return t}Mn.add=en,Mn.calendar=function(t,e){var n=t||Le(),i=Ue(n,this).startOf("day"),r=a.calendarFormat(this,i)||"sameElse",o=e&&(O(e[r])?e[r].call(this,n):e[r]);return this.format(o||this.localeData().calendar(r,this,Le(n)))},Mn.clone=function(){return new x(this)},Mn.diff=function(t,e,n){var i,a,r;if(!this.isValid())return NaN;if(!(i=Ue(t,this)).isValid())return NaN;switch(a=6e4*(i.utcOffset()-this.utcOffset()),e=R(e)){case"year":r=an(this,i)/12;break;case"month":r=an(this,i);break;case"quarter":r=an(this,i)/3;break;case"second":r=(this-i)/1e3;break;case"minute":r=(this-i)/6e4;break;case"hour":r=(this-i)/36e5;break;case"day":r=(this-i-a)/864e5;break;case"week":r=(this-i-a)/6048e5;break;default:r=this-i}return n?r:w(r)},Mn.endOf=function(t){var e;if(void 0===(t=R(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?gn:fn;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=dn-cn(e+(this._isUTC?0:this.utcOffset()*un),dn)-1;break;case"minute":e=this._d.valueOf(),e+=un-cn(e,un)-1;break;case"second":e=this._d.valueOf(),e+=ln-cn(e,ln)-1}return this._d.setTime(e),a.updateOffset(this,!0),this},Mn.format=function(t){t||(t=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var e=U(this,t);return this.localeData().postformat(e)},Mn.from=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||Le(t).isValid())?Xe({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Mn.fromNow=function(t){return this.from(Le(),t)},Mn.to=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||Le(t).isValid())?Xe({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},Mn.toNow=function(t){return this.to(Le(),t)},Mn.get=function(t){return O(this[t=R(t)])?this[t]():this},Mn.invalidAt=function(){return g(this).overflow},Mn.isAfter=function(t,e){var n=_(t)?t:Le(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},Mn.isBefore=function(t,e){var n=_(t)?t:Le(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},Mn.isBetween=function(t,e,n,i){var a=_(t)?t:Le(t),r=_(e)?e:Le(e);return!!(this.isValid()&&a.isValid()&&r.isValid())&&("("===(i=i||"()")[0]?this.isAfter(a,n):!this.isBefore(a,n))&&(")"===i[1]?this.isBefore(r,n):!this.isAfter(r,n))},Mn.isSame=function(t,e){var n,i=_(t)?t:Le(t);return!(!this.isValid()||!i.isValid())&&("millisecond"===(e=R(e)||"millisecond")?this.valueOf()===i.valueOf():(n=i.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},Mn.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},Mn.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},Mn.isValid=function(){return m(this)},Mn.lang=on,Mn.locale=rn,Mn.localeData=sn,Mn.max=Ne,Mn.min=Re,Mn.parsingFlags=function(){return c({},g(this))},Mn.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:W[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=N(t)),i=0;i<n.length;i++)this[n[i].unit](t[n[i].unit]);else if(O(this[t=R(t)]))return this[t](e);return this},Mn.startOf=function(t){var e;if(void 0===(t=R(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?gn:fn;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=cn(e+(this._isUTC?0:this.utcOffset()*un),dn);break;case"minute":e=this._d.valueOf(),e-=cn(e,un);break;case"second":e=this._d.valueOf(),e-=cn(e,ln)}return this._d.setTime(e),a.updateOffset(this,!0),this},Mn.subtract=nn,Mn.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},Mn.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},Mn.toDate=function(){return new Date(this.valueOf())},Mn.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||n.year()>9999?U(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):O(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",U(n,"Z")):U(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},Mn.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',i=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",a=e+'[")]';return this.format(n+i+"-MM-DD[T]HH:mm:ss.SSS"+a)},Mn.toJSON=function(){return this.isValid()?this.toISOString():null},Mn.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},Mn.unix=function(){return Math.floor(this.valueOf()/1e3)},Mn.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},Mn.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},Mn.year=Tt,Mn.isLeapYear=function(){return Ct(this.year())},Mn.weekYear=function(t){return pn.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},Mn.isoWeekYear=function(t){return pn.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},Mn.quarter=Mn.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},Mn.month=zt,Mn.daysInMonth=function(){return It(this.year(),this.month())},Mn.week=Mn.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},Mn.isoWeek=Mn.isoWeeks=function(t){var e=qt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},Mn.weeksInYear=function(){var t=this.localeData()._week;return Zt(this.year(),t.dow,t.doy)},Mn.isoWeeksInYear=function(){return Zt(this.year(),1,4)},Mn.date=bn,Mn.day=Mn.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(t=function(t,e){return"string"!=typeof t?t:isNaN(t)?"number"==typeof(t=e.weekdaysParse(t))?t:null:parseInt(t,10)}(t,this.localeData()),this.add(t-e,"d")):e},Mn.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},Mn.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null!=t){var e=function(t,e){return"string"==typeof t?e.weekdaysParse(t)%7||7:isNaN(t)?null:t}(t,this.localeData());return this.day(this.day()%7?e:e-7)}return this.day()||7},Mn.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},Mn.hour=Mn.hours=le,Mn.minute=Mn.minutes=yn,Mn.second=Mn.seconds=_n,Mn.millisecond=Mn.milliseconds=kn,Mn.utcOffset=function(t,e,n){var i,r=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if("string"==typeof t){if(null===(t=je(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(i=Ge(this)),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),r!==t&&(!e||this._changeInProgress?tn(this,Xe(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?r:Ge(this)},Mn.utc=function(t){return this.utcOffset(0,t)},Mn.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ge(this),"m")),this},Mn.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=je(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},Mn.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?Le(t).utcOffset():0,(this.utcOffset()-t)%60==0)},Mn.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},Mn.isLocal=function(){return!!this.isValid()&&!this._isUTC},Mn.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},Mn.isUtc=qe,Mn.isUTC=qe,Mn.zoneAbbr=function(){return this._isUTC?"UTC":""},Mn.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},Mn.dates=D("dates accessor is deprecated. Use date instead.",bn),Mn.months=D("months accessor is deprecated. Use month instead",zt),Mn.years=D("years accessor is deprecated. Use year instead",Tt),Mn.zone=D("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),Mn.isDSTShifted=D("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(b(t,this),(t=Fe(t))._a){var e=t._isUTC?f(t._a):Le(t._a);this._isDSTShifted=this.isValid()&&M(t._a,e.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}));var Dn=F.prototype;function Cn(t,e,n,i){var a=pe(),r=f().set(i,e);return a[n](r,t)}function Pn(t,e,n){if(l(t)&&(e=t,t=void 0),t=t||"",null!=e)return Cn(t,e,n,"month");var i,a=[];for(i=0;i<12;i++)a[i]=Cn(t,i,n,"month");return a}function Tn(t,e,n,i){"boolean"==typeof t?(l(e)&&(n=e,e=void 0),e=e||""):(n=e=t,t=!1,l(e)&&(n=e,e=void 0),e=e||"");var a,r=pe(),o=t?r._week.dow:0;if(null!=n)return Cn(e,(n+o)%7,i,"day");var s=[];for(a=0;a<7;a++)s[a]=Cn(e,(a+o)%7,i,"day");return s}Dn.calendar=function(t,e,n){var i=this._calendar[t]||this._calendar.sameElse;return O(i)?i.call(e,n):i},Dn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},Dn.invalidDate=function(){return this._invalidDate},Dn.ordinal=function(t){return this._ordinal.replace("%d",t)},Dn.preparse=Sn,Dn.postformat=Sn,Dn.relativeTime=function(t,e,n,i){var a=this._relativeTime[n];return O(a)?a(t,e,n,i):a.replace(/%d/i,t)},Dn.pastFuture=function(t,e){var n=this._relativeTime[t>0?"future":"past"];return O(n)?n(e):n.replace(/%s/i,e)},Dn.set=function(t){var e,n;for(n in t)O(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},Dn.months=function(t,e){return t?r(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Lt).test(e)?"format":"standalone"][t.month()]:r(this._months)?this._months:this._months.standalone},Dn.monthsShort=function(t,e){return t?r(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Lt.test(e)?"format":"standalone"][t.month()]:r(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},Dn.monthsParse=function(t,e,n){var i,a,r;if(this._monthsParseExact)return Wt.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),i=0;i<12;i++){if(a=f([2e3,i]),n&&!this._longMonthsParse[i]&&(this._longMonthsParse[i]=new RegExp("^"+this.months(a,"").replace(".","")+"$","i"),this._shortMonthsParse[i]=new RegExp("^"+this.monthsShort(a,"").replace(".","")+"$","i")),n||this._monthsParse[i]||(r="^"+this.months(a,"")+"|^"+this.monthsShort(a,""),this._monthsParse[i]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[i].test(t))return i;if(n&&"MMM"===e&&this._shortMonthsParse[i].test(t))return i;if(!n&&this._monthsParse[i].test(t))return i}},Dn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Ht.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Vt),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},Dn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Ht.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Et),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},Dn.week=function(t){return qt(t,this._week.dow,this._week.doy).week},Dn.firstDayOfYear=function(){return this._week.doy},Dn.firstDayOfWeek=function(){return this._week.dow},Dn.weekdays=function(t,e){var n=r(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?$t(n,this._week.dow):t?n[t.day()]:n},Dn.weekdaysMin=function(t){return!0===t?$t(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},Dn.weekdaysShort=function(t){return!0===t?$t(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},Dn.weekdaysParse=function(t,e,n){var i,a,r;if(this._weekdaysParseExact)return Qt.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),i=0;i<7;i++){if(a=f([2e3,1]).day(i),n&&!this._fullWeekdaysParse[i]&&(this._fullWeekdaysParse[i]=new RegExp("^"+this.weekdays(a,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[i]=new RegExp("^"+this.weekdaysShort(a,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[i]=new RegExp("^"+this.weekdaysMin(a,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[i]||(r="^"+this.weekdays(a,"")+"|^"+this.weekdaysShort(a,"")+"|^"+this.weekdaysMin(a,""),this._weekdaysParse[i]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[i].test(t))return i;if(n&&"ddd"===e&&this._shortWeekdaysParse[i].test(t))return i;if(n&&"dd"===e&&this._minWeekdaysParse[i].test(t))return i;if(!n&&this._weekdaysParse[i].test(t))return i}},Dn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=te),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},Dn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=ee),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},Dn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||ie.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=ne),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},Dn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},Dn.meridiem=function(t,e,n){return t>11?n?"pm":"PM":n?"am":"AM"},ge("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===k(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),a.lang=D("moment.lang is deprecated. Use moment.locale instead.",ge),a.langData=D("moment.langData is deprecated. Use moment.localeData instead.",pe);var On=Math.abs;function An(t,e,n,i){var a=Xe(e,n);return t._milliseconds+=i*a._milliseconds,t._days+=i*a._days,t._months+=i*a._months,t._bubble()}function Fn(t){return t<0?Math.floor(t):Math.ceil(t)}function In(t){return 4800*t/146097}function Ln(t){return 146097*t/4800}function Rn(t){return function(){return this.as(t)}}var Nn=Rn("ms"),Wn=Rn("s"),Yn=Rn("m"),zn=Rn("h"),En=Rn("d"),Vn=Rn("w"),Hn=Rn("M"),Bn=Rn("Q"),jn=Rn("y");function Un(t){return function(){return this.isValid()?this._data[t]:NaN}}var Gn=Un("milliseconds"),qn=Un("seconds"),Zn=Un("minutes"),$n=Un("hours"),Xn=Un("days"),Kn=Un("months"),Jn=Un("years"),Qn=Math.round,ti={ss:44,s:45,m:45,h:22,d:26,M:11};function ei(t,e,n,i,a){return a.relativeTime(e||1,!!n,t,i)}var ni=Math.abs;function ii(t){return(t>0)-(t<0)||+t}function ai(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=ni(this._milliseconds)/1e3,i=ni(this._days),a=ni(this._months);t=w(n/60),e=w(t/60),n%=60,t%=60;var r=w(a/12),o=a%=12,s=i,l=e,u=t,d=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var c=h<0?"-":"",f=ii(this._months)!==ii(h)?"-":"",g=ii(this._days)!==ii(h)?"-":"",m=ii(this._milliseconds)!==ii(h)?"-":"";return c+"P"+(r?f+r+"Y":"")+(o?f+o+"M":"")+(s?g+s+"D":"")+(l||u||d?"T":"")+(l?m+l+"H":"")+(u?m+u+"M":"")+(d?m+d+"S":"")}var ri=ze.prototype;return ri.isValid=function(){return this._isValid},ri.abs=function(){var t=this._data;return this._milliseconds=On(this._milliseconds),this._days=On(this._days),this._months=On(this._months),t.milliseconds=On(t.milliseconds),t.seconds=On(t.seconds),t.minutes=On(t.minutes),t.hours=On(t.hours),t.months=On(t.months),t.years=On(t.years),this},ri.add=function(t,e){return An(this,t,e,1)},ri.subtract=function(t,e){return An(this,t,e,-1)},ri.as=function(t){if(!this.isValid())return NaN;var e,n,i=this._milliseconds;if("month"===(t=R(t))||"quarter"===t||"year"===t)switch(e=this._days+i/864e5,n=this._months+In(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(Ln(this._months)),t){case"week":return e/7+i/6048e5;case"day":return e+i/864e5;case"hour":return 24*e+i/36e5;case"minute":return 1440*e+i/6e4;case"second":return 86400*e+i/1e3;case"millisecond":return Math.floor(864e5*e)+i;default:throw new Error("Unknown unit "+t)}},ri.asMilliseconds=Nn,ri.asSeconds=Wn,ri.asMinutes=Yn,ri.asHours=zn,ri.asDays=En,ri.asWeeks=Vn,ri.asMonths=Hn,ri.asQuarters=Bn,ri.asYears=jn,ri.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*k(this._months/12):NaN},ri._bubble=function(){var t,e,n,i,a,r=this._milliseconds,o=this._days,s=this._months,l=this._data;return r>=0&&o>=0&&s>=0||r<=0&&o<=0&&s<=0||(r+=864e5*Fn(Ln(s)+o),o=0,s=0),l.milliseconds=r%1e3,t=w(r/1e3),l.seconds=t%60,e=w(t/60),l.minutes=e%60,n=w(e/60),l.hours=n%24,o+=w(n/24),a=w(In(o)),s+=a,o-=Fn(Ln(a)),i=w(s/12),s%=12,l.days=o,l.months=s,l.years=i,this},ri.clone=function(){return Xe(this)},ri.get=function(t){return t=R(t),this.isValid()?this[t+"s"]():NaN},ri.milliseconds=Gn,ri.seconds=qn,ri.minutes=Zn,ri.hours=$n,ri.days=Xn,ri.weeks=function(){return w(this.days()/7)},ri.months=Kn,ri.years=Jn,ri.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e=this.localeData(),n=function(t,e,n){var i=Xe(t).abs(),a=Qn(i.as("s")),r=Qn(i.as("m")),o=Qn(i.as("h")),s=Qn(i.as("d")),l=Qn(i.as("M")),u=Qn(i.as("y")),d=a<=ti.ss&&["s",a]||a<ti.s&&["ss",a]||r<=1&&["m"]||r<ti.m&&["mm",r]||o<=1&&["h"]||o<ti.h&&["hh",o]||s<=1&&["d"]||s<ti.d&&["dd",s]||l<=1&&["M"]||l<ti.M&&["MM",l]||u<=1&&["y"]||["yy",u];return d[2]=e,d[3]=+t>0,d[4]=n,ei.apply(null,d)}(this,!t,e);return t&&(n=e.pastFuture(+this,n)),e.postformat(n)},ri.toISOString=ai,ri.toString=ai,ri.toJSON=ai,ri.locale=rn,ri.localeData=sn,ri.toIsoString=D("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ai),ri.lang=on,j("X",0,0,"unix"),j("x",0,0,"valueOf"),dt("x",rt),dt("X",/[+-]?\d+(\.\d{1,3})?/),gt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),gt("x",(function(t,e,n){n._d=new Date(k(t))})),a.version="2.24.0",n=Le,a.fn=Mn,a.min=function(){return We("isBefore",[].slice.call(arguments,0))},a.max=function(){return We("isAfter",[].slice.call(arguments,0))},a.now=function(){return Date.now?Date.now():+new Date},a.utc=f,a.unix=function(t){return Le(1e3*t)},a.months=function(t,e){return Pn(t,e,"months")},a.isDate=u,a.locale=ge,a.invalid=p,a.duration=Xe,a.isMoment=_,a.weekdays=function(t,e,n){return Tn(t,e,n,"weekdays")},a.parseZone=function(){return Le.apply(null,arguments).parseZone()},a.localeData=pe,a.isDuration=Ee,a.monthsShort=function(t,e){return Pn(t,e,"monthsShort")},a.weekdaysMin=function(t,e,n){return Tn(t,e,n,"weekdaysMin")},a.defineLocale=me,a.updateLocale=function(t,e){if(null!=e){var n,i,a=ue;null!=(i=fe(t))&&(a=i._config),e=A(a,e),(n=new F(e)).parentLocale=de[t],de[t]=n,ge(t)}else null!=de[t]&&(null!=de[t].parentLocale?de[t]=de[t].parentLocale:null!=de[t]&&delete de[t]);return de[t]},a.locales=function(){return C(de)},a.weekdaysShort=function(t,e,n){return Tn(t,e,n,"weekdaysShort")},a.normalizeUnits=R,a.relativeTimeRounding=function(t){return void 0===t?Qn:"function"==typeof t&&(Qn=t,!0)},a.relativeTimeThreshold=function(t,e){return void 0!==ti[t]&&(void 0===e?ti[t]:(ti[t]=e,"s"===t&&(ti.ss=e-1),!0))},a.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},a.prototype=Mn,a.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},a}()})),mi={datetime:"MMM D, YYYY, h:mm:ss a",millisecond:"h:mm:ss.SSS a",second:"h:mm:ss a",minute:"h:mm a",hour:"hA",day:"MMM D",week:"ll",month:"MMM YYYY",quarter:"[Q]Q - YYYY",year:"YYYY"};rn._date.override("function"==typeof gi?{_id:"moment",formats:function(){return mi},parse:function(t,e){return"string"==typeof t&&"string"==typeof e?t=gi(t,e):t instanceof gi||(t=gi(t)),t.isValid()?t.valueOf():null},format:function(t,e){return gi(t).format(e)},add:function(t,e,n){return gi(t).add(e,n).valueOf()},diff:function(t,e,n){return gi(t).diff(gi(e),n)},startOf:function(t,e,n){return t=gi(t),"isoWeek"===e?t.isoWeekday(n).valueOf():t.startOf(e).valueOf()},endOf:function(t,e){return gi(t).endOf(e).valueOf()},_create:function(t){return gi(t)}}:{}),W._set("global",{plugins:{filler:{propagate:!0}}});var pi={dataset:function(t){var e=t.fill,n=t.chart,i=n.getDatasetMeta(e),a=i&&n.isDatasetVisible(e)&&i.dataset._children||[],r=a.length||0;return r?function(t,e){return e<r&&a[e]._view||null}:null},boundary:function(t){var e=t.boundary,n=e?e.x:null,i=e?e.y:null;return H.isArray(e)?function(t,n){return e[n]}:function(t){return{x:null===n?t.x:n,y:null===i?t.y:i}}}};function vi(t,e,n){var i,a=t._model||{},r=a.fill;if(void 0===r&&(r=!!a.backgroundColor),!1===r||null===r)return!1;if(!0===r)return"origin";if(i=parseFloat(r,10),isFinite(i)&&Math.floor(i)===i)return"-"!==r[0]&&"+"!==r[0]||(i=e+i),!(i===e||i<0||i>=n)&&i;switch(r){case"bottom":return"start";case"top":return"end";case"zero":return"origin";case"origin":case"start":case"end":return r;default:return!1}}function bi(t){return(t.el._scale||{}).getPointPositionForValue?function(t){var e,n,i,a,r,o=t.el._scale,s=o.options,l=o.chart.data.labels.length,u=t.fill,d=[];if(!l)return null;for(e=s.ticks.reverse?o.max:o.min,n=s.ticks.reverse?o.min:o.max,i=o.getPointPositionForValue(0,e),a=0;a<l;++a)r="start"===u||"end"===u?o.getPointPositionForValue(a,"start"===u?e:n):o.getBasePosition(a),s.gridLines.circular&&(r.cx=i.x,r.cy=i.y,r.angle=o.getIndexAngle(a)-Math.PI/2),d.push(r);return d}(t):function(t){var e,n=t.el._model||{},i=t.el._scale||{},a=t.fill,r=null;if(isFinite(a))return null;if("start"===a?r=void 0===n.scaleBottom?i.bottom:n.scaleBottom:"end"===a?r=void 0===n.scaleTop?i.top:n.scaleTop:void 0!==n.scaleZero?r=n.scaleZero:i.getBasePixel&&(r=i.getBasePixel()),null!=r){if(void 0!==r.x&&void 0!==r.y)return r;if(H.isFinite(r))return{x:(e=i.isHorizontal())?r:null,y:e?null:r}}return null}(t)}function yi(t,e,n){var i,a=t[e].fill,r=[e];if(!n)return a;for(;!1!==a&&-1===r.indexOf(a);){if(!isFinite(a))return a;if(!(i=t[a]))return!1;if(i.visible)return a;r.push(a),a=i.fill}return!1}function xi(t){var e=t.fill,n="dataset";return!1===e?null:(isFinite(e)||(n="boundary"),pi[n](t))}function _i(t){return t&&!t.skip}function wi(t,e,n,i,a){var r,o,s,l;if(i&&a){for(t.moveTo(e[0].x,e[0].y),r=1;r<i;++r)H.canvas.lineTo(t,e[r-1],e[r]);if(void 0===n[0].angle)for(t.lineTo(n[a-1].x,n[a-1].y),r=a-1;r>0;--r)H.canvas.lineTo(t,n[r],n[r-1],!0);else for(o=n[0].cx,s=n[0].cy,l=Math.sqrt(Math.pow(n[0].x-o,2)+Math.pow(n[0].y-s,2)),r=a-1;r>0;--r)t.arc(o,s,l,n[r].angle,n[r-1].angle,!0)}}function ki(t,e,n,i,a,r){var o,s,l,u,d,h,c,f,g=e.length,m=i.spanGaps,p=[],v=[],b=0,y=0;for(t.beginPath(),o=0,s=g;o<s;++o)d=n(u=e[l=o%g]._view,l,i),h=_i(u),c=_i(d),r&&void 0===f&&h&&(s=g+(f=o+1)),h&&c?(b=p.push(u),y=v.push(d)):b&&y&&(m?(h&&p.push(u),c&&v.push(d)):(wi(t,p,v,b,y),b=y=0,p=[],v=[]));wi(t,p,v,b,y),t.closePath(),t.fillStyle=a,t.fill()}var Mi={id:"filler",afterDatasetsUpdate:function(t,e){var n,i,a,r,o=(t.data.datasets||[]).length,s=e.propagate,l=[];for(i=0;i<o;++i)r=null,(a=(n=t.getDatasetMeta(i)).dataset)&&a._model&&a instanceof wt.Line&&(r={visible:t.isDatasetVisible(i),fill:vi(a,i,o),chart:t,el:a}),n.$filler=r,l.push(r);for(i=0;i<o;++i)(r=l[i])&&(r.fill=yi(l,i,s),r.boundary=bi(r),r.mapper=xi(r))},beforeDatasetsDraw:function(t){var e,n,i,a,r,o,s,l=t._getSortedVisibleDatasetMetas(),u=t.ctx;for(n=l.length-1;n>=0;--n)(e=l[n].$filler)&&e.visible&&(a=(i=e.el)._view,r=i._children||[],o=e.mapper,s=a.backgroundColor||W.global.defaultColor,o&&s&&r.length&&(H.canvas.clipArea(u,t.chartArea),ki(u,r,o,a,s,i._loop),H.canvas.unclipArea(u)))}},Si=H.rtl.getRtlAdapter,Di=H.noop,Ci=H.valueOrDefault;function Pi(t,e){return t.usePointStyle&&t.boxWidth>e?e:t.boxWidth}W._set("global",{legend:{display:!0,position:"top",align:"center",fullWidth:!0,reverse:!1,weight:1e3,onClick:function(t,e){var n=e.datasetIndex,i=this.chart,a=i.getDatasetMeta(n);a.hidden=null===a.hidden?!i.data.datasets[n].hidden:null,i.update()},onHover:null,onLeave:null,labels:{boxWidth:40,padding:10,generateLabels:function(t){var e=t.data.datasets,n=t.options.legend||{},i=n.labels&&n.labels.usePointStyle;return t._getSortedDatasetMetas().map((function(n){var a=n.controller.getStyle(i?0:void 0);return{text:e[n.index].label,fillStyle:a.backgroundColor,hidden:!t.isDatasetVisible(n.index),lineCap:a.borderCapStyle,lineDash:a.borderDash,lineDashOffset:a.borderDashOffset,lineJoin:a.borderJoinStyle,lineWidth:a.borderWidth,strokeStyle:a.borderColor,pointStyle:a.pointStyle,rotation:a.rotation,datasetIndex:n.index}}),this)}}},legendCallback:function(t){var e,n,i,a=document.createElement("ul"),r=t.data.datasets;for(a.setAttribute("class",t.id+"-legend"),e=0,n=r.length;e<n;e++)(i=a.appendChild(document.createElement("li"))).appendChild(document.createElement("span")).style.backgroundColor=r[e].backgroundColor,r[e].label&&i.appendChild(document.createTextNode(r[e].label));return a.outerHTML}});var Ti=$.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1},beforeUpdate:Di,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Di,beforeSetDimensions:Di,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Di,beforeBuildLabels:Di,buildLabels:function(){var t=this,e=t.options.labels||{},n=H.callback(e.generateLabels,[t.chart],t)||[];e.filter&&(n=n.filter((function(n){return e.filter(n,t.chart.data)}))),t.options.reverse&&n.reverse(),t.legendItems=n},afterBuildLabels:Di,beforeFit:Di,fit:function(){var t=this,e=t.options,n=e.labels,i=e.display,a=t.ctx,r=H.options._parseFont(n),o=r.size,s=t.legendHitBoxes=[],l=t.minSize,u=t.isHorizontal();if(u?(l.width=t.maxWidth,l.height=i?10:0):(l.width=i?10:0,l.height=t.maxHeight),i){if(a.font=r.string,u){var d=t.lineWidths=[0],h=0;a.textAlign="left",a.textBaseline="middle",H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;(0===e||d[d.length-1]+i+2*n.padding>l.width)&&(h+=o+n.padding,d[d.length-(e>0?0:1)]=0),s[e]={left:0,top:0,width:i,height:o},d[d.length-1]+=i+n.padding})),l.height+=h}else{var c=n.padding,f=t.columnWidths=[],g=t.columnHeights=[],m=n.padding,p=0,v=0;H.each(t.legendItems,(function(t,e){var i=Pi(n,o)+o/2+a.measureText(t.text).width;e>0&&v+o+2*c>l.height&&(m+=p+n.padding,f.push(p),g.push(v),p=0,v=0),p=Math.max(p,i),v+=o+c,s[e]={left:0,top:0,width:i,height:o}})),m+=p,f.push(p),g.push(v),l.width+=m}t.width=l.width,t.height=l.height}else t.width=l.width=t.height=l.height=0},afterFit:Di,isHorizontal:function(){return"top"===this.options.position||"bottom"===this.options.position},draw:function(){var t=this,e=t.options,n=e.labels,i=W.global,a=i.defaultColor,r=i.elements.line,o=t.height,s=t.columnHeights,l=t.width,u=t.lineWidths;if(e.display){var d,h=Si(e.rtl,t.left,t.minSize.width),c=t.ctx,f=Ci(n.fontColor,i.defaultFontColor),g=H.options._parseFont(n),m=g.size;c.textAlign=h.textAlign("left"),c.textBaseline="middle",c.lineWidth=.5,c.strokeStyle=f,c.fillStyle=f,c.font=g.string;var p=Pi(n,m),v=t.legendHitBoxes,b=function(t,i){switch(e.align){case"start":return n.padding;case"end":return t-i;default:return(t-i+n.padding)/2}},y=t.isHorizontal();d=y?{x:t.left+b(l,u[0]),y:t.top+n.padding,line:0}:{x:t.left+n.padding,y:t.top+b(o,s[0]),line:0},H.rtl.overrideTextDirection(t.ctx,e.textDirection);var x=m+n.padding;H.each(t.legendItems,(function(e,i){var f=c.measureText(e.text).width,g=p+m/2+f,_=d.x,w=d.y;h.setWidth(t.minSize.width),y?i>0&&_+g+n.padding>t.left+t.minSize.width&&(w=d.y+=x,d.line++,_=d.x=t.left+b(l,u[d.line])):i>0&&w+x>t.top+t.minSize.height&&(_=d.x=_+t.columnWidths[d.line]+n.padding,d.line++,w=d.y=t.top+b(o,s[d.line]));var k=h.x(_);!function(t,e,i){if(!(isNaN(p)||p<=0)){c.save();var o=Ci(i.lineWidth,r.borderWidth);if(c.fillStyle=Ci(i.fillStyle,a),c.lineCap=Ci(i.lineCap,r.borderCapStyle),c.lineDashOffset=Ci(i.lineDashOffset,r.borderDashOffset),c.lineJoin=Ci(i.lineJoin,r.borderJoinStyle),c.lineWidth=o,c.strokeStyle=Ci(i.strokeStyle,a),c.setLineDash&&c.setLineDash(Ci(i.lineDash,r.borderDash)),n&&n.usePointStyle){var s=p*Math.SQRT2/2,l=h.xPlus(t,p/2),u=e+m/2;H.canvas.drawPoint(c,i.pointStyle,s,l,u,i.rotation)}else c.fillRect(h.leftForLtr(t,p),e,p,m),0!==o&&c.strokeRect(h.leftForLtr(t,p),e,p,m);c.restore()}}(k,w,e),v[i].left=h.leftForLtr(k,v[i].width),v[i].top=w,function(t,e,n,i){var a=m/2,r=h.xPlus(t,p+a),o=e+a;c.fillText(n.text,r,o),n.hidden&&(c.beginPath(),c.lineWidth=2,c.moveTo(r,o),c.lineTo(h.xPlus(r,i),o),c.stroke())}(k,w,e,f),y?d.x+=g+n.padding:d.y+=x})),H.rtl.restoreTextDirection(t.ctx,e.textDirection)}},_getLegendItemAt:function(t,e){var n,i,a,r=this;if(t>=r.left&&t<=r.right&&e>=r.top&&e<=r.bottom)for(a=r.legendHitBoxes,n=0;n<a.length;++n)if(t>=(i=a[n]).left&&t<=i.left+i.width&&e>=i.top&&e<=i.top+i.height)return r.legendItems[n];return null},handleEvent:function(t){var e,n=this,i=n.options,a="mouseup"===t.type?"click":t.type;if("mousemove"===a){if(!i.onHover&&!i.onLeave)return}else{if("click"!==a)return;if(!i.onClick)return}e=n._getLegendItemAt(t.x,t.y),"click"===a?e&&i.onClick&&i.onClick.call(n,t.native,e):(i.onLeave&&e!==n._hoveredItem&&(n._hoveredItem&&i.onLeave.call(n,t.native,n._hoveredItem),n._hoveredItem=e),i.onHover&&e&&i.onHover.call(n,t.native,e))}});function Oi(t,e){var n=new Ti({ctx:t.ctx,options:e,chart:t});me.configure(t,n,e),me.addBox(t,n),t.legend=n}var Ai={id:"legend",_element:Ti,beforeInit:function(t){var e=t.options.legend;e&&Oi(t,e)},beforeUpdate:function(t){var e=t.options.legend,n=t.legend;e?(H.mergeIf(e,W.global.legend),n?(me.configure(t,n,e),n.options=e):Oi(t,e)):n&&(me.removeBox(t,n),delete t.legend)},afterEvent:function(t,e){var n=t.legend;n&&n.handleEvent(e)}},Fi=H.noop;W._set("global",{title:{display:!1,fontStyle:"bold",fullWidth:!0,padding:10,position:"top",text:"",weight:2e3}});var Ii=$.extend({initialize:function(t){H.extend(this,t),this.legendHitBoxes=[]},beforeUpdate:Fi,update:function(t,e,n){var i=this;return i.beforeUpdate(),i.maxWidth=t,i.maxHeight=e,i.margins=n,i.beforeSetDimensions(),i.setDimensions(),i.afterSetDimensions(),i.beforeBuildLabels(),i.buildLabels(),i.afterBuildLabels(),i.beforeFit(),i.fit(),i.afterFit(),i.afterUpdate(),i.minSize},afterUpdate:Fi,beforeSetDimensions:Fi,setDimensions:function(){var t=this;t.isHorizontal()?(t.width=t.maxWidth,t.left=0,t.right=t.width):(t.height=t.maxHeight,t.top=0,t.bottom=t.height),t.paddingLeft=0,t.paddingTop=0,t.paddingRight=0,t.paddingBottom=0,t.minSize={width:0,height:0}},afterSetDimensions:Fi,beforeBuildLabels:Fi,buildLabels:Fi,afterBuildLabels:Fi,beforeFit:Fi,fit:function(){var t,e=this,n=e.options,i=e.minSize={},a=e.isHorizontal();n.display?(t=(H.isArray(n.text)?n.text.length:1)*H.options._parseFont(n).lineHeight+2*n.padding,e.width=i.width=a?e.maxWidth:t,e.height=i.height=a?t:e.maxHeight):e.width=i.width=e.height=i.height=0},afterFit:Fi,isHorizontal:function(){var t=this.options.position;return"top"===t||"bottom"===t},draw:function(){var t=this,e=t.ctx,n=t.options;if(n.display){var i,a,r,o=H.options._parseFont(n),s=o.lineHeight,l=s/2+n.padding,u=0,d=t.top,h=t.left,c=t.bottom,f=t.right;e.fillStyle=H.valueOrDefault(n.fontColor,W.global.defaultFontColor),e.font=o.string,t.isHorizontal()?(a=h+(f-h)/2,r=d+l,i=f-h):(a="left"===n.position?h+l:f-l,r=d+(c-d)/2,i=c-d,u=Math.PI*("left"===n.position?-.5:.5)),e.save(),e.translate(a,r),e.rotate(u),e.textAlign="center",e.textBaseline="middle";var g=n.text;if(H.isArray(g))for(var m=0,p=0;p<g.length;++p)e.fillText(g[p],0,m,i),m+=s;else e.fillText(g,0,0,i);e.restore()}}});function Li(t,e){var n=new Ii({ctx:t.ctx,options:e,chart:t});me.configure(t,n,e),me.addBox(t,n),t.titleBlock=n}var Ri={},Ni=Mi,Wi=Ai,Yi={id:"title",_element:Ii,beforeInit:function(t){var e=t.options.title;e&&Li(t,e)},beforeUpdate:function(t){var e=t.options.title,n=t.titleBlock;e?(H.mergeIf(e,W.global.title),n?(me.configure(t,n,e),n.options=e):Li(t,e)):n&&(me.removeBox(t,n),delete t.titleBlock)}};for(var zi in Ri.filler=Ni,Ri.legend=Wi,Ri.title=Yi,en.helpers=H,function(){function t(t,e,n){var i;return"string"==typeof t?(i=parseInt(t,10),-1!==t.indexOf("%")&&(i=i/100*e.parentNode[n])):i=t,i}function e(t){return null!=t&&"none"!==t}function n(n,i,a){var r=document.defaultView,o=H._getParentNode(n),s=r.getComputedStyle(n)[i],l=r.getComputedStyle(o)[i],u=e(s),d=e(l),h=Number.POSITIVE_INFINITY;return u||d?Math.min(u?t(s,n,a):h,d?t(l,o,a):h):"none"}H.where=function(t,e){if(H.isArray(t)&&Array.prototype.filter)return t.filter(e);var n=[];return H.each(t,(function(t){e(t)&&n.push(t)})),n},H.findIndex=Array.prototype.findIndex?function(t,e,n){return t.findIndex(e,n)}:function(t,e,n){n=void 0===n?t:n;for(var i=0,a=t.length;i<a;++i)if(e.call(n,t[i],i,t))return i;return-1},H.findNextWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=-1);for(var i=n+1;i<t.length;i++){var a=t[i];if(e(a))return a}},H.findPreviousWhere=function(t,e,n){H.isNullOrUndef(n)&&(n=t.length);for(var i=n-1;i>=0;i--){var a=t[i];if(e(a))return a}},H.isNumber=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},H.almostEquals=function(t,e,n){return Math.abs(t-e)<n},H.almostWhole=function(t,e){var n=Math.round(t);return n-e<=t&&n+e>=t},H.max=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.max(t,e)}),Number.NEGATIVE_INFINITY)},H.min=function(t){return t.reduce((function(t,e){return isNaN(e)?t:Math.min(t,e)}),Number.POSITIVE_INFINITY)},H.sign=Math.sign?function(t){return Math.sign(t)}:function(t){return 0===(t=+t)||isNaN(t)?t:t>0?1:-1},H.toRadians=function(t){return t*(Math.PI/180)},H.toDegrees=function(t){return t*(180/Math.PI)},H._decimalPlaces=function(t){if(H.isFinite(t)){for(var e=1,n=0;Math.round(t*e)/e!==t;)e*=10,n++;return n}},H.getAngleFromPoint=function(t,e){var n=e.x-t.x,i=e.y-t.y,a=Math.sqrt(n*n+i*i),r=Math.atan2(i,n);return r<-.5*Math.PI&&(r+=2*Math.PI),{angle:r,distance:a}},H.distanceBetweenPoints=function(t,e){return Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2))},H.aliasPixel=function(t){return t%2==0?0:.5},H._alignPixel=function(t,e,n){var i=t.currentDevicePixelRatio,a=n/2;return Math.round((e-a)*i)/i+a},H.splineCurve=function(t,e,n,i){var a=t.skip?e:t,r=e,o=n.skip?e:n,s=Math.sqrt(Math.pow(r.x-a.x,2)+Math.pow(r.y-a.y,2)),l=Math.sqrt(Math.pow(o.x-r.x,2)+Math.pow(o.y-r.y,2)),u=s/(s+l),d=l/(s+l),h=i*(u=isNaN(u)?0:u),c=i*(d=isNaN(d)?0:d);return{previous:{x:r.x-h*(o.x-a.x),y:r.y-h*(o.y-a.y)},next:{x:r.x+c*(o.x-a.x),y:r.y+c*(o.y-a.y)}}},H.EPSILON=Number.EPSILON||1e-14,H.splineCurveMonotone=function(t){var e,n,i,a,r,o,s,l,u,d=(t||[]).map((function(t){return{model:t._model,deltaK:0,mK:0}})),h=d.length;for(e=0;e<h;++e)if(!(i=d[e]).model.skip){if(n=e>0?d[e-1]:null,(a=e<h-1?d[e+1]:null)&&!a.model.skip){var c=a.model.x-i.model.x;i.deltaK=0!==c?(a.model.y-i.model.y)/c:0}!n||n.model.skip?i.mK=i.deltaK:!a||a.model.skip?i.mK=n.deltaK:this.sign(n.deltaK)!==this.sign(i.deltaK)?i.mK=0:i.mK=(n.deltaK+i.deltaK)/2}for(e=0;e<h-1;++e)i=d[e],a=d[e+1],i.model.skip||a.model.skip||(H.almostEquals(i.deltaK,0,this.EPSILON)?i.mK=a.mK=0:(r=i.mK/i.deltaK,o=a.mK/i.deltaK,(l=Math.pow(r,2)+Math.pow(o,2))<=9||(s=3/Math.sqrt(l),i.mK=r*s*i.deltaK,a.mK=o*s*i.deltaK)));for(e=0;e<h;++e)(i=d[e]).model.skip||(n=e>0?d[e-1]:null,a=e<h-1?d[e+1]:null,n&&!n.model.skip&&(u=(i.model.x-n.model.x)/3,i.model.controlPointPreviousX=i.model.x-u,i.model.controlPointPreviousY=i.model.y-u*i.mK),a&&!a.model.skip&&(u=(a.model.x-i.model.x)/3,i.model.controlPointNextX=i.model.x+u,i.model.controlPointNextY=i.model.y+u*i.mK))},H.nextItem=function(t,e,n){return n?e>=t.length-1?t[0]:t[e+1]:e>=t.length-1?t[t.length-1]:t[e+1]},H.previousItem=function(t,e,n){return n?e<=0?t[t.length-1]:t[e-1]:e<=0?t[0]:t[e-1]},H.niceNum=function(t,e){var n=Math.floor(H.log10(t)),i=t/Math.pow(10,n);return(e?i<1.5?1:i<3?2:i<7?5:10:i<=1?1:i<=2?2:i<=5?5:10)*Math.pow(10,n)},H.requestAnimFrame="undefined"==typeof window?function(t){t()}:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)},H.getRelativePosition=function(t,e){var n,i,a=t.originalEvent||t,r=t.target||t.srcElement,o=r.getBoundingClientRect(),s=a.touches;s&&s.length>0?(n=s[0].clientX,i=s[0].clientY):(n=a.clientX,i=a.clientY);var l=parseFloat(H.getStyle(r,"padding-left")),u=parseFloat(H.getStyle(r,"padding-top")),d=parseFloat(H.getStyle(r,"padding-right")),h=parseFloat(H.getStyle(r,"padding-bottom")),c=o.right-o.left-l-d,f=o.bottom-o.top-u-h;return{x:n=Math.round((n-o.left-l)/c*r.width/e.currentDevicePixelRatio),y:i=Math.round((i-o.top-u)/f*r.height/e.currentDevicePixelRatio)}},H.getConstraintWidth=function(t){return n(t,"max-width","clientWidth")},H.getConstraintHeight=function(t){return n(t,"max-height","clientHeight")},H._calculatePadding=function(t,e,n){return(e=H.getStyle(t,e)).indexOf("%")>-1?n*parseInt(e,10)/100:parseInt(e,10)},H._getParentNode=function(t){var e=t.parentNode;return e&&"[object ShadowRoot]"===e.toString()&&(e=e.host),e},H.getMaximumWidth=function(t){var e=H._getParentNode(t);if(!e)return t.clientWidth;var n=e.clientWidth,i=n-H._calculatePadding(e,"padding-left",n)-H._calculatePadding(e,"padding-right",n),a=H.getConstraintWidth(t);return isNaN(a)?i:Math.min(i,a)},H.getMaximumHeight=function(t){var e=H._getParentNode(t);if(!e)return t.clientHeight;var n=e.clientHeight,i=n-H._calculatePadding(e,"padding-top",n)-H._calculatePadding(e,"padding-bottom",n),a=H.getConstraintHeight(t);return isNaN(a)?i:Math.min(i,a)},H.getStyle=function(t,e){return t.currentStyle?t.currentStyle[e]:document.defaultView.getComputedStyle(t,null).getPropertyValue(e)},H.retinaScale=function(t,e){var n=t.currentDevicePixelRatio=e||"undefined"!=typeof window&&window.devicePixelRatio||1;if(1!==n){var i=t.canvas,a=t.height,r=t.width;i.height=a*n,i.width=r*n,t.ctx.scale(n,n),i.style.height||i.style.width||(i.style.height=a+"px",i.style.width=r+"px")}},H.fontString=function(t,e,n){return e+" "+t+"px "+n},H.longestText=function(t,e,n,i){var a=(i=i||{}).data=i.data||{},r=i.garbageCollect=i.garbageCollect||[];i.font!==e&&(a=i.data={},r=i.garbageCollect=[],i.font=e),t.font=e;var o,s,l,u,d,h=0,c=n.length;for(o=0;o<c;o++)if(null!=(u=n[o])&&!0!==H.isArray(u))h=H.measureText(t,a,r,h,u);else if(H.isArray(u))for(s=0,l=u.length;s<l;s++)null==(d=u[s])||H.isArray(d)||(h=H.measureText(t,a,r,h,d));var f=r.length/2;if(f>n.length){for(o=0;o<f;o++)delete a[r[o]];r.splice(0,f)}return h},H.measureText=function(t,e,n,i,a){var r=e[a];return r||(r=e[a]=t.measureText(a).width,n.push(a)),r>i&&(i=r),i},H.numberOfLabelLines=function(t){var e=1;return H.each(t,(function(t){H.isArray(t)&&t.length>e&&(e=t.length)})),e},H.color=k?function(t){return t instanceof CanvasGradient&&(t=W.global.defaultColor),k(t)}:function(t){return console.error("Color.js not found!"),t},H.getHoverColor=function(t){return t instanceof CanvasPattern||t instanceof CanvasGradient?t:H.color(t).saturate(.5).darken(.1).rgbString()}}(),en._adapters=rn,en.Animation=K,en.animationService=J,en.controllers=Jt,en.DatasetController=it,en.defaults=W,en.Element=$,en.elements=wt,en.Interaction=re,en.layouts=me,en.platform=Ie,en.plugins=Le,en.Scale=xn,en.scaleService=Re,en.Ticks=on,en.Tooltip=Ge,en.helpers.each(fi,(function(t,e){en.scaleService.registerScaleType(e,t,t._defaults)})),Ri)Ri.hasOwnProperty(zi)&&en.plugins.register(Ri[zi]);en.platform.initialize();var Ei=en;return"undefined"!=typeof window&&(window.Chart=en),en.Chart=en,en.Legend=Ri.legend._element,en.Title=Ri.title._element,en.pluginService=en.plugins,en.PluginBase=en.Element.extend({}),en.canvasHelpers=en.helpers.canvas,en.layoutService=en.layouts,en.LinearScaleBase=Dn,en.helpers.each(["Bar","Bubble","Doughnut","Line","PolarArea","Radar","Scatter"],(function(t){en[t]=function(e,n){return new en(e,en.helpers.merge(n||{},{type:t.charAt(0).toLowerCase()+t.slice(1)}))}})),Ei}));
 
 
 
 
 
 
 
assets/js/admin-blocked-ips.js DELETED
@@ -1,15 +0,0 @@
1
- (function($) {
2
- $(function() {
3
- var $addIPContainer = $(".wpzerospam-add-ip-container");
4
-
5
- $("#blocked-type", $addIPContainer).change(function() {
6
- if ( $(this).val() == 'permanent' ) {
7
- $("#wpzerospam-add-ip-field-start-date").hide();
8
- $("#wpzerospam-add-ip-field-end-date").hide();
9
- } else {
10
- $("#wpzerospam-add-ip-field-start-date").show();
11
- $("#wpzerospam-add-ip-field-end-date").show();
12
- }
13
- });
14
- });
15
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/admin-tables.js DELETED
@@ -1,22 +0,0 @@
1
- (function($) {
2
- $(function() {
3
- /**
4
- * Handles opening the 'View Details' modal on the spam log.
5
- */
6
- var $detailsTrigger = $( '.wpzerospam-details-trigger' );
7
- $detailsTrigger.click(function( e ) {
8
- e.preventDefault();
9
-
10
- var id = $(this).data('id');
11
- $('#wpzerospam-details-modal-' + id).addClass( 'is-active' );
12
- });
13
-
14
- $('.wpzerospam-details-modal').click(function(){
15
- $(this).removeClass('is-active');
16
- });
17
-
18
- $(".wpzerospam-details-modal .wpzerospam-details-modal-inner").click(function(e) {
19
- e.stopPropagation();
20
- });
21
- });
22
- })(jQuery);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/js/admin.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function($) {
2
+ $(function() {
3
+ $( '.zerospam-details-trigger' ).click(function( e ) {
4
+ e.preventDefault();
5
+ $('.zerospam-modal').removeClass('is-active');
6
+
7
+ var id = $(this).data('id');
8
+ $('#zerospam-details-' + id).addClass('is-active');
9
+ });
10
+
11
+ $('.zerospam-close-modal').click(function(e) {
12
+ e.preventDefault();
13
+ $('.zerospam-modal').removeClass('is-active');
14
+ });
15
+
16
+ $('.zerospam-block-trigger').click(function(e) {
17
+ e.preventDefault();
18
+
19
+ var ip = $(this).data('ip');
20
+ $('input[name="blocked_ip"]', $('#zerospam-block-ip')).val('');
21
+ if ( ip ) {
22
+ $('input[name="blocked_ip"]', $('#zerospam-block-ip')).val(ip);
23
+ }
24
+
25
+ var reason = $(this).data('reason');
26
+ $('input[name="blocked_reason"]', $('#zerospam-block-ip')).val('');
27
+ if ( reason ) {
28
+ $('input[name="blocked_reason"]', $('#zerospam-block-ip')).val(reason);
29
+ }
30
+
31
+ var type = $(this).data('type');
32
+ $('select[name="blocked_type"]', $('#zerospam-block-ip')).val('temporary');
33
+ if ( type ) {
34
+ $('select[name="blocked_type"]', $('#zerospam-block-ip')).val(type);
35
+ }
36
+
37
+ var startDate = $(this).data('start');
38
+ $('input[name="blocked_start_date"]', $('#zerospam-block-ip')).val('');
39
+ if ( startDate ) {
40
+ $('input[name="blocked_start_date"]', $('#zerospam-block-ip')).val(startDate);
41
+ }
42
+
43
+ var endDate = $(this).data('end');
44
+ $('input[name="blocked_end_date"]', $('#zerospam-block-ip')).val('');
45
+ if ( endDate ) {
46
+ $('input[name="blocked_end_date"]', $('#zerospam-block-ip')).val(endDate);
47
+ }
48
+
49
+ $('.zerospam-modal').removeClass('is-active');
50
+ $('#zerospam-block-ip').addClass('is-active');
51
+ });
52
+
53
+ $(document).on('keydown', function(e) {
54
+ if(e.key == "Escape") {
55
+ $('.zerospam-modal').removeClass('is-active');
56
+ }
57
+ });
58
+ });
59
+ })(jQuery);
assets/js/jquery-jvectormap-2.0.5.min.js DELETED
@@ -1 +0,0 @@
1
- !function($){var apiParams={set:{colors:1,values:1,backgroundColor:1,scaleColors:1,normalizeFunction:1,focus:1},get:{selectedRegions:1,selectedMarkers:1,mapObject:1,regionName:1}};$.fn.vectorMap=function(options){var map=this.children(".jvectormap-container").data("mapObject");if("addMap"===options)jvm.Map.maps[arguments[1]]=arguments[2];else{if(("set"===options||"get"===options)&&apiParams[options][arguments[1]])return map[options+(arguments[1].charAt(0).toUpperCase()+arguments[1].substr(1))].apply(map,Array.prototype.slice.call(arguments,2));(options=options||{}).container=this,map=new jvm.Map(options)}return this}}(jQuery),function(factory){"function"==typeof define&&define.amd?define(["jquery"],factory):"object"==typeof exports?module.exports=factory:factory(jQuery)}(function($){var nullLowestDeltaTimeout,lowestDelta,toFix=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],toBind="onwheel"in document||9<=document.documentMode?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],slice=Array.prototype.slice;if($.event.fixHooks)for(var i=toFix.length;i;)$.event.fixHooks[toFix[--i]]=$.event.mouseHooks;var special=$.event.special.mousewheel={version:"3.1.9",setup:function(){if(this.addEventListener)for(var i=toBind.length;i;)this.addEventListener(toBind[--i],handler,!1);else this.onmousewheel=handler;$.data(this,"mousewheel-line-height",special.getLineHeight(this)),$.data(this,"mousewheel-page-height",special.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var i=toBind.length;i;)this.removeEventListener(toBind[--i],handler,!1);else this.onmousewheel=null},getLineHeight:function(elem){return parseInt($(elem)["offsetParent"in $.fn?"offsetParent":"parent"]().css("fontSize"),10)},getPageHeight:function(elem){return $(elem).height()},settings:{adjustOldDeltas:!0}};function handler(event){var absDelta,orgEvent=event||window.event,args=slice.call(arguments,1),delta=0,deltaX=0,deltaY=0;if((event=$.event.fix(orgEvent)).type="mousewheel","detail"in orgEvent&&(deltaY=-1*orgEvent.detail),"wheelDelta"in orgEvent&&(deltaY=orgEvent.wheelDelta),"wheelDeltaY"in orgEvent&&(deltaY=orgEvent.wheelDeltaY),"wheelDeltaX"in orgEvent&&(deltaX=-1*orgEvent.wheelDeltaX),"axis"in orgEvent&&orgEvent.axis===orgEvent.HORIZONTAL_AXIS&&(deltaX=-1*deltaY,deltaY=0),delta=0===deltaY?deltaX:deltaY,"deltaY"in orgEvent&&(delta=deltaY=-1*orgEvent.deltaY),"deltaX"in orgEvent&&(deltaX=orgEvent.deltaX,0===deltaY&&(delta=-1*deltaX)),0!==deltaY||0!==deltaX){if(1===orgEvent.deltaMode){var lineHeight=$.data(this,"mousewheel-line-height");delta*=lineHeight,deltaY*=lineHeight,deltaX*=lineHeight}else if(2===orgEvent.deltaMode){var pageHeight=$.data(this,"mousewheel-page-height");delta*=pageHeight,deltaY*=pageHeight,deltaX*=pageHeight}return absDelta=Math.max(Math.abs(deltaY),Math.abs(deltaX)),(!lowestDelta||absDelta<lowestDelta)&&shouldAdjustOldDeltas(orgEvent,lowestDelta=absDelta)&&(lowestDelta/=40),shouldAdjustOldDeltas(orgEvent,absDelta)&&(delta/=40,deltaX/=40,deltaY/=40),delta=Math[1<=delta?"floor":"ceil"](delta/lowestDelta),deltaX=Math[1<=deltaX?"floor":"ceil"](deltaX/lowestDelta),deltaY=Math[1<=deltaY?"floor":"ceil"](deltaY/lowestDelta),event.deltaX=deltaX,event.deltaY=deltaY,event.deltaFactor=lowestDelta,event.deltaMode=0,args.unshift(event,delta,deltaX,deltaY),nullLowestDeltaTimeout&&clearTimeout(nullLowestDeltaTimeout),nullLowestDeltaTimeout=setTimeout(nullLowestDelta,200),($.event.dispatch||$.event.handle).apply(this,args)}}function nullLowestDelta(){lowestDelta=null}function shouldAdjustOldDeltas(orgEvent,absDelta){return special.settings.adjustOldDeltas&&"mousewheel"===orgEvent.type&&absDelta%120==0}$.fn.extend({mousewheel:function(fn){return fn?this.bind("mousewheel",fn):this.trigger("mousewheel")},unmousewheel:function(fn){return this.unbind("mousewheel",fn)}})});var jvm={inherits:function(child,parent){function temp(){}temp.prototype=parent.prototype,child.prototype=new temp,(child.prototype.constructor=child).parentClass=parent},mixin:function(target,source){var prop;for(prop in source.prototype)source.prototype.hasOwnProperty(prop)&&(target.prototype[prop]=source.prototype[prop])},min:function(values){var i,min=Number.MAX_VALUE;if(values instanceof Array)for(i=0;i<values.length;i++)values[i]<min&&(min=values[i]);else for(i in values)values[i]<min&&(min=values[i]);return min},max:function(values){var i,max=Number.MIN_VALUE;if(values instanceof Array)for(i=0;i<values.length;i++)values[i]>max&&(max=values[i]);else for(i in values)values[i]>max&&(max=values[i]);return max},keys:function(object){var key,keys=[];for(key in object)keys.push(key);return keys},values:function(object){var key,i,values=[];for(i=0;i<arguments.length;i++)for(key in object=arguments[i])values.push(object[key]);return values},whenImageLoaded:function(url){var deferred=new jvm.$.Deferred,img=jvm.$("<img/>");return img.on("error",function(){deferred.reject()}).on("load",function(){deferred.resolve(img)}),img.attr("src",url),deferred},isImageUrl:function(s){return/\.\w{3,4}$/.test(s)}};jvm.$=jQuery,Array.prototype.indexOf||(Array.prototype.indexOf=function(searchElement,fromIndex){var k;if(null==this)throw new TypeError('"this" is null or not defined');var O=Object(this),len=O.length>>>0;if(0==len)return-1;var n=+fromIndex||0;if(Math.abs(n)===1/0&&(n=0),len<=n)return-1;for(k=Math.max(0<=n?n:len-Math.abs(n),0);k<len;){if(k in O&&O[k]===searchElement)return k;k++}return-1}),jvm.AbstractElement=function(name,config){this.node=this.createElement(name),this.name=name,this.properties={},config&&this.set(config)},jvm.AbstractElement.prototype.set=function(property,value){var key;if("object"==typeof property)for(key in property)this.properties[key]=property[key],this.applyAttr(key,property[key]);else this.properties[property]=value,this.applyAttr(property,value)},jvm.AbstractElement.prototype.get=function(property){return this.properties[property]},jvm.AbstractElement.prototype.applyAttr=function(property,value){this.node.setAttribute(property,value)},jvm.AbstractElement.prototype.remove=function(){jvm.$(this.node).remove()},jvm.AbstractCanvasElement=function(container,width,height){this.container=container,this.setSize(width,height),this.rootElement=new jvm[this.classPrefix+"GroupElement"],this.node.appendChild(this.rootElement.node),this.container.appendChild(this.node)},jvm.AbstractCanvasElement.prototype.add=function(element,group){(group=group||this.rootElement).add(element),element.canvas=this},jvm.AbstractCanvasElement.prototype.addPath=function(config,style,group){var el=new jvm[this.classPrefix+"PathElement"](config,style);return this.add(el,group),el},jvm.AbstractCanvasElement.prototype.addCircle=function(config,style,group){var el=new jvm[this.classPrefix+"CircleElement"](config,style);return this.add(el,group),el},jvm.AbstractCanvasElement.prototype.addImage=function(config,style,group){var el=new jvm[this.classPrefix+"ImageElement"](config,style);return this.add(el,group),el},jvm.AbstractCanvasElement.prototype.addText=function(config,style,group){var el=new jvm[this.classPrefix+"TextElement"](config,style);return this.add(el,group),el},jvm.AbstractCanvasElement.prototype.addGroup=function(parentGroup){var el=new jvm[this.classPrefix+"GroupElement"];return parentGroup?parentGroup.node.appendChild(el.node):this.node.appendChild(el.node),el.canvas=this,el},jvm.AbstractShapeElement=function(name,config,style){this.style=style||{},this.style.current=this.style.current||{},this.isHovered=!1,this.isSelected=!1,this.updateStyle()},jvm.AbstractShapeElement.prototype.setStyle=function(property,value){var styles={};"object"==typeof property?styles=property:styles[property]=value,jvm.$.extend(this.style.current,styles),this.updateStyle()},jvm.AbstractShapeElement.prototype.updateStyle=function(){var attrs={};jvm.AbstractShapeElement.mergeStyles(attrs,this.style.initial),jvm.AbstractShapeElement.mergeStyles(attrs,this.style.current),this.isHovered&&jvm.AbstractShapeElement.mergeStyles(attrs,this.style.hover),this.isSelected&&(jvm.AbstractShapeElement.mergeStyles(attrs,this.style.selected),this.isHovered&&jvm.AbstractShapeElement.mergeStyles(attrs,this.style.selectedHover)),this.set(attrs)},jvm.AbstractShapeElement.mergeStyles=function(styles,newStyles){var key;for(key in newStyles=newStyles||{})null===newStyles[key]?delete styles[key]:styles[key]=newStyles[key]},jvm.SVGElement=function(name,config){jvm.SVGElement.parentClass.apply(this,arguments)},jvm.inherits(jvm.SVGElement,jvm.AbstractElement),jvm.SVGElement.svgns="http://www.w3.org/2000/svg",jvm.SVGElement.prototype.createElement=function(tagName){return document.createElementNS(jvm.SVGElement.svgns,tagName)},jvm.SVGElement.prototype.addClass=function(className){this.node.setAttribute("class",className)},jvm.SVGElement.prototype.getElementCtr=function(ctr){return jvm["SVG"+ctr]},jvm.SVGElement.prototype.getBBox=function(){return this.node.getBBox()},jvm.SVGGroupElement=function(){jvm.SVGGroupElement.parentClass.call(this,"g")},jvm.inherits(jvm.SVGGroupElement,jvm.SVGElement),jvm.SVGGroupElement.prototype.add=function(element){this.node.appendChild(element.node)},jvm.SVGCanvasElement=function(container,width,height){this.classPrefix="SVG",jvm.SVGCanvasElement.parentClass.call(this,"svg"),this.defsElement=new jvm.SVGElement("defs"),this.node.appendChild(this.defsElement.node),jvm.AbstractCanvasElement.apply(this,arguments)},jvm.inherits(jvm.SVGCanvasElement,jvm.SVGElement),jvm.mixin(jvm.SVGCanvasElement,jvm.AbstractCanvasElement),jvm.SVGCanvasElement.prototype.setSize=function(width,height){this.width=width,this.height=height,this.node.setAttribute("width",width),this.node.setAttribute("height",height)},jvm.SVGCanvasElement.prototype.applyTransformParams=function(scale,transX,transY){this.scale=scale,this.transX=transX,this.transY=transY,this.rootElement.node.setAttribute("transform","scale("+scale+") translate("+transX+", "+transY+")")},jvm.SVGShapeElement=function(name,config,style){jvm.SVGShapeElement.parentClass.call(this,name,config),jvm.AbstractShapeElement.apply(this,arguments)},jvm.inherits(jvm.SVGShapeElement,jvm.SVGElement),jvm.mixin(jvm.SVGShapeElement,jvm.AbstractShapeElement),jvm.SVGShapeElement.prototype.applyAttr=function(attr,value){var patternEl,imageEl,that=this;"fill"===attr&&jvm.isImageUrl(value)?jvm.SVGShapeElement.images[value]?this.applyAttr("fill","url(#image"+jvm.SVGShapeElement.images[value]+")"):jvm.whenImageLoaded(value).then(function(img){(imageEl=new jvm.SVGElement("image")).node.setAttributeNS("http://www.w3.org/1999/xlink","href",value),imageEl.applyAttr("x","0"),imageEl.applyAttr("y","0"),imageEl.applyAttr("width",img[0].width),imageEl.applyAttr("height",img[0].height),(patternEl=new jvm.SVGElement("pattern")).applyAttr("id","image"+jvm.SVGShapeElement.imageCounter),patternEl.applyAttr("x",0),patternEl.applyAttr("y",0),patternEl.applyAttr("width",img[0].width/2),patternEl.applyAttr("height",img[0].height/2),patternEl.applyAttr("viewBox","0 0 "+img[0].width+" "+img[0].height),patternEl.applyAttr("patternUnits","userSpaceOnUse"),patternEl.node.appendChild(imageEl.node),that.canvas.defsElement.node.appendChild(patternEl.node),jvm.SVGShapeElement.images[value]=jvm.SVGShapeElement.imageCounter++,that.applyAttr("fill","url(#image"+jvm.SVGShapeElement.images[value]+")")}):jvm.SVGShapeElement.parentClass.prototype.applyAttr.apply(this,arguments)},jvm.SVGShapeElement.imageCounter=1,jvm.SVGShapeElement.images={},jvm.SVGPathElement=function(config,style){jvm.SVGPathElement.parentClass.call(this,"path",config,style),this.node.setAttribute("fill-rule","evenodd")},jvm.inherits(jvm.SVGPathElement,jvm.SVGShapeElement),jvm.SVGCircleElement=function(config,style){jvm.SVGCircleElement.parentClass.call(this,"circle",config,style)},jvm.inherits(jvm.SVGCircleElement,jvm.SVGShapeElement),jvm.SVGImageElement=function(config,style){jvm.SVGImageElement.parentClass.call(this,"image",config,style)},jvm.inherits(jvm.SVGImageElement,jvm.SVGShapeElement),jvm.SVGImageElement.prototype.applyAttr=function(attr,value){var imageUrl,that=this;"image"==attr?("object"==typeof value?(imageUrl=value.url,this.offset=value.offset):(imageUrl=value,this.offset=[0,0]),jvm.whenImageLoaded(imageUrl).then(function(img){that.node.setAttributeNS("http://www.w3.org/1999/xlink","href",imageUrl),that.width=img[0].width,that.height=img[0].height,that.applyAttr("width",that.width),that.applyAttr("height",that.height),that.applyAttr("x",that.cx-that.width/2+that.offset[0]),that.applyAttr("y",that.cy-that.height/2+that.offset[1]),jvm.$(that.node).trigger("imageloaded",[img])})):"cx"==attr?(this.cx=value,this.width&&this.applyAttr("x",value-this.width/2+this.offset[0])):"cy"==attr?(this.cy=value,this.height&&this.applyAttr("y",value-this.height/2+this.offset[1])):jvm.SVGImageElement.parentClass.prototype.applyAttr.apply(this,arguments)},jvm.SVGTextElement=function(config,style){jvm.SVGTextElement.parentClass.call(this,"text",config,style)},jvm.inherits(jvm.SVGTextElement,jvm.SVGShapeElement),jvm.SVGTextElement.prototype.applyAttr=function(attr,value){"text"===attr?this.node.textContent=value:jvm.SVGTextElement.parentClass.prototype.applyAttr.apply(this,arguments)},jvm.VMLElement=function(name,config){jvm.VMLElement.VMLInitialized||jvm.VMLElement.initializeVML(),jvm.VMLElement.parentClass.apply(this,arguments)},jvm.inherits(jvm.VMLElement,jvm.AbstractElement),jvm.VMLElement.VMLInitialized=!1,jvm.VMLElement.initializeVML=function(){try{document.namespaces.rvml||document.namespaces.add("rvml","urn:schemas-microsoft-com:vml"),jvm.VMLElement.prototype.createElement=function(tagName){return document.createElement("<rvml:"+tagName+' class="rvml">')}}catch(e){jvm.VMLElement.prototype.createElement=function(tagName){return document.createElement("<"+tagName+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}document.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)"),jvm.VMLElement.VMLInitialized=!0},jvm.VMLElement.prototype.getElementCtr=function(ctr){return jvm["VML"+ctr]},jvm.VMLElement.prototype.addClass=function(className){jvm.$(this.node).addClass(className)},jvm.VMLElement.prototype.applyAttr=function(attr,value){this.node[attr]=value},jvm.VMLElement.prototype.getBBox=function(){var node=jvm.$(this.node);return{x:node.position().left/this.canvas.scale,y:node.position().top/this.canvas.scale,width:node.width()/this.canvas.scale,height:node.height()/this.canvas.scale}},jvm.VMLGroupElement=function(){jvm.VMLGroupElement.parentClass.call(this,"group"),this.node.style.left="0px",this.node.style.top="0px",this.node.coordorigin="0 0"},jvm.inherits(jvm.VMLGroupElement,jvm.VMLElement),jvm.VMLGroupElement.prototype.add=function(element){this.node.appendChild(element.node)},jvm.VMLCanvasElement=function(container,width,height){this.classPrefix="VML",jvm.VMLCanvasElement.parentClass.call(this,"group"),jvm.AbstractCanvasElement.apply(this,arguments),this.node.style.position="absolute"},jvm.inherits(jvm.VMLCanvasElement,jvm.VMLElement),jvm.mixin(jvm.VMLCanvasElement,jvm.AbstractCanvasElement),jvm.VMLCanvasElement.prototype.setSize=function(width,height){var paths,groups,i,l;if(this.width=width,this.height=height,this.node.style.width=width+"px",this.node.style.height=height+"px",this.node.coordsize=width+" "+height,this.node.coordorigin="0 0",this.rootElement){for(i=0,l=(paths=this.rootElement.node.getElementsByTagName("shape")).length;i<l;i++)paths[i].coordsize=width+" "+height,paths[i].style.width=width+"px",paths[i].style.height=height+"px";for(i=0,l=(groups=this.node.getElementsByTagName("group")).length;i<l;i++)groups[i].coordsize=width+" "+height,groups[i].style.width=width+"px",groups[i].style.height=height+"px"}},jvm.VMLCanvasElement.prototype.applyTransformParams=function(scale,transX,transY){this.scale=scale,this.transX=transX,this.transY=transY,this.rootElement.node.coordorigin=this.width-transX-this.width/100+","+(this.height-transY-this.height/100),this.rootElement.node.coordsize=this.width/scale+","+this.height/scale},jvm.VMLShapeElement=function(name,config){jvm.VMLShapeElement.parentClass.call(this,name,config),this.fillElement=new jvm.VMLElement("fill"),this.strokeElement=new jvm.VMLElement("stroke"),this.node.appendChild(this.fillElement.node),this.node.appendChild(this.strokeElement.node),this.node.stroked=!1,jvm.AbstractShapeElement.apply(this,arguments)},jvm.inherits(jvm.VMLShapeElement,jvm.VMLElement),jvm.mixin(jvm.VMLShapeElement,jvm.AbstractShapeElement),jvm.VMLShapeElement.prototype.applyAttr=function(attr,value){switch(attr){case"fill":this.node.fillcolor=value;break;case"fill-opacity":this.fillElement.node.opacity=Math.round(100*value)+"%";break;case"stroke":this.node.stroked="none"!==value,this.node.strokecolor=value;break;case"stroke-opacity":this.strokeElement.node.opacity=Math.round(100*value)+"%";break;case"stroke-width":0===parseInt(value,10)?this.node.stroked=!1:this.node.stroked=!0,this.node.strokeweight=value;break;case"d":this.node.path=jvm.VMLPathElement.pathSvgToVml(value);break;default:jvm.VMLShapeElement.parentClass.prototype.applyAttr.apply(this,arguments)}},jvm.VMLPathElement=function(config,style){var scale=new jvm.VMLElement("skew");jvm.VMLPathElement.parentClass.call(this,"shape",config,style),this.node.coordorigin="0 0",scale.node.on=!0,scale.node.matrix="0.01,0,0,0.01,0,0",scale.node.offset="0,0",this.node.appendChild(scale.node)},jvm.inherits(jvm.VMLPathElement,jvm.VMLShapeElement),jvm.VMLPathElement.prototype.applyAttr=function(attr,value){"d"===attr?this.node.path=jvm.VMLPathElement.pathSvgToVml(value):jvm.VMLShapeElement.prototype.applyAttr.call(this,attr,value)},jvm.VMLPathElement.pathSvgToVml=function(path){var ctrlx,ctrly,cx=0,cy=0;return(path=path.replace(/(-?\d+)e(-?\d+)/g,"0")).replace(/([MmLlHhVvCcSs])\s*((?:-?\d*(?:\.\d+)?\s*,?\s*)+)/g,function(segment,letter,coords,index){(coords=coords.replace(/(\d)-/g,"$1,-").replace(/^\s+/g,"").replace(/\s+$/g,"").replace(/\s+/g,",").split(","))[0]||coords.shift();for(var i=0,l=coords.length;i<l;i++)coords[i]=Math.round(100*coords[i]);switch(letter){case"m":return cx+=coords[0],cy+=coords[1],"t"+coords.join(",");case"M":return cx=coords[0],cy=coords[1],"m"+coords.join(",");case"l":return cx+=coords[0],cy+=coords[1],"r"+coords.join(",");case"L":return cx=coords[0],cy=coords[1],"l"+coords.join(",");case"h":return cx+=coords[0],"r"+coords[0]+",0";case"H":return"l"+(cx=coords[0])+","+cy;case"v":return cy+=coords[0],"r0,"+coords[0];case"V":return cy=coords[0],"l"+cx+","+cy;case"c":return ctrlx=cx+coords[coords.length-4],ctrly=cy+coords[coords.length-3],cx+=coords[coords.length-2],cy+=coords[coords.length-1],"v"+coords.join(",");case"C":return ctrlx=coords[coords.length-4],ctrly=coords[coords.length-3],cx=coords[coords.length-2],cy=coords[coords.length-1],"c"+coords.join(",");case"s":return coords.unshift(cy-ctrly),coords.unshift(cx-ctrlx),ctrlx=cx+coords[coords.length-4],ctrly=cy+coords[coords.length-3],cx+=coords[coords.length-2],cy+=coords[coords.length-1],"v"+coords.join(",");case"S":return coords.unshift(cy+cy-ctrly),coords.unshift(cx+cx-ctrlx),ctrlx=coords[coords.length-4],ctrly=coords[coords.length-3],cx=coords[coords.length-2],cy=coords[coords.length-1],"c"+coords.join(",")}return""}).replace(/z/g,"e")},jvm.VMLCircleElement=function(config,style){jvm.VMLCircleElement.parentClass.call(this,"oval",config,style)},jvm.inherits(jvm.VMLCircleElement,jvm.VMLShapeElement),jvm.VMLCircleElement.prototype.applyAttr=function(attr,value){switch(attr){case"r":this.node.style.width=2*value+"px",this.node.style.height=2*value+"px",this.applyAttr("cx",this.get("cx")||0),this.applyAttr("cy",this.get("cy")||0);break;case"cx":if(!value)return;this.node.style.left=value-(this.get("r")||0)+"px";break;case"cy":if(!value)return;this.node.style.top=value-(this.get("r")||0)+"px";break;default:jvm.VMLCircleElement.parentClass.prototype.applyAttr.call(this,attr,value)}},jvm.VectorCanvas=function(container,width,height){return this.mode=window.SVGAngle?"svg":"vml","svg"==this.mode?this.impl=new jvm.SVGCanvasElement(container,width,height):this.impl=new jvm.VMLCanvasElement(container,width,height),this.impl.mode=this.mode,this.impl},jvm.SimpleScale=function(scale){this.scale=scale},jvm.SimpleScale.prototype.getValue=function(value){return value},jvm.OrdinalScale=function(scale){this.scale=scale},jvm.OrdinalScale.prototype.getValue=function(value){return this.scale[value]},jvm.OrdinalScale.prototype.getTicks=function(){var key,ticks=[];for(key in this.scale)ticks.push({label:key,value:this.scale[key]});return ticks},jvm.NumericScale=function(scale,normalizeFunction,minValue,maxValue){this.scale=[],normalizeFunction=normalizeFunction||"linear",scale&&this.setScale(scale),normalizeFunction&&this.setNormalizeFunction(normalizeFunction),void 0!==minValue&&this.setMin(minValue),void 0!==maxValue&&this.setMax(maxValue)},jvm.NumericScale.prototype={setMin:function(min){this.clearMinValue=min,"function"==typeof this.normalize?this.minValue=this.normalize(min):this.minValue=min},setMax:function(max){this.clearMaxValue=max,"function"==typeof this.normalize?this.maxValue=this.normalize(max):this.maxValue=max},setScale:function(scale){var i;for(this.scale=[],i=0;i<scale.length;i++)this.scale[i]=[scale[i]]},setNormalizeFunction:function(f){"polynomial"===f?this.normalize=function(value){return Math.pow(value,.2)}:"linear"===f?delete this.normalize:this.normalize=f,this.setMin(this.clearMinValue),this.setMax(this.clearMaxValue)},getValue:function(value){var l,c,lengthes=[],fullLength=0,i=0;for("function"==typeof this.normalize&&(value=this.normalize(value)),i=0;i<this.scale.length-1;i++)l=this.vectorLength(this.vectorSubtract(this.scale[i+1],this.scale[i])),lengthes.push(l),fullLength+=l;for(c=(this.maxValue-this.minValue)/fullLength,i=0;i<lengthes.length;i++)lengthes[i]*=c;for(i=0,value-=this.minValue;0<=value-lengthes[i];)value-=lengthes[i],i++;return value=i==this.scale.length-1?this.vectorToNum(this.scale[i]):this.vectorToNum(this.vectorAdd(this.scale[i],this.vectorMult(this.vectorSubtract(this.scale[i+1],this.scale[i]),value/lengthes[i])))},vectorToNum:function(vector){var i,num=0;for(i=0;i<vector.length;i++)num+=Math.round(vector[i])*Math.pow(256,vector.length-i-1);return num},vectorSubtract:function(vector1,vector2){var i,vector=[];for(i=0;i<vector1.length;i++)vector[i]=vector1[i]-vector2[i];return vector},vectorAdd:function(vector1,vector2){var i,vector=[];for(i=0;i<vector1.length;i++)vector[i]=vector1[i]+vector2[i];return vector},vectorMult:function(vector,num){var i,result=[];for(i=0;i<vector.length;i++)result[i]=vector[i]*num;return result},vectorLength:function(vector){var i,result=0;for(i=0;i<vector.length;i++)result+=vector[i]*vector[i];return Math.sqrt(result)},getTicks:function(){var tick,v,extent=[this.clearMinValue,this.clearMaxValue],span=extent[1]-extent[0],step=Math.pow(10,Math.floor(Math.log(span/5)/Math.LN10)),err=5/span*step,ticks=[];for(err<=.15?step*=10:err<=.35?step*=5:err<=.75&&(step*=2),extent[0]=Math.floor(extent[0]/step)*step,extent[1]=Math.ceil(extent[1]/step)*step,tick=extent[0];tick<=extent[1];)v=tick==extent[0]?this.clearMinValue:tick==extent[1]?this.clearMaxValue:tick,ticks.push({label:tick,value:this.getValue(v)}),tick+=step;return ticks}},jvm.ColorScale=function(colors,normalizeFunction,minValue,maxValue){jvm.ColorScale.parentClass.apply(this,arguments)},jvm.inherits(jvm.ColorScale,jvm.NumericScale),jvm.ColorScale.prototype.setScale=function(scale){var i;for(i=0;i<scale.length;i++)this.scale[i]=jvm.ColorScale.rgbToArray(scale[i])},jvm.ColorScale.prototype.getValue=function(value){return jvm.ColorScale.numToRgb(jvm.ColorScale.parentClass.prototype.getValue.call(this,value))},jvm.ColorScale.arrayToRgb=function(ar){var d,i,rgb="#";for(i=0;i<ar.length;i++)rgb+=1==(d=ar[i].toString(16)).length?"0"+d:d;return rgb},jvm.ColorScale.numToRgb=function(num){for(num=num.toString(16);num.length<6;)num="0"+num;return"#"+num},jvm.ColorScale.rgbToArray=function(rgb){return rgb=rgb.substr(1),[parseInt(rgb.substr(0,2),16),parseInt(rgb.substr(2,2),16),parseInt(rgb.substr(4,2),16)]},jvm.Legend=function(params){this.params=params||{},this.map=this.params.map,this.series=this.params.series,this.body=jvm.$("<div/>"),this.body.addClass("jvectormap-legend"),this.params.cssClass&&this.body.addClass(this.params.cssClass),params.vertical?this.map.legendCntVertical.append(this.body):this.map.legendCntHorizontal.append(this.body),this.render()},jvm.Legend.prototype.render=function(){var i,tick,sample,label,ticks=this.series.scale.getTicks(),inner=jvm.$("<div/>").addClass("jvectormap-legend-inner");for(this.body.html(""),this.params.title&&this.body.append(jvm.$("<div/>").addClass("jvectormap-legend-title").html(this.params.title)),this.body.append(inner),i=0;i<ticks.length;i++){switch(tick=jvm.$("<div/>").addClass("jvectormap-legend-tick"),sample=jvm.$("<div/>").addClass("jvectormap-legend-tick-sample"),this.series.params.attribute){case"fill":jvm.isImageUrl(ticks[i].value)?sample.css("background","url("+ticks[i].value+")"):sample.css("background",ticks[i].value);break;case"stroke":sample.css("background",ticks[i].value);break;case"image":sample.css("background","url("+("object"==typeof ticks[i].value?ticks[i].value.url:ticks[i].value)+") no-repeat center center");break;case"r":jvm.$("<div/>").css({"border-radius":ticks[i].value,border:this.map.params.markerStyle.initial["stroke-width"]+"px "+this.map.params.markerStyle.initial.stroke+" solid",width:2*ticks[i].value+"px",height:2*ticks[i].value+"px",background:this.map.params.markerStyle.initial.fill}).appendTo(sample)}tick.append(sample),label=ticks[i].label,this.params.labelRender&&(label=this.params.labelRender(label)),tick.append(jvm.$("<div>"+label+" </div>").addClass("jvectormap-legend-tick-text")),inner.append(tick)}inner.append(jvm.$("<div/>").css("clear","both"))},jvm.DataSeries=function(params,elements,map){var scaleConstructor;(params=params||{}).attribute=params.attribute||"fill",this.elements=elements,this.params=params,this.map=map,params.attributes&&this.setAttributes(params.attributes),jvm.$.isArray(params.scale)?(scaleConstructor="fill"===params.attribute||"stroke"===params.attribute?jvm.ColorScale:jvm.NumericScale,this.scale=new scaleConstructor(params.scale,params.normalizeFunction,params.min,params.max)):params.scale?this.scale=new jvm.OrdinalScale(params.scale):this.scale=new jvm.SimpleScale(params.scale),this.values=params.values||{},this.setValues(this.values),this.params.legend&&(this.legend=new jvm.Legend(jvm.$.extend({map:this.map,series:this},this.params.legend)))},jvm.DataSeries.prototype={setAttributes:function(key,attr){var code,attrs=key;if("string"==typeof key)this.elements[key]&&this.elements[key].setStyle(this.params.attribute,attr);else for(code in attrs)this.elements[code]&&this.elements[code].element.setStyle(this.params.attribute,attrs[code])},setValues:function(values){var val,cc,max=-Number.MAX_VALUE,min=Number.MAX_VALUE,attrs={};if(this.scale instanceof jvm.OrdinalScale||this.scale instanceof jvm.SimpleScale)for(cc in values)values[cc]?attrs[cc]=this.scale.getValue(values[cc]):attrs[cc]=this.elements[cc].element.style.initial[this.params.attribute];else{if(void 0===this.params.min||void 0===this.params.max)for(cc in values)max<(val=parseFloat(values[cc]))&&(max=val),val<min&&(min=val);for(cc in void 0===this.params.min?(this.scale.setMin(min),this.params.min=min):this.scale.setMin(this.params.min),void 0===this.params.max?(this.scale.setMax(max),this.params.max=max):this.scale.setMax(this.params.max),values)"indexOf"!=cc&&(val=parseFloat(values[cc]),isNaN(val)?attrs[cc]=this.elements[cc].element.style.initial[this.params.attribute]:attrs[cc]=this.scale.getValue(val))}this.setAttributes(attrs),jvm.$.extend(this.values,values)},clear:function(){var key,attrs={};for(key in this.values)this.elements[key]&&(attrs[key]=this.elements[key].element.shape.style.initial[this.params.attribute]);this.setAttributes(attrs),this.values={}},setScale:function(scale){this.scale.setScale(scale),this.values&&this.setValues(this.values)},setNormalizeFunction:function(f){this.scale.setNormalizeFunction(f),this.values&&this.setValues(this.values)}},jvm.Proj={degRad:180/Math.PI,radDeg:Math.PI/180,radius:6381372,sgn:function(n){return 0<n?1:n<0?-1:n},mill:function(lat,lng,c){return{x:this.radius*(lng-c)*this.radDeg,y:-this.radius*Math.log(Math.tan((45+.4*lat)*this.radDeg))/.8}},mill_inv:function(x,y,c){return{lat:(2.5*Math.atan(Math.exp(.8*y/this.radius))-5*Math.PI/8)*this.degRad,lng:(c*this.radDeg+x/this.radius)*this.degRad}},merc:function(lat,lng,c){return{x:this.radius*(lng-c)*this.radDeg,y:-this.radius*Math.log(Math.tan(Math.PI/4+lat*Math.PI/360))}},merc_inv:function(x,y,c){return{lat:(2*Math.atan(Math.exp(y/this.radius))-Math.PI/2)*this.degRad,lng:(c*this.radDeg+x/this.radius)*this.degRad}},aea:function(lat,lng,c){var lambda0=c*this.radDeg,fi1=29.5*this.radDeg,fi2=45.5*this.radDeg,fi=lat*this.radDeg,lambda=lng*this.radDeg,n=(Math.sin(fi1)+Math.sin(fi2))/2,C=Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),theta=n*(lambda-lambda0),ro=Math.sqrt(C-2*n*Math.sin(fi))/n,ro0=Math.sqrt(C-2*n*Math.sin(0))/n;return{x:ro*Math.sin(theta)*this.radius,y:-(ro0-ro*Math.cos(theta))*this.radius}},aea_inv:function(xCoord,yCoord,c){var x=xCoord/this.radius,y=yCoord/this.radius,lambda0=c*this.radDeg,fi1=29.5*this.radDeg,fi2=45.5*this.radDeg,n=(Math.sin(fi1)+Math.sin(fi2))/2,C=Math.cos(fi1)*Math.cos(fi1)+2*n*Math.sin(fi1),ro0=Math.sqrt(C-2*n*Math.sin(0))/n,ro=Math.sqrt(x*x+(ro0-y)*(ro0-y)),theta=Math.atan(x/(ro0-y));return{lat:Math.asin((C-ro*ro*n*n)/(2*n))*this.degRad,lng:(lambda0+theta/n)*this.degRad}},lcc:function(lat,lng,c){var lambda0=c*this.radDeg,lambda=lng*this.radDeg,fi1=33*this.radDeg,fi2=45*this.radDeg,fi=lat*this.radDeg,n=Math.log(Math.cos(fi1)*(1/Math.cos(fi2)))/Math.log(Math.tan(Math.PI/4+fi2/2)*(1/Math.tan(Math.PI/4+fi1/2))),F=Math.cos(fi1)*Math.pow(Math.tan(Math.PI/4+fi1/2),n)/n,ro=F*Math.pow(1/Math.tan(Math.PI/4+fi/2),n),ro0=F*Math.pow(1/Math.tan(Math.PI/4+0),n);return{x:ro*Math.sin(n*(lambda-lambda0))*this.radius,y:-(ro0-ro*Math.cos(n*(lambda-lambda0)))*this.radius}},lcc_inv:function(xCoord,yCoord,c){var x=xCoord/this.radius,y=yCoord/this.radius,lambda0=c*this.radDeg,fi1=33*this.radDeg,fi2=45*this.radDeg,n=Math.log(Math.cos(fi1)*(1/Math.cos(fi2)))/Math.log(Math.tan(Math.PI/4+fi2/2)*(1/Math.tan(Math.PI/4+fi1/2))),F=Math.cos(fi1)*Math.pow(Math.tan(Math.PI/4+fi1/2),n)/n,ro0=F*Math.pow(1/Math.tan(Math.PI/4+0),n),ro=this.sgn(n)*Math.sqrt(x*x+(ro0-y)*(ro0-y)),theta=Math.atan(x/(ro0-y));return{lat:(2*Math.atan(Math.pow(F/ro,1/n))-Math.PI/2)*this.degRad,lng:(lambda0+theta/n)*this.degRad}}},jvm.MapObject=function(config){},jvm.MapObject.prototype.getLabelText=function(key){return this.config.label?"function"==typeof this.config.label.render?this.config.label.render(key):key:null},jvm.MapObject.prototype.getLabelOffsets=function(key){var offsets;return this.config.label&&("function"==typeof this.config.label.offsets?offsets=this.config.label.offsets(key):"object"==typeof this.config.label.offsets&&(offsets=this.config.label.offsets[key])),offsets||[0,0]},jvm.MapObject.prototype.setHovered=function(isHovered){this.isHovered!==isHovered&&(this.isHovered=isHovered,this.shape.isHovered=isHovered,this.shape.updateStyle(),this.label&&(this.label.isHovered=isHovered,this.label.updateStyle()))},jvm.MapObject.prototype.setSelected=function(isSelected){this.isSelected!==isSelected&&(this.isSelected=isSelected,this.shape.isSelected=isSelected,this.shape.updateStyle(),this.label&&(this.label.isSelected=isSelected,this.label.updateStyle()),jvm.$(this.shape).trigger("selected",[isSelected]))},jvm.MapObject.prototype.setStyle=function(){this.shape.setStyle.apply(this.shape,arguments)},jvm.MapObject.prototype.remove=function(){this.shape.remove(),this.label&&this.label.remove()},jvm.Region=function(config){var bbox,text,offsets;this.config=config,this.map=this.config.map,this.shape=config.canvas.addPath({d:config.path,"data-code":config.code},config.style,config.canvas.rootElement),this.shape.addClass("jvectormap-region jvectormap-element"),bbox=this.shape.getBBox(),text=this.getLabelText(config.code),this.config.label&&text&&(offsets=this.getLabelOffsets(config.code),this.labelX=bbox.x+bbox.width/2+offsets[0],this.labelY=bbox.y+bbox.height/2+offsets[1],this.label=config.canvas.addText({text:text,"text-anchor":"middle","alignment-baseline":"central",x:this.labelX,y:this.labelY,"data-code":config.code},config.labelStyle,config.labelsGroup),this.label.addClass("jvectormap-region jvectormap-element"))},jvm.inherits(jvm.Region,jvm.MapObject),jvm.Region.prototype.updateLabelPosition=function(){this.label&&this.label.set({x:this.labelX*this.map.scale+this.map.transX*this.map.scale,y:this.labelY*this.map.scale+this.map.transY*this.map.scale})},jvm.Marker=function(config){var text;this.config=config,this.map=this.config.map,this.isImage=!!this.config.style.initial.image,this.createShape(),text=this.getLabelText(config.index),this.config.label&&text&&(this.offsets=this.getLabelOffsets(config.index),this.labelX=config.cx/this.map.scale-this.map.transX,this.labelY=config.cy/this.map.scale-this.map.transY,this.label=config.canvas.addText({text:text,"data-index":config.index,dy:"0.6ex",x:this.labelX,y:this.labelY},config.labelStyle,config.labelsGroup),this.label.addClass("jvectormap-marker jvectormap-element"))},jvm.inherits(jvm.Marker,jvm.MapObject),jvm.Marker.prototype.createShape=function(){var that=this;this.shape&&this.shape.remove(),this.shape=this.config.canvas[this.isImage?"addImage":"addCircle"]({"data-index":this.config.index,cx:this.config.cx,cy:this.config.cy},this.config.style,this.config.group),this.shape.addClass("jvectormap-marker jvectormap-element"),this.isImage&&jvm.$(this.shape.node).on("imageloaded",function(){that.updateLabelPosition()})},jvm.Marker.prototype.updateLabelPosition=function(){this.label&&this.label.set({x:this.labelX*this.map.scale+this.offsets[0]+this.map.transX*this.map.scale+5+(this.isImage?(this.shape.width||0)/2:this.shape.properties.r),y:this.labelY*this.map.scale+this.map.transY*this.map.scale+this.offsets[1]})},jvm.Marker.prototype.setStyle=function(property,value){var isImage;jvm.Marker.parentClass.prototype.setStyle.apply(this,arguments),"r"===property&&this.updateLabelPosition(),(isImage=!!this.shape.get("image"))!=this.isImage&&(this.isImage=isImage,this.config.style=jvm.$.extend(!0,{},this.shape.style),this.createShape())},jvm.Map=function(params){var e,map=this;if(this.params=jvm.$.extend(!0,{},jvm.Map.defaultParams,params),!jvm.Map.maps[this.params.map])throw new Error("Attempt to use map which was not loaded: "+this.params.map);for(e in this.mapData=jvm.Map.maps[this.params.map],this.markers={},this.regions={},this.regionsColors={},this.regionsData={},this.container=jvm.$("<div>").addClass("jvectormap-container"),this.params.container&&this.params.container.append(this.container),this.container.data("mapObject",this),this.defaultWidth=this.mapData.width,this.defaultHeight=this.mapData.height,this.setBackgroundColor(this.params.backgroundColor),this.onResize=function(){map.updateSize()},jvm.$(window).resize(this.onResize),jvm.Map.apiEvents)this.params[e]&&this.container.bind(jvm.Map.apiEvents[e]+".jvectormap",this.params[e]);this.canvas=new jvm.VectorCanvas(this.container[0],this.width,this.height),this.params.bindTouchEvents&&("ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch?this.bindContainerTouchEvents():window.MSGesture&&this.bindContainerPointerEvents()),this.bindContainerEvents(),this.bindElementEvents(),this.createTip(),this.params.zoomButtons&&this.bindZoomButtons(),this.createRegions(),this.createMarkers(this.params.markers||{}),this.updateSize(),this.params.focusOn&&("string"==typeof this.params.focusOn?this.params.focusOn={region:this.params.focusOn}:jvm.$.isArray(this.params.focusOn)&&(this.params.focusOn={regions:this.params.focusOn}),this.setFocus(this.params.focusOn)),this.params.selectedRegions&&this.setSelectedRegions(this.params.selectedRegions),this.params.selectedMarkers&&this.setSelectedMarkers(this.params.selectedMarkers),this.legendCntHorizontal=jvm.$("<div/>").addClass("jvectormap-legend-cnt jvectormap-legend-cnt-h"),this.legendCntVertical=jvm.$("<div/>").addClass("jvectormap-legend-cnt jvectormap-legend-cnt-v"),this.container.append(this.legendCntHorizontal),this.container.append(this.legendCntVertical),this.params.series&&this.createSeries()},jvm.Map.prototype={transX:0,transY:0,scale:1,baseTransX:0,baseTransY:0,baseScale:1,width:0,height:0,setBackgroundColor:function(backgroundColor){this.container.css("background-color",backgroundColor)},resize:function(){var curBaseScale=this.baseScale;this.width/this.height>this.defaultWidth/this.defaultHeight?(this.baseScale=this.height/this.defaultHeight,this.baseTransX=Math.abs(this.width-this.defaultWidth*this.baseScale)/(2*this.baseScale)):(this.baseScale=this.width/this.defaultWidth,this.baseTransY=Math.abs(this.height-this.defaultHeight*this.baseScale)/(2*this.baseScale)),this.scale*=this.baseScale/curBaseScale,this.transX*=this.baseScale/curBaseScale,this.transY*=this.baseScale/curBaseScale},updateSize:function(){this.width=this.container.width(),this.height=this.container.height(),this.resize(),this.canvas.setSize(this.width,this.height),this.applyTransform()},reset:function(){var key,i;for(key in this.series)for(i=0;i<this.series[key].length;i++)this.series[key][i].clear();this.scale=this.baseScale,this.transX=this.baseTransX,this.transY=this.baseTransY,this.applyTransform()},applyTransform:function(){var maxTransX,maxTransY,minTransX,minTransY;minTransX=this.defaultWidth*this.scale<=this.width?(maxTransX=(this.width-this.defaultWidth*this.scale)/(2*this.scale),(this.width-this.defaultWidth*this.scale)/(2*this.scale)):(maxTransX=0,(this.width-this.defaultWidth*this.scale)/this.scale),minTransY=this.defaultHeight*this.scale<=this.height?(maxTransY=(this.height-this.defaultHeight*this.scale)/(2*this.scale),(this.height-this.defaultHeight*this.scale)/(2*this.scale)):(maxTransY=0,(this.height-this.defaultHeight*this.scale)/this.scale),this.transY>maxTransY?this.transY=maxTransY:this.transY<minTransY&&(this.transY=minTransY),this.transX>maxTransX?this.transX=maxTransX:this.transX<minTransX&&(this.transX=minTransX),this.canvas.applyTransformParams(this.scale,this.transX,this.transY),this.markers&&this.repositionMarkers(),this.repositionLabels(),this.container.trigger("viewportChange",[this.scale/this.baseScale,this.transX,this.transY])},bindContainerEvents:function(){var oldPageX,oldPageY,mouseDown=!1,map=this;this.params.panOnDrag&&(this.container.mousemove(function(e){return mouseDown&&(map.transX-=(oldPageX-e.pageX)/map.scale,map.transY-=(oldPageY-e.pageY)/map.scale,map.applyTransform(),oldPageX=e.pageX,oldPageY=e.pageY),!1}).mousedown(function(e){return mouseDown=!0,oldPageX=e.pageX,oldPageY=e.pageY,!1}),this.onContainerMouseUp=function(){mouseDown=!1},jvm.$("body").mouseup(this.onContainerMouseUp)),this.params.zoomOnScroll&&this.container.mousewheel(function(event,delta,deltaX,deltaY){var offset=jvm.$(map.container).offset(),centerX=event.pageX-offset.left,centerY=event.pageY-offset.top,zoomStep=Math.pow(1+map.params.zoomOnScrollSpeed/1e3,event.deltaFactor*event.deltaY);map.tip.hide(),map.setScale(map.scale*zoomStep,centerX,centerY),event.preventDefault()})},bindContainerTouchEvents:function(){function handleTouchEvent(e){var offset,scale,transXOld,transYOld,touches=e.originalEvent.touches;"touchstart"==e.type&&(lastTouchesLength=0),1==touches.length?(1==lastTouchesLength&&(transXOld=map.transX,transYOld=map.transY,map.transX-=(touchX-touches[0].pageX)/map.scale,map.transY-=(touchY-touches[0].pageY)/map.scale,map.applyTransform(),map.tip.hide(),transXOld==map.transX&&transYOld==map.transY||e.preventDefault()),touchX=touches[0].pageX,touchY=touches[0].pageY):2==touches.length&&(2==lastTouchesLength?(scale=Math.sqrt(Math.pow(touches[0].pageX-touches[1].pageX,2)+Math.pow(touches[0].pageY-touches[1].pageY,2))/touchStartDistance,map.setScale(touchStartScale*scale,centerTouchX,centerTouchY),map.tip.hide(),e.preventDefault()):(offset=jvm.$(map.container).offset(),centerTouchX=touches[0].pageX>touches[1].pageX?touches[1].pageX+(touches[0].pageX-touches[1].pageX)/2:touches[0].pageX+(touches[1].pageX-touches[0].pageX)/2,centerTouchY=touches[0].pageY>touches[1].pageY?touches[1].pageY+(touches[0].pageY-touches[1].pageY)/2:touches[0].pageY+(touches[1].pageY-touches[0].pageY)/2,centerTouchX-=offset.left,centerTouchY-=offset.top,touchStartScale=map.scale,touchStartDistance=Math.sqrt(Math.pow(touches[0].pageX-touches[1].pageX,2)+Math.pow(touches[0].pageY-touches[1].pageY,2)))),lastTouchesLength=touches.length}var touchStartScale,touchStartDistance,touchX,touchY,centerTouchX,centerTouchY,lastTouchesLength,map=this;jvm.$(this.container).bind("touchstart",handleTouchEvent),jvm.$(this.container).bind("touchmove",handleTouchEvent)},bindContainerPointerEvents:function(){var map=this,gesture=new MSGesture,element=this.container[0];(gesture.target=element).addEventListener("MSGestureChange",function(e){var transXOld,transYOld;0==e.translationX&&0==e.translationY||(transXOld=map.transX,transYOld=map.transY,map.transX+=e.translationX/map.scale,map.transY+=e.translationY/map.scale,map.applyTransform(),map.tip.hide(),transXOld==map.transX&&transYOld==map.transY||e.preventDefault()),1!=e.scale&&(map.setScale(map.scale*e.scale,e.offsetX,e.offsetY),map.tip.hide(),e.preventDefault())},!1),element.addEventListener("pointerdown",function(e){gesture.addPointer(e.pointerId)},!1)},bindElementEvents:function(){var pageX,pageY,mouseMoved,map=this;this.container.mousemove(function(e){2<Math.abs(pageX-e.pageX)+Math.abs(pageY-e.pageY)&&(mouseMoved=!0)}),this.container.delegate("[class~='jvectormap-element']","mouseover mouseout",function(e){var type=-1===(jvm.$(this).attr("class").baseVal||jvm.$(this).attr("class")).indexOf("jvectormap-region")?"marker":"region",code="region"==type?jvm.$(this).attr("data-code"):jvm.$(this).attr("data-index"),element="region"==type?map.regions[code].element:map.markers[code].element,tipText="region"==type?map.mapData.paths[code].name:map.markers[code].config.name||"",tipShowEvent=jvm.$.Event(type+"TipShow.jvectormap"),overEvent=jvm.$.Event(type+"Over.jvectormap");"mouseover"==e.type?(map.container.trigger(overEvent,[code]),overEvent.isDefaultPrevented()||element.setHovered(!0),map.tip.text(tipText),map.container.trigger(tipShowEvent,[map.tip,code]),tipShowEvent.isDefaultPrevented()||(map.tip.show(),map.tipWidth=map.tip.width(),map.tipHeight=map.tip.height())):(element.setHovered(!1),map.tip.hide(),map.container.trigger(type+"Out.jvectormap",[code]))}),this.container.delegate("[class~='jvectormap-element']","mousedown",function(e){pageX=e.pageX,pageY=e.pageY,mouseMoved=!1}),this.container.delegate("[class~='jvectormap-element']","mouseup",function(){var type=-1===(jvm.$(this).attr("class").baseVal?jvm.$(this).attr("class").baseVal:jvm.$(this).attr("class")).indexOf("jvectormap-region")?"marker":"region",code="region"==type?jvm.$(this).attr("data-code"):jvm.$(this).attr("data-index"),clickEvent=jvm.$.Event(type+"Click.jvectormap"),element="region"==type?map.regions[code].element:map.markers[code].element;mouseMoved||(map.container.trigger(clickEvent,[code]),("region"==type&&map.params.regionsSelectable||"marker"==type&&map.params.markersSelectable)&&(clickEvent.isDefaultPrevented()||(map.params[type+"sSelectableOne"]&&map.clearSelected(type+"s"),element.setSelected(!element.isSelected))))})},bindZoomButtons:function(){var map=this;jvm.$("<div/>").addClass("jvectormap-zoomin").text("+").appendTo(this.container),jvm.$("<div/>").addClass("jvectormap-zoomout").html("&#x2212;").appendTo(this.container),this.container.find(".jvectormap-zoomin").click(function(){map.setScale(map.scale*map.params.zoomStep,map.width/2,map.height/2,!1,map.params.zoomAnimate)}),this.container.find(".jvectormap-zoomout").click(function(){map.setScale(map.scale/map.params.zoomStep,map.width/2,map.height/2,!1,map.params.zoomAnimate)})},createTip:function(){var map=this;this.tip=jvm.$("<div/>").addClass("jvectormap-tip").appendTo(jvm.$("body")),this.container.mousemove(function(e){var left=e.pageX-15-map.tipWidth,top=e.pageY-15-map.tipHeight;left<5&&(left=e.pageX+15),top<5&&(top=e.pageY+15),map.tip.css({left:left,top:top})})},setScale:function(scale,anchorX,anchorY,isCentered,animate){var interval,scaleStart,scaleDiff,transXStart,transXDiff,transYStart,transYDiff,transX,transY,viewportChangeEvent=jvm.$.Event("zoom.jvectormap"),that=this,i=0,count=Math.abs(Math.round(60*(scale-this.scale)/Math.max(scale,this.scale))),deferred=new jvm.$.Deferred;return scale>this.params.zoomMax*this.baseScale?scale=this.params.zoomMax*this.baseScale:scale<this.params.zoomMin*this.baseScale&&(scale=this.params.zoomMin*this.baseScale),void 0!==anchorX&&void 0!==anchorY&&(zoomStep=scale/this.scale,transY=isCentered?(transX=anchorX+this.defaultWidth*(this.width/(this.defaultWidth*scale))/2,anchorY+this.defaultHeight*(this.height/(this.defaultHeight*scale))/2):(transX=this.transX-(zoomStep-1)/scale*anchorX,this.transY-(zoomStep-1)/scale*anchorY)),animate&&0<count?(scaleStart=this.scale,scaleDiff=(scale-scaleStart)/count,transXStart=this.transX*this.scale,transYStart=this.transY*this.scale,transXDiff=(transX*scale-transXStart)/count,transYDiff=(transY*scale-transYStart)/count,interval=setInterval(function(){i+=1,that.scale=scaleStart+scaleDiff*i,that.transX=(transXStart+transXDiff*i)/that.scale,that.transY=(transYStart+transYDiff*i)/that.scale,that.applyTransform(),i==count&&(clearInterval(interval),that.container.trigger(viewportChangeEvent,[scale/that.baseScale]),deferred.resolve())},10)):(this.transX=transX,this.transY=transY,this.scale=scale,this.applyTransform(),this.container.trigger(viewportChangeEvent,[scale/this.baseScale]),deferred.resolve()),deferred},setFocus:function(config){var bbox,itemBbox,codes,i,point;if((config=config||{}).region?codes=[config.region]:config.regions&&(codes=config.regions),codes){for(i=0;i<codes.length;i++)this.regions[codes[i]]&&(itemBbox=this.regions[codes[i]].element.shape.getBBox())&&(bbox=void 0===bbox?itemBbox:{x:Math.min(bbox.x,itemBbox.x),y:Math.min(bbox.y,itemBbox.y),width:Math.max(bbox.x+bbox.width,itemBbox.x+itemBbox.width)-Math.min(bbox.x,itemBbox.x),height:Math.max(bbox.y+bbox.height,itemBbox.y+itemBbox.height)-Math.min(bbox.y,itemBbox.y)});return this.setScale(Math.min(this.width/bbox.width,this.height/bbox.height),-(bbox.x+bbox.width/2),-(bbox.y+bbox.height/2),!0,config.animate)}return void 0!==config.lat&&void 0!==config.lng?(point=this.latLngToPoint(config.lat,config.lng),config.x=this.transX-point.x/this.scale,config.y=this.transY-point.y/this.scale):config.x&&config.y&&(config.x*=-this.defaultWidth,config.y*=-this.defaultHeight),this.setScale(config.scale*this.baseScale,config.x,config.y,!0,config.animate)},getSelected:function(type){var key,selected=[];for(key in this[type])this[type][key].element.isSelected&&selected.push(key);return selected},getSelectedRegions:function(){return this.getSelected("regions")},getSelectedMarkers:function(){return this.getSelected("markers")},setSelected:function(type,keys){var i;if("object"!=typeof keys&&(keys=[keys]),jvm.$.isArray(keys))for(i=0;i<keys.length;i++)this[type][keys[i]].element.setSelected(!0);else for(i in keys)this[type][i].element.setSelected(!!keys[i])},setSelectedRegions:function(keys){this.setSelected("regions",keys)},setSelectedMarkers:function(keys){this.setSelected("markers",keys)},clearSelected:function(type){var i,select={},selected=this.getSelected(type);for(i=0;i<selected.length;i++)select[selected[i]]=!1;this.setSelected(type,select)},clearSelectedRegions:function(){this.clearSelected("regions")},clearSelectedMarkers:function(){this.clearSelected("markers")},getMapObject:function(){return this},getRegionName:function(code){return this.mapData.paths[code].name},createRegions:function(){var key,region,map=this;for(key in this.regionLabelsGroup=this.regionLabelsGroup||this.canvas.addGroup(),this.mapData.paths)region=new jvm.Region({map:this,path:this.mapData.paths[key].path,code:key,style:jvm.$.extend(!0,{},this.params.regionStyle),labelStyle:jvm.$.extend(!0,{},this.params.regionLabelStyle),canvas:this.canvas,labelsGroup:this.regionLabelsGroup,label:"vml"!=this.canvas.mode?this.params.labels&&this.params.labels.regions:null}),jvm.$(region.shape).bind("selected",function(e,isSelected){map.container.trigger("regionSelected.jvectormap",[jvm.$(this.node).attr("data-code"),isSelected,map.getSelectedRegions()])}),this.regions[key]={element:region,config:this.mapData.paths[key]}},createMarkers:function(markers){var i,marker,point,markerConfig,markersArray,map=this;if(this.markersGroup=this.markersGroup||this.canvas.addGroup(),this.markerLabelsGroup=this.markerLabelsGroup||this.canvas.addGroup(),jvm.$.isArray(markers))for(markersArray=markers.slice(),markers={},i=0;i<markersArray.length;i++)markers[i]=markersArray[i];for(i in markers)markerConfig=markers[i]instanceof Array?{latLng:markers[i]}:markers[i],!1!==(point=this.getMarkerPosition(markerConfig))&&(marker=new jvm.Marker({map:this,style:jvm.$.extend(!0,{},this.params.markerStyle,{initial:markerConfig.style||{}}),labelStyle:jvm.$.extend(!0,{},this.params.markerLabelStyle),index:i,cx:point.x,cy:point.y,group:this.markersGroup,canvas:this.canvas,labelsGroup:this.markerLabelsGroup,label:"vml"!=this.canvas.mode?this.params.labels&&this.params.labels.markers:null}),jvm.$(marker.shape).bind("selected",function(e,isSelected){map.container.trigger("markerSelected.jvectormap",[jvm.$(this.node).attr("data-index"),isSelected,map.getSelectedMarkers()])}),this.markers[i]&&this.removeMarkers([i]),this.markers[i]={element:marker,config:markerConfig})},repositionMarkers:function(){var i,point;for(i in this.markers)!1!==(point=this.getMarkerPosition(this.markers[i].config))&&this.markers[i].element.setStyle({cx:point.x,cy:point.y})},repositionLabels:function(){var key;for(key in this.regions)this.regions[key].element.updateLabelPosition();for(key in this.markers)this.markers[key].element.updateLabelPosition()},getMarkerPosition:function(markerConfig){return jvm.Map.maps[this.params.map].projection?this.latLngToPoint.apply(this,markerConfig.latLng||[0,0]):{x:markerConfig.coords[0]*this.scale+this.transX*this.scale,y:markerConfig.coords[1]*this.scale+this.transY*this.scale}},addMarker:function(key,marker,seriesData){var values,i,markers={},data=[];seriesData=seriesData||[];for(markers[key]=marker,i=0;i<seriesData.length;i++)values={},void 0!==seriesData[i]&&(values[key]=seriesData[i]),data.push(values);this.addMarkers(markers,data)},addMarkers:function(markers,seriesData){var i;for(seriesData=seriesData||[],this.createMarkers(markers),i=0;i<seriesData.length;i++)this.series.markers[i].setValues(seriesData[i]||{})},removeMarkers:function(markers){var i;for(i=0;i<markers.length;i++)this.markers[markers[i]].element.remove(),delete this.markers[markers[i]]},removeAllMarkers:function(){var i,markers=[];for(i in this.markers)markers.push(i);this.removeMarkers(markers)},latLngToPoint:function(lat,lng){var point,inset,bbox,proj=jvm.Map.maps[this.params.map].projection,centralMeridian=proj.centralMeridian;return lng<-180+centralMeridian&&(lng+=360),point=jvm.Proj[proj.type](lat,lng,centralMeridian),!!(inset=this.getInsetForPoint(point.x,point.y))&&(bbox=inset.bbox,point.x=(point.x-bbox[0].x)/(bbox[1].x-bbox[0].x)*inset.width*this.scale,point.y=(point.y-bbox[0].y)/(bbox[1].y-bbox[0].y)*inset.height*this.scale,{x:point.x+this.transX*this.scale+inset.left*this.scale,y:point.y+this.transY*this.scale+inset.top*this.scale})},pointToLatLng:function(x,y){var i,inset,bbox,nx,ny,proj=jvm.Map.maps[this.params.map].projection,centralMeridian=proj.centralMeridian,insets=jvm.Map.maps[this.params.map].insets;for(i=0;i<insets.length;i++)if(bbox=(inset=insets[i]).bbox,nx=x-(this.transX*this.scale+inset.left*this.scale),ny=y-(this.transY*this.scale+inset.top*this.scale),nx=nx/(inset.width*this.scale)*(bbox[1].x-bbox[0].x)+bbox[0].x,ny=ny/(inset.height*this.scale)*(bbox[1].y-bbox[0].y)+bbox[0].y,nx>bbox[0].x&&nx<bbox[1].x&&ny>bbox[0].y&&ny<bbox[1].y)return jvm.Proj[proj.type+"_inv"](nx,-ny,centralMeridian);return!1},getInsetForPoint:function(x,y){var i,bbox,insets=jvm.Map.maps[this.params.map].insets;for(i=0;i<insets.length;i++)if(x>(bbox=insets[i].bbox)[0].x&&x<bbox[1].x&&y>bbox[0].y&&y<bbox[1].y)return insets[i]},createSeries:function(){var i,key;for(key in this.series={markers:[],regions:[]},this.params.series)for(i=0;i<this.params.series[key].length;i++)this.series[key][i]=new jvm.DataSeries(this.params.series[key][i],this[key],this)},remove:function(){this.tip.remove(),this.container.remove(),jvm.$(window).unbind("resize",this.onResize),jvm.$("body").unbind("mouseup",this.onContainerMouseUp)}},jvm.Map.maps={},jvm.Map.defaultParams={map:"world_mill_en",backgroundColor:"#505050",zoomButtons:!0,zoomOnScroll:!0,zoomOnScrollSpeed:3,panOnDrag:!0,zoomMax:8,zoomMin:1,zoomStep:1.6,zoomAnimate:!0,regionsSelectable:!1,markersSelectable:!1,bindTouchEvents:!0,regionStyle:{initial:{fill:"white","fill-opacity":1,stroke:"none","stroke-width":0,"stroke-opacity":1},hover:{"fill-opacity":.8,cursor:"pointer"},selected:{fill:"yellow"},selectedHover:{}},regionLabelStyle:{initial:{"font-family":"Verdana","font-size":"12","font-weight":"bold",cursor:"default",fill:"black"},hover:{cursor:"pointer"}},markerStyle:{initial:{fill:"grey",stroke:"#505050","fill-opacity":1,"stroke-width":1,"stroke-opacity":1,r:5},hover:{stroke:"black","stroke-width":2,cursor:"pointer"},selected:{fill:"blue"},selectedHover:{}},markerLabelStyle:{initial:{"font-family":"Verdana","font-size":"12","font-weight":"bold",cursor:"default",fill:"black"},hover:{cursor:"pointer"}}},jvm.Map.apiEvents={onRegionTipShow:"regionTipShow",onRegionOver:"regionOver",onRegionOut:"regionOut",onRegionClick:"regionClick",onRegionSelected:"regionSelected",onMarkerTipShow:"markerTipShow",onMarkerOver:"markerOver",onMarkerOut:"markerOut",onMarkerClick:"markerClick",onMarkerSelected:"markerSelected",onViewportChange:"viewportChange"},jvm.MultiMap=function(params){var that=this;this.maps={},this.params=jvm.$.extend(!0,{},jvm.MultiMap.defaultParams,params),this.params.maxLevel=this.params.maxLevel||Number.MAX_VALUE,this.params.main=this.params.main||{},this.params.main.multiMapLevel=0,this.history=[this.addMap(this.params.main.map,this.params.main)],this.defaultProjection=this.history[0].mapData.projection.type,this.mapsLoaded={},this.params.container.css({position:"relative"}),this.backButton=jvm.$("<div/>").addClass("jvectormap-goback").text("Back").appendTo(this.params.container),this.backButton.hide(),this.backButton.click(function(){that.goBack()}),this.spinner=jvm.$("<div/>").addClass("jvectormap-spinner").appendTo(this.params.container),this.spinner.hide()},jvm.MultiMap.prototype={addMap:function(name,config){var cnt=jvm.$("<div/>").css({width:"100%",height:"100%"});return this.params.container.append(cnt),this.maps[name]=new jvm.Map(jvm.$.extend(config,{container:cnt})),this.params.maxLevel>config.multiMapLevel&&this.maps[name].container.on("regionClick.jvectormap",{scope:this},function(e,code){var multimap=e.data.scope,mapName=multimap.params.mapNameByCode(code,multimap);multimap.drillDownPromise&&"pending"===multimap.drillDownPromise.state()||multimap.drillDown(mapName,code)}),this.maps[name]},downloadMap:function(code){var that=this,deferred=jvm.$.Deferred();return this.mapsLoaded[code]?deferred.resolve():jvm.$.get(this.params.mapUrlByCode(code,this)).then(function(){that.mapsLoaded[code]=!0,deferred.resolve()},function(){deferred.reject()}),deferred},drillDown:function(name,code){var currentMap=this.history[this.history.length-1],that=this,focusPromise=currentMap.setFocus({region:code,animate:!0}),downloadPromise=this.downloadMap(code);focusPromise.then(function(){"pending"===downloadPromise.state()&&that.spinner.show()}),downloadPromise.always(function(){that.spinner.hide()}),this.drillDownPromise=jvm.$.when(downloadPromise,focusPromise),this.drillDownPromise.then(function(){currentMap.params.container.hide(),that.maps[name]?that.maps[name].params.container.show():that.addMap(name,{map:name,multiMapLevel:currentMap.params.multiMapLevel+1}),that.history.push(that.maps[name]),that.backButton.show()})},goBack:function(){var currentMap=this.history.pop(),prevMap=this.history[this.history.length-1],that=this;currentMap.setFocus({scale:1,x:.5,y:.5,animate:!0}).then(function(){currentMap.params.container.hide(),prevMap.params.container.show(),prevMap.updateSize(),1===that.history.length&&that.backButton.hide(),prevMap.setFocus({scale:1,x:.5,y:.5,animate:!0})})}},jvm.MultiMap.defaultParams={mapNameByCode:function(code,multiMap){return code.toLowerCase()+"_"+multiMap.defaultProjection+"_en"},mapUrlByCode:function(code,multiMap){return"jquery-jvectormap-data-"+code.toLowerCase()+"-"+multiMap.defaultProjection+"-en.js"}};
 
assets/js/jquery-jvectormap-world-mill.js DELETED
@@ -1 +0,0 @@
1
- jQuery.fn.vectorMap('addMap', 'world_mill',{"insets": [{"width": 900, "top": 0, "height": 440.7063107441331, "bbox": [{"y": -12671671.123330014, "x": -20004297.151525836}, {"y": 6930392.025135122, "x": 20026572.394749384}], "left": 0}], "paths": {"BD": {"path": "M651.84,230.21l-0.6,-2.0l-1.36,-1.71l-2.31,-0.11l-0.41,0.48l0.2,0.94l-0.53,0.99l-0.72,-0.36l-0.68,0.35l-1.2,-0.36l-0.37,-2.0l-0.81,-1.86l0.39,-1.46l-0.22,-0.47l-1.14,-0.53l0.29,-0.5l1.48,-0.94l0.03,-0.65l-1.55,-1.22l0.55,-1.14l1.61,0.94l1.04,0.15l0.18,1.54l0.34,0.35l5.64,0.63l-0.84,1.64l-1.22,0.34l-0.77,1.51l0.07,0.47l1.37,1.37l0.67,-0.19l0.42,-1.39l1.21,3.84l-0.03,1.21l-0.33,-0.15l-0.4,0.28Z", "name": "Bangladesh"}, "BE": {"path": "M429.29,144.05l1.91,0.24l2.1,-0.63l2.63,1.99l-0.21,1.66l-0.69,0.4l-0.18,1.2l-1.66,-1.13l-1.39,0.15l-2.73,-2.7l-1.17,-0.18l-0.16,-0.52l1.54,-0.5Z", "name": "Belgium"}, "BF": {"path": "M421.42,247.64l-0.11,0.95l0.34,1.16l1.4,1.71l0.07,1.1l0.32,0.37l2.55,0.51l-0.04,1.28l-0.38,0.53l-1.07,0.21l-0.72,1.18l-0.63,0.21l-3.22,-0.25l-0.94,0.39l-5.4,-0.05l-0.39,0.38l0.16,2.73l-1.23,-0.43l-1.17,0.1l-0.89,0.57l-2.27,-1.72l-0.13,-1.11l0.61,-0.96l0.02,-0.93l1.87,-1.98l0.44,-1.81l0.43,-0.39l1.28,0.26l1.05,-0.52l0.47,-0.73l1.84,-1.09l0.55,-0.83l2.2,-1.0l1.15,-0.3l0.72,0.45l1.13,-0.01Z", "name": "Burkina Faso"}, "BG": {"path": "M491.65,168.18l-0.86,0.88l-0.91,2.17l0.48,1.34l-1.6,-0.24l-2.55,0.95l-0.28,1.51l-1.8,0.22l-2.0,-1.0l-1.92,0.79l-1.42,-0.07l-0.15,-1.63l-1.05,-0.97l0.0,-0.8l1.2,-1.57l0.01,-0.56l-1.14,-1.23l-0.05,-0.94l0.88,0.97l0.88,-0.2l1.91,0.47l3.68,0.16l1.42,-0.81l2.72,-0.66l2.55,1.24Z", "name": "Bulgaria"}, "BA": {"path": "M463.49,163.65l2.1,0.5l1.72,-0.03l1.52,0.68l-0.36,0.78l0.08,0.45l1.04,1.02l-0.25,0.98l-1.81,1.15l-0.38,1.38l-1.67,-0.87l-0.89,-1.2l-2.11,-1.83l-1.63,-2.22l0.23,-0.57l0.48,0.38l0.55,-0.06l0.43,-0.51l0.94,-0.06Z", "name": "Bosnia and Herz."}, "BN": {"path": "M707.48,273.58l0.68,-0.65l1.41,-0.91l-0.15,1.63l-0.81,-0.05l-0.61,0.58l-0.53,-0.6Z", "name": "Brunei"}, "BO": {"path": "M263.83,340.69l-3.09,-0.23l-0.38,0.23l-0.7,1.52l-1.31,-1.53l-3.28,-0.64l-2.37,2.4l-1.31,0.26l-0.88,-3.26l-1.3,-2.86l0.74,-2.37l-0.13,-0.43l-1.2,-1.01l-0.37,-1.89l-1.08,-1.55l1.45,-2.56l-0.96,-2.33l0.47,-1.06l-0.34,-0.73l0.91,-1.32l0.16,-3.84l0.5,-1.18l-1.81,-3.41l2.46,0.07l0.8,-0.85l3.4,-1.91l2.66,-0.35l-0.19,1.38l0.3,1.07l-0.05,1.97l2.72,2.27l2.88,0.49l0.89,0.86l1.79,0.58l0.98,0.7l1.71,0.05l1.17,0.61l0.6,2.7l-0.7,0.54l0.96,2.99l0.37,0.28l4.3,0.1l-0.25,1.2l0.27,1.02l1.43,0.9l0.5,1.35l-0.41,1.86l-0.65,1.08l0.12,1.35l-2.69,-1.65l-2.4,-0.03l-4.36,0.76l-1.49,2.5l-0.11,1.52l-0.75,2.37Z", "name": "Bolivia"}, "JP": {"path": "M781.12,166.87l1.81,0.68l1.62,-0.97l0.39,2.42l-3.35,0.75l-2.23,2.88l-3.63,-1.9l-0.56,0.2l-1.26,3.05l-2.16,0.03l-0.29,-2.51l1.08,-2.03l2.45,-0.16l0.37,-0.33l1.25,-5.94l2.47,2.71l2.03,1.12ZM773.56,187.34l-0.91,2.22l0.37,1.52l-1.14,1.75l-3.02,1.26l-4.58,0.27l-3.34,3.01l-1.25,-0.8l-0.09,-1.9l-0.46,-0.38l-4.35,0.62l-3.0,1.32l-2.85,0.05l-0.37,0.27l0.13,0.44l2.32,1.89l-1.54,4.34l-1.26,0.9l-0.79,-0.7l0.56,-2.27l-0.21,-0.45l-1.47,-0.75l-0.74,-1.4l2.12,-0.84l1.26,-1.7l2.45,-1.42l1.83,-1.91l4.78,-0.81l2.6,0.57l0.44,-0.21l2.39,-4.66l1.29,1.06l0.5,0.01l5.1,-4.02l1.69,-3.73l-0.38,-3.4l0.9,-1.61l2.14,-0.44l1.23,3.72l-0.07,2.18l-2.23,2.84l-0.04,3.16ZM757.78,196.26l0.19,0.56l-1.01,1.21l-1.16,-0.68l-1.28,0.65l-0.69,1.45l-1.02,-0.5l0.01,-0.93l1.14,-1.38l1.57,0.14l0.85,-0.98l1.4,0.46Z", "name": "Japan"}, "BI": {"path": "M495.45,295.49l-1.08,-2.99l1.14,-0.11l0.64,-1.19l0.76,0.09l0.65,1.83l-2.1,2.36Z", "name": "Burundi"}, "BJ": {"path": "M429.57,255.75l-0.05,0.8l0.5,1.34l-0.42,0.86l0.17,0.79l-1.81,2.12l-0.57,1.76l-0.08,5.42l-1.41,0.2l-0.48,-1.36l0.11,-5.71l-0.52,-0.7l-0.2,-1.35l-1.48,-1.48l0.21,-0.9l0.89,-0.43l0.42,-0.92l1.27,-0.36l1.22,-1.34l0.61,-0.0l1.62,1.24Z", "name": "Benin"}, "BT": {"path": "M650.32,213.86l0.84,0.71l-0.12,1.1l-3.76,-0.11l-1.57,0.4l-1.93,-0.87l1.48,-1.96l1.13,-0.57l1.63,0.57l1.33,0.08l0.99,0.65Z", "name": "Bhutan"}, "JM": {"path": "M228.38,239.28l-0.8,0.4l-2.26,-1.06l0.84,-0.23l2.14,0.3l1.17,0.56l-1.08,0.03Z", "name": "Jamaica"}, "BW": {"path": "M483.92,330.07l2.27,4.01l2.83,2.86l0.96,0.31l0.78,2.43l2.13,0.61l1.02,0.76l-3.0,1.64l-2.32,2.02l-1.54,2.69l-1.52,0.45l-0.64,1.94l-1.34,0.52l-1.85,-0.12l-1.21,-0.74l-1.35,-0.3l-1.22,0.62l-0.75,1.37l-2.31,1.9l-1.4,0.21l-0.35,-0.59l0.16,-1.75l-1.48,-2.54l-0.62,-0.43l-0.0,-7.1l2.08,-0.08l0.39,-0.4l0.07,-8.9l5.19,-0.93l0.8,0.89l0.51,0.07l1.5,-0.95l2.21,-0.49Z", "name": "Botswana"}, "BR": {"path": "M259.98,275.05l3.24,0.7l0.65,-0.53l4.55,-1.32l1.08,-1.06l-0.02,-0.63l0.55,-0.05l0.28,0.28l-0.26,0.87l0.22,0.48l0.73,0.32l0.4,0.81l-0.62,0.86l-0.4,2.13l0.82,2.56l1.69,1.43l1.43,0.2l3.17,-1.68l3.18,0.3l0.65,-0.75l-0.27,-0.92l1.9,-0.09l2.39,0.99l1.06,-0.61l0.84,0.78l1.2,-0.18l1.18,-1.06l0.84,-1.94l1.36,-2.11l0.37,-0.05l1.89,5.45l1.33,0.59l0.05,1.28l-1.77,1.94l0.02,0.56l1.02,0.87l4.07,0.36l0.08,2.16l0.66,0.29l1.74,-1.5l6.97,2.32l1.02,1.22l-0.35,1.18l0.49,0.5l2.81,-0.74l4.77,1.3l3.75,-0.08l3.57,2.0l3.29,2.86l1.93,0.72l2.12,0.12l0.71,0.62l1.21,4.51l-0.95,3.98l-4.72,5.06l-1.64,2.92l-1.72,2.05l-0.8,0.3l-0.72,2.03l0.18,4.75l-0.94,5.53l-0.81,1.13l-0.43,3.36l-2.55,3.5l-0.4,2.51l-1.86,1.04l-0.67,1.53l-2.54,0.01l-3.94,1.01l-1.83,1.2l-2.87,0.82l-3.03,2.19l-2.2,2.83l-0.36,2.0l0.4,1.58l-0.44,2.6l-0.51,1.2l-1.77,1.54l-2.75,4.78l-3.83,3.42l-1.24,2.74l-1.18,1.15l-0.36,-0.83l0.95,-1.14l0.01,-0.5l-1.52,-1.97l-4.56,-3.32l-1.03,-0.0l-2.38,-2.02l-0.81,-0.0l5.34,-5.45l3.77,-2.58l0.22,-2.46l-1.35,-1.81l-0.91,0.07l0.58,-2.33l0.01,-1.54l-1.11,-0.83l-1.75,0.3l-0.44,-3.11l-0.52,-0.95l-1.88,-0.88l-1.24,0.47l-2.17,-0.41l0.15,-3.21l-0.62,-1.34l0.66,-0.73l-0.22,-1.34l0.66,-1.13l0.44,-2.04l-0.61,-1.83l-1.4,-0.86l-0.2,-0.75l0.34,-1.39l-0.38,-0.5l-4.52,-0.1l-0.72,-2.22l0.59,-0.42l-0.03,-1.1l-0.5,-0.87l-0.32,-1.7l-1.45,-0.76l-1.63,-0.02l-1.05,-0.72l-1.6,-0.48l-1.13,-0.99l-2.69,-0.4l-2.47,-2.06l0.13,-4.35l-0.45,-0.45l-3.46,0.5l-3.44,1.94l-0.6,0.74l-2.9,-0.17l-1.47,0.42l-0.72,-0.18l0.15,-3.52l-0.63,-0.34l-1.94,1.41l-1.87,-0.06l-0.83,-1.18l-1.37,-0.26l0.21,-1.01l-1.35,-1.49l-0.88,-1.91l0.56,-0.6l-0.0,-0.81l1.29,-0.62l0.22,-0.43l-0.22,-1.19l0.61,-0.91l0.15,-0.99l2.65,-1.58l1.99,-0.47l0.42,-0.36l2.06,0.11l0.42,-0.33l1.19,-8.0l-0.41,-1.56l-1.1,-1.0l0.01,-1.33l1.91,-0.42l0.08,-0.96l-0.33,-0.43l-1.14,-0.2l-0.02,-0.83l4.47,0.05l0.82,-0.67l0.82,1.81l0.8,0.07l1.15,1.1l2.26,-0.05l0.71,-0.83l2.78,-0.96l0.48,-1.13l1.6,-0.64l0.24,-0.47l-0.48,-0.82l-1.83,-0.19l-0.36,-3.22Z", "name": "Brazil"}, "BS": {"path": "M226.4,223.87l-0.48,-1.15l-0.84,-0.75l0.36,-1.11l0.95,1.95l0.01,1.06ZM225.56,216.43l-1.87,0.29l-0.04,-0.22l0.74,-0.14l1.17,0.06Z", "name": "Bahamas"}, "BY": {"path": "M493.84,128.32l0.29,0.7l0.49,0.23l1.19,-0.38l2.09,0.72l0.19,1.26l-0.45,1.24l1.57,2.26l0.89,0.59l0.17,0.81l1.58,0.56l0.4,0.5l-0.53,0.41l-1.87,-0.11l-0.73,0.38l-0.13,0.52l1.04,2.74l-1.91,0.26l-0.89,0.99l-0.11,1.18l-2.73,-0.04l-0.53,-0.62l-0.52,-0.08l-0.75,0.46l-0.91,-0.42l-1.92,-0.07l-2.75,-0.79l-2.6,-0.28l-2.0,0.07l-1.5,0.92l-0.67,0.07l-0.08,-1.22l-0.59,-1.19l1.36,-0.88l0.01,-1.35l-0.7,-1.41l-0.07,-1.0l2.16,-0.02l2.72,-1.3l0.75,-2.04l1.91,-1.04l0.2,-0.41l-0.19,-1.25l3.8,-1.78l2.3,0.77Z", "name": "Belarus"}, "BZ": {"path": "M198.03,244.38l0.1,-4.49l0.69,-0.06l0.74,-1.3l0.34,0.28l-0.4,1.3l0.17,0.58l-0.34,2.25l-1.3,1.42Z", "name": "Belize"}, "RU": {"path": "M491.55,115.25l2.55,-1.85l-0.01,-0.65l-2.2,-1.5l7.32,-6.76l1.03,-2.11l-0.13,-0.49l-3.46,-2.52l0.86,-2.7l-2.11,-2.81l1.56,-3.67l-2.77,-4.52l2.15,-2.99l-0.08,-0.55l-3.65,-2.73l0.3,-2.54l1.81,-0.37l4.26,-1.77l2.42,-1.45l4.06,2.61l6.79,1.04l9.34,4.85l1.78,1.88l0.14,2.46l-2.55,2.02l-3.9,1.06l-11.07,-3.14l-2.06,0.53l-0.13,0.7l3.94,2.94l0.31,5.86l0.26,0.36l5.14,2.24l0.58,-0.29l0.32,-1.94l-1.35,-1.78l1.13,-1.09l6.13,2.42l2.11,-0.98l0.18,-0.56l-1.51,-2.67l5.41,-3.76l2.07,0.22l2.26,1.41l0.57,-0.16l1.46,-2.87l-0.05,-0.44l-1.92,-2.32l1.12,-2.32l-1.32,-2.27l5.87,1.16l1.04,1.75l-2.59,0.43l-0.33,0.4l0.02,2.36l2.46,1.83l3.87,-0.91l0.86,-2.8l13.69,-5.65l0.99,0.11l-1.92,2.06l0.23,0.67l3.11,0.45l2.0,-1.48l4.56,-0.12l3.64,-1.73l2.65,2.44l0.56,-0.01l2.85,-2.88l-0.01,-0.57l-2.35,-2.29l0.9,-1.01l7.14,1.3l3.41,1.36l9.05,4.97l0.51,-0.11l1.67,-2.27l-0.05,-0.53l-2.43,-2.21l-0.06,-0.78l-0.34,-0.36l-2.52,-0.36l0.64,-1.93l-1.32,-3.46l-0.06,-1.21l4.48,-4.06l1.69,-4.29l1.6,-0.81l6.23,1.18l0.44,2.21l-2.29,3.64l0.06,0.5l1.47,1.39l0.76,3.0l-0.56,6.03l2.69,2.82l-0.96,2.57l-4.86,5.95l0.23,0.64l2.86,0.61l0.42,-0.17l0.93,-1.4l2.64,-1.03l0.87,-2.24l2.09,-1.96l0.07,-0.5l-1.36,-2.28l1.09,-2.69l-0.32,-0.55l-2.47,-0.33l-0.5,-2.06l1.94,-4.38l-0.06,-0.42l-2.96,-3.4l4.12,-2.88l0.16,-0.4l-0.51,-2.93l0.54,-0.05l1.13,2.25l-0.96,4.35l0.27,0.47l2.68,0.84l0.5,-0.51l-1.02,-2.99l3.79,-1.66l5.01,-0.24l4.53,2.61l0.48,-0.06l0.07,-0.48l-2.18,-3.82l-0.23,-4.67l3.98,-0.9l5.97,0.21l5.49,-0.64l0.27,-0.65l-1.83,-2.31l2.56,-2.9l2.87,-0.17l4.8,-2.47l6.54,-0.67l1.03,-1.42l6.25,-0.45l2.32,1.11l5.53,-2.7l4.5,0.08l0.39,-0.28l0.66,-2.15l2.26,-2.12l5.69,-2.11l3.21,1.29l-2.46,0.94l-0.25,0.42l0.34,0.35l5.41,0.77l0.61,2.33l0.58,0.25l2.2,-1.22l7.13,0.07l5.51,2.47l1.79,1.72l-0.53,2.24l-9.16,4.15l-1.97,1.52l0.16,0.71l6.77,1.91l2.16,-0.78l1.13,2.74l0.67,0.11l1.01,-1.15l3.81,-0.73l7.7,0.77l0.54,1.99l0.36,0.29l10.47,0.71l0.43,-0.38l0.13,-3.23l4.87,0.78l3.95,-0.02l3.83,2.4l1.03,2.71l-1.35,1.79l0.02,0.5l3.15,3.64l4.07,1.96l0.53,-0.18l2.23,-4.47l3.95,1.93l4.16,-1.21l4.73,1.39l2.05,-1.26l3.94,0.62l0.43,-0.55l-1.68,-4.02l2.89,-1.8l22.31,3.03l2.16,2.75l6.55,3.51l10.29,-0.81l4.82,0.73l1.85,1.66l-0.29,3.08l0.25,0.41l3.08,1.26l3.56,-0.88l4.35,-0.11l4.8,0.87l4.57,-0.47l4.23,3.79l0.43,0.07l3.1,-1.4l0.16,-0.6l-1.88,-2.62l0.85,-1.52l7.71,1.21l5.22,-0.26l7.09,2.09l9.59,5.22l6.35,4.11l-0.2,2.38l1.88,1.41l0.6,-0.42l-0.48,-2.53l6.15,0.57l4.4,3.51l-1.97,1.43l-4.0,0.41l-0.36,0.39l-0.06,3.79l-0.74,0.62l-2.07,-0.11l-1.91,-1.39l-3.14,-1.11l-0.78,-1.85l-2.72,-0.68l-2.63,0.49l-1.04,-1.1l0.46,-1.31l-0.5,-0.51l-3.0,0.98l-0.22,0.58l0.99,1.7l-1.21,1.48l-3.04,1.68l-3.12,-0.28l-0.4,0.23l0.09,0.46l2.2,2.09l1.46,3.2l1.15,1.1l0.24,1.33l-0.42,0.67l-4.63,-0.77l-6.96,2.9l-2.19,0.44l-7.6,5.06l-0.84,1.45l-3.61,-2.37l-6.24,2.82l-0.94,-1.15l-0.53,-0.08l-2.28,1.52l-3.2,-0.49l-0.44,0.27l-0.78,2.37l-3.05,3.78l0.09,1.47l0.29,0.36l2.54,0.72l-0.29,4.53l-1.97,0.11l-0.35,0.26l-1.07,2.94l0.8,1.45l-3.91,1.58l-1.05,3.95l-3.48,0.77l-0.3,0.3l-0.72,3.29l-3.09,2.65l-0.7,-1.74l-2.44,-12.44l1.16,-4.71l2.04,-2.06l0.22,-1.64l3.8,-0.86l4.46,-4.61l4.28,-3.81l4.48,-3.01l2.17,-5.63l-0.42,-0.54l-3.04,0.33l-1.77,3.31l-5.86,3.86l-1.86,-4.25l-0.45,-0.23l-6.46,1.3l-6.47,6.44l-0.01,0.55l1.58,1.74l-8.24,1.17l0.15,-2.2l-0.34,-0.42l-3.89,-0.56l-3.25,1.81l-7.62,-0.62l-8.45,1.19l-17.71,15.41l0.22,0.7l3.74,0.41l1.36,2.17l2.43,0.76l1.88,-1.68l2.4,0.2l3.4,3.54l0.08,2.6l-1.95,3.42l-0.21,3.9l-1.1,5.06l-3.71,4.54l-0.87,2.21l-8.29,8.89l-3.19,1.7l-1.32,0.03l-1.45,-1.36l-0.49,-0.04l-2.27,1.5l0.41,-3.65l-0.59,-2.47l1.75,-0.89l2.91,0.53l0.42,-0.2l1.68,-3.03l0.87,-3.46l0.97,-1.18l1.32,-2.88l-0.45,-0.56l-4.14,0.95l-2.19,1.25l-3.41,-0.0l-1.06,-2.93l-2.97,-2.3l-4.28,-1.06l-1.75,-5.07l-2.66,-5.01l-2.29,-1.29l-3.75,-1.01l-3.44,0.08l-3.18,0.62l-2.24,1.77l0.05,0.66l1.18,0.69l0.02,1.43l-1.33,1.05l-2.26,3.51l-0.04,1.43l-3.16,1.84l-2.82,-1.16l-3.01,0.23l-1.35,-1.07l-1.5,-0.35l-3.9,2.31l-3.22,0.52l-2.27,0.79l-3.05,-0.51l-2.21,0.03l-1.48,-1.6l-2.6,-1.63l-2.63,-0.43l-5.46,1.01l-3.23,-1.25l-0.72,-2.57l-5.2,-1.24l-2.75,-1.36l-0.5,0.12l-2.59,3.45l0.84,2.1l-2.06,1.93l-3.41,-0.77l-2.42,-0.12l-1.83,-1.54l-2.53,-0.05l-2.42,-0.98l-3.86,1.57l-4.72,2.78l-3.3,0.75l-1.55,-1.92l-3.0,0.41l-1.11,-1.33l-1.62,-0.59l-1.31,-1.94l-1.38,-0.6l-3.7,0.79l-3.31,-1.83l-0.51,0.11l-0.99,1.29l-5.29,-8.05l-2.96,-2.48l0.65,-0.77l0.01,-0.51l-0.5,-0.11l-6.2,3.21l-1.84,0.15l0.15,-1.39l-0.26,-0.42l-3.22,-1.17l-2.46,0.7l-0.69,-3.16l-0.32,-0.31l-4.5,-0.75l-2.47,1.47l-6.19,1.27l-1.29,0.86l-9.51,1.3l-1.15,1.17l-0.03,0.53l1.47,1.9l-1.89,0.69l-0.22,0.56l0.31,0.6l-2.11,1.44l0.03,0.68l3.75,2.12l-0.39,0.98l-3.23,-0.13l-0.86,0.86l-3.09,-1.59l-3.97,0.07l-2.66,1.35l-8.32,-3.56l-4.07,0.06l-5.39,3.68l-0.39,2.0l-2.03,-1.5l-0.59,0.13l-2.0,3.59l0.57,0.93l-1.28,2.16l0.06,0.48l2.13,2.17l1.95,0.04l1.37,1.82l-0.23,1.46l0.25,0.43l0.83,0.33l-0.8,1.31l-2.49,0.62l-2.49,3.2l0.0,0.49l2.17,2.78l-0.15,2.18l2.5,3.24l-1.58,1.59l-0.7,-0.13l-1.63,-1.72l-2.29,-0.84l-0.94,-1.31l-2.34,-0.63l-1.48,0.4l-0.43,-0.47l-3.51,-1.48l-5.76,-1.01l-0.45,0.19l-2.89,-2.34l-2.9,-1.2l-1.53,-1.29l1.29,-0.43l2.08,-2.61l-0.05,-0.55l-0.89,-0.79l3.05,-1.06l0.27,-0.42l-0.07,-0.69l-0.49,-0.35l-1.73,0.39l0.04,-0.68l1.04,-0.72l2.66,-0.48l0.4,-1.32l-0.5,-1.6l0.92,-1.54l0.03,-1.17l-0.29,-0.37l-3.69,-1.06l-1.41,0.02l-1.42,-1.41l-2.19,0.38l-2.77,-1.01l-0.03,-0.59l-0.89,-1.43l-2.0,-0.32l-0.11,-0.54l0.49,-0.53l0.01,-0.53l-1.6,-1.9l-3.58,0.02l-0.88,0.73l-0.46,-0.07l-1.0,-2.79l2.22,-0.02l0.97,-0.74l0.07,-0.57l-0.9,-1.04l-1.35,-0.48l-0.11,-0.7l-0.95,-0.58l-1.38,-1.99l0.46,-0.98l-0.51,-1.96l-2.45,-0.84l-1.21,0.3l-0.46,-0.76l-2.46,-0.83l-0.72,-1.87l-0.21,-1.69l-0.99,-0.85l0.85,-1.17l-0.7,-3.21l1.66,-1.97l-0.16,-0.79ZM749.2,170.72l-0.6,0.4l-0.13,0.16l-0.01,-0.51l0.74,-0.05ZM871.88,65.81l2.17,-0.13l3.19,1.16l-2.39,1.09l-5.63,0.48l-0.26,-0.84l2.92,-1.76ZM797.39,48.49l-2.0,1.36l-3.8,-0.42l-4.25,-1.8l0.35,-0.97l9.69,1.83ZM783.67,46.12l-1.63,3.09l-8.98,-0.13l-4.09,1.14l-4.54,-2.97l1.16,-3.01l3.05,-0.89l6.5,0.22l8.54,2.56ZM778.2,134.98l-0.56,-0.9l0.27,-0.12l0.29,1.01ZM778.34,135.48l0.94,3.53l-0.05,3.38l1.05,3.39l2.18,5.0l-2.89,-0.83l-0.49,0.26l-1.54,4.65l2.42,3.5l-0.04,1.13l-1.24,-1.24l-0.61,0.06l-1.09,1.61l-0.28,-1.61l0.27,-3.1l-0.28,-3.4l0.58,-2.47l0.11,-4.39l-1.46,-3.36l0.21,-4.32l2.15,-1.46l0.07,-0.34ZM771.95,56.61l1.76,-1.42l2.89,-0.42l3.28,1.71l0.14,0.6l-3.27,0.03l-4.81,-0.5ZM683.76,31.09l-13.01,1.93l4.03,-6.35l1.82,-0.56l1.73,0.34l5.99,2.98l-0.56,1.66ZM670.85,27.93l-5.08,0.64l-6.86,-1.57l-3.99,-2.05l-2.1,-4.16l-2.6,-0.87l5.72,-3.5l5.2,-1.28l4.69,2.85l5.59,5.4l-0.56,4.53ZM564.15,68.94l-0.64,0.17l-7.85,-0.57l-0.86,-2.04l-4.28,-1.17l-0.28,-1.94l2.27,-0.89l0.25,-0.39l-0.08,-2.38l4.81,-3.97l-0.15,-0.7l-1.47,-0.38l5.3,-3.81l0.15,-0.44l-0.58,-1.94l5.28,-2.51l8.21,-3.27l8.28,-0.96l4.35,-1.94l4.6,-0.64l1.36,1.61l-1.34,1.28l-16.43,4.94l-7.97,4.88l-7.74,9.63l0.66,4.14l4.16,3.27ZM548.81,18.48l-5.5,1.18l-0.58,1.02l-2.59,0.84l-2.13,-1.07l1.12,-1.42l-0.3,-0.65l-2.33,-0.07l1.68,-0.36l3.47,-0.06l0.42,1.29l0.66,0.16l1.38,-1.34l2.15,-0.88l2.94,1.01l-0.39,0.36ZM477.37,133.15l-4.08,0.05l-2.56,-0.32l0.33,-0.87l3.17,-1.03l3.24,0.96l-0.09,1.23Z", "name": "Russia"}, "RW": {"path": "M497.0,288.25l0.71,1.01l-0.11,1.09l-1.63,0.03l-1.04,1.39l-0.83,-0.11l0.51,-1.2l0.08,-1.34l0.42,-0.41l0.7,0.14l1.19,-0.61Z", "name": "Rwanda"}, "RS": {"path": "M469.4,163.99l0.42,-0.5l-0.01,-0.52l-1.15,-1.63l1.43,-0.62l1.33,0.12l1.17,1.06l0.46,1.13l1.34,0.64l0.35,1.35l1.46,0.9l0.76,-0.29l0.2,0.69l-0.48,0.78l0.22,1.12l1.05,1.22l-0.77,0.8l-0.37,1.52l-1.21,0.08l0.24,-0.64l-0.39,-0.54l-2.08,-1.64l-0.9,0.05l-0.48,0.94l-2.12,-1.37l0.53,-1.6l-1.11,-1.37l0.51,-1.1l-0.41,-0.57Z", "name": "Serbia"}, "TL": {"path": "M734.55,307.93l-0.1,-0.97l4.5,-0.86l-2.82,1.28l-1.59,0.55Z", "name": "Timor-Leste"}, "TM": {"path": "M553.03,173.76l-0.04,0.34l-0.09,-0.22l0.13,-0.12ZM555.87,172.66l0.45,-0.1l1.48,0.74l2.06,2.43l4.07,-0.18l0.38,-0.51l-0.32,-1.19l1.92,-0.94l1.91,-1.59l2.94,1.39l0.43,2.47l1.19,0.67l2.58,-0.13l0.62,0.4l1.32,3.12l4.54,3.44l2.67,1.45l3.06,1.14l-0.04,1.05l-1.33,-0.75l-0.59,0.19l-0.32,0.84l-2.2,0.81l-0.46,2.13l-1.21,0.74l-1.91,0.42l-0.73,1.33l-1.56,0.31l-2.22,-0.94l-0.2,-2.17l-0.38,-0.36l-1.73,-0.09l-2.76,-2.46l-2.14,-0.4l-2.84,-1.48l-1.78,-0.27l-1.24,0.53l-1.57,-0.08l-2.0,1.69l-1.7,0.43l-0.36,-1.58l0.36,-2.98l-0.22,-0.4l-1.65,-0.84l0.54,-1.69l-0.34,-0.52l-1.22,-0.13l0.36,-1.64l2.22,0.59l2.2,-0.95l0.12,-0.65l-1.77,-1.74l-0.66,-1.57Z", "name": "Turkmenistan"}, "TJ": {"path": "M597.75,178.82l-2.54,-0.44l-0.47,0.34l-0.24,1.7l0.43,0.45l2.64,-0.22l3.18,0.95l4.39,-0.41l0.56,2.37l0.52,0.29l0.67,-0.24l1.11,0.49l0.21,2.13l-3.76,-0.21l-1.8,1.32l-1.76,0.74l-0.61,-0.58l0.21,-2.23l-0.64,-0.49l-0.07,-0.93l-1.36,-0.66l-0.45,0.07l-1.08,1.01l-0.55,1.48l-1.31,-0.05l-0.95,1.16l-0.9,-0.35l-1.86,0.74l1.26,-2.83l-0.54,-2.17l-1.67,-0.82l0.33,-0.66l2.18,-0.04l1.19,-1.63l0.76,-1.79l2.43,-0.5l-0.26,1.0l0.73,1.05Z", "name": "Tajikistan"}, "RO": {"path": "M487.53,154.23l0.6,0.24l2.87,3.98l-0.17,2.69l0.45,1.42l1.32,0.81l1.35,-0.42l0.76,0.36l0.02,0.31l-0.83,0.45l-0.59,-0.22l-0.54,0.3l-0.62,3.3l-1.0,-0.22l-2.07,-1.13l-2.95,0.71l-1.25,0.76l-3.51,-0.15l-1.89,-0.47l-0.87,0.16l-0.82,-1.3l0.29,-0.26l-0.06,-0.64l-1.09,-0.34l-0.56,0.5l-1.05,-0.64l-0.39,-1.39l-1.36,-0.65l-0.35,-1.0l-0.83,-0.75l1.54,-0.54l2.66,-4.21l2.4,-1.24l2.96,0.34l1.48,0.73l0.79,-0.45l1.78,-0.3l0.75,-0.74l0.79,0.0Z", "name": "Romania"}, "GW": {"path": "M386.23,253.6l-0.29,0.84l0.15,0.6l-2.21,0.59l-0.86,0.96l-1.04,-0.83l-1.09,-0.23l-0.54,-1.06l-0.66,-0.49l2.41,-0.48l4.13,0.1Z", "name": "Guinea-Bissau"}, "GT": {"path": "M195.08,249.77l-2.48,-0.37l-1.03,-0.45l-1.14,-0.89l0.3,-0.99l-0.24,-0.68l0.96,-1.66l2.98,-0.01l0.4,-0.37l-0.19,-1.28l-1.67,-1.4l0.51,-0.4l0.0,-1.05l3.85,0.02l-0.21,4.53l0.4,0.43l1.46,0.38l-1.48,0.98l-0.35,0.7l0.12,0.57l-2.2,1.96Z", "name": "Guatemala"}, "GR": {"path": "M487.07,174.59l-0.59,1.43l-0.37,0.21l-2.84,-0.35l-3.03,0.77l-0.18,0.68l1.28,1.23l-0.61,0.23l-1.14,0.0l-1.2,-1.39l-0.63,0.03l-0.53,1.01l0.56,1.76l1.03,1.19l-0.56,0.38l-0.05,0.62l2.52,2.12l0.02,0.87l-1.78,-0.59l-0.48,0.56l0.5,1.0l-1.07,0.2l-0.3,0.53l0.75,2.01l-0.98,0.02l-1.84,-1.12l-1.37,-4.2l-2.21,-2.95l-0.11,-0.56l1.04,-1.28l0.2,-0.95l0.85,-0.66l0.03,-0.46l1.32,-0.21l1.01,-0.64l1.22,0.05l0.65,-0.56l2.26,-0.0l1.82,-0.75l1.85,1.0l2.28,-0.28l0.35,-0.39l0.01,-0.77l0.34,0.22ZM480.49,192.16l0.58,0.4l-0.68,-0.12l0.11,-0.28ZM482.52,192.82l2.51,0.06l0.24,0.32l-1.99,0.13l-0.77,-0.51Z", "name": "Greece"}, "GQ": {"path": "M448.79,279.62l0.02,2.22l-4.09,0.0l0.69,-2.27l3.38,0.05Z", "name": "Eq. Guinea"}, "GY": {"path": "M277.42,270.07l-0.32,1.83l-1.32,0.57l-0.23,0.46l-0.28,2.0l1.11,1.82l0.83,0.19l0.32,1.25l1.13,1.62l-1.21,-0.19l-1.08,0.71l-1.77,0.5l-0.44,0.46l-0.86,-0.09l-1.32,-1.01l-0.77,-2.27l0.36,-1.9l0.68,-1.23l-0.57,-1.17l-0.74,-0.43l0.12,-1.16l-0.9,-0.69l-1.1,0.09l-1.31,-1.48l0.53,-0.72l-0.04,-0.84l1.99,-0.86l0.05,-0.59l-0.71,-0.78l0.14,-0.57l1.66,-1.24l1.36,0.77l1.41,1.49l0.06,1.15l0.37,0.38l0.8,0.05l2.06,1.86Z", "name": "Guyana"}, "GE": {"path": "M521.71,168.93l5.29,0.89l4.07,2.01l1.41,-0.44l2.07,0.56l0.68,1.1l1.07,0.55l-0.12,0.59l0.98,1.29l-1.01,-0.13l-1.81,-0.83l-0.94,0.47l-3.23,0.43l-2.29,-1.39l-2.33,0.05l0.21,-0.97l-0.76,-2.26l-1.45,-1.12l-1.43,-0.39l-0.41,-0.42Z", "name": "Georgia"}, "GB": {"path": "M412.61,118.72l-2.19,3.22l-0.0,0.45l5.13,-0.3l-0.53,2.37l-2.2,3.12l0.29,0.63l2.37,0.21l2.33,4.3l1.76,0.69l2.2,5.12l2.94,0.77l-0.23,1.62l-1.15,0.88l-0.1,0.52l0.82,1.42l-1.86,1.43l-3.3,-0.02l-4.12,0.87l-1.04,-0.58l-0.47,0.06l-1.51,1.41l-2.12,-0.34l-1.86,1.18l-0.6,-0.29l3.19,-3.0l2.16,-0.69l0.28,-0.41l-0.34,-0.36l-3.73,-0.53l-0.4,-0.76l2.2,-0.87l0.17,-0.61l-1.26,-1.67l0.36,-1.7l3.38,0.28l0.43,-0.33l0.37,-1.99l-1.79,-2.49l-3.11,-0.72l-0.38,-0.59l0.79,-1.35l-0.04,-0.46l-0.82,-0.97l-0.61,0.01l-0.68,0.84l-0.1,-2.34l-1.23,-1.88l0.85,-3.47l1.77,-2.68l1.85,0.26l2.17,-0.22ZM406.26,132.86l-1.01,1.77l-1.57,-0.59l-1.16,0.01l0.37,-1.54l-0.39,-1.39l1.45,-0.1l2.3,1.84Z", "name": "United Kingdom"}, "GA": {"path": "M453.24,279.52l-0.08,0.98l0.7,1.29l2.36,0.24l-0.98,2.63l1.18,1.79l0.25,1.78l-0.29,1.52l-0.6,0.93l-1.84,-0.09l-1.23,-1.11l-0.66,0.23l-0.15,0.84l-1.42,0.26l-1.02,0.7l-0.11,0.52l0.77,1.35l-1.34,0.97l-3.94,-4.3l-1.44,-2.45l0.06,-0.6l0.54,-0.81l1.05,-3.46l4.17,-0.07l0.4,-0.4l-0.02,-2.66l2.39,0.21l1.25,-0.27Z", "name": "Gabon"}, "GN": {"path": "M391.8,254.11l0.47,0.8l1.11,-0.32l0.98,0.7l1.07,0.2l2.26,-1.22l0.64,0.44l1.13,1.56l-0.48,1.4l0.8,0.3l-0.08,0.48l0.46,0.68l-0.35,1.36l1.05,2.61l-1.0,0.69l0.03,1.41l-0.72,-0.06l-1.08,1.0l-0.24,-0.27l0.07,-1.11l-1.05,-1.54l-1.79,0.21l-0.35,-2.01l-1.6,-2.18l-2.0,-0.0l-1.31,0.54l-1.95,2.18l-1.86,-2.19l-1.2,-0.78l-0.3,-1.11l-0.8,-0.85l0.65,-0.72l0.81,-0.03l1.64,-0.8l0.23,-1.87l2.67,0.64l0.89,-0.3l1.21,0.15Z", "name": "Guinea"}, "GM": {"path": "M379.31,251.39l0.1,-0.35l2.43,-0.07l0.74,-0.61l0.51,-0.03l0.77,0.49l-1.03,-0.3l-1.87,0.9l-1.65,-0.04ZM384.03,250.91l0.91,0.05l0.75,-0.24l-0.59,0.31l-1.08,-0.13Z", "name": "Gambia"}, "GL": {"path": "M353.02,1.2l14.69,4.67l-3.68,1.89l-22.97,0.86l-0.36,0.27l0.12,0.43l1.55,1.18l8.79,-0.66l7.48,2.07l4.86,-1.77l1.66,1.73l-2.53,3.19l-0.01,0.48l0.46,0.15l6.35,-2.2l12.06,-2.31l7.24,1.13l1.09,1.99l-9.79,4.01l-1.44,1.32l-7.87,0.98l-0.35,0.41l0.38,0.38l5.07,0.24l-2.53,3.58l-2.07,3.81l0.08,6.05l2.57,3.11l-3.22,0.2l-4.12,1.66l-0.05,0.72l4.45,2.65l0.51,3.75l-2.3,0.4l-0.25,0.64l2.79,3.69l-4.82,0.31l-0.36,0.29l0.16,0.44l2.62,1.8l-0.59,1.22l-3.3,0.7l-3.45,0.01l-0.29,0.68l3.03,3.12l0.02,1.34l-4.4,-1.73l-1.72,1.35l0.15,0.66l3.31,1.15l3.13,2.71l0.81,3.16l-3.85,0.75l-4.89,-4.26l-0.47,-0.03l-0.17,0.44l0.79,2.86l-2.71,2.21l-0.13,0.44l0.37,0.27l8.73,0.34l-12.32,6.64l-7.24,1.48l-2.94,0.08l-2.69,1.75l-3.43,4.41l-5.24,2.84l-1.73,0.18l-7.12,2.1l-2.15,2.52l-0.13,2.99l-1.19,2.45l-4.01,3.09l-0.14,0.44l0.97,2.9l-2.28,6.48l-3.1,0.2l-3.83,-3.07l-4.86,-0.02l-2.25,-1.93l-1.7,-3.79l-4.3,-4.84l-1.21,-2.49l-0.44,-3.8l-3.32,-3.63l0.84,-2.86l-1.56,-1.7l2.28,-4.6l3.83,-1.74l1.03,-1.96l0.52,-3.47l-0.59,-0.41l-4.17,2.21l-2.07,0.58l-2.72,-1.28l-0.15,-2.71l0.85,-2.09l2.01,-0.06l5.06,1.2l0.46,-0.23l-0.14,-0.49l-6.54,-4.47l-2.67,0.55l-1.58,-0.86l2.56,-4.01l-0.03,-0.48l-1.5,-1.74l-4.98,-8.5l-3.13,-1.96l0.03,-1.88l-0.24,-0.37l-6.85,-3.02l-5.36,-0.38l-12.7,0.58l-2.78,-1.57l-3.66,-2.77l5.73,-1.45l5.0,-0.28l0.38,-0.38l-0.35,-0.41l-10.67,-1.38l-5.3,-2.06l0.25,-1.54l18.41,-5.26l1.22,-2.27l-0.25,-0.55l-6.14,-1.86l1.68,-1.77l8.55,-4.03l3.59,-0.63l0.3,-0.54l-0.88,-2.27l5.47,-1.47l7.65,-0.95l7.55,-0.05l3.04,1.85l6.48,-3.27l5.81,2.22l3.56,0.5l5.16,1.94l0.5,-0.21l-0.17,-0.52l-5.71,-3.13l0.28,-2.13l8.12,-3.6l8.7,0.28l3.35,-2.34l8.71,-0.6l19.93,0.8Z", "name": "Greenland"}, "GH": {"path": "M420.53,257.51l-0.01,0.72l0.96,1.2l0.24,3.73l0.59,0.95l-0.51,2.1l0.19,1.41l1.02,2.21l-6.97,2.84l-1.8,-0.57l0.04,-0.89l-1.02,-2.04l0.61,-2.65l1.07,-2.32l-0.96,-6.47l5.01,0.07l0.94,-0.39l0.61,0.11Z", "name": "Ghana"}, "OM": {"path": "M568.09,230.93l-0.91,1.67l-1.22,0.04l-0.6,0.76l-0.41,1.51l0.27,1.58l-1.16,0.05l-1.56,0.97l-0.76,1.74l-1.62,0.05l-0.98,0.65l-0.17,1.15l-0.89,0.52l-1.49,-0.18l-2.4,0.94l-2.47,-5.4l7.35,-2.71l1.67,-5.23l-1.12,-2.09l0.05,-0.83l0.67,-1.0l0.07,-1.05l0.9,-0.42l-0.05,-2.07l0.7,-0.01l1.0,1.62l1.51,1.08l3.3,0.84l1.73,2.29l0.81,0.37l-1.23,2.35l-0.99,0.79Z", "name": "Oman"}, "TN": {"path": "M448.1,188.24l-1.0,1.27l-0.02,1.32l0.84,0.88l-0.28,2.09l-1.53,1.32l-0.12,0.42l0.48,1.54l1.42,0.32l0.53,1.11l0.9,0.52l-0.11,1.67l-3.54,2.64l-0.1,2.38l-0.58,0.3l-0.96,-4.45l-1.54,-1.25l-0.16,-0.78l-1.92,-1.56l-0.18,-1.76l1.51,-1.62l0.59,-2.34l-0.38,-2.78l0.42,-1.21l2.45,-1.05l1.29,0.26l-0.06,1.11l0.58,0.38l1.47,-0.73Z", "name": "Tunisia"}, "JO": {"path": "M518.64,201.38l-5.14,1.56l-0.19,0.65l2.16,2.39l-0.89,1.14l-1.71,0.34l-1.71,1.8l-2.34,-0.37l1.21,-4.32l0.56,-4.07l2.8,0.94l4.46,-2.71l0.79,2.66Z", "name": "Jordan"}, "HR": {"path": "M455.59,162.84l1.09,0.07l-0.82,0.94l-0.27,-1.01ZM456.96,162.92l0.62,-0.41l1.73,0.45l0.42,-0.4l-0.01,-0.59l0.86,-0.52l0.2,-1.05l1.63,-0.68l2.57,1.68l2.07,0.6l0.87,-0.31l1.05,1.57l-0.52,0.63l-1.05,-0.56l-1.68,0.04l-2.1,-0.5l-1.29,0.06l-0.57,0.49l-0.59,-0.47l-0.62,0.16l-0.46,1.7l1.79,2.42l2.79,2.75l-1.18,-0.87l-2.21,-0.87l-1.67,-1.78l0.13,-0.63l-1.05,-1.19l-0.32,-1.27l-1.42,-0.43Z", "name": "Croatia"}, "HT": {"path": "M237.05,238.38l-1.16,0.43l-0.91,-0.55l0.05,-0.2l2.02,0.31ZM237.53,238.43l1.06,0.12l-0.05,0.01l-1.01,-0.12ZM239.25,238.45l0.79,-0.51l0.06,-0.62l-1.02,-1.0l0.02,-0.82l-0.3,-0.4l-0.93,-0.32l3.16,0.45l0.02,1.84l-0.48,0.34l-0.08,0.58l0.54,0.72l-1.78,-0.26Z", "name": "Haiti"}, "HU": {"path": "M462.08,157.89l0.65,-1.59l-0.09,-0.44l0.64,-0.0l0.39,-0.34l0.1,-0.69l1.75,0.87l2.32,-0.37l0.43,-0.66l3.49,-0.78l0.69,-0.78l0.57,-0.14l2.57,0.93l0.67,-0.23l1.03,0.65l0.08,0.37l-1.42,0.71l-2.59,4.14l-1.8,0.53l-1.68,-0.1l-2.74,1.23l-1.85,-0.54l-2.54,-1.66l-0.66,-1.1Z", "name": "Hungary"}, "HN": {"path": "M199.6,249.52l-1.7,-1.21l0.06,-0.94l3.04,-2.14l2.37,0.28l1.27,-0.09l1.1,-0.52l1.3,0.28l1.14,-0.25l1.38,0.37l2.23,1.37l-2.36,0.93l-1.23,-0.39l-0.88,1.3l-1.28,0.99l-0.98,-0.22l-0.42,0.52l-0.96,0.05l-0.36,0.41l0.04,0.88l-0.52,0.6l-0.3,0.04l-0.3,-0.55l-0.66,-0.31l0.11,-0.67l-0.48,-0.65l-0.87,-0.26l-0.73,0.2Z", "name": "Honduras"}, "PR": {"path": "M256.17,238.73l-0.26,0.27l-2.83,0.05l-0.07,-0.55l1.95,-0.1l1.22,0.33Z", "name": "Puerto Rico"}, "PS": {"path": "M509.21,203.07l0.1,-0.06l-0.02,0.03l-0.09,0.03ZM509.36,202.91l-0.02,-0.63l-0.33,-0.16l0.31,-1.09l0.24,0.1l-0.2,1.78Z", "name": "Palestine"}, "PT": {"path": "M401.84,187.38l-0.64,0.47l-1.13,-0.35l-0.91,0.17l0.28,-1.78l-0.24,-1.78l-1.25,-0.56l-0.45,-0.84l0.17,-1.66l1.01,-1.18l0.69,-2.92l-0.04,-1.39l-0.59,-1.9l1.3,-0.85l0.84,1.35l3.1,-0.3l0.46,0.99l-1.05,0.94l-0.03,2.16l-0.41,0.57l-0.08,1.1l-0.79,0.18l-0.26,0.59l0.91,1.6l-0.63,1.75l0.76,1.09l-1.1,1.52l0.07,1.05Z", "name": "Portugal"}, "PY": {"path": "M274.9,336.12l0.74,1.52l-0.16,3.45l0.32,0.41l2.64,0.5l1.11,-0.47l1.4,0.59l0.36,0.6l0.53,3.42l1.27,0.4l0.98,-0.38l0.51,0.27l-0.0,1.18l-1.21,5.32l-2.09,1.9l-1.8,0.4l-4.71,-0.98l2.2,-3.63l-0.32,-1.5l-2.78,-1.28l-3.03,-1.94l-2.07,-0.44l-4.34,-4.06l0.91,-2.9l0.08,-1.42l1.07,-2.04l4.13,-0.72l2.18,0.03l2.05,1.17l0.03,0.59Z", "name": "Paraguay"}, "PA": {"path": "M213.8,263.68l0.26,-1.52l-0.36,-0.26l-0.01,-0.49l0.44,-0.1l0.93,1.4l1.26,0.03l0.77,0.49l1.38,-0.23l2.51,-1.11l0.86,-0.72l3.45,0.85l1.4,1.18l0.41,1.74l-0.21,0.34l-0.53,-0.12l-0.47,0.29l-0.16,0.6l-0.68,-1.28l0.45,-0.49l-0.19,-0.66l-0.47,-0.13l-0.54,-0.84l-1.5,-0.75l-1.1,0.16l-0.75,0.99l-1.62,0.84l-0.18,0.96l0.85,0.97l-0.58,0.45l-0.69,0.08l-0.34,-1.18l-1.27,0.03l-0.71,-1.05l-2.59,-0.46Z", "name": "Panama"}, "PG": {"path": "M808.58,298.86l2.54,2.56l-0.13,0.26l-0.33,0.12l-0.87,-0.78l-1.22,-2.16ZM801.41,293.04l0.5,0.29l0.26,0.27l-0.49,-0.35l-0.27,-0.21ZM803.17,294.58l0.59,0.5l0.08,1.06l-0.29,-0.91l-0.38,-0.65ZM796.68,298.41l0.52,0.75l1.43,-0.19l2.27,-1.81l-0.01,-1.43l1.12,0.16l-0.04,1.1l-0.7,1.28l-1.12,0.18l-0.62,0.79l-2.46,1.11l-1.17,-0.0l-3.08,-1.25l3.41,0.0l0.45,-0.68ZM789.15,303.55l2.31,1.8l1.59,2.61l1.34,0.13l-0.06,0.66l0.31,0.43l1.06,0.24l0.06,0.65l2.25,1.05l-1.22,0.13l-0.72,-0.63l-4.56,-0.65l-3.22,-2.87l-1.49,-2.34l-3.27,-1.1l-2.38,0.72l-1.59,0.86l-0.2,0.42l0.27,1.55l-1.55,0.68l-1.36,-0.4l-2.21,-0.09l-0.08,-15.41l8.39,2.93l2.95,2.4l0.6,1.64l4.02,1.49l0.31,0.68l-1.76,0.21l-0.33,0.52l0.55,1.68Z", "name": "Papua New Guinea"}, "PE": {"path": "M244.96,295.21l-1.26,-0.07l-0.57,0.42l-1.93,0.45l-2.98,1.75l-0.36,1.36l-0.58,0.8l0.12,1.37l-1.24,0.59l-0.22,1.22l-0.62,0.84l1.04,2.27l1.28,1.44l-0.41,0.84l0.32,0.57l1.48,0.13l1.16,1.37l2.21,0.07l1.63,-1.08l-0.13,3.02l0.3,0.4l1.14,0.29l1.31,-0.34l1.9,3.59l-0.48,0.85l-0.17,3.85l-0.94,1.59l0.35,0.75l-0.47,1.07l0.98,1.97l-2.1,3.82l-0.98,0.5l-2.17,-1.28l-0.39,-1.16l-4.95,-2.58l-4.46,-2.79l-1.84,-1.51l-0.91,-1.84l0.3,-0.96l-2.11,-3.33l-4.82,-9.68l-1.04,-1.2l-0.87,-1.94l-3.4,-2.48l0.58,-1.18l-1.13,-2.23l0.66,-1.49l1.45,-1.15l-0.6,0.98l0.07,0.92l0.47,0.36l1.74,0.03l0.97,1.17l0.54,0.07l1.42,-1.03l0.6,-1.84l1.42,-2.02l3.04,-1.04l2.73,-2.62l0.86,-1.74l-0.1,-1.87l1.44,1.02l0.9,1.25l1.06,0.59l1.7,2.73l1.86,0.31l1.45,-0.61l0.96,0.39l1.36,-0.19l1.45,0.89l-1.4,2.21l0.31,0.61l0.59,0.05l0.47,0.5Z", "name": "Peru"}, "PK": {"path": "M615.09,192.34l-1.83,1.81l-2.6,0.39l-3.73,-0.68l-1.58,1.33l-0.09,0.42l1.77,4.39l1.7,1.23l-1.69,1.27l-0.12,2.14l-2.33,2.64l-1.6,2.8l-2.46,2.67l-3.03,-0.07l-2.76,2.83l0.05,0.6l1.5,1.11l0.26,1.9l1.44,1.5l0.37,1.68l-5.01,-0.01l-1.78,1.7l-1.42,-0.52l-0.76,-1.87l-2.27,-2.15l-11.61,0.86l0.71,-2.34l3.43,-1.32l0.25,-0.44l-0.21,-1.24l-1.2,-0.65l-0.28,-2.46l-2.29,-1.14l-1.28,-1.94l2.82,0.94l2.62,-0.38l1.42,0.33l0.76,-0.56l1.71,0.19l3.25,-1.14l0.27,-0.36l0.08,-2.19l1.18,-1.32l1.68,0.0l0.58,-0.82l1.6,-0.3l1.19,0.16l0.98,-0.78l0.02,-1.88l0.93,-1.47l1.48,-0.66l0.19,-0.55l-0.66,-1.25l2.04,-0.11l0.69,-1.01l-0.02,-1.16l1.11,-1.06l-0.17,-1.78l-0.49,-1.03l1.15,-0.98l5.42,-0.91l2.6,-0.82l1.6,1.16l0.97,2.34l3.45,0.97Z", "name": "Pakistan"}, "PH": {"path": "M737.01,263.84l0.39,2.97l-0.44,1.18l-0.55,-1.53l-0.67,-0.14l-1.17,1.28l0.65,2.09l-0.42,0.69l-2.48,-1.23l-0.57,-1.49l0.65,-1.03l-0.1,-0.54l-1.59,-1.19l-0.56,0.08l-0.65,0.87l-1.23,0.0l-1.58,0.97l0.83,-1.8l2.56,-1.42l0.65,0.84l0.45,0.13l1.9,-0.69l0.56,-1.11l1.5,-0.06l0.38,-0.43l-0.09,-1.19l1.21,0.71l0.36,2.02ZM733.59,256.58l0.05,0.75l0.08,0.26l-0.8,-0.42l-0.18,-0.71l0.85,0.12ZM734.08,256.1l-0.12,-1.12l-1.0,-1.27l1.36,0.03l0.53,0.73l0.51,2.04l-1.27,-0.4ZM733.76,257.68l0.38,0.98l-0.32,0.15l-0.07,-1.13ZM724.65,238.43l1.46,0.7l0.72,-0.31l-0.32,1.17l0.79,1.71l-0.57,1.84l-1.53,1.04l-0.39,2.25l0.56,2.04l1.63,0.57l1.16,-0.27l2.71,1.23l-0.19,1.08l0.76,0.84l-0.08,0.36l-1.4,-0.9l-0.88,-1.27l-0.66,0.0l-0.38,0.55l-1.6,-1.31l-2.15,0.36l-0.87,-0.39l0.07,-0.61l0.66,-0.55l-0.01,-0.62l-0.75,-0.59l-0.72,0.44l-0.74,-0.87l-0.39,-2.49l0.32,0.27l0.66,-0.28l0.26,-3.97l0.7,-2.02l1.14,0.0ZM731.03,258.87l-0.88,0.85l-1.19,1.94l-1.05,-1.19l0.93,-1.1l0.32,-1.47l0.52,-0.06l-0.27,1.15l0.22,0.45l0.49,-0.12l1.0,-1.32l-0.08,0.85ZM726.83,255.78l0.83,0.38l1.17,-0.0l-0.02,0.48l-2.0,1.4l0.03,-2.26ZM724.81,252.09l-0.38,1.27l-1.42,-1.95l1.2,0.05l0.6,0.63ZM716.55,261.82l1.1,-0.95l0.03,-0.03l-0.28,0.36l-0.85,0.61ZM719.22,259.06l0.04,-0.06l0.8,-1.53l0.16,0.75l-1.0,0.84Z", "name": "Philippines"}, "PL": {"path": "M468.44,149.42l-1.11,-1.54l-1.86,-0.33l-0.48,-1.05l-1.72,-0.37l-0.65,0.69l-0.72,-0.36l0.11,-0.61l-0.33,-0.46l-1.75,-0.27l-1.04,-0.93l-0.94,-1.94l0.16,-1.22l-0.62,-1.8l-0.78,-1.07l0.57,-1.04l-0.48,-1.43l1.41,-0.83l6.91,-2.71l2.14,0.5l0.52,0.91l5.51,0.44l4.55,-0.05l1.07,0.31l0.48,0.84l0.15,1.58l0.65,1.2l-0.01,0.99l-1.27,0.58l-0.19,0.54l0.73,1.48l0.08,1.55l1.2,2.76l-0.17,0.58l-1.23,0.44l-2.27,2.72l0.18,0.95l-1.97,-1.03l-1.98,0.4l-1.36,-0.28l-1.24,0.58l-1.07,-0.97l-1.16,0.24Z", "name": "Poland"}, "ZM": {"path": "M481.47,313.3l0.39,0.31l2.52,0.14l0.99,1.17l2.01,0.35l1.4,-0.64l0.69,1.17l1.78,0.33l1.84,2.35l2.23,0.18l0.4,-0.43l-0.21,-2.74l-0.62,-0.3l-0.48,0.32l-1.98,-1.17l0.72,-5.29l-0.51,-1.18l0.57,-1.3l3.68,-0.62l0.26,0.63l1.21,0.63l0.9,-0.22l2.16,0.67l1.33,0.71l1.07,1.02l0.56,1.87l-0.88,2.7l0.43,2.09l-0.73,0.87l-0.76,2.37l0.59,0.68l-6.6,1.83l-0.29,0.44l0.19,1.45l-1.68,0.35l-1.43,1.02l-0.38,0.87l-0.87,0.26l-3.48,3.69l-4.16,-0.53l-1.52,-1.0l-1.77,-0.13l-1.83,0.52l-3.04,-3.4l0.11,-7.59l4.82,0.03l0.39,-0.49l-0.18,-0.76l0.33,-0.83l-0.4,-1.36l0.24,-1.05Z", "name": "Zambia"}, "EH": {"path": "M384.42,230.28l0.25,-0.79l1.06,-1.29l0.8,-3.51l3.38,-2.78l0.7,-1.81l0.06,4.84l-1.98,0.2l-0.94,1.59l0.39,3.56l-3.7,-0.01ZM392.01,218.1l0.7,-1.8l1.77,-0.24l2.09,0.34l0.95,-0.62l1.28,-0.07l-0.0,2.51l-6.79,-0.12Z", "name": "W. Sahara"}, "EE": {"path": "M485.71,115.04l2.64,0.6l2.56,0.11l-1.6,1.91l0.61,3.54l-0.81,0.87l-1.78,-0.01l-3.22,-1.76l-1.8,0.45l0.21,-1.53l-0.58,-0.41l-0.69,0.34l-1.26,-1.03l-0.17,-1.63l2.83,-0.92l3.05,-0.52Z", "name": "Estonia"}, "EG": {"path": "M492.06,205.03l1.46,0.42l2.95,-1.64l2.04,-0.21l1.53,0.3l0.59,1.19l0.69,0.04l0.41,-0.64l1.81,0.58l1.95,0.16l1.04,-0.51l1.42,4.08l-2.03,4.54l-1.66,-1.77l-1.76,-3.85l-0.64,-0.12l-0.36,0.67l1.04,2.88l3.44,6.95l1.78,3.04l2.03,2.65l-0.36,0.53l0.23,2.01l2.7,2.19l-28.41,0.0l0.0,-18.96l-0.73,-2.2l0.59,-1.56l-0.32,-1.26l0.68,-0.99l3.06,-0.04l4.82,1.52Z", "name": "Egypt"}, "ZA": {"path": "M467.14,373.21l-0.13,-1.96l-0.68,-1.56l0.7,-0.68l-0.13,-2.33l-4.56,-8.19l0.77,-0.86l0.6,0.45l0.69,1.31l2.83,0.72l1.5,-0.26l2.24,-1.39l0.19,-9.55l1.35,2.3l-0.21,1.5l0.61,1.2l0.4,0.19l1.79,-0.27l2.6,-2.07l0.69,-1.32l0.96,-0.48l2.19,1.04l2.04,0.13l1.77,-0.65l0.85,-2.12l1.38,-0.33l1.59,-2.76l2.15,-1.89l3.41,-1.87l2.0,0.45l1.02,-0.28l0.99,0.2l1.75,5.29l-0.38,3.25l-0.81,-0.23l-1.0,0.46l-0.87,1.68l-0.05,1.16l1.97,1.84l1.47,-0.29l0.69,-1.18l1.09,0.01l-0.76,3.69l-0.58,1.09l-2.2,1.79l-3.17,4.76l-2.8,2.83l-3.57,2.88l-2.53,1.05l-1.22,0.14l-0.51,0.7l-1.18,-0.32l-1.39,0.5l-2.59,-0.52l-1.61,0.33l-1.18,-0.11l-2.55,1.1l-2.1,0.44l-1.6,1.07l-0.85,0.05l-0.93,-0.89l-0.93,-0.15l-0.97,-1.13l-0.25,0.05ZM491.45,364.19l0.62,-0.93l1.48,-0.59l1.18,-2.19l-0.07,-0.49l-1.99,-1.69l-1.66,0.56l-1.43,1.14l-1.34,1.73l0.02,0.51l1.88,2.11l1.31,-0.16Z", "name": "South Africa"}, "EC": {"path": "M231.86,285.53l0.29,1.59l-0.69,1.45l-2.61,2.51l-3.13,1.11l-1.53,2.18l-0.49,1.68l-1.0,0.73l-1.02,-1.11l-1.78,-0.16l0.67,-1.15l-0.24,-0.86l1.25,-2.13l-0.54,-1.09l-0.67,-0.08l-0.72,0.87l-0.87,-0.64l0.35,-0.69l-0.36,-1.96l0.81,-0.51l0.45,-1.51l0.92,-1.57l-0.07,-0.97l2.65,-1.33l2.75,1.35l0.77,1.05l2.12,0.35l0.76,-0.32l1.96,1.21Z", "name": "Ecuador"}, "IT": {"path": "M451.59,158.63l3.48,0.94l-0.21,1.17l0.3,0.83l-1.49,-0.24l-2.04,1.1l-0.21,0.39l0.13,1.45l-0.25,1.12l0.82,1.57l2.39,1.63l1.31,2.54l2.79,2.43l2.05,0.08l0.21,0.23l-0.39,0.33l0.09,0.67l4.05,1.97l2.17,1.76l-0.16,0.36l-1.17,-1.08l-2.18,-0.49l-0.44,0.2l-1.05,1.91l0.14,0.54l1.57,0.95l-0.19,0.98l-1.06,0.33l-1.25,2.34l-0.37,0.08l0.0,-0.33l1.0,-2.45l-1.73,-3.17l-1.12,-0.51l-0.88,-1.33l-1.51,-0.51l-1.27,-1.25l-1.75,-0.18l-4.12,-3.21l-1.62,-1.65l-1.03,-3.19l-3.53,-1.36l-1.3,0.51l-1.69,1.41l0.16,-0.72l-0.28,-0.47l-1.14,-0.33l-0.53,-1.96l0.72,-0.78l0.04,-0.48l-0.65,-1.17l0.8,0.39l1.4,-0.23l1.11,-0.84l0.52,0.35l1.19,-0.1l0.75,-1.2l1.53,0.33l1.36,-0.56l0.35,-1.14l1.08,0.32l0.68,-0.64l1.98,-0.44l0.42,0.82ZM459.19,184.75l-0.65,1.65l0.32,1.05l-0.31,0.89l-1.5,-0.85l-4.5,-1.67l0.19,-0.82l2.67,0.23l3.78,-0.48ZM443.93,176.05l1.18,1.66l-0.3,3.32l-1.06,-0.01l-0.77,0.73l-0.53,-0.44l-0.1,-3.37l-0.39,-1.22l1.04,0.01l0.92,-0.68Z", "name": "Italy"}, "VN": {"path": "M690.56,230.25l-2.7,1.82l-2.09,2.46l-0.63,1.95l4.31,6.45l2.32,1.65l1.43,1.94l1.11,4.59l-0.32,4.24l-1.93,1.54l-2.84,1.61l-2.11,2.15l-2.73,2.06l-0.59,-1.05l0.63,-1.53l-0.13,-0.47l-1.34,-1.04l1.51,-0.71l2.55,-0.18l0.3,-0.63l-0.82,-1.14l4.0,-2.07l0.31,-3.05l-0.57,-1.77l0.42,-2.66l-0.73,-1.97l-1.86,-1.76l-3.63,-5.29l-2.72,-1.46l0.36,-0.47l1.5,-0.64l0.21,-0.52l-0.97,-2.27l-0.37,-0.24l-2.83,-0.02l-2.24,-3.9l0.83,-0.4l4.39,-0.29l2.06,-1.31l1.15,0.89l1.88,0.4l-0.17,1.51l1.35,1.16l1.67,0.45Z", "name": "Vietnam"}, "SB": {"path": "M826.69,311.6l-0.61,0.09l-0.2,-0.33l0.37,0.15l0.44,0.09ZM824.18,307.38l-0.26,-0.3l-0.31,-0.91l0.03,0.0l0.54,1.21ZM823.04,309.33l-1.66,-0.22l-0.2,-0.52l1.16,0.28l0.69,0.46ZM819.28,304.68l1.14,0.65l0.02,0.03l-0.81,-0.44l-0.35,-0.23Z", "name": "Solomon Is."}, "ET": {"path": "M516.04,247.79l1.1,0.84l1.63,-0.45l0.68,0.47l1.63,0.03l2.01,0.94l1.73,1.66l1.64,2.07l-1.52,2.04l0.16,1.72l0.39,0.38l2.05,0.0l-0.36,1.03l2.86,3.58l8.32,3.08l1.31,0.02l-6.32,6.75l-3.1,0.11l-2.36,1.77l-1.47,0.04l-0.86,0.79l-1.38,-0.0l-1.32,-0.81l-2.29,1.05l-0.76,0.98l-3.29,-0.41l-3.07,-2.07l-1.8,-0.07l-0.62,-0.6l0.0,-1.24l-0.28,-0.38l-1.15,-0.37l-1.4,-2.59l-1.19,-0.68l-0.47,-1.0l-1.27,-1.23l-1.16,-0.22l0.43,-0.72l1.45,-0.28l0.41,-0.95l-0.03,-2.21l0.68,-2.44l1.05,-0.63l1.43,-3.06l1.57,-1.37l1.02,-2.51l0.35,-1.88l2.52,0.46l0.44,-0.24l0.58,-1.43Z", "name": "Ethiopia"}, "SO": {"path": "M525.13,288.48l-1.13,-1.57l-0.03,-8.86l2.66,-3.38l1.67,-0.13l2.13,-1.69l3.41,-0.23l7.08,-7.55l2.91,-3.69l0.08,-4.82l2.98,-0.67l1.24,-0.86l0.45,-0.0l-0.2,3.0l-1.21,3.62l-2.73,5.97l-2.13,3.65l-5.03,6.16l-8.56,6.4l-2.78,3.08l-0.8,1.56Z", "name": "Somalia"}, "ZW": {"path": "M498.91,341.09l-1.11,-0.22l-0.92,0.28l-2.09,-0.44l-1.5,-1.11l-1.89,-0.43l-0.62,-1.4l-0.01,-0.84l-0.3,-0.38l-0.97,-0.25l-2.71,-2.74l-1.92,-3.32l3.83,0.45l3.73,-3.82l1.08,-0.44l0.26,-0.77l1.25,-0.9l1.41,-0.26l0.5,0.89l1.99,-0.05l1.72,1.17l1.11,0.17l1.05,0.66l0.01,2.99l-0.59,3.76l0.38,0.86l-0.23,1.23l-0.39,0.35l-0.63,1.81l-2.43,2.75Z", "name": "Zimbabwe"}, "ES": {"path": "M416.0,169.21l1.07,1.17l4.61,1.38l1.06,-0.57l2.6,1.26l2.71,-0.3l0.09,1.12l-2.14,1.8l-3.11,0.61l-0.31,0.31l-0.2,0.89l-1.54,1.69l-0.97,2.4l0.84,1.74l-1.32,1.27l-0.48,1.68l-1.88,0.65l-1.66,2.07l-5.36,-0.01l-1.79,1.08l-0.89,0.98l-0.88,-0.17l-0.79,-0.82l-0.68,-1.59l-2.37,-0.63l-0.11,-0.5l1.21,-1.82l-0.77,-1.13l0.61,-1.68l-0.76,-1.62l0.87,-0.49l0.09,-1.25l0.42,-0.6l0.03,-2.11l0.99,-0.69l0.13,-0.5l-1.03,-1.73l-1.46,-0.11l-0.61,0.38l-1.06,0.0l-0.52,-1.23l-0.53,-0.21l-1.32,0.67l-0.01,-1.49l-0.75,-0.96l3.03,-1.88l2.99,0.53l3.32,-0.02l2.63,0.51l6.01,-0.06Z", "name": "Spain"}, "ER": {"path": "M520.38,246.23l3.42,2.43l3.5,3.77l0.84,0.54l-0.95,-0.01l-3.51,-3.89l-2.33,-1.15l-1.73,-0.07l-0.91,-0.51l-1.26,0.51l-1.34,-1.02l-0.61,0.17l-0.66,1.61l-2.35,-0.43l-0.17,-0.67l1.29,-5.29l0.61,-0.61l1.95,-0.53l0.87,-1.01l1.17,2.41l0.68,2.33l1.49,1.43Z", "name": "Eritrea"}, "ME": {"path": "M468.91,172.53l-1.22,-1.02l0.47,-1.81l0.89,-0.72l2.26,1.51l-0.5,0.57l-0.75,-0.27l-1.14,1.73Z", "name": "Montenegro"}, "MD": {"path": "M488.41,153.73l1.4,-0.27l1.72,0.93l1.07,0.15l0.85,0.65l-0.14,0.84l0.96,0.85l1.12,2.47l-1.15,-0.07l-0.66,-0.41l-0.52,0.25l-0.09,0.86l-1.08,1.89l-0.27,-0.86l0.25,-1.34l-0.16,-1.6l-3.29,-4.34Z", "name": "Moldova"}, "MG": {"path": "M545.91,319.14l0.4,3.03l0.62,1.21l-0.21,1.02l-0.57,-0.8l-0.69,-0.01l-0.47,0.76l0.41,2.12l-0.18,0.87l-0.73,0.78l-0.15,2.14l-4.71,15.2l-1.06,2.88l-3.92,1.64l-3.12,-1.49l-0.6,-1.21l-0.19,-2.4l-0.86,-2.05l-0.21,-1.77l0.38,-1.62l1.21,-0.75l0.01,-0.76l1.19,-2.04l0.23,-1.66l-1.06,-2.99l-0.19,-2.21l0.81,-1.33l0.32,-1.46l4.63,-1.22l3.44,-3.0l0.85,-1.4l-0.08,-0.7l0.78,-0.04l1.38,-1.77l0.13,-1.64l0.45,-0.61l1.16,1.69l0.59,1.6Z", "name": "Madagascar"}, "MA": {"path": "M378.78,230.02l0.06,-0.59l0.92,-0.73l0.82,-1.37l-0.09,-1.04l0.79,-1.7l1.31,-1.58l0.96,-0.59l0.66,-1.55l0.09,-1.47l0.81,-1.48l1.72,-1.07l1.55,-2.69l1.16,-0.96l2.44,-0.39l1.94,-1.82l1.31,-0.78l2.09,-2.28l-0.51,-3.65l1.24,-3.7l1.5,-1.75l4.46,-2.57l2.37,-4.47l1.44,0.01l1.68,1.21l2.32,-0.19l3.47,0.65l0.8,1.54l0.16,1.71l0.86,2.96l0.56,0.59l-0.26,0.61l-3.05,0.44l-1.26,1.05l-1.33,0.22l-0.33,0.37l-0.09,1.78l-2.68,1.0l-1.07,1.42l-4.47,1.13l-4.04,2.01l-0.54,4.64l-1.15,0.06l-0.92,0.61l-1.96,-0.35l-2.42,0.54l-0.74,1.9l-0.86,0.4l-1.14,3.26l-3.53,3.01l-0.8,3.55l-0.96,1.1l-0.29,0.82l-4.95,0.18Z", "name": "Morocco"}, "UZ": {"path": "M598.64,172.75l-1.63,1.52l0.06,0.64l1.85,1.12l1.97,-0.64l2.21,1.17l-2.52,1.68l-2.59,-0.22l-0.18,-0.41l0.46,-1.23l-0.45,-0.53l-3.35,0.69l-2.1,3.51l-1.87,-0.12l-1.03,1.51l0.22,0.55l1.64,0.62l0.46,1.83l-1.19,2.49l-2.66,-0.53l0.05,-1.36l-0.26,-0.39l-3.3,-1.23l-2.56,-1.4l-4.4,-3.34l-1.34,-3.14l-1.08,-0.6l-2.58,0.13l-0.69,-0.44l-0.47,-2.52l-3.37,-1.6l-0.43,0.05l-2.07,1.72l-2.1,1.01l-0.21,0.47l0.28,1.01l-1.91,0.03l-0.09,-10.5l5.99,-1.7l6.19,3.54l2.71,2.84l7.05,-0.67l2.71,2.01l-0.17,2.81l0.39,0.42l0.9,0.02l0.44,2.14l0.38,0.32l2.94,0.09l0.95,1.42l1.28,-0.24l1.05,-2.04l4.43,-2.5Z", "name": "Uzbekistan"}, "MM": {"path": "M673.9,230.21l-1.97,1.57l-0.57,0.96l-1.4,0.6l-1.36,1.05l-1.99,0.36l-1.08,2.66l-0.91,0.4l-0.19,0.55l1.21,2.27l2.52,3.43l-0.79,1.91l-0.74,0.41l-0.17,0.52l0.65,1.37l1.61,1.95l0.25,2.58l0.9,2.13l-1.92,3.57l0.68,-2.25l-0.81,-1.74l0.19,-2.65l-1.05,-1.53l-1.24,-6.17l-1.12,-2.26l-0.6,-0.13l-4.34,3.02l-2.39,-0.65l0.77,-2.84l-0.52,-2.61l-1.91,-2.96l0.25,-0.75l-0.29,-0.51l-1.33,-0.3l-1.61,-1.93l-0.1,-1.3l0.82,-0.24l0.04,-1.64l1.02,-0.52l0.21,-0.45l-0.23,-0.95l0.54,-0.96l0.08,-2.22l1.46,0.45l0.47,-0.2l1.12,-2.19l0.16,-1.35l1.33,-2.16l-0.0,-1.52l2.89,-1.66l1.63,0.44l0.5,-0.44l-0.17,-1.4l0.64,-0.36l0.08,-1.04l0.77,-0.11l0.71,1.35l1.06,0.69l-0.03,3.86l-2.38,2.37l-0.3,3.15l0.46,0.43l2.28,-0.38l0.51,2.08l1.47,0.67l-0.6,1.8l0.19,0.48l2.97,1.48l1.64,-0.55l0.02,0.32Z", "name": "Myanmar"}, "ML": {"path": "M392.61,254.08l-0.19,-2.37l-0.99,-0.87l-0.44,-1.3l-0.09,-1.28l0.81,-0.58l0.35,-1.24l2.37,0.65l1.31,-0.47l0.86,0.15l0.66,-0.56l9.83,-0.04l0.38,-0.28l0.56,-1.8l-0.44,-0.65l-2.35,-21.95l3.27,-0.04l16.7,11.38l0.74,1.31l2.5,1.09l0.02,1.38l0.44,0.39l2.34,-0.21l0.01,5.38l-1.28,1.61l-0.26,1.49l-5.31,0.57l-1.07,0.92l-2.9,0.1l-0.86,-0.48l-1.38,0.36l-2.4,1.08l-0.6,0.87l-1.85,1.09l-0.43,0.7l-0.79,0.39l-1.44,-0.21l-0.81,0.84l-0.34,1.64l-1.91,2.02l-0.06,1.03l-0.67,1.22l0.13,1.16l-0.97,0.39l-0.23,-0.64l-0.52,-0.24l-1.35,0.4l-0.34,0.55l-2.69,-0.28l-0.37,-0.35l-0.02,-0.9l-0.65,-0.35l0.45,-0.64l-0.03,-0.53l-2.12,-2.44l-0.76,-0.01l-2.0,1.16l-0.78,-0.15l-0.8,-0.67l-1.21,0.23Z", "name": "Mali"}, "MN": {"path": "M676.61,146.48l3.81,1.68l5.67,-1.0l2.37,0.41l2.34,1.5l1.79,1.75l2.29,-0.03l3.12,0.52l2.47,-0.81l3.41,-0.59l3.53,-2.21l1.25,0.29l1.53,1.13l2.27,-0.21l-2.66,5.01l0.64,1.68l0.47,0.21l1.32,-0.38l2.38,0.48l2.02,-1.11l1.76,0.89l2.06,2.02l-0.13,0.53l-1.72,-0.29l-3.77,0.46l-1.88,0.99l-1.76,1.99l-3.71,1.17l-2.45,1.6l-3.83,-0.87l-0.41,0.17l-1.31,1.99l1.04,2.24l-1.52,0.9l-1.74,1.57l-2.79,1.02l-3.78,0.13l-4.05,1.05l-2.77,1.52l-1.16,-0.85l-2.94,0.0l-3.62,-1.79l-2.58,-0.49l-3.4,0.41l-5.12,-0.67l-2.63,0.06l-1.31,-1.6l-1.4,-3.0l-1.48,-0.33l-3.13,-1.94l-6.16,-0.93l-0.71,-1.06l0.86,-3.82l-1.93,-2.71l-3.5,-1.18l-1.95,-1.58l-0.5,-1.72l2.34,-0.52l4.75,-2.8l3.62,-1.47l2.18,0.97l2.46,0.05l1.81,1.53l2.46,0.12l3.95,0.71l2.43,-2.28l0.08,-0.48l-0.9,-1.72l2.24,-2.98l2.62,1.27l4.94,1.17l0.43,2.24Z", "name": "Mongolia"}, "MK": {"path": "M472.8,173.98l0.49,-0.71l3.57,-0.71l1.0,0.77l0.13,1.45l-0.65,0.53l-1.15,-0.05l-1.12,0.67l-1.39,0.22l-0.79,-0.55l-0.29,-1.03l0.19,-0.6Z", "name": "Macedonia"}, "MW": {"path": "M505.5,309.31l0.85,1.95l0.15,2.86l-0.69,1.65l0.71,1.8l0.06,1.28l0.49,0.64l0.07,1.06l0.4,0.55l0.8,-0.23l0.55,0.61l0.69,-0.21l0.34,0.6l0.19,2.94l-1.04,0.62l-0.54,1.25l-1.11,-1.08l-0.16,-1.56l0.51,-1.31l-0.32,-1.3l-0.99,-0.65l-0.82,0.12l-2.36,-1.64l0.63,-1.96l0.82,-1.18l-0.46,-2.01l0.9,-2.86l-0.94,-2.51l0.96,0.18l0.29,0.4Z", "name": "Malawi"}, "MR": {"path": "M407.36,220.66l-2.58,0.03l-0.39,0.44l2.42,22.56l0.36,0.43l-0.39,1.24l-9.75,0.04l-0.56,0.53l-0.91,-0.11l-1.27,0.45l-1.61,-0.66l-0.97,0.03l-0.36,0.29l-0.38,1.35l-0.42,0.23l-2.93,-3.4l-2.96,-1.52l-1.62,-0.03l-1.27,0.54l-1.12,-0.2l-0.65,0.4l-0.08,-0.49l0.68,-1.29l0.31,-2.43l-0.57,-3.91l0.23,-1.21l-0.69,-1.5l-1.15,-1.02l0.25,-0.39l9.58,0.02l0.4,-0.45l-0.46,-3.68l0.47,-1.04l2.12,-0.21l0.36,-0.4l-0.08,-6.4l7.81,0.13l0.41,-0.4l0.01,-3.31l7.76,5.35Z", "name": "Mauritania"}, "UG": {"path": "M498.55,276.32l0.7,-0.46l1.65,0.5l1.96,-0.57l1.7,0.01l1.45,-0.98l0.91,1.33l1.33,3.95l-2.57,4.03l-1.46,-0.4l-2.54,0.91l-1.37,1.61l-0.01,0.81l-2.42,-0.01l-2.26,1.01l-0.17,-1.59l0.58,-1.04l0.14,-1.94l1.37,-2.28l1.78,-1.58l-0.17,-0.65l-0.72,-0.24l0.13,-2.43Z", "name": "Uganda"}, "MY": {"path": "M717.47,273.46l-1.39,0.65l-2.12,-0.41l-2.88,-0.0l-0.38,0.28l-0.84,2.75l-0.99,0.96l-1.21,3.29l-1.73,0.45l-2.45,-0.68l-1.39,0.31l-1.33,1.15l-1.59,-0.14l-1.41,0.44l-1.44,-1.19l-0.18,-0.73l1.34,0.53l1.93,-0.47l0.75,-2.22l4.02,-1.03l2.75,-3.21l0.82,0.94l0.64,-0.05l0.4,-0.65l0.96,0.06l0.42,-0.36l0.24,-2.68l1.81,-1.64l1.21,-1.86l0.63,-0.01l1.07,1.05l0.34,1.28l3.44,1.35l-0.06,0.35l-1.37,0.1l-0.35,0.54l0.32,0.88ZM673.68,269.59l0.17,1.09l0.47,0.33l1.65,-0.3l0.87,-0.94l1.61,1.52l0.98,1.56l-0.12,2.81l0.41,2.29l0.95,0.9l0.88,2.44l-1.27,0.12l-5.1,-3.67l-0.34,-1.29l-1.37,-1.59l-0.33,-1.97l-0.88,-1.4l0.25,-1.68l-0.46,-1.05l1.63,0.84Z", "name": "Malaysia"}, "MX": {"path": "M133.12,200.41l0.2,0.47l9.63,3.33l6.96,-0.02l0.4,-0.4l0.0,-0.74l3.77,0.0l3.55,2.93l1.39,2.83l1.52,1.04l2.08,0.82l0.47,-0.14l1.46,-2.0l1.73,-0.04l1.59,0.98l2.05,3.35l1.47,1.56l1.26,3.14l2.18,1.02l2.26,0.58l-1.18,3.72l-0.42,5.04l1.79,4.89l1.62,1.89l0.61,1.52l1.2,1.42l2.55,0.66l1.37,1.1l7.54,-1.89l1.86,-1.3l1.14,-4.3l4.1,-1.21l3.57,-0.11l0.32,0.3l-0.06,0.94l-1.26,1.45l-0.67,1.71l0.38,0.7l-0.72,2.27l-0.49,-0.3l-1.0,0.08l-1.0,1.39l-0.47,-0.11l-0.53,0.47l-4.26,-0.02l-0.4,0.4l-0.0,1.06l-1.1,0.26l0.1,0.44l1.82,1.44l0.56,0.91l-3.19,0.21l-1.21,2.09l0.24,0.72l-0.2,0.44l-2.24,-2.18l-1.45,-0.93l-2.22,-0.69l-1.52,0.22l-3.07,1.16l-10.55,-3.85l-2.86,-1.96l-3.78,-0.92l-1.08,-1.19l-2.62,-1.43l-1.18,-1.54l-0.38,-0.81l0.66,-0.63l-0.18,-0.53l0.52,-0.76l0.01,-0.91l-2.0,-3.82l-2.21,-2.63l-2.53,-2.09l-1.19,-1.62l-2.2,-1.17l-0.3,-0.43l0.34,-1.48l-0.21,-0.45l-1.23,-0.6l-1.36,-1.2l-0.59,-1.78l-1.54,-0.47l-2.44,-2.55l-0.16,-0.9l-1.33,-2.03l-0.84,-1.99l-0.16,-1.33l-1.81,-1.1l-0.97,0.05l-1.31,-0.7l-0.57,0.22l-0.4,1.12l0.72,3.77l3.51,3.89l0.28,0.78l0.53,0.26l0.41,1.43l1.33,1.73l1.58,1.41l0.8,2.39l1.43,2.41l0.13,1.32l0.37,0.36l1.04,0.08l1.67,2.28l-0.85,0.76l-0.66,-1.51l-1.68,-1.54l-2.91,-1.87l0.06,-1.82l-0.54,-1.68l-2.91,-2.03l-0.55,0.09l-1.95,-1.1l-0.88,-0.94l0.68,-0.08l0.93,-1.01l0.08,-1.78l-1.93,-1.94l-1.46,-0.77l-3.75,-7.56l4.88,-0.42Z", "name": "Mexico"}, "IL": {"path": "M507.76,203.05l0.4,-0.78l0.18,0.4l-0.33,1.03l0.52,0.44l0.68,-0.22l-0.86,3.6l-1.16,-3.32l0.59,-0.74l-0.03,-0.41ZM508.73,200.34l0.37,-1.02l0.64,0.0l0.52,-0.51l-0.49,1.53l-0.56,-0.24l-0.48,0.23Z", "name": "Israel"}, "FR": {"path": "M444.48,172.62l-0.64,1.78l-0.58,-0.31l-0.49,-1.72l0.4,-0.89l1.0,-0.72l0.3,1.85ZM429.64,147.1l1.78,1.58l1.46,-0.13l2.1,1.42l1.35,0.27l1.23,0.83l3.04,0.5l-1.03,1.85l-0.3,2.12l-0.41,0.32l-0.95,-0.24l-0.5,0.43l0.06,0.61l-1.81,1.92l-0.04,1.42l0.55,0.38l0.88,-0.36l0.61,0.97l-0.03,1.0l0.57,0.91l-0.75,1.09l0.65,2.39l1.27,0.57l-0.18,0.82l-2.01,1.53l-4.77,-0.8l-3.82,1.0l-0.53,1.85l-2.49,0.34l-2.71,-1.31l-1.16,0.57l-4.31,-1.29l-0.72,-0.86l1.19,-1.78l0.39,-6.45l-2.58,-3.3l-1.9,-1.66l-3.72,-1.23l-0.19,-1.72l2.81,-0.61l4.12,0.81l0.47,-0.48l-0.6,-2.77l1.94,0.95l5.83,-2.54l0.92,-2.74l1.6,-0.49l0.24,0.78l1.36,0.33l1.05,1.19ZM289.01,278.39l-0.81,0.8l-0.78,0.12l-0.5,-0.66l-0.56,-0.1l-0.91,0.6l-0.46,-0.22l1.09,-2.96l-0.96,-1.77l-0.17,-1.49l1.07,-1.77l2.32,0.75l2.51,2.01l0.3,0.74l-2.14,3.96Z", "name": "France"}, "XS": {"path": "M531.15,258.94l1.51,0.12l5.13,-0.95l5.3,-1.48l-0.01,4.4l-2.67,3.39l-1.85,0.01l-8.04,-2.94l-2.55,-3.17l1.12,-1.71l2.04,2.34Z", "name": "Somaliland"}, "FI": {"path": "M492.17,76.39l-0.23,3.5l3.52,2.63l-2.08,2.88l-0.02,0.44l2.8,4.56l-1.59,3.31l2.16,3.24l-0.94,2.39l0.14,0.47l3.44,2.51l-0.77,1.62l-7.52,6.95l-4.5,0.31l-4.38,1.37l-3.8,0.74l-1.44,-1.96l-2.17,-1.11l0.5,-3.66l-1.16,-3.33l1.09,-2.08l2.21,-2.42l5.67,-4.32l1.64,-0.83l0.21,-0.42l-0.46,-2.02l-3.38,-1.89l-0.75,-1.43l-0.22,-6.74l-6.79,-4.8l0.8,-0.62l2.54,2.12l3.46,-0.12l3.0,0.96l2.51,-2.11l1.17,-3.08l3.55,-1.38l2.76,1.53l-0.95,2.79Z", "name": "Finland"}, "FJ": {"path": "M869.95,326.98l-1.21,0.41l-0.08,-0.23l2.97,-1.21l-0.14,0.42l-1.54,0.61ZM867.58,329.25l0.43,0.37l-0.27,0.88l-1.24,0.28l-1.04,-0.24l-0.14,-0.66l0.63,-0.58l0.92,0.26l0.7,-0.31Z", "name": "Fiji"}, "FK": {"path": "M274.36,425.85l1.44,1.08l-0.47,0.73l-3.0,0.89l-0.96,-1.0l-0.52,-0.05l-1.83,1.29l-0.73,-0.88l2.46,-1.64l1.93,0.76l1.67,-1.19Z", "name": "Falkland Is."}, "NI": {"path": "M202.33,252.67l0.81,-0.18l1.03,-1.02l-0.04,-0.88l0.68,-0.0l0.63,-0.54l0.97,0.22l1.53,-1.26l0.58,-0.99l1.17,0.34l2.41,-0.94l0.13,1.32l-0.81,1.94l0.1,2.74l-0.36,0.37l-0.11,1.75l-0.47,0.81l0.18,1.14l-1.73,-0.85l-0.71,0.27l-1.47,-0.6l-0.52,0.16l-4.01,-3.81Z", "name": "Nicaragua"}, "NL": {"path": "M430.31,143.39l0.6,-0.5l2.13,-4.8l3.2,-1.33l1.74,0.08l0.33,0.8l-0.59,2.92l-0.5,0.99l-1.26,0.0l-0.4,0.45l0.33,2.7l-2.2,-1.78l-2.62,0.58l-0.75,-0.11Z", "name": "Netherlands"}, "NO": {"path": "M491.44,67.41l6.8,2.89l-2.29,0.86l-0.15,0.65l2.33,2.38l-4.98,1.79l0.84,-2.45l-0.18,-0.48l-3.55,-1.8l-3.89,1.52l-1.42,3.38l-2.12,1.72l-2.64,-1.0l-3.11,0.21l-2.66,-2.22l-0.5,-0.01l-1.41,1.1l-1.44,0.17l-0.35,0.35l-0.32,2.47l-4.32,-0.64l-0.44,0.29l-0.58,2.11l-2.45,0.2l-4.15,7.68l-3.88,5.76l0.78,1.62l-0.64,1.16l-2.24,-0.06l-0.38,0.24l-1.66,3.89l0.15,5.17l1.57,2.04l-0.78,4.16l-2.02,2.48l-0.85,1.63l-1.3,-1.75l-0.58,-0.07l-4.87,4.19l-3.1,0.79l-3.16,-1.7l-0.85,-3.77l-0.77,-8.55l2.14,-2.31l6.55,-3.27l5.02,-4.17l10.63,-13.84l10.98,-8.7l5.35,-1.91l4.34,0.12l3.69,-3.64l4.49,0.19l4.37,-0.89ZM484.55,20.04l4.26,1.75l-3.1,2.55l-7.1,0.65l-7.08,-0.9l-0.37,-1.31l-0.37,-0.29l-3.44,-0.1l-2.08,-2.0l6.87,-1.44l3.9,1.31l2.39,-1.64l6.13,1.4ZM481.69,33.93l-4.45,1.74l-3.54,-0.99l1.12,-0.9l0.05,-0.58l-1.06,-1.22l4.22,-0.89l1.09,1.97l2.57,0.87ZM466.44,24.04l7.43,3.77l-5.41,1.86l-1.58,4.08l-2.26,1.2l-1.12,4.11l-2.61,0.18l-4.79,-2.86l1.84,-1.54l-0.1,-0.68l-3.69,-1.53l-4.77,-4.51l-1.73,-3.89l6.11,-1.82l1.54,1.92l3.57,-0.08l1.2,-1.96l3.32,-0.18l3.05,1.92Z", "name": "Norway"}, "NA": {"path": "M474.26,330.66l-0.97,0.04l-0.38,0.4l-0.07,8.9l-2.09,0.08l-0.39,0.4l-0.0,17.42l-1.98,1.23l-1.17,0.17l-2.44,-0.66l-0.48,-1.13l-0.99,-0.74l-0.54,0.05l-0.9,1.01l-1.53,-1.68l-0.93,-1.88l-1.99,-8.56l-0.06,-3.12l-0.33,-1.52l-2.3,-3.34l-1.91,-4.83l-1.96,-2.43l-0.12,-1.57l2.33,-0.79l1.43,0.07l1.81,1.13l10.23,-0.25l1.84,1.23l5.87,0.35ZM474.66,330.64l6.51,-1.6l1.9,0.39l-1.69,0.4l-1.31,0.83l-1.12,-0.94l-4.29,0.92Z", "name": "Namibia"}, "VU": {"path": "M839.04,322.8l0.22,1.14l-0.44,0.03l-0.2,-1.45l0.42,0.27Z", "name": "Vanuatu"}, "NC": {"path": "M838.78,341.24l-0.33,0.22l-2.9,-1.75l-3.26,-3.37l1.65,0.83l4.85,4.07Z", "name": "New Caledonia"}, "NE": {"path": "M454.75,226.53l1.33,1.37l0.48,0.07l1.27,-0.7l0.53,3.52l0.94,0.83l0.17,0.92l0.81,0.69l-0.44,0.95l-0.96,5.26l-0.13,3.22l-3.04,2.31l-1.22,3.57l1.02,1.24l-0.0,1.46l0.39,0.4l1.13,0.04l-0.9,1.25l-1.47,-2.42l-0.86,-0.29l-2.09,1.37l-1.74,-0.67l-1.45,-0.17l-0.85,0.35l-1.36,-0.07l-1.64,1.09l-1.06,0.05l-2.94,-1.28l-1.44,0.59l-1.01,-0.03l-0.97,-0.94l-2.7,-0.98l-2.69,0.3l-0.87,0.64l-0.47,1.6l-0.75,1.16l-0.12,1.53l-1.57,-1.1l-1.31,0.24l0.03,-0.81l-0.32,-0.41l-2.59,-0.52l-0.15,-1.16l-1.35,-1.6l-0.29,-1.0l0.13,-0.84l1.29,-0.08l1.08,-0.92l3.31,-0.22l2.22,-0.41l0.32,-0.34l0.2,-1.47l1.39,-1.88l-0.01,-5.66l3.36,-1.12l7.24,-5.12l8.42,-4.92l3.69,1.06Z", "name": "Niger"}, "NG": {"path": "M456.32,253.89l0.64,0.65l-0.28,1.04l-2.11,2.01l-2.03,5.18l-1.37,1.16l-1.15,3.18l-1.33,0.66l-1.46,-0.97l-1.21,0.16l-1.38,1.36l-0.91,0.24l-1.79,4.06l-2.33,0.81l-1.11,-0.07l-0.86,0.5l-1.71,-0.05l-1.19,-1.39l-0.89,-1.89l-1.77,-1.66l-3.95,-0.08l0.07,-5.21l0.42,-1.43l1.95,-2.3l-0.14,-0.91l0.43,-1.18l-0.53,-1.41l0.25,-2.92l0.72,-1.07l0.32,-1.34l0.46,-0.39l2.47,-0.28l2.34,0.89l1.15,1.02l1.28,0.04l1.22,-0.58l3.03,1.27l1.49,-0.14l1.36,-1.0l1.33,0.07l0.82,-0.35l3.45,0.8l1.82,-1.32l1.84,2.67l0.66,0.16Z", "name": "Nigeria"}, "NZ": {"path": "M857.8,379.65l1.86,3.12l0.44,0.18l0.3,-0.38l0.03,-1.23l0.38,0.27l0.57,2.31l2.02,0.94l1.81,0.27l1.57,-1.06l0.7,0.18l-1.15,3.59l-1.98,0.11l-0.74,1.2l0.2,1.11l-2.42,3.98l-1.49,0.92l-1.04,-0.85l1.21,-2.05l-0.81,-2.01l-2.63,-1.25l0.04,-0.57l1.82,-1.19l0.43,-2.34l-0.16,-2.03l-0.95,-1.82l-0.06,-0.72l-3.11,-3.64l-0.79,-1.52l1.56,1.45l1.76,0.66l0.65,2.34ZM853.83,393.59l0.57,1.24l0.59,0.16l1.42,-0.97l0.46,0.79l0.0,1.03l-2.47,3.48l-1.26,1.2l-0.06,0.5l0.55,0.87l-1.41,0.07l-2.33,1.38l-2.03,5.02l-3.02,2.16l-2.06,-0.06l-1.71,-1.04l-2.47,-0.2l-0.27,-0.73l1.22,-2.1l3.05,-2.94l1.62,-0.59l4.02,-2.82l1.57,-1.67l1.07,-2.16l0.88,-0.7l0.48,-1.75l1.24,-0.97l0.35,0.79Z", "name": "New Zealand"}, "NP": {"path": "M641.14,213.62l0.01,3.19l-1.74,0.04l-4.8,-0.86l-1.58,-1.39l-3.37,-0.34l-7.65,-3.7l0.8,-2.09l2.33,-1.7l1.77,0.75l2.49,1.76l1.38,0.41l0.99,1.35l1.9,0.52l1.99,1.17l5.49,0.9Z", "name": "Nepal"}, "XK": {"path": "M472.77,172.64l-1.08,-1.29l0.96,-0.77l0.29,-0.83l1.98,1.64l-0.36,0.67l-1.79,0.58Z", "name": "Kosovo"}, "CI": {"path": "M407.4,259.27l0.86,0.42l0.56,0.9l1.13,0.53l1.19,-0.61l0.97,-0.08l1.42,0.54l0.6,3.24l-1.03,2.08l-0.65,2.84l1.06,2.33l-0.06,0.53l-2.54,-0.47l-1.66,0.03l-3.06,0.46l-4.11,1.6l0.32,-3.06l-1.18,-1.31l-1.32,-0.66l0.42,-0.85l-0.2,-1.4l0.5,-0.67l0.01,-1.59l0.84,-0.32l0.26,-0.5l-1.15,-3.01l0.12,-0.5l0.51,-0.25l0.66,0.31l1.93,0.02l0.67,-0.71l0.71,-0.14l0.25,0.69l0.57,0.22l1.4,-0.61Z", "name": "C\u00f4te d'Ivoire"}, "CH": {"path": "M444.62,156.35l-0.29,0.87l0.18,0.53l1.13,0.58l1.0,0.1l-0.1,0.65l-0.79,0.38l-1.72,-0.37l-0.45,0.23l-0.45,1.04l-0.75,0.06l-0.84,-0.4l-1.32,1.0l-0.96,0.12l-0.88,-0.55l-0.81,-1.3l-0.49,-0.16l-0.63,0.26l0.02,-0.65l1.71,-1.66l0.1,-0.56l0.93,0.08l0.58,-0.46l1.99,0.02l0.66,-0.61l2.19,0.79Z", "name": "Switzerland"}, "CO": {"path": "M242.07,254.93l-1.7,0.59l-0.59,1.18l-1.7,1.69l-0.38,1.93l-0.67,1.43l0.31,0.57l1.03,0.13l0.25,0.9l0.57,0.64l-0.04,2.34l1.64,1.42l3.16,-0.24l1.26,0.28l1.67,2.06l0.41,0.13l4.09,-0.39l0.45,0.22l-0.92,1.95l-0.2,1.8l0.52,1.83l0.75,1.05l-1.12,1.1l0.07,0.63l0.84,0.51l0.74,1.29l-0.39,-0.45l-0.59,-0.01l-0.71,0.74l-4.71,-0.05l-0.4,0.41l0.03,1.57l0.33,0.39l1.11,0.2l-1.68,0.4l-0.29,0.38l-0.01,1.82l1.16,1.14l0.34,1.25l-1.05,7.05l-1.04,-0.87l1.26,-1.99l-0.13,-0.56l-2.18,-1.23l-1.38,0.2l-1.14,-0.38l-1.27,0.61l-1.55,-0.26l-1.38,-2.46l-1.23,-0.75l-0.85,-1.2l-1.67,-1.19l-0.86,0.13l-2.11,-1.32l-1.01,0.31l-1.8,-0.29l-0.52,-0.91l-3.09,-1.68l0.77,-0.52l-0.1,-1.12l0.41,-0.64l1.34,-0.32l2.0,-2.88l-0.11,-0.57l-0.66,-0.43l0.39,-1.38l-0.52,-2.1l0.49,-0.83l-0.4,-2.13l-0.97,-1.35l0.17,-0.66l0.86,-0.08l0.47,-0.75l-0.46,-1.63l1.41,-0.07l1.8,-1.69l0.93,-0.24l0.3,-0.38l0.45,-2.76l1.22,-1.0l1.44,-0.04l0.45,-0.5l1.91,0.12l2.93,-1.84l1.15,-1.14l0.91,0.46l-0.25,0.45Z", "name": "Colombia"}, "CN": {"path": "M740.23,148.97l4.57,1.3l2.8,2.17l0.98,2.9l0.38,0.27l3.8,0.0l2.32,-1.28l3.29,-0.75l-0.96,2.09l-1.02,1.28l-0.85,3.4l-1.52,2.73l-2.76,-0.5l-2.4,1.13l-0.21,0.45l0.64,2.57l-0.32,3.2l-0.94,0.06l-0.37,0.89l-0.91,-1.01l-0.64,0.07l-0.92,1.57l-3.73,1.25l-0.26,0.48l0.26,1.06l-1.5,-0.08l-1.09,-0.86l-0.56,0.06l-1.67,2.06l-2.7,1.56l-2.03,1.88l-3.4,0.83l-1.93,1.4l-1.15,0.34l0.33,-0.7l-0.41,-0.89l1.79,-1.79l0.02,-0.54l-1.32,-1.56l-0.48,-0.1l-2.24,1.09l-2.83,2.06l-1.51,1.83l-2.28,0.13l-1.55,1.49l-0.04,0.5l1.32,1.97l2.0,0.58l0.31,1.35l1.98,0.84l3.0,-1.96l2.0,1.02l1.49,0.11l0.22,0.83l-3.37,0.86l-1.12,1.48l-2.5,1.52l-1.29,1.99l0.14,0.56l2.57,1.48l0.97,2.7l3.17,4.63l-0.03,1.66l-1.35,0.65l-0.2,0.51l0.6,1.47l1.4,0.91l-0.89,3.82l-1.43,0.38l-3.85,6.44l-2.27,3.11l-6.78,4.57l-2.73,0.29l-1.45,1.04l-0.62,-0.61l-0.55,-0.01l-1.36,1.25l-3.39,1.27l-2.61,0.4l-1.1,2.79l-0.81,0.09l-0.49,-1.42l0.5,-0.85l-0.25,-0.59l-3.36,-0.84l-1.3,0.4l-2.31,-0.62l-0.94,-0.84l0.33,-1.28l-0.3,-0.49l-2.19,-0.46l-1.13,-0.93l-0.47,-0.02l-2.06,1.36l-4.29,0.28l-2.76,1.05l-0.28,0.43l0.32,2.53l-0.59,-0.03l-0.19,-1.34l-0.55,-0.34l-1.68,0.7l-2.46,-1.23l0.62,-1.87l-0.26,-0.51l-1.37,-0.44l-0.54,-2.22l-0.45,-0.3l-2.13,0.35l0.24,-2.48l2.39,-2.4l0.03,-4.31l-1.19,-0.92l-0.78,-1.49l-0.41,-0.21l-1.41,0.19l-1.98,-0.3l0.46,-1.07l-1.17,-1.7l-0.55,-0.11l-1.63,1.05l-2.25,-0.57l-2.89,1.73l-2.25,1.98l-1.75,0.29l-1.17,-0.71l-3.31,-0.65l-1.48,0.79l-1.04,1.27l-0.12,-1.17l-0.54,-0.34l-1.44,0.54l-5.55,-0.86l-1.98,-1.16l-1.89,-0.54l-0.99,-1.35l-1.34,-0.37l-2.55,-1.79l-2.01,-0.84l-1.21,0.56l-5.57,-3.45l-0.53,-2.31l1.19,0.25l0.48,-0.37l0.08,-1.42l-0.98,-1.56l0.15,-2.44l-2.69,-3.32l-4.12,-1.23l-0.67,-2.0l-1.92,-1.48l-0.38,-0.7l-0.51,-3.01l-1.52,-0.66l-0.7,0.13l-0.48,-2.05l0.55,-0.51l-0.09,-0.82l2.03,-1.19l1.6,-0.54l2.56,0.38l0.42,-0.22l0.85,-1.7l3.0,-0.33l1.1,-1.26l4.05,-1.77l0.39,-0.91l-0.17,-1.44l1.45,-0.67l0.2,-0.52l-2.07,-4.9l4.51,-1.12l1.37,-0.73l1.89,-5.51l4.98,0.86l1.51,-1.7l0.11,-2.87l1.99,-0.38l1.83,-2.06l0.49,-0.13l0.68,2.08l2.23,1.77l3.44,1.16l1.55,2.29l-0.92,3.49l0.96,1.67l6.54,1.13l2.95,1.87l1.47,0.35l1.06,2.62l1.53,1.91l3.05,0.08l5.14,0.67l3.37,-0.41l2.36,0.43l3.65,1.8l3.06,0.04l1.45,0.88l2.87,-1.59l3.95,-1.02l3.83,-0.14l3.06,-1.14l1.77,-1.6l1.72,-1.01l0.17,-0.49l-1.1,-2.05l1.02,-1.54l4.02,0.8l2.45,-1.61l3.76,-1.19l1.96,-2.13l1.63,-0.83l3.51,-0.4l1.92,0.34l0.46,-0.3l0.17,-1.5l-2.27,-2.22l-2.11,-1.09l-2.18,1.11l-2.32,-0.47l-1.29,0.32l-0.4,-0.82l2.73,-5.16l3.02,1.06l3.53,-2.06l0.18,-1.68l2.16,-3.35l1.49,-1.35l-0.03,-1.85l-1.07,-0.85l1.54,-1.26l2.98,-0.59l3.23,-0.09l3.64,0.99l2.04,1.16l3.29,6.71l0.92,3.19ZM696.92,237.31l-1.87,1.08l-1.63,-0.64l-0.06,-1.79l1.03,-0.98l2.58,-0.69l1.16,0.05l0.3,0.54l-0.98,1.06l-0.53,1.37Z", "name": "China"}, "CM": {"path": "M457.92,257.49l1.05,1.91l-1.4,0.16l-1.05,-0.23l-0.45,0.22l-0.54,1.19l0.08,0.45l1.48,1.47l1.05,0.45l1.01,2.46l-1.52,2.99l-0.68,0.68l-0.13,3.69l2.38,3.84l1.09,0.8l0.24,2.48l-3.67,-1.14l-11.27,-0.13l0.23,-1.79l-0.98,-1.66l-1.19,-0.54l-0.44,-0.97l-0.6,-0.42l1.71,-4.27l0.75,-0.13l1.38,-1.36l0.65,-0.03l1.71,0.99l1.93,-1.12l1.14,-3.18l1.38,-1.17l2.0,-5.14l2.17,-2.13l0.3,-1.64l-0.86,-0.88l0.03,-0.33l0.94,1.28l0.07,3.22Z", "name": "Cameroon"}, "CL": {"path": "M246.5,429.18l-3.14,1.83l-0.57,3.16l-0.64,0.05l-2.68,-1.06l-2.82,-2.33l-3.04,-1.89l-0.69,-1.85l0.63,-2.14l-1.21,-2.11l-0.31,-5.37l1.01,-2.91l2.57,-2.38l-0.18,-0.68l-3.16,-0.77l2.05,-2.47l0.77,-4.65l2.32,0.9l0.54,-0.29l1.31,-6.31l-0.22,-0.44l-1.68,-0.8l-0.56,0.28l-0.7,3.36l-0.81,-0.22l1.56,-9.41l1.15,-2.24l-0.71,-2.82l-0.18,-2.84l1.01,-0.33l3.26,-9.14l1.07,-4.22l-0.56,-4.21l0.74,-2.34l-0.29,-3.27l1.46,-3.34l2.04,-16.59l-0.66,-7.76l1.03,-0.53l0.54,-0.9l0.79,1.14l0.32,1.78l1.25,1.16l-0.69,2.55l1.33,2.9l0.97,3.59l0.46,0.29l1.5,-0.3l0.11,0.23l-0.76,2.44l-2.57,1.23l-0.23,0.37l0.08,4.33l-0.46,0.77l0.56,1.21l-1.58,1.51l-1.68,2.62l-0.89,2.47l0.2,2.7l-1.48,2.73l1.12,5.09l0.64,0.61l-0.01,2.29l-1.38,2.68l0.01,2.4l-1.89,2.04l0.02,2.75l0.69,2.57l-1.43,1.13l-1.26,5.68l0.39,3.51l-0.97,0.89l0.58,3.5l1.02,1.14l-0.65,1.02l0.15,0.57l1.0,0.53l0.16,0.69l-1.03,0.85l0.26,1.75l-0.89,4.03l-1.31,2.66l0.24,1.75l-0.71,1.83l-1.99,1.7l0.3,3.67l0.88,1.19l1.58,0.01l0.01,2.21l1.04,1.95l5.98,0.63ZM248.69,430.79l0.0,7.33l0.4,0.4l3.52,0.05l-0.44,0.75l-1.94,0.98l-2.49,-0.37l-1.88,-1.06l-2.55,-0.49l-5.59,-3.71l-2.38,-2.63l4.1,2.48l3.32,1.23l0.45,-0.12l1.29,-1.57l0.83,-2.32l2.05,-1.24l1.31,0.29Z", "name": "Chile"}, "XC": {"path": "M504.91,192.87l0.34,0.01l0.27,-0.07l-0.29,0.26l-0.31,-0.2Z", "name": "N. Cyprus"}, "CA": {"path": "M280.06,145.6l-1.67,2.88l0.07,0.49l0.5,0.04l1.46,-0.98l1.0,0.42l-0.56,0.72l0.17,0.62l2.22,0.89l1.35,-0.71l1.95,0.78l-0.66,2.01l0.5,0.51l1.32,-0.42l0.98,3.17l-0.91,2.41l-0.8,0.08l-1.23,-0.45l0.47,-2.25l-0.89,-0.83l-0.48,0.06l-2.78,2.63l-0.34,-0.02l1.02,-0.85l-0.14,-0.69l-2.4,-0.77l-7.4,0.08l-0.17,-0.41l1.3,-0.94l0.02,-0.64l-0.73,-0.58l1.85,-1.74l2.57,-5.16l1.47,-1.79l1.99,-1.05l0.46,0.06l-1.53,2.45ZM68.32,74.16l4.13,0.95l4.02,2.14l2.61,0.4l2.47,-1.89l2.88,-1.31l3.85,0.48l3.71,-1.94l3.82,-1.04l1.56,1.68l0.49,0.08l1.87,-1.04l0.65,-1.98l1.24,0.35l4.16,3.94l0.54,0.01l2.75,-2.49l0.26,2.59l0.49,0.35l3.08,-0.73l1.04,-1.27l2.73,0.23l3.83,1.86l5.86,1.61l3.47,0.75l2.44,-0.26l2.73,1.78l-2.98,1.81l-0.19,0.41l0.31,0.32l4.53,0.92l6.87,-0.5l2.0,-0.69l2.49,2.39l0.53,0.02l2.72,-2.16l-0.02,-0.64l-2.16,-1.54l1.15,-1.06l4.83,-0.61l1.84,0.95l2.48,2.31l3.01,-0.23l4.55,1.92l3.85,-0.67l3.61,0.1l0.41,-0.44l-0.25,-2.36l1.79,-0.61l3.49,1.32l-0.01,3.77l0.31,0.39l0.45,-0.22l1.48,-3.16l1.74,0.1l0.41,-0.3l1.13,-4.37l-2.78,-3.11l-2.8,-1.74l0.19,-4.64l2.71,-3.07l2.98,0.67l2.41,1.95l3.19,4.8l-1.99,1.97l0.21,0.68l4.33,0.84l-0.01,4.15l0.25,0.37l0.44,-0.09l3.07,-3.15l2.54,2.39l-0.61,3.33l2.42,2.88l0.61,0.0l2.61,-3.08l1.88,-3.82l0.17,-4.58l6.72,0.94l3.13,2.04l0.13,1.82l-1.76,2.19l-0.01,0.49l1.66,2.16l-0.26,1.71l-4.68,2.8l-3.28,0.61l-2.47,-1.2l-0.55,0.23l-0.73,2.04l-2.38,3.43l-0.74,1.77l-2.74,2.57l-3.44,0.25l-2.21,1.78l-0.28,2.53l-2.82,0.55l-3.12,3.22l-2.72,4.31l-1.03,3.17l-0.14,4.31l0.33,0.41l3.44,0.57l2.24,5.95l0.45,0.23l3.4,-0.69l4.52,1.51l2.43,1.31l1.91,1.73l3.1,0.96l2.62,1.46l6.6,0.54l-0.35,2.74l0.81,3.53l1.81,3.78l3.83,3.3l0.45,0.04l2.1,-1.28l1.37,-3.69l-1.31,-5.38l-1.45,-1.58l3.57,-1.47l2.84,-2.46l1.52,-2.8l-0.25,-2.55l-1.7,-3.07l-2.85,-2.61l2.8,-3.95l-1.08,-3.37l-0.79,-5.67l1.36,-0.7l6.76,1.41l2.12,-0.96l5.12,3.36l1.05,1.61l4.08,0.26l-0.06,2.87l0.83,4.7l0.3,0.32l2.16,0.54l1.73,2.06l0.5,0.09l3.63,-2.03l2.52,-4.19l1.26,-1.32l7.6,11.72l-0.92,2.04l0.16,0.51l3.3,1.97l2.22,1.98l4.1,0.98l1.43,0.99l0.95,2.79l2.1,0.68l0.84,1.08l0.17,3.45l-3.37,2.26l-4.22,1.24l-3.06,2.63l-4.06,0.51l-5.35,-0.69l-6.39,0.2l-2.3,2.41l-3.26,1.51l-6.47,7.15l-0.06,0.48l0.44,0.19l2.13,-0.52l4.17,-4.24l5.12,-2.62l3.52,-0.3l1.69,1.21l-2.12,2.21l0.81,3.47l1.02,2.61l3.47,1.6l4.14,-0.45l2.15,-2.8l0.26,1.48l1.14,0.8l-2.56,1.69l-5.5,1.82l-2.54,1.27l-2.74,2.15l-1.4,-0.16l-0.07,-2.01l4.14,-2.44l0.18,-0.45l-0.39,-0.29l-6.63,0.45l-1.39,-1.49l-0.14,-4.43l-1.11,-0.91l-1.82,0.39l-0.66,-0.66l-0.6,0.03l-1.91,2.39l-0.82,2.52l-0.8,1.27l-1.67,0.56l-0.46,0.76l-8.31,0.07l-1.21,0.62l-2.35,1.97l-0.71,-0.14l-1.37,0.96l-1.12,-0.48l-4.74,1.26l-0.9,1.17l0.21,0.62l1.73,0.3l-1.81,0.31l-1.85,0.81l-2.11,-0.13l-2.95,1.78l-0.69,-0.09l1.39,-2.1l1.73,-1.21l0.1,-2.29l1.16,-1.99l0.49,0.53l2.03,0.42l1.2,-1.16l0.02,-0.47l-2.66,-3.51l-2.28,-0.61l-5.64,-0.71l-0.4,-0.57l-0.79,0.13l0.2,-0.41l-0.22,-0.55l-0.68,-0.26l0.19,-1.26l-0.78,-0.73l0.31,-0.64l-0.29,-0.57l-2.6,-0.44l-0.75,-1.63l-0.94,-0.66l-4.31,-0.65l-1.13,1.19l-1.48,0.59l-0.85,1.06l-2.83,-0.76l-2.09,0.39l-2.39,-0.97l-4.24,-0.7l-0.57,-0.4l-0.41,-1.63l-0.4,-0.3l-0.85,0.02l-0.39,0.4l-0.01,0.85l-69.13,-0.01l-6.51,-4.52l-4.5,-1.38l-1.26,-2.66l0.33,-1.93l-0.23,-0.43l-3.01,-1.35l-0.55,-2.77l-2.89,-2.38l-0.04,-1.45l1.39,-1.83l-0.28,-2.55l-4.16,-2.2l-4.07,-6.6l-4.02,-3.22l-1.3,-1.88l-0.5,-0.13l-2.51,1.21l-2.23,1.87l-3.85,-3.88l-2.44,-1.04l-2.22,-0.13l0.03,-37.49ZM260.37,148.65l3.04,0.76l2.26,1.2l-3.78,-0.95l-1.53,-1.01ZM249.4,3.81l6.68,0.49l5.32,0.79l4.26,1.57l-0.07,1.1l-5.85,2.53l-6.02,1.21l-2.39,1.39l-0.18,0.45l0.39,0.29l4.01,-0.02l-4.65,2.82l-4.2,1.74l-4.19,4.59l-5.03,0.92l-1.67,1.15l-7.47,0.59l-0.37,0.37l0.32,0.42l2.41,0.49l-0.81,0.47l-0.12,0.59l1.83,2.41l-2.02,1.59l-3.81,1.51l-1.32,2.16l-3.38,1.53l-0.22,0.48l0.35,1.19l0.4,0.29l3.88,-0.18l0.03,0.61l-6.33,2.95l-6.41,-1.4l-7.43,0.79l-3.72,-0.62l-4.4,-0.25l-0.23,-1.83l4.29,-1.11l0.28,-0.51l-1.1,-3.45l1.0,-0.25l6.58,2.28l0.47,-0.16l-0.05,-0.49l-3.41,-3.45l-3.58,-0.98l1.48,-1.55l4.34,-1.29l0.97,-2.19l-0.16,-0.48l-3.42,-2.13l-0.81,-2.26l6.2,0.22l2.24,0.58l3.91,-2.1l0.2,-0.43l-0.35,-0.32l-5.64,-0.67l-8.73,0.36l-4.26,-1.9l-2.12,-2.4l-2.78,-1.66l-0.41,-1.52l3.31,-1.03l2.93,-0.2l4.91,-0.99l3.7,-2.27l2.87,0.3l2.62,1.67l0.56,-0.14l1.82,-3.2l3.13,-0.94l4.44,-0.69l7.53,-0.26l1.48,0.67l7.19,-1.06l10.8,0.79ZM203.85,57.54l0.01,0.42l1.97,2.97l0.68,-0.02l2.24,-3.72l5.95,-1.86l4.01,4.64l-0.35,2.91l0.5,0.43l4.95,-1.36l2.32,-1.8l5.31,2.28l3.27,2.11l0.3,1.84l0.48,0.33l4.42,-0.99l2.64,2.87l5.97,1.77l2.06,1.72l2.11,3.71l-4.19,1.86l-0.01,0.73l5.9,2.83l3.94,0.94l3.78,3.95l3.46,0.25l-0.63,2.37l-4.11,4.47l-2.76,-1.56l-3.9,-3.94l-3.59,0.41l-0.33,0.34l-0.19,2.72l2.63,2.38l3.42,1.89l0.94,0.97l1.55,3.75l-0.7,2.29l-2.74,-0.92l-6.25,-3.15l-0.51,0.13l0.05,0.52l6.07,5.69l0.18,0.59l-6.09,-1.39l-5.31,-2.24l-2.63,-1.66l0.6,-0.77l-0.12,-0.6l-7.39,-4.01l-0.59,0.37l0.03,0.79l-6.73,0.6l-1.69,-1.1l1.36,-2.46l4.51,-0.07l5.15,-0.52l0.31,-0.6l-0.74,-1.3l0.78,-1.84l3.21,-4.05l-0.67,-2.35l-1.11,-1.6l-3.84,-2.1l-4.35,-1.28l0.91,-0.63l0.06,-0.61l-2.65,-2.75l-2.34,-0.36l-1.89,-1.46l-0.53,0.03l-1.24,1.23l-4.36,0.55l-9.04,-0.99l-9.26,-1.98l-1.6,-1.22l2.22,-1.77l0.13,-0.44l-0.38,-0.27l-3.22,-0.02l-0.72,-4.25l1.83,-4.04l2.42,-1.85l5.5,-1.1l-1.39,2.35ZM261.19,159.33l2.07,0.61l1.44,-0.04l-1.15,0.63l-2.94,-1.23l-0.4,-0.68l0.36,-0.37l0.61,1.07ZM230.83,84.39l-2.37,0.18l-0.49,-1.63l0.93,-2.09l1.94,-0.51l1.62,0.99l0.02,1.52l-1.66,1.54ZM229.43,58.25l0.11,0.65l-4.87,-0.21l-2.72,0.62l-3.1,-2.57l0.08,-1.26l0.86,-0.23l5.57,0.51l4.08,2.5ZM222.0,105.02l-0.72,1.49l-0.63,-0.19l-0.48,-0.84l0.81,-0.99l0.65,0.05l0.37,0.46ZM183.74,38.32l2.9,1.7l4.79,-0.01l1.84,1.46l-0.49,1.68l0.23,0.48l2.82,1.14l1.76,1.26l7.01,0.65l4.1,-1.1l5.03,-0.43l3.93,0.35l2.48,1.77l0.46,1.7l-1.3,1.1l-3.56,1.01l-3.23,-0.59l-7.17,0.76l-5.09,0.09l-3.99,-0.6l-6.42,-1.54l-0.79,-2.51l-0.3,-2.49l-2.64,-2.5l-5.32,-0.72l-2.52,-1.4l0.68,-1.57l4.78,0.31ZM207.38,91.35l0.4,1.56l0.56,0.26l1.06,-0.52l1.32,0.96l5.42,2.57l0.2,1.68l0.46,0.35l1.68,-0.28l1.15,0.85l-1.55,0.87l-3.61,-0.88l-1.32,-1.69l-0.57,-0.06l-2.45,2.1l-3.12,1.79l-0.7,-1.87l-0.42,-0.26l-2.16,0.24l1.39,-1.39l0.32,-3.14l0.76,-3.35l1.18,0.22ZM215.49,102.6l-2.67,1.95l-1.4,-0.07l-0.3,-0.58l1.53,-1.48l2.84,0.18ZM202.7,24.12l2.53,1.59l-2.87,1.4l-4.53,4.05l-4.25,0.38l-5.03,-0.68l-2.45,-2.04l0.03,-1.62l1.82,-1.37l0.14,-0.45l-0.38,-0.27l-4.45,0.04l-2.59,-1.76l-1.41,-2.29l1.57,-2.32l1.62,-1.66l2.44,-0.39l0.25,-0.65l-0.6,-0.74l4.86,-0.25l3.24,3.11l8.16,2.3l1.9,3.61ZM187.47,59.2l-2.76,3.49l-2.38,-0.15l-1.44,-3.84l0.04,-2.2l1.19,-1.88l2.3,-1.23l5.07,0.17l4.11,1.02l-3.24,3.72l-2.88,0.89ZM186.07,48.79l-1.08,1.53l-3.34,-0.34l-2.56,-1.1l1.03,-1.75l3.25,-1.23l1.95,1.58l0.75,1.3ZM185.71,35.32l-5.3,-0.2l-0.32,-0.71l4.31,0.07l1.3,0.84ZM180.68,32.48l-3.34,1.0l-1.79,-1.1l-0.98,-1.87l-0.15,-1.73l4.1,0.53l2.67,1.7l-0.51,1.47ZM180.9,76.31l-1.1,1.08l-3.13,-1.23l-2.12,0.43l-2.71,-1.57l1.72,-1.09l1.55,-1.72l3.81,1.9l1.98,2.2ZM169.74,54.87l2.96,0.97l4.17,-0.57l0.41,0.88l-2.14,2.11l0.09,0.64l3.55,1.92l-0.4,3.72l-3.79,1.65l-2.17,-0.35l-1.72,-1.74l-6.02,-3.5l0.03,-0.85l4.68,0.54l0.4,-0.21l-0.05,-0.45l-2.48,-2.81l2.46,-1.95ZM174.45,40.74l1.37,1.73l0.07,2.44l-1.05,3.45l-3.79,0.47l-2.32,-0.69l0.05,-2.64l-0.44,-0.41l-3.68,0.35l-0.12,-3.1l2.45,0.1l3.67,-1.73l3.41,0.29l0.37,-0.26ZM170.05,31.55l0.67,1.56l-3.33,-0.49l-4.22,-1.77l-4.35,-0.16l1.4,-0.94l-0.06,-0.7l-2.81,-1.23l-0.12,-1.39l4.39,0.68l6.62,1.98l1.81,2.47ZM134.5,58.13l-1.02,1.82l0.45,0.58l5.4,-1.39l3.33,2.29l0.49,-0.03l2.6,-2.23l1.94,1.32l2.0,4.5l0.7,0.06l1.3,-2.29l-1.63,-4.46l1.69,-0.54l2.31,0.71l2.65,1.81l2.49,7.92l8.48,4.27l-0.19,1.35l-3.79,0.33l-0.26,0.67l1.4,1.49l-0.58,1.1l-4.23,-0.64l-4.43,-1.19l-3.0,0.28l-4.66,1.47l-10.52,1.04l-1.43,-2.02l-3.42,-1.2l-2.21,0.43l-2.51,-2.86l4.84,-1.05l3.6,0.19l3.27,-0.78l0.31,-0.39l-0.31,-0.39l-4.84,-1.06l-8.79,0.27l-0.85,-1.07l5.26,-1.66l0.27,-0.45l-0.4,-0.34l-3.8,0.06l-3.81,-1.06l1.81,-3.01l1.66,-1.79l6.48,-2.81l1.97,0.71ZM158.7,56.61l-1.7,2.44l-3.2,-2.75l0.37,-0.3l3.11,-0.18l1.42,0.79ZM149.61,42.73l1.01,1.89l0.5,0.18l2.14,-0.82l2.23,0.19l0.36,2.04l-1.33,2.09l-8.28,0.76l-6.35,2.15l-3.41,0.1l-0.19,-0.96l4.9,-2.08l0.23,-0.46l-0.41,-0.31l-11.25,0.59l-2.89,-0.74l3.04,-4.44l2.14,-1.32l6.81,1.69l4.58,3.06l4.37,0.39l0.36,-0.63l-3.36,-4.6l1.85,-1.53l2.18,0.51l0.77,2.26ZM144.76,34.41l-4.36,1.44l-3.0,-1.4l1.46,-1.24l3.47,-0.52l2.96,0.71l-0.52,1.01ZM145.13,29.83l-1.9,0.66l-3.67,-0.0l2.27,-1.61l3.3,0.95ZM118.92,65.79l-6.03,2.02l-1.33,-1.9l-5.38,-2.28l2.59,-5.05l2.16,-3.14l-0.02,-0.48l-1.97,-2.41l7.64,-0.7l3.6,1.02l6.3,0.27l4.42,2.95l-2.53,0.98l-6.24,3.43l-3.1,3.28l-0.11,2.01ZM129.54,35.53l-0.28,3.37l-1.72,1.62l-2.33,0.28l-4.61,2.19l-3.86,0.76l-2.64,-0.87l3.72,-3.4l5.01,-3.34l3.72,0.07l3.0,-0.67ZM111.09,152.69l-0.67,0.24l-3.85,-1.37l-0.83,-1.17l-2.12,-1.07l-0.66,-1.02l-2.4,-0.55l-0.74,-1.71l6.02,1.45l2.0,2.55l2.52,1.39l0.73,1.27ZM87.8,134.64l0.89,0.29l1.86,-0.21l-0.65,3.34l1.69,2.33l-1.31,-1.33l-0.99,-1.62l-1.17,-0.98l-0.33,-1.82Z", "name": "Canada"}, "CG": {"path": "M466.72,276.48l-0.1,1.03l-1.25,2.97l-0.19,3.62l-0.46,1.78l-0.23,0.63l-1.61,1.19l-1.21,1.39l-1.09,2.43l0.04,2.09l-3.25,3.24l-0.5,-0.24l-0.5,-0.83l-1.36,-0.02l-0.98,0.89l-1.68,-0.99l-1.54,1.24l-1.52,-1.96l1.57,-1.14l0.11,-0.52l-0.77,-1.35l2.1,-0.66l0.39,-0.73l1.05,0.82l2.21,0.11l1.12,-1.37l0.37,-1.81l-0.27,-2.09l-1.13,-1.5l1.0,-2.69l-0.13,-0.45l-0.92,-0.58l-1.6,0.17l-0.51,-0.94l0.1,-0.61l2.75,0.09l3.97,1.24l0.51,-0.33l0.17,-1.28l1.24,-2.21l1.28,-1.14l2.76,0.49Z", "name": "Congo"}, "CF": {"path": "M461.16,278.2l-0.26,-1.19l-1.09,-0.77l-0.84,-1.17l-0.29,-1.0l-1.04,-1.15l0.08,-3.43l0.58,-0.49l1.16,-2.35l1.85,-0.17l0.61,-0.62l0.97,0.58l3.15,-0.96l2.48,-1.92l0.02,-0.96l2.81,0.02l2.36,-1.17l1.93,-2.85l1.16,-0.93l1.11,-0.3l0.27,0.86l1.34,1.47l-0.39,2.01l0.3,1.01l4.01,2.75l0.17,0.93l2.63,2.31l0.6,1.44l2.08,1.4l-3.84,-0.21l-1.94,0.88l-1.23,-0.49l-2.67,1.2l-1.29,-0.18l-0.51,0.36l-0.6,1.22l-3.35,-0.65l-1.57,-0.91l-2.42,-0.83l-1.45,0.91l-0.97,1.27l-0.26,1.56l-3.22,-0.43l-1.49,1.33l-0.94,1.62Z", "name": "Central African Rep."}, "CD": {"path": "M487.01,272.38l2.34,-0.14l1.35,1.84l1.34,0.45l0.86,-0.39l1.21,0.12l1.07,-0.41l0.54,0.89l2.04,1.54l-0.14,2.72l0.7,0.54l-1.38,1.13l-1.53,2.54l-0.17,2.05l-0.59,1.08l-0.02,1.72l-0.72,0.84l-0.66,3.01l0.63,1.32l-0.44,4.26l0.64,1.47l-0.37,1.22l0.86,1.8l1.53,1.41l0.3,1.26l0.44,0.5l-4.08,0.75l-0.92,1.81l0.51,1.34l-0.74,5.43l0.17,0.38l2.45,1.46l0.54,-0.1l0.12,1.62l-1.28,-0.01l-1.85,-2.35l-1.94,-0.45l-0.48,-1.13l-0.55,-0.2l-1.41,0.74l-1.71,-0.3l-1.01,-1.18l-2.49,-0.19l-0.44,-0.77l-1.98,-0.21l-2.88,0.36l0.11,-2.41l-0.85,-1.13l-0.16,-1.36l0.32,-1.73l-0.46,-0.89l-0.04,-1.49l-0.4,-0.39l-2.53,0.02l0.1,-0.41l-0.39,-0.49l-1.28,0.01l-0.43,0.45l-1.62,0.32l-0.83,1.79l-1.09,-0.28l-2.4,0.52l-1.37,-1.91l-1.3,-3.3l-0.38,-0.27l-7.39,-0.03l-2.46,0.42l0.5,-0.45l0.37,-1.47l0.66,-0.38l0.92,0.08l0.73,-0.82l0.87,0.02l0.31,0.68l1.4,0.36l3.59,-3.63l0.01,-2.23l1.02,-2.29l2.69,-2.39l0.43,-0.99l0.49,-1.96l0.17,-3.51l1.25,-2.95l0.36,-3.14l0.86,-1.13l1.1,-0.66l3.57,1.73l3.65,0.73l0.46,-0.21l0.8,-1.46l1.24,0.19l2.61,-1.17l0.81,0.44l1.04,-0.03l0.59,-0.66l0.7,-0.16l1.81,0.25Z", "name": "Dem. Rep. Congo"}, "CZ": {"path": "M458.46,144.88l1.22,1.01l1.47,0.23l0.13,0.93l1.36,0.68l0.54,-0.2l0.24,-0.55l1.15,0.25l0.53,1.09l1.68,0.18l0.6,0.84l-1.04,0.73l-0.96,1.28l-1.6,0.17l-0.55,0.56l-1.04,-0.46l-1.05,0.15l-2.12,-0.96l-1.05,0.34l-1.2,1.12l-1.56,-0.87l-2.57,-2.1l-0.53,-1.88l4.7,-2.52l0.71,0.26l0.9,-0.28Z", "name": "Czech Rep."}, "CY": {"path": "M504.36,193.47l0.43,0.28l-1.28,0.57l-0.92,-0.28l-0.24,-0.46l2.01,-0.13Z", "name": "Cyprus"}, "CR": {"path": "M211.34,258.05l0.48,0.99l1.6,1.6l-0.54,0.45l0.29,1.42l-0.25,1.19l-1.09,-0.59l-0.05,-1.25l-2.46,-1.42l-0.28,-0.77l-0.66,-0.45l-0.45,-0.0l-0.11,1.04l-1.32,-0.95l0.31,-1.3l-0.36,-0.6l0.31,-0.27l1.42,0.58l1.29,-0.14l0.56,0.56l0.74,0.17l0.55,-0.27Z", "name": "Costa Rica"}, "CU": {"path": "M221.21,227.25l1.27,1.02l2.19,-0.28l4.43,3.33l2.08,0.43l-0.1,0.38l0.36,0.5l1.75,0.1l1.48,0.84l-3.11,0.51l-4.15,-0.03l0.77,-0.67l-0.04,-0.64l-1.2,-0.74l-1.49,-0.16l-0.7,-0.61l-0.56,-1.4l-0.4,-0.25l-1.34,0.1l-2.2,-0.66l-0.88,-0.58l-3.18,-0.4l-0.27,-0.16l0.58,-0.74l-0.36,-0.29l-2.72,-0.05l-1.7,1.29l-0.91,0.03l-0.61,0.69l-1.01,0.22l1.11,-1.29l1.01,-0.52l3.69,-1.01l3.98,0.21l2.21,0.84Z", "name": "Cuba"}, "SZ": {"path": "M500.35,351.36l0.5,2.04l-0.38,0.89l-1.05,0.21l-1.23,-1.2l-0.02,-0.64l0.83,-1.57l1.34,0.27Z", "name": "Swaziland"}, "SY": {"path": "M511.0,199.79l0.05,-1.33l0.54,-1.36l1.28,-0.99l0.13,-0.45l-0.41,-1.11l-1.14,-0.36l-0.19,-1.74l0.52,-1.0l1.29,-1.21l0.2,-1.18l0.59,0.23l2.62,-0.76l1.36,0.52l2.06,-0.01l2.95,-1.08l3.25,-0.26l-0.67,0.94l-1.28,0.66l-0.21,0.4l0.23,2.01l-0.88,3.19l-10.15,5.73l-2.15,-0.85Z", "name": "Syria"}, "KG": {"path": "M621.35,172.32l-3.87,1.69l-0.96,1.18l-3.04,0.34l-1.13,1.86l-2.36,-0.35l-1.99,0.63l-2.39,1.4l0.06,0.95l-0.4,0.37l-4.52,0.43l-3.02,-0.93l-2.37,0.17l0.11,-0.79l2.32,0.42l1.13,-0.88l1.99,0.2l3.21,-2.14l-0.03,-0.69l-2.97,-1.57l-1.94,0.65l-1.22,-0.74l1.71,-1.58l-0.12,-0.67l-0.36,-0.15l0.32,-0.77l1.36,-0.35l4.02,1.02l0.49,-0.3l0.35,-1.59l1.09,-0.48l3.42,1.22l1.11,-0.31l7.64,0.39l1.16,1.0l1.23,0.39Z", "name": "Kyrgyzstan"}, "KE": {"path": "M506.26,284.69l1.87,-2.56l0.93,-2.15l-1.38,-4.08l-1.06,-1.6l2.82,-2.75l0.79,0.26l0.12,1.41l0.86,0.83l1.9,0.11l3.28,2.13l3.57,0.44l1.05,-1.12l1.96,-0.9l0.82,0.68l1.16,0.09l-1.78,2.45l0.03,9.12l1.3,1.94l-1.37,0.78l-0.67,1.03l-1.08,0.46l-0.34,1.67l-0.81,1.07l-0.45,1.55l-0.68,0.56l-3.2,-2.23l-0.35,-1.58l-8.86,-4.98l0.14,-1.6l-0.57,-1.04Z", "name": "Kenya"}, "SS": {"path": "M481.71,263.34l1.07,-0.72l1.2,-3.18l1.36,-0.26l1.61,1.99l0.87,0.34l1.1,-0.41l1.5,0.07l0.57,0.53l2.49,0.0l0.44,-0.63l1.07,-0.4l0.45,-0.84l0.59,-0.33l1.9,1.33l1.6,-0.2l2.83,-3.33l-0.32,-2.21l1.59,-0.52l-0.24,1.6l0.3,1.83l1.35,1.18l0.2,1.87l0.35,0.41l0.02,1.53l-0.23,0.47l-1.42,0.25l-0.85,1.44l0.3,0.6l1.4,0.16l1.11,1.08l0.59,1.13l1.03,0.53l1.28,2.36l-4.41,3.98l-1.74,0.01l-1.89,0.55l-1.47,-0.52l-1.15,0.57l-2.96,-2.62l-1.3,0.49l-1.06,-0.15l-0.79,0.39l-0.82,-0.22l-1.8,-2.7l-1.91,-1.1l-0.66,-1.5l-2.62,-2.32l-0.18,-0.94l-2.37,-1.6Z", "name": "S. Sudan"}, "SR": {"path": "M283.12,270.19l2.1,0.53l-1.08,1.95l0.2,1.72l0.93,1.49l-0.59,2.03l-0.43,0.71l-1.12,-0.42l-1.32,0.22l-0.93,-0.2l-0.46,0.26l-0.25,0.73l0.33,0.7l-0.89,-0.13l-1.39,-1.97l-0.31,-1.34l-0.97,-0.31l-0.89,-1.47l0.35,-1.61l1.45,-0.82l0.33,-1.87l2.61,0.44l0.57,-0.47l1.75,-0.16Z", "name": "Suriname"}, "KH": {"path": "M689.52,249.39l0.49,1.45l-0.28,2.74l-4.0,1.86l-0.16,0.6l0.68,0.95l-2.06,0.17l-2.05,0.97l-1.82,-0.32l-2.12,-3.7l-0.55,-2.85l1.4,-1.85l3.02,-0.45l2.23,0.35l2.01,0.98l0.51,-0.14l0.95,-1.48l1.74,0.74Z", "name": "Cambodia"}, "SV": {"path": "M195.8,250.13l1.4,-1.19l2.24,1.45l0.98,-0.27l0.44,0.2l-0.27,1.05l-1.14,-0.03l-3.64,-1.21Z", "name": "El Salvador"}, "SK": {"path": "M476.82,151.17l-1.14,1.9l-2.73,-0.92l-0.82,0.2l-0.74,0.8l-3.46,0.73l-0.47,0.69l-1.76,0.33l-1.88,-1.0l-0.18,-0.81l0.38,-0.75l1.87,-0.32l1.74,-1.89l0.83,0.16l0.79,-0.34l1.51,1.04l1.34,-0.63l1.25,0.3l1.65,-0.42l1.81,0.95Z", "name": "Slovakia"}, "KR": {"path": "M737.51,185.84l0.98,-0.1l0.87,-1.17l2.69,-0.32l0.33,-0.29l1.76,2.79l0.58,1.76l0.02,3.12l-0.8,1.32l-2.21,0.55l-1.93,1.13l-1.8,0.19l-0.2,-1.1l0.43,-2.28l-0.95,-2.56l1.43,-0.37l0.23,-0.62l-1.43,-2.06Z", "name": "Korea"}, "SI": {"path": "M456.18,162.07l-0.51,-1.32l0.18,-1.05l1.69,0.2l1.42,-0.71l2.09,-0.07l0.62,-0.51l0.21,0.47l-1.61,0.67l-0.44,1.34l-0.66,0.24l-0.26,0.82l-1.22,-0.49l-0.84,0.46l-0.69,-0.04Z", "name": "Slovenia"}, "KP": {"path": "M736.77,185.16l-0.92,-0.42l-0.88,0.62l-1.21,-0.88l0.96,-1.15l0.59,-2.59l-0.46,-0.74l-2.09,-0.77l1.64,-1.52l2.72,-1.58l1.58,-1.91l1.11,0.78l2.17,0.11l0.41,-0.5l-0.3,-1.22l3.52,-1.18l0.94,-1.4l0.98,1.08l-2.19,2.18l0.01,2.14l-1.06,0.54l-1.41,1.4l-1.7,0.52l-1.25,1.09l-0.14,1.98l0.94,0.45l1.15,1.04l-0.13,0.26l-2.6,0.29l-1.13,1.29l-1.22,0.08Z", "name": "Dem. Rep. Korea"}, "KW": {"path": "M540.81,207.91l0.37,0.86l-0.17,0.76l0.6,1.53l-0.95,0.04l-0.82,-1.28l-1.57,-0.18l1.31,-1.88l1.22,0.17Z", "name": "Kuwait"}, "SN": {"path": "M390.09,248.21l0.12,1.55l0.49,1.46l0.96,0.82l0.05,1.28l-1.26,-0.19l-0.75,0.33l-1.84,-0.61l-5.84,-0.13l-2.54,0.51l-0.22,-1.03l1.77,0.04l2.01,-0.91l1.03,0.48l1.09,0.04l1.29,-0.62l0.14,-0.58l-0.51,-0.74l-1.81,0.25l-1.13,-0.63l-0.79,0.04l-0.72,0.61l-2.31,0.06l-0.92,-1.77l-0.81,-0.64l0.64,-0.35l2.46,-3.74l1.04,0.19l1.38,-0.56l1.19,-0.02l2.72,1.37l3.03,3.48Z", "name": "Senegal"}, "SL": {"path": "M394.46,264.11l-1.73,1.98l-0.58,1.33l-2.07,-1.06l-1.22,-1.26l-0.65,-2.39l1.16,-0.96l0.67,-1.17l1.21,-0.52l1.66,0.0l1.03,1.64l0.52,2.41Z", "name": "Sierra Leone"}, "KZ": {"path": "M552.8,172.89l0.46,-1.27l-0.48,-1.05l-2.96,-1.19l-1.06,-2.58l-1.37,-0.87l-0.03,-0.3l1.95,0.23l0.45,-0.38l0.08,-1.96l1.75,-0.41l2.1,0.45l0.48,-0.33l0.45,-3.04l-0.45,-2.09l-0.41,-0.31l-2.42,0.15l-2.36,-0.73l-2.87,1.37l-2.17,0.61l-0.85,-0.34l0.13,-1.61l-1.6,-2.12l-2.02,-0.08l-1.78,-1.82l1.29,-2.18l-0.57,-0.95l1.62,-2.91l2.21,1.63l0.63,-0.27l0.29,-2.22l4.92,-3.43l3.71,-0.08l8.4,3.6l2.92,-1.36l3.77,-0.06l3.11,1.66l0.51,-0.11l0.6,-0.81l3.31,0.13l0.39,-0.25l0.63,-1.57l-0.17,-0.5l-3.5,-1.98l1.87,-1.27l-0.13,-1.03l1.98,-0.72l0.18,-0.62l-1.59,-2.06l0.81,-0.82l9.23,-1.18l1.33,-0.88l6.18,-1.26l2.26,-1.42l4.08,0.68l0.73,3.33l0.51,0.3l2.48,-0.8l2.79,1.02l-0.17,1.56l0.43,0.44l2.55,-0.24l4.89,-2.53l0.03,0.32l3.15,2.61l5.56,8.47l0.65,0.02l1.12,-1.46l3.15,1.74l3.76,-0.78l1.15,0.49l1.14,1.8l1.84,0.76l0.99,1.29l3.35,-0.25l1.02,1.52l-1.6,1.81l-1.93,0.28l-0.34,0.38l-0.11,3.05l-1.13,1.16l-4.75,-1.0l-0.46,0.27l-1.76,5.47l-1.1,0.59l-4.91,1.23l-0.27,0.54l2.1,4.97l-1.37,0.63l-0.23,0.41l0.13,1.13l-0.88,-0.25l-1.42,-1.13l-7.89,-0.4l-0.92,0.31l-3.73,-1.22l-1.42,0.63l-0.53,1.66l-3.72,-0.94l-1.85,0.43l-0.76,1.4l-4.65,2.62l-1.13,2.08l-0.44,0.01l-0.92,-1.4l-2.87,-0.09l-0.45,-2.14l-0.38,-0.32l-0.8,-0.01l0.0,-2.96l-3.0,-2.22l-7.31,0.58l-2.35,-2.68l-6.71,-3.69l-6.45,1.83l-0.29,0.39l0.1,10.85l-0.7,0.08l-1.62,-2.17l-1.83,-0.96l-3.11,0.59l-0.64,0.51Z", "name": "Kazakhstan"}, "SA": {"path": "M537.53,210.34l2.0,0.24l0.9,1.32l1.49,-0.06l0.87,2.08l1.29,0.76l0.51,0.99l1.56,1.03l-0.1,1.9l0.32,0.9l1.58,2.47l0.76,0.53l0.7,-0.04l1.68,4.23l7.53,1.33l0.51,-0.29l0.77,1.25l-1.55,4.87l-7.29,2.52l-7.3,1.03l-2.34,1.17l-1.88,2.74l-0.76,0.28l-0.82,-0.78l-0.91,0.12l-2.88,-0.51l-3.51,0.25l-0.86,-0.56l-0.57,0.15l-0.66,1.27l0.16,1.11l-0.43,0.32l-0.93,-1.4l-0.33,-1.16l-1.23,-0.88l-1.27,-2.06l-0.78,-2.22l-1.73,-1.79l-1.14,-0.48l-1.54,-2.31l-0.21,-3.41l-1.44,-2.93l-1.27,-1.16l-1.33,-0.57l-1.31,-3.37l-0.77,-0.67l-0.97,-1.97l-2.8,-4.03l-1.06,-0.17l0.37,-1.96l0.2,-0.72l2.74,0.3l1.08,-0.84l0.6,-0.94l1.74,-0.35l0.65,-1.03l0.71,-0.4l0.1,-0.62l-2.06,-2.28l4.39,-1.22l0.48,-0.37l2.77,0.69l3.66,1.9l7.03,5.5l4.87,0.3Z", "name": "Saudi Arabia"}, "SE": {"path": "M480.22,89.3l-4.03,1.17l-2.43,2.86l0.26,2.57l-8.77,6.64l-1.78,5.79l1.78,2.68l2.22,1.96l-2.07,3.77l-2.72,1.13l-0.95,6.04l-1.29,3.01l-2.74,-0.31l-0.4,0.22l-1.31,2.59l-2.34,0.13l-0.75,-3.09l-2.08,-4.03l-1.83,-4.96l1.0,-1.93l2.14,-2.7l0.83,-4.45l-1.6,-2.17l-0.15,-4.94l1.48,-3.39l2.58,-0.15l0.87,-1.59l-0.78,-1.57l3.76,-5.59l4.04,-7.48l2.17,0.01l0.39,-0.29l0.57,-2.07l4.37,0.64l0.46,-0.34l0.33,-2.56l1.1,-0.13l6.94,4.87l0.06,6.32l0.66,1.36Z", "name": "Sweden"}, "SD": {"path": "M505.98,259.4l-0.34,-0.77l-1.17,-0.9l-0.26,-1.61l0.29,-1.81l-0.34,-0.46l-1.16,-0.17l-0.54,0.59l-1.23,0.11l-0.28,0.65l0.53,0.65l0.17,1.22l-2.44,3.0l-0.96,0.19l-2.39,-1.4l-0.95,0.52l-0.38,0.78l-1.11,0.41l-0.29,0.5l-1.94,0.0l-0.54,-0.52l-1.81,-0.09l-0.95,0.4l-2.45,-2.35l-2.07,0.54l-0.73,1.26l-0.6,2.1l-1.25,0.58l-0.75,-0.62l0.27,-2.65l-1.48,-1.78l-0.22,-1.48l-0.92,-0.96l-0.02,-1.29l-0.57,-1.16l-0.68,-0.16l0.69,-1.29l-0.18,-1.14l0.65,-0.62l0.03,-0.55l-0.36,-0.41l1.55,-2.97l1.91,0.16l0.43,-0.4l-0.1,-10.94l2.49,-0.01l0.4,-0.4l-0.0,-4.82l29.02,0.0l0.64,2.04l-0.49,0.66l0.36,2.69l0.93,3.16l2.12,1.55l-0.89,1.04l-1.72,0.39l-0.98,0.9l-1.43,5.65l0.24,1.15l-0.38,2.06l-0.96,2.38l-1.53,1.31l-1.32,2.91l-1.22,0.86l-0.37,1.34Z", "name": "Sudan"}, "DO": {"path": "M241.8,239.2l0.05,-0.65l-0.46,-0.73l0.42,-0.44l0.19,-1.0l-0.09,-1.53l1.66,0.01l1.99,0.63l0.33,0.67l1.28,0.19l0.33,0.76l1.0,0.08l0.8,0.62l-0.45,0.51l-1.13,-0.47l-1.88,-0.01l-1.27,0.59l-0.75,-0.55l-1.01,0.54l-0.79,1.4l-0.23,-0.61Z", "name": "Dominican Rep."}, "DJ": {"path": "M528.43,256.18l-0.45,0.66l-0.58,-0.25l-1.51,0.13l-0.18,-1.01l1.45,-1.95l0.83,0.17l0.77,-0.44l0.2,1.0l-1.2,0.51l-0.06,0.7l0.73,0.47Z", "name": "Djibouti"}, "DK": {"path": "M452.28,129.07l-1.19,2.24l-2.13,-1.6l-0.23,-0.95l2.98,-0.95l0.57,1.26ZM447.74,126.31l-0.26,0.57l-0.88,-0.07l-1.8,2.53l0.48,1.69l-1.09,0.36l-1.61,-0.39l-0.89,-1.69l-0.07,-3.43l0.96,-1.73l2.02,-0.2l1.09,-1.07l1.33,-0.67l-0.05,1.06l-0.73,1.41l0.3,1.0l1.2,0.64Z", "name": "Denmark"}, "DE": {"path": "M453.14,155.55l-0.55,-0.36l-1.2,-0.1l-1.87,0.57l-2.13,-0.13l-0.56,0.63l-0.86,-0.6l-0.96,0.09l-2.57,-0.93l-0.85,0.67l-1.47,-0.02l0.24,-1.75l1.23,-2.14l-0.28,-0.59l-3.52,-0.58l-0.92,-0.66l0.12,-1.2l-0.48,-0.88l0.27,-2.17l-0.37,-3.03l1.41,-0.22l0.63,-1.26l0.66,-3.19l-0.41,-1.18l0.26,-0.39l1.66,-0.15l0.33,0.54l0.62,0.07l1.7,-1.69l-0.54,-3.02l1.37,0.33l1.31,-0.37l0.31,1.18l2.25,0.71l-0.02,0.92l0.5,0.4l2.55,-0.65l1.34,-0.87l2.57,1.24l1.06,0.98l0.48,1.44l-0.57,0.74l-0.0,0.48l0.87,1.15l0.57,1.64l-0.14,1.29l0.82,1.7l-1.5,-0.07l-0.56,0.57l-4.47,2.15l-0.22,0.54l0.68,2.26l2.58,2.16l-0.66,1.11l-0.79,0.36l-0.23,0.43l0.32,1.87Z", "name": "Germany"}, "YE": {"path": "M528.27,246.72l0.26,-0.42l-0.22,-1.01l0.19,-1.5l0.92,-0.69l-0.07,-1.35l0.39,-0.75l1.01,0.47l3.34,-0.27l3.76,0.41l0.95,0.81l1.36,-0.58l1.74,-2.62l2.18,-1.09l6.86,-0.94l2.48,5.41l-1.64,0.76l-0.56,1.9l-6.23,2.16l-2.29,1.8l-1.93,0.05l-1.41,1.02l-4.24,0.74l-1.72,1.49l-3.28,0.19l-0.52,-1.18l0.02,-1.51l-1.34,-3.29Z", "name": "Yemen"}, "DZ": {"path": "M441.46,188.44l-0.32,1.07l0.39,2.64l-0.54,2.16l-1.58,1.82l0.37,2.39l1.91,1.55l0.18,0.8l1.42,1.03l1.84,7.23l0.12,1.16l-0.57,5.0l0.2,1.51l-0.87,0.99l-0.02,0.51l1.41,1.86l0.14,1.2l0.89,1.48l0.5,0.16l0.98,-0.41l1.73,1.08l0.82,1.23l-8.22,4.81l-7.23,5.11l-3.43,1.13l-2.3,0.21l-0.28,-1.59l-2.56,-1.09l-0.67,-1.25l-26.12,-17.86l0.01,-3.47l3.77,-1.88l2.44,-0.41l2.12,-0.75l1.08,-1.42l2.81,-1.05l0.35,-2.08l1.33,-0.29l1.04,-0.94l3.47,-0.69l0.46,-1.08l-0.1,-0.45l-0.58,-0.52l-0.82,-2.81l-0.19,-1.83l-0.78,-1.49l2.03,-1.31l2.63,-0.48l1.7,-1.22l2.31,-0.84l8.24,-0.73l1.49,0.38l2.28,-1.1l2.46,-0.02l0.92,0.6l1.35,-0.05Z", "name": "Algeria"}, "US": {"path": "M892.72,99.2l1.31,0.53l1.41,-0.37l1.89,0.98l1.89,0.42l-1.32,0.58l-2.9,-1.53l-2.08,0.22l-0.26,-0.15l0.07,-0.67ZM183.22,150.47l0.37,1.47l1.12,0.85l4.23,0.7l2.39,0.98l2.17,-0.38l1.85,0.5l-1.55,0.65l-3.49,2.61l-0.16,0.77l0.5,0.39l2.33,-0.61l1.77,1.02l5.15,-2.4l-0.31,0.65l0.25,0.56l1.36,0.38l1.71,1.16l4.7,-0.88l0.67,0.85l1.31,0.21l0.58,0.58l-1.34,0.17l-2.18,-0.32l-3.6,0.89l-2.71,3.25l0.35,0.9l0.59,-0.0l0.55,-0.6l-1.36,4.65l0.29,3.09l0.67,1.58l0.61,0.45l1.77,-0.44l1.6,-1.96l0.14,-2.21l-0.82,-1.96l0.11,-1.13l1.19,-2.37l0.44,-0.33l0.48,0.75l0.4,-0.29l0.4,-1.37l0.6,-0.47l0.24,-0.8l1.69,0.49l1.65,1.08l-0.03,2.37l-1.27,1.13l-0.0,1.13l0.87,0.36l1.66,-1.29l0.5,0.17l0.5,2.6l-2.49,3.75l0.17,0.61l1.54,0.62l1.48,0.17l1.92,-0.44l4.72,-2.15l2.16,-1.8l-0.05,-1.24l0.75,-0.22l3.92,0.36l2.12,-1.05l0.21,-0.4l-0.28,-1.48l3.27,-2.4l8.32,-0.02l0.56,-0.82l1.9,-0.77l0.93,-1.51l0.74,-2.37l1.58,-1.98l0.92,0.62l1.47,-0.47l0.8,0.66l-0.0,4.09l1.96,2.6l-2.34,1.31l-5.37,2.09l-1.83,2.72l0.02,1.79l0.83,1.59l0.54,0.23l-6.19,0.94l-2.2,0.89l-0.23,0.48l0.45,0.29l2.99,-0.46l-2.19,0.56l-1.13,0.0l-0.15,-0.32l-0.48,0.08l-0.76,0.82l0.22,0.67l0.32,0.06l-0.41,1.62l-1.27,1.58l-1.48,-1.07l-0.49,-0.04l-0.16,0.46l0.52,1.58l0.61,0.59l0.03,0.79l-0.95,1.38l-1.21,-1.22l-0.27,-2.27l-0.35,-0.35l-0.42,0.25l-0.48,1.27l0.33,1.41l-0.97,-0.27l-0.48,0.24l0.18,0.5l1.52,0.83l0.1,2.52l0.79,0.51l0.52,3.42l-1.42,1.88l-2.47,0.8l-1.71,1.66l-1.31,0.25l-1.27,1.03l-0.43,0.99l-2.69,1.78l-2.64,3.03l-0.45,2.12l0.45,2.08l0.85,2.38l1.09,1.9l0.04,1.2l1.16,3.06l-0.18,2.69l-0.55,1.43l-0.47,0.21l-0.89,-0.23l-0.49,-1.18l-0.87,-0.56l-2.75,-5.16l0.48,-1.68l-0.72,-1.78l-2.01,-2.38l-1.12,-0.53l-2.72,1.18l-1.47,-1.35l-1.57,-0.68l-2.99,0.31l-2.17,-0.3l-2.0,0.19l-1.15,0.46l-0.19,0.58l0.39,0.63l0.14,1.34l-0.84,-0.2l-0.84,0.46l-1.58,-0.07l-2.08,-1.44l-2.09,0.33l-1.91,-0.62l-3.73,0.84l-2.39,2.07l-2.54,1.22l-1.45,1.41l-0.61,1.38l0.34,3.71l-0.29,0.02l-3.5,-1.33l-1.25,-3.11l-1.44,-1.5l-2.24,-3.56l-1.76,-1.09l-2.27,-0.01l-1.71,2.07l-1.76,-0.69l-1.16,-0.74l-1.52,-2.98l-3.93,-3.16l-4.34,-0.0l-0.4,0.4l-0.0,0.74l-6.5,0.02l-9.02,-3.14l-0.34,-0.71l-5.7,0.49l-0.43,-1.29l-1.62,-1.61l-1.14,-0.38l-0.55,-0.88l-1.28,-0.13l-1.01,-0.77l-2.22,-0.27l-0.43,-0.3l-0.36,-1.58l-2.4,-2.83l-2.01,-3.85l-0.06,-0.9l-2.92,-3.26l-0.33,-2.29l-1.3,-1.66l0.52,-2.37l-0.09,-2.57l-0.78,-2.3l0.95,-2.82l0.61,-5.68l-0.47,-4.27l-1.46,-4.08l3.19,0.79l1.26,2.83l0.69,0.08l0.69,-1.14l-1.1,-4.79l68.76,-0.0l0.4,-0.4l0.14,-0.86ZM32.44,67.52l1.73,1.97l0.55,0.05l0.99,-0.79l3.65,0.24l-0.09,0.62l0.32,0.45l3.83,0.77l2.61,-0.43l5.19,1.4l4.84,0.43l1.89,0.57l3.42,-0.7l6.14,1.87l-0.03,38.06l0.38,0.4l2.39,0.11l2.31,0.98l3.9,3.99l0.55,0.04l2.4,-2.03l2.16,-1.04l1.2,1.71l3.95,3.14l4.09,6.63l4.2,2.29l0.06,1.83l-1.02,1.23l-1.16,-1.08l-2.04,-1.03l-0.67,-2.89l-3.28,-3.03l-1.65,-3.57l-6.35,-0.32l-2.82,-1.01l-5.26,-3.85l-6.77,-2.04l-3.53,0.3l-4.81,-1.69l-3.25,-1.63l-2.78,0.8l-0.28,0.46l0.44,2.21l-3.91,0.96l-2.26,1.27l-2.3,0.65l-0.27,-1.65l1.05,-3.42l2.49,-1.09l0.16,-0.6l-0.69,-0.96l-0.55,-0.1l-3.19,2.12l-1.78,2.56l-3.55,2.61l-0.04,0.61l1.56,1.52l-2.07,2.29l-5.11,2.57l-0.77,1.66l-3.76,1.77l-0.92,1.73l-2.69,1.38l-1.81,-0.22l-6.95,3.32l-3.97,0.91l4.85,-2.5l2.59,-1.86l3.26,-0.52l1.19,-1.4l3.42,-2.1l2.59,-2.27l0.42,-2.68l1.23,-2.1l-0.04,-0.46l-0.45,-0.11l-2.68,1.03l-0.63,-0.49l-0.53,0.03l-1.05,1.04l-1.36,-1.54l-0.66,0.08l-0.32,0.62l-0.58,-1.14l-0.56,-0.16l-2.41,1.42l-1.07,-0.0l-0.17,-1.75l0.3,-1.71l-1.61,-1.33l-3.41,0.59l-1.96,-1.63l-1.57,-0.84l-0.15,-2.21l-1.7,-1.43l0.82,-1.88l1.99,-2.12l0.88,-1.92l1.71,-0.24l2.04,0.51l1.87,-1.77l1.91,0.25l1.91,-1.23l0.17,-0.43l-0.47,-1.82l-1.07,-0.7l1.39,-1.17l0.12,-0.45l-0.39,-0.26l-1.65,0.07l-2.66,0.88l-0.75,0.78l-1.92,-0.8l-3.46,0.44l-3.44,-0.91l-1.06,-1.61l-2.65,-1.99l2.91,-1.43l5.5,-2.0l1.52,0.0l-0.26,1.62l0.41,0.46l5.29,-0.16l0.3,-0.65l-2.03,-2.59l-3.14,-1.68l-1.79,-2.12l-2.4,-1.83l-3.09,-1.24l1.04,-1.69l4.23,-0.14l3.36,-2.07l0.73,-2.27l2.39,-1.99l2.42,-0.52l4.65,-1.97l2.46,0.23l3.71,-2.35l3.5,0.89ZM37.6,123.41l-2.25,1.23l-0.95,-0.69l-0.29,-1.24l3.21,-1.63l1.42,0.21l0.67,0.7l-1.8,1.42ZM31.06,234.03l0.98,0.47l0.74,0.87l-1.77,1.07l-0.44,-1.53l0.49,-0.89ZM29.34,232.07l0.18,0.05l0.08,0.05l-0.16,0.03l-0.11,-0.14ZM25.16,230.17l0.05,-0.03l0.18,0.22l-0.13,-0.01l-0.1,-0.18ZM5.89,113.26l-1.08,0.41l-2.21,-1.12l1.53,-0.4l1.62,0.28l0.14,0.83Z", "name": "United States"}, "UY": {"path": "M286.85,372.74l-0.92,1.5l-2.59,1.44l-1.69,-0.52l-1.42,0.26l-2.39,-1.19l-1.52,0.08l-1.27,-1.3l0.16,-1.5l0.56,-0.79l-0.02,-2.73l1.21,-4.74l1.19,-0.21l2.37,2.0l1.08,0.03l4.36,3.17l1.22,1.6l-0.96,1.5l0.61,1.4Z", "name": "Uruguay"}, "LB": {"path": "M510.37,198.01l-0.88,0.51l1.82,-3.54l0.62,0.08l0.22,0.61l-1.13,0.88l-0.65,1.47Z", "name": "Lebanon"}, "LA": {"path": "M689.54,248.53l-1.76,-0.74l-0.49,0.15l-0.94,1.46l-1.32,-0.64l0.62,-0.98l0.11,-2.17l-2.04,-2.42l-0.25,-2.65l-1.9,-2.1l-2.15,-0.31l-0.78,0.91l-1.12,0.06l-1.05,-0.4l-2.06,1.2l-0.04,-1.59l0.61,-2.68l-0.36,-0.49l-1.35,-0.1l-0.11,-1.23l-0.96,-0.88l1.96,-1.89l0.39,0.36l1.33,0.07l0.42,-0.45l-0.34,-2.66l0.7,-0.21l1.28,1.81l1.11,2.35l0.36,0.23l2.82,0.02l0.71,1.67l-1.39,0.65l-0.72,0.93l0.13,0.6l2.91,1.51l3.6,5.25l1.88,1.78l0.56,1.62l-0.35,1.96Z", "name": "Lao PDR"}, "TW": {"path": "M724.01,226.68l-0.74,1.48l-0.9,-1.52l-0.25,-1.74l1.38,-2.44l1.73,-1.74l0.64,0.44l-1.85,5.52Z", "name": "Taiwan"}, "TT": {"path": "M266.64,259.32l0.28,-1.16l1.13,-0.22l-0.06,1.2l-1.35,0.18Z", "name": "Trinidad and Tobago"}, "TR": {"path": "M513.21,175.47l3.64,1.17l3.05,-0.44l2.1,0.26l3.11,-1.56l2.46,-0.13l2.19,1.33l0.33,0.82l-0.22,1.33l0.25,0.44l2.28,1.13l-1.17,0.57l-0.21,0.45l0.75,3.2l-0.41,1.16l1.13,1.92l-0.55,0.22l-0.9,-0.67l-2.91,-0.37l-1.24,0.46l-4.23,0.41l-2.81,1.05l-1.91,0.01l-1.52,-0.53l-2.58,0.75l-0.66,-0.45l-0.62,0.3l-0.12,1.45l-0.89,0.84l-0.47,-0.67l0.79,-1.3l-0.41,-0.2l-1.43,0.23l-2.0,-0.63l-2.02,1.65l-3.51,0.3l-2.13,-1.53l-2.7,-0.1l-0.86,1.24l-1.38,0.27l-2.29,-1.44l-2.71,-0.01l-1.37,-2.65l-1.68,-1.52l1.07,-1.99l-0.09,-0.49l-1.27,-1.12l2.37,-2.41l3.7,-0.11l1.28,-2.24l4.49,0.37l3.21,-1.97l2.81,-0.82l3.99,-0.06l4.29,2.07ZM488.79,176.72l-1.72,1.31l-0.5,-0.88l1.37,-2.57l-0.7,-0.85l1.7,-0.63l1.8,0.34l0.46,1.17l1.76,0.78l-2.87,0.32l-1.3,1.01Z", "name": "Turkey"}, "LK": {"path": "M624.16,268.99l-1.82,0.48l-0.99,-1.67l-0.42,-3.46l0.95,-3.43l1.21,0.98l2.26,4.19l-0.34,2.33l-0.85,0.58Z", "name": "Sri Lanka"}, "LV": {"path": "M489.16,122.85l0.96,0.66l0.22,1.65l0.68,1.76l-3.65,1.7l-2.23,-1.58l-1.29,-0.26l-0.68,-0.77l-2.42,0.34l-4.16,-0.23l-2.47,0.9l0.06,-1.98l1.13,-2.06l1.95,-1.02l2.12,2.58l2.01,-0.07l0.38,-0.33l0.44,-2.52l1.76,-0.53l3.06,1.7l2.15,0.07Z", "name": "Latvia"}, "LT": {"path": "M486.93,129.3l0.17,1.12l-1.81,0.98l-0.72,2.02l-2.47,1.18l-2.1,-0.02l-0.73,-1.05l-1.06,-0.3l-0.09,-1.87l-3.56,-1.13l-0.43,-2.36l2.48,-0.94l4.12,0.22l2.25,-0.31l0.52,0.69l1.24,0.21l2.19,1.56Z", "name": "Lithuania"}, "LU": {"path": "M436.08,149.45l-0.48,-0.07l0.3,-1.28l0.27,0.4l-0.09,0.96Z", "name": "Luxembourg"}, "LR": {"path": "M399.36,265.97l0.18,1.54l-0.48,0.99l0.08,0.47l2.47,1.8l-0.33,2.8l-2.65,-1.13l-5.78,-4.61l0.58,-1.32l2.1,-2.33l0.86,-0.22l0.77,1.14l-0.14,0.85l0.59,0.87l1.0,0.14l0.76,-0.99Z", "name": "Liberia"}, "LS": {"path": "M491.06,363.48l-0.49,0.15l-1.49,-1.67l1.1,-1.43l2.19,-1.44l1.51,1.27l-0.98,1.82l-1.23,0.38l-0.62,0.93Z", "name": "Lesotho"}, "TH": {"path": "M670.27,255.86l-1.41,3.87l0.15,2.0l0.38,0.36l1.38,0.07l0.9,2.04l0.55,2.34l1.4,1.44l1.61,0.38l0.96,0.97l-0.5,0.64l-1.1,0.2l-0.34,-1.18l-2.04,-1.1l-0.63,0.23l-0.63,-0.62l-0.48,-1.3l-2.56,-2.63l-0.73,0.41l0.95,-3.89l2.16,-4.22ZM670.67,254.77l-0.92,-2.18l-0.26,-2.61l-2.14,-3.06l0.71,-0.49l0.89,-2.59l-3.61,-5.45l0.87,-0.51l1.05,-2.58l1.74,-0.18l2.6,-1.59l0.76,0.56l0.13,1.39l0.37,0.36l1.23,0.09l-0.51,2.28l0.05,2.42l0.6,0.34l2.43,-1.42l0.77,0.39l1.47,-0.07l0.71,-0.88l1.48,0.14l1.71,1.88l0.25,2.65l1.92,2.11l-0.1,1.89l-0.61,0.86l-2.22,-0.33l-3.5,0.64l-1.6,2.12l0.36,2.58l-1.51,-0.79l-1.84,-0.01l0.28,-1.52l-0.4,-0.47l-2.21,0.01l-0.4,0.37l-0.19,2.74l-0.34,0.93Z", "name": "Thailand"}, "TF": {"path": "M596.68,420.38l-3.2,0.18l-0.05,-1.26l0.39,-1.41l1.3,0.78l2.08,0.35l-0.52,1.36Z", "name": "Fr. S. Antarctic Lands"}, "TG": {"path": "M422.7,257.63l-0.09,1.23l1.53,1.52l0.08,1.09l0.5,0.65l-0.11,5.62l0.49,1.47l-1.31,0.35l-1.02,-2.13l-0.18,-1.12l0.53,-2.19l-0.63,-1.16l-0.22,-3.68l-1.01,-1.4l0.07,-0.28l1.37,0.03Z", "name": "Togo"}, "TD": {"path": "M480.25,235.49l0.12,9.57l-2.1,0.05l-1.14,1.89l-0.69,1.63l0.34,0.73l-0.66,0.91l0.24,0.89l-0.86,1.95l0.45,0.5l0.6,-0.1l0.34,0.64l0.03,1.38l0.9,1.04l-1.45,0.43l-1.27,1.03l-1.83,2.76l-2.16,1.07l-2.31,-0.15l-0.86,0.25l-0.26,0.49l0.17,0.61l-2.11,1.68l-2.85,0.87l-1.09,-0.57l-0.73,0.66l-1.12,0.1l-1.1,-3.12l-1.25,-0.64l-1.22,-1.22l0.29,-0.64l3.01,0.04l0.35,-0.6l-1.3,-2.2l-0.08,-3.31l-0.97,-1.66l0.22,-1.04l-0.38,-0.48l-1.22,-0.04l0.0,-1.25l-0.98,-1.07l0.96,-3.01l3.25,-2.65l0.13,-3.33l0.95,-5.18l0.52,-1.07l-0.1,-0.48l-0.91,-0.78l-0.2,-0.96l-0.8,-0.58l-0.55,-3.65l2.1,-1.2l19.57,9.83Z", "name": "Chad"}, "LY": {"path": "M483.48,203.15l-0.75,1.1l0.29,1.39l-0.6,1.83l0.73,2.14l0.0,24.12l-2.48,0.01l-0.41,0.85l-19.41,-9.76l-4.41,2.28l-1.37,-1.33l-3.82,-1.1l-1.14,-1.65l-1.98,-1.23l-1.22,0.32l-0.66,-1.11l-0.17,-1.26l-1.28,-1.69l0.87,-1.19l-0.07,-4.34l0.43,-2.27l-0.86,-3.45l1.13,-0.76l0.22,-1.16l-0.2,-1.03l3.48,-2.61l0.29,-1.94l2.45,0.8l1.18,-0.21l1.98,0.44l3.15,1.18l1.37,2.54l5.72,1.67l2.64,1.35l1.61,-0.72l1.29,-1.34l-0.44,-2.34l0.66,-1.13l1.67,-1.21l1.57,-0.35l3.14,0.53l1.08,1.28l3.99,0.78l0.36,0.54Z", "name": "Libya"}, "AE": {"path": "M550.76,223.97l1.88,-0.4l3.84,0.02l4.78,-4.75l0.19,0.36l0.26,1.58l-0.81,0.01l-0.39,0.35l-0.08,2.04l-0.81,0.63l-0.01,0.96l-0.66,0.99l-0.39,1.41l-7.08,-1.25l-0.7,-1.96Z", "name": "United Arab Emirates"}, "VE": {"path": "M240.68,256.69l0.53,0.75l-0.02,1.06l-1.07,1.78l0.95,2.0l0.42,0.22l1.4,-0.44l0.56,-1.83l-0.77,-1.17l-0.1,-1.47l2.82,-0.93l0.26,-0.49l-0.28,-0.96l0.3,-0.28l0.66,1.31l1.96,0.26l1.4,1.22l0.08,0.68l0.39,0.35l4.81,-0.22l1.49,1.11l1.92,0.31l1.67,-0.84l0.22,-0.6l3.44,-0.14l-0.17,0.55l0.86,1.19l2.19,0.35l1.67,1.1l0.37,1.86l0.41,0.32l1.55,0.17l-1.66,1.35l-0.22,0.92l0.65,0.97l-1.67,0.54l-0.3,0.4l0.04,0.99l-0.56,0.57l-0.01,0.55l1.85,2.27l-0.66,0.69l-4.47,1.29l-0.72,0.54l-3.69,-0.9l-0.71,0.27l-0.02,0.7l0.91,0.53l-0.08,1.54l0.35,1.58l0.35,0.31l1.66,0.17l-1.3,0.52l-0.48,1.13l-2.68,0.91l-0.6,0.77l-1.57,0.13l-1.17,-1.13l-0.8,-2.52l-1.25,-1.26l1.02,-1.23l-1.29,-2.95l0.18,-1.62l1.0,-2.21l-0.2,-0.49l-1.14,-0.46l-4.02,0.36l-1.82,-2.1l-1.57,-0.33l-2.99,0.22l-1.06,-0.97l0.25,-1.23l-0.2,-1.01l-0.59,-0.69l-0.29,-1.06l-1.08,-0.39l0.78,-2.79l1.9,-2.11Z", "name": "Venezuela"}, "AF": {"path": "M600.7,188.88l-1.57,1.3l-0.1,0.48l0.8,2.31l-1.09,1.04l-0.03,1.27l-0.48,0.71l-2.16,-0.08l-0.37,0.59l0.78,1.48l-1.38,0.69l-1.06,1.69l0.06,1.7l-0.65,0.52l-0.91,-0.21l-1.91,0.36l-0.48,0.77l-1.88,0.13l-1.4,1.56l-0.18,2.32l-2.91,1.02l-1.65,-0.23l-0.71,0.55l-1.41,-0.3l-2.41,0.39l-3.52,-1.17l1.96,-2.35l-0.21,-1.78l-0.3,-0.34l-1.63,-0.4l-0.19,-1.58l-0.75,-2.03l0.95,-1.36l-0.19,-0.6l-0.73,-0.28l1.47,-4.8l2.14,0.9l2.12,-0.36l0.74,-1.34l1.77,-0.39l1.54,-0.92l0.63,-2.31l1.87,-0.5l0.49,-0.81l0.94,0.56l2.13,0.11l2.55,0.92l1.95,-0.83l0.65,0.43l0.56,-0.13l0.69,-1.12l1.57,-0.08l0.72,-1.66l0.79,-0.74l0.8,0.39l-0.17,0.56l0.71,0.58l-0.08,2.39l1.11,0.95ZM601.37,188.71l1.73,-0.71l1.43,-1.18l4.03,0.35l-2.23,0.74l-4.95,0.8Z", "name": "Afghanistan"}, "IQ": {"path": "M530.82,187.47l0.79,0.66l1.26,-0.28l1.46,3.08l1.63,0.94l0.14,1.23l-1.22,1.05l-0.53,2.52l1.73,2.67l3.12,1.62l1.15,1.88l-0.38,1.85l0.39,0.48l0.41,-0.0l0.02,1.07l0.76,0.94l-2.47,-0.1l-1.71,2.44l-4.31,-0.2l-7.02,-5.48l-3.73,-1.94l-2.88,-0.73l-0.85,-2.87l5.45,-3.02l0.95,-3.43l-0.19,-1.96l1.27,-0.7l1.22,-1.7l0.87,-0.36l2.69,0.34Z", "name": "Iraq"}, "IS": {"path": "M384.14,88.06l-0.37,2.61l2.54,2.51l-2.9,2.75l-9.19,3.4l-9.25,-1.66l1.7,-1.22l-0.1,-0.7l-4.05,-1.47l2.96,-0.53l0.33,-0.43l-0.11,-1.2l-0.33,-0.36l-4.67,-0.85l1.28,-2.04l3.45,-0.56l3.77,2.72l0.44,0.02l3.64,-2.16l3.3,1.08l3.98,-2.16l3.58,0.26Z", "name": "Iceland"}, "IR": {"path": "M533.43,187.16l-1.27,-2.15l0.42,-0.98l-0.71,-3.04l1.03,-0.5l0.33,0.83l1.26,1.35l2.05,0.51l1.11,-0.16l2.89,-2.11l0.62,-0.14l0.39,0.46l-0.72,1.2l0.06,0.49l1.56,1.53l0.65,0.04l0.67,1.81l2.56,0.83l1.87,1.48l3.69,0.49l3.91,-0.76l0.47,-0.73l2.17,-0.6l1.66,-1.54l1.51,0.08l1.18,-0.53l1.59,0.24l2.83,1.48l1.88,0.3l2.77,2.47l1.77,0.18l0.18,1.99l-1.68,5.49l0.24,0.5l0.61,0.23l-0.82,1.48l0.8,2.18l0.19,1.71l0.3,0.34l1.63,0.4l0.15,1.32l-2.15,2.35l-0.01,0.53l2.21,3.03l2.34,1.24l0.06,2.14l1.24,0.72l0.11,0.69l-3.31,1.27l-1.08,3.03l-9.68,-1.68l-0.99,-3.05l-1.43,-0.73l-2.17,0.46l-2.47,1.26l-2.83,-0.82l-2.46,-2.02l-2.41,-0.8l-3.42,-6.06l-0.48,-0.2l-1.18,0.39l-1.44,-0.82l-0.5,0.08l-0.65,0.74l-0.97,-1.01l-0.02,-1.31l-0.71,-0.39l0.26,-1.81l-1.29,-2.11l-3.13,-1.63l-1.58,-2.43l0.5,-1.9l1.31,-1.26l-0.19,-1.66l-1.74,-1.1l-1.57,-3.3Z", "name": "Iran"}, "AM": {"path": "M536.99,182.33l-0.28,0.03l-1.23,-2.13l-0.93,0.01l-0.62,-0.66l-0.69,-0.07l-0.96,-0.81l-1.56,-0.62l0.19,-1.12l-0.26,-0.79l2.72,-0.36l1.09,1.01l-0.17,0.92l1.02,0.78l-0.47,0.62l0.08,0.56l2.04,1.23l0.04,1.4Z", "name": "Armenia"}, "AL": {"path": "M470.32,171.8l0.74,0.03l0.92,0.89l-0.17,1.95l0.36,1.28l1.01,0.82l-1.82,2.83l-0.19,-0.61l-1.25,-0.89l-0.18,-1.2l0.53,-2.82l-0.54,-1.47l0.6,-0.83Z", "name": "Albania"}, "AO": {"path": "M461.55,300.03l1.26,3.15l1.94,2.36l2.47,-0.53l1.25,0.32l0.44,-0.18l0.93,-1.92l1.31,-0.08l0.41,-0.44l0.47,-0.0l-0.1,0.41l0.39,0.49l2.65,-0.02l0.03,1.19l0.48,1.01l-0.34,1.52l0.18,1.55l0.83,1.04l-0.13,2.85l0.54,0.39l3.96,-0.41l-0.1,1.79l0.39,1.05l-0.24,1.43l-4.7,-0.03l-0.4,0.39l-0.12,8.13l2.92,3.49l-3.83,0.88l-5.89,-0.36l-1.88,-1.24l-10.47,0.22l-1.3,-1.01l-1.85,-0.16l-2.4,0.77l-0.15,-1.06l0.33,-2.16l1.0,-3.45l1.35,-3.2l2.24,-2.8l0.33,-2.06l-0.13,-1.53l-0.8,-1.08l-1.21,-2.87l0.87,-1.62l-1.27,-4.12l-1.17,-1.53l2.47,-0.63l7.03,0.03ZM451.71,298.87l-0.47,-1.25l1.25,-1.11l0.32,0.3l-0.99,1.03l-0.12,1.03Z", "name": "Angola"}, "AR": {"path": "M249.29,428.93l-2.33,-0.52l-5.83,-0.43l-0.89,-1.66l0.05,-2.37l-0.45,-0.4l-1.43,0.18l-0.67,-0.91l-0.2,-3.13l1.88,-1.47l0.79,-2.04l-0.25,-1.7l1.3,-2.68l0.91,-4.15l-0.22,-1.69l0.85,-0.45l0.2,-0.44l-0.27,-1.16l-0.98,-0.68l0.59,-0.92l-0.05,-0.5l-1.04,-1.07l-0.52,-3.1l0.97,-0.86l-0.42,-3.58l1.2,-5.43l1.38,-0.98l0.16,-0.43l-0.75,-2.79l-0.01,-2.43l1.78,-1.75l0.06,-2.57l1.43,-2.85l0.01,-2.58l-0.69,-0.74l-1.09,-4.52l1.47,-2.7l-0.18,-2.79l0.85,-2.35l1.59,-2.46l1.73,-1.64l0.05,-0.52l-0.6,-0.84l0.44,-0.85l-0.07,-4.19l2.7,-1.44l0.86,-2.75l-0.21,-0.71l1.76,-2.01l2.9,0.57l1.38,1.78l0.68,-0.08l0.87,-1.87l2.39,0.09l4.95,4.77l2.17,0.49l3.0,1.92l2.47,1.0l0.25,0.82l-2.37,3.93l0.23,0.59l5.39,1.16l2.12,-0.44l2.45,-2.16l0.5,-2.38l0.76,-0.31l0.98,1.2l-0.04,1.8l-3.67,2.51l-2.85,2.66l-3.43,3.88l-1.3,5.07l0.01,2.72l-0.54,0.73l-0.36,3.28l3.14,2.64l-0.16,2.11l1.4,1.11l-0.1,1.09l-2.29,3.52l-3.55,1.49l-4.92,0.6l-2.71,-0.29l-0.43,0.51l0.5,1.65l-0.49,2.1l0.38,1.42l-1.19,0.83l-2.36,0.38l-2.3,-1.04l-1.38,0.83l0.41,3.64l1.69,0.91l1.4,-0.71l0.36,0.76l-2.04,0.86l-2.01,1.89l-0.97,4.63l-2.34,0.1l-2.09,1.78l-0.61,2.75l2.46,2.31l2.17,0.63l-0.7,2.32l-2.83,1.73l-1.73,3.86l-2.17,1.22l-1.16,1.67l0.75,3.76l1.04,1.28ZM256.71,438.88l-2.0,0.15l-1.4,-1.22l-3.82,-0.1l-0.0,-5.83l1.6,3.05l3.26,2.07l3.08,0.78l-0.71,1.1Z", "name": "Argentina"}, "AU": {"path": "M705.8,353.26l0.26,0.04l0.17,-0.47l-0.48,-1.42l0.92,1.11l0.45,0.15l0.27,-0.39l-0.1,-1.56l-1.98,-3.63l1.09,-3.31l-0.24,-1.57l0.34,-0.62l0.38,1.06l0.43,-0.19l0.99,-1.7l1.91,-0.83l1.29,-1.15l1.81,-0.91l0.96,-0.17l0.92,0.26l1.92,-0.95l1.47,-0.28l1.03,-0.8l1.43,0.04l2.78,-0.84l1.36,-1.15l0.71,-1.45l1.41,-1.26l0.3,-2.58l1.27,-1.59l0.78,1.65l0.54,0.19l1.07,-0.51l0.15,-0.6l-0.73,-1.0l0.45,-0.71l0.78,0.39l0.58,-0.3l0.28,-1.82l1.87,-2.14l1.12,-0.39l0.28,-0.58l0.62,0.17l0.53,-0.73l1.87,-0.57l1.65,1.05l1.35,1.48l3.39,0.38l0.43,-0.54l-0.46,-1.23l1.05,-1.79l1.04,-0.61l0.14,-0.55l-0.25,-0.41l0.88,-1.17l1.31,-0.77l1.3,0.27l2.1,-0.48l0.31,-0.4l-0.05,-1.3l-0.92,-0.77l1.48,0.56l1.41,1.07l2.11,0.65l0.81,-0.2l1.4,0.7l1.69,-0.66l0.8,0.19l0.64,-0.33l0.71,0.77l-1.33,1.94l-0.71,0.07l-0.35,0.51l0.24,0.86l-1.52,2.35l0.12,1.05l2.15,1.65l1.97,0.85l3.04,2.36l1.97,0.65l0.55,0.88l2.72,0.85l1.84,-1.1l2.07,-5.97l-0.42,-3.59l0.3,-1.73l0.47,-0.87l-0.31,-0.68l1.09,-3.28l0.46,-0.47l0.4,0.71l0.16,1.51l0.65,0.52l0.16,1.04l0.85,1.21l0.12,2.38l0.9,2.0l0.57,0.18l1.3,-0.78l1.69,1.7l-0.2,1.08l0.53,2.2l0.39,1.3l0.68,0.48l0.6,1.95l-0.19,1.48l0.81,1.76l6.01,3.69l-0.11,0.76l1.38,1.58l0.95,2.77l0.58,0.22l0.72,-0.41l0.8,0.9l0.61,0.01l0.46,2.41l4.81,4.71l0.66,2.02l-0.07,3.31l1.14,2.2l-0.13,2.24l-1.1,3.68l0.03,1.64l-0.47,1.89l-1.05,2.4l-1.9,1.47l-1.72,3.51l-2.38,6.09l-0.24,2.82l-1.14,0.8l-2.85,0.15l-2.31,1.19l-2.51,2.25l-3.09,-1.57l0.3,-1.15l-0.54,-0.47l-1.5,0.63l-2.01,1.94l-7.12,-2.18l-1.48,-1.63l-1.14,-3.74l-1.45,-1.26l-1.81,-0.26l0.56,-1.18l-0.61,-2.1l-0.72,-0.1l-1.14,1.82l-0.9,0.21l0.63,-0.82l0.36,-1.55l0.92,-1.31l-0.13,-2.34l-0.7,-0.22l-2.0,2.34l-1.51,0.93l-0.94,2.01l-1.35,-0.81l-0.02,-1.52l-1.57,-2.04l-1.09,-0.88l0.24,-0.33l-0.14,-0.59l-3.21,-1.69l-1.83,-0.12l-2.54,-1.35l-4.58,0.28l-6.02,1.9l-2.53,-0.13l-2.62,1.41l-2.13,0.63l-1.49,2.6l-3.49,0.31l-2.29,-0.5l-3.48,0.43l-1.6,1.47l-0.81,-0.04l-2.37,1.63l-3.26,-0.1l-3.72,-2.21l0.04,-1.05l1.19,-0.46l0.49,-0.89l0.21,-2.97l-0.28,-1.64l-1.34,-2.86l-0.38,-1.47l0.05,-1.72l-0.95,-1.7l-0.18,-0.97l-1.01,-0.99l-0.29,-1.98l-1.13,-1.75ZM784.92,393.44l2.65,1.02l3.23,-0.96l1.09,0.14l0.15,3.06l-0.85,1.13l-0.17,1.63l-0.87,-0.24l-1.57,1.91l-1.68,-0.18l-1.4,-2.36l-0.37,-2.04l-1.39,-2.51l0.04,-0.8l1.15,0.18Z", "name": "Australia"}, "AT": {"path": "M462.89,152.8l0.04,2.25l-1.07,0.0l-0.33,0.63l0.36,0.51l-1.04,2.13l-2.02,0.07l-1.33,0.7l-5.29,-0.99l-0.47,-0.93l-0.44,-0.21l-2.47,0.55l-0.42,0.51l-3.18,-0.81l0.43,-0.91l1.12,0.78l0.6,-0.17l0.25,-0.58l1.93,0.12l1.86,-0.56l1.0,0.08l0.68,0.57l0.62,-0.15l0.26,-0.77l-0.3,-1.78l0.8,-0.44l0.68,-1.15l1.52,0.85l0.47,-0.06l1.34,-1.25l0.64,-0.17l1.81,0.92l1.28,-0.11l0.7,0.37Z", "name": "Austria"}, "IN": {"path": "M623.34,207.03l-1.24,1.04l-0.97,2.55l0.22,0.51l8.04,3.87l3.42,0.37l1.57,1.38l4.92,0.88l2.18,-0.04l0.38,-0.3l0.29,-1.24l-0.32,-1.64l0.14,-0.87l0.82,-0.31l0.45,2.48l2.28,1.02l1.77,-0.38l4.14,0.1l0.38,-0.36l0.18,-1.66l-0.5,-0.65l1.37,-0.29l2.25,-1.99l2.7,-1.62l1.93,0.62l1.8,-0.98l0.79,1.14l-0.68,0.91l0.26,0.63l2.42,0.36l0.09,0.47l-0.83,0.75l0.13,1.07l-1.52,-0.29l-3.24,1.86l-0.13,1.78l-1.32,2.14l-0.18,1.39l-0.93,1.82l-1.64,-0.5l-0.52,0.37l-0.09,2.63l-0.56,1.11l0.19,0.81l-0.53,0.27l-1.18,-3.73l-1.08,-0.27l-0.38,0.31l-0.24,1.0l-0.66,-0.66l0.54,-1.06l1.22,-0.34l1.15,-2.25l-0.24,-0.56l-1.57,-0.47l-4.34,-0.28l-0.18,-1.56l-0.35,-0.35l-1.11,-0.12l-1.91,-1.12l-0.56,0.17l-0.88,1.82l0.11,0.49l1.36,1.07l-1.09,0.69l-0.69,1.11l0.18,0.56l1.24,0.57l-0.32,1.54l0.85,1.94l0.36,2.01l-0.22,0.59l-4.58,0.52l-0.33,0.42l0.13,1.8l-1.17,1.36l-3.65,1.81l-2.79,3.03l-4.32,3.28l-0.18,1.27l-4.65,1.79l-0.77,2.16l0.64,5.3l-1.06,2.49l-0.01,3.94l-1.24,0.28l-1.14,1.93l0.39,0.84l-1.68,0.53l-1.04,1.83l-0.65,0.47l-2.06,-2.05l-2.1,-6.02l-2.2,-3.64l-1.05,-4.75l-2.29,-3.57l-1.76,-8.2l0.01,-3.11l-0.49,-2.53l-0.55,-0.29l-3.53,1.52l-1.53,-0.27l-2.86,-2.77l0.85,-0.67l0.08,-0.55l-0.74,-1.03l-2.67,-2.06l1.24,-1.32l5.34,0.01l0.39,-0.49l-0.5,-2.29l-1.42,-1.46l-0.27,-1.93l-1.43,-1.2l2.31,-2.37l3.05,0.06l2.62,-2.85l1.6,-2.81l2.4,-2.73l0.07,-2.04l1.97,-1.48l-0.02,-0.65l-1.93,-1.31l-0.82,-1.78l-0.8,-2.21l0.9,-0.89l3.59,0.65l2.92,-0.42l2.33,-2.19l2.31,2.85l-0.24,2.13l0.99,1.59l-0.05,0.82l-1.34,-0.28l-0.47,0.48l0.7,3.06l2.62,1.99l2.99,1.65Z", "name": "India"}, "TZ": {"path": "M495.56,296.42l2.8,-3.12l-0.02,-0.81l-0.64,-1.3l0.68,-0.52l0.14,-1.47l-0.76,-1.25l0.31,-0.11l2.26,0.03l-0.51,2.76l0.76,1.3l0.5,0.12l1.05,-0.53l1.19,-0.12l0.61,0.24l1.43,-0.62l0.1,-0.67l-0.71,-0.62l1.57,-1.7l8.65,4.86l0.32,1.53l3.34,2.33l-1.05,2.8l0.13,1.61l1.63,1.12l-0.6,1.76l-0.01,2.33l1.89,4.03l0.57,0.43l-1.46,1.08l-2.61,0.94l-1.43,-0.04l-1.06,0.77l-2.29,0.36l-2.87,-0.68l-0.83,0.07l-0.63,-0.75l-0.31,-2.78l-1.32,-1.35l-3.25,-0.77l-3.96,-1.58l-1.18,-2.41l-0.32,-1.75l-1.76,-1.49l0.42,-1.05l-0.44,-0.89l0.08,-0.96l-0.46,-0.58l0.06,-0.56Z", "name": "Tanzania"}, "AZ": {"path": "M539.29,175.73l1.33,0.32l1.94,-1.8l2.3,3.34l1.43,0.43l-1.26,0.15l-0.35,0.32l-0.8,3.14l-0.99,0.96l0.05,1.11l-1.26,-1.13l0.7,-1.18l-0.04,-0.47l-0.74,-0.86l-1.48,0.15l-2.34,1.71l-0.03,-1.27l-2.03,-1.35l0.47,-0.62l-0.08,-0.56l-1.03,-0.79l0.29,-0.43l-0.14,-0.58l-1.13,-0.86l1.89,0.68l1.69,0.06l0.37,-0.87l-0.81,-1.37l0.42,0.06l1.63,1.72ZM533.78,180.57l0.61,0.46l0.69,-0.0l0.59,1.15l-0.68,-0.15l-1.21,-1.45Z", "name": "Azerbaijan"}, "IE": {"path": "M405.08,135.42l0.35,2.06l-1.75,2.78l-4.22,1.88l-2.84,-0.4l1.73,-3.0l-1.18,-3.53l4.6,-3.74l0.32,1.15l-0.49,1.74l0.4,0.51l1.47,-0.04l1.6,0.6Z", "name": "Ireland"}, "ID": {"path": "M756.47,287.89l0.69,4.01l2.79,1.78l0.51,-0.1l2.04,-2.59l2.71,-1.43l2.05,-0.0l3.9,1.73l2.46,0.45l0.08,15.12l-1.75,-1.54l-2.54,-0.51l-0.88,0.71l-2.32,0.06l0.69,-1.33l1.45,-0.64l0.23,-0.46l-0.65,-2.74l-1.24,-2.21l-5.04,-2.29l-2.09,-0.23l-3.68,-2.27l-0.55,0.13l-0.65,1.07l-0.52,0.12l-0.55,-1.89l-1.21,-0.78l1.84,-0.62l1.72,0.05l0.39,-0.52l-0.21,-0.66l-0.38,-0.28l-3.45,-0.0l-1.13,-1.48l-2.1,-0.43l-0.52,-0.6l2.69,-0.48l1.28,-0.78l3.66,0.94l0.3,0.71ZM757.91,300.34l-0.62,0.82l-0.1,-0.8l0.59,-1.12l0.13,1.1ZM747.38,292.98l0.34,0.72l-1.22,-0.57l-4.68,-0.1l0.27,-0.62l2.78,-0.09l2.52,0.67ZM741.05,285.25l-0.67,-2.88l0.64,-2.01l0.41,0.86l1.21,0.18l0.16,0.7l-0.1,1.68l-0.84,-0.16l-0.46,0.3l-0.34,1.34ZM739.05,293.5l-0.5,0.44l-1.34,-0.36l-0.17,-0.37l1.73,-0.08l0.27,0.36ZM721.45,284.51l-0.19,1.97l2.24,2.23l0.54,0.02l1.27,-1.07l2.75,-0.5l-0.9,1.21l-2.11,0.93l-0.16,0.6l2.22,3.01l-0.3,1.07l1.36,1.74l-2.26,0.85l-0.28,-0.31l0.12,-1.19l-1.64,-1.34l0.17,-2.23l-0.56,-0.39l-1.67,0.76l-0.23,0.39l0.3,6.17l-1.1,0.25l-0.69,-0.47l0.64,-2.21l-0.39,-2.42l-0.39,-0.34l-0.8,-0.01l-0.58,-1.29l0.98,-1.6l0.35,-1.96l1.32,-3.87ZM728.59,296.27l0.38,0.49l-0.02,1.28l-0.88,0.49l-0.53,-0.47l1.04,-1.79ZM729.04,286.98l0.27,-0.05l-0.02,0.13l-0.24,-0.08ZM721.68,284.05l0.16,-0.32l1.89,-1.65l1.83,0.68l3.16,0.35l2.94,-0.1l2.39,-1.66l-1.73,2.13l-1.66,0.43l-2.41,-0.48l-4.17,0.13l-2.39,0.51ZM730.55,310.47l1.11,-1.93l2.03,-0.82l0.08,0.62l-1.45,1.67l-1.77,0.46ZM728.12,305.88l-0.1,0.38l-3.46,0.66l-2.91,-0.27l-0.0,-0.25l1.54,-0.41l1.66,0.73l1.67,-0.19l1.61,-0.65ZM722.9,310.24l-0.64,0.03l-2.26,-1.2l1.11,-0.24l1.78,1.41ZM716.26,305.77l0.88,0.51l1.28,-0.17l0.2,0.35l-4.65,0.73l0.39,-0.67l1.15,-0.02l0.75,-0.73ZM711.66,293.84l-0.38,-0.16l-2.54,1.01l-1.12,-1.44l-1.69,-0.13l-1.16,-0.75l-3.04,0.77l-1.1,-1.15l-3.31,-0.11l-0.35,-3.05l-1.35,-0.95l-1.11,-1.98l-0.33,-2.06l0.27,-2.14l0.9,-1.01l0.37,1.15l2.09,1.49l1.53,-0.48l1.82,0.08l1.38,-1.19l1.0,-0.18l2.28,0.67l2.26,-0.53l1.52,-3.64l1.01,-0.99l0.78,-2.57l4.1,0.3l-1.11,1.77l0.02,0.46l1.7,2.2l-0.23,1.39l2.07,1.71l-2.33,0.42l-0.88,1.9l0.1,2.05l-2.4,1.9l-0.06,2.45l-0.7,2.79ZM692.58,302.03l0.35,0.26l4.8,0.25l0.78,-0.97l4.17,1.09l1.13,1.68l3.69,0.45l2.13,1.04l-1.8,0.6l-2.77,-0.99l-4.8,-0.12l-5.24,-1.41l-1.84,-0.25l-1.11,0.3l-4.26,-0.97l-0.7,-1.14l-1.59,-0.13l1.18,-1.65l2.74,0.13l2.87,1.13l0.26,0.68ZM685.53,299.17l-2.22,0.04l-2.06,-2.03l-3.15,-2.01l-2.93,-3.51l-3.11,-5.33l-2.2,-2.12l-1.64,-4.06l-2.32,-1.69l-1.27,-2.07l-1.96,-1.5l-2.51,-2.65l-0.11,-0.66l4.81,0.53l2.15,2.38l3.31,2.74l2.35,2.66l2.7,0.17l1.95,1.59l1.54,2.17l1.59,0.95l-0.84,1.71l0.15,0.52l1.44,0.87l0.79,0.1l0.4,1.58l0.87,1.4l1.96,0.39l1.0,1.31l-0.6,3.01l-0.09,3.5Z", "name": "Indonesia"}, "UA": {"path": "M492.5,162.44l1.28,-2.49l1.82,0.19l0.66,-0.23l0.09,-0.71l-0.25,-0.75l-0.79,-0.72l-0.33,-1.21l-0.86,-0.62l-0.02,-1.19l-1.13,-0.86l-1.15,-0.19l-2.04,-1.0l-1.66,0.32l-0.66,0.47l-0.92,-0.0l-0.84,0.78l-2.48,0.7l-1.18,-0.71l-3.07,-0.36l-0.89,0.43l-0.24,-0.55l-1.11,-0.7l0.35,-0.93l1.26,-1.02l-0.54,-1.23l2.04,-2.43l1.4,-0.62l0.25,-1.19l-1.04,-2.39l0.83,-0.13l1.28,-0.84l1.8,-0.07l2.47,0.26l2.86,0.81l1.88,0.06l0.86,0.44l1.04,-0.41l0.77,0.66l2.18,-0.15l0.92,0.3l0.52,-0.34l0.15,-1.53l0.56,-0.54l2.85,-0.05l0.84,-0.72l3.04,-0.18l1.23,1.46l-0.48,0.77l0.21,1.03l0.36,0.32l1.8,0.14l0.93,2.08l3.18,1.15l1.94,-0.45l1.67,1.49l1.4,-0.03l3.35,0.96l0.02,0.54l-0.96,1.59l0.47,1.97l-0.26,0.7l-2.36,0.28l-1.29,0.89l-0.23,1.38l-1.83,0.27l-1.58,0.97l-2.41,0.21l-2.16,1.17l-0.21,0.38l0.34,2.26l1.23,0.75l2.13,-0.08l-0.14,0.31l-2.65,0.53l-3.23,1.69l-0.87,-0.39l0.42,-1.1l-0.25,-0.52l-2.21,-0.73l2.35,-1.06l0.12,-0.65l-0.93,-0.82l-3.62,-0.74l-0.13,-0.89l-0.46,-0.34l-2.61,0.59l-0.91,1.69l-1.71,2.04l-0.86,-0.4l-1.62,0.27Z", "name": "Ukraine"}, "QA": {"path": "M549.33,221.64l-0.76,-0.23l-0.14,-1.64l0.84,-1.29l0.47,0.52l0.04,1.34l-0.45,1.3Z", "name": "Qatar"}, "MZ": {"path": "M508.58,318.75l-0.34,-2.57l0.51,-2.05l3.55,0.63l2.5,-0.38l1.02,-0.76l1.49,0.01l2.74,-0.98l1.66,-1.2l0.5,9.24l0.41,1.23l-0.68,1.67l-0.93,1.71l-1.5,1.5l-5.16,2.28l-2.78,2.73l-1.02,0.53l-1.71,1.8l-0.98,0.57l-0.35,2.41l1.16,1.94l0.49,2.17l0.43,0.31l-0.06,2.06l-0.39,1.17l0.5,0.72l-0.25,0.73l-0.92,0.83l-5.12,2.39l-1.22,1.36l0.21,1.13l0.58,0.39l-0.11,0.72l-1.22,-0.01l-0.73,-2.97l0.42,-3.09l-1.78,-5.37l2.49,-2.81l0.69,-1.89l0.44,-0.43l0.28,-1.53l-0.39,-0.93l0.59,-3.65l-0.01,-3.26l-1.49,-1.16l-1.2,-0.22l-1.74,-1.17l-1.92,0.01l-0.29,-2.08l7.06,-1.96l1.28,1.09l0.89,-0.1l0.67,0.44l0.1,0.73l-0.51,1.29l0.19,1.81l1.75,1.83l0.65,-0.13l0.71,-1.65l1.17,-0.86l-0.26,-3.47l-1.05,-1.85l-1.04,-0.94Z", "name": "Mozambique"}}, "height": 440.7063107441331, "projection": {"type": "mill", "centralMeridian": 11.5}, "width": 900.0});
 
assets/js/wpzerospam.js DELETED
@@ -1,58 +0,0 @@
1
- /**
2
- * WordPress Zero Spam jQuery plugin.
3
- *
4
- * Handles adding the required functionality for spam detections.
5
- *
6
- * @since 4.9.11
7
- */
8
- (function ($) {
9
- $.fn.WordPressZeroSpam = function () {
10
- // Check if the required WPZS key is defined.
11
- if (typeof wpzerospam.key == "undefined") {
12
- // The key is not defined, alert the site owner via the console.
13
- console.log(
14
- "WordPress Zero Spam is unable to initialize, missing the required key."
15
- );
16
-
17
- return this;
18
- }
19
-
20
- // Check if the element is on the page.
21
- if (!this.length) {
22
- console.log(
23
- "WordPress Zero Spam could not find a " + this.selector + " instance."
24
- );
25
-
26
- return this;
27
- }
28
-
29
- console.log(
30
- "WordPress Zero Spam found " +
31
- this.length +
32
- " instance(s) of " +
33
- this.selector +
34
- "."
35
- );
36
-
37
- // Add an attribute to the element to show its been initialized by WPZS.
38
- this.attr("data-wpzerospam", "protected");
39
-
40
- // Check if the WPZS hidden input already exists.
41
- if ($('[name="wpzerospam_key"]', this).length) {
42
- // Hidden input already exists, update its value.
43
- $('[name="wpzerospam_key"]', this).val(wpzerospam.key);
44
- } else {
45
- // Hidden input isn't present, add it.
46
- $(
47
- '<input type="hidden" name="wpzerospam_key" value="' +
48
- wpzerospam.key +
49
- '" />'
50
- ).appendTo(this);
51
- }
52
- };
53
- })(jQuery);
54
-
55
- // Initialize WPZS on form elements with the wpzerospam class.
56
- jQuery(function() {
57
- jQuery(".wpzerospam").WordPressZeroSpam();
58
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-wpzerospam-blacklisted-table.php DELETED
@@ -1,305 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress Zero Spam blacklisted table
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
10
-
11
- if ( ! class_exists( 'WP_List_Table' ) ) {
12
- require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
13
- }
14
-
15
- class WPZeroSpam_Blacklisted_Table extends WP_List_Table {
16
- function __construct() {
17
- global $status, $page;
18
-
19
- $args = [
20
- 'singular' => __( 'Blacklist', 'zero-spam' ),
21
- 'plural' => __( 'Blacklist IPs', 'zero-spam' ),
22
- 'ajax' => true
23
- ];
24
- parent::__construct( $args );
25
- }
26
-
27
- // Register columns
28
- function get_columns() {
29
- // Render a checkbox instead of text
30
- $columns = [
31
- 'cb' => '<input type="checkbox" />',
32
- 'last_updated' => __( 'Last Updated', 'zero-spam' ),
33
- 'user_ip' => __( 'IP Address', 'zero-spam' ),
34
- 'service' => __( 'Service', 'zero-spam' ),
35
- 'attempts' => __( 'Attempts', 'zero-spam' ),
36
- 'details' => __( 'Details', 'zero-spam' )
37
- ];
38
-
39
- return $columns;
40
- }
41
-
42
- // Sortable columns
43
- function get_sortable_columns() {
44
- $sortable_columns = [
45
- 'last_updated' => [ 'last_updated', false ],
46
- 'user_ip' => [ 'user_ip', false ],
47
- 'service' => [ 'service', false ],
48
- 'attempts' => [ 'attempts', false ],
49
- ];
50
-
51
- return $sortable_columns;
52
- }
53
-
54
- function extra_tablenav( $which ) {
55
- global $cat_id;
56
-
57
- if ( 'top' !== $which ) {
58
- return;
59
- }
60
- ?>
61
- <div class="alignleft actions">
62
- <?php
63
- echo '<label class="screen-reader-text" for="filter-by-service">' . __( 'Filter by service', 'zero-spam' ) . '</label>';
64
- $current_service = ! empty( $_REQUEST['service'] ) ? sanitize_text_field( $_REQUEST['service'] ) : false;
65
- ?>
66
- <select name="service" id="filter-by-service">
67
- <option value=""><?php _e( 'All services', 'zero-spam' ); ?></option>
68
- <option<?php if ( $current_service == 'botscout' ): ?> selected="selected" <?php endif; ?> value="botscout"><?php _e( 'BotScout', 'zero-spam' ); ?></option>
69
- <option<?php if ( $current_service == 'stopforumspam' ): ?> selected="selected" <?php endif; ?> value="stopforumspam"><?php _e( 'Stop Forum Spam', 'zero-spam' ); ?></option>
70
- <option<?php if ( $current_service == 'zerospam' ): ?> selected="selected" <?php endif; ?> value="zerospam"><?php _e( 'Zero Spam', 'zero-spam' ); ?></option>
71
- </select>
72
- <?php
73
- submit_button( __( 'Filter' ), '', 'filter_action', false );
74
- ?>
75
- </div>
76
- <?php
77
- }
78
-
79
- // Checkbox column
80
- function column_cb( $item ){
81
- return sprintf(
82
- '<input type="checkbox" name="%1$s[]" value="%2$s" />',
83
- /*$1%s*/ 'ids',
84
- /*$2%s*/ $item->blacklist_id
85
- );
86
- }
87
-
88
- // Render column
89
- function column_default( $item, $column_name ) {
90
- switch( $column_name ) {
91
- case 'service':
92
- switch( $item->blacklist_service ) {
93
- case 'stopforumspam':
94
- return '<a href="https://www.stopforumspam.com/" target="_blank" rel="noopener noreferrer">Stop Forum Spam</a>';
95
- break;
96
- case 'botscout':
97
- return '<a href="https://botscout.com/" target="_blank" rel="noopener noreferrer">BotScout</a>';
98
- break;
99
- case 'zerospam':
100
- return '<a href="https://zerospam.org/" target="_blank" rel="noopener noreferrer">Zero Spam</a>';
101
- break;
102
- default:
103
- return $item->blacklist_service;
104
- }
105
- break;
106
- case 'attempts':
107
- return number_format( $item->attempts, 0 );
108
- break;
109
- case 'user_ip':
110
- return '<a href="https://zerospam.org/ip-lookup/' . $item->user_ip .'/" target="_blank" rel="noopener noreferrer">' . $item->user_ip . '</a>';
111
- break;
112
- case 'last_updated':
113
- return date( 'M j, Y g:ia' , strtotime( $item->last_updated ) );
114
- break;
115
- case 'details':
116
- if ( empty( $item->blacklist_data ) ) { return __( 'No details available.', 'wpzerospam' ); }
117
- ob_start();
118
- ?>
119
- <button class="button action wpzerospam-details-trigger" data-id="<?php echo $item->blacklist_id; ?>"><?php _e( 'View', 'zero-spam' ); ?></button>
120
- <div class="wpzerospam-details-modal" id="wpzerospam-details-modal-<?php echo $item->blacklist_id; ?>">
121
- <div class="wpzerospam-details-modal-inner">
122
- <?php
123
- $item->blacklist_data = json_decode( $item->blacklist_data, true );
124
-
125
- echo '<div class="wpzerospam-details-item">';
126
- echo '<div class="wpzerospam-details-label">' . __( 'Detected Spam IP', 'zero-spam' ) . '</div>';
127
- echo '<div class="wpzerospam-details-data">' . '<a href="https://zerospam.org/ip-lookup/' . urlencode( $item->user_ip ) .'" target="_blank" rel="noopener noreferrer">' . $item->user_ip . '</a>' . '</div>';
128
- echo '</div>';
129
-
130
- echo '<div class="wpzerospam-details-item">';
131
- echo '<div class="wpzerospam-details-label">' . __( 'Last Updated', 'zero-spam' ) . '</div>';
132
- echo '<div class="wpzerospam-details-data">' . date( 'M j, Y g:ia' , strtotime( $item->last_updated ) ) . '</div>';
133
- echo '</div>';
134
-
135
- echo '<div class="wpzerospam-details-item">';
136
- echo '<div class="wpzerospam-details-label">' . __( 'Service', 'zero-spam' ) . '</div>';
137
- echo '<div class="wpzerospam-details-data">';
138
- switch( $item->blacklist_service ) {
139
- case 'stopforumspam':
140
- echo '<a href="https://www.stopforumspam.com/" target="_blank" rel="noopener noreferrer">Stop Forum Spam</a>';
141
- break;
142
- case 'botscout':
143
- echo '<a href="https://botscout.com/" target="_blank" rel="noopener noreferrer">BotScout</a>';
144
- break;
145
- case 'zerospam':
146
- echo '<a href="https://zerospam.org/" target="_blank" rel="noopener noreferrer">Zero Spam</a>';
147
- break;
148
- default:
149
- echo $item->blacklist_service;
150
- }
151
- echo '</div>';
152
- echo '</div>';
153
-
154
- if ( ! empty( $item->blacklist_data ) ) {
155
- foreach( $item->blacklist_data as $key => $value ):
156
- if ( ! $value ) { continue; }
157
- switch( $key ):
158
- case 'appears':
159
- echo '<div class="wpzerospam-details-item">';
160
- echo '<div class="wpzerospam-details-label">' . __( 'Appears', 'zero-spam' ) . '</div>';
161
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
162
- echo '</div>';
163
- break;
164
- case 'confidence':
165
- echo '<div class="wpzerospam-details-item">';
166
- echo '<div class="wpzerospam-details-label">' . __( 'Confidence', 'zero-spam' ) . '</div>';
167
- echo '<div class="wpzerospam-details-data">' . $value . '%</div>';
168
- echo '</div>';
169
- break;
170
- case 'frequency':
171
- echo '<div class="wpzerospam-details-item">';
172
- echo '<div class="wpzerospam-details-label">' . __( 'Frequency', 'zero-spam' ) . '</div>';
173
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
174
- echo '</div>';
175
- break;
176
- case 'lastseen':
177
- echo '<div class="wpzerospam-details-item">';
178
- echo '<div class="wpzerospam-details-label">' . __( 'Last Seen', 'zero-spam' ) . '</div>';
179
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
180
- echo '</div>';
181
- break;
182
- case 'asn':
183
- echo '<div class="wpzerospam-details-item">';
184
- echo '<div class="wpzerospam-details-label">' . __( 'ASN', 'zero-spam' ) . '</div>';
185
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
186
- echo '</div>';
187
- break;
188
- default:
189
- echo '<div class="wpzerospam-details-item">';
190
- echo '<div class="wpzerospam-details-label">' . $key . '</div>';
191
- echo '<div class="wpzerospam-details-data">' . json_encode( $value ) . '</div>';
192
- echo '</div>';
193
- endswitch;
194
- endforeach;
195
- };
196
- ?>
197
- </div>
198
- </div>
199
- <?php
200
- return ob_get_clean();
201
- break;
202
- }
203
- }
204
-
205
- // Register bulk actions
206
- function get_bulk_actions() {
207
- $actions = [
208
- 'delete' => __( 'Delete', 'zero-spam' ),
209
- 'delete_all' => __( 'Delete All Entries', 'zero-spam' )
210
- ];
211
-
212
- return $actions;
213
- }
214
-
215
- /**
216
- * Define which columns are hidden
217
- *
218
- * @return Array
219
- */
220
- public function get_hidden_columns() {
221
- return [];
222
- }
223
-
224
- // Get results
225
- function prepare_items($args = []) {
226
- $this->process_bulk_action();
227
-
228
- $columns = $this->get_columns();
229
- $hidden = $this->get_hidden_columns();
230
- $sortable = $this->get_sortable_columns();
231
-
232
- $per_page = 50;
233
- $current_page = $this->get_pagenum();
234
- $offset = $per_page * ( $current_page - 1 );
235
- $order = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( $_REQUEST['order'] ) : 'desc';
236
- $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_text_field( $_REQUEST['orderby'] ) : 'last_updated';
237
-
238
- $user_ip = ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : false;
239
- $blacklist_service = ! empty( $_REQUEST['service'] ) ? sanitize_text_field( $_REQUEST['service'] ) : false;
240
-
241
- $query_args = [
242
- 'limit' => $per_page,
243
- 'offset' => $offset,
244
- 'order' => $order,
245
- 'orderby' => $orderby
246
- ];
247
-
248
- if ( $blacklist_service || $user_ip ) {
249
- $query_args['where'] = [];
250
-
251
- if ( $blacklist_service ) {
252
- $query_args['where']['blacklist_service'] = $blacklist_service;
253
- }
254
-
255
- if ( $user_ip ) {
256
- $query_args['where']['user_ip'] = $user_ip;
257
- }
258
- }
259
-
260
- $data = wpzerospam_query( 'blacklist', $query_args );
261
- if ( ! $data ) { return false; }
262
-
263
- // Set the $_SERVER['REQUEST_URI'] for paging
264
- wpzerospam_set_list_table_request_uri( $query_args );
265
-
266
- $total_items = wpzerospam_query( 'blacklist', $query_args, true );
267
-
268
- $this->set_pagination_args([
269
- 'total_items' => $total_items,
270
- 'per_page' => $per_page,
271
- 'total_pages' => ceil( $total_items / $per_page ),
272
- 'orderby' => $orderby,
273
- 'order' => $order
274
- ]);
275
-
276
- $this->_column_headers = [ $columns, $hidden, $sortable ];
277
- $this->items = $data;
278
- }
279
-
280
- // Process bulk actions
281
- function process_bulk_action() {
282
- global $wpdb;
283
-
284
- $ids = ( isset( $_REQUEST['ids'] ) ) ? $_REQUEST['ids'] : '';
285
-
286
- switch( $this->current_action() ) {
287
- // Delete
288
- case 'delete':
289
- // Delete query
290
- $nonce = ( isset( $_REQUEST['wpzerospam_nonce'] ) ) ? $_REQUEST['wpzerospam_nonce'] : '';
291
- if ( ! wp_verify_nonce( $nonce, 'wpzerospam_nonce' ) ) return false;
292
-
293
- if ( ! empty ( $ids ) && is_array( $ids ) ) {
294
- // Delete query
295
- foreach( $ids as $k => $blacklist_id ) {
296
- $wpdb->delete( wpzerospam_tables( 'blacklist' ), [ 'blacklist_id' => $blacklist_id ] );
297
- }
298
- }
299
- break;
300
- case 'delete_all':
301
- $wpdb->query( "TRUNCATE TABLE " . wpzerospam_tables( 'blacklist' ) );
302
- break;
303
- }
304
- }
305
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-wpzerospam-blocked-ip-table.php DELETED
@@ -1,260 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress Zero Spam blocked IP table
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
10
-
11
- if ( ! class_exists( 'WP_List_Table' ) ) {
12
- require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
13
- }
14
-
15
- class WPZeroSpam_Blocked_IP_Table extends WP_List_Table {
16
- function __construct() {
17
- global $status, $page;
18
-
19
- $args = [
20
- 'singular' => __( 'Blocked IP', 'zero-spam' ),
21
- 'plural' => __( 'Blocked IPs', 'zero-spam' ),
22
- 'ajax' => true
23
- ];
24
- parent::__construct( $args );
25
- }
26
-
27
- // Register columns
28
- function get_columns() {
29
- // Render a checkbox instead of text
30
- $columns = [
31
- 'cb' => '<input type="checkbox" />',
32
- 'user_ip' => __( 'IP Address', 'zero-spam' ),
33
- 'blocked_type' => __( 'Type', 'zero-spam' ),
34
- 'date_added' => __( 'Date Added', 'zero-spam' ),
35
- 'start_block' => __( 'Start Date', 'zero-spam' ),
36
- 'end_block' => __( 'End Date', 'zero-spam' ),
37
- 'reason' => __( 'Reason', 'zero-spam' ),
38
- 'attempts' => __( 'Attempts', 'zero-spam' ),
39
- ];
40
-
41
- return $columns;
42
- }
43
-
44
- // Sortable columns
45
- function get_sortable_columns() {
46
- $sortable_columns = [
47
- 'user_ip' => [ 'user_ip', false ],
48
- 'blocked_type' => [ 'blocked_type', false ],
49
- 'date_added' => [ 'date_added', false ],
50
- 'start_block' => [ 'start_block', false ],
51
- 'end_block' => [ 'end_block', false ],
52
- 'attempts' => [ 'attempts', false ],
53
- ];
54
-
55
- return $sortable_columns;
56
- }
57
-
58
- function extra_tablenav( $which ) {
59
- global $cat_id;
60
-
61
- if ( 'top' !== $which ) {
62
- return;
63
- }
64
- ?>
65
- <div class="alignleft actions">
66
- <?php
67
- echo '<label class="screen-reader-text" for="filter-by-type">' . __( 'Filter by type', 'zero-spam' ) . '</label>';
68
- $current_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
69
- ?>
70
- <select name="type" id="filter-by-type">
71
- <option value=""><?php _e( 'All types', 'zero-spam' ); ?></option>
72
- <option<?php if ( $current_type == 'permanent' ): ?> selected="selected" <?php endif; ?> value="permanent"><?php _e( 'Permanent', 'zero-spam' ); ?></option>
73
- <option<?php if ( $current_type == 'temporary' ): ?> selected="selected" <?php endif; ?> value="temporary"><?php _e( 'Temporary', 'zero-spam' ); ?></option>
74
- </select>
75
- <?php
76
- submit_button( __( 'Filter', 'zero-spam' ), '', 'filter_action', false );
77
- ?>
78
- </div>
79
- <?php
80
- }
81
-
82
- // Checkbox column
83
- function column_cb( $item ){
84
- return sprintf(
85
- '<input type="checkbox" name="%1$s[]" value="%2$s" />',
86
- /*$1%s*/ 'ids',
87
- /*$2%s*/ $item->blocked_id
88
- );
89
- }
90
-
91
- // Render column
92
- function column_default( $item, $column_name ) {
93
- switch( $column_name ) {
94
- case 'user_ip':
95
- return '<a href="https://zerospam.org/ip-lookup/' . urlencode( $item->user_ip ) .'" target="_blank" rel="noopener noreferrer">' . $item->user_ip . '</a>';
96
- break;
97
- case 'blocked_id':
98
- return $item->blocked_id;
99
- break;
100
- case 'blocked_type':
101
- switch( $item->blocked_type ) {
102
- case 'permanent':
103
- return '<span class="wpzerospam-blocked">' . __( 'Permanent', 'zero-spam' ) . '</span>';
104
- break;
105
- case 'temporary':
106
- return '<span class="wpzerospam-warning">' . __( 'Temporary', 'zero-spam' ) . '</span>';
107
- break;
108
- default:
109
- return $item->blocked_type;
110
- }
111
- break;
112
- case 'date_added':
113
- return date( 'M j, Y g:ia' , strtotime( $item->date_added ) );
114
- break;
115
- case 'start_block':
116
- if ( ! $item->start_block || '0000-00-00 00:00:00' == $item->start_block || 'permanent' == $item->blocked_type ) {
117
- return 'N/A';
118
- }
119
-
120
- return date( 'M j, Y g:ia' , strtotime( $item->start_block ) );
121
- break;
122
- case 'end_block':
123
- if ( ! $item->end_block || '0000-00-00 00:00:00' == $item->end_block || 'permanent' == $item->blocked_type ) {
124
- return 'N/A';
125
- }
126
-
127
- return date( 'M j, Y g:ia' , strtotime( $item->end_block ) );
128
- break;
129
- case 'reason':
130
- if ( ! $item->reason ) {
131
- return 'N/A';
132
- }
133
-
134
- switch( $item->reason ) {
135
- case 'comment (permanently auto-blocked)':
136
- return sprintf(
137
- wp_kses(
138
- __( 'Comment <span class="wpzerospam-small">(<strong>permanently</strong> auto-blocked)</span>', 'zero-spam' ),
139
- [ 'span' => [ 'class' => [] ], 'strong' => [] ]
140
- )
141
- );
142
- break;
143
- case 'comment (auto-blocked)':
144
- return sprintf(
145
- wp_kses(
146
- __( 'Comment <span class="wpzerospam-small">(auto-blocked)</span>', 'zero-spam' ),
147
- [ 'span' => [ 'class' => [] ] ]
148
- )
149
- );
150
- break;
151
- default:
152
- return $item->reason;
153
- }
154
- break;
155
- case 'attempts':
156
- return $item->attempts;
157
- break;
158
- }
159
- }
160
-
161
- // Register bulk actions
162
- function get_bulk_actions() {
163
- $actions = [
164
- 'delete' => __( 'Delete', 'zero-spam' ),
165
- 'delete_all' => __( 'Delete All Entries', 'zero-spam' )
166
- ];
167
-
168
- return $actions;
169
- }
170
-
171
- /**
172
- * Define which columns are hidden
173
- *
174
- * @return Array
175
- */
176
- public function get_hidden_columns() {
177
- return [];
178
- }
179
-
180
- // Get results
181
- function prepare_items($args = []) {
182
- $this->process_bulk_action();
183
-
184
- $columns = $this->get_columns();
185
- $hidden = $this->get_hidden_columns();
186
- $sortable = $this->get_sortable_columns();
187
-
188
- $per_page = 50;
189
- $current_page = $this->get_pagenum();
190
- $offset = $per_page * ( $current_page - 1 );
191
- $order = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( $_REQUEST['order'] ) : 'desc';
192
- $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_text_field( $_REQUEST['orderby'] ) : 'date_added';
193
-
194
- $user_ip = ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : false;
195
- $blocked_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
196
-
197
- $query_args = [
198
- 'limit' => $per_page,
199
- 'offset' => $offset,
200
- 'order' => $order,
201
- 'orderby' => $orderby
202
- ];
203
-
204
- if ( $blocked_type || $user_ip ) {
205
- $query_args['where'] = [];
206
-
207
- if ( $blocked_type ) {
208
- $query_args['where']['blocked_type'] = $blocked_type;
209
- }
210
-
211
- if ( $user_ip ) {
212
- $query_args['where']['user_ip'] = $user_ip;
213
- }
214
- }
215
-
216
- $data = wpzerospam_query( 'blocked', $query_args );
217
- if ( ! $data ) { return false; }
218
-
219
- // Set the $_SERVER['REQUEST_URI'] for paging
220
- wpzerospam_set_list_table_request_uri( $query_args );
221
-
222
- $total_items = wpzerospam_query( 'blocked', $query_args, true );
223
-
224
- $this->set_pagination_args([
225
- 'total_items' => $total_items,
226
- 'per_page' => $per_page,
227
- 'total_pages' => ceil( $total_items / $per_page ),
228
- 'orderby' => $orderby,
229
- 'order' => $order
230
- ]);
231
-
232
- $this->_column_headers = [ $columns, $hidden, $sortable ];
233
- $this->items = $data;
234
- }
235
-
236
- // Process bulk actions
237
- function process_bulk_action() {
238
- global $wpdb;
239
-
240
- $ids = ( isset( $_REQUEST['ids'] ) ) ? $_REQUEST['ids'] : '';
241
-
242
- switch( $this->current_action() ) {
243
- // Delete
244
- case 'delete':
245
- $nonce = ( isset( $_REQUEST['wpzerospam_nonce'] ) ) ? $_REQUEST['wpzerospam_nonce'] : '';
246
- if ( ! wp_verify_nonce( $nonce, 'wpzerospam_nonce' ) ) return false;
247
-
248
- if ( ! empty ( $ids ) && is_array( $ids ) ) {
249
- // Delete query
250
- foreach( $ids as $k => $blocked_id ) {
251
- $wpdb->delete( wpzerospam_tables( 'blocked' ), [ 'blocked_id' => $blocked_id ] );
252
- }
253
- }
254
- break;
255
- case 'delete_all':
256
- $wpdb->query( "TRUNCATE TABLE " . wpzerospam_tables( 'blocked' ) );
257
- break;
258
- }
259
- }
260
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-wpzerospam-comments.php DELETED
@@ -1,16 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress Zero Spam comments class.
4
- *
5
- * @package WordPressZeroSpam
6
- */
7
-
8
- // Security Note: Blocks direct access to the plugin PHP files.
9
- defined( 'ABSPATH' ) || die();
10
-
11
- /**
12
- * WordPress Zero Spam comments class.
13
- */
14
- class WPZeroSpam_Comments {
15
-
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-wpzerospam-log-table.php DELETED
@@ -1,371 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress Zero Spam log table
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
10
-
11
- if ( ! class_exists( 'WP_List_Table' ) ) {
12
- require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
13
- }
14
-
15
- class WPZeroSpam_Log_Table extends WP_List_Table {
16
- function __construct() {
17
- global $status, $page;
18
-
19
- $args = [
20
- 'singular' => __( 'Spam Detection', 'zero-spam' ),
21
- 'plural' => __( 'Spam Detections', 'zero-spam' ),
22
- 'ajax' => true
23
- ];
24
- parent::__construct( $args );
25
- }
26
-
27
- // Register columns
28
- function get_columns() {
29
- // Render a checkbox instead of text
30
- $columns = [
31
- 'cb' => '<input type="checkbox" />',
32
- 'date_recorded' => __( 'Date', 'zero-spam' ),
33
- 'log_type' => __( 'Type', 'zero-spam' ),
34
- 'user_ip' => __( 'IP Address', 'zero-spam' ),
35
- 'country' => __( 'Country', 'zero-spam' ),
36
- 'region' => __( 'Region', 'zero-spam' ),
37
- 'city' => __( 'City', 'zero-spam' ),
38
- 'details' => __( 'Details', 'zero-spam' ),
39
- 'actions' => __( 'Block IP', 'zero-spam' ),
40
- ];
41
-
42
- return $columns;
43
- }
44
-
45
- // Sortable columns
46
- function get_sortable_columns() {
47
- $sortable_columns = [
48
- 'date_recorded' => [ 'date_recorded', false ],
49
- 'log_type' => [ 'log_type', false ],
50
- 'user_ip' => [ 'user_ip', false ],
51
- 'country' => [ 'country', false ],
52
- 'region' => [ 'region', false ],
53
- 'city' => [ 'city', false ],
54
- ];
55
-
56
- return $sortable_columns;
57
- }
58
-
59
- function extra_tablenav( $which ) {
60
- global $cat_id;
61
-
62
- if ( 'top' !== $which ) {
63
- return;
64
- }
65
- ?>
66
- <div class="alignleft actions">
67
- <?php
68
- echo '<label class="screen-reader-text" for="filter-by-type">' . __( 'Filter by type', 'zero-spam' ) . '</label>';
69
- $options = wpzerospam_types();
70
- $current_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
71
- ?>
72
- <select name="type" id="filter-by-type">
73
- <option value=""><?php _e( 'All types', 'zero-spam' ); ?></option>
74
- <?php foreach( $options as $key => $value ): ?>
75
- <option<?php if ( $current_type == $key ): ?> selected="selected" <?php endif; ?> value="<?php echo $key; ?>"><?php echo $value; ?></option>
76
- <?php endforeach; ?>
77
- </select>
78
- <?php
79
- submit_button( __( 'Filter', 'zero-spam' ), '', 'filter_action', false );
80
- ?>
81
- </div>
82
- <?php
83
- }
84
-
85
- // Checkbox column
86
- function column_cb( $item ){
87
- return sprintf(
88
- '<input type="checkbox" name="%1$s[]" value="%2$s" />',
89
- /*$1%s*/ 'ids',
90
- /*$2%s*/ $item->log_id
91
- );
92
- }
93
-
94
- // Render column
95
- function column_default( $item, $column_name ) {
96
- switch( $column_name ) {
97
- case 'actions':
98
- if ( wpzerospam_is_blocked( $item->user_ip ) ) {
99
- return '<span class="wpzerospam-blocked">' . __( 'Blocked', 'zero-spam' ) . '</span>';
100
- } else {
101
- return '<a class="button" href="' . admin_url( 'admin.php?page=wordpress-zero-spam-blocked-ips&ip=' . $item->user_ip ) . '">' . __( 'Block IP', 'zero-spam' ) . '</a>';
102
- }
103
- break;
104
- case 'log_id':
105
- return $item->log_id;
106
- break;
107
- case 'log_type':
108
- return '<span class="wpzerospam-' . $item->log_type . '">' . wpzerospam_types( $item->log_type ) . '</span>';
109
- break;
110
- case 'user_ip':
111
- return '<a href="https://zerospam.org/ip-lookup/' . urlencode( $item->user_ip ) .'" target="_blank" rel="noopener noreferrer">' . $item->user_ip . '</a>';
112
- break;
113
- case 'date_recorded':
114
- return date( 'M j, Y g:ia' , strtotime( $item->date_recorded ) );
115
- break;
116
- case 'country':
117
- if ( ! $item->country ) {
118
- return 'N/A';
119
- }
120
-
121
- return '<img class="wpzerospam-country-flag" width="16" src="https://hatscripts.github.io/circle-flags/flags/' . strtolower( $item->country ) . '.svg" alt="' . wpzerospam_get_location( $item->country ) .'" /> ' . wpzerospam_get_location( $item->country );
122
- break;
123
- case 'region':
124
- $region = wpzerospam_get_location( $item->country, $item->region );
125
- if ( $region ) {
126
- return $region;
127
- } else if ( ! empty( $item->region) ) {
128
- return $item->region;
129
- }
130
-
131
- return 'N/A';
132
- break;
133
- case 'city':
134
- if ( ! $item->city ) {
135
- return 'N/A';
136
- }
137
- return $item->city;
138
- break;
139
- case 'details':
140
- if ( empty( $item->submission_data ) ) { return __( 'No details available.', 'zero-spam' ); }
141
- ob_start();
142
- ?>
143
- <button class="button action wpzerospam-details-trigger" data-id="<?php echo $item->log_id; ?>"><?php _e( 'View', 'zero-spam' ); ?></button>
144
- <div class="wpzerospam-details-modal" id="wpzerospam-details-modal-<?php echo $item->log_id; ?>">
145
- <div class="wpzerospam-details-modal-inner">
146
- <?php
147
- echo '<div class="wpzerospam-details-item">';
148
- echo '<div class="wpzerospam-details-label">' . __( 'Detected Spam IP', 'zero-spam' ) . '</div>';
149
- echo '<div class="wpzerospam-details-data">' . '<a href="https://whatismyipaddress.com/ip/' . $item->user_ip .'" target="_blank" rel="noopener noreferrer">' . $item->user_ip . '</a></div>';
150
- echo '</div>';
151
-
152
- echo '<div class="wpzerospam-details-item">';
153
- echo '<div class="wpzerospam-details-label">' . __( 'Page URL', 'zero-spam' ) . '</div>';
154
- echo '<div class="wpzerospam-details-data"><a href="' . esc_url( $item->page_url ) . '" target="_blank" rel="noreferrer noopener">' . $item->page_url . '</a></div>';
155
- echo '</div>';
156
-
157
- echo '<div class="wpzerospam-details-item">';
158
- echo '<div class="wpzerospam-details-label">' . __( 'Date', 'zero-spam' ) . '</div>';
159
- echo '<div class="wpzerospam-details-data">' . date( 'M j, Y g:ia' , strtotime( $item->date_recorded ) ) . '</div>';
160
- echo '</div>';
161
-
162
- echo '<div class="wpzerospam-details-item">';
163
- echo '<div class="wpzerospam-details-label">' . __( 'Type', 'zero-spam' ) . '</div>';
164
- echo '<div class="wpzerospam-details-data">' . wpzerospam_types( $item->log_type ) . '</div>';
165
- echo '</div>';
166
-
167
- if ( $item->country ) {
168
- echo '<div class="wpzerospam-details-item">';
169
- echo '<div class="wpzerospam-details-label">' . __( 'Country', 'zero-spam' ) . '</div>';
170
- echo '<div class="wpzerospam-details-data">' . wpzerospam_get_location( $item->country ) . '</div>';
171
- echo '</div>';
172
- }
173
-
174
- if ( $item->region ) {
175
- echo '<div class="wpzerospam-details-item">';
176
- echo '<div class="wpzerospam-details-label">' . __( 'Region', 'zero-spam' ) . '</div>';
177
- echo '<div class="wpzerospam-details-data">' . wpzerospam_get_location( $item->country, $item->region ) . '</div>';
178
- echo '</div>';
179
- }
180
-
181
- if ( $item->city ) {
182
- echo '<div class="wpzerospam-details-item">';
183
- echo '<div class="wpzerospam-details-label">' . __( 'City', 'zero-spam' ) . '</div>';
184
- echo '<div class="wpzerospam-details-data">' . $item->city . '</div>';
185
- echo '</div>';
186
- }
187
-
188
- if ( ! empty( $item->submission_data ) ) {
189
- $submission_data = json_decode( $item->submission_data, true );
190
-
191
- foreach( $submission_data as $key => $value ):
192
- if ( ! $value ) { continue; }
193
-
194
- do_action( 'wpzerospam_admin_submission_data_items', $key, $value );
195
-
196
- switch( $key ):
197
- case 'reason':
198
- echo '<div class="wpzerospam-details-item">';
199
- echo '<div class="wpzerospam-details-label">' . __( 'Reason', 'zero-spam' ) . '</div>';
200
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
201
- echo '</div>';
202
- break;
203
-
204
- // Formidable fields
205
- case 'frm_action':
206
- echo '<div class="wpzerospam-details-item">';
207
- echo '<div class="wpzerospam-details-label">' . __( 'Form Action', 'zero-spam' ) . '</div>';
208
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
209
- echo '</div>';
210
- break;
211
- case 'form_id':
212
- echo '<div class="wpzerospam-details-item">';
213
- echo '<div class="wpzerospam-details-label">' . __( 'Form ID', 'zero-spam' ) . '</div>';
214
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
215
- echo '</div>';
216
- break;
217
- case 'form_key':
218
- echo '<div class="wpzerospam-details-item">';
219
- echo '<div class="wpzerospam-details-label">' . __( 'Form Key', 'zero-spam' ) . '</div>';
220
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
221
- echo '</div>';
222
- break;
223
- case 'item_key':
224
- echo '<div class="wpzerospam-details-item">';
225
- echo '<div class="wpzerospam-details-label">' . __( 'Item Key', 'zero-spam' ) . '</div>';
226
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
227
- echo '</div>';
228
- break;
229
- case 'item_meta':
230
- echo '<div class="wpzerospam-details-item">';
231
- echo '<div class="wpzerospam-details-label">' . __( 'Form Values', 'zero-spam' ) . '</div>';
232
- if ( is_array( $value ) ) {
233
- echo '<div class="wpzerospam-details-data">' . implode( ", ", array_filter( $value ) ) . '</div>';
234
- } else {
235
- echo '<div class="wpzerospam-details-data">' . $value . '</div>';
236
- }
237
- echo '</div>';
238
- break;
239
- case '_wp_http_referer':
240
- echo '<div class="wpzerospam-details-item">';
241
- echo '<div class="wpzerospam-details-label">' . __( 'Source', 'zero-spam' ) . '</div>';
242
- if ( $value ) {
243
- $source_url = esc_url( site_url( $value ) );
244
- echo '<div class="wpzerospam-details-data"><a href="' . $source_url . '" target="_blank" rel="noopener noreferrer">' . $source_url . '</a></div>';
245
- } else {
246
- echo '<div class="wpzerospam-details-data">N/A</div>';
247
- }
248
- echo '</div>';
249
- break;
250
-
251
- default:
252
- $defined_submission_data = apply_filters( 'wpzerospam_defined_submission_data', [] );
253
- if ( ! in_array( $key, $defined_submission_data ) ) {
254
- echo '<div class="wpzerospam-details-item wpzerospam-details-item-unknown">';
255
- echo '<div class="wpzerospam-details-label">' . $key . '</div>';
256
- echo '<div class="wpzerospam-details-data">' . json_encode( $value ) . '</div>';
257
- echo '</div>';
258
- }
259
- endswitch;
260
- endforeach;
261
- };
262
- ?>
263
- </div>
264
- </div>
265
- <?php
266
- return ob_get_clean();
267
- break;
268
- }
269
- }
270
-
271
- // Register bulk actions
272
- function get_bulk_actions() {
273
- $actions = [
274
- 'delete' => __( 'Delete Selected', 'zero-spam' ) ,
275
- 'delete_all' => __( 'Delete All Entries', 'zero-spam' )
276
- ];
277
-
278
- return $actions;
279
- }
280
-
281
- /**
282
- * Define which columns are hidden
283
- *
284
- * @return Array
285
- */
286
- public function get_hidden_columns() {
287
- return [];
288
- }
289
-
290
- // Get results
291
- function prepare_items($args = []) {
292
- $this->process_bulk_action();
293
-
294
- $columns = $this->get_columns();
295
- $hidden = $this->get_hidden_columns();
296
- $sortable = $this->get_sortable_columns();
297
-
298
- $per_page = 50;
299
- $current_page = $this->get_pagenum();
300
- $offset = $per_page * ( $current_page - 1 );
301
- $order = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( $_REQUEST['order'] ) : 'desc';
302
- $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_text_field( $_REQUEST['orderby'] ) : 'date_recorded';
303
-
304
- $log_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
305
- $user_ip = ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : false;
306
-
307
- $query_args = [
308
- 'limit' => $per_page,
309
- 'offset' => $offset,
310
- 'order' => $order,
311
- 'orderby' => $orderby
312
- ];
313
-
314
- if ( $log_type || $user_ip ) {
315
- $query_args['where'] = [];
316
-
317
- if ( $log_type ) {
318
- $query_args['where']['log_type'] = $log_type;
319
- }
320
-
321
- if ( $user_ip ) {
322
- $query_args['where']['user_ip'] = $user_ip;
323
- }
324
- }
325
-
326
- $data = wpzerospam_query( 'log', $query_args );
327
- if ( ! $data ) { return false; }
328
-
329
- // Set the $_SERVER['REQUEST_URI'] for paging
330
- wpzerospam_set_list_table_request_uri( $query_args );
331
-
332
- $total_items = wpzerospam_query( 'log', $query_args, true );
333
-
334
- $this->set_pagination_args([
335
- 'total_items' => $total_items,
336
- 'per_page' => $per_page,
337
- 'total_pages' => ceil( $total_items / $per_page ),
338
- 'orderby' => $orderby,
339
- 'order' => $order
340
- ]);
341
-
342
- $this->_column_headers = [ $columns, $hidden, $sortable ];
343
- $this->items = $data;
344
- }
345
-
346
- // Process bulk actions
347
- function process_bulk_action() {
348
- global $wpdb;
349
-
350
- $ids = ( isset( $_REQUEST['ids'] ) ) ? $_REQUEST['ids'] : '';
351
-
352
- switch( $this->current_action() ) {
353
- // Delete
354
- case 'delete':
355
- // Delete query
356
- $nonce = ( isset( $_REQUEST['wpzerospam_nonce'] ) ) ? $_REQUEST['wpzerospam_nonce'] : '';
357
- if ( ! wp_verify_nonce( $nonce, 'wpzerospam_nonce' ) ) return false;
358
-
359
- if ( ! empty ( $ids ) && is_array( $ids ) ) {
360
- // Delete query
361
- foreach( $ids as $k => $log_id ) {
362
- $wpdb->delete( wpzerospam_tables( 'log' ), [ 'log_id' => $log_id ] );
363
- }
364
- }
365
- break;
366
- case 'delete_all':
367
- $wpdb->query( "TRUNCATE TABLE " . wpzerospam_tables( 'log' ) );
368
- break;
369
- }
370
- }
371
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-wpzerospam-security.php DELETED
@@ -1,36 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress Zero Spam security class.
4
- *
5
- * @package WordPressZeroSpam
6
- */
7
-
8
- // Security Note: Blocks direct access to the plugin PHP files.
9
- defined( 'ABSPATH' ) || die();
10
-
11
- /**
12
- * WordPress Zero Spam security class.
13
- */
14
- class WPZeroSpam_Security {
15
- /**
16
- * Class constructor.
17
- */
18
- public function __construct() {
19
- // Removes the meta generator tag.
20
- add_action( 'after_setup_theme', array( $this, 'after_setup_theme' ) );
21
- }
22
-
23
- /**
24
- * Removes the meta generator tag.
25
- */
26
- public function after_setup_theme() {
27
- // Remove the meta generator tag.
28
- remove_action( 'wp_head', 'wp_generator' );
29
- add_filter(
30
- 'the_generator',
31
- function() {
32
- return '';
33
- }
34
- );
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class-wpzerospam.php DELETED
@@ -1,977 +0,0 @@
1
- <?php
2
- /**
3
- * WordPress Zero Spam class.
4
- *
5
- * @package WordPressZeroSpam
6
- */
7
-
8
- // Security Note: Blocks direct access to the plugin PHP files.
9
- defined( 'ABSPATH' ) || die();
10
-
11
- /**
12
- * WordPress Zero Spam class.
13
- */
14
- class WPZeroSpam {
15
- /**
16
- * Contains all plugin options.
17
- *
18
- * @var array Array of plugin options.
19
- */
20
- public $options;
21
-
22
- /**
23
- * The default core plugin options.
24
- *
25
- * @var array Array of core plugin options.
26
- */
27
- public $default_options = array(
28
- 'debug' => 'disabled',
29
- 'ip_whitelist' => '',
30
- 'cookie_expiration' => 7,
31
- 'log_spam' => false,
32
- 'log_blocked_ips' => false,
33
- 'share_detections' => true,
34
- 'stop_forum_spam' => 'enabled',
35
- 'stopforumspam_confidence_min' => 20,
36
- 'botscout_count_min' => 5,
37
- 'botscout_api' => false,
38
- 'api_timeout' => 5,
39
- 'block_handler' => 403,
40
- 'blocked_redirect_url' => 'https://google.com',
41
- 'blocked_message' => false,
42
- 'ipstack_api' => false,
43
- );
44
-
45
- /**
46
- * The current user's IP address.
47
- *
48
- * @var string The current user's IP address.
49
- */
50
- public $current_user_ip;
51
-
52
- /**
53
- * The current user's IP address access.
54
- *
55
- * @var array IP address access details.
56
- */
57
- public $current_user_ip_access;
58
-
59
- /**
60
- * Database tables.
61
- *
62
- * @var array Array of plugin database tables.
63
- */
64
- public $tables;
65
-
66
- /**
67
- * Class constructor.
68
- */
69
- public function __construct() {
70
- global $wpdb;
71
-
72
- // Set the database tables.
73
- $this->tables = array(
74
- 'log' => $wpdb->prefix . 'wpzerospam_log',
75
- 'blocked' => $wpdb->prefix . 'wpzerospam_blocked',
76
- 'blacklist' => $wpdb->prefix . 'wpzerospam_blacklist',
77
- );
78
- }
79
-
80
- /**
81
- * Initializes the plugin.
82
- */
83
- public function initialize() {
84
- // Triggered on the WP init action.
85
- add_action( 'init', array( $this, 'wp_init' ) );
86
-
87
- // Triggered on the WP wp_footer action.
88
- add_action( 'wp_footer', array( $this, 'wp_footer' ) );
89
-
90
- // Triggered on he WP plugins_loaded action.
91
- add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) );
92
-
93
- // Handles IPs that have been denied access.
94
- add_action( 'template_redirect', array( $this, 'access_check' ) );
95
- }
96
-
97
- /**
98
- * Loads the plugin text domain.
99
- */
100
- public function plugins_loaded() {
101
- load_plugin_textdomain( 'zero-spam', '', basename( dirname( WORDPRESS_ZERO_SPAM ) ) . '/languages/' );
102
- }
103
-
104
- /**
105
- * Checks is an IP is safe (i.e. from a known bot or crawler)
106
- */
107
- public function is_known_safe_ip() {
108
- $safe_hosts = array(
109
- 'googlebot.com',
110
- 'google.com',
111
- 'search.msn.com',
112
- 'bing.com',
113
- 'yahoo.com',
114
- 'duckduckgo.com',
115
- 'baidu.com',
116
- 'yandex.com',
117
- 'exabot.com',
118
- 'facebook.com',
119
- 'alexa.com',
120
- );
121
-
122
- $safe_user_agents = array(
123
- 'Googlebot',
124
- 'Bingbot',
125
- 'Slurp',
126
- 'DuckDuckBot',
127
- 'Baiduspider',
128
- 'YandexBot',
129
- 'facebot',
130
- 'ia_archiver',
131
- );
132
-
133
- if ( ! $this->current_user_ip ) {
134
- return false;
135
- }
136
-
137
- $ip_host = gethostbyaddr( $this->current_user_ip );
138
- $user_agent = ! empty( $_SERVER['HTTP_USER_AGENT'] ) ? esc_html( $_SERVER['HTTP_USER_AGENT'] ) : false;
139
-
140
- if ( ! $ip_host || ! $user_agent ) {
141
- return false;
142
- }
143
-
144
- // 1. Check hostnames.
145
- foreach ( $safe_hosts as $key => $host ) {
146
- if ( stripos( $ip_host, $host ) !== false ) {
147
- return true;
148
- }
149
- }
150
-
151
- // 2. Check user agents.
152
- foreach ( $safe_user_agents as $key => $agent ) {
153
- if ( stripos( $user_agent, $agent ) !== false ) {
154
- return true;
155
- }
156
- }
157
-
158
- return false;
159
- }
160
-
161
- /**
162
- * Checks an IP access.
163
- *
164
- * @param string $ip The IP address to check.
165
- */
166
- public function get_access( $ip ) {
167
- $access = array(
168
- 'ip_checked' => $ip,
169
- 'has_access' => true,
170
- 'access_checked' => false,
171
- 'cached' => false,
172
- 'blacklist_api' => false,
173
- 'attempts' => false,
174
- );
175
-
176
- if ( ! $ip ) {
177
- return $access;
178
- }
179
-
180
- // Ignore logged in users.
181
- if ( is_user_logged_in() ) {
182
- $access['access_checked'] = 'authenticated';
183
- return $this->set_access_cookies( $access );
184
- }
185
-
186
- // 1. Check if an access check has already been ran for this IP.
187
- if ( $this->get_cookie( 'access_checked' ) && $this->get_cookie( 'ip_checked' ) === $ip ) {
188
- // IP has already been checked, return the saved access.
189
- foreach ( $access as $key => $value ) {
190
- $access[ $key ] = $this->get_cookie( $key );
191
- }
192
-
193
- $access['cached'] = 'cookie';
194
-
195
- return $this->set_access_cookies( $access );
196
- }
197
-
198
- // 2. Check known/safe hosts (i.e. Google crawlers, etc.)
199
- if ( $this->is_known_safe_ip( $ip ) ) {
200
- $access['access_checked'] = 'safe_ip';
201
-
202
- return $this->set_access_cookies( $access );
203
- }
204
-
205
- // 3. Check the whitelisted IP addresses.
206
- $whitelisted_ips = $this->get_whitelisted_ips();
207
-
208
- if ( array_key_exists( $ip, $whitelisted_ips ) ) {
209
- // IP address found in the whitelist.
210
- $access['access_checked'] = 'whitelisted';
211
-
212
- return $this->set_access_cookies( $access );
213
- }
214
-
215
- // 4. Check if IP has been blocked.
216
- $blocked_ip = $this->get_blocked_ip( $ip );
217
- if ( $blocked_ip ) {
218
- $access['attempts'] = $blocked_ip['attempts'];
219
-
220
- // IP has been blocked, check the type.
221
- if ( 'permanent' === $blocked_ip['blocked_type'] ) {
222
- // Permanent block.
223
- $access['access_checked'] = 'permanent_block';
224
- $access['has_access'] = false;
225
- return $this->set_access_cookies( $access );
226
- } else {
227
- // Temporary block, check if still valid.
228
- if ( $this->is_blocked_ip_active( $blocked_ip ) ) {
229
- $access['access_checked'] = 'temporary_block';
230
- $access['has_access'] = false;
231
- return $this->set_access_cookies( $access );
232
- }
233
- }
234
- }
235
-
236
- // 5. Check if the IP appears on the blacklist.
237
- $blacklisted_ip = $this->get_blacklisted_ip( $ip );
238
- if ( $blacklisted_ip ) {
239
- // IP has been blacklisted, check if it needs updated.
240
- $current_datetime = strtotime( current_time( 'mysql' ) );
241
- $last_updated = strtotime( $blacklisted_ip['last_updated'] );
242
- if ( $current_datetime >= ( $last_updated + MONTH_IN_SECONDS ) ) {
243
- // Expired, update the record.
244
- $blacklisted_api_ip = $this->get_ip_from_api( $ip, $blacklisted_ip['blacklist_service'] );
245
- if ( ! $blacklisted_api_ip ) {
246
- // IP not found, delete.
247
- $this->delete_blacklisted_ip( $ip );
248
- } else {
249
- // IP found, update (or delete depending on plugin settings) it.
250
- if ( $this->update_blacklisted_ip( $blacklisted_api_ip, 'update' ) ) {
251
- $access['has_access'] = false;
252
- $access['access_checked'] = 'blacklist';
253
- $access['blacklist_api'] = $blacklisted_ip['blacklist_service'];
254
- $access['attempts'] = $blacklisted_ip['attempts'];
255
-
256
- return $this->set_access_cookies( $access );
257
- }
258
- }
259
- } else {
260
- // Not expired.
261
- $access['has_access'] = false;
262
- $access['access_checked'] = 'blacklist';
263
- $access['blacklist_api'] = $blacklisted_ip['blacklist_service'];
264
- $access['attempts'] = $blacklisted_ip['attempts'];
265
-
266
- return $this->set_access_cookies( $access );
267
- }
268
- }
269
-
270
- // 6. Check the IP against the StopForumSpam API.
271
- $blacklisted_api_ip = $this->get_ip_from_api( $ip, 'stopforumspam' );
272
- if ( $blacklisted_api_ip && $this->update_blacklisted_ip( $blacklisted_api_ip, 'insert' ) ) {
273
- $access['has_access'] = false;
274
- $access['access_checked'] = 'blacklist';
275
- $access['blacklist_api'] = 'stopforumspam';
276
- $access['attempts'] = $blacklisted_api_ip['attempts'];
277
-
278
- return $this->set_access_cookies( $access );
279
- }
280
-
281
- // 7. Check the IP against the BotScout API.
282
- $blacklisted_api_ip = $this->get_ip_from_api( $ip, 'botscout' );
283
- if ( $blacklisted_api_ip && $this->update_blacklisted_ip( $blacklisted_api_ip, 'insert' ) ) {
284
- $access['has_access'] = false;
285
- $access['access_checked'] = 'blacklist';
286
- $access['blacklist_api'] = 'botscout';
287
- $access['attempts'] = $blacklisted_api_ip['attempts'];
288
-
289
- return $this->set_access_cookies( $access );
290
- }
291
-
292
- return $this->set_access_cookies( $access );
293
- }
294
-
295
- /**
296
- * Handles IPs that have been denied access.
297
- */
298
- public function access_check() {
299
- global $wpdb;
300
-
301
- if ( ! $this->current_user_ip_access['has_access'] ) {
302
- $block_type = $this->current_user_ip_access['access_checked'];
303
- $log_data = array();
304
-
305
- // IP doesn't have access, update attempts.
306
- switch ( $block_type ) {
307
- case 'temporary_block':
308
- case 'permanent_block':
309
- case 'blacklist':
310
- $log_data['reason'] = $block_type;
311
-
312
- switch ( $block_type ) {
313
- case 'temporary_block':
314
- case 'permanent_block':
315
- $table = 'blocked';
316
- break;
317
- case 'blacklist':
318
- $table = 'blacklist';
319
- break;
320
- }
321
-
322
- if ( ! $this->current_user_ip_access[ 'attempts' ] ) {
323
- $this->current_user_ip_access[ 'attempts' ] = 1;
324
- } else {
325
- $this->current_user_ip_access[ 'attempts' ]++;
326
- }
327
-
328
- $wpdb->update(
329
- $this->tables[ $table ],
330
- array(
331
- 'attempts' => $this->current_user_ip_access[ 'attempts' ],
332
- ),
333
- array(
334
- 'user_ip' => $this->current_user_ip,
335
- )
336
- );
337
-
338
- $this->set_cookie( 'attempts', $this->current_user_ip_access[ 'attempts' ], 0 );
339
- break;
340
- }
341
-
342
- // Log the detection.
343
- $this->log_detection( 'blocked', $log_data );
344
-
345
- if ( 'redirect' === $this->options['block_handler'] ) {
346
- wp_redirect( esc_url( $this->options['blocked_redirect_url'] ) );
347
- exit();
348
- } else {
349
- status_header( 403 );
350
- die( $this->options['blocked_message'] );
351
- }
352
- }
353
- }
354
-
355
- /**
356
- * Logs a IP detections.
357
- */
358
- public function log_detection( $type, $data ) {
359
- global $wpdb;
360
-
361
- $record = array(
362
- 'user_ip' => $this->current_user_ip,
363
- 'log_type' => $type,
364
- 'date_recorded' => current_time( 'mysql' ),
365
- );
366
-
367
-
368
- // If sharing detections is enabled, send the detection to Zero Spam.
369
- /*if ( 'enabled' === $this->options['share_detections'] ) {
370
- $this->share_detection( $record['user_ip'], $record['log_type'] );
371
- }*/
372
-
373
- // Check if logging detections & 'blocks' are enabled.
374
- if (
375
- 'enabled' !== $this->options['log_spam'] ||
376
- ( 'blocked' === $record['log_type'] && 'enabled' !== $this->options['log_blocked_ips'] )
377
- ) {
378
- // Logging disabled.
379
- return false;
380
- }
381
-
382
- // Logging enabled, get the current URL & IP location information.
383
- $location = $this->get_ip_geolocation( $record['user_ip'] );
384
- $current_url = $this->get_current_url();
385
-
386
- // Add additional information to the detection record.
387
- $record['page_url'] = $current_url;
388
- $record['submission_data'] = wp_json_encode( $data );
389
-
390
- if ( $location ) {
391
- $record['country'] = ! empty( $location['country_code'] ) ? $location['country_code'] : false;
392
- $record['region'] = ! empty( $location['region_code'] ) ? $location['region_code'] : false;
393
- $record['city'] = ! empty( $location['city'] ) ? $location['city'] : false;
394
- $record['latitude'] = ! empty( $location['latitude'] ) ? $location['latitude'] : false;
395
- $record['longitude'] = ! empty( $location['longitude'] ) ? $location['longitude'] : false;
396
- }
397
-
398
- return $wpdb->insert( $this->tables['log'], $record );
399
- }
400
-
401
- /**
402
- * Returns the current page URL.
403
- *
404
- * @return string The current page URL.
405
- */
406
- public function get_current_url() {
407
- global $wp;
408
-
409
- return home_url( add_query_arg( array(), $wp->request ) );
410
- }
411
-
412
- /**
413
- * Retrieves an IP geolocation.
414
- *
415
- * @param string $ip The IP to get geolocation information for.
416
- */
417
- public function get_ip_geolocation( $ip ) {
418
- if ( empty( $this->options['ipstack_api'] ) ) {
419
- return false;
420
- }
421
-
422
- $base_url = 'http://api.ipstack.com/';
423
- $remote_url = $base_url . $ip . '?access_key=' . $this->options['ipstack_api'];
424
- $response = wp_remote_get( $remote_url, array( 'timeout' => $this->options['api_timeout'] ) );
425
-
426
- if ( is_array( $response ) && ! is_wp_error( $response ) ) {
427
- $info = json_decode( $response['body'], true );
428
-
429
- return array(
430
- 'type' => ! empty( $info['type'] ) ? sanitize_text_field( $info['type'] ) : false,
431
- 'continent_code' => ! empty( $info['continent_code'] ) ? sanitize_text_field( $info['continent_code'] ) : false,
432
- 'continent_name' => ! empty( $info['continent_name'] ) ? sanitize_text_field( $info['continent_name'] ) : false,
433
- 'country_code' => ! empty( $info['country_code'] ) ? sanitize_text_field( $info['country_code'] ) : false,
434
- 'country_name' => ! empty( $info['country_name'] ) ? sanitize_text_field( $info['country_name'] ) : false,
435
- 'region_code' => ! empty( $info['region_code'] ) ? sanitize_text_field( $info['region_code'] ) : false,
436
- 'region_name' => ! empty( $info['region_name'] ) ? sanitize_text_field( $info['region_name'] ) : false,
437
- 'city' => ! empty( $info['city'] ) ? sanitize_text_field( $info['city'] ) : false,
438
- 'zip' => ! empty( $info['zip'] ) ? sanitize_text_field( $info['zip'] ) : false,
439
- 'latitude' => ! empty( $info['latitude'] ) ? sanitize_text_field( $info['latitude'] ) : false,
440
- 'longitude' => ! empty( $info['longitude'] ) ? sanitize_text_field( $info['longitude'] ) : false,
441
- 'flag' => ! empty( $info['location']['country_flag'] ) ? sanitize_text_field( $info['location']['country_flag'] ) : false,
442
- );
443
- }
444
-
445
- return false;
446
- }
447
-
448
- /**
449
- * Share a detection with Zero Spam.
450
- */
451
- public function share_detection( $ip, $type ) {
452
- // The Zero Spam API endpoint for sharing detections.
453
- $api_url = 'https://zerospam.org/wp-json/wpzerospamapi/v1/detection/';
454
-
455
- // Setup the request parameters.
456
- $request_args = array(
457
- 'method' => 'POST',
458
- 'body' => array(
459
- 'ip' => $ip,
460
- 'type' => $type,
461
- 'site' => site_url(),
462
- 'email' => get_bloginfo( 'admin_email' ),
463
- 'wpversion' => get_bloginfo( 'version' ),
464
- 'name' => get_bloginfo( 'name' ),
465
- 'desc' => get_bloginfo( 'description' ),
466
- 'language' => get_bloginfo( 'language' ),
467
- 'version' => WORDPRESS_ZERO_SPAM_VERSION,
468
- ),
469
- 'sslverify' => true,
470
- );
471
-
472
- // For debugging purposes only.
473
- if ( WP_DEBUG ) {
474
- $request_args['sslverify'] = false;
475
- }
476
-
477
- // Send the request.
478
- $request = wp_remote_post( $api_url, $request_args );
479
- if ( is_wp_error( $request ) ) {
480
- // Request failed.
481
- return false;
482
- }
483
-
484
- // Request succeeded, return the result.
485
- return wp_remote_retrieve_body( $request );
486
- }
487
-
488
- /**
489
- * Triggered on the WP init action.
490
- */
491
- public function wp_footer() {
492
- // Display debug info if enabled.
493
- if ( 'enabled' === $this->options['debug'] ) {
494
- wp_enqueue_style(
495
- 'wpzerospam-debug',
496
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
497
- 'assets/css/debug.css',
498
- false,
499
- WORDPRESS_ZERO_SPAM_VERSION
500
- );
501
- ?>
502
- <div class="wpzerospam-debug-overlay">
503
- <div class="wpzerospam-debug-item">
504
- <div class="wpzerospam-debug-item-label"><strong><?php esc_html_e( 'Options', 'wpzerospam' ); ?>:</strong></div>
505
- <div class="wpzerospam-debug-item-value">
506
- <?php foreach ( $this->options as $key => $value ): ?>
507
- <div class="wpzerospam-debug-item">
508
- <div class="wpzerospam-debug-item-label"><?php esc_html_e( $key ); ?></div>
509
- <div class="wpzerospam-debug-item-value"><?php esc_html_e( $value ); ?></div>
510
- </div>
511
- <?php endforeach; ?>
512
- </div>
513
- </div>
514
- <div class="wpzerospam-debug-item">
515
- <div class="wpzerospam-debug-item-label"><strong><?php esc_html_e( 'Current User IP', 'wpzerospam' ); ?>:</strong></div>
516
- <div class="wpzerospam-debug-item-value"><?php echo esc_html( $this->current_user_ip ); ?></div>
517
- </div>
518
- <div class="wpzerospam-debug-item">
519
- <div class="wpzerospam-debug-item-label"><strong><?php esc_html_e( 'Current User IP Access', 'wpzerospam' ); ?>:</strong></div>
520
- <div class="wpzerospam-debug-item-value">
521
- <?php foreach ( $this->current_user_ip_access as $key => $value ): ?>
522
- <div class="wpzerospam-debug-item">
523
- <div class="wpzerospam-debug-item-label"><?php esc_html_e( $key ); ?></div>
524
- <div class="wpzerospam-debug-item-value"><?php esc_html_e( $value ); ?></div>
525
- </div>
526
- <?php endforeach; ?>
527
- </div>
528
- </div>
529
- </div>
530
- <?php
531
- }
532
- }
533
-
534
- /**
535
- * Triggered on the WP init action.
536
- */
537
- public function wp_init() {
538
- // Set the plugin options.
539
- $this->options = $this->get_options();
540
- if ( empty( $this->options['blocked_message'] ) ) {
541
- $this->options['blocked_message'] = __( 'You have been blocked from visiting this site by WordPress Zero Spam due to detected spam activity.', 'wpzerospam' );
542
- }
543
-
544
- // Set the current user's IP address.
545
- if ( 'enabled' === $this->options['debug'] && ! empty( $_REQUEST['wpzerospamip'] ) ) {
546
- $this->current_user_ip = $_REQUEST['wpzerospamip'];
547
- } else {
548
- $this->current_user_ip = $this->get_user_ip();
549
- }
550
-
551
- // Set the current user's IP address access.
552
- $this->current_user_ip_access = $this->get_access( $this->current_user_ip );
553
- }
554
-
555
- /**
556
- * Returns the saved plugin options.
557
- */
558
- public function get_options() {
559
- $saved_options = get_option( 'wpzerospam' );
560
- if ( ! $saved_options || ! is_array( $saved_options ) ) {
561
- $saved_options = array();
562
- }
563
-
564
- $options = $this->default_options;
565
- $options = array_merge( $options, $saved_options );
566
- $options = apply_filters( 'wpzerospam_options', $options );
567
-
568
- return $options;
569
- }
570
-
571
- /**
572
- * Sets access cookies.
573
- *
574
- * @param array $access Access details.
575
- */
576
- public function set_access_cookies( $access ) {
577
- foreach ( $access as $key => $value ) {
578
- $this->set_cookie( $key, $value, 0 );
579
- }
580
-
581
- return $access;
582
- }
583
-
584
- /**
585
- * Updates a blacklisted IP.
586
- *
587
- * @param array $api_data The returned IP data from the API.
588
- */
589
- public function update_blacklisted_ip( $api_data, $type ) {
590
- global $wpdb;
591
-
592
- if ( ! $api_data ) {
593
- return false;
594
- }
595
-
596
- switch ( $api_data['api'] ) {
597
- case 'stopforumspam':
598
- if ( ! empty( $api_data['confidence'] ) && $api_data['confidence'] < $this->options['stopforumspam_confidence_min'] ) {
599
- // Doesn't meet the threshold, delete the IP from the database.
600
- $this->delete_blacklisted_ip( $api_data['ip_address'] );
601
- return false;
602
- }
603
- break;
604
- case 'botscout':
605
- if ( ! empty( $api_data['count'] ) && $api_data['count'] < $this->options['botscout_count_min'] ) {
606
- // Doesn't meet the threshold, delete the IP from the database.
607
- $this->delete_blacklisted_ip( $api_data['ip_address'] );
608
- return false;
609
- }
610
- break;
611
- }
612
-
613
- // Update the record.
614
- if ( 'update' === $type ) {
615
- $wpdb->update(
616
- $this->tables['blacklist'],
617
- array(
618
- 'last_updated' => current_time( 'mysql' ),
619
- 'blacklist_data' => wp_json_encode( $api_data ),
620
- ),
621
- array(
622
- 'user_ip' => $api_data['ip_address'],
623
- )
624
- );
625
- } else {
626
- $wpdb->insert(
627
- $this->tables['blacklist'],
628
- array(
629
- 'user_ip' => $api_data['ip_address'],
630
- 'last_updated' => current_time( 'mysql' ),
631
- 'blacklist_service' => $api_data['api'],
632
- 'blacklist_data' => wp_json_encode( $api_data ),
633
- )
634
- );
635
- }
636
-
637
- return true;
638
- }
639
-
640
- /**
641
- * Deletes an IP from the blacklist.
642
- *
643
- * @param string $ip The IP address to delete.
644
- */
645
- public function delete_blacklisted_ip( $ip ) {
646
- global $wpdb;
647
-
648
- $wpdb->delete(
649
- $this->tables['blacklist'],
650
- array(
651
- 'user_ip' => $ip
652
- )
653
- );
654
- }
655
-
656
- /**
657
- * Checks an IP using a third-party API.
658
- *
659
- * @param string $ip IP address to query.
660
- * @param string $api The API to query. stopforumspam | botscout.
661
- * @return array The API query result.
662
- */
663
- public function get_ip_from_api( $ip, $api ) {
664
- $cache_key = sanitize_title( $api . '_' . $ip );
665
- $data = wp_cache_get( $cache_key );
666
-
667
- if ( false === $data ) {
668
- switch ( $api ) {
669
- case 'stopforumspam':
670
- if ( 'enabled' !== $this->options['stop_forum_spam'] ) {
671
- return false;
672
- }
673
-
674
- $api_url = 'https://api.stopforumspam.org/api?';
675
- $params = array(
676
- 'ip' => $ip,
677
- 'json' => '',
678
- );
679
- break;
680
- case 'botscout':
681
- if ( ! $this->options['botscout_api'] ) {
682
- return false;
683
- }
684
-
685
- $api_url = 'https://botscout.com/test/?';
686
- $params = array(
687
- 'ip' => $ip,
688
- 'key' => $this->options['botscout_api'],
689
- );
690
- break;
691
- }
692
-
693
- if ( ! empty( $api_url ) ) {
694
- $endpoint = $api_url . http_build_query( $params );
695
- $response = wp_remote_get( $endpoint, array( 'timeout' => $this->options['api_timeout'] ) );
696
- if ( is_array( $response ) && ! is_wp_error( $response ) ) {
697
- $body_data = wp_remote_retrieve_body( $response );
698
- switch ( $api ) {
699
- case 'stopforumspam':
700
- $body_data = json_decode( $body_data, true );
701
- if (
702
- ! empty( $body_data['success'] ) &&
703
- $body_data['success'] &&
704
- ! empty( $body_data['ip'] ) &&
705
- ! empty( $body_data['ip']['appears'] )
706
- ) {
707
- $data = $body_data['ip'];
708
- $data['api'] = 'stopforumspam';
709
- $data['ip_address'] = $ip;
710
- }
711
- break;
712
- case 'botscout':
713
- if ( strpos( $body_data, '!' ) === false ) {
714
- list( $matched, $type, $count ) = explode( '|', $body_data );
715
- if ( 'Y' === $matched ) {
716
- $data = array(
717
- 'type' => $type,
718
- 'count' => $count,
719
- 'api' => 'botscout',
720
- 'ip_address' => $ip,
721
- );
722
- }
723
- }
724
- break;
725
- }
726
-
727
- if ( $data ) {
728
- wp_cache_set( $cache_key, $data );
729
- }
730
- }
731
- }
732
- }
733
-
734
- return $data;
735
- }
736
-
737
- /**
738
- * Checks if a temporary blocked IP is active.
739
- *
740
- * @param string $blocked_ip The blocked IP record from the DB.
741
- */
742
- public function is_blocked_ip_active( $blocked_ip ) {
743
- $current_datetime = strtotime( current_time( 'mysql' ) );
744
- $start_block = strtotime( $blocked_ip['start_block'] );
745
- $end_block = strtotime( $blocked_ip['end_block'] );
746
- if (
747
- $current_datetime >= $start_block &&
748
- $current_datetime < $end_block
749
- ) {
750
- return true;
751
- }
752
-
753
- return false;
754
- }
755
-
756
- /**
757
- * Checks if an IP has been blacklisted & returns the record if found.
758
- *
759
- * @param string $ip The IP address to check.
760
- * @return array|false The blacklisted API record or false if not found.
761
- */
762
- public function get_blacklisted_ip( $ip ) {
763
- $blacklisted_ip = $this->table_query(
764
- 'blacklist',
765
- array(
766
- 'type' => 'row',
767
- 'select' => array(
768
- 'blacklist_service',
769
- 'blacklist_id',
770
- 'last_updated',
771
- 'attempts',
772
- ),
773
- 'where' => array(
774
- array(
775
- array(
776
- 'key' => 'user_ip',
777
- 'value' => $ip,
778
- 'relation' => '=',
779
- ),
780
- ),
781
- ),
782
- 'limit' => 1,
783
- )
784
- );
785
-
786
- return $blacklisted_ip;
787
- }
788
-
789
- /**
790
- * Checks if an IP has been blocked & returns the record if found.
791
- *
792
- * @param string $ip The IP address to check.
793
- * @return array|false The blocked API record or false if not found.
794
- */
795
- public function get_blocked_ip( $ip ) {
796
- $blocked_ip = $this->table_query(
797
- 'blocked',
798
- array(
799
- 'type' => 'row',
800
- 'select' => array(
801
- 'blocked_type',
802
- 'start_block',
803
- 'end_block',
804
- 'reason',
805
- 'attempts',
806
- ),
807
- 'where' => array(
808
- array(
809
- array(
810
- 'key' => 'user_ip',
811
- 'value' => $ip,
812
- 'relation' => '=',
813
- ),
814
- ),
815
- ),
816
- 'limit' => 1,
817
- )
818
- );
819
-
820
- return $blocked_ip;
821
- }
822
-
823
- /**
824
- * Queries a database table.
825
- *
826
- * @param string $table Table key.
827
- * @param array $args Array of query arguments.
828
- */
829
- public function table_query( $table, $args = array() ) {
830
- global $wpdb;
831
-
832
- $sql = 'SELECT';
833
-
834
- // Select.
835
- $select = '';
836
- if ( ! empty( $args['select'] ) ) {
837
- foreach ( $args['select'] as $key => $value ) {
838
- if ( $select ) {
839
- $select .= ', ';
840
- }
841
- $select .= $value;
842
- }
843
- } else {
844
- $select = '*';
845
- }
846
-
847
- $sql .= ' ' . $select;
848
-
849
- // From.
850
- $sql .= ' FROM ' . $this->tables[ $table ];
851
-
852
- // Where.
853
- $where = '';
854
- if ( ! empty( $args['where'] ) ) {
855
- foreach ( $args['where'] as $key => $where_stmt ) {
856
- if ( ! $where ) {
857
- $where .= 'WHERE ';
858
- }
859
-
860
- foreach ( $where_stmt as $k => $array ) {
861
- $where .= $array['key'];
862
- switch ( $array['relation'] ) {
863
- case '=':
864
- $where .= ' = ';
865
- if ( is_numeric( $array['value'] ) ) {
866
- $where .= ' ' . $array['value'];
867
- } else {
868
- $where .= ' "' . $array['value'] . '"';
869
- }
870
- break;
871
- }
872
- }
873
- }
874
- }
875
-
876
- $sql .= ' ' . $where;
877
-
878
- // Limit.
879
- if ( ! empty( $args['limit'] ) ) {
880
- $sql .= ' LIMIT ' . $args['limit'];
881
- }
882
-
883
- // Offset.
884
- if ( ! empty( $args['offset'] ) ) {
885
- $sql .= ' OFFSET ' . $args['offset'];
886
- }
887
-
888
- if ( ! empty( $args['type'] ) ) {
889
- if ( 'row' === $args['type'] ) {
890
- return $wpdb->get_row( $sql, ARRAY_A );
891
- }
892
- } else {
893
- return $wpdb->get_results( $sql, ARRAY_A );
894
- }
895
- }
896
-
897
- /**
898
- * Returns the whitelisted IPs.
899
- *
900
- * @return array Array of whitelisted IP addresses defined in the plugin settings.
901
- */
902
- public function get_whitelisted_ips() {
903
- $whitelist = explode( PHP_EOL, $this->options['ip_whitelist'] );
904
- if ( ! $whitelist ) {
905
- return array();
906
- }
907
-
908
- $whitelisted = array();
909
- foreach ( $whitelist as $k => $whitelisted_ip ) {
910
- $whitelisted[ $whitelisted_ip ] = $whitelisted_ip;
911
- }
912
-
913
- $whitelisted = apply_filters( 'wpzerospam_whitelisted_ips', $whitelisted );
914
-
915
- return $whitelisted;
916
- }
917
-
918
- /**
919
- * Gets a cookie.
920
- *
921
- * @param string $cookie_key The cookie of the cookie to retrieve.
922
- */
923
- public function get_cookie( $cookie_key ) {
924
- $cookie_key = 'wpzerospam_' . $cookie_key;
925
-
926
- return ! empty( $_COOKIE[ $cookie_key ] ) ? $_COOKIE[ $cookie_key ] : false;
927
- }
928
-
929
- /**
930
- * Sets a cookie.
931
- *
932
- * @param string $cookie_key The cookie key.
933
- * @param string $value The value of the cookie.
934
- */
935
- public function set_cookie( $cookie_key, $value, $expiration ) {
936
- $current_time = current_time( 'mysql' );
937
-
938
- setcookie( 'wpzerospam_' . $cookie_key, $value, $expiration, COOKIEPATH, COOKIE_DOMAIN );
939
- }
940
-
941
- /**
942
- * Returns the current user's IP address.
943
- *
944
- * @link https://www.benmarshall.me/get-ip-address/
945
- */
946
- public function get_user_ip() {
947
- foreach (
948
- array(
949
- 'HTTP_CLIENT_IP',
950
- 'HTTP_X_FORWARDED_FOR',
951
- 'HTTP_X_FORWARDED',
952
- 'HTTP_X_CLUSTER_CLIENT_IP',
953
- 'HTTP_FORWARDED_FOR',
954
- 'HTTP_FORWARDED',
955
- 'REMOTE_ADDR',
956
- )
957
- as $key ) {
958
- if ( array_key_exists( $key, $_SERVER ) === true ) {
959
- foreach ( explode(',', $_SERVER[ $key ]) as $ip_address ) {
960
- $ip_address = trim( $ip_address );
961
-
962
- if (
963
- filter_var(
964
- $ip_address,
965
- FILTER_VALIDATE_IP,
966
- FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
967
- ) !== false
968
- ) {
969
- return $ip_address;
970
- }
971
- }
972
- }
973
- }
974
-
975
- return false;
976
- }
977
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
composer.json DELETED
@@ -1,36 +0,0 @@
1
- {
2
- "name": "bmarshall511/wordpress_zero_spam",
3
- "description": "WordPress Zero Spam makes blocking spam & malicious visitors a cinch. Just install, activate and enjoy a spam-free site.",
4
- "keywords": ["wordpress-plugin"],
5
- "type": "wordpress-plugin",
6
- "homepage": "https://www.benmarshall.me/wordpress-zero-spam",
7
- "authors": [
8
- {
9
- "name": "Ben Marshall (bmarshall)",
10
- "homepage": "https://www.benmarshall.me",
11
- "role": "Maintainer"
12
- }
13
- ],
14
- "support": {
15
- "issues": "https://github.com/bmarshall511/wordpress-zero-spam/issues"
16
- },
17
- "license": "GPL-2.0-or-later",
18
- "minimum-stability": "stable",
19
- "repositories": [
20
- {
21
- "type": "package",
22
- "package": {
23
- "name": "bmarshall511/wordpress-zero-spam",
24
- "version": "master",
25
- "source": {
26
- "url": "git://github.com/bmarshall511/wordpress-zero-spam.git",
27
- "type": "git",
28
- "reference": "master"
29
- }
30
- }
31
- }
32
- ],
33
- "require": {
34
- "php" : ">=7.1"
35
- }
36
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
core/admin/class-admin.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Admin class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core\Admin;
9
+
10
+ use ZeroSpam\Core\Admin\Admin;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Admin.
17
+ *
18
+ * Handles access checks.
19
+ *
20
+ * @since 5.0.0
21
+ */
22
+ class Admin {
23
+
24
+ /**
25
+ * Admin constructor.
26
+ *
27
+ * @since 5.0.0
28
+ * @access public
29
+ */
30
+ public function __construct() {
31
+ new Settings();
32
+ new Dashboard();
33
+
34
+ add_filter( 'plugin_action_links_' . ZEROSPAM_PLUGIN_BASE, array( $this, 'plugin_action_links' ) );
35
+ add_filter( 'plugin_row_meta', array( $this, 'plugin_row_meta' ), 10, 2 );
36
+ add_filter( 'admin_footer_text', array( $this, 'admin_footer_text' ) );
37
+ add_action( 'admin_enqueue_scripts', array( $this, 'scripts' ) );
38
+ }
39
+
40
+ /**
41
+ * Scripts.
42
+ *
43
+ * @since 5.0.0
44
+ * @access public
45
+ */
46
+ public function scripts( $hook_suffix ) {
47
+ if (
48
+ 'dashboard_page_wordpress-zero-spam-dashboard' === $hook_suffix ||
49
+ 'settings_page_wordpress-zero-spam-settings' === $hook_suffix
50
+ ) {
51
+ wp_enqueue_style(
52
+ 'zerospam-admin',
53
+ plugin_dir_url( ZEROSPAM ) . 'assets/css/admin.css',
54
+ false,
55
+ ZEROSPAM_VERSION
56
+ );
57
+
58
+ wp_enqueue_script(
59
+ 'zerospam-admin',
60
+ plugin_dir_url( ZEROSPAM ) . 'assets/js/admin.js',
61
+ array(),
62
+ ZEROSPAM_VERSION,
63
+ true
64
+ );
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Plugin action links.
70
+ *
71
+ * Adds action links to the plugin list table
72
+ *
73
+ * Fired by `plugin_action_links` filter.
74
+ *
75
+ * @since 5.0.0
76
+ * @access public
77
+ *
78
+ * @param array $links An array of plugin action links.
79
+ *
80
+ * @return array An array of plugin action links.
81
+ */
82
+ public function plugin_action_links( $links ) {
83
+ $settings_link = sprintf( '<a href="%1$s">%2$s</a>', admin_url( 'options-general.php?page=wordpress-zero-spam-settings' ), __( 'Settings', 'zerospam' ) );
84
+
85
+ array_unshift( $links, $settings_link );
86
+
87
+ return $links;
88
+ }
89
+
90
+ /**
91
+ * Plugin row meta.
92
+ *
93
+ * Adds row meta links to the plugin list table
94
+ *
95
+ * Fired by `plugin_row_meta` filter.
96
+ *
97
+ * @since 5.0.0
98
+ * @access public
99
+ *
100
+ * @param array $plugin_meta An array of the plugin's metadata, including
101
+ * the version, author, author URI, and plugin URI.
102
+ * @param string $plugin_file Path to the plugin file, relative to the plugins
103
+ * directory.
104
+ *
105
+ * @return array An array of plugin row meta links.
106
+ */
107
+ public function plugin_row_meta( $plugin_meta, $plugin_file ) {
108
+ if ( ZEROSPAM_PLUGIN_BASE === $plugin_file ) {
109
+ $row_meta = [
110
+ 'docs' => '<a href="https://github.com/bmarshall511/wordpress-zero-spam/wiki" aria-label="' . esc_attr( __( 'View WordPress Zero Spam Documentation', 'zerospam' ) ) . '" target="_blank">' . __( 'Docs & FAQs', 'zerospam' ) . '</a>',
111
+ ];
112
+
113
+ $plugin_meta = array_merge( $plugin_meta, $row_meta );
114
+ }
115
+
116
+ return $plugin_meta;
117
+ }
118
+
119
+ /**
120
+ * Admin footer text.
121
+ *
122
+ * Modifies the "Thank you" text displayed in the admin footer.
123
+ *
124
+ * Fired by `admin_footer_text` filter.
125
+ *
126
+ * @since 5.0.0
127
+ * @access public
128
+ *
129
+ * @param string $footer_text The content that will be printed.
130
+ *
131
+ * @return string The content that will be printed.
132
+ */
133
+ public function admin_footer_text( $footer_text ) {
134
+ $current_screen = get_current_screen();
135
+ $is_zerospam_screen = ( $current_screen && false !== strpos( $current_screen->id, 'wordpress-zero-spam' ) );
136
+
137
+ if ( $is_zerospam_screen ) {
138
+ $footer_text = sprintf(
139
+ /* translators: 1: Elementor, 2: Link to plugin review */
140
+ __( 'Enjoyed %1$s? Please leave us a %2$s rating. We really appreciate your support!', 'zerospam' ),
141
+ '<strong>' . __( 'WordPress Zero Spam', 'zerospam' ) . '</strong>',
142
+ '<a href="https://wordpress.org/plugins/zero-spam/#reviews" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
143
+ );
144
+ }
145
+
146
+ return $footer_text;
147
+ }
148
+ }
core/admin/class-dashboard.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Dashboard class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core\Admin;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Dashboard.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class Dashboard {
21
+
22
+ /**
23
+ * Dashboard constructor.
24
+ *
25
+ * @since 5.0.0
26
+ * @access public
27
+ */
28
+ public function __construct() {
29
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
30
+ add_action( 'admin_action_add_blocked_ip', array( $this, 'block_ip' ) );
31
+ }
32
+
33
+ /**
34
+ * Block IP handler.
35
+ *
36
+ * @since 5.0.0
37
+ * @access public
38
+ */
39
+ public function block_ip() {
40
+ $url = parse_url( $_POST['redirect'] );
41
+ $url['query'] = str_replace(
42
+ array(
43
+ 'zerospam-success=1',
44
+ 'zerospam-error=1',
45
+ 'zerospam-error=2',
46
+ 'zerospam-error=3',
47
+ 'zerospam-error=4',
48
+ 'zerospam-error=5',
49
+ ),
50
+ '',
51
+ $url['query']
52
+ );
53
+
54
+ $url = $url['scheme'] . '://' . $url['host'] . ( ! empty( $url['port'] ) ? ':' . $url['port'] : '' ) . $url['path'] . '?' . $url['query'] . ( ! empty( $url['fragment'] ) ? '#' . $url['fragment'] : '' );
55
+
56
+ if ( ! isset( $_POST['zerospam'] ) || ! wp_verify_nonce( $_POST['zerospam'], 'zerospam' ) ) {
57
+ wp_redirect( $url . '&zerospam-error=1' );
58
+ exit;
59
+ }
60
+
61
+ $record = array();
62
+
63
+ $record['user_ip'] = sanitize_text_field( $_POST['blocked_ip'] );
64
+ $record['blocked_type'] = in_array( sanitize_text_field( $_POST['blocked_type'] ), [ 'permanent', 'temporary' ] ) ? sanitize_text_field( $_POST['blocked_type'] ) : false;
65
+ $record['reason'] = sanitize_text_field( $_POST['blocked_reason'] );
66
+ $record['start_block'] = sanitize_text_field( $_POST['blocked_start_date'] );
67
+ $record['end_block'] = sanitize_text_field( $_POST['blocked_end_date'] );
68
+
69
+ if ( ! $record['user_ip'] || ! rest_is_ip_address( $record['user_ip'] ) ) {
70
+ wp_safe_redirect( $url . '&zerospam-error=1' );
71
+ exit;
72
+ }
73
+
74
+ if ( ! $record['blocked_type'] ) {
75
+ wp_safe_redirect( $url . '&zerospam-error=2' );
76
+ exit;
77
+ }
78
+
79
+ if ( 'temporary' === $record['blocked_type'] && ! $record['end_block'] ) {
80
+ wp_safe_redirect( $url . '&zerospam-error=5' );
81
+ exit;
82
+ }
83
+
84
+ if ( $record['start_block'] ) {
85
+ $record['start_block'] = gmdate( 'Y-m-d G:i:s', strtotime( $record['start_block'] ) );
86
+ } else {
87
+ $record['start_block'] = current_time( 'mysql' );
88
+ }
89
+
90
+ if ( $record['end_block'] ) {
91
+ $record['end_block'] = gmdate( 'Y-m-d G:i:s', strtotime( $record['end_block'] ) );
92
+ }
93
+
94
+ if ( 'temporary' === $record['blocked_type'] && ! $record['end_block'] ) {
95
+ wp_safe_redirect( $url . '&error=3' );
96
+ exit;
97
+ }
98
+
99
+ if ( ! ZeroSpam\Includes\DB::blocked( $record ) ) {
100
+ wp_safe_redirect( $url . '&zerospam-error=4' );
101
+ exit;
102
+ }
103
+
104
+ wp_safe_redirect( $url . '&zerospam-success=1' );
105
+ exit;
106
+ }
107
+
108
+ /**
109
+ * Admin menu.
110
+ *
111
+ * @since 5.0.0
112
+ * @access public
113
+ */
114
+ public function admin_menu() {
115
+ add_submenu_page(
116
+ 'index.php',
117
+ __( 'WordPress Zero Spam Dashboard', 'zerospam' ),
118
+ __( 'Zero Spam', 'zerospam' ),
119
+ 'manage_options',
120
+ 'wordpress-zero-spam-dashboard',
121
+ array( $this, 'dashboard_page' )
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Dashboard page.
127
+ *
128
+ * @since 5.0.0
129
+ * @access public
130
+ */
131
+ public function dashboard_page() {
132
+ if ( ! current_user_can( 'manage_options' ) ) {
133
+ return;
134
+ }
135
+ ?>
136
+ <div class="wrap">
137
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
138
+ <?php require ZEROSPAM_PATH . 'includes/templates/admin-callout.php'; ?>
139
+
140
+ <?php if ( ! empty( $_GET['zerospam-error'] ) ): ?>
141
+ <div class="notice notice-error is-dismissible">
142
+ <p><strong>
143
+ <?php
144
+ switch( $_GET['zerospam-error'] ):
145
+ case 1:
146
+ _e( 'Please enter a valid IP address.', 'zerospam' );
147
+ break;
148
+ case 2:
149
+ _e( 'Please select a valid type.', 'zerospam' );
150
+ break;
151
+ case 3:
152
+ _e( 'Please select a date & time when the temporary block should end.', 'zerospam' );
153
+ break;
154
+ case 4:
155
+ _e( 'There was a problem add the IP to the database. Please try again.', 'zerospam' );
156
+ break;
157
+ case 5:
158
+ _e( 'Temporary blocks require an end date.', 'zerospam' );
159
+ break;
160
+ endswitch;
161
+ ?>
162
+ </strong></p>
163
+ <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss this notice.', 'zerospam' ); ?></span></button>
164
+ </div>
165
+ <?php elseif ( ! empty( $_GET['zerospam-success'] ) ): ?>
166
+ <div class="notice notice-success is-dismissible">
167
+ <p><strong><?php _e( 'The blocked IP has been successfully added.', 'wpzerospam' ); ?></strong></p>
168
+ <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss this notice.', 'zerospam' ); ?>.</span></button>
169
+ </div>
170
+ <?php endif; ?>
171
+
172
+ <?php
173
+ $active_tab = 'log';
174
+ if ( ! empty( $_REQUEST['tab'] ) ) {
175
+ $active_tab = sanitize_text_field( $_REQUEST['tab'] );
176
+ }
177
+ ?>
178
+ <div class="nav-tab-wrapper">
179
+ <a id="zerospam-settings-tab-log" class="nav-tab<?php if ( 'log' === $active_tab ) : ?> nav-tab-active<?php endif; ?>" href="<?php echo esc_url( admin_url( 'index.php?page=wordpress-zero-spam-dashboard&tab=log' ) ); ?>"><?php echo __( 'Log', 'zerospam' ); ?></a>
180
+ <a id="zerospam-settings-tab-blocked-ips" class="nav-tab<?php if ( 'blocked' === $active_tab ) : ?> nav-tab-active<?php endif; ?>" href="<?php echo esc_url( admin_url( 'index.php?page=wordpress-zero-spam-dashboard&tab=blocked' ) ); ?>"><?php echo __( 'Blocked IPs', 'zerospam' ); ?></a>
181
+ </div>
182
+
183
+ <div class="zerospam-tabs">
184
+ <?php if ( 'log' === $active_tab ) : ?>
185
+ <div id="tab-log" class="zerospam-tab is-active">
186
+ <h2><?php echo __( 'WordPress Zero Spam Log', 'zerospam' ); ?></h2>
187
+ <?php
188
+ $table_data = new ZeroSpam\Core\Admin\Tables\LogTable();
189
+ $table_data->prepare_items();
190
+ ?>
191
+ <form id="zerospam-log-table" method="post">
192
+ <?php wp_nonce_field( 'zerospam_nonce', 'zerospam_nonce' ); ?>
193
+ <input type="hidden" name="paged" value="1" />
194
+ <?php $table_data->search_box( __( 'Search IPs', 'zerospam' ), 'search-ip' ); ?>
195
+ <?php $table_data->display(); ?>
196
+ </form>
197
+ </div>
198
+ <?php endif; ?>
199
+
200
+ <?php if ( 'blocked' === $active_tab ) : ?>
201
+ <div id="tab-blocked-ips" class="zerospam-tab is-active">
202
+ <h2><?php echo __( 'Blocked IPs', 'zerospam' ); ?></h2>
203
+ <?php
204
+ $table_data = new ZeroSpam\Core\Admin\Tables\BlockedTable();
205
+ $table_data->prepare_items();
206
+ ?>
207
+ <form id="zerospam-blocked-table" method="post">
208
+ <?php wp_nonce_field( 'zerospam_nonce', 'zerospam_nonce' ); ?>
209
+ <input type="hidden" name="paged" value="1" />
210
+ <?php $table_data->search_box( __( 'Search IPs', 'zerospam' ), 'search-ip' ); ?>
211
+ <?php $table_data->display(); ?>
212
+ </form>
213
+ </div>
214
+ <?php endif; ?>
215
+ </div>
216
+
217
+ <div class="zerospam-modal" id="zerospam-block-ip">
218
+ <button class="zerospam-close-modal" aria-label="<?php echo esc_attr( __( 'Close Modal', 'zerospam' ) ); ?>"></button>
219
+ <div class="zerospam-modal-details">
220
+ <div class="zerospam-modal-title">
221
+ <h3><?php echo __( 'Add/Update Blocked IP', 'zerospam' ); ?></h3>
222
+ </div>
223
+ <div class="zerospam-modal-subtitle">
224
+
225
+ </div>
226
+
227
+ <?php require ZEROSPAM_PATH . 'includes/templates/admin-block-ip.php'; ?>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ <?php
232
+ }
233
+ }
core/admin/class-settings.php ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Settings class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core\Admin;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Settings.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class Settings {
21
+
22
+ /**
23
+ * Admin constructor.
24
+ *
25
+ * @since 5.0.0
26
+ * @access public
27
+ */
28
+ public function __construct() {
29
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
30
+ add_action( 'admin_init', array( $this, 'register_settings' ) );
31
+ }
32
+
33
+ /**
34
+ * Admin menu.
35
+ *
36
+ * @since 5.0.0
37
+ * @access public
38
+ */
39
+ public function admin_menu() {
40
+ add_submenu_page(
41
+ 'options-general.php',
42
+ __( 'WordPress Zero Spam Settings', 'zerospam' ),
43
+ __( 'Zero Spam', 'zerospam' ),
44
+ 'manage_options',
45
+ 'wordpress-zero-spam-settings',
46
+ array( $this, 'settings_page' )
47
+ );
48
+ }
49
+
50
+ /**
51
+ * Register settings.
52
+ *
53
+ * @since 5.0.0
54
+ * @access public
55
+ */
56
+ public function register_settings() {
57
+ register_setting( 'wpzerospam', 'wpzerospam' );
58
+
59
+ foreach ( ZeroSpam\Core\Settings::get_sections() as $key => $section ) {
60
+ add_settings_section(
61
+ 'zerospam_' . $key,
62
+ $section['title'],
63
+ array( $this, 'settings_section' ),
64
+ 'wpzerospam'
65
+ );
66
+ }
67
+
68
+ foreach ( ZeroSpam\Core\Settings::get_settings() as $key => $setting ) {
69
+ $options = array(
70
+ 'label_for' => $key,
71
+ 'type' => $setting['type'],
72
+ );
73
+
74
+ if ( ! empty( $setting['options'] ) ) {
75
+ $options['options'] = $setting['options'];
76
+ }
77
+
78
+ if ( ! empty( $setting['value'] ) ) {
79
+ $options['value'] = $setting['value'];
80
+ }
81
+
82
+ if ( ! empty( $setting['placeholder'] ) ) {
83
+ $options['placeholder'] = $setting['placeholder'];
84
+ }
85
+
86
+ if ( ! empty( $setting['class'] ) ) {
87
+ $options['class'] = $setting['class'];
88
+ }
89
+
90
+ if ( ! empty( $setting['desc'] ) ) {
91
+ $options['desc'] = $setting['desc'];
92
+ }
93
+
94
+ if ( ! empty( $setting['suffix'] ) ) {
95
+ $options['suffix'] = $setting['suffix'];
96
+ }
97
+
98
+ if ( ! empty( $setting['min'] ) ) {
99
+ $options['min'] = $setting['min'];
100
+ }
101
+
102
+ if ( ! empty( $setting['max'] ) ) {
103
+ $options['max'] = $setting['max'];
104
+ }
105
+
106
+ if ( ! empty( $setting['step'] ) ) {
107
+ $options['step'] = $setting['step'];
108
+ }
109
+
110
+ if ( ! empty( $setting['field_class'] ) ) {
111
+ $options['field_class'] = $setting['field_class'];
112
+ }
113
+
114
+ add_settings_field(
115
+ $key,
116
+ $setting['title'],
117
+ array( $this, 'settings_field' ),
118
+ 'wpzerospam',
119
+ 'zerospam_' . $setting['section'],
120
+ $options
121
+ );
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Settings section.
127
+ *
128
+ * @since 5.0.0
129
+ * @access public
130
+ */
131
+ public function settings_section( $arg ) {
132
+ }
133
+
134
+ /**
135
+ * Settings field.
136
+ *
137
+ * @since 5.0.0
138
+ * @access public
139
+ */
140
+ public function settings_field( $args ) {
141
+ switch ( $args['type'] ) {
142
+ case 'textarea':
143
+ ?>
144
+ <textarea
145
+ id="<?php echo esc_attr( $args['label_for'] ); ?>"
146
+ name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]"
147
+ rows="5"
148
+ <?php if ( ! empty( $args['field_class'] ) ) : ?>
149
+ class="<?php echo esc_attr( $args['field_class'] ); ?>"
150
+ <?php endif; ?>
151
+ <?php if ( ! empty( $args['placeholder'] ) ) : ?>
152
+ placeholder="<?php echo esc_attr( $args['placeholder'] ); ?>"
153
+ <?php endif; ?>
154
+ ><?php if( ! empty( $args['value'] ) ) : ?><?php echo esc_attr( $args['value'] ); ?><?php endif; ?></textarea>
155
+ <?php
156
+ break;
157
+ case 'url':
158
+ case 'text':
159
+ case 'password':
160
+ case 'number':
161
+ case 'email':
162
+ ?>
163
+ <input
164
+ id="<?php echo esc_attr( $args['label_for'] ); ?>"
165
+ name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]"
166
+ type="<?php echo esc_attr( $args['type'] ); ?>"
167
+ <?php if( ! empty( $args['value'] ) ) : ?>
168
+ value="<?php echo esc_attr( $args['value'] ); ?>"
169
+ <?php endif; ?>
170
+ <?php if ( ! empty( $args['field_class'] ) ) : ?>
171
+ class="<?php echo esc_attr( $args['field_class'] ); ?>"
172
+ <?php endif; ?>
173
+ <?php if ( ! empty( $args['placeholder'] ) ) : ?>
174
+ placeholder="<?php echo esc_attr( $args['placeholder'] ); ?>"
175
+ <?php endif; ?>
176
+ <?php if( ! empty( $args['min'] ) ) : ?>
177
+ min="<?php echo esc_attr( $args['min'] ); ?>"
178
+ <?php endif; ?>
179
+ <?php if( ! empty( $args['max'] ) ) : ?>
180
+ max="<?php echo esc_attr( $args['max'] ); ?>"
181
+ <?php endif; ?>
182
+ <?php if( ! empty( $args['step'] ) ) : ?>
183
+ step="<?php echo esc_attr( $args['step'] ); ?>"
184
+ <?php endif; ?>
185
+ />
186
+ <?php
187
+ break;
188
+ case 'checkbox':
189
+ case 'radio':
190
+ if ( empty( $args['options'] ) ) {
191
+ return;
192
+ }
193
+
194
+ foreach ( $args['options'] as $key => $label ) {
195
+ $selected = false;
196
+ $name = 'wpzerospam[' . esc_attr( $args['label_for'] ) . ']';
197
+ if ( count( $args['options'] ) > 1 && 'checkbox' === $args['type'] ) {
198
+ $name .= '[' . esc_attr( $key ) . ']';
199
+ }
200
+
201
+ if ( ! empty( $args['value'] ) && $args['value'] == $key ) {
202
+ $selected = true;
203
+ }
204
+
205
+ ?>
206
+ <label for="<?php echo esc_attr( $args['label_for'] . $key ); ?>">
207
+ <input
208
+ type="<?php echo esc_attr( $args['type'] ); ?>"
209
+ id="<?php echo esc_attr( $args['label_for'] . $key ); ?>"
210
+ name="<?php echo esc_attr( $name ); ?>"
211
+ value="<?php echo esc_attr( $key ); ?>"
212
+ <?php if ( ! empty( $args['field_class'] ) ) : ?>
213
+ class="<?php echo esc_attr( $args['field_class'] ); ?>"
214
+ <?php endif; ?>
215
+ <?php if ( $selected ) : ?>
216
+ checked="checked"
217
+ <?php endif; ?>
218
+ /> <?php echo $label; ?>
219
+ </label><br />
220
+ <?php
221
+ }
222
+ break;
223
+ }
224
+
225
+ if ( ! empty( $args['suffix'] ) ) {
226
+ echo $args['suffix'];
227
+ }
228
+
229
+ if ( ! empty( $args['desc'] ) ) {
230
+ echo '<p class="description">' . $args['desc'] . '</p>';
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Settings page.
236
+ *
237
+ * @since 5.0.0
238
+ * @access public
239
+ */
240
+ public function settings_page() {
241
+ if ( ! current_user_can( 'manage_options' ) ) {
242
+ return;
243
+ }
244
+ ?>
245
+ <div class="wrap">
246
+ <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
247
+ <?php require ZEROSPAM_PATH . 'includes/templates/admin-callout.php'; ?>
248
+
249
+ <form action="options.php" method="post">
250
+ <?php
251
+ // Output security fields for the registered setting "wpzerospam".
252
+ settings_fields( 'wpzerospam' );
253
+
254
+ // Output setting sections and their fields.
255
+ do_settings_sections( 'wpzerospam' );
256
+
257
+ // Output save settings button.
258
+ submit_button( 'Save Settings' );
259
+ ?>
260
+ </form>
261
+ </div>
262
+ <?php
263
+ }
264
+ }
core/admin/tables/class-blockedtable.php ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Blocked table class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core\Admin\Tables;
9
+
10
+ use ZeroSpam;
11
+ use WP_List_Table;
12
+
13
+ // Security Note: Blocks direct access to the plugin PHP files.
14
+ defined( 'ABSPATH' ) || die();
15
+
16
+ /**
17
+ * Log table.
18
+ *
19
+ * @since 5.0.0
20
+ */
21
+ class BlockedTable extends WP_List_Table {
22
+
23
+ /**
24
+ * Log table constructor.
25
+ *
26
+ * @since 5.0.0
27
+ * @access public
28
+ */
29
+ public function __construct() {
30
+ global $status, $page;
31
+
32
+ $args = array(
33
+ 'singular' => __( 'WordPress Zero Spam Blocked IP', 'zerospam' ),
34
+ 'plural' => __( 'WordPress Zero Spam Blocked IPs', 'zerospam' ),
35
+ );
36
+ parent::__construct( $args );
37
+ }
38
+
39
+ /**
40
+ * Column values.
41
+ *
42
+ * @since 5.0.0
43
+ * @access public
44
+ */
45
+ public function column_default( $item, $column_name ) {
46
+ switch ( $column_name ) {
47
+ case 'user_ip':
48
+ return '<a href="https://www.zerospam.org/ip-lookup/' . urlencode( $item[ $column_name ] ) .'" target="_blank" rel="noopener noreferrer">' . $item[ $column_name ] . '</a>';
49
+ break;
50
+ case 'date_added':
51
+ case 'start_block':
52
+ case 'end_block':
53
+ if ( empty( $item[ $column_name ] ) || '0000-00-00 00:00:00' === $item[ $column_name ] ) {
54
+ return 'N/A';
55
+ } else {
56
+ return gmdate( 'M j, Y g:ia' , strtotime( $item[ $column_name ] ) );
57
+ }
58
+ break;
59
+ case 'actions':
60
+ ob_start();
61
+ ?>
62
+ <button
63
+ class="button zerospam-block-trigger"
64
+ data-ip="<?php echo esc_attr( $item['user_ip'] ); ?>"
65
+ data-reason="<?php echo esc_attr( $item['reason'] ); ?>"
66
+ data-start="<?php echo esc_attr( gmdate( 'Y-m-d', strtotime( $item['start_block'] ) ) ); ?>T<?php echo esc_attr( gmdate( 'H:i', strtotime( $item['start_block'] ) ) ); ?>"
67
+ data-end="<?php echo esc_attr( gmdate( 'Y-m-d', strtotime( $item['end_block'] ) ) ); ?>T<?php echo esc_attr( gmdate( 'H:i', strtotime( $item['end_block'] ) ) ); ?>"
68
+ data-type="<?php echo esc_attr( $item['blocked_type'] ); ?>"
69
+ >
70
+ <?php _e( 'Update Block', 'zerospam' ); ?>
71
+ </button>
72
+ <?php
73
+ return ob_get_clean();
74
+ break;
75
+ default:
76
+ if ( empty( $item[ $column_name ] ) ) {
77
+ return 'N/A';
78
+ } else {
79
+ return $item[ $column_name ];
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Bulk actions.
86
+ *
87
+ * @since 5.0.0
88
+ * @access public
89
+ */
90
+ public function get_bulk_actions() {
91
+ $actions = array(
92
+ 'delete' => __( 'Delete Selected', 'zerospam' ),
93
+ 'delete_all' => __( 'Delete All IPs', 'zerospam' ),
94
+ );
95
+
96
+ return $actions;
97
+ }
98
+
99
+ /**
100
+ * Hidable columns.
101
+ *
102
+ * @since 5.0.0
103
+ * @access public
104
+ */
105
+ public function get_hidden_columns() {
106
+ return array();
107
+ }
108
+
109
+ /**
110
+ * Prepare log items.
111
+ *
112
+ * @since 5.0.0
113
+ * @access public
114
+ */
115
+ public function prepare_items( $args = array() ) {
116
+ $this->process_bulk_action();
117
+
118
+ $columns = $this->get_columns();
119
+ $hidden = $this->get_hidden_columns();
120
+ $sortable = $this->get_sortable_columns();
121
+
122
+ $per_page = 50;
123
+ $current_page = $this->get_pagenum();
124
+ $offset = $per_page * ( $current_page - 1 );
125
+ $order = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( $_REQUEST['order'] ) : 'desc';
126
+ $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_text_field( $_REQUEST['orderby'] ) : 'date_added';
127
+
128
+ $log_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
129
+ $user_ip = ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : false;
130
+
131
+ $query_args = array(
132
+ 'limit' => $per_page,
133
+ 'offset' => $offset,
134
+ 'order' => $order,
135
+ 'orderby' => $orderby,
136
+ 'where' => array(),
137
+ );
138
+
139
+ if ( $log_type ) {
140
+ $query_args['where']['blocked_type'] = array(
141
+ 'value' => $log_type,
142
+ );
143
+ }
144
+
145
+ if ( $user_ip ) {
146
+ $query_args['where']['user_ip'] = array(
147
+ 'value' => $user_ip,
148
+ );
149
+ }
150
+
151
+ $data = ZeroSpam\Includes\DB::query( 'blocked', $query_args );
152
+ if ( ! $data ) {
153
+ return false;
154
+ }
155
+
156
+ $this->items = $data;
157
+
158
+ unset( $query_args['limit'] );
159
+ unset( $query_args['offset'] );
160
+ $data = ZeroSpam\Includes\DB::query( 'blocked', $query_args );
161
+ $total_items = count( $data );
162
+
163
+ $this->set_pagination_args(
164
+ array(
165
+ 'total_items' => $total_items,
166
+ 'per_page' => $per_page,
167
+ 'total_pages' => ceil( $total_items / $per_page ),
168
+ 'orderby' => $orderby,
169
+ 'order' => $order,
170
+ )
171
+ );
172
+
173
+ $this->_column_headers = array( $columns, $hidden, $sortable );
174
+
175
+ $paging_options = array();
176
+ if ( ! empty( $query_args['where'] ) ) {
177
+ foreach ( $query_args['where'] as $key => $value ) {
178
+ switch( $key ) {
179
+ case 'blocked_type':
180
+ $paging_options['type'] = $value['value'];
181
+ break;
182
+ case 'user_ip':
183
+ $paging_options['s'] = $value['value'];
184
+ break;
185
+ }
186
+ }
187
+ }
188
+
189
+ $_SERVER['REQUEST_URI'] = add_query_arg( $paging_options, $_SERVER['REQUEST_URI'] );
190
+ }
191
+
192
+ /**
193
+ * Add more filters.
194
+ *
195
+ * @since 5.0.0
196
+ * @access public
197
+ */
198
+ public function extra_tablenav( $which ) {
199
+ if ( 'top' !== $which ) {
200
+ return;
201
+ }
202
+ ?>
203
+ <div class="alignleft actions">
204
+ <?php
205
+ /*
206
+ echo '<label class="screen-reader-text" for="filter-by-type">' . __( 'Filter by type', 'zerospam' ) . '</label>';
207
+ $options = apply_filters( 'zerospam_types', array() );
208
+ $current_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
209
+ ?>
210
+ <select name="type" id="filter-by-type">
211
+ <option value=""><?php _e( 'All types', 'zerospam' ); ?></option>
212
+ <?php foreach ( $options as $key => $value ) : ?>
213
+ <option<?php if ( $current_type === $key ) : ?> selected="selected"<?php endif; ?> value="<?php echo esc_attr( $key ); ?>"><?php echo $value; ?></option>
214
+ <?php endforeach; ?>
215
+ </select>
216
+ <?php
217
+ submit_button( __( 'Filter', 'zerospam' ), '', 'filter_action', false );
218
+ */
219
+ ?>
220
+ <button class="button zerospam-block-trigger"><?php echo __( 'Add Blocked IP Address', 'zerospam' ); ?></button>
221
+ </div>
222
+ <?php
223
+ }
224
+
225
+ /**
226
+ * Define table columns.
227
+ *
228
+ * @since 5.0.0
229
+ * @access public
230
+ */
231
+ public function get_columns() {
232
+ $columns = array(
233
+ 'cb' => '<input type="checkbox" />',
234
+ 'date_added' => __( 'Date', 'zerospam' ),
235
+ 'blocked_type' => __( 'Type', 'zerospam' ),
236
+ 'user_ip' => __( 'IP Address', 'zerospam' ),
237
+ 'start_block' => __( 'Starts', 'zerospam' ),
238
+ 'end_block' => __( 'Ends', 'zerospam' ),
239
+ 'reason' => __( 'Reason', 'zerospam' ),
240
+ 'actions' => __( 'Actions', 'zerospam' ),
241
+ );
242
+
243
+ return $columns;
244
+ }
245
+
246
+ /**
247
+ * Sortable columns.
248
+ *
249
+ * @since 5.0.0
250
+ * @access public
251
+ */
252
+ public function get_sortable_columns() {
253
+ $sortable_columns = array(
254
+ 'date_added' => array( 'date_added', false ),
255
+ 'blocked_type' => array( 'blocked_type', false ),
256
+ 'user_ip' => array( 'user_ip', false ),
257
+ 'start_block' => array( 'start_block', false ),
258
+ 'end_block' => array( 'end_block', false ),
259
+ );
260
+
261
+ return $sortable_columns;
262
+ }
263
+
264
+ /**
265
+ * Column contact.
266
+ *
267
+ * @since 5.0.0
268
+ * @access public
269
+ */
270
+ public function column_cb( $item ) {
271
+ return sprintf(
272
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
273
+ /*$1%s*/ 'ids',
274
+ /*$2%s*/ $item['blocked_id']
275
+ );
276
+ }
277
+
278
+ /**
279
+ * Process bulk actions.
280
+ *
281
+ * @since 5.0.0
282
+ * @access public
283
+ */
284
+ public function process_bulk_action() {
285
+ global $wpdb;
286
+
287
+ $ids = ( isset( $_REQUEST['ids'] ) ) ? $_REQUEST['ids'] : '';
288
+
289
+ switch( $this->current_action() ) {
290
+ case 'delete':
291
+ $nonce = ( isset( $_REQUEST['zerospam_nonce'] ) ) ? sanitize_text_field( $_REQUEST['zerospam_nonce'] ) : '';
292
+ if ( ! wp_verify_nonce( $nonce, 'zerospam_nonce' ) ) {
293
+ return false;
294
+ }
295
+
296
+ if ( ! empty ( $ids ) && is_array( $ids ) ) {
297
+ foreach ( $ids as $k => $blocked_id ) {
298
+ ZeroSpam\Includes\DB::delete( 'blocked', 'blocked_id', $blocked_id );
299
+ }
300
+ }
301
+ break;
302
+ case 'delete_all':
303
+ ZeroSpam\Includes\DB::delete_all( 'blocked' );
304
+ break;
305
+ }
306
+ }
307
+ }
core/admin/tables/class-logtable.php ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Log table class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core\Admin\Tables;
9
+
10
+ use ZeroSpam;
11
+ use WP_List_Table;
12
+
13
+ // Security Note: Blocks direct access to the plugin PHP files.
14
+ defined( 'ABSPATH' ) || die();
15
+
16
+ /**
17
+ * Log table.
18
+ *
19
+ * @since 5.0.0
20
+ */
21
+ class LogTable extends WP_List_Table {
22
+
23
+ /**
24
+ * Log table constructor.
25
+ *
26
+ * @since 5.0.0
27
+ * @access public
28
+ */
29
+ public function __construct() {
30
+ global $status, $page;
31
+
32
+ $args = array(
33
+ 'singular' => __( 'WordPress Zero Spam Log', 'zerospam' ),
34
+ 'plural' => __( 'WordPress Zero Spam Logs', 'zerospam' ),
35
+ );
36
+ parent::__construct( $args );
37
+ }
38
+
39
+ /**
40
+ * Column values.
41
+ *
42
+ * @since 5.0.0
43
+ * @access public
44
+ */
45
+ public function column_default( $item, $column_name ) {
46
+ switch ( $column_name ) {
47
+ case 'log_type':
48
+ $types = apply_filters( 'zerospam_types', array() );
49
+ if ( ! empty( $types[ $item[ $column_name ] ] ) ) {
50
+ return $types[ $item[ $column_name ] ];
51
+ } else {
52
+ return $item[ $column_name ];
53
+ }
54
+ break;
55
+ case 'user_ip':
56
+ return '<a href="https://www.zerospam.org/ip-lookup/' . urlencode( $item[ $column_name ] ) .'" target="_blank" rel="noopener noreferrer">' . $item[ $column_name ] . '</a>';
57
+ break;
58
+ case 'date_recorded':
59
+ return date( 'M j, Y g:ia' , strtotime( $item[ $column_name ] ) );
60
+ break;
61
+ case 'details':
62
+ ob_start();
63
+ ?>
64
+ <button class="button zerospam-details-trigger" data-id="<?php echo esc_attr( $item['log_id'] ); ?>"><?php _e( 'View', 'zerospam' ); ?></button>
65
+ <div class="zerospam-modal" id="zerospam-details-<?php echo esc_attr( $item['log_id'] ); ?>">
66
+ <button class="zerospam-close-modal" aria-label="<?php echo esc_attr( __( 'Close Modal', 'zerospam' ) ); ?>"></button>
67
+ <?php require ZEROSPAM_PATH . 'includes/templates/admin-modal-details.php'; ?>
68
+ </div>
69
+ <?php
70
+ return ob_get_clean();
71
+ break;
72
+ case 'actions':
73
+ ob_start();
74
+ $blocked = ZeroSpam\Includes\DB::blocked( $item['user_ip'] );
75
+ if ( $blocked ) :
76
+ ?>
77
+ <button
78
+ class="button zerospam-block-trigger"
79
+ data-ip="<?php echo esc_attr( $item['user_ip'] ); ?>"
80
+ data-reason="<?php echo esc_attr( $blocked['reason'] ); ?>"
81
+ data-start="<?php echo esc_attr( gmdate( 'Y-m-d', strtotime( $blocked['start_block'] ) ) ); ?>T<?php echo esc_attr( gmdate( 'H:i', strtotime( $blocked['start_block'] ) ) ); ?>"
82
+ data-end="<?php echo esc_attr( gmdate( 'Y-m-d', strtotime( $blocked['end_block'] ) ) ); ?>T<?php echo esc_attr( gmdate( 'H:i', strtotime( $blocked['end_block'] ) ) ); ?>"
83
+ data-type="<?php echo esc_attr( $blocked['blocked_type'] ); ?>"
84
+ >
85
+ <?php _e( 'Update Block', 'zerospam' ); ?>
86
+ </button>
87
+ <?php
88
+ else :
89
+ ?>
90
+ <button class="button zerospam-block-trigger" data-ip="<?php echo esc_attr( $item['user_ip'] ); ?>"><?php _e( 'Block IP', 'zerospam' ); ?></button>
91
+ <?php
92
+ endif;
93
+
94
+ return ob_get_clean();
95
+ break;
96
+ default:
97
+ if ( empty( $item[ $column_name ] ) ) {
98
+ return 'N/A';
99
+ } else {
100
+ return $item[ $column_name ];
101
+ }
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Bulk actions.
107
+ *
108
+ * @since 5.0.0
109
+ * @access public
110
+ */
111
+ public function get_bulk_actions() {
112
+ $actions = array(
113
+ 'delete' => __( 'Delete Selected', 'zerospam' ),
114
+ 'delete_all' => __( 'Delete All Logs', 'zerospam' ),
115
+ );
116
+
117
+ return $actions;
118
+ }
119
+
120
+ /**
121
+ * Hidable columns.
122
+ *
123
+ * @since 5.0.0
124
+ * @access public
125
+ */
126
+ public function get_hidden_columns() {
127
+ return array();
128
+ }
129
+
130
+ /**
131
+ * Prepare log items.
132
+ *
133
+ * @since 5.0.0
134
+ * @access public
135
+ */
136
+ public function prepare_items( $args = array() ) {
137
+ $this->process_bulk_action();
138
+
139
+ $columns = $this->get_columns();
140
+ $hidden = $this->get_hidden_columns();
141
+ $sortable = $this->get_sortable_columns();
142
+
143
+ $per_page = 50;
144
+ $current_page = $this->get_pagenum();
145
+ $offset = $per_page * ( $current_page - 1 );
146
+ $order = ! empty( $_REQUEST['order'] ) ? sanitize_text_field( $_REQUEST['order'] ) : 'desc';
147
+ $orderby = ! empty( $_REQUEST['orderby'] ) ? sanitize_text_field( $_REQUEST['orderby'] ) : 'date_recorded';
148
+
149
+ $log_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
150
+ $user_ip = ! empty( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : false;
151
+
152
+ $query_args = array(
153
+ 'limit' => $per_page,
154
+ 'offset' => $offset,
155
+ 'order' => $order,
156
+ 'orderby' => $orderby,
157
+ 'where' => array(),
158
+ );
159
+
160
+ if ( $log_type ) {
161
+ $query_args['where']['log_type'] = array(
162
+ 'value' => $log_type,
163
+ );
164
+ }
165
+
166
+ if ( $user_ip ) {
167
+ $query_args['where']['user_ip'] = array(
168
+ 'value' => $user_ip,
169
+ );
170
+ }
171
+
172
+ $data = ZeroSpam\Includes\DB::query( 'log', $query_args );
173
+ if ( ! $data ) {
174
+ return false;
175
+ }
176
+
177
+ $this->items = $data;
178
+
179
+ unset( $query_args['limit'] );
180
+ unset( $query_args['offset'] );
181
+ $data = ZeroSpam\Includes\DB::query( 'log', $query_args );
182
+ $total_items = count( $data );
183
+
184
+ $this->set_pagination_args(
185
+ array(
186
+ 'total_items' => $total_items,
187
+ 'per_page' => $per_page,
188
+ 'total_pages' => ceil( $total_items / $per_page ),
189
+ 'orderby' => $orderby,
190
+ 'order' => $order,
191
+ )
192
+ );
193
+
194
+ $this->_column_headers = array( $columns, $hidden, $sortable );
195
+
196
+ $paging_options = array();
197
+ if ( ! empty( $query_args['where'] ) ) {
198
+ foreach ( $query_args['where'] as $key => $value ) {
199
+ switch( $key ) {
200
+ case 'log_type':
201
+ $paging_options['type'] = $value['value'];
202
+ break;
203
+ case 'user_ip':
204
+ $paging_options['s'] = $value['value'];
205
+ break;
206
+ }
207
+ }
208
+ }
209
+
210
+ $_SERVER['REQUEST_URI'] = add_query_arg( $paging_options, $_SERVER['REQUEST_URI'] );
211
+ }
212
+
213
+ /**
214
+ * Add more filters.
215
+ *
216
+ * @since 5.0.0
217
+ * @access public
218
+ */
219
+ public function extra_tablenav( $which ) {
220
+ if ( 'top' !== $which ) {
221
+ return;
222
+ }
223
+ ?>
224
+ <div class="alignleft actions">
225
+ <?php
226
+ echo '<label class="screen-reader-text" for="filter-by-type">' . __( 'Filter by type', 'zerospam' ) . '</label>';
227
+ $options = apply_filters( 'zerospam_types', array() );
228
+ $current_type = ! empty( $_REQUEST['type'] ) ? sanitize_text_field( $_REQUEST['type'] ) : false;
229
+ ?>
230
+ <select name="type" id="filter-by-type">
231
+ <option value=""><?php _e( 'All types', 'zerospam' ); ?></option>
232
+ <?php foreach ( $options as $key => $value ) : ?>
233
+ <option<?php if ( $current_type === $key ) : ?> selected="selected"<?php endif; ?> value="<?php echo esc_attr( $key ); ?>"><?php echo $value; ?></option>
234
+ <?php endforeach; ?>
235
+ </select>
236
+ <?php
237
+ submit_button( __( 'Filter', 'zerospam' ), '', 'filter_action', false );
238
+ ?>
239
+ </div>
240
+ <?php
241
+ }
242
+
243
+ /**
244
+ * Define table columns.
245
+ *
246
+ * @since 5.0.0
247
+ * @access public
248
+ */
249
+ public function get_columns() {
250
+ $columns = array(
251
+ 'cb' => '<input type="checkbox" />',
252
+ 'date_recorded' => __( 'Date', 'zerospam' ),
253
+ 'log_type' => __( 'Type', 'zerospam' ),
254
+ 'user_ip' => __( 'IP Address', 'zerospam' ),
255
+ 'country' => __( 'Country', 'zerospam' ),
256
+ 'region' => __( 'Region', 'zerospam' ),
257
+ 'city' => __( 'City', 'zerospam' ),
258
+ 'details' => __( 'Details', 'zerospam' ),
259
+ 'actions' => __( 'Actions', 'zerospam' ),
260
+ );
261
+
262
+ return $columns;
263
+ }
264
+
265
+ /**
266
+ * Sortable columns.
267
+ *
268
+ * @since 5.0.0
269
+ * @access public
270
+ */
271
+ public function get_sortable_columns() {
272
+ $sortable_columns = array(
273
+ 'date_recorded' => array( 'date_recorded', false ),
274
+ 'log_type' => array( 'log_type', false ),
275
+ 'user_ip' => array( 'user_ip', false ),
276
+ 'country' => array( 'country', false ),
277
+ 'region' => array( 'region', false ),
278
+ 'city' => array( 'city', false ),
279
+ );
280
+
281
+ return $sortable_columns;
282
+ }
283
+
284
+ /**
285
+ * Column contact.
286
+ *
287
+ * @since 5.0.0
288
+ * @access public
289
+ */
290
+ public function column_cb( $item ) {
291
+ return sprintf(
292
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
293
+ /*$1%s*/ 'ids',
294
+ /*$2%s*/ $item['log_id']
295
+ );
296
+ }
297
+
298
+ /**
299
+ * Process bulk actions.
300
+ *
301
+ * @since 5.0.0
302
+ * @access public
303
+ */
304
+ public function process_bulk_action() {
305
+ global $wpdb;
306
+
307
+ $ids = ( isset( $_REQUEST['ids'] ) ) ? $_REQUEST['ids'] : '';
308
+
309
+ switch( $this->current_action() ) {
310
+ case 'delete':
311
+ $nonce = ( isset( $_REQUEST['zerospam_nonce'] ) ) ? sanitize_text_field( $_REQUEST['zerospam_nonce'] ) : '';
312
+ if ( ! wp_verify_nonce( $nonce, 'zerospam_nonce' ) ) {
313
+ return false;
314
+ }
315
+
316
+ if ( ! empty ( $ids ) && is_array( $ids ) ) {
317
+ foreach ( $ids as $k => $log_id ) {
318
+ ZeroSpam\Includes\DB::delete( 'log', 'log_id', $log_id );
319
+ }
320
+ }
321
+ break;
322
+ case 'delete_all':
323
+ ZeroSpam\Includes\DB::delete_all( 'log' );
324
+ break;
325
+ }
326
+ }
327
+ }
core/class-access.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Access class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Admin.
17
+ *
18
+ * Handles access checks.
19
+ *
20
+ * @since 5.0.0
21
+ */
22
+ class Access {
23
+
24
+ /**
25
+ * Access constructor.
26
+ *
27
+ * @since 5.0.0
28
+ * @access private
29
+ */
30
+ public function __construct() {
31
+ add_action( 'template_redirect', array( $this, 'access_check' ), 0 );
32
+ add_filter( 'zerospam_access_checks', array( $this, 'check_blocked' ), 0, 3 );
33
+ }
34
+
35
+ /**
36
+ * Access check.
37
+ *
38
+ * Determines if the current user should be blocked.
39
+ *
40
+ * @since 5.0.0
41
+ * @access public
42
+ */
43
+ public function access_check() {
44
+ $access = self::get_access();
45
+
46
+ if ( ! empty( $access['blocked'] ) ) {
47
+ $settings = ZeroSpam\Core\Settings::get_settings();
48
+
49
+
50
+ if ( ! empty( $access['details'] ) && is_array( $access['details'] ) ) {
51
+ if ( ! empty( $settings['share_data']['value'] ) && 'enabled' === $settings['share_data']['value'] ) {
52
+ do_action( 'zerospam_share_blocked', $access['details'] );
53
+ }
54
+
55
+ foreach ( $access['details'] as $key => $detail ) {
56
+ if ( ! empty( $detail['blocked'] ) ) {
57
+ if ( empty( $detail['details']['failed'] ) ) {
58
+ $detail['details']['failed'] = $key;
59
+ }
60
+
61
+ if ( ! empty( $settings['log_blocked_ips']['value'] ) && 'enabled' === $settings['log_blocked_ips']['value'] ) {
62
+ ZeroSpam\Includes\DB::log( $detail['type'], $detail['details'] );
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ if ( ! empty( $settings['block_handler']['value'] ) ) {
69
+ switch ( $settings['block_handler']['value'] ) {
70
+ case 403:
71
+ $message = __( 'Your IP address has been blocked by WordPress Zero Spam due to detected spam/malicious activity.', 'zerospam' );
72
+ if ( ! empty( $settings['blocked_message']['value'] ) ) {
73
+ $message = $settings['blocked_message']['value'];
74
+ }
75
+ wp_die(
76
+ $message,
77
+ __( 'Blocked by WordPress Zero Spam', 'zerospam' ),
78
+ array(
79
+ 'response' => 403,
80
+ )
81
+ );
82
+ break;
83
+ case 'redirect':
84
+ $url = 'https://wordpress.org/plugins/zero-spam/';
85
+ if ( ! empty( $settings['blocked_redirect_url']['value'] ) ) {
86
+ $url = esc_url( $settings['blocked_redirect_url']['value'] );
87
+ }
88
+ wp_redirect( $url );
89
+ exit;
90
+ break;
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Checks if an IP has been blocked.
98
+ *
99
+ * @since 5.0.0
100
+ * @access public
101
+ */
102
+ public function check_blocked( $access_checks, $user_ip, $settings ) {
103
+ $access_checks['blocked'] = array(
104
+ 'blocked' => false,
105
+ );
106
+
107
+ $blocked = ZeroSpam\Includes\DB::blocked( $user_ip );
108
+
109
+ if ( $blocked ) {
110
+ $today = new \DateTime();
111
+
112
+ // Check the start & end dates (all blocks require a start date).
113
+ $start_date = new \DateTime();
114
+ if ( ! empty( $blocked['start_block'] ) ) {
115
+ $start_date = new \DateTime( $blocked['start_block'] );
116
+ }
117
+
118
+ if ( $today >= $start_date ) {
119
+ // Check the end date if temporary block.
120
+ if (
121
+ ! empty( $blocked['blocked_type'] ) &&
122
+ 'temporary' === $blocked['blocked_type']
123
+ ) {
124
+ // Temporary block.
125
+ if ( ! empty( $blocked['end_block'] ) ) {
126
+ $end_date = new \DateTime( $blocked['end_block'] );
127
+
128
+ if ( $today < $end_date ) {
129
+ $access_checks['blocked']['blocked'] = true;
130
+ $access_checks['blocked']['type'] = 'blocked';
131
+ $access_checks['blocked']['details'] = $blocked;
132
+ $access_checks['blocked']['details']['failed'] = 'blocked_ips';
133
+ }
134
+ }
135
+ } else {
136
+ // Permanent block.
137
+ $access_checks['blocked']['blocked'] = true;
138
+ $access_checks['blocked']['type'] = 'blocked';
139
+ $access_checks['blocked']['details'] = $blocked;
140
+ $access_checks['blocked']['details']['failed'] = 'blocked_ips';
141
+ }
142
+ }
143
+ }
144
+
145
+ return $access_checks;
146
+ }
147
+
148
+ /**
149
+ * Gets the current user's access.
150
+ *
151
+ * @since 5.0.0
152
+ * @access public
153
+ */
154
+ public function get_access() {
155
+ $settings = ZeroSpam\Core\Settings::get_settings();
156
+
157
+ $access = array(
158
+ 'blocked' => false,
159
+ );
160
+
161
+ $user_ip = ZeroSpam\Core\User::get_ip();
162
+
163
+ if ( $user_ip ) {
164
+ if ( ZeroSpam\Core\Utilities::is_whitelisted( $user_ip ) ) {
165
+ return $access;
166
+ }
167
+
168
+ $access_checks = apply_filters( 'zerospam_access_checks', array(), $user_ip, $settings );
169
+ foreach ( $access_checks as $key => $check ) {
170
+ if ( ! empty( $check['blocked'] ) ) {
171
+ $access['blocked'] = true;
172
+ break;
173
+ }
174
+ }
175
+
176
+ $access['details'] = $access_checks;
177
+ }
178
+
179
+ return $access;
180
+ }
181
+ }
core/class-settings.php ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Settings class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core;
9
+
10
+ // Security Note: Blocks direct access to the plugin PHP files.
11
+ defined( 'ABSPATH' ) || die();
12
+
13
+ /**
14
+ * Settings.
15
+ *
16
+ * @since 5.0.0
17
+ */
18
+ class Settings {
19
+ /**
20
+ * Settings.
21
+ *
22
+ * Holds the plugin settings.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ * @static
27
+ *
28
+ * @var Settings
29
+ */
30
+ public static $settings = array();
31
+
32
+ /**
33
+ * Sections.
34
+ *
35
+ * @since 5.0.0
36
+ * @access public
37
+ * @static
38
+ *
39
+ * @var Settings
40
+ */
41
+ public static $sections = array();
42
+
43
+ /**
44
+ * Returns the plugin setting sections.
45
+ *
46
+ * @since 5.0.0
47
+ * @access public
48
+ */
49
+ public static function get_sections() {
50
+ self::$sections['improve'] = array(
51
+ 'title' => __( 'Improve WordPress Zero Spam', 'zerospam' ),
52
+ );
53
+
54
+ self::$sections['general'] = array(
55
+ 'title' => __( 'General Settings', 'zerospam' ),
56
+ );
57
+
58
+ self::$sections['debug'] = array(
59
+ 'title' => __( 'Debug', 'zerospam' ),
60
+ );
61
+
62
+ return apply_filters( 'zerospam_setting_sections', self::$sections );
63
+ }
64
+
65
+ /**
66
+ * Returns the plugin settings.
67
+ *
68
+ * @since 5.0.0
69
+ * @access public
70
+ */
71
+ public static function get_settings( $key = false ) {
72
+ $options = get_option( 'wpzerospam' );
73
+
74
+ self::$settings['share_data'] = array(
75
+ 'title' => __( 'Usage Data Sharing', 'zerospam' ),
76
+ 'section' => 'improve',
77
+ 'type' => 'checkbox',
78
+ 'options' => array(
79
+ 'enabled' => __( 'Become a super contributor by opting in to share non-sensitive plugin data.', 'zerospam' ),
80
+ ),
81
+ 'value' => ! empty( $options['share_data'] ) ? $options['share_data'] : false,
82
+ );
83
+
84
+ self::$settings['block_handler'] = array(
85
+ 'title' => __( 'IP Block Handler', 'zerospam' ),
86
+ 'desc' => __( 'Determines how blocked IPs are handled when they attempt to access the site.', 'zerospam' ),
87
+ 'section' => 'general',
88
+ 'type' => 'radio',
89
+ 'options' => array(
90
+ 'redirect' => __( 'Redirect user', 'zerospam' ),
91
+ '403' => sprintf(
92
+ wp_kses(
93
+ __( 'Display a <a href="%s" target="_blank" rel="noreferrer noopener"><code>403 Forbidden</code></a> error', 'zerospam' ),
94
+ array(
95
+ 'code' => array(),
96
+ 'a' => array(
97
+ 'target' => array(),
98
+ 'href' => array(),
99
+ 'rel' => array(),
100
+ ),
101
+ )
102
+ ),
103
+ esc_url( 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403' )
104
+ )
105
+ ),
106
+ 'value' => ! empty( $options['block_handler'] ) ? $options['block_handler'] : 403,
107
+ );
108
+
109
+ switch ( self::$settings['block_handler']['value'] ) {
110
+ case 403:
111
+ $message = __( 'Your IP address has been blocked by WordPress Zero Spam due to detected spam/malicious activity.', 'zerospam' );
112
+
113
+ self::$settings['blocked_message'] = array(
114
+ 'title' => __( 'Blocked Message', 'zerospam' ),
115
+ 'desc' => __( 'The message that will be displayed to a blocked user.', 'zerospam' ),
116
+ 'section' => 'general',
117
+ 'type' => 'text',
118
+ 'field_class' => 'large-text',
119
+ 'placeholder' => $message,
120
+ 'value' => ! empty( $options['blocked_message'] ) ? $options['blocked_message'] : $message,
121
+ );
122
+ break;
123
+ case 'redirect':
124
+ self::$settings['blocked_redirect_url'] = array(
125
+ 'title' => __( 'Redirect for Blocked Users', 'zerospam' ),
126
+ 'desc' => __( 'URL blocked users will be redirected to.', 'zerospam' ),
127
+ 'section' => 'general',
128
+ 'type' => 'url',
129
+ 'field_class' => 'regular-text',
130
+ 'placeholder' => 'https://wordpress.org/plugins/zero-spam/',
131
+ 'value' => ! empty( $options['blocked_redirect_url'] ) ? $options['blocked_redirect_url'] : 'https://wordpress.org/plugins/zero-spam/',
132
+ );
133
+ break;
134
+ }
135
+
136
+ self::$settings['log_blocked_ips'] = array(
137
+ 'title' => __( 'Log Blocked IPs', 'zerospam' ),
138
+ 'section' => 'general',
139
+ 'type' => 'checkbox',
140
+ 'desc' => __( 'Enables logging IPs that are blocked from accessing the site. High traffic sites should leave this disabled.', 'zerospam' ),
141
+ 'options' => array(
142
+ 'enabled' => __( 'Enabled', 'zerospam' ),
143
+ ),
144
+ 'value' => ! empty( $options['log_blocked_ips'] ) ? $options['log_blocked_ips'] : false,
145
+ );
146
+
147
+ self::$settings['ip_whitelist'] = array(
148
+ 'title' => __( 'IP Whitelist', 'zerospam' ),
149
+ 'desc' => __( 'Enter IPs that should be whitelisted (IPs that should never be blocked), one per line.', 'zerospam' ),
150
+ 'section' => 'general',
151
+ 'type' => 'textarea',
152
+ 'field_class' => 'regular-text code',
153
+ 'placeholder' => '',
154
+ 'value' => ! empty( $options['ip_whitelist'] ) ? $options['ip_whitelist'] : false,
155
+ );
156
+
157
+ self::$settings['debug'] = array(
158
+ 'title' => __( 'Debug', 'zerospam' ),
159
+ 'desc' => __( 'For troubleshooting site issues.', 'zerospam' ),
160
+ 'section' => 'debug',
161
+ 'type' => 'checkbox',
162
+ 'options' => array(
163
+ 'enabled' => __( 'Enabled', 'zerospam' ),
164
+ ),
165
+ 'value' => ! empty( $options['debug'] ) ? $options['debug'] : false,
166
+ );
167
+
168
+ if ( 'enabled' === self::$settings['debug']['value'] ) {
169
+ self::$settings['debug_ip'] = array(
170
+ 'title' => __( 'Debug IP', 'zerospam' ),
171
+ 'desc' => __( 'Mock a IP address for debugging.', 'zerospam' ),
172
+ 'section' => 'debug',
173
+ 'type' => 'text',
174
+ 'placeholder' => '127.0.0.1',
175
+ 'value' => ! empty( $options['debug_ip'] ) ? $options['debug_ip'] : false,
176
+ );
177
+ }
178
+
179
+ $settings = apply_filters( 'zerospam_settings', self::$settings );
180
+
181
+ if ( $key ) {
182
+ if ( ! empty( $settings[ $key ]['value'] ) ) {
183
+ return $key;
184
+ }
185
+
186
+ return false;
187
+ }
188
+
189
+ return $settings;
190
+ }
191
+ }
core/class-user.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * User class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core;
9
+
10
+ // Security Note: Blocks direct access to the plugin PHP files.
11
+ defined( 'ABSPATH' ) || die();
12
+
13
+ /**
14
+ * Admin.
15
+ *
16
+ * Handles access checks.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class User {
21
+
22
+ /**
23
+ * Gets the current user's IP.
24
+ *
25
+ * @since 5.0.0
26
+ * @access public
27
+ */
28
+ public static function get_ip() {
29
+ $settings = Settings::get_settings();
30
+ if (
31
+ ! empty( $settings['debug']['value'] ) &&
32
+ 'enabled' === $settings['debug']['value'] &&
33
+ ! empty( $settings['debug_ip']['value'] )
34
+ ) {
35
+ return $settings['debug_ip']['value'];
36
+ }
37
+
38
+ if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
39
+ $ip = $_SERVER['HTTP_CLIENT_IP'];
40
+ } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
41
+ $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
42
+ } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED'] ) ) {
43
+ $ip = $_SERVER['HTTP_X_FORWARDED'];
44
+ } elseif ( ! empty( $_SERVER['HTTP_FORWARDED_FOR'] ) ) {
45
+ $ip = $_SERVER['HTTP_FORWARDED_FOR'];
46
+ } elseif ( ! empty( $_SERVER['HTTP_FORWARDED'] ) ) {
47
+ $ip = $_SERVER['HTTP_FORWARDED'];
48
+ } else {
49
+ $ip = $_SERVER['REMOTE_ADDR'];
50
+ }
51
+
52
+ $ip = explode( ',', $ip );
53
+ $ip = trim( $ip[0] );
54
+
55
+ if ( ! rest_is_ip_address( $ip ) ) {
56
+ return false;
57
+ }
58
+
59
+ return $ip;
60
+ }
61
+ }
core/class-utilities.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Utilities class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Core;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Utilities.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class Utilities {
21
+
22
+ /**
23
+ * Outputs a honeypot field
24
+ *
25
+ * @since 5.0.0
26
+ * @access public
27
+ *
28
+ * @return string Returns a HTML honeypot field.
29
+ */
30
+ public static function honeypot_field() {
31
+ return '<input type="text" name="' . self::get_honeypot() . '" value="" style="display: none !important;" />';
32
+ }
33
+
34
+ /**
35
+ * Returns the generated key for checking submissions.
36
+ *
37
+ * @since 5.0.0
38
+ * @access public
39
+ *
40
+ * @return string A unique key used for the 'honeypot' field.
41
+ */
42
+ public static function get_honeypot() {
43
+ $key = get_option( 'wpzerospam_honeypot' );
44
+ if ( ! $key ) {
45
+ $key = wp_generate_password( 5, false, false );
46
+ update_option( 'wpzerospam_honeypot', $key );
47
+ }
48
+
49
+ return $key;
50
+ }
51
+
52
+ /**
53
+ * Returns a cache key.
54
+ *
55
+ * @since 5.0.0
56
+ * @access public
57
+ */
58
+ public static function cache_key( $args, $table = false ) {
59
+ return sanitize_title( $table . '_' . implode( '_', $args ) );
60
+ }
61
+
62
+ /**
63
+ * Remote get.
64
+ *
65
+ * @since 5.0.0
66
+ * @access public
67
+ */
68
+ public static function remote_get( $endpoint, $args = array() ) {
69
+ $response = wp_remote_get( $endpoint, $args );
70
+ if ( is_array( $response ) && ! is_wp_error( $response ) ) {
71
+ return wp_remote_retrieve_body( $response );
72
+ }
73
+
74
+ return false;
75
+ }
76
+
77
+ /**
78
+ * Returns the current URL.
79
+ *
80
+ * @since 5.0.0
81
+ * @access public
82
+ */
83
+ public static function current_url() {
84
+ return ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on' ? "https" : "http" ) . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
85
+ }
86
+
87
+ /**
88
+ * Checks if an IP is on the whitelist.
89
+ *
90
+ * @since 5.0.0
91
+ * @access public
92
+ */
93
+ public static function is_whitelisted( $ip ) {
94
+ $settings = ZeroSpam\Core\Settings::get_settings();
95
+
96
+ // Check whitelist.
97
+ if ( ! empty( $settings['ip_whitelist']['value'] ) ) {
98
+ $whitelisted = explode( PHP_EOL, $settings['ip_whitelist']['value'] );
99
+ if ( $whitelisted ) {
100
+ foreach ( $whitelisted as $key => $whitelisted_ip ) {
101
+ $whitelisted_ip = trim( $whitelisted_ip );
102
+ if ( $whitelisted_ip === $ip ) {
103
+ return true;
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ return false;
110
+ }
111
+ }
inc/admin.php DELETED
@@ -1,849 +0,0 @@
1
- <?php
2
- /**
3
- * Admin interface & functionality
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- /**
10
- * Returns output for detail item.
11
- *
12
- * @since 4.9.9
13
- *
14
- * @param string $label The item label.
15
- * @param string $value The item value.
16
- * @return string HTML for the detail item output.
17
- */
18
- if ( ! function_exists( 'wpzerospam_details_item') ) {
19
- function wpzerospam_admin_details_item( $label, $value ) {
20
- ob_start();
21
- ?>
22
- <div class="wpzerospam-details-item">
23
- <div class="wpzerospam-details-label"><?php echo $label; ?></div>
24
- <div class="wpzerospam-details-data">
25
- <?php
26
- if ( is_array( $value ) ):
27
- print_r( $value );
28
- else:
29
- echo $value;
30
- endif;
31
- ?>
32
- </div>
33
- </div>
34
- <?php
35
- return ob_get_clean();
36
- }
37
- }
38
-
39
- function wpzerospam_admin_menu() {
40
- add_menu_page(
41
- __( 'WordPress Zero Spam Dashboard', 'zero-spam' ),
42
- __( 'WP Zero Spam', 'zero-spam' ),
43
- 'manage_options',
44
- 'wordpress-zero-spam',
45
- 'wpzerospam_dashboard',
46
- 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNTEycHgiIGhlaWdodD0iNDc4cHgiIHZpZXdCb3g9IjAgMCA1MTIgNDc4IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA1OCAoODQ2NjMpIC0gaHR0cHM6Ly9za2V0Y2guY29tIC0tPgogICAgPHRpdGxlPmljb248L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iUGFnZS0xIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iaWNvbiIgZmlsbD0iI0ExQTVBOSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPHBhdGggZD0iTTE1LDExMiBMMjU2LjIyMDA1MiwxMTIgTDI1Ni4yMjAwNTIsMTEyIEwyNTYuMjIwMDUyLDE1IEMyNTYuMjIwMDUyLDYuNzE1NzI4NzUgMjYyLjkzNTc4MSwwIDI3MS4yMjAwNTIsMCBMNDE2LDAgQzQyNC4yODQyNzEsMCA0MzEsNi43MTU3Mjg3NSA0MzEsMTUgTDQzMSwxMTIgTDQzMSwxMTIgTDQ5NywxMTIgQzUwNS4yODQyNzEsMTEyIDUxMiwxMTguNzE1NzI5IDUxMiwxMjcgTDUxMiw0NjMgQzUxMiw0NzEuMjg0MjcxIDUwNS4yODQyNzEsNDc4IDQ5Nyw0NzggTDE1LDQ3OCBDNi43MTU3Mjg3NSw0NzggMCw0NzEuMjg0MjcxIDAsNDYzIEwwLDEyNyBDMCwxMTguNzE1NzI5IDYuNzE1NzI4NzUsMTEyIDE1LDExMiBaIiBpZD0iUmVjdGFuZ2xlIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4='
47
- );
48
-
49
- add_submenu_page(
50
- 'wordpress-zero-spam',
51
- __( 'WordPress Zero Spam Dashboard', 'zero-spam' ),
52
- __( 'Dashboard', 'zero-spam' ),
53
- 'manage_options',
54
- 'wordpress-zero-spam',
55
- 'wpzerospam_dashboard'
56
- );
57
-
58
- add_submenu_page(
59
- 'wordpress-zero-spam',
60
- __( 'Spam Detections', 'zero-spam' ),
61
- __( 'Spam Detections', 'zero-spam' ),
62
- 'manage_options',
63
- 'wordpress-zero-spam-detections',
64
- 'wpzerospam_spam_detections_page'
65
- );
66
-
67
- add_submenu_page(
68
- 'wordpress-zero-spam',
69
- __( 'Blocked IP Addresses', 'zero-spam' ),
70
- __( 'Blocked IPs', 'zero-spam' ),
71
- 'manage_options',
72
- 'wordpress-zero-spam-blocked-ips',
73
- 'wpzerospam_blocked_ips_page'
74
- );
75
-
76
- add_submenu_page(
77
- 'wordpress-zero-spam',
78
- __( 'Blacklisted IPs', 'zero-spam' ),
79
- __( 'Blacklisted IPs', 'zero-spam' ),
80
- 'manage_options',
81
- 'wordpress-zero-spam-blacklisted',
82
- 'wpzerospam_blacklist_page'
83
- );
84
-
85
- add_submenu_page(
86
- 'wordpress-zero-spam',
87
- __( 'WordPress Zero Spam Settings', 'zero-spam' ),
88
- __( 'Settings', 'zero-spam' ),
89
- 'manage_options',
90
- 'wordpress-zero-spam-settings',
91
- 'wpzerospam_options_page'
92
- );
93
- }
94
- add_action( 'admin_menu', 'wpzerospam_admin_menu' );
95
-
96
- function wpzerospam_spam_detections_page() {
97
- if ( ! current_user_can( 'manage_options' ) ) { return; }
98
- ?>
99
- <div class="wrap">
100
- <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
101
-
102
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/callout.php'; ?>
103
-
104
- <?php
105
- /**
106
- * Log table
107
- */
108
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'classes/class-wpzerospam-log-table.php';
109
-
110
- $table_data = new WPZeroSpam_Log_Table();
111
-
112
- // Fetch, prepare, sort, and filter our data...
113
- $table_data->prepare_items();
114
- ?>
115
- <form id="log-table" method="post">
116
- <?php wp_nonce_field( 'wpzerospam_nonce', 'wpzerospam_nonce' ); ?>
117
- <input type="hidden" name="paged" value="1" />
118
- <?php $table_data->search_box( __( 'Search IPs', 'zero-spam' ), 'search-ip' ); ?>
119
- <?php $table_data->display(); ?>
120
- </form>
121
- </div>
122
- <?php
123
- }
124
-
125
- function wpzerospam_blacklist_page() {
126
- if ( ! current_user_can( 'manage_options' ) ) { return; }
127
- ?>
128
- <div class="wrap">
129
- <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
130
-
131
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/callout.php'; ?>
132
-
133
- <?php
134
- /**
135
- * Blocked IP table
136
- */
137
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'classes/class-wpzerospam-blacklisted-table.php';
138
-
139
- $table_data = new WPZeroSpam_Blacklisted_Table();
140
-
141
- // Fetch, prepare, sort, and filter our data...
142
- $table_data->prepare_items();
143
- ?>
144
- <form id="blacklist-table" method="post">
145
- <?php wp_nonce_field( 'wpzerospam_nonce', 'wpzerospam_nonce' ); ?>
146
- <input type="hidden" name="paged" value="1" />
147
- <?php $table_data->search_box( __( 'Search IPs', 'zero-spam' ), 'search-ip' ); ?>
148
- <?php $table_data->display(); ?>
149
- </form>
150
- </div>
151
- <?php
152
- }
153
-
154
- function wpzerospam_add_blocked_ip_action() {
155
- if ( ! empty( $_POST ) ) {
156
- $ip = sanitize_text_field( $_POST['blocked_ip'] );
157
- $type = in_array( sanitize_text_field( $_POST['blocked_type'] ), [ 'permanent', 'temporary' ] ) ? sanitize_text_field( $_POST['blocked_type'] ) : false;
158
- $reason = sanitize_text_field( $_POST['blocked_reason'] );
159
- $blocked_start_date = sanitize_text_field( $_POST['blocked_start_date'] );
160
- $blocked_end_date = sanitize_text_field( $_POST['blocked_end_date'] );
161
-
162
- if ( ! $ip || false === WP_Http::is_ip_address( $ip ) ) {
163
- wp_redirect( $_SERVER['HTTP_REFERER'] . '&error=1' );
164
- exit;
165
- }
166
-
167
- if ( ! $type ) {
168
- wp_redirect( $_SERVER['HTTP_REFERER'] . '&error=2' );
169
- exit;
170
- }
171
-
172
- $data = [ 'blocked_type' => $type ];
173
-
174
- if ( $reason ) {
175
- $data['reason'] = $reason;
176
- } else {
177
- $data['reason'] = NULL;
178
- }
179
-
180
- if ( $blocked_start_date ) {
181
- $data['start_block'] = date( 'Y-m-d G:i:s', strtotime( $blocked_start_date ));
182
- } else {
183
- $data['start_block'] = NULL;
184
- }
185
-
186
- if ( $blocked_end_date ) {
187
- $data['end_block'] = date( 'Y-m-d G:i:s', strtotime( $blocked_end_date ));
188
- } else {
189
- $data['end_block'] = NULL;
190
- }
191
-
192
- if ( 'temporary' == $type && ! $data['end_block'] ) {
193
- wp_redirect( $_SERVER['HTTP_REFERER'] . '&error=3' );
194
- exit;
195
- }
196
-
197
- $data['attempts'] = 0;
198
-
199
- wpzerospam_update_blocked_ip( $ip, $data );
200
- }
201
-
202
- wp_redirect( $_SERVER['HTTP_REFERER'] . '&success=1' );
203
- exit();
204
- }
205
- add_action( 'admin_action_add_blocked_ip', 'wpzerospam_add_blocked_ip_action' );
206
-
207
- function wpzerospam_blocked_ips_page() {
208
- if ( ! current_user_can( 'manage_options' ) ) { return; }
209
- ?>
210
- <div class="wrap">
211
- <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
212
-
213
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/callout.php'; ?>
214
-
215
- <?php if ( ! empty( $_GET['error'] ) ): ?>
216
- <div class="notice notice-error is-dismissible">
217
- <p><strong>
218
- <?php
219
- switch( $_GET['error'] ):
220
- case 1:
221
- _e( 'Please enter a valid IP address.', 'zero-spam' );
222
- break;
223
- case 2:
224
- _e( 'Please select a valid type.', 'zero-spam' );
225
- break;
226
- case 3:
227
- _e( 'Please select a date & time when the temporary block should end.', 'zero-spam' );
228
- break;
229
- endswitch;
230
- ?>
231
- </strong></p>
232
- <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss this notice.', 'zero-spam' ); ?></span></button>
233
- </div>
234
- <?php elseif ( ! empty( $_GET['success'] ) ): ?>
235
- <div class="notice notice-success is-dismissible">
236
- <p><strong><?php _e( 'The blocked IP has been successfully added.', 'wpzerospam' ); ?></strong></p>
237
- <button type="button" class="notice-dismiss"><span class="screen-reader-text"><?php _e( 'Dismiss this notice.', 'zero-spam' ); ?>.</span></button>
238
- </div>
239
- <?php endif; ?>
240
- <form method="post" action="<?php echo admin_url( 'admin.php' ); ?>">
241
- <input type="hidden" name="action" value="add_blocked_ip" />
242
- <div class="wpzerospam-callout wpzerospam-add-ip-container<?php if( ! empty( $_REQUEST['ip'] ) ): ?> wpzerospam-add-ip-container-highlight<?php endif; ?>">
243
- <h2><?php _e( 'Add Blocked IP', 'wpzerospam' ); ?></h2>
244
- <div class="wpzerospam-add-ip-field">
245
- <label for="blocked-ip"><?php _e( 'IP Address', 'zero-spam' ); ?></label>
246
- <input
247
- type="text"
248
- id="blocked-ip"
249
- name="blocked_ip"
250
- value="<?php if( ! empty( $_REQUEST['ip'] ) ): echo esc_attr( $_REQUEST['ip'] ); endif; ?>"
251
- placeholder="e.g. xxx.xxx.x.x"
252
- />
253
- </div>
254
- <div class="wpzerospam-add-ip-field">
255
- <label for="blocked-type"><?php _e( 'Type', 'zero-spam' ); ?></label>
256
- <select id="blocked-type" name="blocked_type">
257
- <option value="temporary"><?php _e( 'Temporary', 'zero-spam' ); ?></option>
258
- <option value="permanent"><?php _e( 'Permanent', 'zero-spam' ); ?></option>
259
- </select>
260
- </div>
261
- <div class="wpzerospam-add-ip-field" id="wpzerospam-add-ip-field-reason">
262
- <label for="blocked-reason"><?php _e( 'Reason', 'zero-spam' ); ?></label>
263
- <input type="text" id="blocked-reason" name="blocked_reason" value="" placeholder="<?php _e( 'e.g. Spammed form', 'zero-spam' ); ?>" />
264
- </div>
265
- <div class="wpzerospam-add-ip-field" id="wpzerospam-add-ip-field-start-date">
266
- <label for="blocked-start-date"><?php _e( 'Start Date', 'zero-spam' ); ?></label>
267
- <input type="datetime-local" id="blocked-start-date" name="blocked_start_date" value="" placeholder="<?php _e( 'Optional', 'zero-spam' ); ?>" />
268
- </div>
269
- <div class="wpzerospam-add-ip-field" id="wpzerospam-add-ip-field-end-date">
270
- <label for="blocked-end-date"><?php _e( 'End Date', 'zero-spam' ); ?></label>
271
- <input type="datetime-local" id="blocked-end-date" name="blocked_end_date" value="" placeholder="<?php _e( 'Optional', 'zero-spam' ); ?>" />
272
- </div>
273
- <div class="wpzerospam-add-ip-field" id="wpzerospam-add-ip-field-submit">
274
- <input type="submit" class="button button-primary" value="<?php _e( 'Add Blocked IP', 'zero-spam' ); ?>" />
275
- </div>
276
- </div>
277
- </form>
278
-
279
- <?php
280
- /**
281
- * Blocked IP table
282
- */
283
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'classes/class-wpzerospam-blocked-ip-table.php';
284
-
285
- $table_data = new WPZeroSpam_Blocked_IP_Table();
286
-
287
- // Fetch, prepare, sort, and filter our data...
288
- $table_data->prepare_items();
289
- ?>
290
- <form id="blocked-table" method="post">
291
- <?php wp_nonce_field( 'wpzerospam_nonce', 'wpzerospam_nonce' ); ?>
292
- <input type="hidden" name="paged" value="1" />
293
- <?php $table_data->search_box( __( 'Search IPs', 'zero-spam' ), 'search-ip' ); ?>
294
- <?php $table_data->display(); ?>
295
- </form>
296
- </div>
297
- <?php
298
- }
299
-
300
- function wpzerospam_dashboard() {
301
- if ( ! current_user_can( 'manage_options' ) ) { return; }
302
-
303
- $log = wpzerospam_query( 'log' );
304
-
305
- $predefined_colors = [
306
- '#1a0003', '#4d000a', '#800011', '#b30017', '#e6001e', '#ff1a38', '#ff4d64', '#ff8090', '#ffb3bd', '#ffe5e9'
307
- ];
308
- ?>
309
- <div class="wrap">
310
- <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
311
-
312
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/callout.php'; ?>
313
-
314
- <h2><?php _e( 'Statistics', 'wpzerospam' ); ?></h2>
315
- <div class="wpzerospam-boxes">
316
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/map.php'; ?>
317
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/ip-list.php'; ?>
318
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/countries-pie-chart.php'; ?>
319
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/regions-pie-chart.php'; ?>
320
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/spam-line-chart.php'; ?>
321
- </div>
322
- </div>
323
- <?php
324
- }
325
-
326
- function wpzerospam_options_page() {
327
- if ( ! current_user_can( 'manage_options' ) ) { return; }
328
- ?>
329
- <div class="wrap">
330
- <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
331
-
332
- <?php require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'templates/callout.php'; ?>
333
-
334
- <form action="options.php" method="post">
335
- <?php
336
- // Output security fields for the registered setting "wpzerospam"
337
- settings_fields( 'wpzerospam' );
338
-
339
- // Output setting sections and their fields
340
- do_settings_sections( 'wpzerospam' );
341
-
342
- // Output save settings button
343
- submit_button( 'Save Settings' );
344
- ?>
345
- </form>
346
- </div>
347
- <?php
348
- }
349
-
350
- function wpzerospam_validate_options( $input ) {
351
- if ( empty( $input['log_spam'] ) ) { $input['log_spam'] = 'disabled'; }
352
- if ( empty( $input['log_blocked_ips'] ) ) { $input['log_blocked_ips'] = 'disabled'; }
353
- if ( empty( $input['auto_block_ips'] ) ) { $input['auto_block_ips'] = 'disabled'; }
354
- if ( empty( $input['auto_block_period'] ) ) { $input['auto_block_period'] = 0; }
355
- if ( empty( $input['botscout_api'] ) ) { $input['botscout'] = false; }
356
- if ( empty( $input['auto_block_permanently'] ) ) { $input['auto_block_permanently'] = 3; }
357
- if ( empty( $input['api_timeout'] ) ) { $input['api_timeout'] = 5; }
358
- if ( empty( $input['stopforumspam_confidence_min'] ) ) { $input['stopforumspam_confidence_min'] = 20; }
359
- if ( empty( $input['botscout_count_min'] ) ) { $input['botscout_count_min'] = 5; }
360
- if ( empty( $input['cookie_expiration'] ) ) { $input['cookie_expiration'] = 7; }
361
-
362
- if ( empty( $input['ip_whitelist'] ) ) {
363
- $input['ip_whitelist'] = '';
364
- } else {
365
- $whitelist = explode( PHP_EOL, $input['ip_whitelist'] );
366
- $cleaned_whitelist = '';
367
- foreach( $whitelist as $k => $whitelisted_ip ) {
368
- $whitelisted_ip = trim( $whitelisted_ip );
369
-
370
- if ( rest_is_ip_address( $whitelisted_ip ) ) {
371
- if ( $cleaned_whitelist ) { $cleaned_whitelist .= "\n"; }
372
- $cleaned_whitelist .= $whitelisted_ip;
373
- }
374
- }
375
-
376
- $input['ip_whitelist'] = $cleaned_whitelist;
377
- }
378
-
379
- if ( empty( $input['verify_bp_registrations'] ) ) {
380
- $input['verify_bp_registrations'] = 'disabled';
381
- }
382
-
383
- if ( empty( $input['verify_wpforms'] ) ) {
384
- $input['verify_wpforms'] = 'disabled';
385
- }
386
-
387
- if ( empty( $input['verify_fluentform'] ) ) {
388
- $input['verify_fluentform'] = 'disabled';
389
- }
390
-
391
- if ( empty( $input['verify_formidable'] ) ) {
392
- $input['verify_formidable'] = 'disabled';
393
- }
394
-
395
- if ( empty( $input['stop_forum_spam'] ) ) {
396
- $input['stop_forum_spam'] = 'disabled';
397
- }
398
-
399
- if ( empty( $input['share_detections'] ) ) {
400
- $input['share_detections'] = 'disabled';
401
- }
402
-
403
- if ( empty( $input['blocked_message'] ) ) {
404
- $input['blocked_message'] = __( 'You have been blocked from visiting this site by WordPress Zero Spam due to detected spam activity.', 'zero-spam' );
405
- }
406
-
407
- $input = apply_filters( 'wpzerospam_admin_validation', $input );
408
-
409
- return $input;
410
- }
411
-
412
- /**
413
- * Add settings link to plugin description
414
- */
415
- function wpzerospam_admin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
416
- $links = [
417
- 'settings' => '<a href="' . admin_url( 'admin.php?page=wordpress-zero-spam-settings' ) . '">' . __( 'Settings' ) . '</a>'
418
- ];
419
-
420
- return array_merge( $links, $actions );
421
- }
422
-
423
-
424
- function wpzerospam_admin_init() {
425
- if( ! function_exists( 'is_plugin_active' ) ) {
426
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
427
- }
428
-
429
- $options = wpzerospam_options();
430
-
431
- // Add settings link to plugin description
432
- add_filter( 'plugin_action_links_' . plugin_basename( WORDPRESS_ZERO_SPAM ), 'wpzerospam_admin_action_links', 10, 4 );
433
-
434
- register_setting( 'wpzerospam', 'wpzerospam', 'wpzerospam_validate_options' );
435
-
436
- add_settings_section( 'wpzerospam_general_settings', __( 'General Plugin Settings', 'zero-spam' ), 'wpzerospam_general_settings_cb', 'wpzerospam' );
437
- add_settings_section( 'wpzerospam_autoblocks', __( 'Auto-block Settings', 'zero-spam' ), 'wpzerospam_autoblock_settings_cb', 'wpzerospam' );
438
- add_settings_section( 'wpzerospam_onsite', __( 'On-site Spam Prevention', 'zero-spam' ), 'wpzerospam_onsite_cb', 'wpzerospam' );
439
- add_settings_section( 'wpzerospam_spam_checks', __( 'Integrations & Third-party APIs', 'zero-spam' ), 'wpzerospam_spam_checks_cb', 'wpzerospam' );
440
-
441
- /*add_settings_field( 'share_detections', __( 'Share Spam/Malicious IP Detections', 'wpzerospam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
442
- 'label_for' => 'share_detections',
443
- 'type' => 'checkbox',
444
- 'multi' => false,
445
- 'desc' => sprintf(
446
- wp_kses(
447
- __( 'Help support WordPress Zero Spam and strengthen its ability to detect spammers by sharing spam detections. For more information about what\'s being shared, see the plugin\'s <a href="%1$s" target="_blank" rel="noreferrer noopener">documentation</a>.', 'wpzerospam' ),
448
- array(
449
- 'strong' => array(),
450
- 'a' => array(
451
- 'href' => array(),
452
- 'rel' => array(),
453
- 'target' => array(),
454
- )
455
- )
456
- ),
457
- esc_url('https://www.benmarshall.me/wordpress-zero-spam/')
458
- ),
459
- 'options' => [
460
- 'enabled' => __( 'Enabled', 'wpzerospam' )
461
- ]
462
- ]);*/
463
-
464
- // Cookie expiration
465
- add_settings_field( 'cookie_expiration', __( 'Cookie Expiration', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
466
- 'label_for' => 'cookie_expiration',
467
- 'type' => 'number',
468
- 'desc' => wp_kses(
469
- __( 'Number of days until a user\'s cookie is expired. Helps boost site performance so access & blacklist checks aren\'t sent each page visit. <strong>Minimum recommend is 7 days</strong>.', 'zero-spam' ),
470
- [ 'strong' => [] ]
471
- ),
472
- 'class' => 'small-text',
473
- 'placeholder' => '7',
474
- 'suffix' => __( 'days', 'zero-spam' )
475
- ]);
476
-
477
- // Determines is spam detected IPs should automatically be blocked
478
- add_settings_field( 'auto_block_ips', __( 'Auto-block IPs', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_autoblocks', [
479
- 'label_for' => 'auto_block_ips',
480
- 'type' => 'checkbox',
481
- 'multi' => false,
482
- 'desc' => __( 'Auto-blocks IPs addresses that trigger a spam detection.', 'zero-spam' ),
483
- 'options' => [
484
- 'enabled' => __( 'Enabled', 'zero-spam' )
485
- ]
486
- ]);
487
-
488
- if ( 'enabled' == $options['auto_block_ips'] ) {
489
- // Number of minutes a IP should be blocked after a auto-block
490
- add_settings_field( 'auto_block_period', __( 'Auto-block Period', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_autoblocks', [
491
- 'label_for' => 'auto_block_period',
492
- 'type' => 'number',
493
- 'desc' => __( 'Number of minutes a user will be blocked from viewing the site after being auto-blocked.', 'zero-spam' ),
494
- 'class' => 'small-text',
495
- 'placeholder' => '30',
496
- 'suffix' => __( 'minutes', 'zero-spam' )
497
- ]);
498
- }
499
-
500
- // Number of spam attempts before the IP is permanently blocked
501
- add_settings_field( 'auto_block_permanently', __( 'Permanently Auto-block', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_autoblocks', [
502
- 'label_for' => 'auto_block_permanently',
503
- 'type' => 'number',
504
- 'desc' => __( 'Number of spam detections before an IP is permanently blocked.', 'zero-spam' ),
505
- 'class' => 'small-text',
506
- 'placeholder' => 3
507
- ]);
508
-
509
- // API timeout
510
- add_settings_field( 'api_timeout', __( 'API Timeout', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
511
- 'label_for' => 'api_timeout',
512
- 'type' => 'number',
513
- 'desc' => wp_kses(
514
- __( 'Number of seconds to allow an API to return a response.<br /><strong>WARNING:</strong> Setting this too high could cause your site to load slowly. Setting too low may not allow an API enough time to respond with a result. <strong>Recommended is 5 seconds.</strong>.', 'zero-spam' ),
515
- [ 'strong' => [], 'br' => [] ]
516
- ),
517
- 'class' => 'small-text',
518
- 'placeholder' => '30',
519
- 'suffix' => __( 'seconds', 'zero-spam' )
520
- ]);
521
-
522
- if ( 'enabled' == $options['log_spam'] ) {
523
- // Redirect URL for spam detections
524
- add_settings_field( 'ipstack_api', __( 'ipstack API Key', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
525
- 'label_for' => 'ipstack_api',
526
- 'type' => 'text',
527
- 'placeholder' => __( 'Enter your ipstack API key.', 'zero-spam' ),
528
- 'class' => 'regular-text',
529
- 'desc' => sprintf(
530
- wp_kses(
531
- __( 'Enter your <a href="%s" target="_blank" rel="noopener noreferrer">ipstack API key</a> to enable location-based statistics. Don\'t have an API key? <a href="%s" target="_blank" rel="noopener noreferrer"><strong>Get one for free!</strong></a>', 'zero-spam' ),
532
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
533
- ),
534
- esc_url( 'https://ipstack.com/' ),
535
- esc_url( 'https://ipstack.com/signup/free' )
536
- )
537
- ]);
538
- }
539
-
540
- // Enables the ability to check IPs against BotScout blacklists.
541
- add_settings_field( 'botscout_api', __( 'BotScout API Key', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
542
- 'label_for' => 'botscout_api',
543
- 'type' => 'text',
544
- 'class' => 'regular-text',
545
- 'placeholder' => __( 'Enter your free BotScout API key.', 'zero-spam' ),
546
- 'desc' => sprintf(
547
- wp_kses(
548
- __( 'Enter your BotScout API key to check user IPs against <a href="%s" target="_blank" rel="noopener noreferrer">BotScout</a>\'s blacklist. Don\'t have an API key? <a href="%s" target="_blank" rel="noopener noreferrer"><strong>Get one for free!</strong></a>', 'zero-spam' ),
549
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
550
- ),
551
- esc_url( 'https://botscout.com/' ),
552
- esc_url( 'https://botscout.com/getkey.htm' )
553
- )
554
- ]);
555
-
556
- // BotScout count minimum
557
- add_settings_field( 'botscout_count_min', __( 'BotScout Count Minimum', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
558
- 'label_for' => 'botscout_count_min',
559
- 'type' => 'number',
560
- 'desc' => sprintf(
561
- wp_kses(
562
- __( 'Minimum <a href="%s" target="_blank" rel="noopener noreferrer">count</a> an IP must meet before being marked as spam/malicious.<br /><strong>WARNING:</strong> Setting this too low could cause users to be blocked that shouldn\'t be, <strong>recommended is 5</strong>.', 'zero-spam' ),
563
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ], 'br' => [] ]
564
- ),
565
- esc_url( 'https://botscout.com/api.htm' )
566
- ),
567
- 'class' => 'small-text',
568
- 'placeholder' => '20',
569
- ]);
570
-
571
- // Enables the ability to check IPs against Stop Forum Spam blacklists.
572
- add_settings_field( 'stop_forum_spam', __( 'Stop Forum Spam', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
573
- 'label_for' => 'stop_forum_spam',
574
- 'type' => 'checkbox',
575
- 'multi' => false,
576
- 'desc' => sprintf(
577
- wp_kses(
578
- __( 'Checks user IPs against <a href="%s" target="_blank" rel="noopener noreferrer">Stop Forum Spam</a>\'s blacklist.', 'zero-spam' ),
579
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
580
- ),
581
- esc_url( 'https://www.stopforumspam.com/' )
582
- ),
583
- 'options' => [
584
- 'enabled' => __( 'Enabled', 'zero-spam' )
585
- ]
586
- ]);
587
-
588
- // StopForumSpam confidence minimum
589
- add_settings_field( 'stopforumspam_confidence_min', __( 'Stop Forum Spam Confidence Minimum', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
590
- 'label_for' => 'stopforumspam_confidence_min',
591
- 'type' => 'number',
592
- 'desc' => sprintf(
593
- wp_kses(
594
- __( 'Minimum <a href="%s" target="_blank" rel="noopener noreferrer">confidence score</a> an IP must meet before being marked as spam/malicious.<br /><strong>WARNING:</strong> Setting this too low could cause users to be blocked that shouldn\'t be, <strong>recommended is 20%%</strong>.', 'zero-spam' ),
595
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ], 'br' => [] ]
596
- ),
597
- esc_url( 'https://www.stopforumspam.com/usage' )
598
- ),
599
- 'class' => 'small-text',
600
- 'placeholder' => '20',
601
- 'suffix' => '%'
602
- ]);
603
-
604
- // How to handle blocks
605
- add_settings_field( 'block_handler', __( 'Blocked IPs', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
606
- 'label_for' => 'block_handler',
607
- 'type' => 'radio',
608
- 'desc' => __( 'Determines how blocked IPs are handled when they attempt to visit the site.', 'zero-spam' ),
609
- 'options' => [
610
- 'redirect' => __( 'Redirect user', 'zero-spam' ),
611
- '403' => sprintf(
612
- wp_kses(
613
- __( 'Display a <a href="%s" target="_blank" rel="noreferrer noopener"><code>403 Forbidden</code></a> error', 'zero-spam' ),
614
- [ 'code' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
615
- ),
616
- esc_url( 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403' )
617
- )
618
- ]
619
- ]);
620
-
621
- if ( 'redirect' == $options['block_handler'] ) {
622
- // Redirect URL for blocked users
623
- add_settings_field( 'blocked_redirect_url', __( 'Redirect for Blocked Users', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
624
- 'label_for' => 'blocked_redirect_url',
625
- 'type' => 'url',
626
- 'class' => 'regular-text',
627
- 'desc' => __( 'URL blocked users will be taken to.', 'zero-spam' ),
628
- 'placeholder' => 'e.g. https://google.com'
629
- ]);
630
- } else {
631
- // Blocked message
632
- add_settings_field( 'blocked_message', __( 'Blocked Message', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
633
- 'label_for' => 'blocked_message',
634
- 'type' => 'text',
635
- 'class' => 'large-text',
636
- 'desc' => __( 'The message that will be displayed to a blocked user.', 'zero-spam' ),
637
- 'placeholder' => __( 'You have been blocked from visiting this site by WordPress Zero Spam due to detected spam activity.', 'zero-spam' )
638
- ]);
639
- }
640
-
641
- // How to handle spam detections
642
- add_settings_field( 'spam_handler', __( 'Spam Detections', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
643
- 'label_for' => 'spam_handler',
644
- 'type' => 'radio',
645
- 'desc' => __( 'Determines how users are handled when spam is detected.', 'zero-spam' ),
646
- 'options' => [
647
- 'redirect' => __( 'Redirect user', 'zero-spam' ),
648
- '403' => sprintf(
649
- wp_kses(
650
- __( 'Display a <a href="%s" target="_blank" rel="noreferrer noopener"><code>403 Forbidden</code></a> error', 'zero-spam' ),
651
- [ 'code' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
652
- ),
653
- esc_url( 'https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403' )
654
- )
655
- ]
656
- ]);
657
-
658
- if ( 'redirect' == $options['spam_handler'] ) {
659
- // Redirect URL for spam detections
660
- add_settings_field( 'spam_redirect_url', __( 'Redirect for Spam', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
661
- 'label_for' => 'spam_redirect_url',
662
- 'type' => 'url',
663
- 'class' => 'regular-text',
664
- 'desc' => __( 'URL users will be taken to when a spam submission is detected.', 'zero-spam' ),
665
- 'placeholder' => 'e.g. https://google.com'
666
- ]);
667
- } else {
668
- // Spam message
669
- add_settings_field( 'spam_message', __( 'Spam Detection Message', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
670
- 'label_for' => 'spam_message',
671
- 'type' => 'text',
672
- 'class' => 'large-text',
673
- 'desc' => __( 'The message that will be displayed when spam is detected.', 'zero-spam' ),
674
- 'placeholder' => __( 'There was a problem with your submission. Please go back and try again.', 'zero-spam' )
675
- ]);
676
- }
677
-
678
- // Toggle logging of blocked IPs
679
- add_settings_field( 'log_blocked_ips', __( 'Log Blocked IPs', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
680
- 'label_for' => 'log_blocked_ips',
681
- 'type' => 'checkbox',
682
- 'multi' => false,
683
- 'desc' => __( 'Enables logging of when IPs are blocked from accessing the site.', 'zero-spam' ),
684
- 'options' => [
685
- 'enabled' => __( 'Enabled', 'zero-spam' )
686
- ]
687
- ]);
688
-
689
- // Log spam detections
690
- add_settings_field( 'log_spam', __( 'Log Spam Detections', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
691
- 'label_for' => 'log_spam',
692
- 'type' => 'checkbox',
693
- 'multi' => false,
694
- 'desc' => __( 'Enables logging of spam detections and provides an admin interface to view statistics.', 'zero-spam' ),
695
- 'options' => [
696
- 'enabled' => __( 'Enabled', 'zero-spam' )
697
- ]
698
- ]);
699
-
700
- do_action( 'wpzerospam_admin_options' );
701
-
702
- // BuddyPress registrations spam check
703
- if ( function_exists( 'bp_is_active' ) ) {
704
- add_settings_field( 'verify_bp_registrations', __( 'Verify BuddyPress Registrations', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
705
- 'label_for' => 'verify_bp_registrations',
706
- 'type' => 'checkbox',
707
- 'multi' => false,
708
- 'desc' => __( 'Enables spam detection for BuddyPress registrations.', 'zero-spam' ),
709
- 'options' => [
710
- 'enabled' => __( 'Enabled', 'zero-spam' )
711
- ]
712
- ]);
713
- }
714
-
715
- // WPForms spam check
716
- if ( is_plugin_active( 'wpforms/wpforms.php' ) || is_plugin_active( 'wpforms-lite/wpforms.php' ) ) {
717
- add_settings_field( 'verify_wpforms', __( 'Verify WPForms Submissions', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
718
- 'label_for' => 'verify_wpforms',
719
- 'type' => 'checkbox',
720
- 'multi' => false,
721
- 'desc' => __( 'Enables spam detection for WPForms submissions.'. 'zero-spam' ),
722
- 'options' => [
723
- 'enabled' => __( 'Enabled', 'zero-spam' )
724
- ]
725
- ]);
726
- }
727
-
728
- // Fluent Form spam check
729
- if ( is_plugin_active( 'fluentform/fluentform.php' ) ) {
730
- add_settings_field( 'verify_fluentform', __( 'Verify Fluent Form Submissions', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
731
- 'label_for' => 'verify_fluentform',
732
- 'type' => 'checkbox',
733
- 'multi' => false,
734
- 'desc' => __( 'Enables spam detection for Fluent Form submissions.', 'zero-spam' ),
735
- 'options' => [
736
- 'enabled' => __( 'Enabled', 'zero-spam' )
737
- ]
738
- ]);
739
- }
740
-
741
- // Formidable forms spam check
742
- if ( is_plugin_active( 'formidable/formidable.php' ) ) {
743
- add_settings_field( 'verify_formidable', __( 'Verify Formidable Form Submissions', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
744
- 'label_for' => 'verify_formidable',
745
- 'type' => 'checkbox',
746
- 'multi' => false,
747
- 'desc' => __( 'Enables spam detection for Formidable form submissions.', 'zero-spam' ),
748
- 'options' => [
749
- 'enabled' => __( 'Enabled', 'zero-spam' )
750
- ]
751
- ]);
752
- }
753
-
754
- // IP whitelist
755
- add_settings_field( 'ip_whitelist', __( 'IP Whitelist', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_general_settings', [
756
- 'label_for' => 'ip_whitelist',
757
- 'type' => 'textarea',
758
- 'class' => 'large-text',
759
- 'desc' => __( 'Enter IPs that should be whitelisted (IPs that should never be blocked), one per line.', 'zero-spam' ),
760
- 'placeholder' => __( 'e.g. xxx.xxx.x.x', 'zero-spam' )
761
- ]);
762
- }
763
- add_action( 'admin_init', 'wpzerospam_admin_init' );
764
-
765
- function wpzerospam_general_settings_cb() {
766
- echo wp_kses(
767
- __( '<strong>WordPress Zero Spam just works &mdash; no need for additional configuration.</strong> However, for more control over its functionality, you can configure the general settings below.', 'wpzerospam' ),
768
- array(
769
- 'strong' => array(),
770
- )
771
- );
772
- }
773
-
774
- function wpzerospam_autoblock_settings_cb() {
775
- }
776
-
777
- function wpzerospam_spam_checks_cb() {
778
- }
779
-
780
- function wpzerospam_onsite_cb() {
781
- }
782
-
783
- function wpzerospam_whitelist_cb() {
784
- }
785
-
786
- function wpzerospam_field_cb( $args ) {
787
- $options = wpzerospam_options();
788
-
789
- switch( $args['type'] ) {
790
- case 'url':
791
- case 'text':
792
- case 'password':
793
- case 'number':
794
- case 'email':
795
- ?>
796
- <input class="<?php echo $args['class']; ?>" type="<?php echo $args['type']; ?>" value="<?php if ( ! empty( $options[ $args['label_for'] ] ) ): echo esc_attr( $options[ $args['label_for'] ] ); endif; ?>" placeholder="<?php if ( ! empty( $args['placeholder'] ) ): echo $args['placeholder']; endif; ?>" id="<?php echo esc_attr( $args['label_for'] ); ?>" name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]"><?php if ( ! empty( $args['suffix'] ) ): echo ' ' . $args['suffix']; endif; ?>
797
- <p class="description"><?php echo $args['desc'] ?></p>
798
- <?php
799
- break;
800
- case 'textarea':
801
- ?>
802
- <textarea rows="10" class="<?php echo $args['class']; ?>" id="<?php echo esc_attr( $args['label_for'] ); ?>" name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]"><?php if ( ! empty( $options[ $args['label_for'] ] ) ): echo esc_attr( $options[ $args['label_for'] ] ); endif; ?></textarea>
803
- <p class="description"><?php echo $args['desc'] ?></p>
804
- <?php
805
- break;
806
- case 'select':
807
- ?>
808
- <select name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]" id="<?php echo esc_attr( $args['label_for'] ); ?>">
809
- <?php foreach( $args['options'] as $key => $label ): ?>
810
- <option value="<?php echo $key; ?>"<?php if ( $key === $options[ $args['label_for'] ] ): ?> selected="selected"<?php endif; ?>><?php echo $label; ?></option>
811
- <?php endforeach; ?>
812
- </select>
813
- <p class="description"><?php echo $args['desc'] ?></p>
814
- <?php
815
- break;
816
- case 'checkbox':
817
- ?>
818
- <?php foreach( $args['options'] as $key => $label ): ?>
819
- <label for="<?php echo esc_attr( $args['label_for'] . $key ); ?>">
820
- <input
821
- type="checkbox"
822
- <?php if ( ! empty( $args['class'] ) ): ?>class="<?php echo $args['class']; ?>"<?php endif; ?>
823
- id="<?php echo esc_attr( $args['label_for'] . $key ); ?>"
824
- name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]<?php if( $args['multi'] ): ?>[<?php echo $key; ?>]<?php endif; ?>" value="<?php echo $key; ?>"
825
- <?php if( $args['multi'] && $key === $options[ $args['label_for'] ][ $key ] || ! $args['multi'] && $key === $options[ $args['label_for'] ] ): ?> checked="checked"<?php endif; ?> /> <?php echo $label; ?>
826
- </label>
827
- <?php endforeach; ?>
828
- <p class="description"><?php echo $args['desc'] ?></p>
829
- <?php
830
- break;
831
- case 'radio':
832
- ?>
833
- <?php foreach( $args['options'] as $key => $label ): ?>
834
- <label for="<?php echo esc_attr( $args['label_for'] . $key ); ?>">
835
- <input
836
- type="radio"
837
- <?php if ( ! empty( $args['class'] ) ): ?>class="<?php echo $args['class']; ?>"<?php endif; ?>
838
- id="<?php echo esc_attr( $args['label_for'] . $key ); ?>"
839
- name="wpzerospam[<?php echo esc_attr( $args['label_for'] ); ?>]" value="<?php echo $key; ?>"
840
- <?php if( $key == $options[ $args['label_for'] ] ): ?> checked="checked"<?php endif; ?> /> <?php echo $label; ?>
841
- </label><br />
842
- <?php endforeach; ?>
843
- <p class="description"><?php echo $args['desc'] ?></p>
844
- <?php
845
- break;
846
- }
847
- ?>
848
- <?php
849
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/helpers.php DELETED
@@ -1,600 +0,0 @@
1
- <?php
2
- /**
3
- * Plugin helpers
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- * @link https://benmarshall.me/wordpress-zero-spam/
8
- */
9
-
10
- /**
11
- * Logs a spam detection.
12
- *
13
- * This functions logs (if enabled) detections & handles sharing those
14
- * detections with Zero Spam (if enabled).
15
- *
16
- * @since 4.9.7
17
- *
18
- * @param string $type Machine-readable name of the detection type. Pass an 'ip'
19
- * key to define a specific IP address vs. inferring it
20
- * from the current users IP address.
21
- * @param array $data Optional. Array of additional information to log.
22
- */
23
- if ( ! function_exists( 'wpzerospam_log_detection' ) ) {
24
- function wpzerospam_log_detection( $type, $data = [] ) {
25
- global $wpdb;
26
- $options = wpzerospam_options();
27
-
28
- // Setup the detection record.
29
- $record = [
30
- 'user_ip' => wpzerospam_ip(),
31
- 'log_type' => $type,
32
- 'date_recorded' => current_time( 'mysql' )
33
- ];
34
-
35
- // Check if an IP address is present, if not, get it from the current user.
36
- if ( ! empty( $data['ip'] ) && rest_is_ip_address( $data['ip'] ) ) {
37
- $record['user_ip'] = $data['ip'];
38
- }
39
-
40
- // Make sure an IP address was found.
41
- if (
42
- empty( $record['user_ip'] ) ||
43
- ! rest_is_ip_address( $record['user_ip'] )
44
- ) {
45
- return false;
46
- }
47
-
48
- // If sharing detections is enabled, send the detection to Zero Spam.
49
- if ( 'enabled' == $options['share_detections'] ) {
50
- /*wpzerospam_share_detection([
51
- 'ip' => $record['user_ip'],
52
- 'type' => $record['log_type']
53
- ]);*/
54
- }
55
-
56
- // Check if logging detections & 'blocks' are enabled.
57
- if (
58
- 'enabled' != $options['log_spam'] ||
59
- ('blocked' == $record['log_type'] && 'enabled' != $options['log_blocked_ips'])
60
- ) {
61
- // Logging disabled.
62
- return false;
63
- }
64
-
65
- // Logging enabled, get the current URL & IP location information.
66
- $location = wpzerospam_get_ip_info( $record['user_ip'] );
67
- $current_url = wpzerospam_current_url();
68
-
69
- // Add additional information to the detection record.
70
- $record['page_url'] = ! empty( $current_url['full'] ) ? $current_url['full'] : false;
71
- $record['submission_data'] = json_encode( $data );
72
-
73
- if ( $location ) {
74
- $record['country'] = ! empty( $location['country_code'] ) ? $location['country_code'] : false;
75
- $record['region'] = ! empty( $location['region_code'] ) ? $location['region_code'] : false;
76
- $record['city'] = ! empty( $location['city'] ) ? $location['city'] : false;
77
- $record['latitude'] = ! empty( $location['latitude'] ) ? $location['latitude'] : false;
78
- $record['longitude'] = ! empty( $location['longitude'] ) ? $location['longitude'] : false;
79
- }
80
-
81
- return $wpdb->insert( wpzerospam_tables( 'log' ), $record );
82
- }
83
- }
84
-
85
- /**
86
- * Shares a detection with the Zero Spam database.
87
- */
88
- function wpzerospam_share_detection( $data ) {
89
- // The Zero Spam API endpoint for sharing detections.
90
- $api_url = 'https://zerospam.org/wp-json/wpzerospamapi/v1/detection/';
91
-
92
- // Make sure a type & valid IP address are provided.
93
- if (
94
- empty( $data['ip'] ) ||
95
- ! rest_is_ip_address( $data['ip'] ) ||
96
- empty( $data['type'] )
97
- ) {
98
- return false;
99
- }
100
-
101
- // Setup the request parameters.
102
- $request_args = [
103
- 'method' => 'POST',
104
- 'timeout' => 100,
105
- 'body' => [
106
- 'ip' => $data['ip'],
107
- 'type' => $data['type'],
108
- 'site' => site_url(),
109
- 'email' => get_bloginfo( 'admin_email' ),
110
- 'wpversion' => get_bloginfo( 'version' ),
111
- 'name' => get_bloginfo( 'name' ),
112
- 'desc' => get_bloginfo( 'description' ),
113
- 'language' => get_bloginfo( 'language' ),
114
- 'version' => WORDPRESS_ZERO_SPAM_VERSION
115
- ],
116
- 'sslverify' => true
117
- ];
118
-
119
- // For debugging purposes only.
120
- if ( WP_DEBUG ) {
121
- $request_args['sslverify'] = false;
122
- }
123
-
124
- // Send the request.
125
- $request = wp_remote_post( $api_url, $request_args );
126
- if ( is_wp_error( $request ) ) {
127
- // Request failed.
128
- return false;
129
- }
130
-
131
- // Request succeeded, return the result.
132
- return wp_remote_retrieve_body( $request );
133
- }
134
-
135
- /**
136
- * Returns the generated key for checking submissions.
137
- *
138
- * @since 4.0.0
139
- *
140
- * @return string A unique key used for detections.
141
- */
142
- if ( ! function_exists( 'wpzerospam_get_key' ) ) {
143
- function wpzerospam_get_key() {
144
- $key = get_option( 'wpzerospam_key' );
145
- if ( ! $key ) {
146
- $key = wp_generate_password( 64 );
147
- update_option( 'wpzerospam_key', $key );
148
- }
149
-
150
- return $key;
151
- }
152
- }
153
-
154
- /**
155
- * Returns the generated key for checking submissions.
156
- *
157
- * @since 4.9.9
158
- *
159
- * @return string A unique key used for the 'honeypot' field.
160
- */
161
- if ( ! function_exists( 'wpzerospam_get_honeypot' ) ) {
162
- function wpzerospam_get_honeypot() {
163
- $key = get_option( 'wpzerospam_honeypot' );
164
- if ( ! $key ) {
165
- $key = wp_generate_password( 5, false, false );
166
- update_option( 'wpzerospam_honeypot', $key );
167
- }
168
-
169
- return $key;
170
- }
171
- }
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
- /**
183
- * Handles what happens when spam is detected.
184
- *
185
- * @since 4.0.0
186
- *
187
- * @param string $type Machine-readable name for the type of spam.
188
- * @param array $data Additional information submitted when the spam was detected.
189
- * @param boolean $handle_detection Determines if this function should handle the function or is handled in the submission hook.
190
- * @return void
191
- */
192
- if ( ! function_exists( 'wpzerospam_spam_detected' ) ) {
193
- function wpzerospam_spam_detected( $type, $data = [], $handle_detection = true ) {
194
- $options = wpzerospam_options();
195
- $ip = wpzerospam_ip();
196
-
197
- // Log the spam sttempt
198
- wpzerospam_log_detection( $type, $data );
199
-
200
- // Check if number attempts should result in a permanent block
201
- $blocked_ip = wpzerospam_get_blocked_ips( $ip );
202
- if ( $blocked_ip && $blocked_ip->attempts >= $options['auto_block_permanently'] ) {
203
- // Permanently block the IP
204
- wpzerospam_update_blocked_ip( $ip , [
205
- 'blocked_type' => 'permanent',
206
- 'reason' => $type . ' (permanently auto-blocked)'
207
- ]);
208
-
209
- // Check if the IP should be temporarily auto-blocked
210
- } elseif ( 'enabled' == $options['auto_block_ips'] ) {
211
-
212
- $start_block = current_time( 'mysql' );
213
- $end_block = new DateTime( $start_block );
214
- $end_block->add( new DateInterval( 'PT' . $options['auto_block_period'] . 'M' ) );
215
-
216
- wpzerospam_update_blocked_ip( $ip , [
217
- 'blocked_type' => 'temporary',
218
- 'start_block' => $start_block,
219
- 'end_block' => $end_block->format('Y-m-d G:i:s'),
220
- 'reason' => $type . ' (auto-blocked)'
221
- ]);
222
- }
223
-
224
- // Check if WordPress Zero Spam should handle the error. False for forms
225
- // that process via AJAX & expect a json response.
226
- if ( $handle_detection ) {
227
- if ( 'redirect' == $options['spam_handler'] ) {
228
- wp_redirect( esc_url( $options['spam_redirect_url'] ) );
229
- exit();
230
- } else {
231
- status_header( 403 );
232
- die( $options['spam_message'] );
233
- }
234
- }
235
- }
236
- }
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
- /**
292
- * Checks if either the submission data or $_POST contain the wpzerospam_key and
293
- * if it matches whats in the database.
294
- *
295
- * @param array $submission_data An array of submission data that contains the
296
- * wpzerospam_key field
297
- * @return boolean true if the submission key matches the one in the database,
298
- * false if it doesn’t.
299
- */
300
- if ( ! function_exists( 'wpzerospam_key_check' ) ) {
301
- function wpzerospam_key_check( $submission_data = false ) {
302
- if (
303
- $submission_data &&
304
- ! empty( $submission_data['wpzerospam_key'] ) &&
305
- $submission_data['wpzerospam_key'] == wpzerospam_get_key()
306
- ) {
307
- return true;
308
- }
309
-
310
- if ( ! empty( $_POST['wpzerospam_key'] ) && $_POST['wpzerospam_key'] == wpzerospam_get_key() ) {
311
- return true;
312
- }
313
-
314
- return false;
315
- }
316
- }
317
-
318
- /**
319
- * Sets the $_SERVER['REQUEST_URI'] for pages that extend WP_List_Table
320
- *
321
- * Fix for passing filters to WP_List_Table paging. See @link below.
322
- *
323
- * @since 4.8.2
324
- * @link https://wordpress.stackexchange.com/questions/67669/how-to-stop-wpnonce-and-wp-http-referer-from-appearing-in-url/185006#185006
325
- * @param array $query_args Array of the current query arguments for a table
326
- * query.
327
- * @return void
328
- */
329
- if ( ! function_exists( 'wpzerospam_set_list_table_request_uri' ) ) {
330
- function wpzerospam_set_list_table_request_uri( $query_args ) {
331
- $paging_options = $query_args;
332
- unset( $paging_options['offset'] );
333
- unset( $paging_options['where'] );
334
-
335
- if ( ! empty( $query_args['where'] ) ) {
336
- foreach( $query_args['where'] as $key => $value ) {
337
- switch( $key ) {
338
- case 'blacklist_service':
339
- $paging_options['service'] = $value;
340
- break;
341
- case 'user_ip':
342
- $paging_options['s'] = $value;
343
- break;
344
- case 'blocked_type':
345
- case 'log_type':
346
- $paging_options['type'] = $value;
347
- break;
348
- }
349
- }
350
- }
351
-
352
- $_SERVER['REQUEST_URI'] = add_query_arg( $paging_options, $_SERVER['REQUEST_URI'] );
353
- }
354
- }
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
- /**
364
- * Query the database
365
- */
366
- if ( ! function_exists( 'wpzerospam_query' ) ) {
367
- function wpzerospam_query( $table, $args = [], $return_total = false ) {
368
- global $wpdb;
369
-
370
- $sql = 'SELECT';
371
-
372
- if ( ! $return_total ) {
373
- if ( ! empty( $args['select'] ) ) {
374
- $sql .= ' ' . implode( ',', $args['select'] );
375
- } else {
376
- $sql .= ' *';
377
- }
378
- } else {
379
- $sql .= ' COUNT(*)';
380
- }
381
-
382
- $sql .= ' FROM ' . wpzerospam_tables( $table );
383
-
384
- if ( ! empty( $args['where'] ) ) {
385
- $sql .= ' WHERE';
386
- $cnt = 0;
387
- foreach( $args['where'] as $k => $v ) {
388
- if ( $cnt ) {
389
- $sql .= ' AND ';
390
- } else {
391
- $sql .= ' ';
392
- }
393
-
394
- if ( is_int( $v ) ) {
395
- $sql .= $k . ' = ' . $v;
396
- } else {
397
- $sql .= $k . ' = "' . $v . '"';
398
- }
399
-
400
- $cnt++;
401
- }
402
- }
403
-
404
- if ( ! empty( $args['orderby'] ) ) {
405
- $sql .= ' ORDER BY ' . $args['orderby'];
406
- }
407
-
408
- if ( ! empty( $args['order'] ) ) {
409
- $sql .= ' ' . $args['order'];
410
- }
411
-
412
- if ( ! $return_total ) {
413
- if ( ! empty( $args['limit'] ) ) {
414
- $sql .= ' LIMIT ' . $args['limit'];
415
- }
416
-
417
- if ( ! empty( $args['offset'] ) ) {
418
- $sql .= ', ' . $args['offset'];
419
- }
420
- }
421
-
422
- if ( ! $return_total ) {
423
- return $wpdb->get_results( $sql );
424
- } else {
425
- return $wpdb->get_var( $sql );
426
- }
427
- }
428
- }
429
-
430
-
431
-
432
-
433
-
434
-
435
-
436
-
437
-
438
-
439
-
440
-
441
-
442
- /**
443
- * Add a IP address to the blocked table
444
- */
445
- if ( ! function_exists( 'wpzerospam_update_blocked_ip' ) ) {
446
- function wpzerospam_update_blocked_ip( $ip, $args = [] ) {
447
- global $wpdb;
448
-
449
- $options = wpzerospam_options();
450
-
451
- $record = wp_parse_args( $args, [
452
- 'blocked_type' => 'permanent',
453
- 'date_added' => current_time( 'mysql' ),
454
- 'start_block' => false,
455
- 'end_block' => false,
456
- 'reason' => false,
457
- 'attempts' => 1
458
- ]);
459
-
460
- $record['user_ip'] = $ip;
461
-
462
- // First, check if the IP is already in the DB
463
- $check = wpzerospam_get_blocked_ips( $record['user_ip'] );
464
- if ( $check ) {
465
- $attempts = $check->attempts;
466
- $attempts++;
467
-
468
- // IP exists, update accordingly
469
- $update = [ 'attempts' => $attempts ];
470
-
471
- if ( $record['blocked_type'] && $record['blocked_type'] != $check->blocked_type ) {
472
- $update['blocked_type'] = $record['blocked_type'];
473
- }
474
-
475
- if ( $record['start_block'] && $record['start_block'] != $check->start_block ) {
476
- $update['start_block'] = $record['start_block'];
477
- }
478
-
479
- if ( $record['end_block'] && $record['end_block'] != $check->end_block ) {
480
- $update['end_block'] = $record['end_block'];
481
- }
482
-
483
- if ( $record['reason'] && $record['reason'] != $check->reason ) {
484
- $update['reason'] = $record['reason'];
485
- }
486
-
487
- if ( $update ) {
488
- $wpdb->update( wpzerospam_tables( 'blocked' ), $update, [
489
- 'blocked_id' => $check->blocked_id
490
- ]);
491
- }
492
- } else {
493
- // IP doesn't exist, add it
494
- $wpdb->insert( wpzerospam_tables( 'blocked' ), $record );
495
- }
496
- }
497
- }
498
-
499
-
500
-
501
- /**
502
- * Validates a post submission
503
- */
504
- if ( ! function_exists( 'wpzerospam_validate_submission' ) ) {
505
- function wpzerospam_validate_submission() {
506
- if ( ! empty( $_POST['wpzerospam'] ) && wpzerospam_get_key() == $_POST['wpzerospam'] ) {
507
- return true;
508
- }
509
-
510
- return false;
511
- }
512
- }
513
-
514
- /**
515
- * Returns an array of blocked IPs or an individual IP's details
516
- */
517
- if ( ! function_exists( 'wpzerospam_get_blocked_ips' ) ) {
518
- function wpzerospam_get_blocked_ips( $ip = false ) {
519
- global $wpdb;
520
-
521
- if ( ! $ip ) {
522
- return $wpdb->get_results( 'SELECT * FROM ' . wpzerospam_tables( 'blocked' ) );
523
- }
524
-
525
- return $wpdb->get_row($wpdb->prepare(
526
- 'SELECT * FROM ' . wpzerospam_tables( 'blocked' ) . ' WHERE user_ip = %s',
527
- $ip
528
- ));
529
- }
530
- }
531
-
532
- /**
533
- * Checks if a specific plugin integration is turned on & plugin active.
534
- */
535
- if ( ! function_exists( 'wpzerospam_plugin_integration_enabled' ) ) {
536
- function wpzerospam_plugin_integration_enabled( $plugin ) {
537
- if( ! function_exists( 'is_plugin_active' ) ) {
538
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
539
- }
540
-
541
- $options = wpzerospam_options();
542
-
543
- $integrations = [
544
- 'fluentform' => 'fluentform/fluentform.php',
545
- 'wpforms' => [ 'wpforms/wpforms.php', 'wpforms-lite/wpforms.php' ],
546
- 'formidable' => 'formidable/formidable.php',
547
- ];
548
-
549
- // Handle BuddyPress check a little differently for presence of a function
550
- if ( 'bp_registrations' == $plugin ) {
551
- if (
552
- ! empty( $options['verify_bp_registrations'] ) &&
553
- 'enabled' == $options['verify_bp_registrations']
554
- ) {
555
- return true;
556
- } else {
557
- return false;
558
- }
559
- }
560
-
561
- // Handling all other plugin checks
562
- if (
563
- ! empty( $options['verify_' . $plugin] ) &&
564
- 'enabled' == $options['verify_' . $plugin ] &&
565
- ! empty( $integrations[ $plugin ] )
566
- ) {
567
- if ( is_array( $integrations[ $plugin ] ) ) {
568
- // Check at least one of the defined plugins are active
569
- foreach( $integrations[ $plugin ] as $key => $value ) {
570
- if ( is_plugin_active( $value ) ) {
571
- return true;
572
- }
573
- }
574
- } else {
575
- // Check if one plugin is active
576
- if ( is_plugin_active( $integrations[ $plugin ] ) ) {
577
- return true;
578
- }
579
- }
580
- }
581
-
582
- return false;
583
- }
584
- }
585
-
586
- /**
587
- * Determines if the current page is the login page
588
- */
589
- if ( ! function_exists( 'wpzerospam_is_login' ) ) {
590
- function wpzerospam_is_login() {
591
- $login_url = wp_login_url();
592
- $current_url = wpzerospam_current_url();
593
-
594
- if ( $login_url == $current_url['full'] ) {
595
- return true;
596
- }
597
-
598
- return false;
599
- }
600
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/install.php DELETED
@@ -1,86 +0,0 @@
1
- <?php
2
- /**
3
- * Install plugin tables.
4
- *
5
- * @package WordPressZeroSpam
6
- */
7
-
8
- // Security Note: Blocks direct access to the plugin PHP files.
9
- defined( 'ABSPATH' ) || die();
10
-
11
- /**
12
- * Installs the plugin tables.
13
- */
14
- function wpzerospam_install() {
15
- global $wpdb;
16
-
17
- $wordpress_zero_spam = new WPZeroSpam();
18
- $charset_collate = $wpdb->get_charset_collate();
19
- $installed_db_version = get_option( 'wpzerospam_db_version' );
20
-
21
- if ( WORDPRESS_ZERO_SPAM_DB_VERSION !== $installed_db_version ) {
22
- $log_table = wpzerospam_tables( 'log' );
23
- $blocked_table = wpzerospam_tables( 'blocked' );
24
- $blacklist_table = wpzerospam_tables( 'blacklist' );
25
-
26
- $sql = 'CREATE TABLE ' . $wordpress_zero_spam->tables['log'] . " (
27
- log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
28
- log_type VARCHAR(255) NOT NULL,
29
- user_ip VARCHAR(39) NOT NULL,
30
- date_recorded DATETIME NOT NULL,
31
- page_url VARCHAR(255) NULL DEFAULT NULL,
32
- submission_data LONGTEXT NULL DEFAULT NULL,
33
- country VARCHAR(2) NULL DEFAULT NULL,
34
- country_name VARCHAR(255) NULL DEFAULT NULL,
35
- region VARCHAR(255) NULL DEFAULT NULL,
36
- region_name VARCHAR(255) NULL DEFAULT NULL,
37
- city VARCHAR(255) NULL DEFAULT NULL,
38
- latitude VARCHAR(255) NULL DEFAULT NULL,
39
- longitude VARCHAR(255) NULL DEFAULT NULL,
40
- PRIMARY KEY (`log_id`)) $charset_collate;";
41
-
42
- $sql .= 'CREATE TABLE ' . $wordpress_zero_spam->tables['blocked'] . " (
43
- blocked_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
44
- blocked_type ENUM('permanent','temporary') NOT NULL DEFAULT 'temporary',
45
- user_ip VARCHAR(39) NOT NULL,
46
- date_added DATETIME NOT NULL,
47
- start_block DATETIME NULL DEFAULT NULL,
48
- end_block DATETIME NULL DEFAULT NULL,
49
- reason VARCHAR(255) NULL DEFAULT NULL,
50
- attempts BIGINT UNSIGNED NOT NULL,
51
- PRIMARY KEY (`blocked_id`)) $charset_collate;";
52
-
53
- $sql .= 'CREATE TABLE ' . $wordpress_zero_spam->tables['blacklist'] . " (
54
- blacklist_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
55
- user_ip VARCHAR(39) NOT NULL,
56
- last_updated DATETIME NOT NULL,
57
- blacklist_service VARCHAR(255) NULL DEFAULT NULL,
58
- attempts BIGINT UNSIGNED NOT NULL,
59
- blacklist_data LONGTEXT NULL DEFAULT NULL,
60
- PRIMARY KEY (`blacklist_id`)) $charset_collate;";
61
-
62
- require_once ABSPATH . 'wp-admin/includes/upgrade.php';
63
- dbDelta( $sql );
64
-
65
- if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $blocked_table ) ) === $blocked_table ) {
66
- $wpdb->query( "DELETE t1 FROM $blocked_table AS t1 JOIN $blocked_table AS t2 ON t2.blocked_id = t1.blocked_id WHERE t1.blocked_id < t2.blocked_id AND t1.user_ip = t2.user_ip" );
67
- }
68
-
69
- if ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $blacklist_table ) ) === $blacklist_table ) {
70
- $wpdb->query( "DELETE t1 FROM $blacklist_table AS t1 JOIN $blacklist_table AS t2 ON t2.blacklist_id = t1.blacklist_id WHERE t1.blacklist_id < t2.blacklist_id AND t1.user_ip = t2.user_ip" );
71
- }
72
-
73
- update_option( 'wpzerospam_db_version', WORDPRESS_ZERO_SPAM_DB_VERSION );
74
- }
75
- }
76
- register_activation_hook( WORDPRESS_ZERO_SPAM, 'wpzerospam_install' );
77
-
78
- /**
79
- * Check to ensure the database tables have been installed
80
- */
81
- function wpzerospam_db_check() {
82
- if ( WORDPRESS_ZERO_SPAM_DB_VERSION !== get_site_option( 'wpzerospam_db_version' ) ) {
83
- wpzerospam_install();
84
- }
85
- }
86
- add_action( 'plugins_loaded', 'wpzerospam_db_check' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/locations.php DELETED
@@ -1,2152 +0,0 @@
1
- <?php
2
- /**
3
- * Location helper
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- /**
10
- * Returns the human-readable country or region name.
11
- *
12
- * * If no region is provided, it returns the country name.
13
- * * If the country’s name isn’t available, it returns the country abbreviation.
14
- * * If a region is provided, it returns the region name.
15
- * * If the region name isn’t available, it returns the region abbreviation.
16
- *
17
- * @link https://en.wikipedia.org/wiki/ISO_3166-2:COUNTRY_CODE
18
- * @param string $country_code The two letter country code.
19
- * @param string $region_code The two letter region code.
20
- */
21
- if ( ! function_exists( 'wpzerospam_get_location' ) ) {
22
- function wpzerospam_get_location( $country_code, $region_code = false ) {
23
- $locations = [
24
- 'US' => [
25
- 'name' => 'United States',
26
- 'regions' => [
27
- 'AK' => [
28
- 'name' => 'Alaska',
29
- ],
30
- 'AZ' => [
31
- 'name' => 'Arizona',
32
- ],
33
- 'AR' => [
34
- 'name' => 'Arkansas',
35
- ],
36
- 'CA' => [
37
- 'name' => 'California',
38
- ],
39
- 'CO' => [
40
- 'name' => 'Colorado',
41
- ],
42
- 'CT' => [
43
- 'name' => 'Connecticut',
44
- ],
45
- 'DE' => [
46
- 'name' => 'Delaware',
47
- ],
48
- 'DC' => [
49
- 'name' => 'District Of Columbia',
50
- ],
51
- 'FL' => [
52
- 'name' => 'Florida',
53
- ],
54
- 'GA' => [
55
- 'name' => 'Georgia',
56
- ],
57
- 'HI' => [
58
- 'name' => 'Hawaii',
59
- ],
60
- 'ID' => [
61
- 'name' => 'Idaho',
62
- ],
63
- 'IL' => [
64
- 'name' => 'Illinois',
65
- ],
66
- 'IN' => [
67
- 'name' => 'Indiana',
68
- ],
69
- 'IA' => [
70
- 'name' => 'Iowa',
71
- ],
72
- 'KS' => [
73
- 'name' => 'Kansas',
74
- ],
75
- 'KY' => [
76
- 'name' => 'Kentucky',
77
- ],
78
- 'LA' => [
79
- 'name' => 'Louisiana',
80
- ],
81
- 'ME' => [
82
- 'name' => 'Maine',
83
- ],
84
- 'MD' => [
85
- 'name' => 'Maryland',
86
- ],
87
- 'MA' => [
88
- 'name' => 'Massachusetts',
89
- ],
90
- 'MI' => [
91
- 'name' => 'Michigan',
92
- ],
93
- 'MN' => [
94
- 'name' => 'Minnesota',
95
- ],
96
- 'MS' => [
97
- 'name' => 'Mississippi',
98
- ],
99
- 'MO' => [
100
- 'name' => 'Missouri',
101
- ],
102
- 'MT' => [
103
- 'name' => 'Montana',
104
- ],
105
- 'NE' => [
106
- 'name' => 'Nebraska',
107
- ],
108
- 'NV' => [
109
- 'name' => 'Nevada',
110
- ],
111
- 'NH' => [
112
- 'name' => 'New Hampshire',
113
- ],
114
- 'NJ' => [
115
- 'name' => 'New Jersey',
116
- ],
117
- 'NM' => [
118
- 'name' => 'New Mexico',
119
- ],
120
- 'NY' => [
121
- 'name' => 'New York',
122
- ],
123
- 'NC' => [
124
- 'name' => 'North Carolina',
125
- ],
126
- 'ND' => [
127
- 'name' => 'North Dakota',
128
- ],
129
- 'OH' => [
130
- 'name' => 'Ohio',
131
- ],
132
- 'OK' => [
133
- 'name' => 'Oklahoma',
134
- ],
135
- 'OR' => [
136
- 'name' => 'Oregon',
137
- ],
138
- 'PA' => [
139
- 'name' => 'Pennsylvania',
140
- ],
141
- 'RI' => [
142
- 'name' => 'Rhode Island',
143
- ],
144
- 'SC' => [
145
- 'name' => 'South Carolina',
146
- ],
147
- 'SD' => [
148
- 'name' => 'South Dakota',
149
- ],
150
- 'TN' => [
151
- 'name' => 'Tennessee',
152
- ],
153
- 'TX' => [
154
- 'name' => 'Texas',
155
- ],
156
- 'UT' => [
157
- 'name' => 'Utah',
158
- ],
159
- 'VT' => [
160
- 'name' => 'Vermont',
161
- ],
162
- 'VA' => [
163
- 'name' => 'Virginia',
164
- ],
165
- 'WA' => [
166
- 'name' => 'Washington',
167
- ],
168
- 'WV' => [
169
- 'name' => 'West Virginia',
170
- ],
171
- 'WI' => [
172
- 'name' => 'Wisconsin',
173
- ],
174
- 'WY' => [
175
- 'name' => 'Wyoming'
176
- ]
177
- ]
178
- ],
179
- 'AF' => [
180
- 'name' => 'Afghanistan',
181
- 'regions' => []
182
- ],
183
- 'AX' => [
184
- 'name' => 'Aland Islands',
185
- 'regions' => []
186
- ],
187
- 'AL' => [
188
- 'name' => 'Albania',
189
- 'regions' => []
190
- ],
191
- 'DZ' => [
192
- 'name' => 'Algeria',
193
- 'regions' => []
194
- ],
195
- 'AS' => [
196
- 'name' => 'American Samoa',
197
- 'regions' => []
198
- ],
199
- 'AD' => [
200
- 'name' => 'Andorra',
201
- 'regions' => []
202
- ],
203
- 'AO' => [
204
- 'name' => 'Angola',
205
- 'regions' => []
206
- ],
207
- 'AI' => [
208
- 'name' => 'Anguilla',
209
- 'regions' => []
210
- ],
211
- 'AQ' => [
212
- 'name' => 'Antarctica',
213
- 'regions' => []
214
- ],
215
- 'AG' => [
216
- 'name' => 'Antigua And Barbuda',
217
- 'regions' => []
218
- ],
219
- 'AR' => [
220
- 'name' => 'Argentina',
221
- 'regions' => []
222
- ],
223
- 'AM' => [
224
- 'name' => 'Armenia',
225
- 'regions' => []
226
- ],
227
- 'AW' => [
228
- 'name' => 'Aruba',
229
- 'regions' => []
230
- ],
231
- 'AU' => [
232
- 'name' => 'Australia',
233
- 'regions' => []
234
- ],
235
- 'AT' => [
236
- 'name' => 'Austria',
237
- 'regions' => []
238
- ],
239
- 'AZ' => [
240
- 'name' => 'Azerbaijan',
241
- 'regions' => []
242
- ],
243
- 'BS' => [
244
- 'name' => 'Bahamas',
245
- 'regions' => []
246
- ],
247
- 'BH' => [
248
- 'name' => 'Bahrain',
249
- 'regions' => []
250
- ],
251
- 'BD' => [
252
- 'name' => 'Bangladesh',
253
- 'regions' => []
254
- ],
255
- 'BB' => [
256
- 'name' => 'Barbados',
257
- 'regions' => []
258
- ],
259
- 'BY' => [
260
- 'name' => 'Belarus',
261
- 'regions' => []
262
- ],
263
- 'BE' => [
264
- 'name' => 'Belgium',
265
- 'regions' => []
266
- ],
267
- 'BZ' => [
268
- 'name' => 'Belize',
269
- 'regions' => []
270
- ],
271
- 'BJ' => [
272
- 'name' => 'Benin',
273
- 'regions' => []
274
- ],
275
- 'BM' => [
276
- 'name' => 'Bermuda',
277
- 'regions' => []
278
- ],
279
- 'BT' => [
280
- 'name' => 'Bhutan',
281
- 'regions' => []
282
- ],
283
- 'BO' => [
284
- 'name' => 'Bolivia',
285
- 'regions' => []
286
- ],
287
- 'BA' => [
288
- 'name' => 'Bosnia And Herzegovina',
289
- 'regions' => []
290
- ],
291
- 'BW' => [
292
- 'name' => 'Botswana',
293
- 'regions' => []
294
- ],
295
- 'BV' => [
296
- 'name' => 'Bouvet Island',
297
- 'regions' => []
298
- ],
299
- 'BR' => [
300
- 'name' => 'Brazil',
301
- 'regions' => []
302
- ],
303
- 'IO' => [
304
- 'name' => 'British Indian Ocean Territory',
305
- 'regions' => []
306
- ],
307
- 'BN' => [
308
- 'name' => 'Brunei Darussalam',
309
- 'regions' => []
310
- ],
311
- 'BG' => [
312
- 'name' => 'Bulgaria',
313
- 'regions' => []
314
- ],
315
- 'BF' => [
316
- 'name' => 'Burkina Faso',
317
- 'regions' => []
318
- ],
319
- 'BI' => [
320
- 'name' => 'Burundi',
321
- 'regions' => []
322
- ],
323
- 'KH' => [
324
- 'name' => 'Cambodia',
325
- 'regions' => []
326
- ],
327
- 'CM' => [
328
- 'name' => 'Cameroon',
329
- 'regions' => []
330
- ],
331
- 'CA' => [
332
- 'name' => 'Canada',
333
- 'regions' => [
334
- 'AB' => [
335
- 'name' => 'Alberta'
336
- ],
337
- 'BC' => [
338
- 'name' => 'British Columbia'
339
- ],
340
- 'MB' => [
341
- 'name' => 'Manitoba'
342
- ],
343
- 'NB' => [
344
- 'name' => 'New Brunswick'
345
- ],
346
- 'NL' => [
347
- 'name' => 'Newfoundland and Labrador'
348
- ],
349
- 'NS' => [
350
- 'name' => 'Nova Scotia'
351
- ],
352
- 'ON' => [
353
- 'name' => 'Ontario'
354
- ],
355
- 'PE' => [
356
- 'name' => 'Prince Edward Island'
357
- ],
358
- 'QC' => [
359
- 'name' => 'Quebec'
360
- ],
361
- 'SK' => [
362
- 'name' => 'Saskatchewan'
363
- ],
364
- 'NT' => [
365
- 'name' => 'Northwest Territories'
366
- ],
367
- 'NU' => [
368
- 'name' => 'Nunavut'
369
- ],
370
- 'YT' => [
371
- 'name' => 'Yukon'
372
- ]
373
- ]
374
- ],
375
- 'CV' => [
376
- 'name' => 'Cape Verde',
377
- 'regions' => []
378
- ],
379
- 'KY' => [
380
- 'name' => 'Cayman Islands',
381
- 'regions' => []
382
- ],
383
- 'CF' => [
384
- 'name' => 'Central African Republic',
385
- 'regions' => []
386
- ],
387
- 'TD' => [
388
- 'name' => 'Chad',
389
- 'regions' => []
390
- ],
391
- 'CL' => [
392
- 'name' => 'Chile',
393
- 'regions' => []
394
- ],
395
- 'CN' => [
396
- 'name' => 'China',
397
- 'regions' => []
398
- ],
399
- 'CX' => [
400
- 'name' => 'Christmas Island',
401
- 'regions' => []
402
- ],
403
- 'CC' => [
404
- 'name' => 'Cocos (Keeling) Islands',
405
- 'regions' => []
406
- ],
407
- 'CO' => [
408
- 'name' => 'Colombia',
409
- 'regions' => []
410
- ],
411
- 'KM' => [
412
- 'name' => 'Comoros',
413
- 'regions' => []
414
- ],
415
- 'CG' => [
416
- 'name' => 'Congo',
417
- 'regions' => []
418
- ],
419
- 'CD' => [
420
- 'name' => 'Congo, Democratic Republic',
421
- 'regions' => []
422
- ],
423
- 'CK' => [
424
- 'name' => 'Cook Islands',
425
- 'regions' => []
426
- ],
427
- 'CR' => [
428
- 'name' => 'Costa Rica',
429
- 'regions' => []
430
- ],
431
- 'CI' => [
432
- 'name' => 'Cote D\'Ivoire',
433
- 'regions' => []
434
- ],
435
- 'HR' => [
436
- 'name' => 'Croatia',
437
- 'regions' => []
438
- ],
439
- 'CU' => [
440
- 'name' => 'Cuba',
441
- 'regions' => []
442
- ],
443
- 'CY' => [
444
- 'name' => 'Cyprus',
445
- 'regions' => []
446
- ],
447
- 'CZ' => [
448
- 'name' => 'Czech Republic',
449
- 'regions' => []
450
- ],
451
- 'DK' => [
452
- 'name' => 'Denmark',
453
- 'regions' => []
454
- ],
455
- 'DJ' => [
456
- 'name' => 'Djibouti',
457
- 'regions' => []
458
- ],
459
- 'DM' => [
460
- 'name' => 'Dominica',
461
- 'regions' => []
462
- ],
463
- 'DO' => [
464
- 'name' => 'Dominican Republic',
465
- 'regions' => []
466
- ],
467
- 'EC' => [
468
- 'name' => 'Ecuador',
469
- 'regions' => []
470
- ],
471
- 'EG' => [
472
- 'name' => 'Egypt',
473
- 'regions' => []
474
- ],
475
- 'SV' => [
476
- 'name' => 'El Salvador',
477
- 'regions' => []
478
- ],
479
- 'GQ' => [
480
- 'name' => 'Equatorial Guinea',
481
- 'regions' => []
482
- ],
483
- 'ER' => [
484
- 'name' => 'Eritrea',
485
- 'regions' => []
486
- ],
487
- 'EE' => [
488
- 'name' => 'Estonia',
489
- 'regions' => []
490
- ],
491
- 'ET' => [
492
- 'name' => 'Ethiopia',
493
- 'regions' => []
494
- ],
495
- 'FK' => [
496
- 'name' => 'Falkland Islands (Malvinas)',
497
- 'regions' => []
498
- ],
499
- 'FO' => [
500
- 'name' => 'Faroe Islands',
501
- 'regions' => []
502
- ],
503
- 'FJ' => [
504
- 'name' => 'Fiji',
505
- 'regions' => []
506
- ],
507
- 'FI' => [
508
- 'name' => 'Finland',
509
- 'regions' => []
510
- ],
511
- 'FR' => [
512
- 'name' => 'France',
513
- 'regions' => [
514
- 'ARA' => [
515
- 'name' => 'Auvergne-Rhône-Alpes'
516
- ],
517
- 'BFC' => [
518
- 'name' => 'Bourgogne-Franche-Comté'
519
- ],
520
- 'BRE' => [
521
- 'name' => 'Bretagne'
522
- ],
523
- 'CVL' => [
524
- 'name' => 'Centre-Val de Loire'
525
- ],
526
- 'COR' => [
527
- 'name' => 'Corse'
528
- ],
529
- 'GES' => [
530
- 'name' => 'Grand-Est'
531
- ],
532
- 'GUA' => [
533
- 'name' => 'Guadeloupe'
534
- ],
535
- 'HDF' => [
536
- 'name' => 'Hauts-de-France'
537
- ],
538
- 'IDF' => [
539
- 'name' => 'Île-de-France'
540
- ],
541
- 'MAY' => [
542
- 'name' => 'Mayotte'
543
- ],
544
- 'NOR' => [
545
- 'name' => 'Normandie'
546
- ],
547
- 'NAQ' => [
548
- 'name' => 'Nouvelle-Aquitaine'
549
- ],
550
- 'OCC' => [
551
- 'name' => 'Occitanie'
552
- ],
553
- 'PDL' => [
554
- 'name' => 'Pays-de-la-Loire'
555
- ],
556
- 'PDL' => [
557
- 'name' => 'Provence-Alpes-Côte-d’Azur'
558
- ],
559
- 'LRE' => [
560
- 'name' => 'La Réunion'
561
- ],
562
- ]
563
- ],
564
- 'GF' => [
565
- 'name' => 'French Guiana',
566
- 'regions' => []
567
- ],
568
- 'PF' => [
569
- 'name' => 'French Polynesia',
570
- 'regions' => []
571
- ],
572
- 'TF' => [
573
- 'name' => 'French Southern Territories',
574
- 'regions' => []
575
- ],
576
- 'GA' => [
577
- 'name' => 'Gabon',
578
- 'regions' => []
579
- ],
580
- 'GM' => [
581
- 'name' => 'Gambia',
582
- 'regions' => []
583
- ],
584
- 'GE' => [
585
- 'name' => 'Georgia',
586
- 'regions' => []
587
- ],
588
- 'DE' => [
589
- 'name' => 'Germany',
590
- 'regions' => []
591
- ],
592
- 'GH' => [
593
- 'name' => 'Ghana',
594
- 'regions' => []
595
- ],
596
- 'GI' => [
597
- 'name' => 'Gibraltar',
598
- 'regions' => []
599
- ],
600
- 'GR' => [
601
- 'name' => 'Greece',
602
- 'regions' => []
603
- ],
604
- 'GL' => [
605
- 'name' => 'Greenland',
606
- 'regions' => []
607
- ],
608
- 'GD' => [
609
- 'name' => 'Grenada',
610
- 'regions' => []
611
- ],
612
- 'GP' => [
613
- 'name' => 'Guadeloupe',
614
- 'regions' => []
615
- ],
616
- 'GU' => [
617
- 'name' => 'Guam',
618
- 'regions' => []
619
- ],
620
- 'GT' => [
621
- 'name' => 'Guatemala',
622
- 'regions' => []
623
- ],
624
- 'GG' => [
625
- 'name' => 'Guernsey',
626
- 'regions' => []
627
- ],
628
- 'GN' => [
629
- 'name' => 'Guinea',
630
- 'regions' => []
631
- ],
632
- 'GW' => [
633
- 'name' => 'Guinea-Bissau',
634
- 'regions' => []
635
- ],
636
- 'GY' => [
637
- 'name' => 'Guyana',
638
- 'regions' => []
639
- ],
640
- 'HT' => [
641
- 'name' => 'Haiti',
642
- 'regions' => []
643
- ],
644
- 'HM' => [
645
- 'name' => 'Heard Island & Mcdonald Islands',
646
- 'regions' => []
647
- ],
648
- 'VA' => [
649
- 'name' => 'Holy See (Vatican City State)',
650
- 'regions' => []
651
- ],
652
- 'HN' => [
653
- 'name' => 'Honduras',
654
- 'regions' => []
655
- ],
656
- 'HK' => [
657
- 'name' => 'Hong Kong',
658
- 'regions' => []
659
- ],
660
- 'HU' => [
661
- 'name' => 'Hungary',
662
- 'regions' => []
663
- ],
664
- 'IS' => [
665
- 'name' => 'Iceland',
666
- 'regions' => []
667
- ],
668
- 'IN' => [
669
- 'name' => 'India',
670
- 'regions' => [
671
- 'AP' => [
672
- 'name' => 'Andhra Pradesh'
673
- ],
674
- 'AR' => [
675
- 'name' => 'Arunachal Pradesh'
676
- ],
677
- 'AS' => [
678
- 'name' => 'Assam'
679
- ],
680
- 'BR' => [
681
- 'name' => 'Bihar'
682
- ],
683
- 'CT' => [
684
- 'name' => 'Chhattisgarh'
685
- ],
686
- 'GA' => [
687
- 'name' => 'Goa'
688
- ],
689
- 'GJ' => [
690
- 'name' => 'Gujarat'
691
- ],
692
- 'HR' => [
693
- 'name' => 'Haryana'
694
- ],
695
- 'HP' => [
696
- 'name' => 'Himachal Pradesh'
697
- ],
698
- 'JH' => [
699
- 'name' => 'Jharkhand'
700
- ],
701
- 'KA' => [
702
- 'name' => 'Karnataka'
703
- ],
704
- 'KL' => [
705
- 'name' => 'Kerala'
706
- ],
707
- 'MP' => [
708
- 'name' => 'Madhya Pradesh'
709
- ],
710
- 'MH' => [
711
- 'name' => 'Maharashtra'
712
- ],
713
- 'MN' => [
714
- 'name' => 'Manipur'
715
- ],
716
- 'ML' => [
717
- 'name' => 'Meghalaya'
718
- ],
719
- 'MZ' => [
720
- 'name' => 'Mizoram'
721
- ],
722
- 'NL' => [
723
- 'name' => 'Nagaland'
724
- ],
725
- 'OR' => [
726
- 'name' => 'Odisha'
727
- ],
728
- 'PB' => [
729
- 'name' => 'Punjab'
730
- ],
731
- 'RJ' => [
732
- 'name' => 'Rajasthan'
733
- ],
734
- 'SK' => [
735
- 'name' => 'Sikkim'
736
- ],
737
- 'TN' => [
738
- 'name' => 'Tamil Nadu'
739
- ],
740
- 'TG' => [
741
- 'name' => 'Telangana'
742
- ],
743
- 'TR' => [
744
- 'name' => 'Tripura'
745
- ],
746
- 'UT' => [
747
- 'name' => 'Uttarakhand'
748
- ],
749
- 'UP' => [
750
- 'name' => 'Uttar Pradesh'
751
- ],
752
- 'WP' => [
753
- 'name' => 'West Bengal'
754
- ],
755
- 'AN' => [
756
- 'name' => 'Andaman and Nicobar Islands'
757
- ],
758
- 'CH' => [
759
- 'name' => 'Chandigarh'
760
- ],
761
- 'DN' => [
762
- 'name' => 'Dadra and Nagar Haveli'
763
- ],
764
- 'DD' => [
765
- 'name' => 'Daman and Diu'
766
- ],
767
- 'DL' => [
768
- 'name' => 'Delhi'
769
- ],
770
- 'JK' => [
771
- 'name' => 'Jammu and Kashmir'
772
- ],
773
- 'LA' => [
774
- 'name' => 'Ladakh'
775
- ],
776
- 'LD' => [
777
- 'name' => 'Lakshadweep'
778
- ],
779
- 'PY' => [
780
- 'name' => 'Puducherry'
781
- ]
782
- ]
783
- ],
784
- 'ID' => [
785
- 'name' => 'Indonesia',
786
- 'regions' => []
787
- ],
788
- 'IR' => [
789
- 'name' => 'Iran, Islamic Republic Of',
790
- 'regions' => []
791
- ],
792
- 'IQ' => [
793
- 'name' => 'Iraq',
794
- 'regions' => []
795
- ],
796
- 'IE' => [
797
- 'name' => 'Ireland',
798
- 'regions' => []
799
- ],
800
- 'IM' => [
801
- 'name' => 'Isle Of Man',
802
- 'regions' => []
803
- ],
804
- 'IL' => [
805
- 'name' => 'Israel',
806
- 'regions' => []
807
- ],
808
- 'IT' => [
809
- 'name' => 'Italy',
810
- 'regions' => [
811
- '65' => [
812
- 'name' => 'Abruzzo'
813
- ],
814
- '77' => [
815
- 'name' => 'Basilicata'
816
- ],
817
- '78' => [
818
- 'name' => 'Calabria'
819
- ],
820
- '72' => [
821
- 'name' => 'Campania'
822
- ],
823
- '45' => [
824
- 'name' => 'Emilia-Romagna'
825
- ],
826
- '62' => [
827
- 'name' => 'Lazio'
828
- ],
829
- '42' => [
830
- 'name' => 'Liguria'
831
- ],
832
- '25' => [
833
- 'name' => 'Lombardy'
834
- ],
835
- '57' => [
836
- 'name' => 'Marche'
837
- ],
838
- '67' => [
839
- 'name' => 'Molise'
840
- ],
841
- '21' => [
842
- 'name' => 'Piedmont'
843
- ],
844
- '75' => [
845
- 'name' => 'Apulia'
846
- ],
847
- '52' => [
848
- 'name' => 'Tuscany'
849
- ],
850
- '55' => [
851
- 'name' => 'Umbria'
852
- ],
853
- '34' => [
854
- 'name' => 'Veneto'
855
- ]
856
- ]
857
- ],
858
- 'JM' => [
859
- 'name' => 'Jamaica',
860
- 'regions' => []
861
- ],
862
- 'JP' => [
863
- 'name' => 'Japan',
864
- 'regions' => []
865
- ],
866
- 'JE' => [
867
- 'name' => 'Jersey',
868
- 'regions' => []
869
- ],
870
- 'JO' => [
871
- 'name' => 'Jordan',
872
- 'regions' => []
873
- ],
874
- 'KZ' => [
875
- 'name' => 'Kazakhstan',
876
- 'regions' => []
877
- ],
878
- 'KE' => [
879
- 'name' => 'Kenya',
880
- 'regions' => []
881
- ],
882
- 'KI' => [
883
- 'name' => 'Kiribati',
884
- 'regions' => []
885
- ],
886
- 'KR' => [
887
- 'name' => 'Korea',
888
- 'regions' => []
889
- ],
890
- 'KW' => [
891
- 'name' => 'Kuwait',
892
- 'regions' => []
893
- ],
894
- 'KG' => [
895
- 'name' => 'Kyrgyzstan',
896
- 'regions' => []
897
- ],
898
- 'LA' => [
899
- 'name' => 'Lao People\'s Democratic Republic',
900
- 'regions' => []
901
- ],
902
- 'LV' => [
903
- 'name' => 'Latvia',
904
- 'regions' => []
905
- ],
906
- 'LB' => [
907
- 'name' => 'Lebanon',
908
- 'regions' => []
909
- ],
910
- 'LS' => [
911
- 'name' => 'Lesotho',
912
- 'regions' => []
913
- ],
914
- 'LR' => [
915
- 'name' => 'Liberia',
916
- 'regions' => []
917
- ],
918
- 'LY' => [
919
- 'name' => 'Libyan Arab Jamahiriya',
920
- 'regions' => []
921
- ],
922
- 'LI' => [
923
- 'name' => 'Liechtenstein',
924
- 'regions' => []
925
- ],
926
- 'LT' => [
927
- 'name' => 'Lithuania',
928
- 'regions' => []
929
- ],
930
- 'LU' => [
931
- 'name' => 'Luxembourg',
932
- 'regions' => []
933
- ],
934
- 'MO' => [
935
- 'name' => 'Macao',
936
- 'regions' => []
937
- ],
938
- 'MK' => [
939
- 'name' => 'Macedonia',
940
- 'regions' => []
941
- ],
942
- 'MG' => [
943
- 'name' => 'Madagascar',
944
- 'regions' => []
945
- ],
946
- 'MW' => [
947
- 'name' => 'Malawi',
948
- 'regions' => []
949
- ],
950
- 'MY' => [
951
- 'name' => 'Malaysia',
952
- 'regions' => [
953
- '14' => [
954
- 'name' => 'Wilayah Persekutuan Kuala Lumpur'
955
- ],
956
- '15' => [
957
- 'name' => 'Wilayah Persekutuan Labuan'
958
- ],
959
- '16' => [
960
- 'name' => 'Wilayah Persekutuan Putrajaya'
961
- ],
962
- '01' => [
963
- 'name' => 'Johor'
964
- ],
965
- '02' => [
966
- 'name' => 'Kedah'
967
- ],
968
- '03' => [
969
- 'name' => 'Kelantan'
970
- ],
971
- '04' => [
972
- 'name' => 'Melaka'
973
- ],
974
- '05' => [
975
- 'name' => 'Negeri Sembilan'
976
- ],
977
- '06' => [
978
- 'name' => 'Pahang'
979
- ],
980
- '08' => [
981
- 'name' => 'Perak'
982
- ],
983
- '09' => [
984
- 'name' => 'Perlis'
985
- ],
986
- '07' => [
987
- 'name' => 'Pulau Pinang'
988
- ],
989
- '12' => [
990
- 'name' => 'Sabah'
991
- ],
992
- '13' => [
993
- 'name' => 'Sarawak'
994
- ],
995
- '10' => [
996
- 'name' => 'Selangor'
997
- ],
998
- '11' => [
999
- 'name' => 'Terengganu'
1000
- ]
1001
- ]
1002
- ],
1003
- 'MV' => [
1004
- 'name' => 'Maldives',
1005
- 'regions' => []
1006
- ],
1007
- 'ML' => [
1008
- 'name' => 'Mali',
1009
- 'regions' => []
1010
- ],
1011
- 'MT' => [
1012
- 'name' => 'Malta',
1013
- 'regions' => []
1014
- ],
1015
- 'MH' => [
1016
- 'name' => 'Marshall Islands',
1017
- 'regions' => []
1018
- ],
1019
- 'MQ' => [
1020
- 'name' => 'Martinique',
1021
- 'regions' => []
1022
- ],
1023
- 'MR' => [
1024
- 'name' => 'Mauritania',
1025
- 'regions' => []
1026
- ],
1027
- 'MU' => [
1028
- 'name' => 'Mauritius',
1029
- 'regions' => []
1030
- ],
1031
- 'YT' => [
1032
- 'name' => 'Mayotte',
1033
- 'regions' => []
1034
- ],
1035
- 'MX' => [
1036
- 'name' => 'Mexico',
1037
- 'regions' => []
1038
- ],
1039
- 'FM' => [
1040
- 'name' => 'Micronesia, Federated States Of',
1041
- 'regions' => []
1042
- ],
1043
- 'MD' => [
1044
- 'name' => 'Moldova',
1045
- 'regions' => []
1046
- ],
1047
- 'MC' => [
1048
- 'name' => 'Monaco',
1049
- 'regions' => []
1050
- ],
1051
- 'MN' => [
1052
- 'name' => 'Mongolia',
1053
- 'regions' => []
1054
- ],
1055
- 'ME' => [
1056
- 'name' => 'Montenegro',
1057
- 'regions' => []
1058
- ],
1059
- 'MS' => [
1060
- 'name' => 'Montserrat',
1061
- 'regions' => []
1062
- ],
1063
- 'MA' => [
1064
- 'name' => 'Morocco',
1065
- 'regions' => []
1066
- ],
1067
- 'MZ' => [
1068
- 'name' => 'Mozambique',
1069
- 'regions' => []
1070
- ],
1071
- 'MM' => [
1072
- 'name' => 'Myanmar',
1073
- 'regions' => []
1074
- ],
1075
- 'NA' => [
1076
- 'name' => 'Namibia',
1077
- 'regions' => []
1078
- ],
1079
- 'NR' => [
1080
- 'name' => 'Nauru',
1081
- 'regions' => []
1082
- ],
1083
- 'NP' => [
1084
- 'name' => 'Nepal',
1085
- 'regions' => []
1086
- ],
1087
- 'NL' => [
1088
- 'name' => 'Netherlands',
1089
- 'regions' => [
1090
- 'NH' => [
1091
- 'name' => 'North Holland'
1092
- ],
1093
- 'ZH' => [
1094
- 'name' => 'South Holland'
1095
- ],
1096
- 'ZE' => [
1097
- 'name' => 'Zeeland'
1098
- ],
1099
- 'UT' => [
1100
- 'name' => 'Utrecht'
1101
- ],
1102
- 'OV' => [
1103
- 'name' => 'Overijssel'
1104
- ],
1105
- 'NB' => [
1106
- 'name' => 'North Brabant'
1107
- ],
1108
- 'LI' => [
1109
- 'name' => 'Limburg'
1110
- ],
1111
- 'GR' => [
1112
- 'name' => 'Groningen'
1113
- ],
1114
- 'GE' => [
1115
- 'name' => 'Gelderland'
1116
- ],
1117
- 'FR' => [
1118
- 'name' => 'Friesland'
1119
- ],
1120
- 'FL' => [
1121
- 'name' => 'Flevoland'
1122
- ],
1123
- 'DR' => [
1124
- 'name' => 'Drenthe'
1125
- ]
1126
- ]
1127
- ],
1128
- 'AN' => [
1129
- 'name' => 'Netherlands Antilles',
1130
- 'regions' => []
1131
- ],
1132
- 'NC' => [
1133
- 'name' => 'New Caledonia',
1134
- 'regions' => []
1135
- ],
1136
- 'NZ' => [
1137
- 'name' => 'New Zealand',
1138
- 'regions' => []
1139
- ],
1140
- 'NI' => [
1141
- 'name' => 'Nicaragua',
1142
- 'regions' => []
1143
- ],
1144
- 'NE' => [
1145
- 'name' => 'Niger',
1146
- 'regions' => []
1147
- ],
1148
- 'NG' => [
1149
- 'name' => 'Nigeria',
1150
- 'regions' => []
1151
- ],
1152
- 'NU' => [
1153
- 'name' => 'Niue',
1154
- 'regions' => []
1155
- ],
1156
- 'NF' => [
1157
- 'name' => 'Norfolk Island',
1158
- 'regions' => []
1159
- ],
1160
- 'MP' => [
1161
- 'name' => 'Northern Mariana Islands',
1162
- 'regions' => []
1163
- ],
1164
- 'NO' => [
1165
- 'name' => 'Norway',
1166
- 'regions' => []
1167
- ],
1168
- 'OM' => [
1169
- 'name' => 'Oman',
1170
- 'regions' => []
1171
- ],
1172
- 'PK' => [
1173
- 'name' => 'Pakistan',
1174
- 'regions' => []
1175
- ],
1176
- 'PW' => [
1177
- 'name' => 'Palau',
1178
- 'regions' => []
1179
- ],
1180
- 'PS' => [
1181
- 'name' => 'Palestinian Territory, Occupied',
1182
- 'regions' => []
1183
- ],
1184
- 'PA' => [
1185
- 'name' => 'Panama',
1186
- 'regions' => []
1187
- ],
1188
- 'PG' => [
1189
- 'name' => 'Papua New Guinea',
1190
- 'regions' => []
1191
- ],
1192
- 'PY' => [
1193
- 'name' => 'Paraguay',
1194
- 'regions' => []
1195
- ],
1196
- 'PE' => [
1197
- 'name' => 'Peru',
1198
- 'regions' => []
1199
- ],
1200
- 'PH' => [
1201
- 'name' => 'Philippines',
1202
- 'regions' => []
1203
- ],
1204
- 'PN' => [
1205
- 'name' => 'Pitcairn',
1206
- 'regions' => []
1207
- ],
1208
- 'PL' => [
1209
- 'name' => 'Poland',
1210
- 'regions' => [
1211
- '02' => [
1212
- 'name' => 'Lower Silesia'
1213
- ],
1214
- '04' => [
1215
- 'name' => 'Kuyavia-Pomerania'
1216
- ],
1217
- '06' => [
1218
- 'name' => 'Lublin'
1219
- ],
1220
- '08' => [
1221
- 'name' => 'Lubusz'
1222
- ],
1223
- '10' => [
1224
- 'name' => 'Łódź'
1225
- ],
1226
- '12' => [
1227
- 'name' => 'Lesser Poland'
1228
- ],
1229
- '14' => [
1230
- 'name' => 'Mazovia'
1231
- ],
1232
- 'MZ' => [
1233
- 'name' => 'Mazovia'
1234
- ],
1235
- '16' => [
1236
- 'name' => 'Opole (Upper Silesia)'
1237
- ],
1238
- '18' => [
1239
- 'name' => 'Subcarpathia'
1240
- ],
1241
- '20' => [
1242
- 'name' => 'Podlaskie'
1243
- ],
1244
- '22' => [
1245
- 'name' => 'Pomerania'
1246
- ],
1247
- '24' => [
1248
- 'name' => 'Silesia'
1249
- ],
1250
- '26' => [
1251
- 'name' => 'Holy Cross'
1252
- ],
1253
- '28' => [
1254
- 'name' => 'Warmia-Masuria'
1255
- ],
1256
- '30' => [
1257
- 'name' => 'Greater Poland'
1258
- ],
1259
- '32' => [
1260
- 'name' => 'West Pomerania'
1261
- ],
1262
- ]
1263
- ],
1264
- 'PT' => [
1265
- 'name' => 'Portugal',
1266
- 'regions' => []
1267
- ],
1268
- 'PR' => [
1269
- 'name' => 'Puerto Rico',
1270
- 'regions' => []
1271
- ],
1272
- 'QA' => [
1273
- 'name' => 'Qatar',
1274
- 'regions' => []
1275
- ],
1276
- 'RE' => [
1277
- 'name' => 'Reunion',
1278
- 'regions' => []
1279
- ],
1280
- 'RO' => [
1281
- 'name' => 'Romania',
1282
- 'regions' => []
1283
- ],
1284
- 'RU' => [
1285
- 'name' => 'Russian Federation',
1286
- 'regions' => [
1287
- 'AD' => [
1288
- 'name' => 'Adygeya, Respublika'
1289
- ],
1290
- 'AL' => [
1291
- 'name' => 'Altay, Respublika'
1292
- ],
1293
- 'BA' => [
1294
- 'name' => 'Bashkortostan, Respublika'
1295
- ],
1296
- 'BU' => [
1297
- 'name' => 'Buryatiya, Respublika'
1298
- ],
1299
- 'CE' => [
1300
- 'name' => 'Chechenskaya Respublika'
1301
- ],
1302
- 'CU' => [
1303
- 'name' => 'Chuvashskaya Respublika'
1304
- ],
1305
- 'DA' => [
1306
- 'name' => 'Dagestan, Respublika'
1307
- ],
1308
- 'IN' => [
1309
- 'name' => 'Ingushetiya, Respublika'
1310
- ],
1311
- 'KB' => [
1312
- 'name' => 'Kabardino-Balkarskaya Respublika'
1313
- ],
1314
- 'KL' => [
1315
- 'name' => 'Kalmykiya, Respublika'
1316
- ],
1317
- 'KC' => [
1318
- 'name' => 'Karachayevo-Cherkesskaya Respublika'
1319
- ],
1320
- 'KR' => [
1321
- 'name' => 'Kareliya, Respublika'
1322
- ],
1323
- 'KK' => [
1324
- 'name' => 'Khakasiya, Respublika'
1325
- ],
1326
- 'KO' => [
1327
- 'name' => 'Komi, Respublika'
1328
- ],
1329
- 'ME' => [
1330
- 'name' => 'Mariy El, Respublika'
1331
- ],
1332
- 'MO' => [
1333
- 'name' => 'Mordoviya, Respublika'
1334
- ],
1335
- 'SA' => [
1336
- 'name' => 'Saha, Respublika'
1337
- ],
1338
- 'SE' => [
1339
- 'name' => 'Severnaya Osetiya, Respublika'
1340
- ],
1341
- 'TA' => [
1342
- 'name' => 'Tatarstan, Respublika'
1343
- ],
1344
- 'TY' => [
1345
- 'name' => 'Tyva, Respublika'
1346
- ],
1347
- 'UD' => [
1348
- 'name' => 'Udmurtskaya Respublika'
1349
- ],
1350
- 'ALT' => [
1351
- 'name' => 'Altayskiy kray'
1352
- ],
1353
- 'KAM' => [
1354
- 'name' => 'Kamchatskiy kray'
1355
- ],
1356
- 'KHA' => [
1357
- 'name' => 'Khabarovskiy kray'
1358
- ],
1359
- 'KDA' => [
1360
- 'name' => 'Krasnodarskiy kray'
1361
- ],
1362
- 'KYA' => [
1363
- 'name' => 'Krasnoyarskiy kray'
1364
- ],
1365
- 'PER' => [
1366
- 'name' => 'Permskiy kray'
1367
- ],
1368
- 'PRI' => [
1369
- 'name' => 'Primorskiy kray'
1370
- ],
1371
- 'STA' => [
1372
- 'name' => 'Stavropol\'skiy kray'
1373
- ],
1374
- 'ZAB' => [
1375
- 'name' => 'Zabaykal\'skiy kray'
1376
- ],
1377
- 'AMU' => [
1378
- 'name' => 'Amurskaya oblast\''
1379
- ],
1380
- 'ARK' => [
1381
- 'name' => 'Arkhangel\'skaya oblast\''
1382
- ],
1383
- 'AST' => [
1384
- 'name' => 'Astrakhanskaya oblast\''
1385
- ],
1386
- 'BEL' => [
1387
- 'name' => 'Belgorodskaya oblast\''
1388
- ],
1389
- 'BRY' => [
1390
- 'name' => 'Bryanskaya oblast\''
1391
- ],
1392
- 'CHE' => [
1393
- 'name' => 'Chelyabinskaya oblast\''
1394
- ],
1395
- 'IRK' => [
1396
- 'name' => 'Irkutskaya oblast\''
1397
- ],
1398
- 'IVA' => [
1399
- 'name' => 'Ivanovskaya oblast\''
1400
- ],
1401
- 'KGD' => [
1402
- 'name' => 'Kaliningradskaya oblast\''
1403
- ],
1404
- 'KLU' => [
1405
- 'name' => 'Kaluzhskaya oblast\''
1406
- ],
1407
- 'KEM' => [
1408
- 'name' => 'Kemerovskaya oblast\''
1409
- ],
1410
- 'KIR' => [
1411
- 'name' => 'Kirovskaya oblast\''
1412
- ],
1413
- 'KOS' => [
1414
- 'name' => 'Kostromskaya oblast\''
1415
- ],
1416
- 'KGN' => [
1417
- 'name' => 'Kurganskaya oblast\''
1418
- ],
1419
- 'KRS' => [
1420
- 'name' => 'Kurskaya oblast\''
1421
- ],
1422
- 'LEN' => [
1423
- 'name' => 'Leningradskaya oblast\''
1424
- ],
1425
- 'LIP' => [
1426
- 'name' => 'Lipetskaya oblast\''
1427
- ],
1428
- 'MAG' => [
1429
- 'name' => 'Magadanskaya oblast\''
1430
- ],
1431
- 'MOS' => [
1432
- 'name' => 'Moskovskaya oblast\''
1433
- ],
1434
- 'MUR' => [
1435
- 'name' => 'Murmanskaya oblast\''
1436
- ],
1437
- 'NIZ' => [
1438
- 'name' => 'Nizhegorodskaya oblast\''
1439
- ],
1440
- 'NGR' => [
1441
- 'name' => 'Novgorodskaya oblast\''
1442
- ],
1443
- 'NVS' => [
1444
- 'name' => 'Novosibirskaya oblast\''
1445
- ],
1446
- 'OMS' => [
1447
- 'name' => 'Omskaya oblast\''
1448
- ],
1449
- 'ORE' => [
1450
- 'name' => 'Orenburgskaya oblast\''
1451
- ],
1452
- 'ORL' => [
1453
- 'name' => 'Orlovskaya oblast\''
1454
- ],
1455
- 'PNZ' => [
1456
- 'name' => 'Penzenskaya oblast\''
1457
- ],
1458
- 'PSK' => [
1459
- 'name' => 'Pskovskaya oblast\''
1460
- ],
1461
- 'ROS' => [
1462
- 'name' => 'Rostovskaya oblast\''
1463
- ],
1464
- 'RYA' => [
1465
- 'name' => 'Ryazanskaya oblast\''
1466
- ],
1467
- 'SAK' => [
1468
- 'name' => 'Sakhalinskaya oblast\''
1469
- ],
1470
- 'SAM' => [
1471
- 'name' => 'Samarskaya oblast\''
1472
- ],
1473
- 'SAR' => [
1474
- 'name' => 'Saratovskaya oblast\''
1475
- ],
1476
- 'SMO' => [
1477
- 'name' => 'Smolenskaya oblast\''
1478
- ],
1479
- 'SVE' => [
1480
- 'name' => 'Sverdlovskaya oblast\''
1481
- ],
1482
- 'TAM' => [
1483
- 'name' => 'Tambovskaya oblast\''
1484
- ],
1485
- 'TOM' => [
1486
- 'name' => 'Tomskaya oblast\''
1487
- ],
1488
- 'TUL' => [
1489
- 'name' => 'Tul\'skaya oblast\''
1490
- ],
1491
- 'TVE' => [
1492
- 'name' => 'Tverskaya oblast\''
1493
- ],
1494
- 'TYU' => [
1495
- 'name' => 'Tyumenskaya oblast\''
1496
- ],
1497
- 'ULY' => [
1498
- 'name' => 'Ul\'yanovskaya oblast\''
1499
- ],
1500
- 'VLA' => [
1501
- 'name' => 'Vladimirskaya oblast\''
1502
- ],
1503
- 'VGG' => [
1504
- 'name' => 'Volgogradskaya oblast\''
1505
- ],
1506
- 'VLG' => [
1507
- 'name' => 'Vologodskaya oblast\''
1508
- ],
1509
- 'VOR' => [
1510
- 'name' => 'Voronezhskaya oblast\''
1511
- ],
1512
- 'YAR' => [
1513
- 'name' => 'Yaroslavskaya oblast\''
1514
- ],
1515
- 'MOW' => [
1516
- 'name' => 'Moskva'
1517
- ],
1518
- 'SPE' => [
1519
- 'name' => 'Sankt-Peterburg'
1520
- ],
1521
- 'YEV' => [
1522
- 'name' => 'Yevreyskaya avtonomnaya oblast\''
1523
- ],
1524
- 'CHU' => [
1525
- 'name' => 'Chukotskiy avtonomnyy okrug'
1526
- ],
1527
- 'KHM' => [
1528
- 'name' => 'Khanty-Mansiyskiy avtonomnyy okrug'
1529
- ],
1530
- 'NEN' => [
1531
- 'name' => 'Nenetskiy avtonomnyy okrug'
1532
- ],
1533
- 'YAN' => [
1534
- 'name' => 'Yamalo-Nenetskiy avtonomnyy okrug'
1535
- ],
1536
- ]
1537
- ],
1538
- 'RW' => [
1539
- 'name' => 'Rwanda',
1540
- 'regions' => []
1541
- ],
1542
- 'BL' => [
1543
- 'name' => 'Saint Barthelemy',
1544
- 'regions' => []
1545
- ],
1546
- 'SH' => [
1547
- 'name' => 'Saint Helena',
1548
- 'regions' => []
1549
- ],
1550
- 'KN' => [
1551
- 'name' => 'Saint Kitts And Nevis',
1552
- 'regions' => []
1553
- ],
1554
- 'LC' => [
1555
- 'name' => 'Saint Lucia',
1556
- 'regions' => []
1557
- ],
1558
- 'MF' => [
1559
- 'name' => 'Saint Martin',
1560
- 'regions' => []
1561
- ],
1562
- 'PM' => [
1563
- 'name' => 'Saint Pierre And Miquelon',
1564
- 'regions' => []
1565
- ],
1566
- 'VC' => [
1567
- 'name' => 'Saint Vincent And Grenadines',
1568
- 'regions' => []
1569
- ],
1570
- 'WS' => [
1571
- 'name' => 'Samoa',
1572
- 'regions' => []
1573
- ],
1574
- 'SM' => [
1575
- 'name' => 'San Marino',
1576
- 'regions' => []
1577
- ],
1578
- 'ST' => [
1579
- 'name' => 'Sao Tome And Principe',
1580
- 'regions' => []
1581
- ],
1582
- 'SA' => [
1583
- 'name' => 'Saudi Arabia',
1584
- 'regions' => []
1585
- ],
1586
- 'SN' => [
1587
- 'name' => 'Senegal',
1588
- 'regions' => []
1589
- ],
1590
- 'RS' => [
1591
- 'name' => 'Serbia',
1592
- 'regions' => []
1593
- ],
1594
- 'SC' => [
1595
- 'name' => 'Seychelles',
1596
- 'regions' => []
1597
- ],
1598
- 'SL' => [
1599
- 'name' => 'Sierra Leone',
1600
- 'regions' => []
1601
- ],
1602
- 'SG' => [
1603
- 'name' => 'Singapore',
1604
- 'regions' => [
1605
- '01' => [
1606
- 'name' => 'Central Singapore'
1607
- ],
1608
- '02' => [
1609
- 'name' => 'North East'
1610
- ],
1611
- '03' => [
1612
- 'name' => 'North West'
1613
- ],
1614
- '04' => [
1615
- 'name' => 'South East'
1616
- ],
1617
- '05' => [
1618
- 'name' => 'South West'
1619
- ]
1620
- ]
1621
- ],
1622
- 'SK' => [
1623
- 'name' => 'Slovakia',
1624
- 'regions' => []
1625
- ],
1626
- 'SI' => [
1627
- 'name' => 'Slovenia',
1628
- 'regions' => []
1629
- ],
1630
- 'SB' => [
1631
- 'name' => 'Solomon Islands',
1632
- 'regions' => []
1633
- ],
1634
- 'SO' => [
1635
- 'name' => 'Somalia',
1636
- 'regions' => []
1637
- ],
1638
- 'ZA' => [
1639
- 'name' => 'South Africa',
1640
- 'regions' => []
1641
- ],
1642
- 'GS' => [
1643
- 'name' => 'South Georgia And Sandwich Isl.',
1644
- 'regions' => []
1645
- ],
1646
- 'ES' => [
1647
- 'name' => 'Spain',
1648
- 'regions' => []
1649
- ],
1650
- 'LK' => [
1651
- 'name' => 'Sri Lanka',
1652
- 'regions' => []
1653
- ],
1654
- 'SD' => [
1655
- 'name' => 'Sudan',
1656
- 'regions' => []
1657
- ],
1658
- 'SR' => [
1659
- 'name' => 'Suriname',
1660
- 'regions' => []
1661
- ],
1662
- 'SJ' => [
1663
- 'name' => 'Svalbard And Jan Mayen',
1664
- 'regions' => []
1665
- ],
1666
- 'SZ' => [
1667
- 'name' => 'Swaziland',
1668
- 'regions' => []
1669
- ],
1670
- 'SE' => [
1671
- 'name' => 'Sweden',
1672
- 'regions' => []
1673
- ],
1674
- 'CH' => [
1675
- 'name' => 'Switzerland',
1676
- 'regions' => []
1677
- ],
1678
- 'SY' => [
1679
- 'name' => 'Syrian Arab Republic',
1680
- 'regions' => []
1681
- ],
1682
- 'TW' => [
1683
- 'name' => 'Taiwan',
1684
- 'regions' => []
1685
- ],
1686
- 'TJ' => [
1687
- 'name' => 'Tajikistan',
1688
- 'regions' => []
1689
- ],
1690
- 'TZ' => [
1691
- 'name' => 'Tanzania',
1692
- 'regions' => []
1693
- ],
1694
- 'TH' => [
1695
- 'name' => 'Thailand',
1696
- 'regions' => []
1697
- ],
1698
- 'TL' => [
1699
- 'name' => 'Timor-Leste',
1700
- 'regions' => []
1701
- ],
1702
- 'TG' => [
1703
- 'name' => 'Togo',
1704
- 'regions' => []
1705
- ],
1706
- 'TK' => [
1707
- 'name' => 'Tokelau',
1708
- 'regions' => []
1709
- ],
1710
- 'TO' => [
1711
- 'name' => 'Tonga',
1712
- 'regions' => []
1713
- ],
1714
- 'TT' => [
1715
- 'name' => 'Trinidad And Tobago',
1716
- 'regions' => []
1717
- ],
1718
- 'TN' => [
1719
- 'name' => 'Tunisia',
1720
- 'regions' => []
1721
- ],
1722
- 'TR' => [
1723
- 'name' => 'Turkey',
1724
- 'regions' => [
1725
- '01' => [
1726
- 'name' => 'Adana'
1727
- ],
1728
- '02' => [
1729
- 'name' => 'Adıyaman'
1730
- ],
1731
- '03' => [
1732
- 'name' => 'Afyonkarahisar'
1733
- ],
1734
- '04' => [
1735
- 'name' => 'Ağrı'
1736
- ],
1737
- '68' => [
1738
- 'name' => 'Aksaray'
1739
- ],
1740
- '05' => [
1741
- 'name' => 'Amasya'
1742
- ],
1743
- '06' => [
1744
- 'name' => 'Ankara'
1745
- ],
1746
- '07' => [
1747
- 'name' => 'Antalya'
1748
- ],
1749
- '75' => [
1750
- 'name' => 'Ardahan'
1751
- ],
1752
- '08' => [
1753
- 'name' => 'Artvin'
1754
- ],
1755
- '09' => [
1756
- 'name' => 'Aydın'
1757
- ],
1758
- '10' => [
1759
- 'name' => 'Balıkesir'
1760
- ],
1761
- '74' => [
1762
- 'name' => 'Bartın'
1763
- ],
1764
- '72' => [
1765
- 'name' => 'Batman'
1766
- ],
1767
- '69' => [
1768
- 'name' => 'Bayburt'
1769
- ],
1770
- '11' => [
1771
- 'name' => 'Bilecik'
1772
- ],
1773
- '12' => [
1774
- 'name' => 'Bingöl'
1775
- ],
1776
- '13' => [
1777
- 'name' => 'Bitlis'
1778
- ],
1779
- '14' => [
1780
- 'name' => 'Bolu'
1781
- ],
1782
- '15' => [
1783
- 'name' => 'Burdur'
1784
- ],
1785
- '16' => [
1786
- 'name' => 'Bursa'
1787
- ],
1788
- '17' => [
1789
- 'name' => 'Çanakkale'
1790
- ],
1791
- '18' => [
1792
- 'name' => 'Çankırı'
1793
- ],
1794
- '19' => [
1795
- 'name' => 'Çorum'
1796
- ],
1797
- '20' => [
1798
- 'name' => 'Denizli'
1799
- ],
1800
- '21' => [
1801
- 'name' => 'Diyarbakır'
1802
- ],
1803
- '81' => [
1804
- 'name' => 'Düzce'
1805
- ],
1806
- '22' => [
1807
- 'name' => 'Edirne'
1808
- ],
1809
- '23' => [
1810
- 'name' => 'Elazığ'
1811
- ],
1812
- '24' => [
1813
- 'name' => 'Erzincan'
1814
- ],
1815
- '25' => [
1816
- 'name' => 'Erzurum'
1817
- ],
1818
- '26' => [
1819
- 'name' => 'Eskişehir'
1820
- ],
1821
- '27' => [
1822
- 'name' => 'Gaziantep'
1823
- ],
1824
- '28' => [
1825
- 'name' => 'Giresun'
1826
- ],
1827
- '29' => [
1828
- 'name' => 'Gümüşhane'
1829
- ],
1830
- '30' => [
1831
- 'name' => 'Hakkâri'
1832
- ],
1833
- '31' => [
1834
- 'name' => 'Hatay'
1835
- ],
1836
- '76' => [
1837
- 'name' => 'Iğdır'
1838
- ],
1839
- '32' => [
1840
- 'name' => 'Isparta'
1841
- ],
1842
- '34' => [
1843
- 'name' => 'İstanbul'
1844
- ],
1845
- '35' => [
1846
- 'name' => 'İzmir'
1847
- ],
1848
- '46' => [
1849
- 'name' => 'Kahramanmaraş'
1850
- ],
1851
- '78' => [
1852
- 'name' => 'Karabük'
1853
- ],
1854
- '70' => [
1855
- 'name' => 'Karaman'
1856
- ],
1857
- '36' => [
1858
- 'name' => 'Kars'
1859
- ],
1860
- '37' => [
1861
- 'name' => 'Kastamonu'
1862
- ],
1863
- '38' => [
1864
- 'name' => 'Kayseri'
1865
- ],
1866
- '71' => [
1867
- 'name' => 'Kırıkkale'
1868
- ],
1869
- '39' => [
1870
- 'name' => 'Kırklareli'
1871
- ],
1872
- '40' => [
1873
- 'name' => 'Kırşehir'
1874
- ],
1875
- '79' => [
1876
- 'name' => 'Kilis'
1877
- ],
1878
- '41' => [
1879
- 'name' => 'Kocaeli'
1880
- ],
1881
- '42' => [
1882
- 'name' => 'Konya'
1883
- ],
1884
- '43' => [
1885
- 'name' => 'Kütahya'
1886
- ],
1887
- '44' => [
1888
- 'name' => 'Malatya'
1889
- ],
1890
- '45' => [
1891
- 'name' => 'Manisa'
1892
- ],
1893
- '47' => [
1894
- 'name' => 'Mardin'
1895
- ],
1896
- '33' => [
1897
- 'name' => 'Mersin'
1898
- ],
1899
- '48' => [
1900
- 'name' => 'Muğla'
1901
- ],
1902
- '49' => [
1903
- 'name' => 'Muş'
1904
- ],
1905
- '50' => [
1906
- 'name' => 'Nevşehir'
1907
- ],
1908
- '51' => [
1909
- 'name' => 'Niğde'
1910
- ],
1911
- '52' => [
1912
- 'name' => 'Ordu'
1913
- ],
1914
- '80' => [
1915
- 'name' => 'Osmaniye'
1916
- ],
1917
- '53' => [
1918
- 'name' => 'Rize'
1919
- ],
1920
- '54' => [
1921
- 'name' => 'Sakarya'
1922
- ],
1923
- '55' => [
1924
- 'name' => 'Samsun'
1925
- ],
1926
- '56' => [
1927
- 'name' => 'Siirt'
1928
- ],
1929
- '57' => [
1930
- 'name' => 'Sinop'
1931
- ],
1932
- '58' => [
1933
- 'name' => 'Sivas'
1934
- ],
1935
- '63' => [
1936
- 'name' => 'Şanlıurfa'
1937
- ],
1938
- '73' => [
1939
- 'name' => 'Şırnak'
1940
- ],
1941
- '59' => [
1942
- 'name' => 'Tekirdağ'
1943
- ],
1944
- '60' => [
1945
- 'name' => 'Tokat'
1946
- ],
1947
- '61' => [
1948
- 'name' => 'Trabzon'
1949
- ],
1950
- '62' => [
1951
- 'name' => 'Tunceli'
1952
- ],
1953
- '64' => [
1954
- 'name' => 'Uşak'
1955
- ],
1956
- '65' => [
1957
- 'name' => 'Van'
1958
- ],
1959
- '77' => [
1960
- 'name' => 'Yalova'
1961
- ],
1962
- '66' => [
1963
- 'name' => 'Yozgat'
1964
- ],
1965
- '67' => [
1966
- 'name' => 'Zonguldak'
1967
- ]
1968
- ]
1969
- ],
1970
- 'TM' => [
1971
- 'name' => 'Turkmenistan',
1972
- 'regions' => []
1973
- ],
1974
- 'TC' => [
1975
- 'name' => 'Turks And Caicos Islands',
1976
- 'regions' => []
1977
- ],
1978
- 'TV' => [
1979
- 'name' => 'Tuvalu',
1980
- 'regions' => []
1981
- ],
1982
- 'UG' => [
1983
- 'name' => 'Uganda',
1984
- 'regions' => []
1985
- ],
1986
- 'UA' => [
1987
- 'name' => 'Ukraine',
1988
- 'regions' => [
1989
- '40' => [
1990
- 'name' => 'Sevastopol'
1991
- ],
1992
- '30' => [
1993
- 'name' => 'Kyiv'
1994
- ],
1995
- '43' => [
1996
- 'name' => 'Avtonomna Respublika Krym'
1997
- ],
1998
- '18' => [
1999
- 'name' => 'Zhytomyrska oblast'
2000
- ],
2001
- '23' => [
2002
- 'name' => 'Zaporizka oblast'
2003
- ],
2004
- '21' => [
2005
- 'name' => 'Zakarpatska oblast'
2006
- ],
2007
- '07' => [
2008
- 'name' => 'Volynska oblast'
2009
- ],
2010
- '05' => [
2011
- 'name' => 'Vinnytska oblast'
2012
- ],
2013
- '61' => [
2014
- 'name' => 'Ternopilska oblast'
2015
- ],
2016
- '59' => [
2017
- 'name' => 'Sumska oblast'
2018
- ],
2019
- '56' => [
2020
- 'name' => 'Rivnenska oblast'
2021
- ],
2022
- '53' => [
2023
- 'name' => 'Poltavska oblast'
2024
- ],
2025
- '51' => [
2026
- 'name' => 'Odeska oblast'
2027
- ],
2028
- '48' => [
2029
- 'name' => 'Mykolaivska oblast'
2030
- ],
2031
- '46' => [
2032
- 'name' => 'Lvivska oblast'
2033
- ],
2034
- '09' => [
2035
- 'name' => 'Luhanska oblast'
2036
- ],
2037
- '32' => [
2038
- 'name' => 'Kyivska oblast'
2039
- ],
2040
- '35' => [
2041
- 'name' => 'Kirovohradska oblast'
2042
- ],
2043
- '68' => [
2044
- 'name' => 'Khmelnytska oblast'
2045
- ],
2046
- '65' => [
2047
- 'name' => 'Khersonska oblast'
2048
- ],
2049
- '63' => [
2050
- 'name' => 'Kharkivska oblast'
2051
- ],
2052
- '26' => [
2053
- 'name' => 'Ivano-Frankivska oblast'
2054
- ],
2055
- '14' => [
2056
- 'name' => 'Donetska oblast'
2057
- ],
2058
- '12' => [
2059
- 'name' => 'Dnipropetrovska oblast'
2060
- ],
2061
- '77' => [
2062
- 'name' => 'Chernivetska oblast'
2063
- ],
2064
- '74' => [
2065
- 'name' => 'Chernihivska oblast'
2066
- ],
2067
- '71' => [
2068
- 'name' => 'Cherkaska oblast'
2069
- ]
2070
- ]
2071
- ],
2072
- 'AE' => [
2073
- 'name' => 'United Arab Emirates',
2074
- 'regions' => []
2075
- ],
2076
- 'GB' => [
2077
- 'name' => 'United Kingdom',
2078
- 'regions' => []
2079
- ],
2080
- 'UM' => [
2081
- 'name' => 'United States Outlying Islands',
2082
- 'regions' => []
2083
- ],
2084
- 'UY' => [
2085
- 'name' => 'Uruguay',
2086
- 'regions' => []
2087
- ],
2088
- 'UZ' => [
2089
- 'name' => 'Uzbekistan',
2090
- 'regions' => []
2091
- ],
2092
- 'VU' => [
2093
- 'name' => 'Vanuatu',
2094
- 'regions' => []
2095
- ],
2096
- 'VE' => [
2097
- 'name' => 'Venezuela',
2098
- 'regions' => []
2099
- ],
2100
- 'VN' => [
2101
- 'name' => 'Viet Nam',
2102
- 'regions' => []
2103
- ],
2104
- 'VG' => [
2105
- 'name' => 'Virgin Islands, British',
2106
- 'regions' => []
2107
- ],
2108
- 'VI' => [
2109
- 'name' => 'Virgin Islands, U.S.',
2110
- 'regions' => []
2111
- ],
2112
- 'WF' => [
2113
- 'name' => 'Wallis And Futuna',
2114
- 'regions' => []
2115
- ],
2116
- 'EH' => [
2117
- 'name' => 'Western Sahara',
2118
- 'regions' => []
2119
- ],
2120
- 'YE' => [
2121
- 'name' => 'Yemen',
2122
- 'regions' => []
2123
- ],
2124
- 'ZM' => [
2125
- 'name' => 'Zambia',
2126
- 'regions' => []
2127
- ],
2128
- 'ZW' => [
2129
- 'name' => 'Zimbabwe',
2130
- 'regions' => []
2131
- ]
2132
- ];
2133
-
2134
- if ( ! $region_code ) {
2135
- if ( ! empty( $locations[ $country_code ]['name'] ) ) {
2136
- return $locations[ $country_code ]['name'];
2137
- } elseif( ! $country_code ) {
2138
- return false;
2139
- } else {
2140
- return $country_code;
2141
- }
2142
- } else {
2143
- if ( ! empty( $locations[ $country_code ]['regions'][ $region_code ]['name'] ) ) {
2144
- return $locations[ $country_code ]['regions'][ $region_code ]['name'];
2145
- } elseif( ! $region_code ) {
2146
- return false;
2147
- } else {
2148
- return $region_code;
2149
- }
2150
- }
2151
- }
2152
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/scripts.php DELETED
@@ -1,160 +0,0 @@
1
- <?php
2
- /**
3
- * Contains all JS & CSS scripts for the WordPress Zero Spam plugin.
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- /**
10
- * Register & enqueue the WordPress Zero Spam JS & CSS for the admin dashboard
11
- */
12
- if ( ! function_exists( 'wpzerospam_admin_scripts' ) ) {
13
- function wpzerospam_admin_scripts( $hook_suffix ) {
14
- wp_enqueue_style(
15
- 'wpzerospam-admin',
16
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
17
- 'assets/css/admin.css',
18
- false,
19
- WORDPRESS_ZERO_SPAM_VERSION
20
- );
21
-
22
- // Register Chart.js for graphs
23
- wp_register_script(
24
- 'wpzerospam-charts',
25
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
26
- 'assets/js/Chart.bundle.min.js',
27
- [],
28
- '2.9.3'
29
- );
30
-
31
- // Register Chart.css for graphs
32
- wp_register_style(
33
- 'wpzerospam-charts',
34
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
35
- 'assets/css/Chart.min.css',
36
- false,
37
- '2.9.3'
38
- );
39
-
40
- // Register jvectormap for world map
41
- wp_register_script(
42
- 'wpzerospam-map',
43
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
44
- 'assets/js/jquery-jvectormap-2.0.5.min.js',
45
- [ 'jquery' ],
46
- '2.0.5'
47
- );
48
-
49
- wp_register_script(
50
- 'wpzerospam-world-map',
51
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
52
- 'assets/js/jquery-jvectormap-world-mill.js',
53
- [ 'wpzerospam-map' ],
54
- '2.0.5'
55
- );
56
-
57
- // Register jvectormap for world map
58
- wp_register_style(
59
- 'wpzerospam-map',
60
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
61
- 'assets/css/jquery-jvectormap-2.0.5.css',
62
- false,
63
- '2.9.3'
64
- );
65
-
66
- wp_register_script(
67
- 'wpzerospam-admin-tables',
68
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
69
- 'assets/js/admin-tables.js',
70
- [ 'jquery' ],
71
- WORDPRESS_ZERO_SPAM_VERSION,
72
- true
73
- );
74
-
75
- wp_register_style(
76
- 'wpzerospam-admin-tables',
77
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
78
- 'assets/css/admin-tables.css',
79
- false,
80
- WORDPRESS_ZERO_SPAM_VERSION
81
- );
82
-
83
- // Handle registering & enqueuing scripts based on the current admin page
84
- switch( $hook_suffix ) {
85
- case 'wp-zero-spam_page_wordpress-zero-spam-detections':
86
- case 'wp-zero-spam_page_wordpress-zero-spam-blacklisted':
87
- wp_enqueue_script( 'wpzerospam-admin-tables' );
88
- wp_enqueue_style( 'wpzerospam-admin-tables' );
89
- break;
90
- case 'toplevel_page_wordpress-zero-spam':
91
- // Enqueue Chart.js
92
- wp_enqueue_script( 'wpzerospam-charts' );
93
- wp_enqueue_style( 'wpzerospam-charts' );
94
-
95
- // Enqueue jvectormap.js
96
- wp_enqueue_script( 'wpzerospam-world-map' );
97
- wp_enqueue_style( 'wpzerospam-map' );
98
-
99
- wp_enqueue_style(
100
- 'wpzerospam-admin-dashboard',
101
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
102
- 'assets/css/admin-dashboard.css',
103
- false,
104
- WORDPRESS_ZERO_SPAM_VERSION
105
- );
106
- break;
107
- case 'wp-zero-spam_page_wordpress-zero-spam-blocked-ips':
108
- wp_enqueue_script( 'wpzerospam-admin-tables' );
109
- wp_enqueue_style( 'wpzerospam-admin-tables' );
110
-
111
- // Enqueue the JS for the WordPress Zero Spam blocked IPs page
112
- wp_enqueue_script(
113
- 'wpzerospam-admin-blocked-ips',
114
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
115
- 'assets/js/admin-blocked-ips.js',
116
- [ 'jquery' ],
117
- WORDPRESS_ZERO_SPAM_VERSION,
118
- true
119
- );
120
-
121
- // Enqueue the CSS for the WordPress Zero Spam blocked IPs page
122
- wp_enqueue_style(
123
- 'wpzerospam-admin-block_ips',
124
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
125
- 'assets/css/admin-blocked-ips.css',
126
- false,
127
- WORDPRESS_ZERO_SPAM_VERSION
128
- );
129
- break;
130
- }
131
- }
132
- }
133
- add_action( 'admin_enqueue_scripts', 'wpzerospam_admin_scripts' );
134
-
135
- /**
136
- * Register & enqueue the WordPress Zero Spam JS & CSS for the frontend
137
- */
138
- if ( ! function_exists( 'wpzerospam_enqueue_scripts' ) ) {
139
- function wpzerospam_enqueue_scripts() {
140
- // Load the JS that contains the WordPressZeroSpam oject, needed on all
141
- // pages
142
- wp_enqueue_script(
143
- 'wpzerospam',
144
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
145
- 'assets/js/wpzerospam.js',
146
- [ 'jquery' ],
147
- WORDPRESS_ZERO_SPAM_VERSION,
148
- true
149
- );
150
-
151
- // Pass the latest generate key to the frontend script
152
- wp_localize_script(
153
- 'wpzerospam',
154
- 'wpzerospam',
155
- [ 'key' => wpzerospam_get_key() ]
156
- );
157
- }
158
- }
159
- add_action( 'wp_enqueue_scripts', 'wpzerospam_enqueue_scripts' );
160
- add_action( 'login_footer', 'wpzerospam_enqueue_scripts' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/uninstall.php DELETED
@@ -1,50 +0,0 @@
1
- <?php
2
- /**
3
- * Handles uninstalling the plugin
4
- *
5
- * @package WordPressZeroSpam
6
- */
7
-
8
- // Security Note: Blocks direct access to the plugin PHP files.
9
- defined( 'ABSPATH' ) || die();
10
-
11
- /**
12
- * Fired when the plugin is deleted.
13
- */
14
- function wpzerospam_uninstall() {
15
- global $wpdb;
16
-
17
- $wordpress_zero_spam = new WPZeroSpam();
18
-
19
- if ( is_multisite() ) {
20
- $blogs = $wpdb->get_results( "SELECT blog_id FROM {$wpdb->blogs}", ARRAY_A );
21
-
22
- if ( $blogs ) {
23
- foreach ( $blogs as $blog ) {
24
- switch_to_blog( $blog['blog_id'] );
25
-
26
- delete_option( 'wpzerospam' );
27
- delete_option( 'wpzerospam_key' );
28
- delete_option( 'wpzerospam_honeypot' );
29
- delete_option( 'wpzerospam_db_version' );
30
- delete_option( 'wpzerospam_update_version' );
31
-
32
- foreach ( $wordpress_zero_spam->tables as $key => $table ) {
33
- $wpdb->query( "DROP TABLE IF EXISTS $table" );
34
- }
35
- }
36
- restore_current_blog();
37
- }
38
- } else {
39
- delete_option( 'wpzerospam' );
40
- delete_option( 'wpzerospam_key' );
41
- delete_option( 'wpzerospam_honeypot' );
42
- delete_option( 'wpzerospam_db_version' );
43
- delete_option( 'wpzerospam_update_version' );
44
-
45
- foreach ( $wordpress_zero_spam->tables as $key => $table ) {
46
- $wpdb->query( "DROP TABLE IF EXISTS $table" );
47
- }
48
- }
49
- }
50
- register_uninstall_hook( WORDPRESS_ZERO_SPAM, 'wpzerospam_uninstall' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/updates.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- /**
3
- * Contains plugin updates
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.0.0
7
- */
8
-
9
- /**
10
- * Check if any updates should be preformed
11
- */
12
- add_action( 'init', function() {
13
- global $wpdb;
14
- $update_version = get_option( 'wpzerospam_update_version' );
15
-
16
- if ( ! $update_version ) {
17
- // Clear the blacklist.
18
- $wpdb->query( "TRUNCATE TABLE " . wpzerospam_tables( 'blacklist' ) );
19
- update_option( 'wpzerospam_update_version', '1' );
20
- }
21
- });
22
-
23
-
24
- /**
25
- * Fixes issue with upgrade from 3 to 4
26
- *
27
- * @since 4.2.0
28
- */
29
- add_action( 'admin_init', function() {
30
- if( ! function_exists( 'is_plugin_active' ) ) {
31
- require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
32
- }
33
-
34
- // Deactive deprecated versions of the plugin & activate the new one
35
- if (
36
- is_plugin_active( 'zero-spam/zero-spam.php' ) &&
37
- function_exists( 'deactivate_plugins' )
38
- ) {
39
- deactivate_plugins( 'zero-spam/zero-spam.php', false, true );
40
- }
41
-
42
- if (
43
- ! is_plugin_active( 'zero-spam/wordpress-zero-spam.php' ) &&
44
- function_exists( 'activate_plugin' )
45
- ) {
46
- activate_plugin( 'zero-spam/wordpress-zero-spam.php', '', true );
47
- }
48
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/utilities.php DELETED
@@ -1,309 +0,0 @@
1
- <?php
2
- /**
3
- * Utility helpers
4
- *
5
- * Helpers functions to return data vs. altering it or doing anything with it.
6
- *
7
- * @package WordPressZeroSpam
8
- * @since 4.9.3
9
- * @link https://benmarshall.me/wordpress-zero-spam/
10
- */
11
-
12
- /**
13
- * Locations helper
14
- */
15
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/locations.php';
16
-
17
- /**
18
- * Outputs a honeypot field
19
- *
20
- * @since 4.9.9
21
- *
22
- * @return string Returns a HTML honeypot field.
23
- */
24
- if ( ! function_exists( 'wpzerospam_honeypot_field' ) ) {
25
- function wpzerospam_honeypot_field() {
26
- return '<input type="text" name="' . wpzerospam_get_honeypot() . '" value="" style="display: none !important;" />';
27
- }
28
- }
29
-
30
- /**
31
- * Get the user's current URL
32
- */
33
- if ( ! function_exists( 'wpzerospam_current_url' ) ) {
34
- function wpzerospam_current_url() {
35
- $url = [];
36
-
37
- $url['full'] = ( isset( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
38
- $url = array_merge( $url, parse_url( $url['full'] ) );
39
-
40
- // Parse the URL query string
41
- if ( ! empty( $url['query'] ) ) {
42
- parse_str( $url['query'], $url['query'] );
43
- }
44
-
45
- return $url;
46
- }
47
- }
48
-
49
- /**
50
- * Returns the current user's IP address
51
- */
52
- if ( ! function_exists( 'wpzerospam_ip' ) ) {
53
- function wpzerospam_ip() {
54
- if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
55
- $ip = $_SERVER['HTTP_CLIENT_IP'];
56
- } elseif ( ! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
57
- $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
58
- } elseif ( ! empty($_SERVER['HTTP_X_FORWARDED'])) {
59
- $ip = $_SERVER['HTTP_X_FORWARDED'];
60
- } elseif ( ! empty($_SERVER['HTTP_FORWARDED_FOR'])) {
61
- $ip = $_SERVER['HTTP_FORWARDED_FOR'];
62
- } elseif ( ! empty($_SERVER['HTTP_FORWARDED'])) {
63
- $ip = $_SERVER['HTTP_FORWARDED'];
64
- } else {
65
- $ip = $_SERVER['REMOTE_ADDR'];
66
- }
67
-
68
- $ip = explode( ',', $ip );
69
- $ip = trim( $ip[0] );
70
-
71
- if ( ! rest_is_ip_address( $ip ) ) { return false; }
72
-
73
- return $ip;
74
- }
75
- }
76
-
77
- /**
78
- * Returns an array of tables the plugin uses
79
- */
80
- if ( ! function_exists( 'wpzerospam_tables' ) ) {
81
- function wpzerospam_tables( $key = false ) {
82
- global $wpdb;
83
-
84
- $tables = [
85
- 'log' => $wpdb->prefix . 'wpzerospam_log',
86
- 'blocked' => $wpdb->prefix . 'wpzerospam_blocked',
87
- 'blacklist' => $wpdb->prefix . 'wpzerospam_blacklist'
88
- ];
89
-
90
- if ( ! $key ) {
91
- return $tables;
92
- } elseif( ! empty( $tables[ $key ] ) ) {
93
- return $tables[ $key ];
94
- }
95
-
96
- return false;
97
- }
98
- }
99
-
100
- /**
101
- * Returns the plugin settings.
102
- */
103
- if ( ! function_exists( 'wpzerospam_options' ) ) {
104
- function wpzerospam_options() {
105
- $options = get_option( 'wpzerospam' );
106
-
107
- if ( empty( $options['share_data'] ) ) { $options['share_data'] = 'enabled'; }
108
- if ( empty( $options['auto_block_ips'] ) ) { $options['auto_block_ips'] = 'disabled'; }
109
- if ( empty( $options['auto_block_period'] ) ) { $options['auto_block_period'] = 30; }
110
- if ( empty( $options['blocked_redirect_url'] ) ) { $options['blocked_redirect_url'] = 'https://www.google.com'; }
111
- if ( empty( $options['spam_handler'] ) ) { $options['spam_handler'] = '403'; }
112
- if ( empty( $options['block_handler'] ) ) { $options['block_handler'] = '403'; }
113
- if ( empty( $options['spam_redirect_url'] ) ) { $options['spam_redirect_url'] = 'https://www.google.com'; }
114
- if ( empty( $options['spam_message'] ) ) { $options['spam_message'] = __( 'There was a problem with your submission. Please go back and try again.', 'zero-spam' ); }
115
- if ( empty( $options['blocked_message'] ) ) { $options['blocked_message'] = __( 'You have been blocked from visiting this site by WordPress Zero Spam due to detected spam activity.', 'zero-spam' ); }
116
- if ( empty( $options['log_spam'] ) ) { $options['log_spam'] = 'disabled'; }
117
- if ( empty( $options['log_blocked_ips'] ) ) { $options['log_blocked_ips'] = 'disabled'; }
118
- if ( empty( $options['auto_block_permanently'] ) ) { $options['auto_block_permanently'] = 3; }
119
- if ( empty( $options['botscout_api'] ) ) { $options['botscout_api'] = false; }
120
- if ( empty( $options['ip_whitelist'] ) ) { $options['ip_whitelist'] = false; }
121
- if ( empty( $options['api_timeout'] ) ) { $options['api_timeout'] = 5; }
122
- if ( empty( $options['stopforumspam_confidence_min'] ) ) { $options['stopforumspam_confidence_min'] = 20; }
123
- if ( empty( $options['botscout_count_min'] ) ) { $options['botscout_count_min'] = 5; }
124
- if ( empty( $options['cookie_expiration'] ) ) { $options['cookie_expiration'] = 7; }
125
-
126
- if ( empty( $options['share_detections'] ) ) {
127
- $options['share_detections'] = 'disabled';
128
- }
129
-
130
- if ( empty( $options['verify_bp_registrations'] ) ) {
131
- $options['verify_bp_registrations'] = 'enabled';
132
- }
133
-
134
- if ( empty( $options['verify_wpforms'] ) ) {
135
- $options['verify_wpforms'] = 'enabled';
136
- }
137
-
138
- if ( empty( $options['verify_fluentform'] ) ) {
139
- $options['verify_fluentform'] = 'enabled';
140
- }
141
-
142
- if ( empty( $options['verify_formidable'] ) ) {
143
- $options['verify_formidable'] = 'enabled';
144
- }
145
-
146
- if ( empty( $options['stop_forum_spam'] ) ) {
147
- $options['stop_forum_spam'] = 'enabled';
148
- }
149
-
150
- $options = apply_filters( 'wpzerospam_admin_options_defaults', $options );
151
- return $options;
152
- }
153
- }
154
-
155
- /**
156
- * Query the database tables
157
- *
158
- * @return false/array False if not found, otherwise the blocked IP info.
159
- */
160
- if ( ! function_exists( 'wpzerospam_query_table' ) ) {
161
- function wpzerospam_query_table( $table, $args = [] ) {
162
- global $wpdb;
163
-
164
- // Select
165
- $sql = 'SELECT ';
166
- if ( ! empty( $args['select'] ) ) {
167
- $sql .= implode( ',', $args['select'] );
168
- } else {
169
- $sql .= '*';
170
- }
171
-
172
- // From
173
- $sql .= " from " . wpzerospam_tables( $table );
174
-
175
- // Where
176
- if ( ! empty( $args['where'] ) ) {
177
- $sql .= ' WHERE ';
178
- foreach( $args['where'] as $key => $value ) {
179
- if ( is_int( $value ) ) {
180
- $sql .= $key . ' = ' . $value . ' ';
181
- } else {
182
- $sql .= $key . ' = "' . $value . '" ';
183
- }
184
- }
185
- }
186
-
187
- // Limit
188
- if ( ! empty( $args['limit'] ) ) {
189
- $sql .= 'LIMIT ' . $args['limit'];
190
-
191
- // Offset
192
- if ( ! empty( $args['offset'] ) ) {
193
- $sql .= ', ' . $args['offset'];
194
- }
195
- }
196
-
197
- if ( ! empty( $args['limit'] ) && 1 == $args['limit'] ) {
198
- return $wpdb->get_row( $sql, ARRAY_A );
199
- } else {
200
- return $wpdb->get_results( $sql, ARRAY_A );
201
- }
202
- }
203
- }
204
-
205
- /**
206
- * Returns the geolocation information for a specified IP address.
207
- *
208
- * @param string $ip IP address.
209
- * @return array/false An array with the IP address location information or
210
- * false if not found.
211
- */
212
- if ( ! function_exists( 'wpzerospam_get_ip_info' ) ) {
213
- function wpzerospam_get_ip_info( $ip ) {
214
- $options = wpzerospam_options();
215
-
216
- if ( empty( $options['ipstack_api'] ) ) { return false; }
217
-
218
- $base_url = 'http://api.ipstack.com/';
219
- $remote_url = $base_url . $ip . '?access_key=' . $options['ipstack_api'];
220
- $response = wp_remote_get( $remote_url, [ 'timeout' => $options['api_timeout'] ] );
221
-
222
- if ( is_array( $response ) && ! is_wp_error( $response ) ) {
223
- $info = json_decode( $response['body'], true );
224
-
225
- return [
226
- 'type' => ! empty( $info['type'] ) ? sanitize_text_field( $info['type'] ) : false,
227
- 'continent_code' => ! empty( $info['continent_code'] ) ? sanitize_text_field( $info['continent_code'] ) : false,
228
- 'continent_name' => ! empty( $info['continent_name'] ) ? sanitize_text_field( $info['continent_name'] ) : false,
229
- 'country_code' => ! empty( $info['country_code'] ) ? sanitize_text_field( $info['country_code'] ) : false,
230
- 'country_name' => ! empty( $info['country_name'] ) ? sanitize_text_field( $info['country_name'] ) : false,
231
- 'region_code' => ! empty( $info['region_code'] ) ? sanitize_text_field( $info['region_code'] ) : false,
232
- 'region_name' => ! empty( $info['region_name'] ) ? sanitize_text_field( $info['region_name'] ) : false,
233
- 'city' => ! empty( $info['city'] ) ? sanitize_text_field( $info['city'] ) : false,
234
- 'zip' => ! empty( $info['zip'] ) ? sanitize_text_field( $info['zip'] ) : false,
235
- 'latitude' => ! empty( $info['latitude'] ) ? sanitize_text_field( $info['latitude'] ) : false,
236
- 'longitude' => ! empty( $info['longitude'] ) ? sanitize_text_field( $info['longitude'] ) : false,
237
- 'flag' => ! empty( $info['location']['country_flag'] ) ? sanitize_text_field( $info['location']['country_flag'] ) : false,
238
- ];
239
- }
240
-
241
- return false;
242
- }
243
- }
244
-
245
- /**
246
- * Returns the human-readable spam type or an array of available spam types.
247
- *
248
- * @param string $type_key The key of the type that should be returned.
249
- * @return string/array The human-readable type name or an array of all the
250
- * available types.
251
- */
252
- if ( ! function_exists( 'wpzerospam_types' ) ) {
253
- function wpzerospam_types( $type_key = false ) {
254
- $types = apply_filters( 'wpzerospam_types', [ 'blocked' => __( 'Access Blocked', 'wpzerospam' ) ] );
255
-
256
- if ( $type_key ) {
257
- if ( ! empty( $types[ $type_key ] ) ) {
258
- return $types[ $type_key ];
259
- }
260
-
261
- return $type_key;
262
- }
263
-
264
- return $types;
265
- }
266
- }
267
-
268
- /**
269
- * Checks if an IP is blocked
270
- *
271
- * @param string IP address to check.
272
- * @return boolean/array False is not blocked, otherwise an array with the
273
- * blocked IP information.
274
- */
275
- if ( ! function_exists( 'wpzerospam_is_blocked' ) ) {
276
- function wpzerospam_is_blocked( $ip ) {
277
- $blocked_ip = wpzerospam_query_table( 'blocked', [
278
- 'select' => [
279
- 'blocked_type',
280
- 'start_block',
281
- 'end_block',
282
- 'reason',
283
- 'attempts'
284
- ],
285
- 'where' => [ 'user_ip' => $ip ],
286
- 'limit' => 1
287
- ]);
288
-
289
- if ( $blocked_ip ) {
290
- if ( 'permanent' == $blocked_ip['blocked_type'] ) {
291
- // Permanently blocked
292
- return $blocked_ip;
293
- } else {
294
- // Temporarily blocked
295
- $current_datetime = current_time( 'timestamp' );
296
- $start_block = strtotime( $blocked_ip['start_block'] );
297
- $end_block = strtotime( $blocked_ip['end_block'] );
298
- if (
299
- $current_datetime >= $start_block &&
300
- $current_datetime < $end_block
301
- ) {
302
- return $blocked_ip;
303
- }
304
- }
305
- }
306
-
307
- return false;
308
- }
309
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/class-autoloader.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Autoloader class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam;
9
+
10
+ // Security Note: Blocks direct access to the plugin PHP files.
11
+ defined( 'ABSPATH' ) || die();
12
+
13
+ /**
14
+ * WordPress Zero Spam autoloader.
15
+ *
16
+ * WordPress Zero Spam autoloader handler class is responsible for loading the
17
+ * different classes needed to run the plugin.
18
+ *
19
+ * @since 5.0.0
20
+ */
21
+ class Autoloader {
22
+
23
+ /**
24
+ * Default path for autoloader.
25
+ *
26
+ * @var string
27
+ */
28
+ private static $default_path;
29
+
30
+ /**
31
+ * Default namespace for autoloader.
32
+ *
33
+ * @var string
34
+ */
35
+ private static $default_namespace;
36
+
37
+ /**
38
+ * Run autoloader.
39
+ *
40
+ * Register a function as `__autoload()` implementation.
41
+ *
42
+ * @param string $default_path
43
+ * @param string $default_namespace
44
+ *
45
+ * @since 5.0.0
46
+ * @access public
47
+ * @static
48
+ */
49
+ public static function run( $default_path = '', $default_namespace = '' ) {
50
+ if ( '' === $default_path ) {
51
+ $default_path = ZEROSPAM_PATH;
52
+ }
53
+
54
+ if ( '' === $default_namespace ) {
55
+ $default_namespace = __NAMESPACE__;
56
+ }
57
+
58
+ self::$default_path = $default_path;
59
+ self::$default_namespace = $default_namespace;
60
+
61
+ spl_autoload_register( array( __CLASS__, 'autoload' ) );
62
+ }
63
+
64
+ /**
65
+ * Load class.
66
+ *
67
+ * For a given class name, require the class file.
68
+ *
69
+ * @since 5.0.0
70
+ * @access private
71
+ * @static
72
+ *
73
+ * @param string $relative_class_name Class name.
74
+ */
75
+ private static function load_class( $relative_class_name ) {
76
+ $filename = strtolower(
77
+ preg_replace(
78
+ array( '/_/', '/\\\/' ),
79
+ array( '', DIRECTORY_SEPARATOR ),
80
+ $relative_class_name
81
+ )
82
+ );
83
+
84
+ $filename_parts = explode( '/', $filename );
85
+ $last_part = array_key_last( $filename_parts );
86
+
87
+ $filename_parts[ $last_part ] = 'class-' . $filename_parts[ $last_part ];
88
+
89
+ $filename = implode( '/', $filename_parts );
90
+ $filename = self::$default_path . $filename . '.php';
91
+
92
+ if ( is_readable( $filename ) ) {
93
+ require $filename;
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Autoload.
99
+ *
100
+ * For a given class, check if it exist and load it.
101
+ *
102
+ * @since 5.0.0
103
+ * @access private
104
+ * @static
105
+ *
106
+ * @param string $class Class name.
107
+ */
108
+ private static function autoload( $class ) {
109
+ if ( 0 !== strpos( $class, self::$default_namespace . '\\' ) ) {
110
+ return;
111
+ }
112
+
113
+ $relative_class_name = preg_replace( '/^' . self::$default_namespace . '\\\/', '', $class );
114
+
115
+ $final_class_name = self::$default_namespace . '\\' . $relative_class_name;
116
+
117
+ if ( ! class_exists( $final_class_name ) ) {
118
+ self::load_class( $relative_class_name );
119
+ }
120
+ }
121
+ }
includes/class-db.php ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * DB class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Includes;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * WordPress Zero Spam DB class.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class DB {
21
+
22
+ /**
23
+ * Current DB version.
24
+ */
25
+ const DB_VERSION = '0.4';
26
+
27
+ /**
28
+ * DB tables.
29
+ *
30
+ * @since 5.0.0
31
+ * @access public
32
+ *
33
+ * @var Assets_Manager
34
+ */
35
+ public static $tables = array(
36
+ 'log' => 'wpzerospam_log',
37
+ 'blocked' => 'wpzerospam_blocked',
38
+ 'blacklist' => 'wpzerospam_blacklist',
39
+ );
40
+
41
+ /**
42
+ * DB constructor.
43
+ *
44
+ * @since 5.0.0
45
+ * @access public
46
+ */
47
+ public function __construct() {
48
+ add_action( 'init', array( $this, 'update' ) );
49
+ }
50
+
51
+ /**
52
+ * Installs & updates the DB tables.
53
+ *
54
+ * @since 5.0.0
55
+ * @access public
56
+ */
57
+ public function update() {
58
+ if ( self::DB_VERSION !== get_site_option( 'zerospam_db_version' ) ) {
59
+ global $wpdb;
60
+
61
+ $charset_collate = $wpdb->get_charset_collate();
62
+ $installed_db_version = get_option( 'zerospam_db_version' );
63
+
64
+ $sql = 'CREATE TABLE ' . $wpdb->prefix . self::$tables['log'] . " (
65
+ log_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
66
+ log_type VARCHAR(255) NOT NULL,
67
+ user_ip VARCHAR(39) NOT NULL,
68
+ date_recorded DATETIME NOT NULL,
69
+ page_url VARCHAR(255) NULL DEFAULT NULL,
70
+ submission_data LONGTEXT NULL DEFAULT NULL,
71
+ country VARCHAR(2) NULL DEFAULT NULL,
72
+ country_name VARCHAR(255) NULL DEFAULT NULL,
73
+ region VARCHAR(255) NULL DEFAULT NULL,
74
+ region_name VARCHAR(255) NULL DEFAULT NULL,
75
+ city VARCHAR(255) NULL DEFAULT NULL,
76
+ zip VARCHAR(10) NULL DEFAULT NULL,
77
+ latitude VARCHAR(255) NULL DEFAULT NULL,
78
+ longitude VARCHAR(255) NULL DEFAULT NULL,
79
+ PRIMARY KEY (`log_id`)) $charset_collate;";
80
+
81
+ $sql .= 'CREATE TABLE ' . $wpdb->prefix . self::$tables['blocked'] . " (
82
+ blocked_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
83
+ blocked_type ENUM('permanent','temporary') NOT NULL DEFAULT 'temporary',
84
+ user_ip VARCHAR(39) NOT NULL,
85
+ blocked_key VARCHAR(255) NULL,
86
+ key_type ENUM('ip','email') NOT NULL DEFAULT 'ip',
87
+ date_added DATETIME NOT NULL,
88
+ start_block DATETIME NULL DEFAULT NULL,
89
+ end_block DATETIME NULL DEFAULT NULL,
90
+ reason VARCHAR(255) NULL DEFAULT NULL,
91
+ PRIMARY KEY (`blocked_id`)) $charset_collate;";
92
+
93
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
94
+ dbDelta( $sql );
95
+
96
+ update_option( 'zerospam_db_version', self::DB_VERSION );
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Adds/returns a blocked IP.
102
+ *
103
+ * @since 5.0.0
104
+ * @access public
105
+ */
106
+ public static function blocked( $record, $key_type = false ) {
107
+ global $wpdb;
108
+
109
+ if ( is_array( $record ) ) {
110
+ $blocked = false;
111
+
112
+ // Add or update a record.
113
+ if ( ! empty( $record['blocked_id'] ) ) {
114
+ // Update a record.
115
+ $blocked['blocked_id'] = $record['blocked_id'];
116
+ } elseif ( ! empty( $record['user_ip'] ) ) {
117
+ // Add a record, but first check if IP is unique.
118
+ $blocked = self::blocked( $record['user_ip'] );
119
+ } elseif ( ! empty( $record['key_type'] ) && ! empty( $record['blocked_key'] ) ) {
120
+ // Add a record, but first check if key is unique.
121
+ $blocked = self::blocked( $record['blocked_key'], $record['key_type'] );
122
+ }
123
+
124
+ if ( $blocked ) {
125
+ // Update the record.
126
+ $record['last_updated'] = current_time( 'mysql' );
127
+ return $wpdb->update(
128
+ $wpdb->prefix . self::$tables['blocked'],
129
+ $record,
130
+ array(
131
+ 'blocked_id' => $blocked['blocked_id'],
132
+ )
133
+ );
134
+ } else {
135
+ // Insert the record.
136
+ $record['date_added'] = current_time( 'mysql' );
137
+ return $wpdb->insert( $wpdb->prefix . self::$tables['blocked'], $record );
138
+ }
139
+ } elseif ( $key_type ) {
140
+ // Get record by key.
141
+ return $wpdb->get_row( 'SELECT * FROM ' . $wpdb->prefix . self::$tables['blocked'] . ' WHERE key_type = "' . $key_type . '" AND blocked_key = "' . $record . '"', ARRAY_A );
142
+ } elseif ( is_int( $record ) ) {
143
+ // Get record by ID.
144
+ return $wpdb->get_row( 'SELECT * FROM ' . $wpdb->prefix . self::$tables['blocked'] . ' WHERE blocked_id = "' . $record . '"', ARRAY_A );
145
+ } elseif ( rest_is_ip_address( $record ) ) {
146
+ // Get record by IP.
147
+ return $wpdb->get_row( 'SELECT * FROM ' . $wpdb->prefix . self::$tables['blocked'] . ' WHERE user_ip = "' . $record . '"', ARRAY_A );
148
+ }
149
+
150
+ return false;
151
+ }
152
+
153
+ /**
154
+ * Log.
155
+ *
156
+ * @since 5.0.0
157
+ * @access public
158
+ */
159
+ public static function log( $type, $details ) {
160
+ global $wpdb;
161
+
162
+ $page_url = ZeroSpam\Core\Utilities::current_url();
163
+ $extension = substr( $page_url, strrpos( $page_url, '.' ) + 1 );
164
+ $ignore = array( 'map', 'js', 'css' );
165
+ if ( in_array( $extension, $ignore, true ) ) {
166
+ // Ignore assets.
167
+ return false;
168
+ }
169
+
170
+ $record = array(
171
+ 'user_ip' => ZeroSpam\Core\User::get_ip(),
172
+ 'log_type' => $type,
173
+ 'date_recorded' => current_time( 'mysql' ),
174
+ 'page_url' => $page_url,
175
+ 'submission_data' => wp_json_encode( $details ),
176
+ );
177
+
178
+ $record = apply_filters( 'zerospam_log_record', $record );
179
+
180
+ return $wpdb->insert( $wpdb->prefix . self::$tables['log'], $record );
181
+ }
182
+
183
+ /**
184
+ * Delete a record.
185
+ *
186
+ * @since 5.0.0
187
+ * @access public
188
+ */
189
+ public static function delete( $table, $key, $value ) {
190
+ global $wpdb;
191
+
192
+ $wpdb->delete(
193
+ $wpdb->prefix . self::$tables[ $table ],
194
+ array(
195
+ $key => $value,
196
+ )
197
+ );
198
+ }
199
+
200
+ /**
201
+ * Delete everything in a table.
202
+ *
203
+ * @since 5.0.0
204
+ * @access public
205
+ */
206
+ public static function delete_all( $table ) {
207
+ global $wpdb;
208
+
209
+ $wpdb->query( "TRUNCATE TABLE " . $wpdb->prefix . self::$tables[ $table ] );
210
+ }
211
+
212
+ /**
213
+ * Query the DB.
214
+ *
215
+ * @since 5.0.0
216
+ * @access public
217
+ */
218
+ public static function query( $table, $args = array() ) {
219
+ global $wpdb;
220
+
221
+ if ( ! array_key_exists( $table, self::$tables ) ) {
222
+ return false;
223
+ }
224
+
225
+ $sql = 'SELECT';
226
+
227
+ if ( ! empty( $args['select'] ) ) {
228
+ $sql .= implode( ', ', $args['select'] );
229
+ } else {
230
+ $sql .= ' * ';
231
+ }
232
+
233
+ $sql .= 'FROM ' . $wpdb->prefix . self::$tables[ $table ];
234
+
235
+ if ( ! empty( $args['where'] ) ) {
236
+ $sql .= ' WHERE ';
237
+
238
+ $where_stmt = '';
239
+ foreach ( $args['where'] as $key => $where ) {
240
+ if ( $where_stmt ) {
241
+ $where_stmt .= ' AND ';
242
+ }
243
+
244
+ $where_stmt .= $key;
245
+
246
+ if ( ! empty( $where['relation'] ) ) {
247
+ $where_stmt .= ' ' . $where['relation'] . ' ';
248
+ } else {
249
+ $where_stmt .= ' = ';
250
+ }
251
+
252
+ if ( is_numeric( $where['value'] ) ) {
253
+ $where_stmt .= $where['value'];
254
+ } else {
255
+ $where_stmt .= '"' . $where['value'] . '"';
256
+ }
257
+ }
258
+
259
+ $sql .= $where_stmt;
260
+ }
261
+
262
+ if ( ! empty( $args['orderby'] ) ) {
263
+ $sql .= ' ORDER BY ' . $args['orderby'];
264
+ }
265
+
266
+ if ( ! empty( $args['order'] ) ) {
267
+ $sql .= ' ' . $args['order'];
268
+ }
269
+
270
+ if ( ! empty( $args['limit'] ) ) {
271
+ $sql .= ' LIMIT ' . $args['limit'];
272
+ }
273
+
274
+ if ( ! empty( $args['offset'] ) ) {
275
+ $sql .= ' OFFSET ' . $args['offset'];
276
+ }
277
+
278
+ return $wpdb->get_results( $sql, ARRAY_A );
279
+ }
280
+ }
includes/class-plugin.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Main plugin class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam;
9
+
10
+ use ZeroSpam\Includes\DB;
11
+ use ZeroSpam\Core\Access;
12
+ use ZeroSpam\Core\User;
13
+ use ZeroSpam\Core\Admin\Admin;
14
+ use ZeroSpam\Modules\BotScout;
15
+ use ZeroSpam\Modules\StopForumSpam;
16
+ use ZeroSpam\Modules\ipstack;
17
+ use ZeroSpam\Modules\Google;
18
+ use ZeroSpam\Modules\Zero_Spam;
19
+ use ZeroSpam\Modules\Registration\Registration;
20
+ use ZeroSpam\Modules\Comments\Comments;
21
+ use ZeroSpam\Modules\ContactForm7\ContactForm7;
22
+
23
+ // Security Note: Blocks direct access to the plugin PHP files.
24
+ defined( 'ABSPATH' ) || die();
25
+
26
+ /**
27
+ * WordPress Zero Spam plugin.
28
+ *
29
+ * The main plugin handler class is responsible for initializing WordPress Zero
30
+ * Spam. The class registers and all the components required to run the plugin.
31
+ *
32
+ * @since 5.0.0
33
+ */
34
+ class Plugin {
35
+
36
+ /**
37
+ * Instance.
38
+ *
39
+ * Holds the plugin instance.
40
+ *
41
+ * @since 5.0.0
42
+ * @access public
43
+ * @static
44
+ *
45
+ * @var Plugin
46
+ */
47
+ public static $instance = null;
48
+
49
+ /**
50
+ * Plugin constructor.
51
+ *
52
+ * Initializing WordPress Zero Spam plugin.
53
+ *
54
+ * @since 5.0.0
55
+ * @access private
56
+ */
57
+ private function __construct() {
58
+ $this->register_autoloader();
59
+
60
+ add_action( 'init', array( $this, 'init' ), 0 );
61
+ add_filter( 'zerospam_types', array( $this, 'types' ), 10, 1 );
62
+ }
63
+
64
+ /**
65
+ * Register autoloader.
66
+ *
67
+ * WordPress Zero Spam autoloader loads all the classes needed to run the
68
+ * plugin.
69
+ *
70
+ * @since 5.0.0
71
+ * @access private
72
+ */
73
+ private function register_autoloader() {
74
+ require_once ZEROSPAM_PATH . 'includes/class-autoloader.php';
75
+
76
+ Autoloader::run();
77
+ }
78
+
79
+ /**
80
+ * Instance.
81
+ *
82
+ * Ensures only one instance of the plugin class is loaded or can be loaded.
83
+ *
84
+ * @since 1.0.0
85
+ * @access public
86
+ * @static
87
+ *
88
+ * @return Plugin An instance of the class.
89
+ */
90
+ public static function instance() {
91
+ if ( is_null( self::$instance ) ) {
92
+ self::$instance = new self();
93
+
94
+ /**
95
+ * Elementor loaded.
96
+ *
97
+ * Fires when Elementor was fully loaded and instantiated.
98
+ *
99
+ * @since 1.0.0
100
+ */
101
+ do_action( 'zerospam_loaded' );
102
+ }
103
+
104
+ return self::$instance;
105
+ }
106
+
107
+ /**
108
+ * Init.
109
+ *
110
+ * Initialize WordPress Zero Spam Plugin. Checks if the current user should be
111
+ * blocked.
112
+ *
113
+ * @since 5.0.0
114
+ * @access public
115
+ */
116
+ public function init() {
117
+ $this->init_components();
118
+
119
+ /**
120
+ * WordPress Zero Spam init.
121
+ *
122
+ * Fires on WordPress Zero Spam init, after WordPress Zero Spam has finished
123
+ * loading but before any headers are sent.
124
+ *
125
+ * @since 5.0.0
126
+ */
127
+ do_action( 'zerospam_init' );
128
+ }
129
+
130
+ /**
131
+ * Init components.
132
+ *
133
+ * Initialize WordPress Zero Spam components. Register actions, initialize all
134
+ * the components that run WordPress Zero Spam, and if in admin page
135
+ * initialize admin components.
136
+ *
137
+ * @since 5.0.0
138
+ * @access private
139
+ */
140
+ private function init_components() {
141
+ new DB();
142
+ new Registration();
143
+ new Comments();
144
+
145
+ include_once ABSPATH . 'wp-admin/includes/plugin.php';
146
+ if ( is_plugin_active( 'contact-form-7/wp-contact-form-7.php' ) ) {
147
+ new ContactForm7();
148
+ }
149
+
150
+ //= new BotScout();
151
+ new StopForumSpam();
152
+ new ipstack();
153
+ new Zero_Spam();
154
+
155
+ if (
156
+ ! is_admin() &&
157
+ is_main_query()
158
+ ) {
159
+ new Access();
160
+ }
161
+
162
+ if ( is_admin() ) {
163
+ new Google();
164
+ new Admin();
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Add to the types array.
170
+ *
171
+ * @since 5.0.0
172
+ * @access public
173
+ */
174
+ public function types( $types ) {
175
+ $types['blocked'] = __( 'Blocked', 'zerospam' );
176
+
177
+ return $types;
178
+ }
179
+ }
180
+
181
+ Plugin::instance();
includes/templates/admin-block-ip.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Block IP.
4
+ *
5
+ * @package ZeroSpam
6
+ * @since 5.0.0
7
+ */
8
+ ?>
9
+
10
+ <form method="post" action="<?php echo esc_url( admin_url( 'admin.php' ) ); ?>">
11
+ <?php wp_nonce_field( 'zerospam', 'zerospam' ); ?>
12
+ <input type="hidden" name="action" value="add_blocked_ip" />
13
+ <input type="hidden" name="redirect" value="<?php echo esc_url( ZeroSpam\Core\Utilities::current_url() ); ?>" />
14
+
15
+ <label for="blocked-ip">
16
+ <?php _e( 'IP Address', 'zerospam' ); ?>
17
+ <input
18
+ type="text"
19
+ name="blocked_ip"
20
+ value="<?php if( ! empty( $_REQUEST['ip'] ) ) : echo esc_attr( $_REQUEST['ip'] ); endif; ?>"
21
+ placeholder="e.g. xxx.xxx.x.x"
22
+ />
23
+ </label>
24
+
25
+ <label for="blocked-type"><?php _e( 'Type', 'zerospam' ); ?>
26
+ <select id="blocked-type" name="blocked_type">
27
+ <option value="temporary"><?php _e( 'Temporary', 'zerospam' ); ?></option>
28
+ <option value="permanent"><?php _e( 'Permanent', 'zerospam' ); ?></option>
29
+ </select>
30
+ </label>
31
+
32
+ <label for="blocked-reason">
33
+ <?php _e( 'Reason', 'zerospam' ); ?>
34
+ <input type="text" id="blocked-reason" name="blocked_reason" value="" placeholder="<?php _e( 'e.g. Spammed form', 'zerospam' ); ?>" />
35
+ </label>
36
+
37
+ <label for="blocked-start-date">
38
+ <?php esc_html_e( 'Start Date', 'zerospam' ); ?>
39
+ <input
40
+ type="datetime-local"
41
+ id="blocked-start-date"
42
+ name="blocked_start_date"
43
+ value=""
44
+ placeholder="<?php echo esc_attr( __( 'Optional', 'zerospam' ) ); ?>"
45
+ />
46
+ </label>
47
+
48
+ <label for="blocked-end-date">
49
+ <?php _e( 'End Date', 'zerospam' ); ?>
50
+ <input type="datetime-local" id="blocked-end-date" name="blocked_end_date" value="" placeholder="<?php _e( 'Optional', 'zerospam' ); ?>" />
51
+ </label>
52
+
53
+ <input type="submit" class="button button-primary" value="<?php _e( 'Add/Update Blocked IP', 'zerospam' ); ?>" />
54
+
55
+ </form>
includes/templates/admin-callout.php ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Callout.
4
+ *
5
+ * @package ZeroSpam
6
+ * @since 5.0.0
7
+ */
8
+ ?>
9
+
10
+ <div class="zerospam-callout">
11
+ <div class="zerospam-callout-col">
12
+ <h2>
13
+ <?php
14
+ echo sprintf(
15
+ wp_kses(
16
+ __( 'Help support the <a href="%s" target="_blank" rel="noopener noreferrer">WordPress Zero Spam</a> plugin.', 'zerospam' ),
17
+ array(
18
+ 'a' => array(
19
+ 'target' => array(),
20
+ 'href' => array(),
21
+ 'rel' => array(),
22
+ ),
23
+ )
24
+ ),
25
+ esc_url( 'https://benmarshall.me/wordpress-zero-spam/?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=donation' )
26
+ );
27
+ ?>
28
+ </h2>
29
+ <p>
30
+ <?php
31
+ echo sprintf(
32
+ wp_kses(
33
+ __( 'Support the continued development of the WPZS by <a href="%s" target="_blank" rel="noopener noreferrer">donating today</a>. Donation goes towards the time it takes to develop new features &amp; updates, but also helps provide pro bono work for nonprofits.', 'zerospam' ),
34
+ array(
35
+ 'a' => array(
36
+ 'target' => array(),
37
+ 'href' => array(),
38
+ 'rel' => array(),
39
+ ),
40
+ )
41
+ ),
42
+ esc_url( 'https://benmarshall.me/donate/?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=donation' )
43
+ );
44
+ ?>
45
+ </p>
46
+ <p>
47
+ <?php
48
+ echo sprintf(
49
+ wp_kses(
50
+ __( '<strong>Integrate Zero Spam in any application</strong> with the <a href="%s" target="_blank" rel="noopener noreferrer">Zero Spam Blacklist API</a>.', 'zerospam' ),
51
+ array(
52
+ 'strong' => array(),
53
+ 'a' => array(
54
+ 'target' => array(),
55
+ 'href' => array(),
56
+ 'rel' => array(),
57
+ ),
58
+ )
59
+ ),
60
+ esc_url( 'https://zerospam.org/spam-blacklist-api/' )
61
+ );
62
+ ?>
63
+ </p>
64
+ </div>
65
+ <div class="zerospam-callout-col zerospam-callout-actions">
66
+ <ul>
67
+ <li><a href="https://github.com/bmarshall511/wordpress-zero-spam/issues" target="_blank"><?php _e( 'Submit Bug/Feature Request', 'zerospam' ); ?></a></li>
68
+ <li><a href="https://twitter.com/ZeroSpamOrg" target="_blank"><?php _e( 'Follow us on Twitter', 'zerospam' ); ?></a></li>
69
+ <li><a href="https://www.facebook.com/zerospamorg/" target="_blank"><?php _e( 'Like us on Facebook', 'zerospam' ); ?></a></li>
70
+ <li><a href="https://benmarshall.me/donate?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=admin" target="_blank"><?php _e( 'Show your Support &mdash; Donate', 'zerospam' ); ?></a></li>
71
+ </ul>
72
+ </div>
73
+ </div>
includes/templates/admin-modal-details.php ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Modal details.
4
+ *
5
+ * @package ZeroSpam
6
+ * @since 5.0.0
7
+ */
8
+ ?>
9
+
10
+ <div class="zerospam-modal-details">
11
+ <div class="zerospam-modal-title">
12
+ <h3>ID #<?php echo $item['log_id']; ?></h3>
13
+ </div>
14
+ <div class="zerospam-modal-subtitle">
15
+ <?php echo gmdate( 'M j, Y g:ia' , strtotime( $item[ 'date_recorded' ] ) ); ?>
16
+ </div>
17
+
18
+ <ul class="zerospam-modal-list">
19
+ <li>
20
+ <strong><?php echo __( 'IP Address', 'zerospam' ); ?></strong>
21
+ <span><?php echo '<a href="https://www.zerospam.org/ip-lookup/' . urlencode( $item['user_ip'] ) .'" target="_blank" rel="noopener noreferrer">' . $item['user_ip'] . '</a>'; ?></span>
22
+ </li>
23
+ <li>
24
+ <strong><?php echo __( 'Type', 'zerospam' ); ?></strong>
25
+ <span><?php echo $item['log_type']; ?></span>
26
+ </li>
27
+ </ul>
28
+
29
+ <button class="button action zerospam-block-trigger" data-id="<?php echo esc_attr( $item['log_id'] ); ?>"><?php _e( 'Block IP', 'zerospam' ); ?></button>
30
+
31
+ <?php
32
+ if ( ! empty( $item['latitude'] ) && ! empty( $item['longitude'] ) ) {
33
+ ?>
34
+ <h4 class="zerospam-modal-headline"><?php echo __( 'Location', 'zerospam' ); ?></h4>
35
+ <?php
36
+ $coordinates = $item['latitude'] . ',' . $item['longitude'];
37
+ do_action( 'zerospam_google_map', $coordinates );
38
+ ?>
39
+ <ul class="zerospam-modal-list">
40
+ <?php if ( ! empty( $item['country_name'] ) || ! empty( $item['country'] ) ) : ?>
41
+ <li>
42
+ <strong><?php echo __( 'Country', 'zerospam' ); ?></strong>
43
+ <span>
44
+ <?php if ( ! empty( $item['country_name'] ) ) : ?>
45
+ <?php echo $item['country_name']; ?>
46
+ <?php endif; ?>
47
+ <?php if ( ! empty( $item['country'] ) ) : ?>
48
+ (<?php echo $item['country']; ?>)
49
+ <?php endif; ?>
50
+ </span>
51
+ </li>
52
+ <?php endif; ?>
53
+ <?php if ( ! empty( $item['region'] ) || ! empty( $item['region_name'] ) ) : ?>
54
+ <li>
55
+ <strong><?php echo __( 'Region', 'zerospam' ); ?></strong>
56
+ <span>
57
+ <?php if ( ! empty( $item['region_name'] ) ) : ?>
58
+ <?php echo $item['region_name']; ?>
59
+ <?php endif; ?>
60
+ <?php if ( ! empty( $item['region'] ) ) : ?>
61
+ (<?php echo $item['region']; ?>)
62
+ <?php endif; ?>
63
+ </span>
64
+ </li>
65
+ <?php endif; ?>
66
+ <?php if ( ! empty( $item['city'] ) ) : ?>
67
+ <li>
68
+ <strong><?php echo __( 'City', 'zerospam' ); ?></strong>
69
+ <span><?php echo $item['city']; ?>
70
+ </span>
71
+ </li>
72
+ <?php endif; ?>
73
+ <?php if ( ! empty( $item['zip'] ) ) : ?>
74
+ <li>
75
+ <strong><?php echo __( 'Zip/Postal Code', 'zerospam' ); ?></strong>
76
+ <span><?php echo $item['zip']; ?>
77
+ </span>
78
+ </li>
79
+ <?php endif; ?>
80
+ <?php if ( ! empty( $item['latitude'] ) || ! empty( $item['region_name'] ) ) : ?>
81
+ <li>
82
+ <strong><?php echo __( 'Coordinates', 'zerospam' ); ?></strong>
83
+ <span>
84
+ <?php if ( ! empty( $item['latitude'] ) ) : ?>
85
+ <?php echo $item['latitude']; ?>&deg;,
86
+ <?php endif; ?>
87
+ <?php if ( ! empty( $item['longitude'] ) ) : ?>
88
+ <?php echo $item['longitude']; ?>&deg;
89
+ <?php endif; ?>
90
+ </span>
91
+ </li>
92
+ <?php endif; ?>
93
+ </ul>
94
+ <?php
95
+ }
96
+ ?>
97
+
98
+ <h4 class="zerospam-modal-headline"><?php echo __( 'Additional Details', 'zerospam' ); ?></h4>
99
+ <?php
100
+ if ( ! empty( $item['submission_data'] ) ) :
101
+ $submission_data = json_decode( $item['submission_data'], true );
102
+ echo '<ul class="zerospam-modal-list">';
103
+ foreach ( $submission_data as $key => $value ) :
104
+ ?>
105
+ <li><strong><?php echo $key; ?></strong> <span><?php echo $value; ?></span></li>
106
+ <?php
107
+ endforeach;
108
+ echo '</ul>';
109
+ endif;
110
+ ?>
111
+ </div>
integrations/buddypress/buddypress.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- /**
3
- * Handles checking submitted BuddyPress registrations
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.1.0
7
- */
8
-
9
- /**
10
- * Add the 'bp_registration' spam type
11
- */
12
- add_filter( 'wpzerospam_types', function( $types ) {
13
- $types = array_merge( $types, [ 'bp_registration' => __( 'BuddyPress Registration', 'zero-spam' ) ] );
14
- return $types;
15
- });
16
-
17
- /**
18
- * Validation for BuddyPress registrations
19
- */
20
- if ( ! function_exists( 'wpzerospam_bp_signup_validate' ) ) {
21
- function wpzerospam_bp_signup_validate() {
22
- if ( is_user_logged_in() || wpzerospam_key_check() ) {
23
- return;
24
- }
25
-
26
- do_action( 'wpzerospam_bp_registration_spam' );
27
-
28
- wpzerospam_spam_detected( 'bp_registration' );
29
- }
30
- }
31
- add_action( 'bp_signup_validate', 'wpzerospam_bp_signup_validate' );
32
-
33
- /**
34
- * Enqueue the BuddyPress form JS
35
- */
36
- if ( ! function_exists( 'wpzerospam_buddy_press' ) ) {
37
- function wpzerospam_buddy_press() {
38
- wp_enqueue_script(
39
- 'wpzerospam-integration-buddy-press',
40
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
41
- 'integrations/buddypress/js/buddypress.js',
42
- [ 'wpzerospam' ],
43
- WORDPRESS_ZERO_SPAM_VERSION,
44
- true
45
- );
46
- }
47
- }
48
- add_action( 'bp_before_register_page', 'wpzerospam_buddy_press' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
integrations/buddypress/js/buddypress.js DELETED
@@ -1,4 +0,0 @@
1
- // Initialize WPZS on BuddyPress registration forms.
2
- jQuery(function() {
3
- jQuery("#buddypress #signup_form").WordPressZeroSpam();
4
- });
 
 
 
 
integrations/comments/comments.php DELETED
@@ -1,379 +0,0 @@
1
- <?php
2
- /**
3
- * Comment form spam detection
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.3.7
7
- */
8
-
9
- /**
10
- * Adds admin settings for comment submission protection.
11
- *
12
- * @since 4.9.9
13
- *
14
- * @return void
15
- */
16
- if ( ! function_exists( 'wpzerospam_comments_admin_fields' ) ) {
17
- function wpzerospam_comments_admin_fields() {
18
- // Option to strips links in comments
19
- add_settings_field( 'strip_comment_links', __( 'Strip Comment Links', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_onsite', [
20
- 'label_for' => 'strip_comment_links',
21
- 'type' => 'checkbox',
22
- 'multi' => false,
23
- 'desc' => __( 'Spambots commonly post spam links in comments. Enable this option to strip links from comments.', 'zero-spam' ),
24
- 'options' => [
25
- 'enabled' => __( 'Enabled', 'zero-spam' )
26
- ]
27
- ]);
28
-
29
- // Option to remove author links
30
- add_settings_field( 'strip_comment_author_links', __( 'Strip Comment Author Links', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_onsite', [
31
- 'label_for' => 'strip_comment_author_links',
32
- 'type' => 'checkbox',
33
- 'multi' => false,
34
- 'desc' => __( 'Spammers are well-known at injecting malicious links in the comment author website field, this option disables it.', 'zero-spam' ),
35
- 'options' => [
36
- 'enabled' => __( 'Enabled', 'zero-spam' )
37
- ]
38
- ]);
39
-
40
- // Add option to enable/disable comment form submission protection.
41
- add_settings_field( 'verify_comments', __( 'Detect Spam/Malicious Comments', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
42
- 'label_for' => 'verify_comments',
43
- 'type' => 'checkbox',
44
- 'multi' => false,
45
- 'desc' => __( 'Monitors comments for malicious links and automated spambot submissions.', 'zero-spam' ),
46
- 'options' => [
47
- 'enabled' => __( 'Enabled', 'zero-spam' )
48
- ]
49
- ]);
50
- }
51
- }
52
- add_action( 'wpzerospam_admin_options', 'wpzerospam_comments_admin_fields' );
53
-
54
- /**
55
- * Add validation to the comment form submission protection admin fields.
56
- *
57
- * @since 4.9.9
58
- *
59
- * @param array $fields Array on available admin fields.
60
- */
61
- if ( ! function_exists( 'wpzerospam_comments_admin_validation' ) ) {
62
- function wpzerospam_comments_admin_validation( $fields ) {
63
- if ( empty( $fields['verify_comments'] ) ) { $fields['verify_comments'] = 'disabled'; }
64
- if ( empty( $fields['strip_comment_links'] ) ) { $fields['strip_comment_links'] = 'disabled'; }
65
- if ( empty( $fields['strip_comment_author_links'] ) ) { $fields['strip_comment_author_links'] = 'disabled'; }
66
-
67
- return $fields;
68
- }
69
- }
70
- add_filter( 'wpzerospam_admin_validation', 'wpzerospam_comments_admin_validation' );
71
-
72
- if ( ! function_exists( 'wpzerospam_comments_admin_fields_default' ) ) {
73
- function wpzerospam_comments_admin_fields_default( $defaults ) {
74
- if ( empty( $defaults['verify_comments'] ) ) { $defaults['verify_comments'] = 'enabled'; }
75
- if ( empty( $defaults['strip_comment_links'] ) ) { $defaults['strip_comment_links'] = 'disabled'; }
76
- if ( empty( $defaults['strip_comment_author_links'] ) ) { $defaults['strip_comment_author_links'] = 'disabled'; }
77
-
78
- return $defaults;
79
- }
80
- }
81
- add_filter( 'wpzerospam_admin_options_defaults', 'wpzerospam_comments_admin_fields_default' );
82
-
83
- if ( ! function_exists( 'wpzerospam_comments_admin_submission_data_item' ) ) {
84
- function wpzerospam_comments_admin_submission_data_item( $key, $value ) {
85
- switch( $key ) {
86
- case 'comment_post_ID':
87
- $post = get_post( $value );
88
- if ( $post ) {
89
- $item_value = '<a href="' . get_the_permalink( $value ) . '">' . get_the_title( $value ) . '</a>';
90
- } else {
91
- $item_value = 'N/A';
92
- }
93
- echo wpzerospam_admin_details_item( __( 'Comment Post', 'zero-spam' ), $item_value );
94
- break;
95
- case 'comment_author':
96
- echo wpzerospam_admin_details_item( __( 'Author', 'zero-spam' ), $value );
97
- break;
98
- case 'comment_author_email':
99
- echo wpzerospam_admin_details_item( __( 'Email', 'zero-spam' ), $value );
100
- break;
101
- case 'comment_author_url':
102
- echo wpzerospam_admin_details_item( __( 'Website', 'zero-spam' ), $value );
103
- break;
104
- case 'comment_content':
105
- echo wpzerospam_admin_details_item( __( 'Comment', 'zero-spam' ), sanitize_text_field( $value ) );
106
- break;
107
- case 'comment_type':
108
- echo wpzerospam_admin_details_item( __( 'Comment Type', 'zero-spam' ), $value );
109
- break;
110
- case 'comment_parent':
111
- echo wpzerospam_admin_details_item( __( 'Comment Parent ID', 'zero-spam' ), '<a href="' . get_comment_link( $value ) . '">' . $value . '</a>' );
112
- break;
113
- case 'comment_as_submitted':
114
- foreach( $value as $k => $v ):
115
- if ( ! $v ) { continue; }
116
- switch( $k ):
117
- case 'comment_author':
118
- if ( empty( $author_shown ) ) {
119
- echo wpzerospam_admin_details_item( __( 'Author', 'zero-spam' ), $v );
120
- }
121
- break;
122
- case 'comment_author_email':
123
- if ( empty( $author_email ) ) {
124
- echo wpzerospam_admin_details_item( __( 'Email', 'zero-spam' ), $v );
125
- }
126
- break;
127
- case 'comment_author_url':
128
- if ( empty( $author_url ) ) {
129
- echo wpzerospam_admin_details_item( __( 'Website', 'zero-spam' ), $v );
130
- }
131
- break;
132
- case 'comment_content':
133
- echo wpzerospam_admin_details_item( __( 'Comment', 'zero-spam' ), sanitize_text_field( $v ) );
134
- break;
135
- case 'user_ip':
136
- echo wpzerospam_admin_details_item( __( 'User IP', 'zero-spam' ), '<a href="https://zerospam.org/ip-lookup/' . urlencode( $v ) .'" target="_blank" rel="noopener noreferrer">' . $v . '</a>' );
137
- break;
138
- case 'user_agent':
139
- echo wpzerospam_admin_details_item( __( 'User Agent', 'zero-spam' ), $v );
140
- break;
141
- case 'blog':
142
- echo wpzerospam_admin_details_item( __( 'Site', 'zero-spam' ), $v );
143
- break;
144
- case 'blog_lang':
145
- echo wpzerospam_admin_details_item( __( 'Site Language', 'zero-spam' ), $v );
146
- break;
147
- case 'blog_charset':
148
- echo wpzerospam_admin_details_item( __( 'Site Charset', 'zero-spam' ), $v );
149
- break;
150
- case 'permalink':
151
- echo wpzerospam_admin_details_item( __( 'Permalink', 'zero-spam' ), '<a href="' . $v . '" target="_blank">' . $v . '</a>' );
152
- break;
153
- default:
154
- echo wpzerospam_admin_details_item( $k, $v );
155
- endswitch;
156
- endforeach;
157
- break;
158
- case 'akismet_result':
159
- echo wpzerospam_admin_details_item( __( 'Akismet Result', 'zero-spam' ), $value );
160
- break;
161
- case 'akismet_pro_tip':
162
- echo wpzerospam_admin_details_item( __( 'Akismet Pro Tip', 'zero-spam' ), $value );
163
- break;
164
- }
165
- }
166
- }
167
- add_action( 'wpzerospam_admin_submission_data_items', 'wpzerospam_comments_admin_submission_data_item', 10, 2 );
168
-
169
- if ( ! function_exists( 'wpzerospam_comments_defined_submission_data' ) ) {
170
- function wpzerospam_comments_defined_submission_data( $submission_data_keys ) {
171
- $submission_data_keys[] = 'comment_post_ID';
172
- $submission_data_keys[] = 'comment_author';
173
- $submission_data_keys[] = 'comment_author_email';
174
- $submission_data_keys[] = 'comment_author_url';
175
- $submission_data_keys[] = 'comment_content';
176
- $submission_data_keys[] = 'comment_type';
177
- $submission_data_keys[] = 'comment_parent';
178
- $submission_data_keys[] = 'comment_as_submitted';
179
- $submission_data_keys[] = 'akismet_result';
180
- $submission_data_keys[] = 'akismet_pro_tip';
181
-
182
- return $submission_data_keys;
183
- }
184
- }
185
- add_filter( 'wpzerospam_defined_submission_data', 'wpzerospam_comments_defined_submission_data', 10, 1 );
186
-
187
- /**
188
- * Runs the comment form spam detections.
189
- *
190
- * Runs all action & filter hooks needed for monitoring comment submissions for
191
- * spam (when enabled via the 'Detect Comment Spam' option).
192
- *
193
- * @since 4.9.9
194
- *
195
- * @return void
196
- */
197
- if ( ! function_exists( 'wpzerospam_comments_after_setup_theme' ) ) {
198
- function wpzerospam_comments_after_setup_theme() {
199
- $options = wpzerospam_options();
200
-
201
- // Add the 'comment' spam type.
202
- add_filter( 'wpzerospam_types', 'wpzerospam_comments_types' );
203
-
204
- // Determines is author links should be stripped.
205
- if ( 'enabled' == $options['strip_comment_author_links'] ) {
206
- add_filter( 'get_comment_author_link', 'wpzerospam_remove_comment_author_link', 10, 3 );
207
- add_filter( 'get_comment_author_url', 'wpzerospam_remove_author_url' );
208
- add_filter( 'comment_form_default_fields', 'wpzerospam_remove_author_url_field' );
209
- }
210
-
211
- // Determines if comment links should be stripped.
212
- if ( 'enabled' == $options['strip_comment_links'] ) {
213
- remove_filter( 'comment_text', 'make_clickable', 9 );
214
- add_filter( 'comment_text', 'wpzerospam_strip_comment_links_display', 10, 1);
215
- add_filter( 'comment_text_rss', 'wpzerospam_strip_comment_links_display', 10, 1);
216
- add_filter( 'comment_excerpt', 'wpzerospam_strip_comment_links_display', 10, 1);
217
- add_filter( 'preprocess_comment', 'wpzerospam_strip_comment_links', 10, 1 );
218
- }
219
-
220
- // Check if detecting comments is enabled & user is unauthenticated.
221
- if ( 'enabled' != $options['verify_comments'] || is_user_logged_in() ) { return false; }
222
-
223
- // Add the 'honeypot' field to the comment form.
224
- add_filter( 'comment_form_defaults', 'wpzerospam_comments_form_defaults' );
225
-
226
- // Preprocess comment submissions.
227
- add_action( 'preprocess_comment', 'wpzerospam_comments_preprocess' );
228
-
229
- // Register & enqueue needed CSS & JS files, only when the comment form is on the page.
230
- add_action( 'comment_form', 'wpzerospam_comments_enqueue_scripts' );
231
- }
232
- }
233
- add_action( 'after_setup_theme', 'wpzerospam_comments_after_setup_theme' );
234
-
235
- /**
236
- * Strips links from comment submissions.
237
- */
238
- if ( ! function_exists( 'wpzerospam_strip_comment_links' ) ) {
239
- function wpzerospam_strip_comment_links( $comment ) {
240
- global $allowedtags;
241
-
242
- $tags = $allowedtags;
243
- unset( $tags['a'] );
244
- $content = addslashes( wp_kses( stripslashes( $comment ), $tags) );
245
-
246
- return $comment;
247
- }
248
- }
249
-
250
- /**
251
- * Strips links from comments when displayed.
252
- */
253
- if ( ! function_exists( 'wpzerospam_strip_comment_links_display' ) ) {
254
- function wpzerospam_strip_comment_links_display( $comment ) {
255
- global $allowedtags;
256
-
257
- $tags = $allowedtags;
258
- unset( $tags['a'] );
259
- $content = addslashes( wp_kses( stripslashes( $comment ), $tags) );
260
-
261
- return $comment;
262
- }
263
- }
264
-
265
- /**
266
- * Removes the comment author link.
267
- */
268
- if ( ! function_exists( 'wpzerospam_remove_comment_author_link' ) ) {
269
- function wpzerospam_remove_comment_author_link( $return, $author, $comment_ID ) {
270
- return $author;
271
- }
272
- }
273
-
274
- /**
275
- * Removes the comment author url from display.
276
- */
277
- if ( ! function_exists( 'wpzerospam_remove_author_url' ) ) {
278
- function wpzerospam_remove_author_url() {
279
- return false;
280
- }
281
- }
282
-
283
- /**
284
- * Removed the comment author url field.
285
- */
286
- if ( ! function_exists( 'wpzerospam_remove_author_url_field' ) ) {
287
- function wpzerospam_remove_author_url_field( $fields ) {
288
- if ( isset( $fields['url'] ) ) {
289
- unset( $fields['url'] );
290
- }
291
-
292
- return $fields;
293
- }
294
- }
295
-
296
- /**
297
- * Adds the 'comment' spam type.
298
- *
299
- * @param array An array of the current spam types.
300
- * @return array The resulting current spam types.
301
- */
302
- if ( ! function_exists( 'wpzerospam_comments_types' ) ) {
303
- function wpzerospam_comments_types( $types ) {
304
- $types = array_merge( $types, [ 'comment' => __( 'Comment', 'zero-spam' ) ] );
305
-
306
- return $types;
307
- }
308
- }
309
-
310
- /**
311
- * Add a 'honeypot' field to the comment form.
312
- *
313
- * @since 4.9.9
314
- *
315
- * @link https://developer.wordpress.org/reference/hooks/comment_form_defaults/
316
- *
317
- * @return array The default comment form arguments.
318
- */
319
- if ( ! function_exists( 'wpzerospam_comments_form_defaults' ) ) {
320
- function wpzerospam_comments_form_defaults( $defaults ) {
321
- $defaults['fields']['wpzerospam_hp'] = wpzerospam_honeypot_field();
322
-
323
- return $defaults;
324
- }
325
- }
326
-
327
- /**
328
- * Preprocess comment fields.
329
- *
330
- * @since 4.3.7
331
- *
332
- * @link https://codex.wordpress.org/Plugin_API/Filter_Reference/preprocess_comment
333
- *
334
- * @return array The $commentdata array which may have been manipulated during the execution of the handler.
335
- */
336
- if ( ! function_exists( 'wpzerospam_comments_preprocess' ) ) {
337
- function wpzerospam_comments_preprocess( $commentdata ) {
338
- $options = wpzerospam_options();
339
- $honeypot = wpzerospam_get_honeypot();
340
-
341
- if (
342
- // First, check the 'honeypot' field.
343
- ( ! isset( $_REQUEST[ $honeypot ] ) || $_REQUEST[ $honeypot ] ) ||
344
- // Next, check the 'wpzerospam_key' field.
345
- ( empty( $_REQUEST['wpzerospam_key'] ) || wpzerospam_get_key() != $_REQUEST['wpzerospam_key'] )
346
- ) {
347
- // Spam comment selected.
348
- do_action( 'wpzerospam_comment_spam', $commentdata );
349
- wpzerospam_spam_detected( 'comment', $commentdata );
350
-
351
- return false;
352
- }
353
-
354
- return $commentdata;
355
- }
356
- }
357
-
358
- /**
359
- * Register & enqueue CSS & JS files for comment spam detection.
360
- *
361
- * @since 4.9.9
362
- *
363
- * @link https://developer.wordpress.org/reference/hooks/wp_enqueue_scripts/
364
- *
365
- * @return void
366
- */
367
- if ( ! function_exists( 'wpzerospam_comments_enqueue_scripts' ) ) {
368
- function wpzerospam_comments_enqueue_scripts() {
369
- // Load the 'wpzerospam_key' form field JS
370
- wp_enqueue_script(
371
- 'wpzerospam-integration-comments',
372
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
373
- 'integrations/comments/js/comments.js',
374
- [ 'wpzerospam' ],
375
- WORDPRESS_ZERO_SPAM_VERSION,
376
- true
377
- );
378
- }
379
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
integrations/comments/js/comments.js DELETED
@@ -1,4 +0,0 @@
1
- // Initialize WPZS on comment forms.
2
- jQuery(function() {
3
- jQuery("#commentform, #ast-commentform").WordPressZeroSpam();
4
- });
 
 
 
 
integrations/contact-form-7/contact-form-7.php DELETED
@@ -1,198 +0,0 @@
1
- <?php
2
- /**
3
- * Handles checking submitted Contact Form 7 forms for spam
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.3.7
7
- */
8
-
9
- /**
10
- * Adds admin settings for CF7 protection.
11
- *
12
- * @since 4.9.12
13
- *
14
- * @return void
15
- */
16
- if ( ! function_exists( 'wpzerospam_cf7_admin_fields' ) ) {
17
- function wpzerospam_cf7_admin_fields() {
18
- if ( is_plugin_active( 'contact-form-7/wp-contact-form-7.php' ) ) {
19
- add_settings_field( 'verify_cf7', __( 'Detect Spam/Malicious CF7 Submissions', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
20
- 'label_for' => 'verify_cf7',
21
- 'type' => 'checkbox',
22
- 'multi' => false,
23
- 'desc' => __( 'Monitors CF7 for malicious or automated spambot submissions.', 'zero-spam' ),
24
- 'options' => [
25
- 'enabled' => __( 'Enabled', 'zero-spam' )
26
- ]
27
- ]);
28
- }
29
- }
30
- }
31
- add_action( 'wpzerospam_admin_options', 'wpzerospam_cf7_admin_fields' );
32
-
33
- /**
34
- * Add validation to the CF7 form protection admin fields.
35
- *
36
- * @since 4.9.12
37
- *
38
- * @param array $fields Array on available admin fields.
39
- */
40
- if ( ! function_exists( 'wpzerospam_cf7_admin_validation' ) ) {
41
- function wpzerospam_cf7_admin_validation( $fields ) {
42
- if ( empty( $fields['verify_cf7'] ) ) { $fields['verify_cf7'] = 'disabled'; }
43
-
44
- return $fields;
45
- }
46
- }
47
- add_filter( 'wpzerospam_admin_validation', 'wpzerospam_cf7_admin_validation' );
48
-
49
- if ( ! function_exists( 'wpzerospam_cf7_admin_fields_default' ) ) {
50
- function wpzerospam_cf7_admin_fields_default( $defaults ) {
51
- if ( empty( $defaults['verify_cf7'] ) ) { $defaults['verify_cf7'] = 'enabled'; }
52
-
53
- return $defaults;
54
- }
55
- }
56
- add_filter( 'wpzerospam_admin_option_defaults', 'wpzerospam_cf7_admin_fields_default' );
57
-
58
- if ( ! function_exists( 'zero_spam_wpcf7_admin_submission_data_item' ) ) {
59
- function zero_spam_wpcf7_admin_submission_data_item( $key, $value ) {
60
- switch( $key ) {
61
- case '_wpcf7':
62
- echo wpzerospam_admin_details_item( __( 'CF7 ID', 'zero-spam' ), $value );
63
- break;
64
- case '_wpcf7_version':
65
- echo wpzerospam_admin_details_item( __( 'CF7 Version', 'zero-spam' ), $value );
66
- break;
67
- case '_wpcf7_locale':
68
- echo wpzerospam_admin_details_item( __( 'CF7 Language', 'zero-spam' ), json_encode( $value ) );
69
- break;
70
- case '_wpcf7_container_post':
71
- echo wpzerospam_admin_details_item( __( 'CF7 Referrer Post ID', 'zero-spam' ), json_encode( $value ) );
72
- break;
73
- case '_wpcf7_unit_tag':
74
- echo wpzerospam_admin_details_item( __( 'CF7 Unit Tag', 'zero-spam' ), json_encode( $value ) );
75
- break;
76
- }
77
- }
78
- }
79
- add_action( 'wpzerospam_admin_submission_data_items', 'zero_spam_wpcf7_admin_submission_data_item', 10, 2 );
80
-
81
- if ( ! function_exists( 'wpzerospam_wpcf7_defined_submission_data' ) ) {
82
- function wpzerospam_wpcf7_defined_submission_data( $submission_data_keys ) {
83
- $submission_data_keys[] = '_wpcf7';
84
- $submission_data_keys[] = '_wpcf7_version';
85
- $submission_data_keys[] = '_wpcf7_locale';
86
- $submission_data_keys[] = '_wpcf7_container_post';
87
- $submission_data_keys[] = '_wpcf7_unit_tag';
88
-
89
- return $submission_data_keys;
90
- }
91
- }
92
- add_filter( 'wpzerospam_defined_submission_data', 'wpzerospam_wpcf7_defined_submission_data', 10, 1 );
93
-
94
- /*
95
- * Runs the CF7 form spam detections.
96
- *
97
- * Runs all action & filter hooks needed for monitoring CF7 for
98
- * spam (when enabled via the 'Detect Spam/Malicious CF7 Submissions' option).
99
- *
100
- * @since 4.9.12
101
- *
102
- * @return void
103
- */
104
- if ( ! function_exists( 'wpzerospam_cf7_after_setup_theme' ) ) {
105
- function wpzerospam_cf7_after_setup_theme() {
106
- // Check if site registrations are enabled
107
- $options = wpzerospam_options();
108
-
109
- // Add the 'cf7' spam type.
110
- add_filter( 'wpzerospam_types', 'wpzerospam_wpcf7_type' );
111
-
112
- // Check if detecting registration spam is enabled & user is unauthenticated.
113
- if ( 'enabled' != $options['verify_cf7'] || is_user_logged_in() ) { return false; }
114
-
115
- // Add the 'honeypot' field to the CF7 form.
116
- add_filter( 'wpcf7_form_elements', 'zero_spam_wpcf7_form_elements', 10, 1 );
117
-
118
- // Preprocess CF7 form submissions.
119
- add_filter( 'wpcf7_validate', 'wpzerospam_wpcf7_preprocess_submission', 10, 2 );
120
- }
121
- }
122
- add_action( 'after_setup_theme', 'wpzerospam_cf7_after_setup_theme' );
123
-
124
- /**
125
- * Adds the 'cf7' spam type.
126
- *
127
- * @param array An array of the current spam types.
128
- * @return array The resulting current spam types.
129
- */
130
- if ( ! function_exists( 'wpzerospam_wpcf7_type' ) ) {
131
- function wpzerospam_wpcf7_type( $types ) {
132
- $types = array_merge( $types, [ 'cf7' => __( 'Contact Form 7', 'zero-spam' ) ] );
133
-
134
- return $types;
135
- }
136
- }
137
-
138
- /**
139
- * Adds the honeypot field to CF7 forms.
140
- *
141
- * @since 4.9.12
142
- *
143
- * @param sting HTML for the form.
144
- * @return string The modified form HTML.
145
- */
146
- function zero_spam_wpcf7_form_elements( $this_form_do_shortcode ) {
147
- $this_form_do_shortcode .= wpzerospam_honeypot_field();
148
-
149
- return $this_form_do_shortcode;
150
- };
151
-
152
- /**
153
- * Preprocess CF7 submissions.
154
- *
155
- * @since 4.9.12
156
- *
157
- * @param object $result CF7 result object.
158
- * @param object $tags CF7 tags object.
159
- * @return object A CF7 object.
160
- */
161
- if ( ! function_exists( 'wpzerospam_wpcf7_preprocess_submission' ) ) {
162
- function wpzerospam_wpcf7_preprocess_submission( $result, $tags ) {
163
- $options = wpzerospam_options();
164
- $honeypot = wpzerospam_get_honeypot();
165
-
166
- if (
167
- // First, check the 'honeypot' field.
168
- ( ! isset( $_REQUEST[ $honeypot ] ) || $_REQUEST[ $honeypot ] ) ||
169
- // Next, check the 'wpzerospam_key' field.
170
- ( empty( $_REQUEST['wpzerospam_key'] ) || wpzerospam_get_key() != $_REQUEST['wpzerospam_key'] )
171
- ) {
172
- // Spam registration selected.
173
- do_action( 'wpzerospam_cf7_spam', $_REQUEST );
174
- wpzerospam_spam_detected( 'cf7', $_REQUEST, false );
175
-
176
- $result->invalidate( $tags[0], $options['spam_message'] );
177
- }
178
-
179
- return $result;
180
- }
181
- }
182
-
183
- /**
184
- * Enqueue the CF7 form JS
185
- */
186
- if ( ! function_exists( 'wpzerospam_cf7' ) ) {
187
- function wpzerospam_cf7() {
188
- wp_enqueue_script(
189
- 'wpzerospam-integration-cf7',
190
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
191
- 'integrations/contact-form-7/js/cf7.js',
192
- [ 'wpzerospam' ],
193
- WORDPRESS_ZERO_SPAM_VERSION,
194
- true
195
- );
196
- }
197
- }
198
- add_action( 'wpcf7_enqueue_scripts', 'wpzerospam_cf7' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
integrations/contact-form-7/js/cf7.js DELETED
@@ -1,4 +0,0 @@
1
- // Initialize WPZS on CF7 forms.
2
- jQuery(function() {
3
- jQuery(".wpcf7-form").WordPressZeroSpam();
4
- });
 
 
 
 
integrations/fluentform/fluentform.php DELETED
@@ -1,58 +0,0 @@
1
- <?php
2
- /**
3
- * Handles checking submitted Fluent Forms for spam
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.4.0
7
- */
8
-
9
- /**
10
- * Add the 'fluentform' spam type
11
- */
12
- add_filter( 'wpzerospam_types', function( $types ) {
13
- $types = array_merge( $types, [ 'fluentform' => __( 'Fluent Forms', 'zero-spam' ) ] );
14
- return $types;
15
- });
16
-
17
- /**
18
- * Validation for Fluent Form submissions
19
- */
20
- if ( ! function_exists( 'wpzerospam_fluentform_validate' ) ) {
21
- function wpzerospam_fluentform_validate( $insertData, $data, $form ) {
22
- if ( is_user_logged_in() || wpzerospam_key_check( $data ) ) {
23
- return;
24
- }
25
-
26
- do_action( 'wpzerospam_fluentform_spam' );
27
-
28
- $data = [
29
- 'insertData' => $insertData,
30
- 'data' => $data,
31
- 'form' => $form
32
- ];
33
-
34
- wpzerospam_spam_detected( 'fluentform', $result, false );
35
-
36
- $options = wpzerospam_options();
37
- echo $options['spam_message'];
38
- die();
39
- }
40
- }
41
- add_action( 'fluentform_before_insert_submission', 'wpzerospam_fluentform_validate', 10, 3 );
42
-
43
- /**
44
- * Enqueue the Fluent Form JS
45
- */
46
- if ( ! function_exists( 'wpzerospam_fluentform' ) ) {
47
- function wpzerospam_fluentform() {
48
- wp_enqueue_script(
49
- 'wpzerospam-integration-fluentform',
50
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
51
- 'integrations/fluentform/js/fluentform.js',
52
- [ 'wpzerospam' ],
53
- WORDPRESS_ZERO_SPAM_VERSION,
54
- true
55
- );
56
- }
57
- }
58
- add_action( 'fluentform_before_form_render', 'wpzerospam_fluentform' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
integrations/fluentform/js/fluentform.js DELETED
@@ -1,4 +0,0 @@
1
- // Initialize WPZS on Fluent Forms.
2
- jQuery(function() {
3
- jQuery(".frm-fluent-form").WordPressZeroSpam();
4
- });
 
 
 
 
integrations/formidable/formidable.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
- /**
3
- * Handles checking submitted Formidable forms for spam
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.9.0
7
- */
8
-
9
- /**
10
- * Add the 'formidable' spam type
11
- *
12
- * @since 4.9.0
13
- */
14
- add_filter( 'wpzerospam_types', function( $types ) {
15
- $types = array_merge( $types, [ 'formidable' => __( 'Formidable Forms', 'zero-spam' ) ] );
16
- return $types;
17
- });
18
-
19
- /**
20
- * Validates the wpzerospam_key
21
- *
22
- * @since 4.9.0
23
- * @link https://formidableforms.com/knowledgebase/frm_validate_entry/
24
- */
25
- if ( ! function_exists( 'wpzerospam_formidable_frm_validate_entry' ) ) {
26
- function wpzerospam_formidable_frm_validate_entry( $errors, $values ) {
27
- // Don't validate for logged in users
28
- if ( is_user_logged_in() ) {
29
- return $errors;
30
- }
31
-
32
- // Validate the wpzerospam_key
33
- if ( ! wpzerospam_key_check( $values ) ) {
34
- $options = wpzerospam_options();
35
-
36
- do_action( 'wpzerospam_formidable_spam' );
37
-
38
- wpzerospam_spam_detected( 'formidable', $values, false );
39
-
40
- $errors['wpzerospam'] = $options['spam_message'];
41
- }
42
-
43
- return $errors;
44
- }
45
- }
46
- add_filter( 'frm_validate_entry', 'wpzerospam_formidable_frm_validate_entry', 20, 2 );
47
-
48
- /**
49
- * Adds the hidden wpzerospam_key field
50
- *
51
- * @since 4.9.0
52
- * @link https://formidableforms.com/knowledgebase/frm_entry_form/
53
- */
54
- if ( ! function_exists( 'wpzerospam_formidable_frm_entry_form' ) ) {
55
- function wpzerospam_formidable_frm_entry_form( $form ) {
56
- echo '<input type="hidden" name="wpzerospam_key" value="" />';
57
- }
58
- }
59
- add_action( 'frm_entry_form', 'wpzerospam_formidable_frm_entry_form' );
60
-
61
- /**
62
- * Adds the wpzerospam_key value to the hidden field via JS
63
- *
64
- * @since 4.9.0
65
- * @link https://formidableforms.com/knowledgebase/frm_entries_footer_scripts/
66
- */
67
- if ( ! function_exists( 'wpzerospam_formidable_frm_entries_footer_scripts' ) ) {
68
- function wpzerospam_formidable_frm_entries_footer_scripts( $fields, $form ) {
69
- ?>
70
- jQuery( '[name="wpzerospam_key"]' ).val( "<?php echo wpzerospam_get_key(); ?>" );
71
- <?php
72
- }
73
- }
74
- add_action( 'frm_entries_footer_scripts', 'wpzerospam_formidable_frm_entries_footer_scripts', 20, 2 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
integrations/registrations/js/registrations.js DELETED
@@ -1,4 +0,0 @@
1
- // Initialize WPZS on registration forms.
2
- jQuery(function() {
3
- jQuery("#registerform").WordPressZeroSpam();
4
- });
 
 
 
 
integrations/registrations/registrations.php DELETED
@@ -1,200 +0,0 @@
1
- <?php
2
- /**
3
- * Handles checking registration submissions for spam
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.3.7
7
- */
8
-
9
- /**
10
- * Adds admin settings for registration protection.
11
- *
12
- * @since 4.9.12
13
- *
14
- * @return void
15
- */
16
- if ( ! function_exists( 'wpzerospam_registrations_admin_fields' ) ) {
17
- function wpzerospam_registrations_admin_fields() {
18
- if ( get_option( 'users_can_register' ) ) {
19
- // Registration spam check
20
- add_settings_field( 'verify_registrations', __( 'Detect Spam/Malicious Registrations', 'zero-spam' ), 'wpzerospam_field_cb', 'wpzerospam', 'wpzerospam_spam_checks', [
21
- 'label_for' => 'verify_registrations',
22
- 'type' => 'checkbox',
23
- 'multi' => false,
24
- 'desc' => __( 'Monitors registrations for malicious or automated spambot submissions.', 'zero-spam' ),
25
- 'options' => [
26
- 'enabled' => __( 'Enabled', 'zero-spam' )
27
- ]
28
- ]);
29
- }
30
- }
31
- }
32
- add_action( 'wpzerospam_admin_options', 'wpzerospam_registrations_admin_fields' );
33
-
34
- /**
35
- * Add validation to the registration form protection admin fields.
36
- *
37
- * @since 4.9.12
38
- *
39
- * @param array $fields Array on available admin fields.
40
- */
41
- if ( ! function_exists( 'wpzerospam_registrations_admin_validation' ) ) {
42
- function wpzerospam_registrations_admin_validation( $fields ) {
43
- if ( empty( $fields['verify_registrations'] ) ) { $fields['verify_registrations'] = 'disabled'; }
44
-
45
- return $fields;
46
- }
47
- }
48
- add_filter( 'wpzerospam_admin_validation', 'wpzerospam_registrations_admin_validation' );
49
-
50
- /**
51
- * Sets the default admin option fields for registrations.
52
- *
53
- * @since 4.9.12
54
- *
55
- * @param array $defaults Array of WPZS admin option fields.
56
- * @return array The modified array of the WPZS admin option fields.
57
- */
58
- if ( ! function_exists( 'wpzerospam_registrations_admin_fields_default' ) ) {
59
- function wpzerospam_registrations_admin_fields_default( $defaults ) {
60
- if ( empty( $defaults['verify_registrations'] ) ) { $defaults['verify_registrations'] = 'enabled'; }
61
-
62
- return $defaults;
63
- }
64
- }
65
- add_filter( 'wpzerospam_admin_options_defaults', 'wpzerospam_registrations_admin_fields_default' );
66
-
67
- if ( ! function_exists( 'wpzerospam_registrations_admin_submission_data_item' ) ) {
68
- function wpzerospam_registrations_admin_submission_data_item( $key, $value ) {
69
- switch( $key ) {
70
- case 'sanitized_user_login':
71
- echo wpzerospam_admin_details_item( __( 'Sanitized User Login', 'zero-spam' ), $value );
72
- break;
73
- case 'user_email':
74
- echo wpzerospam_admin_details_item( __( 'User Email', 'zero-spam' ), $value );
75
- break;
76
- case 'errors':
77
- echo wpzerospam_admin_details_item( __( 'Errors', 'zero-spam' ), json_encode( $value ) );
78
- break;
79
- }
80
- }
81
- }
82
- add_action( 'wpzerospam_admin_submission_data_items', 'wpzerospam_registrations_admin_submission_data_item', 10, 2 );
83
-
84
- if ( ! function_exists( 'wpzerospamregistrations_defined_submission_data' ) ) {
85
- function wpzerospamregistrations_defined_submission_data( $submission_data_keys ) {
86
- $submission_data_keys[] = 'sanitized_user_login';
87
- $submission_data_keys[] = 'user_email';
88
- $submission_data_keys[] = 'errors';
89
-
90
- return $submission_data_keys;
91
- }
92
- }
93
- add_filter( 'wpzerospam_defined_submission_data', 'wpzerospamregistrations_defined_submission_data', 10, 1 );
94
-
95
- /*
96
- * Runs the registration form spam detections.
97
- *
98
- * Runs all action & filter hooks needed for monitoring registrations for
99
- * spam (when enabled via the 'Detect Registration Spam' option).
100
- *
101
- * @since 4.9.12
102
- *
103
- * @return void
104
- */
105
- if ( ! function_exists( 'wpzerospam_registrations_after_setup_theme' ) ) {
106
- function wpzerospam_registrations_after_setup_theme() {
107
- // Add the 'registration' spam type.
108
- add_filter( 'wpzerospam_types', 'wpzerospam_registrations_types' );
109
-
110
- // Check if site registrations are enabled
111
- if ( ! get_option( 'users_can_register' ) ) { return false; }
112
-
113
- $options = wpzerospam_options();
114
-
115
- // Check if detecting registration spam is enabled & user is unauthenticated.
116
- if ( 'enabled' != $options['verify_registrations'] || is_user_logged_in() ) { return false; }
117
-
118
- // Add the 'honeypot' field to the registration form.
119
- add_action( 'register_form', 'wpzerospam_registrations_form' );
120
-
121
- // Preprocess registration submissions.
122
- add_action( 'register_post', 'wpzerospam_registrations_preprocess', 10, 3 );
123
- }
124
- }
125
- add_action( 'after_setup_theme', 'wpzerospam_registrations_after_setup_theme' );
126
-
127
- /**
128
- * Adds the 'registration' spam type.
129
- *
130
- * @param array An array of the current spam types.
131
- * @return array The resulting current spam types.
132
- */
133
- if ( ! function_exists( 'wpzerospam_registrations_types' ) ) {
134
- function wpzerospam_registrations_types( $types ) {
135
- $types = array_merge( $types, [ 'registration' => __( 'Registration', 'zero-spam' ) ] );
136
-
137
- return $types;
138
- }
139
- }
140
-
141
- /**
142
- * Add a 'honeypot' field to the registration form.
143
- *
144
- * @since 4.9.12
145
- *
146
- * @link https://codex.wordpress.org/Plugin_API/Action_Reference/register_form
147
- *
148
- * @return string HTML to append to the registration form.
149
- */
150
- if ( ! function_exists( 'wpzerospam_registrations_form' ) ) {
151
- function wpzerospam_registrations_form( $defaults ) {
152
- echo wpzerospam_honeypot_field();
153
- }
154
- }
155
-
156
- /**
157
- * Preprocess registration form submissions.
158
- */
159
- if ( ! function_exists( 'wpzerospam_registrations_preprocess' ) ) {
160
- function wpzerospam_registrations_preprocess( $sanitized_user_login, $user_email, $errors ) {
161
- $options = wpzerospam_options();
162
- $honeypot = wpzerospam_get_honeypot();
163
-
164
- if (
165
- // First, check the 'honeypot' field.
166
- ( ! isset( $_REQUEST[ $honeypot ] ) || $_REQUEST[ $honeypot ] ) ||
167
- // Next, check the 'wpzerospam_key' field.
168
- ( empty( $_REQUEST['wpzerospam_key'] ) || wpzerospam_get_key() != $_REQUEST['wpzerospam_key'] )
169
- ) {
170
- // Spam registration selected.
171
- do_action( 'wpzerospam_registration_spam', $errors, $sanitized_user_login, $user_email );
172
- wpzerospam_spam_detected( 'registration', [
173
- 'sanitized_user_login' => $sanitized_user_login,
174
- 'user_email' => $user_email,
175
- 'errors' => $errors
176
- ]);
177
- }
178
- }
179
- }
180
-
181
- /**
182
- * Enqueue the registration form JS
183
- */
184
- if ( ! function_exists( 'wpzerospam_registration_form' ) ) {
185
- function wpzerospam_registration_form() {
186
- $options = wpzerospam_options();
187
- if ( 'enabled' != $options['verify_registrations'] ) { return; }
188
-
189
- // WordPress Zero Spam registration integration
190
- wp_enqueue_script(
191
- 'wpzerospam-integration-registrations',
192
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
193
- 'integrations/registrations/js/registrations.js',
194
- [ 'wpzerospam' ],
195
- WORDPRESS_ZERO_SPAM_VERSION,
196
- true
197
- );
198
- }
199
- }
200
- add_action( 'login_enqueue_scripts', 'wpzerospam_registration_form' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
integrations/wpforms/js/wpforms.js DELETED
@@ -1,4 +0,0 @@
1
- // Initialize WPZS on WPForms.
2
- jQuery(function() {
3
- jQuery(".wpforms-form").WordPressZeroSpam();
4
- });
 
 
 
 
integrations/wpforms/wpforms.php DELETED
@@ -1,54 +0,0 @@
1
- <?php
2
- /**
3
- * Handles checking submitted WPForms submissions
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.1.0
7
- */
8
-
9
- /**
10
- * Add the 'wpform' spam type
11
- */
12
- add_filter( 'wpzerospam_types', function( $types ) {
13
- $types = array_merge( $types, [ 'wpform' => __( 'WPForms', 'zero-spam' ) ] );
14
- return $types;
15
- });
16
-
17
- /**
18
- * Validation for WPForms submissions
19
- *
20
- * @link https://wpforms.com/developers/wpforms_process_before/
21
- */
22
- if ( ! function_exists( 'wpzerospam_wpforms_process_before' ) ) {
23
- function wpzerospam_wpforms_process_before( $entry, $form_data ) {
24
- if ( is_user_logged_in() || wpzerospam_key_check() ) {
25
- return;
26
- }
27
-
28
- do_action( 'wpzerospam_wpform_spam' );
29
-
30
- $data = [
31
- 'entry' => $entry,
32
- 'form_data' => $form_data
33
- ];
34
- wpzerospam_spam_detected( 'wpform', $data );
35
- }
36
- }
37
- add_action( 'wpforms_process_before', 'wpzerospam_wpforms_process_before', 10, 2 );
38
-
39
- /**
40
- * Enqueue the WPForms form JS
41
- */
42
- if ( ! function_exists( 'wpzerospam_wpforms' ) ) {
43
- function wpzerospam_wpforms() {
44
- wp_enqueue_script(
45
- 'wpzerospam-integration-wpforms',
46
- plugin_dir_url( WORDPRESS_ZERO_SPAM ) .
47
- 'integrations/wpforms/js/wpforms.js',
48
- [ 'wpzerospam' ],
49
- WORDPRESS_ZERO_SPAM_VERSION,
50
- true
51
- );
52
- }
53
- }
54
- add_action( 'wpforms_frontend_output_before', 'wpzerospam_wpforms' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
languages/zero-spam-fr_FR.pot DELETED
@@ -1,685 +0,0 @@
1
- #, fuzzy
2
- msgid ""
3
- msgstr ""
4
- "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
5
- "Project-Id-Version: WordPress Zero Spam\n"
6
- "POT-Creation-Date: 2020-08-07 10:12-0500\n"
7
- "PO-Revision-Date: 2020-08-07 10:10-0500\n"
8
- "Last-Translator: \n"
9
- "Language-Team: \n"
10
- "MIME-Version: 1.0\n"
11
- "Content-Type: text/plain; charset=UTF-8\n"
12
- "Content-Transfer-Encoding: 8bit\n"
13
- "X-Generator: Poedit 2.4\n"
14
- "X-Poedit-Basepath: ..\n"
15
- "X-Poedit-Flags-xgettext: --add-comments=translators:\n"
16
- "X-Poedit-WPHeader: wordpress-zero-spam.php\n"
17
- "X-Poedit-SourceCharset: UTF-8\n"
18
- "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
19
- "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
20
- "_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
21
- "X-Poedit-SearchPath-0: .\n"
22
- "X-Poedit-SearchPathExcluded-0: *.min.js\n"
23
-
24
- #: classes/class-wpzerospam-blacklisted-table.php:20
25
- msgid "Blacklist"
26
- msgstr ""
27
-
28
- #: classes/class-wpzerospam-blacklisted-table.php:21
29
- msgid "Blacklist IPs"
30
- msgstr ""
31
-
32
- #: classes/class-wpzerospam-blacklisted-table.php:32
33
- #: classes/class-wpzerospam-blacklisted-table.php:131
34
- msgid "Last Updated"
35
- msgstr ""
36
-
37
- #: classes/class-wpzerospam-blacklisted-table.php:33
38
- #: classes/class-wpzerospam-blocked-ip-table.php:32
39
- #: classes/class-wpzerospam-log-table.php:34 inc/admin.php:245
40
- msgid "IP Address"
41
- msgstr ""
42
-
43
- #: classes/class-wpzerospam-blacklisted-table.php:34
44
- #: classes/class-wpzerospam-blacklisted-table.php:136
45
- msgid "Service"
46
- msgstr ""
47
-
48
- #: classes/class-wpzerospam-blacklisted-table.php:35
49
- #: classes/class-wpzerospam-blocked-ip-table.php:38
50
- msgid "Attempts"
51
- msgstr ""
52
-
53
- #: classes/class-wpzerospam-blacklisted-table.php:36
54
- #: classes/class-wpzerospam-log-table.php:38
55
- msgid "Details"
56
- msgstr ""
57
-
58
- #: classes/class-wpzerospam-blacklisted-table.php:63
59
- msgid "Filter by service"
60
- msgstr ""
61
-
62
- #: classes/class-wpzerospam-blacklisted-table.php:67
63
- msgid "All services"
64
- msgstr ""
65
-
66
- #: classes/class-wpzerospam-blacklisted-table.php:68
67
- msgid "BotScout"
68
- msgstr ""
69
-
70
- #: classes/class-wpzerospam-blacklisted-table.php:69 inc/admin.php:539
71
- msgid "Stop Forum Spam"
72
- msgstr ""
73
-
74
- #: classes/class-wpzerospam-blacklisted-table.php:70
75
- msgid "Zero Spam"
76
- msgstr ""
77
-
78
- #: classes/class-wpzerospam-blacklisted-table.php:73
79
- #: classes/class-wpzerospam-blocked-ip-table.php:76
80
- #: classes/class-wpzerospam-log-table.php:79
81
- msgid "Filter"
82
- msgstr ""
83
-
84
- #: classes/class-wpzerospam-blacklisted-table.php:116
85
- #: classes/class-wpzerospam-log-table.php:140
86
- msgid "No details available."
87
- msgstr ""
88
-
89
- #: classes/class-wpzerospam-blacklisted-table.php:119
90
- #: classes/class-wpzerospam-log-table.php:143
91
- msgid "View"
92
- msgstr ""
93
-
94
- #: classes/class-wpzerospam-blacklisted-table.php:126
95
- #: classes/class-wpzerospam-log-table.php:148
96
- msgid "Detected Spam IP"
97
- msgstr ""
98
-
99
- #: classes/class-wpzerospam-blacklisted-table.php:160
100
- msgid "Appears"
101
- msgstr ""
102
-
103
- #: classes/class-wpzerospam-blacklisted-table.php:166
104
- msgid "Confidence"
105
- msgstr ""
106
-
107
- #: classes/class-wpzerospam-blacklisted-table.php:172
108
- msgid "Frequency"
109
- msgstr ""
110
-
111
- #: classes/class-wpzerospam-blacklisted-table.php:178
112
- msgid "Last Seen"
113
- msgstr ""
114
-
115
- #: classes/class-wpzerospam-blacklisted-table.php:184
116
- msgid "ASN"
117
- msgstr ""
118
-
119
- #: classes/class-wpzerospam-blacklisted-table.php:208
120
- #: classes/class-wpzerospam-blocked-ip-table.php:154
121
- msgid "Delete"
122
- msgstr ""
123
-
124
- #: classes/class-wpzerospam-blacklisted-table.php:209
125
- #: classes/class-wpzerospam-blocked-ip-table.php:155
126
- #: classes/class-wpzerospam-log-table.php:289
127
- msgid "Delete All Entries"
128
- msgstr ""
129
-
130
- #: classes/class-wpzerospam-blocked-ip-table.php:20
131
- msgid "Blocked IP"
132
- msgstr ""
133
-
134
- #: classes/class-wpzerospam-blocked-ip-table.php:21 inc/admin.php:70
135
- #: inc/admin.php:560
136
- msgid "Blocked IPs"
137
- msgstr ""
138
-
139
- #: classes/class-wpzerospam-blocked-ip-table.php:33
140
- #: classes/class-wpzerospam-log-table.php:33
141
- #: classes/class-wpzerospam-log-table.php:163 inc/admin.php:255
142
- msgid "Type"
143
- msgstr ""
144
-
145
- #: classes/class-wpzerospam-blocked-ip-table.php:34
146
- msgid "Date Added"
147
- msgstr ""
148
-
149
- #: classes/class-wpzerospam-blocked-ip-table.php:35 inc/admin.php:266
150
- msgid "Start Date"
151
- msgstr ""
152
-
153
- #: classes/class-wpzerospam-blocked-ip-table.php:36 inc/admin.php:270
154
- msgid "End Date"
155
- msgstr ""
156
-
157
- #: classes/class-wpzerospam-blocked-ip-table.php:37
158
- #: classes/class-wpzerospam-log-table.php:216 inc/admin.php:262
159
- msgid "Reason"
160
- msgstr ""
161
-
162
- #: classes/class-wpzerospam-blocked-ip-table.php:67
163
- #: classes/class-wpzerospam-log-table.php:68
164
- msgid "Filter by type"
165
- msgstr ""
166
-
167
- #: classes/class-wpzerospam-blocked-ip-table.php:71
168
- #: classes/class-wpzerospam-log-table.php:73
169
- msgid "All types"
170
- msgstr ""
171
-
172
- #: classes/class-wpzerospam-blocked-ip-table.php:72 inc/admin.php:258
173
- msgid "Permanent"
174
- msgstr ""
175
-
176
- #: classes/class-wpzerospam-blocked-ip-table.php:73 inc/admin.php:257
177
- msgid "Temporary"
178
- msgstr ""
179
-
180
- #: classes/class-wpzerospam-blocked-ip-table.php:136
181
- msgid ""
182
- "Comment <span class=\"wpzerospam-small\">(<strong>permanently</strong> auto-"
183
- "blocked)</span>"
184
- msgstr ""
185
-
186
- #: classes/class-wpzerospam-blocked-ip-table.php:139
187
- msgid "Comment <span class=\"wpzerospam-small\">(auto-blocked)</span>"
188
- msgstr ""
189
-
190
- #: classes/class-wpzerospam-log-table.php:20
191
- msgid "Spam Detection"
192
- msgstr ""
193
-
194
- #: classes/class-wpzerospam-log-table.php:21 inc/admin.php:60 inc/admin.php:61
195
- #: inc/admin.php:591 templates/map.php:97
196
- msgid "Spam Detections"
197
- msgstr ""
198
-
199
- #: classes/class-wpzerospam-log-table.php:32
200
- #: classes/class-wpzerospam-log-table.php:158
201
- msgid "Date"
202
- msgstr ""
203
-
204
- #: classes/class-wpzerospam-log-table.php:35
205
- #: classes/class-wpzerospam-log-table.php:169
206
- msgid "Country"
207
- msgstr ""
208
-
209
- #: classes/class-wpzerospam-log-table.php:36
210
- #: classes/class-wpzerospam-log-table.php:176
211
- msgid "Region"
212
- msgstr ""
213
-
214
- #: classes/class-wpzerospam-log-table.php:37
215
- #: classes/class-wpzerospam-log-table.php:183
216
- msgid "City"
217
- msgstr ""
218
-
219
- #: classes/class-wpzerospam-log-table.php:39
220
- #: classes/class-wpzerospam-log-table.php:101 templates/ip-list.php:63
221
- msgid "Block IP"
222
- msgstr ""
223
-
224
- #: classes/class-wpzerospam-log-table.php:99 templates/ip-list.php:60
225
- msgid "Blocked"
226
- msgstr ""
227
-
228
- #: classes/class-wpzerospam-log-table.php:153
229
- msgid "Page URL"
230
- msgstr ""
231
-
232
- #: classes/class-wpzerospam-log-table.php:198
233
- msgid "Sanitized User Login"
234
- msgstr ""
235
-
236
- #: classes/class-wpzerospam-log-table.php:204
237
- msgid "User Email"
238
- msgstr ""
239
-
240
- #: classes/class-wpzerospam-log-table.php:210
241
- msgid "Errors"
242
- msgstr ""
243
-
244
- #: classes/class-wpzerospam-log-table.php:224
245
- msgid "Form Action"
246
- msgstr ""
247
-
248
- #: classes/class-wpzerospam-log-table.php:230
249
- msgid "Form ID"
250
- msgstr ""
251
-
252
- #: classes/class-wpzerospam-log-table.php:236
253
- msgid "Form Key"
254
- msgstr ""
255
-
256
- #: classes/class-wpzerospam-log-table.php:242
257
- msgid "Item Key"
258
- msgstr ""
259
-
260
- #: classes/class-wpzerospam-log-table.php:248
261
- msgid "Form Values"
262
- msgstr ""
263
-
264
- #: classes/class-wpzerospam-log-table.php:258
265
- msgid "Source"
266
- msgstr ""
267
-
268
- #: classes/class-wpzerospam-log-table.php:288
269
- msgid "Delete Selected"
270
- msgstr ""
271
-
272
- #: inc/admin.php:41 inc/admin.php:51
273
- msgid "WordPress Zero Spam Dashboard"
274
- msgstr ""
275
-
276
- #: inc/admin.php:42
277
- msgid "WP Zero Spam"
278
- msgstr ""
279
-
280
- #: inc/admin.php:52
281
- msgid "Dashboard"
282
- msgstr ""
283
-
284
- #: inc/admin.php:69
285
- msgid "Blocked IP Addresses"
286
- msgstr ""
287
-
288
- #: inc/admin.php:78 inc/admin.php:79
289
- msgid "Blacklisted IPs"
290
- msgstr ""
291
-
292
- #: inc/admin.php:87
293
- msgid "WordPress Zero Spam Settings"
294
- msgstr ""
295
-
296
- #: inc/admin.php:88 inc/admin.php:422
297
- msgid "Settings"
298
- msgstr ""
299
-
300
- #: inc/admin.php:118 inc/admin.php:147 inc/admin.php:293
301
- msgid "Search IPs"
302
- msgstr ""
303
-
304
- #: inc/admin.php:221
305
- msgid "Please enter a valid IP address."
306
- msgstr ""
307
-
308
- #: inc/admin.php:224
309
- msgid "Please select a valid type."
310
- msgstr ""
311
-
312
- #: inc/admin.php:227
313
- msgid "Please select a date & time when the temporary block should end."
314
- msgstr ""
315
-
316
- #: inc/admin.php:236
317
- msgid "The blocked IP has been successfully added."
318
- msgstr ""
319
-
320
- #: inc/admin.php:243 inc/admin.php:274
321
- msgid "Add Blocked IP"
322
- msgstr ""
323
-
324
- #: inc/admin.php:263
325
- msgid "e.g. Spammed form"
326
- msgstr ""
327
-
328
- #: inc/admin.php:267 inc/admin.php:271
329
- msgid "Optional"
330
- msgstr ""
331
-
332
- #: inc/admin.php:314
333
- msgid "Statistics"
334
- msgstr ""
335
-
336
- #: inc/admin.php:441
337
- msgid "General Settings"
338
- msgstr ""
339
-
340
- #: inc/admin.php:442
341
- msgid "Auto-block Settings"
342
- msgstr ""
343
-
344
- #: inc/admin.php:443
345
- msgid "On-site Spam Prevention"
346
- msgstr ""
347
-
348
- #: inc/admin.php:444
349
- msgid "Integrations & Third-party APIs"
350
- msgstr ""
351
-
352
- #: inc/admin.php:447
353
- msgid "Cookie Expiration"
354
- msgstr ""
355
-
356
- #: inc/admin.php:453
357
- msgid "days"
358
- msgstr ""
359
-
360
- #: inc/admin.php:457
361
- msgid "Share Spam Detections"
362
- msgstr ""
363
-
364
- #: inc/admin.php:463 inc/admin.php:474 inc/admin.php:545 inc/admin.php:628
365
- #: inc/admin.php:639 inc/admin.php:652 inc/admin.php:664 inc/admin.php:677
366
- #: inc/admin.php:690 inc/admin.php:703 inc/admin.php:716
367
- #: integrations/comments/comments.php:25 integrations/comments/comments.php:36
368
- #: integrations/comments/comments.php:47
369
- msgid "Enabled"
370
- msgstr ""
371
-
372
- #: inc/admin.php:468
373
- msgid "Auto-block IPs"
374
- msgstr ""
375
-
376
- #: inc/admin.php:480
377
- msgid "Auto-block Period"
378
- msgstr ""
379
-
380
- #: inc/admin.php:486
381
- msgid "minutes"
382
- msgstr ""
383
-
384
- #: inc/admin.php:491
385
- msgid "Permanently Auto-block"
386
- msgstr ""
387
-
388
- #: inc/admin.php:500
389
- msgid "API Timeout"
390
- msgstr ""
391
-
392
- #: inc/admin.php:506
393
- msgid "seconds"
394
- msgstr ""
395
-
396
- #: inc/admin.php:511
397
- msgid "ipstack API Key"
398
- msgstr ""
399
-
400
- #: inc/admin.php:514
401
- msgid "Enter your ipstack API key."
402
- msgstr ""
403
-
404
- #: inc/admin.php:521
405
- msgid "BotScout API Key"
406
- msgstr ""
407
-
408
- #: inc/admin.php:525
409
- msgid "Enter your free BotScout API key."
410
- msgstr ""
411
-
412
- #: inc/admin.php:530
413
- msgid "BotScout Count Minimum"
414
- msgstr ""
415
-
416
- #: inc/admin.php:550
417
- msgid "Stop Forum Spam Confidence Minimum"
418
- msgstr ""
419
-
420
- #: inc/admin.php:556
421
- msgid "%"
422
- msgstr ""
423
-
424
- #: inc/admin.php:565 inc/admin.php:596
425
- msgid "Redirect user"
426
- msgstr ""
427
-
428
- #: inc/admin.php:566 inc/admin.php:597
429
- msgid ""
430
- "Display a <a href=\"https://developer.mozilla.org/en-US/docs/Web/HTTP/"
431
- "Status/403\" target=\"_blank\"><code>403 Forbidden</code></a> error"
432
- msgstr ""
433
-
434
- #: inc/admin.php:572
435
- msgid "Redirect for Blocked Users"
436
- msgstr ""
437
-
438
- #: inc/admin.php:581
439
- msgid "Blocked Message"
440
- msgstr ""
441
-
442
- #: inc/admin.php:586 inc/utilities.php:115
443
- msgid ""
444
- "You have been blocked from visiting this site by WordPress Zero Spam due to "
445
- "detected spam activity."
446
- msgstr ""
447
-
448
- #: inc/admin.php:603
449
- msgid "Redirect for Spam"
450
- msgstr ""
451
-
452
- #: inc/admin.php:612
453
- msgid "Spam Detection Message"
454
- msgstr ""
455
-
456
- #: inc/admin.php:617 inc/utilities.php:114
457
- msgid "There was a problem with your submission. Please go back and try again."
458
- msgstr ""
459
-
460
- #: inc/admin.php:622
461
- msgid "Log Blocked IPs"
462
- msgstr ""
463
-
464
- #: inc/admin.php:633
465
- msgid "Log Spam Detections"
466
- msgstr ""
467
-
468
- #: inc/admin.php:646
469
- msgid "Verify Registrations"
470
- msgstr ""
471
-
472
- #: inc/admin.php:658
473
- msgid "Verify CF7 Submissions"
474
- msgstr ""
475
-
476
- #: inc/admin.php:671
477
- msgid "Verify BuddyPress Registrations"
478
- msgstr ""
479
-
480
- #: inc/admin.php:684
481
- msgid "Verify WPForms Submissions"
482
- msgstr ""
483
-
484
- #: inc/admin.php:697
485
- msgid "Verify Fluent Form Submissions"
486
- msgstr ""
487
-
488
- #: inc/admin.php:710
489
- msgid "Verify Formidable Form Submissions"
490
- msgstr ""
491
-
492
- #: inc/admin.php:722
493
- msgid "IP Whitelist"
494
- msgstr ""
495
-
496
- #: inc/admin.php:727
497
- msgid "e.g. xxx.xxx.x.x"
498
- msgstr ""
499
-
500
- #: inc/utilities.php:324
501
- msgid "Access Blocked"
502
- msgstr ""
503
-
504
- #: integrations/comments/comments.php:19
505
- msgid "Strip Comment Links"
506
- msgstr ""
507
-
508
- #: integrations/comments/comments.php:30
509
- msgid "Strip Comment Author Links"
510
- msgstr ""
511
-
512
- #: integrations/comments/comments.php:41
513
- msgid "Detect Comment Spam"
514
- msgstr ""
515
-
516
- #: integrations/comments/comments.php:93
517
- msgid "Comment Post"
518
- msgstr ""
519
-
520
- #: integrations/comments/comments.php:96 integrations/comments/comments.php:119
521
- msgid "Author"
522
- msgstr ""
523
-
524
- #: integrations/comments/comments.php:99 integrations/comments/comments.php:124
525
- msgid "Email"
526
- msgstr ""
527
-
528
- #: integrations/comments/comments.php:102
529
- #: integrations/comments/comments.php:129
530
- msgid "Website"
531
- msgstr ""
532
-
533
- #: integrations/comments/comments.php:105
534
- #: integrations/comments/comments.php:133
535
- #: integrations/comments/comments.php:286
536
- msgid "Comment"
537
- msgstr ""
538
-
539
- #: integrations/comments/comments.php:108
540
- msgid "Comment Type"
541
- msgstr ""
542
-
543
- #: integrations/comments/comments.php:111
544
- msgid "Comment Parent ID"
545
- msgstr ""
546
-
547
- #: integrations/comments/comments.php:136
548
- msgid "User IP"
549
- msgstr ""
550
-
551
- #: integrations/comments/comments.php:139
552
- msgid "User Agent"
553
- msgstr ""
554
-
555
- #: integrations/comments/comments.php:142
556
- msgid "Site"
557
- msgstr ""
558
-
559
- #: integrations/comments/comments.php:145
560
- msgid "Site Language"
561
- msgstr ""
562
-
563
- #: integrations/comments/comments.php:148
564
- msgid "Site Charset"
565
- msgstr ""
566
-
567
- #: integrations/comments/comments.php:151
568
- msgid "Permalink"
569
- msgstr ""
570
-
571
- #: integrations/comments/comments.php:159
572
- msgid "Akismet Result"
573
- msgstr ""
574
-
575
- #: integrations/comments/comments.php:162
576
- msgid "Akismet Pro Tip"
577
- msgstr ""
578
-
579
- #: templates/callout.php:12
580
- msgid ""
581
- "Are you a fan of the <a href=\"https://benmarshall.me/wordpress-zero-spam/?"
582
- "utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=admin\" "
583
- "target=\"_blank\">WordPress Zero Spam</a> plugin? Show your support."
584
- msgstr ""
585
-
586
- #: templates/callout.php:13
587
- msgid ""
588
- "Help support the continued development of the WordPress Zero Spam plugin by "
589
- "<a href=\"https://benmarshall.me/donate?"
590
- "utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=admin\" "
591
- "target=\"_blank\">donating today</a>. Your donation goes towards the time it "
592
- "takes to develop new features &amp; updates, but also helps provide pro bono "
593
- "work for nonprofits. <a href=\"https://benmarshall.me/donate?"
594
- "utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=admin\" "
595
- "target=\"_blank\">Learn more</a>."
596
- msgstr ""
597
-
598
- #: templates/callout.php:14
599
- msgid "For the latest updates,"
600
- msgstr ""
601
-
602
- #: templates/callout.php:14
603
- msgid "follow us on Twitter"
604
- msgstr ""
605
-
606
- #: templates/callout.php:14
607
- msgid "Facebook"
608
- msgstr ""
609
-
610
- #: templates/callout.php:14
611
- msgid "or"
612
- msgstr ""
613
-
614
- #: templates/callout.php:14
615
- msgid "visit our website"
616
- msgstr ""
617
-
618
- #: templates/callout.php:17
619
- msgid "Submit Bug/Feature Request"
620
- msgstr ""
621
-
622
- #: templates/callout.php:18
623
- msgid "Fork on Github"
624
- msgstr ""
625
-
626
- #: templates/callout.php:19
627
- msgid "Show your Support &mdash; Donate"
628
- msgstr ""
629
-
630
- #: templates/callout.php:23
631
- msgid "Your IP Address:"
632
- msgstr ""
633
-
634
- #: templates/countries-pie-chart.php:12
635
- msgid "Most Spam by Country"
636
- msgstr ""
637
-
638
- #: templates/countries-pie-chart.php:67 templates/ip-list.php:71
639
- #: templates/regions-pie-chart.php:82 templates/spam-line-chart.php:58
640
- msgid "No data to report yet."
641
- msgstr ""
642
-
643
- #: templates/ip-list.php:12
644
- msgid "Most Spam by IP Address"
645
- msgstr ""
646
-
647
- #: templates/map.php:11
648
- msgid "Spam Detections World Map"
649
- msgstr ""
650
-
651
- #: templates/map.php:103
652
- msgid "<strong>Enter your <a href=\""
653
- msgstr ""
654
-
655
- #: templates/regions-pie-chart.php:12
656
- msgid "Most Spam by Region"
657
- msgstr ""
658
-
659
- #: templates/spam-line-chart.php:10
660
- msgid "Spam by Date"
661
- msgstr ""
662
-
663
- #. Plugin Name of the plugin/theme
664
- msgid "WordPress Zero Spam"
665
- msgstr ""
666
-
667
- #. Plugin URI of the plugin/theme
668
- msgid "https://benmarshall.me/wordpress-zero-spam"
669
- msgstr ""
670
-
671
- #. Description of the plugin/theme
672
- msgid ""
673
- "Tired of all the useless and bloated WordPress spam plugins? The WordPress "
674
- "Zero Spam plugin makes blocking spam a cinch. <strong>Just install, activate "
675
- "and say goodbye to spam.</strong> Based on work by <a href=\"http://"
676
- "davidwalsh.name/wordpress-comment-spam\" target=\"_blank\">David Walsh</a>."
677
- msgstr ""
678
-
679
- #. Author of the plugin/theme
680
- msgid "Ben Marshall"
681
- msgstr ""
682
-
683
- #. Author URI of the plugin/theme
684
- msgid "https://benmarshall.me"
685
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
languages/zero-spam-it_IT.pot DELETED
@@ -1,875 +0,0 @@
1
- #, fuzzy
2
- msgid ""
3
- msgstr ""
4
- "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
5
- "Project-Id-Version: WordPress Zero Spam\n"
6
- "POT-Creation-Date: 2020-08-07 13:08-0500\n"
7
- "PO-Revision-Date: 2020-08-07 13:06-0500\n"
8
- "Last-Translator: \n"
9
- "Language-Team: \n"
10
- "MIME-Version: 1.0\n"
11
- "Content-Type: text/plain; charset=UTF-8\n"
12
- "Content-Transfer-Encoding: 8bit\n"
13
- "X-Generator: Poedit 2.4\n"
14
- "X-Poedit-Basepath: ..\n"
15
- "X-Poedit-Flags-xgettext: --add-comments=translators:\n"
16
- "X-Poedit-WPHeader: wordpress-zero-spam.php\n"
17
- "X-Poedit-SourceCharset: UTF-8\n"
18
- "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
19
- "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
20
- "_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
21
- "X-Poedit-SearchPath-0: .\n"
22
- "X-Poedit-SearchPathExcluded-0: *.min.js\n"
23
-
24
- #: classes/class-wpzerospam-blacklisted-table.php:20
25
- msgid "Blacklist"
26
- msgstr ""
27
-
28
- #: classes/class-wpzerospam-blacklisted-table.php:21
29
- msgid "Blacklist IPs"
30
- msgstr ""
31
-
32
- #: classes/class-wpzerospam-blacklisted-table.php:32
33
- #: classes/class-wpzerospam-blacklisted-table.php:131
34
- msgid "Last Updated"
35
- msgstr ""
36
-
37
- #: classes/class-wpzerospam-blacklisted-table.php:33
38
- #: classes/class-wpzerospam-blocked-ip-table.php:32
39
- #: classes/class-wpzerospam-log-table.php:34 inc/admin.php:245
40
- msgid "IP Address"
41
- msgstr ""
42
-
43
- #: classes/class-wpzerospam-blacklisted-table.php:34
44
- #: classes/class-wpzerospam-blacklisted-table.php:136
45
- msgid "Service"
46
- msgstr ""
47
-
48
- #: classes/class-wpzerospam-blacklisted-table.php:35
49
- #: classes/class-wpzerospam-blocked-ip-table.php:38
50
- msgid "Attempts"
51
- msgstr ""
52
-
53
- #: classes/class-wpzerospam-blacklisted-table.php:36
54
- #: classes/class-wpzerospam-log-table.php:38
55
- msgid "Details"
56
- msgstr ""
57
-
58
- #: classes/class-wpzerospam-blacklisted-table.php:63
59
- msgid "Filter by service"
60
- msgstr ""
61
-
62
- #: classes/class-wpzerospam-blacklisted-table.php:67
63
- msgid "All services"
64
- msgstr ""
65
-
66
- #: classes/class-wpzerospam-blacklisted-table.php:68
67
- msgid "BotScout"
68
- msgstr ""
69
-
70
- #: classes/class-wpzerospam-blacklisted-table.php:69 inc/admin.php:568
71
- msgid "Stop Forum Spam"
72
- msgstr ""
73
-
74
- #: classes/class-wpzerospam-blacklisted-table.php:70
75
- msgid "Zero Spam"
76
- msgstr ""
77
-
78
- #: classes/class-wpzerospam-blacklisted-table.php:73
79
- #: classes/class-wpzerospam-blocked-ip-table.php:76
80
- #: classes/class-wpzerospam-log-table.php:79
81
- msgid "Filter"
82
- msgstr ""
83
-
84
- #: classes/class-wpzerospam-blacklisted-table.php:116
85
- #: classes/class-wpzerospam-log-table.php:140
86
- msgid "No details available."
87
- msgstr ""
88
-
89
- #: classes/class-wpzerospam-blacklisted-table.php:119
90
- #: classes/class-wpzerospam-log-table.php:143
91
- msgid "View"
92
- msgstr ""
93
-
94
- #: classes/class-wpzerospam-blacklisted-table.php:126
95
- #: classes/class-wpzerospam-log-table.php:148
96
- msgid "Detected Spam IP"
97
- msgstr ""
98
-
99
- #: classes/class-wpzerospam-blacklisted-table.php:160
100
- msgid "Appears"
101
- msgstr ""
102
-
103
- #: classes/class-wpzerospam-blacklisted-table.php:166
104
- msgid "Confidence"
105
- msgstr ""
106
-
107
- #: classes/class-wpzerospam-blacklisted-table.php:172
108
- msgid "Frequency"
109
- msgstr ""
110
-
111
- #: classes/class-wpzerospam-blacklisted-table.php:178
112
- msgid "Last Seen"
113
- msgstr ""
114
-
115
- #: classes/class-wpzerospam-blacklisted-table.php:184
116
- msgid "ASN"
117
- msgstr ""
118
-
119
- #: classes/class-wpzerospam-blacklisted-table.php:208
120
- #: classes/class-wpzerospam-blocked-ip-table.php:164
121
- msgid "Delete"
122
- msgstr ""
123
-
124
- #: classes/class-wpzerospam-blacklisted-table.php:209
125
- #: classes/class-wpzerospam-blocked-ip-table.php:165
126
- #: classes/class-wpzerospam-log-table.php:289
127
- msgid "Delete All Entries"
128
- msgstr ""
129
-
130
- #: classes/class-wpzerospam-blocked-ip-table.php:20
131
- msgid "Blocked IP"
132
- msgstr ""
133
-
134
- #: classes/class-wpzerospam-blocked-ip-table.php:21 inc/admin.php:70
135
- #: inc/admin.php:601
136
- msgid "Blocked IPs"
137
- msgstr ""
138
-
139
- #: classes/class-wpzerospam-blocked-ip-table.php:33
140
- #: classes/class-wpzerospam-log-table.php:33
141
- #: classes/class-wpzerospam-log-table.php:163 inc/admin.php:255
142
- msgid "Type"
143
- msgstr ""
144
-
145
- #: classes/class-wpzerospam-blocked-ip-table.php:34
146
- msgid "Date Added"
147
- msgstr ""
148
-
149
- #: classes/class-wpzerospam-blocked-ip-table.php:35 inc/admin.php:266
150
- msgid "Start Date"
151
- msgstr ""
152
-
153
- #: classes/class-wpzerospam-blocked-ip-table.php:36 inc/admin.php:270
154
- msgid "End Date"
155
- msgstr ""
156
-
157
- #: classes/class-wpzerospam-blocked-ip-table.php:37
158
- #: classes/class-wpzerospam-log-table.php:216 inc/admin.php:262
159
- msgid "Reason"
160
- msgstr ""
161
-
162
- #: classes/class-wpzerospam-blocked-ip-table.php:67
163
- #: classes/class-wpzerospam-log-table.php:68
164
- msgid "Filter by type"
165
- msgstr ""
166
-
167
- #: classes/class-wpzerospam-blocked-ip-table.php:71
168
- #: classes/class-wpzerospam-log-table.php:73
169
- msgid "All types"
170
- msgstr ""
171
-
172
- #: classes/class-wpzerospam-blocked-ip-table.php:72
173
- #: classes/class-wpzerospam-blocked-ip-table.php:103 inc/admin.php:258
174
- msgid "Permanent"
175
- msgstr ""
176
-
177
- #: classes/class-wpzerospam-blocked-ip-table.php:73
178
- #: classes/class-wpzerospam-blocked-ip-table.php:106 inc/admin.php:257
179
- msgid "Temporary"
180
- msgstr ""
181
-
182
- #: classes/class-wpzerospam-blocked-ip-table.php:138
183
- msgid ""
184
- "Comment <span class=\"wpzerospam-small\">(<strong>permanently</strong> auto-"
185
- "blocked)</span>"
186
- msgstr ""
187
-
188
- #: classes/class-wpzerospam-blocked-ip-table.php:146
189
- msgid "Comment <span class=\"wpzerospam-small\">(auto-blocked)</span>"
190
- msgstr ""
191
-
192
- #: classes/class-wpzerospam-log-table.php:20
193
- msgid "Spam Detection"
194
- msgstr ""
195
-
196
- #: classes/class-wpzerospam-log-table.php:21 inc/admin.php:60 inc/admin.php:61
197
- #: inc/admin.php:638 templates/map.php:97
198
- msgid "Spam Detections"
199
- msgstr ""
200
-
201
- #: classes/class-wpzerospam-log-table.php:32
202
- #: classes/class-wpzerospam-log-table.php:158
203
- msgid "Date"
204
- msgstr ""
205
-
206
- #: classes/class-wpzerospam-log-table.php:35
207
- #: classes/class-wpzerospam-log-table.php:169
208
- msgid "Country"
209
- msgstr ""
210
-
211
- #: classes/class-wpzerospam-log-table.php:36
212
- #: classes/class-wpzerospam-log-table.php:176
213
- msgid "Region"
214
- msgstr ""
215
-
216
- #: classes/class-wpzerospam-log-table.php:37
217
- #: classes/class-wpzerospam-log-table.php:183
218
- msgid "City"
219
- msgstr ""
220
-
221
- #: classes/class-wpzerospam-log-table.php:39
222
- #: classes/class-wpzerospam-log-table.php:101 templates/ip-list.php:63
223
- msgid "Block IP"
224
- msgstr ""
225
-
226
- #: classes/class-wpzerospam-log-table.php:99 templates/ip-list.php:60
227
- msgid "Blocked"
228
- msgstr ""
229
-
230
- #: classes/class-wpzerospam-log-table.php:153
231
- msgid "Page URL"
232
- msgstr ""
233
-
234
- #: classes/class-wpzerospam-log-table.php:198
235
- msgid "Sanitized User Login"
236
- msgstr ""
237
-
238
- #: classes/class-wpzerospam-log-table.php:204
239
- msgid "User Email"
240
- msgstr ""
241
-
242
- #: classes/class-wpzerospam-log-table.php:210
243
- msgid "Errors"
244
- msgstr ""
245
-
246
- #: classes/class-wpzerospam-log-table.php:224
247
- msgid "Form Action"
248
- msgstr ""
249
-
250
- #: classes/class-wpzerospam-log-table.php:230
251
- msgid "Form ID"
252
- msgstr ""
253
-
254
- #: classes/class-wpzerospam-log-table.php:236
255
- msgid "Form Key"
256
- msgstr ""
257
-
258
- #: classes/class-wpzerospam-log-table.php:242
259
- msgid "Item Key"
260
- msgstr ""
261
-
262
- #: classes/class-wpzerospam-log-table.php:248
263
- msgid "Form Values"
264
- msgstr ""
265
-
266
- #: classes/class-wpzerospam-log-table.php:258
267
- msgid "Source"
268
- msgstr ""
269
-
270
- #: classes/class-wpzerospam-log-table.php:288
271
- msgid "Delete Selected"
272
- msgstr ""
273
-
274
- #: inc/admin.php:41 inc/admin.php:51
275
- msgid "WordPress Zero Spam Dashboard"
276
- msgstr ""
277
-
278
- #: inc/admin.php:42
279
- msgid "WP Zero Spam"
280
- msgstr ""
281
-
282
- #: inc/admin.php:52
283
- msgid "Dashboard"
284
- msgstr ""
285
-
286
- #: inc/admin.php:69
287
- msgid "Blocked IP Addresses"
288
- msgstr ""
289
-
290
- #: inc/admin.php:78 inc/admin.php:79
291
- msgid "Blacklisted IPs"
292
- msgstr ""
293
-
294
- #: inc/admin.php:87
295
- msgid "WordPress Zero Spam Settings"
296
- msgstr ""
297
-
298
- #: inc/admin.php:88 inc/admin.php:422
299
- msgid "Settings"
300
- msgstr ""
301
-
302
- #: inc/admin.php:118 inc/admin.php:147 inc/admin.php:293
303
- msgid "Search IPs"
304
- msgstr ""
305
-
306
- #: inc/admin.php:221
307
- msgid "Please enter a valid IP address."
308
- msgstr ""
309
-
310
- #: inc/admin.php:224
311
- msgid "Please select a valid type."
312
- msgstr ""
313
-
314
- #: inc/admin.php:227
315
- msgid "Please select a date & time when the temporary block should end."
316
- msgstr ""
317
-
318
- #: inc/admin.php:232 inc/admin.php:237
319
- msgid "Dismiss this notice."
320
- msgstr ""
321
-
322
- #: inc/admin.php:236
323
- msgid "The blocked IP has been successfully added."
324
- msgstr ""
325
-
326
- #: inc/admin.php:243 inc/admin.php:274
327
- msgid "Add Blocked IP"
328
- msgstr ""
329
-
330
- #: inc/admin.php:263
331
- msgid "e.g. Spammed form"
332
- msgstr ""
333
-
334
- #: inc/admin.php:267 inc/admin.php:271
335
- msgid "Optional"
336
- msgstr ""
337
-
338
- #: inc/admin.php:314
339
- msgid "Statistics"
340
- msgstr ""
341
-
342
- #: inc/admin.php:409 inc/admin.php:633 inc/utilities.php:115
343
- msgid ""
344
- "You have been blocked from visiting this site by WordPress Zero Spam due to "
345
- "detected spam activity."
346
- msgstr ""
347
-
348
- #: inc/admin.php:441
349
- msgid "General Settings"
350
- msgstr ""
351
-
352
- #: inc/admin.php:442
353
- msgid "Auto-block Settings"
354
- msgstr ""
355
-
356
- #: inc/admin.php:443
357
- msgid "On-site Spam Prevention"
358
- msgstr ""
359
-
360
- #: inc/admin.php:444
361
- msgid "Integrations & Third-party APIs"
362
- msgstr ""
363
-
364
- #: inc/admin.php:447
365
- msgid "Cookie Expiration"
366
- msgstr ""
367
-
368
- #: inc/admin.php:451
369
- msgid ""
370
- "Number of days until a user's cookie is expired. Helps boost site "
371
- "performance so access & blacklist checks aren't sent each page visit. "
372
- "<strong>Minimum recommend is 7 days</strong>."
373
- msgstr ""
374
-
375
- #: inc/admin.php:456
376
- msgid "days"
377
- msgstr ""
378
-
379
- #: inc/admin.php:460
380
- msgid "Share Spam Detections"
381
- msgstr ""
382
-
383
- #: inc/admin.php:465
384
- msgid ""
385
- "Help support WordPress Zero Spam and strenghten its ability to detect "
386
- "spammers by sharing spam detections. The only data that's shared is the IP "
387
- "address, type & site where the spam was detected. <strong>Absolutely no "
388
- "personal data is shared.</strong>."
389
- msgstr ""
390
-
391
- #: inc/admin.php:469 inc/admin.php:480 inc/admin.php:580 inc/admin.php:681
392
- #: inc/admin.php:692 inc/admin.php:705 inc/admin.php:717 inc/admin.php:730
393
- #: inc/admin.php:743 inc/admin.php:756 inc/admin.php:769
394
- #: integrations/comments/comments.php:25 integrations/comments/comments.php:36
395
- #: integrations/comments/comments.php:47
396
- msgid "Enabled"
397
- msgstr ""
398
-
399
- #: inc/admin.php:474
400
- msgid "Auto-block IPs"
401
- msgstr ""
402
-
403
- #: inc/admin.php:478
404
- msgid "Auto-blocks IPs addresses that trigger a spam detection."
405
- msgstr ""
406
-
407
- #: inc/admin.php:486
408
- msgid "Auto-block Period"
409
- msgstr ""
410
-
411
- #: inc/admin.php:489
412
- msgid ""
413
- "Number of minutes a user will be blocked from viewing the site after being "
414
- "auto-blocked."
415
- msgstr ""
416
-
417
- #: inc/admin.php:492
418
- msgid "minutes"
419
- msgstr ""
420
-
421
- #: inc/admin.php:497
422
- msgid "Permanently Auto-block"
423
- msgstr ""
424
-
425
- #: inc/admin.php:500
426
- msgid "Number of spam detections before an IP is permanently blocked."
427
- msgstr ""
428
-
429
- #: inc/admin.php:506
430
- msgid "API Timeout"
431
- msgstr ""
432
-
433
- #: inc/admin.php:510
434
- msgid ""
435
- "Number of seconds to allow an API to return a response.<br /><strong>WARNING:"
436
- "</strong> Setting this too high could cause your site to load slowly. "
437
- "Setting too low may not allow an API enough time to respond with a result. "
438
- "<strong>Recommended is 5 seconds.</strong>."
439
- msgstr ""
440
-
441
- #: inc/admin.php:515
442
- msgid "seconds"
443
- msgstr ""
444
-
445
- #: inc/admin.php:520
446
- msgid "ipstack API Key"
447
- msgstr ""
448
-
449
- #: inc/admin.php:523
450
- msgid "Enter your ipstack API key."
451
- msgstr ""
452
-
453
- #: inc/admin.php:527
454
- #, php-format
455
- msgid ""
456
- "Enter your <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
457
- "\">ipstack API key</a> to enable location-based statistics. Don't have an "
458
- "API key? <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
459
- "\"><strong>Get one for free!</strong></a>"
460
- msgstr ""
461
-
462
- #: inc/admin.php:537
463
- msgid "BotScout API Key"
464
- msgstr ""
465
-
466
- #: inc/admin.php:541
467
- msgid "Enter your free BotScout API key."
468
- msgstr ""
469
-
470
- #: inc/admin.php:544
471
- #, php-format
472
- msgid ""
473
- "Enter your BotScout API key to check user IPs against <a href=\"%s\" target="
474
- "\"_blank\" rel=\"noopener noreferrer\">BotScout</a>'s blacklist. Don't have "
475
- "an API key? <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
476
- "\"><strong>Get one for free!</strong></a>"
477
- msgstr ""
478
-
479
- #: inc/admin.php:553
480
- msgid "BotScout Count Minimum"
481
- msgstr ""
482
-
483
- #: inc/admin.php:558
484
- #, php-format
485
- msgid ""
486
- "Minimum <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer\">count</"
487
- "a> an IP must meet before being marked as spam/malicious.<br /"
488
- "><strong>WARNING:</strong> Setting this too low could cause users to be "
489
- "blocked that shouldn't be, <strong>recommended is 5</strong>."
490
- msgstr ""
491
-
492
- #: inc/admin.php:574
493
- #, php-format
494
- msgid ""
495
- "Checks user IPs against <a href=\"%s\" target=\"_blank\" rel=\"noopener "
496
- "noreferrer\">Stop Forum Spam</a>'s blacklist."
497
- msgstr ""
498
-
499
- #: inc/admin.php:585
500
- msgid "Stop Forum Spam Confidence Minimum"
501
- msgstr ""
502
-
503
- #: inc/admin.php:590
504
- #, php-format
505
- msgid ""
506
- "Minimum <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
507
- "\">confidence score</a> an IP must meet before being marked as spam/"
508
- "malicious.<br /><strong>WARNING:</strong> Setting this too low could cause "
509
- "users to be blocked that shouldn't be, <strong>recommended is 20%%</strong>."
510
- msgstr ""
511
-
512
- #: inc/admin.php:604
513
- msgid ""
514
- "Determines how blocked IPs are handled when they attempt to visit the site."
515
- msgstr ""
516
-
517
- #: inc/admin.php:606 inc/admin.php:643
518
- msgid "Redirect user"
519
- msgstr ""
520
-
521
- #: inc/admin.php:609 inc/admin.php:646
522
- #, php-format
523
- msgid ""
524
- "Display a <a href=\"%s\" target=\"_blank\" rel=\"noreferrer noopener"
525
- "\"><code>403 Forbidden</code></a> error"
526
- msgstr ""
527
-
528
- #: inc/admin.php:619
529
- msgid "Redirect for Blocked Users"
530
- msgstr ""
531
-
532
- #: inc/admin.php:623
533
- msgid "URL blocked users will be taken to."
534
- msgstr ""
535
-
536
- #: inc/admin.php:628
537
- msgid "Blocked Message"
538
- msgstr ""
539
-
540
- #: inc/admin.php:632
541
- msgid "The message that will be displayed to a blocked user."
542
- msgstr ""
543
-
544
- #: inc/admin.php:641
545
- msgid "Determines how users are handled when spam is detected."
546
- msgstr ""
547
-
548
- #: inc/admin.php:656
549
- msgid "Redirect for Spam"
550
- msgstr ""
551
-
552
- #: inc/admin.php:660
553
- msgid "URL users will be taken to when a spam submission is detected."
554
- msgstr ""
555
-
556
- #: inc/admin.php:665
557
- msgid "Spam Detection Message"
558
- msgstr ""
559
-
560
- #: inc/admin.php:669
561
- msgid "The message that will be displayed when spam is detected."
562
- msgstr ""
563
-
564
- #: inc/admin.php:670 inc/utilities.php:114
565
- msgid "There was a problem with your submission. Please go back and try again."
566
- msgstr ""
567
-
568
- #: inc/admin.php:675
569
- msgid "Log Blocked IPs"
570
- msgstr ""
571
-
572
- #: inc/admin.php:679
573
- msgid "Enables logging of when IPs are blocked from accessing the site."
574
- msgstr ""
575
-
576
- #: inc/admin.php:686
577
- msgid "Log Spam Detections"
578
- msgstr ""
579
-
580
- #: inc/admin.php:690
581
- msgid ""
582
- "Enables logging of spam detections and provides an admin interface to view "
583
- "statistics."
584
- msgstr ""
585
-
586
- #: inc/admin.php:699
587
- msgid "Verify Registrations"
588
- msgstr ""
589
-
590
- #: inc/admin.php:703
591
- msgid "Enables spam detection for site registrations."
592
- msgstr ""
593
-
594
- #: inc/admin.php:711
595
- msgid "Verify CF7 Submissions"
596
- msgstr ""
597
-
598
- #: inc/admin.php:715
599
- msgid "Enables spam detection for Contact Form 7 submissions."
600
- msgstr ""
601
-
602
- #: inc/admin.php:724
603
- msgid "Verify BuddyPress Registrations"
604
- msgstr ""
605
-
606
- #: inc/admin.php:728
607
- msgid "Enables spam detection for BuddyPress registrations."
608
- msgstr ""
609
-
610
- #: inc/admin.php:737
611
- msgid "Verify WPForms Submissions"
612
- msgstr ""
613
-
614
- #: inc/admin.php:741
615
- msgid "Enables spam detection for WPForms submissions.zero-spam"
616
- msgstr ""
617
-
618
- #: inc/admin.php:750
619
- msgid "Verify Fluent Form Submissions"
620
- msgstr ""
621
-
622
- #: inc/admin.php:754
623
- msgid "Enables spam detection for Fluent Form submissions."
624
- msgstr ""
625
-
626
- #: inc/admin.php:763
627
- msgid "Verify Formidable Form Submissions"
628
- msgstr ""
629
-
630
- #: inc/admin.php:767
631
- msgid "Enables spam detection for Formidable form submissions."
632
- msgstr ""
633
-
634
- #: inc/admin.php:775
635
- msgid "IP Whitelist"
636
- msgstr ""
637
-
638
- #: inc/admin.php:779
639
- msgid ""
640
- "Enter IPs that should be whitelisted (IPs that should never be blocked), one "
641
- "per line."
642
- msgstr ""
643
-
644
- #: inc/admin.php:780
645
- msgid "e.g. xxx.xxx.x.x"
646
- msgstr ""
647
-
648
- #: inc/utilities.php:324
649
- msgid "Access Blocked"
650
- msgstr ""
651
-
652
- #: integrations/buddypress/buddypress.php:13
653
- msgid "BuddyPress Registration"
654
- msgstr ""
655
-
656
- #: integrations/comments/comments.php:19
657
- msgid "Strip Comment Links"
658
- msgstr ""
659
-
660
- #: integrations/comments/comments.php:23
661
- msgid ""
662
- "Spambots commonly post spam links in comments. Enable this option to strip "
663
- "links from comments."
664
- msgstr ""
665
-
666
- #: integrations/comments/comments.php:30
667
- msgid "Strip Comment Author Links"
668
- msgstr ""
669
-
670
- #: integrations/comments/comments.php:34
671
- msgid ""
672
- "Spammers are well-known at injecting malicious links in the comment author "
673
- "website field, this option disables it."
674
- msgstr ""
675
-
676
- #: integrations/comments/comments.php:41
677
- msgid "Detect Comment Spam"
678
- msgstr ""
679
-
680
- #: integrations/comments/comments.php:45
681
- msgid "Enables comment form submission protection."
682
- msgstr ""
683
-
684
- #: integrations/comments/comments.php:93
685
- msgid "Comment Post"
686
- msgstr ""
687
-
688
- #: integrations/comments/comments.php:96 integrations/comments/comments.php:119
689
- msgid "Author"
690
- msgstr ""
691
-
692
- #: integrations/comments/comments.php:99 integrations/comments/comments.php:124
693
- msgid "Email"
694
- msgstr ""
695
-
696
- #: integrations/comments/comments.php:102
697
- #: integrations/comments/comments.php:129
698
- msgid "Website"
699
- msgstr ""
700
-
701
- #: integrations/comments/comments.php:105
702
- #: integrations/comments/comments.php:133
703
- #: integrations/comments/comments.php:286
704
- msgid "Comment"
705
- msgstr ""
706
-
707
- #: integrations/comments/comments.php:108
708
- msgid "Comment Type"
709
- msgstr ""
710
-
711
- #: integrations/comments/comments.php:111
712
- msgid "Comment Parent ID"
713
- msgstr ""
714
-
715
- #: integrations/comments/comments.php:136
716
- msgid "User IP"
717
- msgstr ""
718
-
719
- #: integrations/comments/comments.php:139
720
- msgid "User Agent"
721
- msgstr ""
722
-
723
- #: integrations/comments/comments.php:142
724
- msgid "Site"
725
- msgstr ""
726
-
727
- #: integrations/comments/comments.php:145
728
- msgid "Site Language"
729
- msgstr ""
730
-
731
- #: integrations/comments/comments.php:148
732
- msgid "Site Charset"
733
- msgstr ""
734
-
735
- #: integrations/comments/comments.php:151
736
- msgid "Permalink"
737
- msgstr ""
738
-
739
- #: integrations/comments/comments.php:159
740
- msgid "Akismet Result"
741
- msgstr ""
742
-
743
- #: integrations/comments/comments.php:162
744
- msgid "Akismet Pro Tip"
745
- msgstr ""
746
-
747
- #: integrations/contact-form-7/contact-form-7.php:13
748
- msgid "Contact Form 7"
749
- msgstr ""
750
-
751
- #: integrations/fluentform/fluentform.php:13
752
- msgid "Fluent Forms"
753
- msgstr ""
754
-
755
- #: integrations/formidable/formidable.php:15
756
- msgid "Formidable Forms"
757
- msgstr ""
758
-
759
- #: integrations/registrations/registrations.php:13
760
- msgid "Registration"
761
- msgstr ""
762
-
763
- #: integrations/wpforms/wpforms.php:13
764
- msgid "WPForms"
765
- msgstr ""
766
-
767
- #: templates/callout.php:15
768
- #, php-format
769
- msgid ""
770
- "Help support the <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
771
- "\">WordPress Zero Spam</a> plugin."
772
- msgstr ""
773
-
774
- #: templates/callout.php:24
775
- #, php-format
776
- msgid ""
777
- "Support the continued development of the WPZS by <a href=\"%s\" target="
778
- "\"_blank\" rel=\"noopener noreferrer\">donating today</a>. Donation goes "
779
- "towards the time it takes to develop new features &amp; updates, but also "
780
- "helps provide pro bono work for nonprofits. <a href=\"%s\" target=\"_blank\" "
781
- "rel=\"noopener noreferrer\">Learn more</a>."
782
- msgstr ""
783
-
784
- #: templates/callout.php:34
785
- #, php-format
786
- msgid ""
787
- "<strong>Integrate Zero Spam in any application. </strong> Use the <a href="
788
- "\"%s\" target=\"_blank\" rel=\"noopener noreferrer\">Zero Spam Blacklist "
789
- "API</a> to easily put a stop to spam on any site or application."
790
- msgstr ""
791
-
792
- #: templates/callout.php:42
793
- msgid "Submit Bug/Feature Request"
794
- msgstr ""
795
-
796
- #: templates/callout.php:43
797
- msgid "Follow us on Twitter"
798
- msgstr ""
799
-
800
- #: templates/callout.php:44
801
- msgid "Like us on Facebook"
802
- msgstr ""
803
-
804
- #: templates/callout.php:45
805
- msgid "Learn more about Zero Spam"
806
- msgstr ""
807
-
808
- #: templates/callout.php:46
809
- msgid "Fork on Github"
810
- msgstr ""
811
-
812
- #: templates/callout.php:47
813
- msgid "Show your Support &mdash; Donate"
814
- msgstr ""
815
-
816
- #: templates/callout.php:51
817
- msgid "Your IP Address:"
818
- msgstr ""
819
-
820
- #: templates/countries-pie-chart.php:12
821
- msgid "Most Spam by Country"
822
- msgstr ""
823
-
824
- #: templates/countries-pie-chart.php:67 templates/ip-list.php:71
825
- #: templates/regions-pie-chart.php:82 templates/spam-line-chart.php:58
826
- msgid "No data to report yet."
827
- msgstr ""
828
-
829
- #: templates/ip-list.php:12
830
- msgid "Most Spam by IP Address"
831
- msgstr ""
832
-
833
- #: templates/map.php:11
834
- msgid "Spam Detections World Map"
835
- msgstr ""
836
-
837
- #: templates/map.php:106
838
- #, php-format
839
- msgid ""
840
- "<strong>Enter your <a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer"
841
- "\">ipstack API Key</a></strong> to enable the world mp view of spam "
842
- "detections."
843
- msgstr ""
844
-
845
- #: templates/regions-pie-chart.php:12
846
- msgid "Most Spam by Region"
847
- msgstr ""
848
-
849
- #: templates/spam-line-chart.php:10
850
- msgid "Spam by Date"
851
- msgstr ""
852
-
853
- #. Plugin Name of the plugin/theme
854
- msgid "WordPress Zero Spam"
855
- msgstr ""
856
-
857
- #. Plugin URI of the plugin/theme
858
- msgid "https://benmarshall.me/wordpress-zero-spam"
859
- msgstr ""
860
-
861
- #. Description of the plugin/theme
862
- msgid ""
863
- "Tired of all the useless and bloated WordPress spam plugins? The WordPress "
864
- "Zero Spam plugin makes blocking spam a cinch. <strong>Just install, activate "
865
- "and say goodbye to spam.</strong> Based on work by <a href=\"http://"
866
- "davidwalsh.name/wordpress-comment-spam\" target=\"_blank\">David Walsh</a>."
867
- msgstr ""
868
-
869
- #. Author of the plugin/theme
870
- msgid "Ben Marshall"
871
- msgstr ""
872
-
873
- #. Author URI of the plugin/theme
874
- msgid "https://benmarshall.me"
875
- msgstr ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/class-botscout.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Botscout class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * BotScout.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class BotScout {
21
+ /**
22
+ * Botscout constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
29
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
30
+
31
+ add_filter( 'zerospam_access_checks', array( $this, 'access_check' ), 10, 3 );
32
+ }
33
+
34
+ /**
35
+ * Botscout access_check.
36
+ *
37
+ * @since 5.0.0
38
+ * @access public
39
+ */
40
+ public function access_check( $access_checks, $user_ip, $settings ) {
41
+ $access_checks['botscout'] = array(
42
+ 'blocked' => false,
43
+ );
44
+
45
+ if ( empty( $settings['botscout_api']['value'] ) ) {
46
+ return $access_checks;
47
+ }
48
+
49
+ $cache_key = ZeroSpam\Core\Utilities::cache_key(
50
+ array(
51
+ 'botscout',
52
+ $user_ip,
53
+ )
54
+ );
55
+
56
+ $result = wp_cache_get( $cache_key );
57
+ if ( false === $result ) {
58
+ $endpoint = 'https://botscout.com/test/?';
59
+ $params = array(
60
+ 'ip' => $user_ip,
61
+ 'key' => $settings['botscout_api']['value'],
62
+ );
63
+ $endpoint = $endpoint . http_build_query( $params );
64
+
65
+ $timeout = 5;
66
+ if ( ! empty( $settings['botscout_timeout'] ) ) {
67
+ $timeout = intval( $settings['botscout_timeout']['value'] );
68
+ }
69
+
70
+ $response = ZeroSpam\Core\Utilities::remote_get( $endpoint, array( 'timeout' => $timeout ) );
71
+
72
+
73
+ print_r($response);
74
+ }
75
+
76
+ return $access_checks;
77
+ }
78
+
79
+ /**
80
+ * Botscout sections.
81
+ *
82
+ * @since 5.0.0
83
+ * @access public
84
+ */
85
+ public function sections( $sections ) {
86
+ $sections['botscout'] = array(
87
+ 'title' => __( 'BotScout Integration', 'zerospam' ),
88
+ );
89
+
90
+ return $sections;
91
+ }
92
+
93
+ /**
94
+ * Botscout settings.
95
+ *
96
+ * @since 5.0.0
97
+ * @access public
98
+ */
99
+ public function settings( $settings ) {
100
+ $options = get_option( 'wpzerospam' );
101
+
102
+ $settings['botscout_api'] = array(
103
+ 'title' => __( 'BotScout API Key', 'zerospam' ),
104
+ 'section' => 'botscout',
105
+ 'type' => 'text',
106
+ 'field_class' => 'regular-text',
107
+ 'placeholder' => __( 'Enter your free BotScout API key.', 'zerospam' ),
108
+ 'desc' => sprintf(
109
+ wp_kses(
110
+ __( 'Enter your BotScout API key to check user IPs against <a href="%1$s" target="_blank" rel="noopener noreferrer">BotScout</a>\'s blacklist. Don\'t have an API key? <a href="%2$s" target="_blank" rel="noopener noreferrer"><strong>Get one for free!</strong></a>', 'zerospam' ),
111
+ array(
112
+ 'strong' => array(),
113
+ 'a' => array(
114
+ 'target' => array(),
115
+ 'href' => array(),
116
+ 'rel' => array(),
117
+ ),
118
+ )
119
+ ),
120
+ esc_url( 'https://botscout.com/#utm_source=wordpresszerospam&utm_medium=admin_link&utm_campaign=wordpresszerospam' ),
121
+ esc_url( 'https://botscout.com/getkey.htm#utm_source=wordpresszerospam&utm_medium=admin_link&utm_campaign=wordpresszerospam' )
122
+ ),
123
+ 'value' => ! empty( $options['botscout_api'] ) ? $options['botscout_api'] : false,
124
+ );
125
+
126
+ $settings['botscout_timeout'] = array(
127
+ 'title' => __( 'BotScout API Timeout', 'zerospam' ),
128
+ 'section' => 'botscout',
129
+ 'type' => 'number',
130
+ 'field_class' => 'small-text',
131
+ 'suffix' => __( 'seconds', 'zerospam' ),
132
+ 'placeholder' => __( '5', 'zerospam' ),
133
+ 'desc' => __( 'Recommended setting is 5 seconds. Setting to high could result in degraded site performance, too low won\'t allow to API enough time to respond.', 'zerospam' ),
134
+ 'value' => ! empty( $options['botscout_timeout'] ) ? $options['botscout_timeout'] : 5,
135
+ );
136
+
137
+ return $settings;
138
+ }
139
+ }
modules/class-google.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Google maps class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Google maps.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class Google {
21
+ /**
22
+ * Google maps constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
29
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
30
+
31
+ $settings = ZeroSpam\Core\Settings::get_settings();
32
+ if ( ! empty( $settings['google_api']['value'] ) ) {
33
+ add_action( 'zerospam_google_map', array( $this, 'map' ), 10, 2 );
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Embeds a map;
39
+ *
40
+ * @since 5.0.0
41
+ * @access public
42
+ */
43
+ public function map( $coordinates ) {
44
+ $settings = ZeroSpam\Core\Settings::get_settings();
45
+
46
+ if ( ! empty( $settings['google_api']['value'] ) ) {
47
+ $url = 'https://www.google.com/maps/embed/v1/place?';
48
+ $url_params = array(
49
+ 'key' => $settings['google_api']['value'],
50
+ 'q' => $coordinates,
51
+ );
52
+ $url .= http_build_query( $url_params );
53
+
54
+ $api_key
55
+ ?>
56
+ <iframe
57
+ width="100%"
58
+ height="200"
59
+ style="border:0"
60
+ loading="lazy"
61
+ allowfullscreen
62
+ src="<?php echo esc_url( $url ); ?>">
63
+ </iframe>
64
+ <?php
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Google maps sections.
70
+ *
71
+ * @since 5.0.0
72
+ * @access public
73
+ */
74
+ public function sections( $sections ) {
75
+ $sections['google'] = array(
76
+ 'title' => __( 'Google Integration', 'zerospam' ),
77
+ );
78
+
79
+ return $sections;
80
+ }
81
+
82
+ /**
83
+ * Botscout settings.
84
+ *
85
+ * @since 5.0.0
86
+ * @access public
87
+ */
88
+ public function settings( $settings ) {
89
+ $options = get_option( 'wpzerospam' );
90
+
91
+ $settings['google_api'] = array(
92
+ 'title' => __( 'Google API Key', 'zerospam' ),
93
+ 'section' => 'google',
94
+ 'type' => 'text',
95
+ 'field_class' => 'regular-text',
96
+ 'placeholder' => __( 'Enter your Google API key.', 'zerospam' ),
97
+ 'desc' => sprintf(
98
+ wp_kses(
99
+ __( 'Enter your <a href="%1$s" target="_blank" rel="noopener noreferrer">Google API key</a> for Google Maps integration.', 'zerospam' ),
100
+ array(
101
+ 'a' => array(
102
+ 'target' => array(),
103
+ 'href' => array(),
104
+ 'rel' => array(),
105
+ ),
106
+ )
107
+ ),
108
+ esc_url( 'https://developers.google.com/maps/documentation/embed/get-api-key' ),
109
+ ),
110
+ 'value' => ! empty( $options['google_api'] ) ? $options['google_api'] : false,
111
+ );
112
+
113
+ return $settings;
114
+ }
115
+ }
modules/class-ipstack.php ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * ipstack class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * ipstack.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class ipstack {
21
+ /**
22
+ * ipstack constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
29
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
30
+ add_filter( 'zerospam_log_record', array( $this, 'log_record' ) );
31
+ }
32
+
33
+ /**
34
+ * ipstack sections.
35
+ *
36
+ * @since 5.0.0
37
+ * @access public
38
+ */
39
+ public function sections( $sections ) {
40
+ $sections['ipstack'] = array(
41
+ 'title' => __( 'ipstack Integration', 'zerospam' ),
42
+ );
43
+
44
+ return $sections;
45
+ }
46
+
47
+ /**
48
+ * Botscout settings.
49
+ *
50
+ * @since 5.0.0
51
+ * @access public
52
+ */
53
+ public function settings( $settings ) {
54
+ $options = get_option( 'wpzerospam' );
55
+
56
+ $settings['ipstack_api'] = array(
57
+ 'title' => __( 'ipstack API Key', 'zerospam' ),
58
+ 'desc' => sprintf(
59
+ wp_kses(
60
+ __( 'Enter your <a href="%1$s" target="_blank" rel="noopener noreferrer">ipstack API key</a> to enable location-based statistics. Don\'t have an API key? <a href="%2$s" target="_blank" rel="noopener noreferrer"><strong>Get one for free!</strong></a>', 'zerospam' ),
61
+ array(
62
+ 'strong' => array(),
63
+ 'a' => array(
64
+ 'target' => array(),
65
+ 'href' => array(),
66
+ 'rel' => array(),
67
+ ),
68
+ )
69
+ ),
70
+ esc_url( 'https://ipstack.com/#utm_source=wordpresszerospam&utm_medium=admin_link&utm_campaign=wordpresszerospam' ),
71
+ esc_url( 'https://ipstack.com/signup/free#utm_source=wordpresszerospam&utm_medium=admin_link&utm_campaign=wordpresszerospam' )
72
+ ),
73
+ 'section' => 'ipstack',
74
+ 'type' => 'text',
75
+ 'field_class' => 'regular-text',
76
+ 'placeholder' => __( 'Enter your ipstack API key.', 'zerospam' ),
77
+ 'value' => ! empty( $options['ipstack_api'] ) ? $options['ipstack_api'] : false,
78
+ );
79
+
80
+ $settings['ipstack_timeout'] = array(
81
+ 'title' => __( 'ipstack API Timeout', 'zerospam' ),
82
+ 'section' => 'ipstack',
83
+ 'type' => 'number',
84
+ 'field_class' => 'small-text',
85
+ 'suffix' => __( 'seconds', 'zerospam' ),
86
+ 'placeholder' => __( '5', 'zerospam' ),
87
+ 'desc' => __( 'Recommended setting is 5 seconds. Setting to high could result in degraded site performance, too low won\'t allow to API enough time to respond.', 'zerospam' ),
88
+ 'value' => ! empty( $options['ipstack_timeout'] ) ? $options['ipstack_timeout'] : 5,
89
+ );
90
+
91
+ $settings['ipstack_cache'] = array(
92
+ 'title' => __( 'ipstack Cache Expiration', 'zerospam' ),
93
+ 'section' => 'ipstack',
94
+ 'type' => 'number',
95
+ 'field_class' => 'small-text',
96
+ 'suffix' => __( 'day(s)', 'zerospam' ),
97
+ 'placeholder' => __( '14', 'zerospam' ),
98
+ 'desc' => __( 'Recommended setting is 14 days. Setting to high could result in outdated information, too low could cause a decrease in performance.', 'zerospam' ),
99
+ 'value' => ! empty( $options['ipstack_cache'] ) ? $options['ipstack_cache'] : 14,
100
+ );
101
+
102
+ return $settings;
103
+ }
104
+
105
+ /**
106
+ * Log record filter.
107
+ *
108
+ * @since 5.0.0
109
+ * @access public
110
+ */
111
+ public static function log_record( $record ) {
112
+ $location = self::get_geolocation( ZeroSpam\Core\User::get_ip() );
113
+ if ( $location ) {
114
+ if ( ! empty( $location['country_code'] ) ) {
115
+ $record['country'] = $location['country_code'];
116
+ }
117
+
118
+ if ( ! empty( $location['country_name'] ) ) {
119
+ $record['country_name'] = $location['country_name'];
120
+ }
121
+
122
+ if ( ! empty( $location['region_code'] ) ) {
123
+ $record['region'] = $location['region_code'];
124
+ }
125
+
126
+ if ( ! empty( $location['region_name'] ) ) {
127
+ $record['region_name'] = $location['region_name'];
128
+ }
129
+
130
+ if ( ! empty( $location['city'] ) ) {
131
+ $record['city'] = $location['city'];
132
+ }
133
+
134
+ if ( ! empty( $location['latitude'] ) ) {
135
+ $record['latitude'] = $location['latitude'];
136
+ }
137
+
138
+ if ( ! empty( $location['longitude'] ) ) {
139
+ $record['longitude'] = $location['longitude'];
140
+ }
141
+
142
+ if ( ! empty( $location['zip'] ) ) {
143
+ $record['zip'] = $location['zip'];
144
+ }
145
+ }
146
+
147
+ return $record;
148
+ }
149
+
150
+ /**
151
+ * Get geolocation.
152
+ *
153
+ * @since 5.0.0
154
+ * @access public
155
+ */
156
+ public static function get_geolocation( $ip ) {
157
+ $settings = ZeroSpam\Core\Settings::get_settings();
158
+
159
+ if ( empty( $settings['ipstack_api']['value'] ) ) {
160
+ return false;
161
+ }
162
+
163
+ $cache_key = ZeroSpam\Core\Utilities::cache_key(
164
+ array(
165
+ 'ipstack',
166
+ $ip,
167
+ )
168
+ );
169
+
170
+ $result = wp_cache_get( $cache_key );
171
+ if ( false === $result ) {
172
+ $endpoint = 'http://api.ipstack.com/';
173
+ $endpoint .= $ip . '?access_key=' . $settings['ipstack_api']['value'];
174
+
175
+ $timeout = 5;
176
+ if ( ! empty( $settings['ipstack_timeout'] ) ) {
177
+ $timeout = intval( $settings['ipstack_timeout']['value'] );
178
+ }
179
+
180
+ $response = ZeroSpam\Core\Utilities::remote_get( $endpoint, array( 'timeout' => $timeout ) );
181
+ if ( $response ) {
182
+ $result = json_decode( $response, true );
183
+
184
+ $expiration = 14 * DAY_IN_SECONDS;
185
+ if ( ! empty( $settings['ipstack_cache']['value'] ) ) {
186
+ $expiration = $settings['ipstack_cache']['value'] * DAY_IN_SECONDS;
187
+ }
188
+ wp_cache_set( $cache_key, $result, 'zerospam', $expiration );
189
+ }
190
+ }
191
+
192
+ return $result;
193
+ }
194
+ }
modules/class-stopforumspam.php ADDED
@@ -0,0 +1,344 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Stop Forum Spam class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Stop Forum Spam.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class StopForumSpam {
21
+ /**
22
+ * Stop Forum Spam constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
29
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
30
+ add_filter( 'zerospam_access_checks', array( $this, 'access_check' ), 10, 3 );
31
+ add_filter( 'zerospam_registration_errors', array( $this, 'preprocess_registrations' ), 10, 3 );
32
+ add_filter( 'zerospam_preprocess_comment', array( $this, 'preprocess_comments' ), 10, 1 );
33
+ }
34
+
35
+ /**
36
+ * Stop Forum Spam sections.
37
+ *
38
+ * @since 5.0.0
39
+ * @access public
40
+ */
41
+ public function sections( $sections ) {
42
+ $sections['stop_forum_spam'] = array(
43
+ 'title' => __( 'Stop Forum Spam Integration', 'zerospam' ),
44
+ );
45
+
46
+ return $sections;
47
+ }
48
+
49
+ /**
50
+ * Stop Forum Spam settings.
51
+ *
52
+ * @since 5.0.0
53
+ * @access public
54
+ */
55
+ public function settings( $settings ) {
56
+ $options = get_option( 'wpzerospam' );
57
+
58
+ $settings['stop_forum_spam'] = array(
59
+ 'title' => __( 'Stop Forum Spam', 'zerospam' ),
60
+ 'section' => 'stop_forum_spam',
61
+ 'type' => 'checkbox',
62
+ 'options' => array(
63
+ 'enabled' => __( 'Enabled', 'zerospam' ),
64
+ ),
65
+ 'desc' => sprintf(
66
+ wp_kses(
67
+ __( 'Checks user IPs against <a href="%s" target="_blank" rel="noopener noreferrer">Stop Forum Spam</a>\'s blacklist.', 'zerospam' ),
68
+ array(
69
+ 'strong' => array(),
70
+ 'a' => array(
71
+ 'target' => array(),
72
+ 'href' => array(),
73
+ 'rel' => array(),
74
+ ),
75
+ )
76
+ ),
77
+ esc_url( 'https://www.stopforumspam.com/#utm_source=wordpresszerospam&utm_medium=admin_link&utm_campaign=wordpresszerospam' )
78
+ ),
79
+ 'value' => ! empty( $options['stop_forum_spam'] ) ? $options['stop_forum_spam'] : false,
80
+ );
81
+
82
+ $settings['stop_forum_spam_timeout'] = array(
83
+ 'title' => __( 'Stop Forum Spam API Timeout', 'zerospam' ),
84
+ 'section' => 'stop_forum_spam',
85
+ 'type' => 'number',
86
+ 'field_class' => 'small-text',
87
+ 'suffix' => __( 'seconds', 'zerospam' ),
88
+ 'placeholder' => __( '5', 'zerospam' ),
89
+ 'min' => 0,
90
+ 'desc' => __( 'Recommended setting is 5 seconds. Setting to high could result in degraded site performance, too low won\'t allow to API enough time to respond.', 'zerospam' ),
91
+ 'value' => ! empty( $options['stop_forum_spam_timeout'] ) ? $options['stop_forum_spam_timeout'] : 5,
92
+ );
93
+
94
+ $settings['stop_forum_spam_cache'] = array(
95
+ 'title' => __( 'Stop Forum Spam Cache Expiration', 'zerospam' ),
96
+ 'section' => 'stop_forum_spam',
97
+ 'type' => 'number',
98
+ 'field_class' => 'small-text',
99
+ 'suffix' => __( 'day(s)', 'zerospam' ),
100
+ 'placeholder' => __( WEEK_IN_SECONDS, 'zerospam' ),
101
+ 'min' => 0,
102
+ 'desc' => __( 'Recommended setting is 14 days. Setting to high could result in outdated information, too low could cause a decrease in performance.', 'zerospam' ),
103
+ 'value' => ! empty( $options['stop_forum_spam_cache'] ) ? $options['stop_forum_spam_cache'] : 14,
104
+ );
105
+
106
+ $settings['stop_forum_spam_confidence_min'] = array(
107
+ 'title' => __( 'Stop Forum Spam Confidence Minimum', 'zerospam' ),
108
+ 'section' => 'stop_forum_spam',
109
+ 'type' => 'number',
110
+ 'field_class' => 'small-text',
111
+ 'suffix' => __( '%', 'zerospam' ),
112
+ 'placeholder' => __( '30', 'zerospam' ),
113
+ 'min' => 0,
114
+ 'max' => 100,
115
+ 'step' => 0.1,
116
+ 'desc' => sprintf(
117
+ wp_kses(
118
+ __( 'Recommended setting is 20%%. Minimum <a href="%s" target="_blank" rel="noopener noreferrer">confidence score</a> an IP must meet before being blocked. Setting this too low could cause users to be blocked that shouldn\'t be.', 'zerospam' ),
119
+ array(
120
+ 'a' => array(
121
+ 'target' => array(),
122
+ 'href' => array(),
123
+ 'rel' => array(),
124
+ ),
125
+ )
126
+ ),
127
+ esc_url( 'https://www.stopforumspam.com/usage#utm_source=wordpresszerospam&utm_medium=admin_link&utm_campaign=wordpresszerospam' )
128
+ ),
129
+ 'value' => ! empty( $options['stop_forum_spam_confidence_min'] ) ? $options['stop_forum_spam_confidence_min'] : 30,
130
+ );
131
+
132
+ return $settings;
133
+ }
134
+
135
+ /**
136
+ * Processes comments.
137
+ *
138
+ * @since 5.0.0
139
+ * @access public
140
+ */
141
+ public function preprocess_comments( $commentdata ) {
142
+ $settings = ZeroSpam\Core\Settings::get_settings();
143
+
144
+ if ( empty( $settings['stop_forum_spam']['value'] ) || 'enabled' !== $settings['stop_forum_spam']['value'] ) {
145
+ return $errors;
146
+ }
147
+
148
+ $response = self::query(
149
+ array(
150
+ 'email' => $commentdata['comment_author_email'],
151
+ )
152
+ );
153
+ if ( $response ) {
154
+ $response = json_decode( $response, true );
155
+ if ( ! empty( $response['success'] ) && $response['success'] ) {
156
+
157
+ // Check email.
158
+ if (
159
+ ! empty( $response['email'] ) &&
160
+ ! empty( $response['email']['confidence'] ) &&
161
+ ! empty( $settings['stop_forum_spam_confidence_min']['value'] ) &&
162
+ floatval( $response['email']['confidence'] ) >= floatval( $settings['stop_forum_spam_confidence_min']['value'] )
163
+ ) {
164
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
165
+ if ( ! empty( $settings['registration_spam_message']['value'] ) ) {
166
+ $message = $settings['registration_spam_message']['value'];
167
+ }
168
+
169
+ if ( count( $errors->errors ) == 0 ) {
170
+ $errors->add( 'zerospam_error_stopformspam_email', __( $message, 'zerospam' ) );
171
+ }
172
+
173
+ $details = array(
174
+ 'user_login' => $sanitized_user_login,
175
+ 'user_email' => $user_email,
176
+ 'failed' => 'stop_forum_spam_email',
177
+ );
178
+ ZeroSpam\Includes\DB::log( 'registration', $details );
179
+ }
180
+ }
181
+ }
182
+
183
+
184
+ return $errors;
185
+ }
186
+
187
+ /**
188
+ * Processes registrations.
189
+ *
190
+ * @since 5.0.0
191
+ * @access public
192
+ */
193
+ public function preprocess_registrations( $errors, $sanitized_user_login, $user_email ) {
194
+ $settings = ZeroSpam\Core\Settings::get_settings();
195
+
196
+ if ( empty( $settings['stop_forum_spam']['value'] ) || 'enabled' !== $settings['stop_forum_spam']['value'] ) {
197
+ return $errors;
198
+ }
199
+
200
+ $response = self::query(
201
+ array(
202
+ 'username' => $sanitized_user_login,
203
+ 'email' => $user_email,
204
+ )
205
+ );
206
+ if ( $response ) {
207
+ $response = json_decode( $response, true );
208
+ if ( ! empty( $response['success'] ) && $response['success'] ) {
209
+ // Check username.
210
+ if (
211
+ ! empty( $response['username'] ) &&
212
+ ! empty( $response['username']['confidence'] ) &&
213
+ ! empty( $settings['stop_forum_spam_confidence_min']['value'] ) &&
214
+ floatval( $response['username']['confidence'] ) >= floatval( $settings['stop_forum_spam_confidence_min']['value'] )
215
+ ) {
216
+ $message = __( 'Your been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
217
+ if ( ! empty( $settings['registration_spam_message']['value'] ) ) {
218
+ $message = $settings['registration_spam_message']['value'];
219
+ }
220
+ $errors->add( 'zerospam_error_stopformspam_username', __( $message, 'zerospam' ) );
221
+
222
+ $details = array(
223
+ 'user_login' => $sanitized_user_login,
224
+ 'user_email' => $user_email,
225
+ 'failed' => 'stop_forum_spam_username',
226
+ );
227
+ ZeroSpam\Includes\DB::log( 'registration', $details );
228
+ }
229
+
230
+ // Check email.
231
+ if (
232
+ ! empty( $response['email'] ) &&
233
+ ! empty( $response['email']['confidence'] ) &&
234
+ ! empty( $settings['stop_forum_spam_confidence_min']['value'] ) &&
235
+ floatval( $response['email']['confidence'] ) >= floatval( $settings['stop_forum_spam_confidence_min']['value'] )
236
+ ) {
237
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
238
+ if ( ! empty( $settings['registration_spam_message']['value'] ) ) {
239
+ $message = $settings['registration_spam_message']['value'];
240
+ }
241
+
242
+ if ( count( $errors->errors ) == 0 ) {
243
+ $errors->add( 'zerospam_error_stopformspam_email', __( $message, 'zerospam' ) );
244
+ }
245
+
246
+ $details = array(
247
+ 'user_login' => $sanitized_user_login,
248
+ 'user_email' => $user_email,
249
+ 'failed' => 'stop_forum_spam_email',
250
+ );
251
+ ZeroSpam\Includes\DB::log( 'registration', $details );
252
+ }
253
+ }
254
+ }
255
+
256
+
257
+ return $errors;
258
+ }
259
+
260
+ /**
261
+ * Query the Stop Forum Spam API.
262
+ *
263
+ * @since 5.0.0
264
+ * @access public
265
+ */
266
+ public function query( $params ) {
267
+ $settings = ZeroSpam\Core\Settings::get_settings();
268
+
269
+ $cache_array = array( 'stop_forum_spam' );
270
+ $cache_array = array_merge( $cache_array, $params );
271
+ $cache_key = ZeroSpam\Core\Utilities::cache_key( $cache_array );
272
+
273
+ $response = wp_cache_get( $cache_key );
274
+ if ( false === $response ) {
275
+ $endpoint = 'https://api.stopforumspam.org/api?';
276
+ $params = array( 'json' => '' );
277
+ $params = array_merge( $cache_array, $params );
278
+ $endpoint = $endpoint . http_build_query( $params );
279
+
280
+ $timeout = 5;
281
+ if ( ! empty( $settings['stop_forum_spam_timeout'] ) ) {
282
+ $timeout = intval( $settings['stop_forum_spam_timeout']['value'] );
283
+ }
284
+
285
+ $response = ZeroSpam\Core\Utilities::remote_get( $endpoint, array( 'timeout' => $timeout ) );
286
+ if ( $response ) {
287
+ $expiration = 14 * DAY_IN_SECONDS;
288
+ if ( ! empty( $settings['stop_forum_spam_cache']['value'] ) ) {
289
+ $expiration = $settings['stop_forum_spam_cache']['value'] * DAY_IN_SECONDS;
290
+ }
291
+ wp_cache_set( $cache_key, $response, 'zerospam', $expiration );
292
+ }
293
+ }
294
+
295
+ return $response;
296
+ }
297
+
298
+ /**
299
+ * Stop Forum Spam access_check.
300
+ *
301
+ * @since 5.0.0
302
+ * @access public
303
+ */
304
+ public function access_check( $access_checks, $user_ip, $settings ) {
305
+ $access_checks['stop_forum_spam'] = array(
306
+ 'blocked' => false,
307
+ );
308
+
309
+ if ( empty( $settings['stop_forum_spam']['value'] ) || 'enabled' !== $settings['stop_forum_spam']['value'] ) {
310
+ return $access_checks;
311
+ }
312
+
313
+ $response = self::query( array( 'ip' => $user_ip ) );
314
+ if ( $response ) {
315
+ $response = json_decode( $response, true );
316
+ if (
317
+ ! empty( $response['success'] ) &&
318
+ $response['success'] &&
319
+ ! empty( $response['ip'] ) &&
320
+ ! empty( $response['ip']['appears'] )
321
+ ) {
322
+
323
+ $blacklisted = array(
324
+ 'user_ip' => $user_ip,
325
+ 'blacklist_service' => 'stop_forum_spam',
326
+ 'blacklist_data' => wp_json_encode( $response['ip'] ),
327
+ );
328
+ ZeroSpam\Includes\DB::blacklisted( $blacklisted );
329
+
330
+ if (
331
+ ! empty( $response['ip']['confidence'] ) &&
332
+ ! empty( $settings['stop_forum_spam_confidence_min']['value'] ) &&
333
+ floatval( $response['ip']['confidence'] ) >= floatval( $settings['stop_forum_spam_confidence_min']['value'] )
334
+ ) {
335
+ $access_checks['stop_forum_spam']['blocked'] = true;
336
+ $access_checks['stop_forum_spam']['type'] = 'blocked';
337
+ $access_checks['stop_forum_spam']['details'] = $response['ip'];
338
+ }
339
+ }
340
+ }
341
+
342
+ return $access_checks;
343
+ }
344
+ }
modules/class-zerospam.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Zero Spam class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Zero Spam.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class Zero_Spam {
21
+ /**
22
+ * Zero Spam constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+ add_action( 'zerospam_share_blocked', array( $this, 'share_blocked' ), 10, 1 );
29
+ }
30
+
31
+ /**
32
+ * Share blocked.
33
+ *
34
+ * @since 5.0.0
35
+ * @access public
36
+ */
37
+ public function share_blocked( $details ) {
38
+
39
+ }
40
+ }
modules/comments/class-comments.php ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Comments class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules\Comments;
9
+
10
+ use ZeroSpam;
11
+ use WP_Error;
12
+
13
+ // Security Note: Blocks direct access to the plugin PHP files.
14
+ defined( 'ABSPATH' ) || die();
15
+
16
+ /**
17
+ * Comments.
18
+ *
19
+ * @since 5.0.0
20
+ */
21
+ class Comments {
22
+ /**
23
+ * Comments constructor.
24
+ *
25
+ * @since 5.0.0
26
+ * @access public
27
+ */
28
+ public function __construct() {
29
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
30
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
31
+ add_filter( 'zerospam_types', array( $this, 'types' ), 10, 1 );
32
+
33
+ $settings = ZeroSpam\Core\Settings::get_settings();
34
+ if ( ! empty( $settings['verify_comments']['value'] ) && 'enabled' === $settings['verify_comments']['value'] ) {
35
+ add_filter( 'comment_form_defaults', array( $this, 'honeypot' ) );
36
+ add_action( 'preprocess_comment', array( $this, 'preprocess_comments' ) );
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Add to the types array.
42
+ *
43
+ * @since 5.0.0
44
+ * @access public
45
+ */
46
+ public function types( $types ) {
47
+ $types['comment'] = __( 'Comment', 'zerospam' );
48
+
49
+ return $types;
50
+ }
51
+
52
+ /**
53
+ * Preprocess comments.
54
+ *
55
+ * @since 5.0.0
56
+ * @access public
57
+ */
58
+ public function preprocess_comments( $commentdata ) {
59
+ $settings = ZeroSpam\Core\Settings::get_settings();
60
+ $honeypot = ZeroSpam\Core\Utilities::get_honeypot();
61
+
62
+ // Check honeypot.
63
+ if (
64
+ ! empty( $_REQUEST[ $honeypot ] )
65
+ ) {
66
+ if ( ! empty( $settings['log_blocked_comments']['value'] ) && 'enabled' === $settings['log_blocked_comments']['value'] ) {
67
+ $details = array(
68
+ 'failed' => 'honeypot',
69
+ );
70
+ $details = array_merge( $details, $commentdata );
71
+ ZeroSpam\Includes\DB::log( 'comment', $details );
72
+ }
73
+
74
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
75
+ if ( ! empty( $settings['comment_spam_message']['value'] ) ) {
76
+ $message = $settings['comment_spam_message']['value'];
77
+ }
78
+
79
+ wp_die(
80
+ $message,
81
+ __( 'Blocked by WordPress Zero Spam', 'zerospam' ),
82
+ array(
83
+ 'response' => 403,
84
+ )
85
+ );
86
+ }
87
+
88
+ return apply_filters( 'zerospam_preprocess_comment', $commentdata );
89
+ }
90
+
91
+ /**
92
+ * Add a 'honeypot' field to the comment form.
93
+ *
94
+ * @since 5.0.0
95
+ *
96
+ * @return array The default comment form arguments.
97
+ */
98
+ function honeypot( $defaults ) {
99
+ $defaults['fields']['wpzerospam_hp'] = ZeroSpam\Core\Utilities::honeypot_field();
100
+
101
+ return $defaults;
102
+ }
103
+
104
+ /**
105
+ * Comment sections.
106
+ *
107
+ * @since 5.0.0
108
+ * @access public
109
+ */
110
+ public function sections( $sections ) {
111
+ $sections['comments'] = array(
112
+ 'title' => __( 'Comment Settings', 'zerospam' ),
113
+ );
114
+
115
+ return $sections;
116
+ }
117
+
118
+ /**
119
+ * Botscout settings.
120
+ *
121
+ * @since 5.0.0
122
+ * @access public
123
+ */
124
+ public function settings( $settings ) {
125
+ $options = get_option( 'wpzerospam' );
126
+
127
+ $settings['verify_comments'] = array(
128
+ 'title' => __( 'Detect Spam/Malicious Comments', 'zerospam' ),
129
+ 'section' => 'comments',
130
+ 'type' => 'checkbox',
131
+ 'options' => array(
132
+ 'enabled' => __( 'Monitor comments for malicious or automated spambots.', 'zerospam' ),
133
+ ),
134
+ 'value' => ! empty( $options['verify_comments'] ) ? $options['verify_comments'] : false,
135
+ );
136
+
137
+ if ( ! empty( $options['verify_comments'] ) && 'enabled' === $options['verify_comments'] ) {
138
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
139
+ $settings['comment_spam_message'] = array(
140
+ 'title' => __( 'Comment Spam/Malicious Message', 'zerospam' ),
141
+ 'desc' => __( 'Displayed to the user when a comment is detected as spam/malicious.', 'zerospam' ),
142
+ 'section' => 'comments',
143
+ 'type' => 'text',
144
+ 'field_class' => 'large-text',
145
+ 'placeholder' => $message,
146
+ 'value' => ! empty( $options['comment_spam_message'] ) ? $options['comment_spam_message'] : $message,
147
+ );
148
+ }
149
+
150
+ $settings['log_blocked_comments'] = array(
151
+ 'title' => __( 'Log Blocked Comments', 'zerospam' ),
152
+ 'section' => 'comments',
153
+ 'type' => 'checkbox',
154
+ 'desc' => __( 'Enables logging blocked comments. High traffic sites should leave this disabled.', 'zerospam' ),
155
+ 'options' => array(
156
+ 'enabled' => __( 'Enabled', 'zerospam' ),
157
+ ),
158
+ 'value' => ! empty( $options['log_blocked_comments'] ) ? $options['log_blocked_comments'] : false,
159
+ );
160
+
161
+ return $settings;
162
+ }
163
+ }
modules/contactform7/class-contactform7.php ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Contact Form 7 class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules\ContactForm7;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Contact Form 7.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class ContactForm7 {
21
+ /**
22
+ * Registration constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
29
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
30
+ add_filter( 'zerospam_types', array( $this, 'types' ), 10, 1 );
31
+
32
+ $settings = ZeroSpam\Core\Settings::get_settings();
33
+ if ( ! empty( $settings['verify_contactform7']['value'] ) && 'enabled' === $settings['verify_contactform7']['value'] ) {
34
+ add_filter( 'wpcf7_form_elements', array( $this, 'honeypot' ), 10, 1 );
35
+ add_filter( 'wpcf7_validate', array( $this, 'preprocess_submission' ), 10, 2 );
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Add a 'honeypot' field to the form.
41
+ *
42
+ * @since 5.0.0
43
+ *
44
+ * @return string HTML to append to the form.
45
+ */
46
+ public function honeypot( $this_form_do_shortcode ) {
47
+ $this_form_do_shortcode .= ZeroSpam\Core\Utilities::honeypot_field();
48
+
49
+ return $this_form_do_shortcode;
50
+ }
51
+
52
+ /**
53
+ * Preprocess submission.
54
+ *
55
+ * @since 5.0.0
56
+ * @access public
57
+ */
58
+ public function preprocess_submission( $result, $tags ) {
59
+ $settings = ZeroSpam\Core\Settings::get_settings();
60
+ $honeypot = ZeroSpam\Core\Utilities::get_honeypot();
61
+
62
+ // Check honeypot.
63
+ if (
64
+ ! empty( $_REQUEST[ $honeypot ] )
65
+ ) {
66
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
67
+ if ( ! empty( $settings['contactform7_spam_message']['value'] ) ) {
68
+ $message = $settings['contactform7_spam_message']['value'];
69
+ }
70
+ $result->invalidate( $tags[0], $message );
71
+
72
+ if ( ! empty( $settings['log_blocked_contactform7']['value'] ) && 'enabled' === $settings['log_blocked_contactform7']['value'] ) {
73
+ $_REQUEST['failed'] = 'honeypot';
74
+ ZeroSpam\Includes\DB::log( 'contactform7', $_REQUEST );
75
+ }
76
+ }
77
+
78
+ return $result;
79
+ }
80
+
81
+ /**
82
+ * Add to the types array.
83
+ *
84
+ * @since 5.0.0
85
+ * @access public
86
+ */
87
+ public function types( $types ) {
88
+ $types['contactform7'] = __( 'Contact Form 7', 'zerospam' );
89
+
90
+ return $types;
91
+ }
92
+
93
+ /**
94
+ * Registration sections.
95
+ *
96
+ * @since 5.0.0
97
+ * @access public
98
+ */
99
+ public function sections( $sections ) {
100
+ $sections['contactform7'] = array(
101
+ 'title' => __( 'Contact Form 7', 'zerospam' ),
102
+ );
103
+
104
+ return $sections;
105
+ }
106
+
107
+ /**
108
+ * Botscout settings.
109
+ *
110
+ * @since 5.0.0
111
+ * @access public
112
+ */
113
+ public function settings( $settings ) {
114
+ $options = get_option( 'wpzerospam' );
115
+
116
+ $settings['verify_contactform7'] = array(
117
+ 'title' => __( 'Detect Contact Form 7 Submissions', 'zerospam' ),
118
+ 'section' => 'contactform7',
119
+ 'type' => 'checkbox',
120
+ 'options' => array(
121
+ 'enabled' => __( 'Monitor Contact Form 7 form submissions for malicious or automated spambots.', 'zerospam' ),
122
+ ),
123
+ 'value' => ! empty( $options['verify_contactform7'] ) ? $options['verify_contactform7'] : false,
124
+ );
125
+
126
+ if ( ! empty( $options['verify_contactform7'] ) && 'enabled' === $options['verify_contactform7'] ) {
127
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
128
+ $settings['contactform7_spam_message'] = array(
129
+ 'title' => __( 'Contact Form 7 Spam/Malicious Message', 'zerospam' ),
130
+ 'desc' => __( 'Displayed to the user when a submission is detected as spam/malicious.', 'zerospam' ),
131
+ 'section' => 'contactform7',
132
+ 'type' => 'text',
133
+ 'field_class' => 'large-text',
134
+ 'placeholder' => $message,
135
+ 'value' => ! empty( $options['contactform7_spam_message'] ) ? $options['contactform7_spam_message'] : $message,
136
+ );
137
+ }
138
+
139
+ $settings['log_blocked_contactform7'] = array(
140
+ 'title' => __( 'Log Blocked Contact Form 7 Submissions', 'zerospam' ),
141
+ 'section' => 'contactform7',
142
+ 'type' => 'checkbox',
143
+ 'desc' => __( 'Enables logging blocked Contact Form 7 submissions. High traffic sites should leave this disabled.', 'zerospam' ),
144
+ 'options' => array(
145
+ 'enabled' => __( 'Enabled', 'zerospam' ),
146
+ ),
147
+ 'value' => ! empty( $options['log_blocked_contactform7'] ) ? $options['log_blocked_contactform7'] : false,
148
+ );
149
+
150
+ return $settings;
151
+ }
152
+ }
modules/registration/class-registration.php ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Registration class.
4
+ *
5
+ * @package ZeroSpam
6
+ */
7
+
8
+ namespace ZeroSpam\Modules\Registration;
9
+
10
+ use ZeroSpam;
11
+
12
+ // Security Note: Blocks direct access to the plugin PHP files.
13
+ defined( 'ABSPATH' ) || die();
14
+
15
+ /**
16
+ * Registration.
17
+ *
18
+ * @since 5.0.0
19
+ */
20
+ class Registration {
21
+ /**
22
+ * Registration constructor.
23
+ *
24
+ * @since 5.0.0
25
+ * @access public
26
+ */
27
+ public function __construct() {
28
+
29
+ if ( get_option( 'users_can_register' ) ) {
30
+ add_filter( 'zerospam_setting_sections', array( $this, 'sections' ) );
31
+ add_filter( 'zerospam_settings', array( $this, 'settings' ) );
32
+ add_filter( 'zerospam_types', array( $this, 'types' ), 10, 1 );
33
+
34
+ $settings = ZeroSpam\Core\Settings::get_settings();
35
+ if ( ! empty( $settings['verify_registrations']['value'] ) && 'enabled' === $settings['verify_registrations']['value'] ) {
36
+ add_action( 'register_form', array( $this, 'honeypot' ) );
37
+ add_filter( 'registration_errors', array( $this, 'preprocess_registrations' ), 10, 3 );
38
+ }
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Add to the types array.
44
+ *
45
+ * @since 5.0.0
46
+ * @access public
47
+ */
48
+ public function types( $types ) {
49
+ $types['registration'] = __( 'Registration', 'zerospam' );
50
+
51
+ return $types;
52
+ }
53
+
54
+ /**
55
+ * Preprocess registrations.
56
+ *
57
+ * @since 5.0.0
58
+ * @access public
59
+ */
60
+ public function preprocess_registrations( $errors, $sanitized_user_login, $user_email ) {
61
+ $settings = ZeroSpam\Core\Settings::get_settings();
62
+ $honeypot = ZeroSpam\Core\Utilities::get_honeypot();
63
+
64
+ // Check honeypot.
65
+ if (
66
+ ! empty( $_REQUEST[ $honeypot ] )
67
+ ) {
68
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
69
+ if ( ! empty( $settings['registration_spam_message']['value'] ) ) {
70
+ $message = $settings['registration_spam_message']['value'];
71
+ }
72
+ $errors->add( 'zerospam_error', __( $message, 'zerospam' ) );
73
+
74
+ if ( ! empty( $settings['log_blocked_registrations']['value'] ) && 'enabled' === $settings['log_blocked_registrations']['value'] ) {
75
+ $details = array(
76
+ 'user_login' => $sanitized_user_login,
77
+ 'user_email' => $user_email,
78
+ 'failed' => 'honeypot',
79
+ );
80
+ ZeroSpam\Includes\DB::log( 'registration', $details );
81
+ }
82
+ }
83
+
84
+ $errors = apply_filters( 'zerospam_registration_errors', $errors, $sanitized_user_login, $user_email );
85
+
86
+ return $errors;
87
+ }
88
+
89
+ /**
90
+ * Add a 'honeypot' field to the registration form.
91
+ *
92
+ * @since 5.0.0
93
+ *
94
+ * @return string HTML to append to the registration form.
95
+ */
96
+ public function honeypot() {
97
+ echo ZeroSpam\Core\Utilities::honeypot_field();
98
+ }
99
+
100
+ /**
101
+ * Registration sections.
102
+ *
103
+ * @since 5.0.0
104
+ * @access public
105
+ */
106
+ public function sections( $sections ) {
107
+ $sections['registration'] = array(
108
+ 'title' => __( 'Registration Settings', 'zerospam' ),
109
+ );
110
+
111
+ return $sections;
112
+ }
113
+
114
+ /**
115
+ * Botscout settings.
116
+ *
117
+ * @since 5.0.0
118
+ * @access public
119
+ */
120
+ public function settings( $settings ) {
121
+ $options = get_option( 'wpzerospam' );
122
+
123
+ $settings['verify_registrations'] = array(
124
+ 'title' => __( 'Detect Spam/Malicious Registrations', 'zerospam' ),
125
+ 'section' => 'registration',
126
+ 'type' => 'checkbox',
127
+ 'options' => array(
128
+ 'enabled' => __( 'Monitor registrations for malicious or automated spambots.', 'zerospam' ),
129
+ ),
130
+ 'value' => ! empty( $options['verify_registrations'] ) ? $options['verify_registrations'] : false,
131
+ );
132
+
133
+ if ( ! empty( $options['verify_registrations'] ) && 'enabled' === $options['verify_registrations'] ) {
134
+ $message = __( 'You have been flagged as spam/malicious by WordPress Zero Spam.', 'zerospam' );
135
+ $settings['registration_spam_message'] = array(
136
+ 'title' => __( 'Registration Spam/Malicious Message', 'zerospam' ),
137
+ 'desc' => __( 'Displayed to the user when a registration is detected as spam/malicious.', 'zerospam' ),
138
+ 'section' => 'registration',
139
+ 'type' => 'text',
140
+ 'field_class' => 'large-text',
141
+ 'placeholder' => $message,
142
+ 'value' => ! empty( $options['registration_spam_message'] ) ? $options['registration_spam_message'] : $message,
143
+ );
144
+ }
145
+
146
+ $settings['log_blocked_registrations'] = array(
147
+ 'title' => __( 'Log Blocked Registrations', 'zerospam' ),
148
+ 'section' => 'registration',
149
+ 'type' => 'checkbox',
150
+ 'desc' => __( 'Enables logging blocked registrations. High traffic sites should leave this disabled.', 'zerospam' ),
151
+ 'options' => array(
152
+ 'enabled' => __( 'Enabled', 'zerospam' ),
153
+ ),
154
+ 'value' => ! empty( $options['log_blocked_registrations'] ) ? $options['log_blocked_registrations'] : false,
155
+ );
156
+
157
+ return $settings;
158
+ }
159
+ }
readme.txt CHANGED
@@ -1,11 +1,11 @@
1
  === WordPress Zero Spam ===
2
- Contributors: bmarshall511, jaredatch, EusebiuOprinoiu
3
  Tags: comments, spam, antispam, anti-spam, comment spam, spambot, spammer, spam free, spam blocker, registration spam
4
  Donate link: https://www.benmarshall.me/donate/?utm_source=wordpress_zero_spam&utm_medium=wordpress_repo&utm_campaign=donate
5
  Requires at least: 5.2
6
  Tested up to: 5.5
7
- Requires PHP: 7.1
8
- Stable tag: 4.10.2
9
  License: GNU GPLv3
10
  License URI: https://choosealicense.com/licenses/gpl-3.0/
11
 
@@ -13,38 +13,27 @@ WordPress Zero Spam makes blocking spam & malicious visitors a cinch. Just insta
13
 
14
  == Description ==
15
 
16
- Quit forcing users to answer silly questions, read confusing captchas, or take additional steps just to prove they're not spam. Stop malicious bots & hackers in their tracks before they ever have a chance to infiltrate your site &mdash; **introducing WordPress Zero Spam**.
17
 
18
- [WordPress Zero Spam](https://www.benmarshall.me/wordpress-zero-spam/?utm_source=wordpress.org&utm_medium=plugin&utm_campaign=wordpress_zero_spam) uses AI in combination with proven spam detection techniques and a database of known malicious IPs from around the world to detect and block unwanted visitors.
19
 
20
- In addition, it integrates with other popular plugins to provide all around protection. **Just install, activate, and enjoy a spam-free site!**
21
 
22
  = WordPress Zero Spam features =
23
 
24
- * **Blocks 99.9% of spam** submissions
25
- * **No captcha**, spam isn't a users' problem
26
- * **No moderation queues**, spam isn't a administrators' problem
27
- * **Multiple spam detection techniques**, including *honeypot*.
28
- * **Site security enhancements**, no config required
29
- * **Blocks malicious IPs** from ever seeing your site
30
- * **IP blacklist spam checks** ([Zero Spam](https://zerospam.org), [Stop Forum Spam](https://www.stopforumspam.com/), [BotScout](https://botscout.com/))
31
- * **Auto-block IPs** when a spam detection is triggered
32
- * **Manually block IPs** either temporarily or permanently
33
- * **Whitelist IPs** to avoid getting blocked
34
- * **Geolocate IP addresses** to see where spammers are coming from
35
- * **Detailed logging** to catch & block recurring spammers
36
- * **Charts &amp; statistics** for easy to understand spam analytics
37
- * **Advanced settings** for complete control over spammers
38
- * **Developer-friendly**, integrate with any theme, plugin or form
39
 
40
  = WordPress Zero Spam also protects =
41
 
42
  * WordPress core comments & user registrations
43
  * [Contact Form 7](https://wordpress.org/plugins/contact-form-7/) submissions
44
- * [BuddyPress](https://wordpress.org/plugins/buddypress/) registrations
45
- * [WPForms](https://wordpress.org/plugins/wpforms-lite/) submissions
46
- * [WP Fluent Form](https://wordpress.org/plugins/fluentform/) submissions
47
- * [Formidable Form Builder](https://wordpress.org/plugins/formidable/) submissions
48
  * and can be easily integrated into any existing theme or plugin
49
 
50
  WordPress Zero Spam is great at blocking spam &mdash; as a site owner there's more you can do to [stop WordPress spam](https://www.benmarshall.me/stop-wordpress-spam/) in its tracks.
@@ -53,16 +42,6 @@ WordPress Zero Spam is great at blocking spam &mdash; as a site owner there's mo
53
 
54
  We’ve integrated multi language support within the framework of our plugin, so you get a translated dashboard out of the box, and developer options to add even more languages. Contribute new languages via [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/zero-spam/).
55
 
56
- * [French](https://translate.wordpress.org/locale/fr/default/wp-plugins/zero-spam/) – (fr_FR)
57
- * [Italian](https://translate.wordpress.org/locale/it/default/wp-plugins/zero-spam/) – (it_IT)
58
-
59
- = Developer API =
60
-
61
- WordPress Zero Spam is free and open source. It’s the perfect solution to stopping spam and can be extended and integrated further. It was created and developed with the developer in mind, and we have already seen some truly remarkable addons already developed.
62
-
63
- To help you get started and learn just how to integrate with WordPress Zero Spam, visit the [plugin's documentation](https://www.benmarshall.me/wordpress-zero-spam/?utm_source=wordpress.org&utm_medium=plugin&utm_campaign=wordpress_zero_spam).
64
-
65
-
66
  = Help test & contribute to WordPress Zero Spam =
67
 
68
  WordPress Zero Spam can only stay up-to-date and bug-free with the help of contributors and testers. You can help test upcoming releases and contribute by forking the project on [GitHub](https://github.com/bmarshall511/wordpress-zero-spam).
@@ -85,54 +64,16 @@ WordPress Zero Spam can only stay up-to-date and bug-free with the help of contr
85
 
86
  1. Upload the entire wordpress-zero-spam folder to the */wp-content/plugins/* directory.
87
  2. Activate the plugin through the Plugins screen (*Plugins > Installed Plugins*).
88
- 3. Visit the plugin setting to configure as needed (*Settings > WP Zero Spam*).
89
 
90
  For more information & developer documentation, see the [plugin’s website](https://www.benmarshall.me/wordpress-zero-spam).
91
 
92
  == Frequently Asked Questions ==
93
 
94
- = Does WordPress Zero Spam check Ninja Forms submissions? =
95
-
96
- **No.** As of v4.10.0, WordPress Zero Spam no longer checks Ninja Form submissions. Support was dropped due to its [requirement of JavaScript](https://developer.ninjaforms.com/codex/loading-the-form-via-ajax/) and how it submits forms. JavaScript is one of the techniques WordPress Zero Spam uses to determine if a submission is spam. Ninja Forms employs a similar method and has its own [spam detection](https://ninjaforms.com/blog/spam-wordpress-form/) feature.
97
-
98
- **Does this mean WPZP won't do you any good? Absolutely not.** WPZS employs other techniques and IP blacklist checks that will help prevent malicious IP and spambots from ever seeing your site. You will still get all of the benefits of this plugin, it just won't provide the extra check on Ninja Form submissions.
99
-
100
- = Does WordPress Zero Spam check Gravity Form submissions? =
101
-
102
- **No.** As of v4.9.9, WordPress Zero Spam no longer checks Gravity Form submissions. Support was dropped due the numerous addon plugins that can be installed & alter GF submissions. These addons will often conflict with how WPZS validates submissions. In addition, Gravity Forms already has a spam detection option that works similar to how this plugin detects forms. You can enable it by going to the form settings and checking the *Enable anti-spam honeypot* option. For more information, see [Gravity Forms documentation](https://docs.gravityforms.com/form-settings/).
103
-
104
- **Does this mean WPZP won't do you any good? Absolutely not.** WPZS employs other techniques and IP blacklist checks that will help prevent malicious IP and spambots from ever seeing your site. You will still get all of the benefits of this plugin, it just won't provide the extra check on Gravity Form submissions.
105
-
106
  = Does WordPress Zero Spam check Jetpack comments? =
107
 
108
  **No.** WordPress Zero Spam is unable to integrate Jetpack. For more information, see [https://wordpress.org/support/topic/incompatible-with-jetpack-comments](https://wordpress.org/support/topic/incompatible-with-jetpack-comments).
109
 
110
- = Spam coments are still getting through, help! =
111
-
112
- WordPress Zero Spam relies on the default core form id (`#commentform`) in order to check comment submissions. Verify your comment forms have this ID or add the class `wpzerospam` to enable it on your site.
113
-
114
- = All registrations are marked as spam, help! =
115
-
116
- This is most likely due to a plugin or theme overriding the default markup of the registration form. Verify the form has an id of `registerform` or add the `wpzerospam` class to it.
117
-
118
- Example with `registerform` id:
119
-
120
- `<form name="registerform" id="registerform" action="https://yourdomain.local/login/?action=register" method="post" novalidate="novalidate">`
121
-
122
- Example with `wpzerospam` class:
123
-
124
- `<form name="registerform" class="wpzerospam" action="https://yourdomain.local/login/?action=register" method="post" novalidate="novalidate">`
125
-
126
- If you need help, please don't hesitate to [reach out](https://www.benmarshall.me/contact/?utm_source=wordpress.org&utm_medium=plugin&utm_campaign=wordpress_zero_spam).
127
-
128
- = How do I integrate this into another plugin or theme? =
129
-
130
- It's easy as adding the class `wpzerospam` to the `form` element, then adding a check in the form processor that the `wpzerospam_key` post value matches the option value in the database using the `wpzerospam_key_check()` helper function. See the [plugin's documentation](https://www.benmarshall.me/wordpress-zero-spam/?utm_source=wordpress.org&utm_medium=plugin&utm_campaign=wordpress_zero_spam) for more information on available hooks & functions.
131
-
132
- = Is JavaScript required to check form submissions? =
133
-
134
- Yes. One of the many techniques WordPress Zero Spam employs requires JavaScript be enabled to work properly.
135
-
136
  == Screenshots ==
137
 
138
  1. WordPress Zero Spam dashboard
@@ -143,240 +84,9 @@ Yes. One of the many techniques WordPress Zero Spam employs requires JavaScript
143
 
144
  == Changelog ==
145
 
146
- = 4.10.2 =
147
-
148
- * Added composer. See [#150](https://github.com/bmarshall511/wordpress-zero-spam/pull/150/).
149
- * Temporarily disabled sharing spam detection information.
150
-
151
- = 4.10.2 =
152
-
153
- * Update to comply with the GDPR/CCPA. See [#216](https://github.com/bmarshall511/wordpress-zero-spam/issues/216).
154
-
155
- = 4.10.1 =
156
-
157
- * Fix - PHP notice `Warning: array_merge(): Expected parameter 2 to be an array, bool given in /wp-content/plugins/zero-spam/classes/class-wordpress-zero-spam.php on line 532`. See https://wordpress.org/support/topic/warning-array_merge-14/#post-13319702.
158
- * Fix - StopForumSpam & BotScout being queried even if the options are disabled. See https://wordpress.org/support/topic/api-timeout/#post-13323551.
159
- * Enhancement - Moved the security functions into the new `WordPress_Zero_Spam_Security` class.
160
- * Enhancement - General code clean-up & documentation.
161
-
162
- = 4.10.0 =
163
-
164
- * Enhancement - Various performance improvements & caching added.
165
- * Enhancement - Easier to use class methods for integrating Zero Spam into any plugin or theme.
166
- * Enhancement - New `WordPress_Zero_Spam` class added.
167
- * Enhancement - IPs are now checked against known, safe hosts and user agents (i.e. search engine crawlers).
168
- * Enhancement - Added advanced debugging functionality.
169
-
170
- = 4.9.13 =
171
-
172
- * Fix - PHP notices for comment options [#209](https://github.com/bmarshall511/wordpress-zero-spam/issues/209)
173
-
174
- = 4.9.12 =
175
-
176
- * Enhancement - Added support for the French & Italian languages. See [#207](https://github.com/bmarshall511/wordpress-zero-spam/issues/207).
177
- * Enhancement - Strengthened spam detection for registrations using a 'honeypot' field.
178
- * Enhancement - Strengthened spam detection for Contact Form 7 using a 'honeypot' field.
179
- * Fix - Fix for Contact Form 7 protection not firing.
180
-
181
- = 4.9.11 =
182
-
183
- * Optimization - Converted the WPZS JS to be a jQuery plugin to initialize and manage easier.
184
- * Fix - Fix for WPZS failing when the Autoptimize plugin is set to aggregate JS files. See [#205](https://github.com/bmarshall511/wordpress-zero-spam/issues/205).
185
-
186
- = 4.9.10 =
187
-
188
- * Fix - Fix for PHP notice relating to an undefined variable, `strip_comment_links`. See [https://wordpress.org/support/topic/im-getting-this-after-latest-update/](https://wordpress.org/support/topic/im-getting-this-after-latest-update/).
189
-
190
- = 4.9.9 =
191
-
192
- * Enhancement - Strengthened spam detection for comment submission using a 'honeypot' field.
193
- * Enhancement - Added a 'honeypot' helper functions (`wpzerospam_honeypot_field()`, `wpzerospam_get_honeypot()`) to allow other forms, plugins, and themes to easily integrate a 'honeypot' check into submissions.
194
- * Enhancement - IP lookup links integrated in the admin dashboard and tables.
195
- * Deprecation - Gravity Forms is no longer supported &mdash; for the time being. See the plugin FAQs for more information.
196
-
197
- = 4.9.8 =
198
-
199
- * Fix - Fix for a reporting issue during detections.
200
-
201
- = 4.9.7 =
202
-
203
- * Enhancement - Added enhanced site security features (no configuration required)
204
- * Enhancement - Added plugin version to the information shared to Zero Spam (optional).
205
- * Optimization - Misc. code clean-up
206
-
207
- = 4.9.6 =
208
-
209
- * Fix - Gravity Forms not catching spam.
210
-
211
- = 4.9.5 =
212
-
213
- * Enhancement - Added the *BotScout Count Minimum* field in settings to allow sites to control when a BotScout result should be marked spam/malicious. See [BotScout's documentation](https://botscout.com/api.htm) for more information.
214
- * Enhancement - Improved performance by only querying the blacklist API once every X number of days (set in the admin settings).
215
- * Fix - Removed double slashes in some required PHP & JS paths.
216
-
217
- = 4.9.4 =
218
-
219
- * Fixed issued with BuddyPress checks not running.
220
-
221
- = 4.9.3 =
222
-
223
- * Added a confidence threshold for Stop Form Spam checks. See [#202](https://github.com/bmarshall511/wordpress-zero-spam/issues/202);
224
- * Added an API timeout field to adjust how long a response is allowed to take.
225
- * Restructred several functions which fixed some interment bugs users were experiencing.
226
- * Added ability to delete all entries from a table.
227
- * This update **will delete all exisiting blacklisted IPs** to ensure visitors aren't getting blocked when that shouldn't be.
228
-
229
- = 4.9.2 =
230
-
231
- * Removed Ninja Form submission support. See FAQs for more details.
232
- * Fix for PHP notice on the log page in the view details modal.
233
- * Fix for double slashes in the JS URLs.
234
- * Fix for BotScout not checking IPs
235
- * Fix for table not found notice on activation
236
-
237
- = 4.9.1 =
238
-
239
- Fix for PHP notice on the modals for spam detections
240
-
241
- = 4.9.0 =
242
-
243
- * Added support for [Formidable Forms](https://wordpress.org/plugins/formidable/). See [#112](https://github.com/bmarshall511/wordpress-zero-spam/issues/112).
244
- * Added additional country regions
245
- * UI enhancments to the admin tables
246
- * Added a spam detection world map to the dashboard
247
-
248
- = 4.8.2 =
249
-
250
- * Fix for admin table paging keeping set filters
251
-
252
- = 4.8.1 =
253
-
254
- * Fix for charts not showing up in the dashboard
255
-
256
- = 4.8.0 =
257
-
258
- * Added filter & seach options to admin tables
259
- * Various performance enhancements
260
- * Added ability to whitelist IP addresses
261
-
262
- = 4.7.1 =
263
-
264
- * Update to the WP Zero Spam API
265
-
266
- = 4.7.0 =
267
-
268
- * Various performance enhancements
269
- * Updates to the admin tables
270
- * Improved UI
271
- * Added functionality & option to permanently auto-block after X spam detections
272
- * Added ability to share spam detections with WordPress Zero Spam to strengthen it's ability to detect spammers
273
-
274
- = 4.6.0 =
275
-
276
- * Added option to strip links from comments
277
- * Added option to strip & disable the comment author website field
278
- * Added integration with the BotScout blacklist API
279
-
280
- = 4.5.0 =
281
-
282
- * Added integration with the Stop Forum Spam known spammy IPs
283
- * Fixed issue with Gravity Forms not being enabled by default
284
-
285
- = 4.4.1 =
286
-
287
- * Fix for Gravity Forms not submitting
288
-
289
- = 4.4.0 =
290
-
291
- * Misc. code clean-up
292
- * Added support for the Fluent Forms plugin
293
-
294
- = 4.3.10 =
295
-
296
- * Updated get_plugin_data calls to use a constant. See [#196](https://github.com/bmarshall511/wordpress-zero-spam/issues/196)
297
- * Added additional country regions for geolocation lookup
298
- * Renamed 'addons' to 'integrations'
299
- * Fixed issue with WPForm spam detections
300
- * Fix for plugin deactivation from v3 to v4 upgrade
301
-
302
- = 4.3.9 =
303
-
304
- * Fix for `Notice: Undefined index: verify_ninja_form`. See [#195](https://github.com/bmarshall511/wordpress-zero-spam/issues/195)
305
-
306
- = 4.3.8 =
307
-
308
- * Fix for `Call to undefined function wpzerospam_tables()` error
309
-
310
- = 4.3.7 =
311
-
312
- * Optimized scripts & when they get loaded (only when needed)
313
- * Fixed bug with incrementing spam detections in the blocked IPs log
314
-
315
- = 4.3.6 =
316
-
317
- * Added a check for the `is_plugin_active` functions to ensure they're available before calling it
318
-
319
- = 4.3.5 =
320
-
321
- * Fix for `Uncaught Error: Call to undefined function get_plugin_data()`. See [#193](https://github.com/bmarshall511/wordpress-zero-spam/issues/193).
322
-
323
- = 4.3.4 =
324
-
325
- * Fixed issue with adding/updating IP addresses manually
326
- * Fixed PHP notice for missing submission data on the log chart
327
- * Fixed PHP notice for "Undefined index: log_blocked_ips". See [#191](https://github.com/bmarshall511/wordpress-zero-spam/issues/191)
328
- * Updated the admin scripts to only login on the plugin admin pages
329
- * Fixed an issue with default add-on plugin options being disabled on first save. See [#192](https://github.com/bmarshall511/wordpress-zero-spam/issues/192)
330
-
331
- = 4.3.3 =
332
-
333
- * Fix for `REFERRER_ANALYTICS` unknown constant
334
-
335
- = 4.3.2 =
336
-
337
- * Fix for Gravity Forms PHP notice. See [#188](https://github.com/bmarshall511/wordpress-zero-spam/issues/188)
338
- * Add more stats & charts. See [#184](https://github.com/bmarshall511/wordpress-zero-spam/issues/184)
339
-
340
- = 4.3.1 =
341
-
342
- * Fixing plugin version
343
-
344
- = 4.3.0 =
345
-
346
- * Added the ability to manually add blocked IPs. See [#185](https://github.com/bmarshall511/wordpress-zero-spam/issues/185)
347
- * Fixed the ignored start & end date of blocked IPs
348
- * Added the ability to auto-block an IP when spam is detected. See [#185](https://github.com/bmarshall511/wordpress-zero-spam/issues/185)
349
- * Added raw data to spammer log table
350
- * Added the ability to uninstall options on Multisite. See [#187](https://github.com/bmarshall511/wordpress-zero-spam/pull/187)
351
-
352
- = 4.2.0 =
353
-
354
- * Re-implemented logging & added admin pages to prepare for charts & statistics. See [#181](https://github.com/bmarshall511/wordpress-zero-spam/issues/181)
355
-
356
- = 4.1.3 =
357
-
358
- * Fixed JS errors for some 3rd-party plugins. See [#178](https://github.com/bmarshall511/wordpress-zero-spam/issues/178)
359
- * Fixed caching conflicts issue relating to using cookies to set & get keys. See [#177](https://github.com/bmarshall511/wordpress-zero-spam/issues/177)
360
- * When plugin is uninstalled, plugin-related data is now deleted. See [#179](https://github.com/bmarshall511/wordpress-zero-spam/issues/179)
361
- * Added an option in the plugin settings to determine how spam detections are handled. See [#180](https://github.com/bmarshall511/wordpress-zero-spam/issues/180)
362
-
363
- = 4.1.2 =
364
-
365
- * Fixed issue with plugin settings not saving. See [#176](https://github.com/bmarshall511/wordpress-zero-spam/issues/176).
366
- * Fixed some PHP notices. See [#175](https://github.com/bmarshall511/wordpress-zero-spam/issues/175).
367
-
368
- = v4.1.1 =
369
-
370
- * Fixed missing JS for new 3rd-party plugin support
371
-
372
- = v4.1.0 =
373
-
374
- * Added support for [Contact Form 7](https://wordpress.org/plugins/contact-form-7/)
375
- * Added support for [Gravity Forms](https://www.gravityforms.com/)
376
- * Added support for [Ninja Forms](https://wordpress.org/plugins/ninja-forms/)
377
- * Added support for [BuddyPress](https://wordpress.org/plugins/buddypress/)
378
- * Added support for [Contact Form by WPForms](https://wordpress.org/plugins/wpforms-lite/)
379
-
380
- = v4.0.0 =
381
 
382
- * A complete rewrite of the original plugin
 
 
 
1
  === WordPress Zero Spam ===
2
+ Contributors: bmarshall511
3
  Tags: comments, spam, antispam, anti-spam, comment spam, spambot, spammer, spam free, spam blocker, registration spam
4
  Donate link: https://www.benmarshall.me/donate/?utm_source=wordpress_zero_spam&utm_medium=wordpress_repo&utm_campaign=donate
5
  Requires at least: 5.2
6
  Tested up to: 5.5
7
+ Requires PHP: 7.2
8
+ Stable tag: 5.0.0
9
  License: GNU GPLv3
10
  License URI: https://choosealicense.com/licenses/gpl-3.0/
11
 
13
 
14
  == Description ==
15
 
16
+ Quit forcing people to answer questions or confusing captchas to prove they're not spam. Stop malicious users before they ever have a chance to infiltrate your site &mdash; **introducing WordPress Zero Spam**.
17
 
18
+ [WordPress Zero Spam](https://www.benmarshall.me/wordpress-zero-spam/?utm_source=wordpress.org&utm_medium=plugin&utm_campaign=wordpress_zero_spam) uses AI in combination with proven spam detection techniques and databases of known malicious IPs from around the world to detect and block unwanted visitors.
19
 
20
+ **Just install, activate, and enjoy a spam-free site!**
21
 
22
  = WordPress Zero Spam features =
23
 
24
+ * Blocks 99.9% of spam &amp; malicious visitors
25
+ * No captcha, spam isn't a users' problem
26
+ * No moderation queues, spam isn't a administrators' problem
27
+ * IP blacklist spam checks ([Zero Spam](https://www.zerospam.org), [Stop Forum Spam](https://www.stopforumspam.com/))
28
+ * Automatically & manually block IPs temporarily or permanently
29
+ * Geolocate IP addresses to see where offenders are coming from
30
+ * Detailed logging to catch, investigate & block recurring offenders
31
+ * Developer-friendly, integrate with any theme, plugin or form
 
 
 
 
 
 
 
32
 
33
  = WordPress Zero Spam also protects =
34
 
35
  * WordPress core comments & user registrations
36
  * [Contact Form 7](https://wordpress.org/plugins/contact-form-7/) submissions
 
 
 
 
37
  * and can be easily integrated into any existing theme or plugin
38
 
39
  WordPress Zero Spam is great at blocking spam &mdash; as a site owner there's more you can do to [stop WordPress spam](https://www.benmarshall.me/stop-wordpress-spam/) in its tracks.
42
 
43
  We’ve integrated multi language support within the framework of our plugin, so you get a translated dashboard out of the box, and developer options to add even more languages. Contribute new languages via [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/zero-spam/).
44
 
 
 
 
 
 
 
 
 
 
 
45
  = Help test & contribute to WordPress Zero Spam =
46
 
47
  WordPress Zero Spam can only stay up-to-date and bug-free with the help of contributors and testers. You can help test upcoming releases and contribute by forking the project on [GitHub](https://github.com/bmarshall511/wordpress-zero-spam).
64
 
65
  1. Upload the entire wordpress-zero-spam folder to the */wp-content/plugins/* directory.
66
  2. Activate the plugin through the Plugins screen (*Plugins > Installed Plugins*).
67
+ 3. Visit the plugin setting to configure as needed (*Settings > Zero Spam*).
68
 
69
  For more information & developer documentation, see the [plugin’s website](https://www.benmarshall.me/wordpress-zero-spam).
70
 
71
  == Frequently Asked Questions ==
72
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  = Does WordPress Zero Spam check Jetpack comments? =
74
 
75
  **No.** WordPress Zero Spam is unable to integrate Jetpack. For more information, see [https://wordpress.org/support/topic/incompatible-with-jetpack-comments](https://wordpress.org/support/topic/incompatible-with-jetpack-comments).
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  == Screenshots ==
78
 
79
  1. WordPress Zero Spam dashboard
84
 
85
  == Changelog ==
86
 
87
+ = v5.0.0 =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
+ * Initial v5.0.0 release
90
+ * Huge performance enhancements
91
+ * More control over settings to fine-tune functionality
92
+ * Lots of bug fixes & improvements
templates/callout.php DELETED
@@ -1,51 +0,0 @@
1
- <?php
2
- /**
3
- * Plugin callout
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 4.6.0
7
- */
8
- ?>
9
-
10
- <div class="wpzerospam-callout">
11
- <div class="wpzerospam-callout-content">
12
- <h2><?php
13
- echo sprintf(
14
- wp_kses(
15
- __( 'Help support the <a href="%s" target="_blank" rel="noopener noreferrer">WordPress Zero Spam</a> plugin.', 'zero-spam' ),
16
- [ 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
17
- ),
18
- esc_url( 'https://benmarshall.me/wordpress-zero-spam/?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=donation' )
19
- );
20
- ?></h2>
21
- <p><?php
22
- echo sprintf(
23
- wp_kses(
24
- __( 'Support the continued development of the WPZS by <a href="%s" target="_blank" rel="noopener noreferrer">donating today</a>. Donation goes towards the time it takes to develop new features &amp; updates, but also helps provide pro bono work for nonprofits. <a href="%s" target="_blank" rel="noopener noreferrer">Learn more</a>.', 'zero-spam' ),
25
- [ 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
26
- ),
27
- esc_url( 'https://benmarshall.me/donate/?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=donation' ),
28
- esc_url( 'https://benmarshall.me/donate/?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=donation' )
29
- );
30
- ?></p>
31
- <p><?php
32
- echo sprintf(
33
- wp_kses(
34
- __( '<strong>Integrate Zero Spam in any application</strong> with the <a href="%s" target="_blank" rel="noopener noreferrer">Zero Spam Blacklist API</a>.', 'zero-spam' ),
35
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
36
- ),
37
- esc_url( 'https://zerospam.org/spam-blacklist-api/' )
38
- );
39
- ?></p>
40
- </div>
41
- <div class="wpzerospam-callout-actions">
42
- <a href="https://github.com/bmarshall511/wordpress-zero-spam/issues" class="button" target="_blank"><?php _e( 'Submit Bug/Feature Request', 'zero-spam' ); ?></a>
43
- <a href="https://twitter.com/ZeroSpamOrg" class="button" target="_blank"><?php _e( 'Follow us on Twitter', 'zero-spam' ); ?></a>
44
- <a href="https://www.facebook.com/zerospamorg/" class="button" target="_blank"><?php _e( 'Like us on Facebook', 'zero-spam' ); ?></a>
45
- <a href="https://zerospam.org/" class="button" target="_blank"><?php _e( 'Learn more about Zero Spam', 'zero-spam' ); ?></a>
46
- <a href="https://github.com/bmarshall511/wordpress-zero-spam" class="button" target="_blank"><?php _e( 'Fork on Github', 'zero-spam' ); ?></a>
47
- <a href="https://benmarshall.me/donate?utm_source=wordpress_zero_spam&utm_medium=settings_page&utm_campaign=admin" class="button button-primary" target="_blank"><?php _e( 'Show your Support &mdash; Donate', 'zero-spam' ); ?></a>
48
- </div>
49
- </div>
50
-
51
- <p><strong><?php _e( 'Your IP Address:', 'zero-spam' ); ?>:</strong> <code><?php echo wpzerospam_ip(); ?></code></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/countries-pie-chart.php DELETED
@@ -1,70 +0,0 @@
1
- <?php
2
- /**
3
- * Countries pie chart
4
- *
5
- * @package ReferrerAnalytics
6
- * @since 1.0.0
7
- */
8
-
9
- $chart_limit = 10;
10
- ?>
11
- <div class="wpzerospam-box wpzerospam-box-countries-pie">
12
- <h3><?php _e( 'Most Spam by Country', 'zero-spam' ); ?></h3>
13
- <div class="inside">
14
- <?php
15
- if ( $log ):
16
- $countries = [];
17
- foreach( $log as $key => $entry ):
18
-
19
- $k = wpzerospam_get_location( $entry->country );
20
- if ( empty( $countries[ $k ] ) ):
21
- $countries[ $k ] = 1;
22
- else:
23
- $countries[ $k ]++;
24
- endif;
25
- endforeach;
26
-
27
- if ( $countries ):
28
- arsort( $countries );
29
- endif;
30
- ?>
31
- <canvas id="wpzerospam-pie-countries"></canvas>
32
- <script>
33
- <?php
34
- $labels = [];
35
- $data = [];
36
- $count = 0;
37
- foreach( $countries as $key => $value ):
38
- if ( $count >= $chart_limit ): break; endif;
39
-
40
- $labels[] = $key;
41
- $data[] = $value;
42
- $colors[] = $predefined_colors[ $count ];
43
- $count++;
44
- endforeach;
45
- ?>
46
- var countries = document.getElementById('wpzerospam-pie-countries');
47
- var countriesAnalyticsPie = new Chart(countries, {
48
- type: 'pie',
49
- data: {
50
- labels: <?php echo json_encode( $labels ); ?>,
51
- datasets: [{
52
- data: <?php echo json_encode( $data ); ?>,
53
- backgroundColor: <?php echo json_encode( $colors ); ?>,
54
- borderWidth: 0,
55
- borderColor: '#f1f1f1'
56
- }],
57
- },
58
- options: {
59
- legend: {
60
- position: 'right',
61
- fullWidth: false
62
- }
63
- }
64
- });
65
- </script>
66
- <?php else: ?>
67
- <?php _e( 'No data to report yet.', 'zero-spam' ); ?>
68
- <?php endif; ?>
69
- </div>
70
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/ip-list.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
- /**
3
- * IP list
4
- *
5
- * @package ReferrerAnalytics
6
- * @since 1.0.0
7
- */
8
-
9
- $chart_limit = 20;
10
- ?>
11
- <div class="wpzerospam-box wpzerospam-box-ip-list">
12
- <h3><?php _e( 'Most Spam by IP Address', 'zero-spam' ); ?></h3>
13
- <div class="inside">
14
- <?php
15
- if ( $log ):
16
- $ips = [];
17
- foreach( $log as $key => $entry ):
18
- if ( empty( $ips[ $entry->user_ip ] ) ):
19
- $ips[ $entry->user_ip ] = [
20
- 'count' => 1,
21
- 'country' => $entry->country
22
- ];
23
- else:
24
- $ips[ $entry->user_ip ]['count']++;
25
- endif;
26
- endforeach;
27
-
28
- if ( $ips ):
29
- arsort( $ips );
30
- endif;
31
- ?>
32
- <ol class="wpzerospam-list">
33
- <?php
34
- $cnt = 0;
35
- foreach( $ips as $ip => $ary ):
36
- $cnt++;
37
- if ( $cnt > $chart_limit ) { break; }
38
- ?>
39
- <li class="wpzerospam-list-item">
40
- <span class="wpzerospam-list-cell wpzerospam-list-cell-icon">
41
- <?php if ( ! empty( $ary['country'] ) ): ?>
42
- <img class="wpzerospam-country-flag" width="16" src="https://hatscripts.github.io/circle-flags/flags/<?php echo strtolower( $ary['country'] ); ?>.svg" alt="<?php echo wpzerospam_get_location( $ary['country'] ); ?>" />
43
- <?php endif; ?>
44
- </span>
45
- <span class="wpzerospam-list-cell wpzerospam-list-cell-ip">
46
- <a href="https://zerospam.org/ip-lookup/<?php echo urlencode( $ip ); ?>" target="_blank" rel="noopener noreferrer"><strong><?php echo $ip; ?></strong></a>
47
- </span>
48
- <span class="wpzerospam-list-cell wpzerospam-list-cell-country<?php if ( empty( $ary['country'] ) ): ?> wpzerospam-list-cell-na<?php endif; ?>">
49
- <?php if ( ! empty( $ary['country'] ) ): ?>
50
- <?php echo wpzerospam_get_location( $ary['country'] ); ?>
51
- <?php else: ?>
52
- N/A
53
- <?php endif; ?>
54
- </span>
55
- <span class="wpzerospam-list-cell wpzerospam-list-cell-count">
56
- <?php echo number_format( $ary['count'], 0 ); ?>
57
- </span>
58
- <span class="wpzerospam-list-cell wpzerospam-list-cell-action">
59
- <?php if ( wpzerospam_is_blocked( $ip ) ): ?>
60
- <span class="wpzerospam-blocked"><?php _e( 'Blocked', 'zero-spam' ); ?></span>
61
- <?php else: ?>
62
- <a href="<?php echo admin_url( 'admin.php?page=wordpress-zero-spam-blocked-ips&ip=' . $ip ); ?>">
63
- <?php _e( 'Block IP', 'zero-spam' ); ?>
64
- </a>
65
- <?php endif; ?>
66
- </span>
67
- </li>
68
- <?php endforeach; ?>
69
- </ol>
70
- <?php else: ?>
71
- <?php _e( 'No data to report yet.', 'zero-spam' ); ?>
72
- <?php endif; ?>
73
- </div>
74
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/map.php DELETED
@@ -1,114 +0,0 @@
1
- <?php
2
- /**
3
- * World map
4
- *
5
- * @package WordPressZeroSpam
6
- * @since 1.0.0
7
- */
8
- $options = wpzerospam_options();
9
- ?>
10
- <div class="wpzerospam-box wpzerospam-box-map">
11
- <h3><?php _e( 'Spam Detections World Map', 'zero-spam' ); ?></h3>
12
- <div class="inside">
13
- <?php if ( ! empty( $options['ipstack_api'] ) ): ?>
14
- <?php
15
- $regions_data = [];
16
- $coords = [];
17
- $names = [];
18
- foreach( $log as $key => $entry ):
19
- if ( ! empty( $entry->latitude ) && ! empty( $entry->longitude ) ) {
20
- $coords[] = [ $entry->latitude, $entry->longitude ];
21
-
22
- $name = '';
23
-
24
- if ( ! empty( $entry->city ) ) {
25
- $name .= $entry->city;
26
- }
27
-
28
- if ( ! empty( $entry->region ) ) {
29
- if ( $name ) { $name .= ', '; }
30
- $name .= $entry->region;
31
- }
32
-
33
- if ( ! empty( $entry->country ) ) {
34
- if ( $name ) { $name .= ', '; }
35
- $name .= $entry->country;
36
- }
37
-
38
- $names[] = $name;
39
- }
40
-
41
- if ( ! empty( $entry->country ) ) {
42
-
43
- if ( array_key_exists( $entry->country, $regions_data ) ) {
44
- $regions_data[ $entry->country ]++;
45
- } else {
46
- $regions_data[ $entry->country ] = 1;
47
- }
48
-
49
- }
50
- endforeach;
51
-
52
- $regions_data = json_encode( $regions_data );
53
- $coords = json_encode( $coords );
54
- $names = json_encode( $names );
55
- ?>
56
- <div id="world-map" style="width: 100%; height: 490px"></div>
57
- <script>
58
- jQuery( function() {
59
- var regionsData = <?php echo $regions_data; ?>;
60
- var coords = <?php echo $coords; ?>;
61
- var names = <?php echo $names; ?>;
62
-
63
- jQuery( '#world-map' ).vectorMap({
64
- map: 'world_mill',
65
- backgroundColor: 'transparent',
66
- markers: coords,
67
- markerStyle: {
68
- initial: {
69
- fill: '#BE0000',
70
- stroke: '#000000',
71
- "fill-opacity": 1,
72
- "stroke-width": 1,
73
- "stroke-opacity": 0.5,
74
- r: 3
75
- },
76
- },
77
- regionStyle: {
78
- initial: {
79
- fill: 'transparent',
80
- "fill-opacity": 1,
81
- stroke: '#63000D',
82
- "stroke-width": 1,
83
- "stroke-opacity": 0.5
84
- },
85
- },
86
- series: {
87
- regions: [{
88
- values: regionsData,
89
- scale: ['#FFA17C','#63000D'],
90
- normalizeFunction: 'polynomial'
91
- }]
92
- },
93
- onMarkerTipShow: function(event, label, index){
94
- label.html( names[index] );
95
- },
96
- onRegionTipShow: function(e, el, code){
97
- el.html( el.html() + ' (<?php _e( 'Spam Detections', 'zero-spam' ); ?>: ' + regionsData[code] + ')' );
98
- }
99
- });
100
- });
101
- </script>
102
- <?php else: ?>
103
- <p><?php
104
- echo sprintf(
105
- wp_kses(
106
- __( '<strong>Enter your <a href="%s" target="_blank" rel="noopener noreferrer">ipstack API Key</a></strong> to enable the world mp view of spam detections.', 'zero-spam' ),
107
- [ 'strong' => [], 'a' => [ 'target' => [], 'href' => [], 'rel' => [] ] ]
108
- ),
109
- admin_url( 'admin.php?page=wordpress-zero-spam-settings' )
110
- );
111
- ?></p>
112
- <?php endif; ?>
113
- </div>
114
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/regions-pie-chart.php DELETED
@@ -1,85 +0,0 @@
1
- <?php
2
- /**
3
- * Regions pie chart
4
- *
5
- * @package ReferrerAnalytics
6
- * @since 1.0.0
7
- */
8
-
9
- $chart_limit = 10;
10
- ?>
11
- <div class="wpzerospam-box wpzerospam-box-countries-pie">
12
- <h3><?php _e( 'Most Spam by Region', 'zero-spam' ); ?></h3>
13
- <div class="inside">
14
- <?php
15
- if ( $log ):
16
- $regions = [];
17
- foreach( $log as $key => $entry ):
18
- $region = wpzerospam_get_location( $entry->country, $entry->region );
19
- if ( $region ) {
20
- if ( empty( $regions[ $region ] ) ) {
21
- $regions[ $region ] = 1;
22
- } else {
23
- $regions[ $region ]++;
24
- }
25
- } else {
26
- if ( empty( $entry->region ) ) {
27
- if ( empty( $regions['N/A'] ) ) {
28
- $regions['N/A'] = 1;
29
- } else {
30
- $regions['N/A']++;
31
- }
32
- } else {
33
- if ( empty( $regions[ $entry->region ] ) ) {
34
- $regions[ $entry->region ] = 1;
35
- } else {
36
- $regions[ $entry->region ]++;
37
- }
38
- }
39
- }
40
- endforeach;
41
-
42
- if ( $regions ):
43
- arsort( $regions );
44
- endif;
45
- ?>
46
- <canvas id="wpzerospam-pie-regions"></canvas>
47
- <script>
48
- <?php
49
- $labels = [];
50
- $data = [];
51
- $count = 0;
52
- foreach( $regions as $key => $value ):
53
- if ( $count >= $chart_limit ): break; endif;
54
-
55
- $labels[] = $key;
56
- $data[] = $value;
57
- $colors[] = $predefined_colors[ $count ];
58
- $count++;
59
- endforeach;
60
- ?>
61
- var regions = document.getElementById('wpzerospam-pie-regions');
62
- var regionsAnalyticsPie = new Chart(regions, {
63
- type: 'pie',
64
- data: {
65
- labels: <?php echo json_encode( $labels ); ?>,
66
- datasets: [{
67
- data: <?php echo json_encode( $data ); ?>,
68
- backgroundColor: <?php echo json_encode( $colors ); ?>,
69
- borderWidth: 0,
70
- borderColor: '#f1f1f1'
71
- }],
72
- },
73
- options: {
74
- legend: {
75
- position: 'right',
76
- fullWidth: false
77
- }
78
- }
79
- });
80
- </script>
81
- <?php else: ?>
82
- <?php _e( 'No data to report yet.', 'zero-spam' ); ?>
83
- <?php endif; ?>
84
- </div>
85
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/spam-line-chart.php DELETED
@@ -1,61 +0,0 @@
1
- <?php
2
- /**
3
- * Spam line chart
4
- *
5
- * @package ReferrerAnalytics
6
- * @since 1.0.0
7
- */
8
- ?>
9
- <div class="wpzerospam-box wpzerospam-box-line-chart">
10
- <h3><?php _e( 'Spam by Date', 'zero-spam' ); ?></h3>
11
- <div class="inside">
12
- <?php
13
- if ( $log ):
14
- ?>
15
- <canvas id="wpzerospam-line-chart"></canvas>
16
- <script>
17
- <?php
18
- $parsed = [];
19
- foreach( $log as $key => $entry ):
20
- $date_key = date( 'Y-m-d', strtotime( $entry->date_recorded ) );
21
- if ( empty( $parsed[ $date_key ] ) ) {
22
- $parsed[ $date_key ] = 1;
23
- } else {
24
- $parsed[ $date_key ]++;
25
- }
26
- endforeach;
27
-
28
- ksort( $parsed );
29
- $labels = [];
30
- $data = [];
31
- foreach( $parsed as $date => $count ) {
32
- $labels[] = date( 'M j, Y', strtotime( $date ) );
33
- $data[] = $count;
34
- }
35
- ?>
36
- var lineChart = document.getElementById('wpzerospam-line-chart');
37
- var lineChartAnalytics= new Chart(lineChart, {
38
- type: 'line',
39
- data: {
40
- labels: <?php echo json_encode( $labels ); ?>,
41
- datasets: [{
42
- data: <?php echo json_encode( $data ); ?>,
43
- backgroundColor: 'rgba(88, 0, 15, 0.5)',
44
- borderColor: '#63000D',
45
- borderWidth: 2,
46
- pointBorderWidth: 2,
47
- pointBackgroundColor: '#58000f',
48
- pointRadius: 2,
49
- fill: false,
50
- }],
51
- },
52
- options: {
53
- legend: false
54
- }
55
- });
56
- </script>
57
- <?php else: ?>
58
- <?php _e( 'No data to report yet.', 'zero-spam' ); ?>
59
- <?php endif; ?>
60
- </div>
61
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
uninstall.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Handles uninstalling the plugin
4
+ *
5
+ * @package WordPressZeroSpam
6
+ */
7
+
8
+ // Security Note: Blocks direct access to the plugin PHP files.
9
+ defined( 'ABSPATH' ) || die();
10
+
11
+ global $wpdb;
12
+
13
+ $tables = array(
14
+ 'log' => 'wpzerospam_log',
15
+ 'blocked' => 'wpzerospam_blocked',
16
+ 'blacklist' => 'wpzerospam_blacklist',
17
+ );
18
+
19
+ if ( is_multisite() ) {
20
+ $blogs = $wpdb->get_results( "SELECT blog_id FROM {$wpdb->blogs}", ARRAY_A );
21
+
22
+ if ( $blogs ) {
23
+ foreach ( $blogs as $blog ) {
24
+ switch_to_blog( $blog['blog_id'] );
25
+
26
+ delete_option( 'wpzerospam' );
27
+ delete_option( 'wpzerospam_honeypot' );
28
+ delete_option( 'zerospam_db_version' );
29
+
30
+ foreach ( $tables as $key => $table ) {
31
+ $wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->prefix . $table );
32
+ }
33
+ }
34
+ restore_current_blog();
35
+ }
36
+ } else {
37
+ delete_option( 'wpzerospam' );
38
+ delete_option( 'wpzerospam_honeypot' );
39
+ delete_option( 'zerospam_db_version' );
40
+
41
+ foreach ( $tables as $key => $table ) {
42
+ $wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->prefix . $table );
43
+ }
44
+ }
wordpress-zero-spam.php CHANGED
@@ -4,21 +4,21 @@
4
  *
5
  * @package WordPressZeroSpam
6
  * @subpackage WordPress
7
- * @since 4.0.0
8
  * @author Ben Marshall
9
- * @copyright 2020 Ben Marshall
10
  * @license GPL-2.0-or-later
11
  *
12
  * @wordpress-plugin
13
  * Plugin Name: WordPress Zero Spam
14
  * Plugin URI: https://benmarshall.me/wordpress-zero-spam
15
  * Description: Tired of all the useless and bloated WordPress spam plugins? The WordPress Zero Spam plugin makes blocking spam a cinch. <strong>Just install, activate and say goodbye to spam.</strong> Based on work by <a href="http://davidwalsh.name/wordpress-comment-spam" target="_blank">David Walsh</a>.
16
- * Version: 4.10.2
17
  * Requires at least: 5.2
18
  * Requires PHP: 7.2
19
  * Author: Ben Marshall
20
  * Author URI: https://benmarshall.me
21
- * Text Domain: zero-spam
22
  * Domain Path: /languages
23
  * License: GPL v2 or later
24
  * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
@@ -28,121 +28,62 @@
28
  defined( 'ABSPATH' ) || die();
29
 
30
  // Define plugin constants.
31
- define( 'WORDPRESS_ZERO_SPAM', __FILE__ );
32
- define( 'WORDPRESS_ZERO_SPAM_DB_VERSION', '0.6' );
33
- define( 'WORDPRESS_ZERO_SPAM_VERSION', '4.10.2' );
34
-
35
- /**
36
- * Include the WordPress Zero Spam plugin class.
37
- */
38
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'classes/class-wpzerospam.php';
39
-
40
- /**
41
- * Include the WordPress Zero Spam security class.
42
- */
43
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'classes/class-wpzerospam-security.php';
44
-
45
- /**
46
- * Include the WordPress Zero Spam comments class.
47
- */
48
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'classes/class-wpzerospam-comments.php';
49
-
50
- // Initialize the plugin.
51
- $wpzerospam = new WPZeroSpam();
52
- $wpzerospam_security = new WPZeroSpam_Security();
53
-
54
- // Fires the plugin WordPress hooks.
55
- $wpzerospam->initialize();
56
-
57
- /**
58
- * Install & upgrade functionality.
59
- */
60
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/install.php';
61
-
62
- /**
63
- * Uninstall functionality.
64
- */
65
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/uninstall.php';
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
- /**
77
- * Utility helper functions.
78
- */
79
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/utilities.php';
80
-
81
- /**
82
- * Helpers.
83
- */
84
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/helpers.php';
85
-
86
- /**
87
- * Plugin updates.
88
- */
89
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/updates.php';
90
 
91
  /**
92
- * Plugin CSS & JS scripts.
 
 
 
 
 
 
93
  */
94
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/scripts.php';
 
 
95
 
96
  /**
97
- * Admin interface & functionality.
 
 
 
 
 
 
98
  */
99
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'inc/admin.php';
 
 
 
 
 
100
 
101
  /**
102
- * Initializes the plugin.
103
  *
104
- * @since 4.9.12
 
 
105
  *
106
  * @return void
107
  */
108
- if ( ! function_exists( 'zero_spam_setup' ) ) {
109
- function zero_spam_setup() {
110
- /**
111
- * Include the WPZS core comments integration.
112
- */
113
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/comments/comments.php';
114
-
115
- /**
116
- * Include the WPZS core registration integration.
117
- */
118
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/registrations/registrations.php';
119
-
120
- /**
121
- * Include the WPZS Contact Form 7 integration if it's active.
122
- */
123
- if ( class_exists( 'WPCF7' ) ) {
124
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/contact-form-7/contact-form-7.php';
125
- }
126
- }
127
- }
128
- add_action( 'plugins_loaded', 'zero_spam_setup' );
129
-
130
-
131
-
132
-
133
-
134
- if ( wpzerospam_plugin_integration_enabled( 'bp_registrations' ) ) {
135
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/buddypress/buddypress.php';
136
- }
137
-
138
- if ( wpzerospam_plugin_integration_enabled( 'wpforms' ) ) {
139
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/wpforms/wpforms.php';
140
- }
141
-
142
- if ( wpzerospam_plugin_integration_enabled( 'fluentform' ) ) {
143
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/fluentform/fluentform.php';
144
- }
145
-
146
- if ( wpzerospam_plugin_integration_enabled( 'formidable' ) ) {
147
- require plugin_dir_path( WORDPRESS_ZERO_SPAM ) . 'integrations/formidable/formidable.php';
148
  }
4
  *
5
  * @package WordPressZeroSpam
6
  * @subpackage WordPress
7
+ * @since 5.0.0
8
  * @author Ben Marshall
9
+ * @copyright 2021 Ben Marshall
10
  * @license GPL-2.0-or-later
11
  *
12
  * @wordpress-plugin
13
  * Plugin Name: WordPress Zero Spam
14
  * Plugin URI: https://benmarshall.me/wordpress-zero-spam
15
  * Description: Tired of all the useless and bloated WordPress spam plugins? The WordPress Zero Spam plugin makes blocking spam a cinch. <strong>Just install, activate and say goodbye to spam.</strong> Based on work by <a href="http://davidwalsh.name/wordpress-comment-spam" target="_blank">David Walsh</a>.
16
+ * Version: 5.0.0
17
  * Requires at least: 5.2
18
  * Requires PHP: 7.2
19
  * Author: Ben Marshall
20
  * Author URI: https://benmarshall.me
21
+ * Text Domain: zerospam
22
  * Domain Path: /languages
23
  * License: GPL v2 or later
24
  * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
28
  defined( 'ABSPATH' ) || die();
29
 
30
  // Define plugin constants.
31
+ define( 'ZEROSPAM', __FILE__ );
32
+ define( 'ZEROSPAM_PATH', plugin_dir_path( ZEROSPAM ) );
33
+ define( 'ZEROSPAM_PLUGIN_BASE', plugin_basename( ZEROSPAM ) );
34
+ define( 'ZEROSPAM_VERSION', '5.0.0' );
35
+
36
+ add_action( 'plugins_loaded', 'zerospam_load_plugin_textdomain' );
37
+
38
+ if ( ! version_compare( PHP_VERSION, '7.2', '>=' ) ) {
39
+ add_action( 'admin_notices', 'zerospam_fail_php_version' );
40
+ } elseif ( ! version_compare( get_bloginfo( 'version' ), '5.2', '>=' ) ) {
41
+ add_action( 'admin_notices', 'zerospam_fail_wp_version' );
42
+ } else {
43
+ require ZEROSPAM_PATH . 'includes/class-plugin.php';
44
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
  /**
47
+ * Load Elementor textdomain.
48
+ *
49
+ * Load gettext translate for Elementor text domain.
50
+ *
51
+ * @since 1.0.0
52
+ *
53
+ * @return void
54
  */
55
+ function zerospam_load_plugin_textdomain() {
56
+ load_plugin_textdomain( 'zerospam' );
57
+ }
58
 
59
  /**
60
+ * WordPress Zero Spam admin notice for minimum PHP version.
61
+ *
62
+ * Warning when the site doesn't have the minimum required PHP version.
63
+ *
64
+ * @since 5.0.0
65
+ *
66
+ * @return void
67
  */
68
+ function zerospam_fail_php_version() {
69
+ /* translators: %s: PHP version */
70
+ $message = sprintf( esc_html__( 'WordPress Zero Spam requires PHP version %s+, plugin is currently NOT RUNNING.', 'zerospam' ), '7.2' );
71
+ $html_message = sprintf( '<div class="error">%s</div>', wpautop( $message ) );
72
+ echo wp_kses_post( $html_message );
73
+ }
74
 
75
  /**
76
+ * WordPress Zero Spam admin notice for minimum WordPress version.
77
  *
78
+ * Warning when the site doesn't have the minimum required WordPress version.
79
+ *
80
+ * @since 5.0.0
81
  *
82
  * @return void
83
  */
84
+ function zerospam_fail_wp_version() {
85
+ /* translators: %s: WordPress version */
86
+ $message = sprintf( esc_html__( 'WordPress Zero Spam requires WordPress version %s+. Because you are using an earlier version, the plugin is currently NOT RUNNING.', 'zerospam' ), '5.2' );
87
+ $html_message = sprintf( '<div class="error">%s</div>', wpautop( $message ) );
88
+ echo wp_kses_post( $html_message );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }