PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.8.5
Independent Analytics – WordPress Analytics Plugin v2.8.5
2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / javascript-source / controllers / real_time_controller.js
independent-analytics / javascript-source / controllers Last commit date
campaign_builder_controller.js 2 years ago chart_controller.js 1 year ago chart_geo_controller.js 2 years ago chart_interval_controller.js 2 years ago clipboard_controller.js 2 years ago copy_report_controller.js 2 years ago create_report_controller.js 2 years ago delete_data_controller.js 2 years ago delete_report_controller.js 2 years ago easepick_controller.js 2 years ago export_reports_controller.js 2 years ago filters_controller.js 1 year ago group_controller.js 2 years ago import_reports_controller.js 2 years ago migration_redirect_controller.js 2 years ago modal_controller.js 2 years ago plugin_group_options_controller.js 1 year ago pruner_controller.js 1 year ago quick_stats_controller.js 2 years ago real_time_controller.js 2 years ago rename_report_controller.js 2 years ago report_controller.js 1 year ago reset_analytics_controller.js 2 years ago save_report_controller.js 2 years ago select_input_controller.js 2 years ago set_favorite_report_controller.js 2 years ago sort_controller.js 2 years ago sortable_reports_controller.js 2 years ago table_columns_controller.js 2 years ago woocommerce_settings_controller.js 1 year ago
real_time_controller.js
312 lines
1 import {Controller} from "@hotwired/stimulus"
2 import {Chart, registerables} from 'chart.js'
3 import Isotope from "isotope-layout"
4 import htmlLegendPlugin from "../chart_plugins/html_legend_plugin";
5 import corsairPlugin from "../chart_plugins/corsair_plugin";
6
7 Chart.register(...registerables);
8 Chart.defaults.font.family = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';
9
10 export default class extends Controller {
11 static targets = [
12 'minuteChart',
13 'secondChart',
14 'visitorMessage',
15 'pageMessage',
16 'referrerMessage',
17 'countryMessage',
18 'pagesList',
19 'referrersList',
20 'countriesList',
21 'campaignsList',
22 'device_typesList'
23 ]
24 static values = {
25 chartData: Object,
26 nonce: String,
27 visible: {
28 type: Boolean,
29 default: true
30 }
31 }
32
33 connect() {
34 document.addEventListener('visibilitychange', this.tabVisibilityChanged)
35 this.initializeChart({
36 element: this.minuteChartTarget,
37 visitors: this.chartDataValue['minute_interval_visitors'],
38 views: this.chartDataValue['minute_interval_views'],
39 labelsShort: this.chartDataValue['minute_interval_labels_short'],
40 labelsFull: this.chartDataValue['minute_interval_labels_full']
41 })
42 this.initializeChart({
43 element: this.secondChartTarget,
44 visitors: this.chartDataValue['second_interval_visitors'],
45 views: this.chartDataValue['second_interval_views'],
46 labelsShort: this.chartDataValue['second_interval_labels_short'],
47 labelsFull: this.chartDataValue['second_interval_labels_full']
48 })
49 this.startServerPolling()
50 }
51
52 disconnect() {
53 document.removeEventListener('visibilitychange', this.tabVisibilityChanged)
54 }
55
56 tabVisibilityChanged = () => {
57 this.visibleValue = !document.hidden
58
59 if (this.visibleValue) {
60 this.refresh()
61 this.startServerPolling()
62 }
63 }
64
65 startServerPolling() {
66 clearInterval(this.interval)
67 this.interval = setInterval(() => {
68 if (this.visibleValue) {
69 this.refresh()
70 } else {
71 clearInterval(this.interval)
72 }
73 }, 10000)
74 }
75
76 refresh() {
77 const data = {
78 ...iawpActions.real_time_data
79 };
80
81 jQuery.post(ajaxurl, data, (response, a, b) => {
82 document.getElementById('real-time-dashboard').classList.remove('refreshed')
83 void document.getElementById('real-time-dashboard').offsetWidth
84 document.getElementById('real-time-dashboard').classList.add('refreshed')
85 if (response.success) {
86 this.rerender(response.data)
87 }
88 });
89 }
90
91 rerender(data) {
92 this.visitorMessageTarget.textContent = data.visitor_message
93 this.pageMessageTarget.textContent = data.page_message
94 this.referrerMessageTarget.textContent = data.referrer_message
95 this.countryMessageTarget.textContent = data.country_message
96 this.updateChart({
97 element: this.minuteChartTarget,
98 visitors: data.chart_data.minute_interval_visitors,
99 views: data.chart_data.minute_interval_views,
100 })
101 this.updateChart({
102 element: this.secondChartTarget,
103 visitors: data.chart_data.second_interval_visitors,
104 views: data.chart_data.second_interval_views
105 })
106 this.updateList({
107 element: this.pagesListTarget,
108 entries: data.lists.pages.entries
109 })
110 this.updateList({
111 element: this.referrersListTarget,
112 entries: data.lists.referrers.entries
113 })
114 this.updateList({
115 element: this.countriesListTarget,
116 entries: data.lists.countries.entries
117 })
118 this.updateList({
119 element: this.campaignsListTarget,
120 entries: data.lists.campaigns.entries
121 })
122 this.updateList({
123 element: this.device_typesListTarget,
124 entries: data.lists.device_types.entries
125 })
126 }
127
128 existingElements() {
129 return Array.from(this.element.querySelectorAll('li:not(.exiting)'))
130 }
131
132 existingIds() {
133 return this.existingElements().map((existingElement) => existingElement.dataset.id)
134 }
135
136 updateList({
137 element,
138 entries
139 }) {
140 let iso = element.iso
141
142 if (!iso) {
143 iso = element.iso = new Isotope(element, {
144 itemSelector: "li",
145 layoutMode: "vertical",
146 sortColumn: "position",
147 getSortData: {
148 position: (element) => {
149 return parseInt(element.dataset.position);
150 }
151 }
152 })
153 }
154
155 // Remove any that are no longer around and update existing ones
156 this.existingElements().forEach((existingElement) => {
157 const existingId = existingElement.dataset.id
158 const match = entries.find((entry) => entry['id'] === existingId)
159
160 if (match) {
161 existingElement.dataset.position = match['position']
162 existingElement.querySelector('.real-time-position').textContent = match['position'] + '.'
163 existingElement.querySelector('.real-time-stat').textContent = match['views']
164 } else {
165 iso.remove(existingElement)
166 }
167 })
168
169 // Add new ones
170 entries.forEach((entry, index) => {
171 if (!this.existingIds().includes(entry['id'])) {
172 const entryElement = this.elementFromEntry(entry)
173 iso.insert(entryElement)
174 }
175 })
176
177 iso.updateSortData()
178 iso.arrange()
179
180 const totalElements = iso.items.length
181 const emptyMessage = element.parentNode.querySelector('.most-popular-empty-message')
182
183 if (totalElements > 0) {
184 emptyMessage.classList.add('hide')
185 } else {
186 emptyMessage.classList.remove('hide')
187 }
188 }
189
190 elementFromEntry(entry) {
191 const id = entry['id']
192 const position = entry['position']
193 const title = entry['title']
194 const subtitle = entry['subtitle']
195 const views = entry['views']
196 const flag = entry['flag'] ? entry['flag'] : ''
197 const subtitleHTML = subtitle ? `<span class="real-time-subtitle">${subtitle}</span>` : '';
198
199 const li = `
200 <li data-id="${id}" data-position="${position}">
201 <span class="real-time-position">${position}.</span>
202 ${flag}
203 <span class="real-time-resource">${title} ${subtitleHTML}</span>
204 <span class="real-time-stat">${views}</span>
205 </li>
206 `
207 const el = document.createElement('div');
208 el.innerHTML = li;
209 return el.firstElementChild;
210 }
211
212 initializeChart({
213 element,
214 visitors,
215 views,
216 labelsShort,
217 labelsFull
218 }) {
219 const data = {
220 labels: labelsFull,
221 datasets: [{
222 id: 'views',
223 label: iawpText.views,
224 data: views,
225 backgroundColor: 'rgba(108,70,174,0.2)',
226 borderColor: 'rgba(108,70,174,1)',
227 borderWidth: {
228 bottom: 0,
229 top: 3,
230 left: 0,
231 right: 0
232 }
233 },]
234 }
235
236 const config = {
237 type: 'bar',
238 data,
239 options: {
240 interaction: {
241 intersect: false,
242 mode: 'index'
243 },
244 responsive: true,
245 scales: {
246 y: {
247 grid: {
248 borderColor: '#DEDAE6',
249 tickColor: '#DEDAE6',
250 display: true,
251 drawOnChartArea: true,
252 borderDash: [2, 4]
253 },
254 beginAtZero: true,
255 suggestedMax: 5,
256 ticks: {
257 color: document.body.classList.contains('iawp-dark-mode') ? '#ffffff' : '#6D6A73',
258 precision: 0
259 }
260 },
261 x: {
262 stacked: true,
263 grid: {
264 borderColor: '#DEDAE6',
265 tickColor: '#DEDAE6',
266 display: true,
267 drawOnChartArea: false,
268 },
269 ticks: {
270 color: document.body.classList.contains('iawp-dark-mode') ? '#ffffff' : '#6D6A73',
271 beginAtZero: true,
272 callback: function (value, index, values) {
273 return labelsShort[index];
274 }
275 }
276 }
277 },
278 plugins: {
279 mode: String, // 'light' or 'dark'
280 htmlLegend: {
281 container: element.parentNode.querySelector('.legend')
282 },
283 legend: {
284 display: false
285 },
286 corsair: {
287 dash: [2, 4],
288 color: '#777',
289 width: 1
290 },
291 },
292 },
293 plugins: [
294 corsairPlugin,
295 htmlLegendPlugin
296 ],
297 };
298
299 new Chart(element, config);
300 }
301
302 updateChart({
303 element,
304 views
305 }) {
306 const chart = Chart.getChart(element);
307 chart.data.datasets.forEach((dataset, index) => {
308 dataset.data.splice(0, dataset.data.length, ...views)
309 });
310 chart.update('none');
311 }
312 }