PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.10.1
Independent Analytics – WordPress Analytics Plugin v2.10.1
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 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 1 year ago chart_controller.js 1 year ago chart_geo_controller.js 2 years ago chart_interval_controller.js 2 years ago clipboard_controller.js 1 year 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 1 year 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
309 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 views: this.chartDataValue['minute_interval_views'],
38 labelsShort: this.chartDataValue['minute_interval_labels_short'],
39 labelsFull: this.chartDataValue['minute_interval_labels_full']
40 })
41 this.initializeChart({
42 element: this.secondChartTarget,
43 views: this.chartDataValue['second_interval_views'],
44 labelsShort: this.chartDataValue['second_interval_labels_short'],
45 labelsFull: this.chartDataValue['second_interval_labels_full']
46 })
47 this.startServerPolling()
48 }
49
50 disconnect() {
51 document.removeEventListener('visibilitychange', this.tabVisibilityChanged)
52 }
53
54 tabVisibilityChanged = () => {
55 this.visibleValue = !document.hidden
56
57 if (this.visibleValue) {
58 this.refresh()
59 this.startServerPolling()
60 }
61 }
62
63 startServerPolling() {
64 clearInterval(this.interval)
65 this.interval = setInterval(() => {
66 if (this.visibleValue) {
67 this.refresh()
68 } else {
69 clearInterval(this.interval)
70 }
71 }, 10000)
72 }
73
74 refresh() {
75 const data = {
76 ...iawpActions.real_time_data
77 };
78
79 jQuery.post(ajaxurl, data, (response, a, b) => {
80 document.getElementById('real-time-dashboard').classList.remove('refreshed')
81 void document.getElementById('real-time-dashboard').offsetWidth
82 document.getElementById('real-time-dashboard').classList.add('refreshed')
83 if (response.success) {
84 this.rerender(response.data)
85 }
86 });
87 }
88
89 rerender(data) {
90 this.visitorMessageTarget.textContent = data.visitor_message
91 this.pageMessageTarget.textContent = data.page_message
92 this.referrerMessageTarget.textContent = data.referrer_message
93 this.countryMessageTarget.textContent = data.country_message
94 this.updateChart({
95 element: this.minuteChartTarget,
96 visitors: data.chart_data.minute_interval_visitors,
97 views: data.chart_data.minute_interval_views,
98 })
99 this.updateChart({
100 element: this.secondChartTarget,
101 visitors: data.chart_data.second_interval_visitors,
102 views: data.chart_data.second_interval_views
103 })
104 this.updateList({
105 element: this.pagesListTarget,
106 entries: data.lists.pages.entries
107 })
108 this.updateList({
109 element: this.referrersListTarget,
110 entries: data.lists.referrers.entries
111 })
112 this.updateList({
113 element: this.countriesListTarget,
114 entries: data.lists.countries.entries
115 })
116 this.updateList({
117 element: this.campaignsListTarget,
118 entries: data.lists.campaigns.entries
119 })
120 this.updateList({
121 element: this.device_typesListTarget,
122 entries: data.lists.device_types.entries
123 })
124 }
125
126 existingElements() {
127 return Array.from(this.element.querySelectorAll('li:not(.exiting)'))
128 }
129
130 existingIds() {
131 return this.existingElements().map((existingElement) => existingElement.dataset.id)
132 }
133
134 updateList({
135 element,
136 entries
137 }) {
138 let iso = element.iso
139
140 if (!iso) {
141 iso = element.iso = new Isotope(element, {
142 itemSelector: "li",
143 layoutMode: "vertical",
144 sortColumn: "position",
145 getSortData: {
146 position: (element) => {
147 return parseInt(element.dataset.position);
148 }
149 }
150 })
151 }
152
153 // Remove any that are no longer around and update existing ones
154 this.existingElements().forEach((existingElement) => {
155 const existingId = existingElement.dataset.id
156 const match = entries.find((entry) => entry['id'] === existingId)
157
158 if (match) {
159 existingElement.dataset.position = match['position']
160 existingElement.querySelector('.real-time-position').textContent = match['position'] + '.'
161 existingElement.querySelector('.real-time-stat').textContent = match['views']
162 } else {
163 iso.remove(existingElement)
164 }
165 })
166
167 // Add new ones
168 entries.forEach((entry, index) => {
169 if (!this.existingIds().includes(entry['id'])) {
170 const entryElement = this.elementFromEntry(entry)
171 iso.insert(entryElement)
172 }
173 })
174
175 iso.updateSortData()
176 iso.arrange()
177
178 const totalElements = iso.items.length
179 const emptyMessage = element.parentNode.querySelector('.most-popular-empty-message')
180
181 if (totalElements > 0) {
182 emptyMessage.classList.add('hide')
183 } else {
184 emptyMessage.classList.remove('hide')
185 }
186 }
187
188 elementFromEntry(entry) {
189 const id = entry['id']
190 const position = entry['position']
191 const title = entry['title']
192 const subtitle = entry['subtitle']
193 const views = entry['views']
194 const flag = entry['flag'] ? entry['flag'] : ''
195 const subtitleHTML = subtitle ? `<span class="real-time-subtitle">${subtitle}</span>` : '';
196
197 const li = `
198 <li data-id="${id}" data-position="${position}">
199 <span class="real-time-position">${position}.</span>
200 ${flag}
201 <span class="real-time-resource">${title} ${subtitleHTML}</span>
202 <span class="real-time-stat">${views}</span>
203 </li>
204 `
205 const el = document.createElement('div');
206 el.innerHTML = li;
207 return el.firstElementChild;
208 }
209
210 initializeChart({
211 element,
212 views,
213 labelsShort,
214 labelsFull
215 }) {
216 const data = {
217 labels: labelsFull,
218 datasets: [{
219 id: 'views',
220 label: iawpText.views,
221 data: views,
222 backgroundColor: 'rgba(108,70,174,0.2)',
223 borderColor: 'rgba(108,70,174,1)',
224 borderWidth: {
225 bottom: 0,
226 top: 3,
227 left: 0,
228 right: 0
229 }
230 },]
231 }
232
233 const config = {
234 type: 'bar',
235 data,
236 options: {
237 interaction: {
238 intersect: false,
239 mode: 'index'
240 },
241 responsive: true,
242 scales: {
243 y: {
244 grid: {
245 borderColor: '#DEDAE6',
246 tickColor: '#DEDAE6',
247 display: true,
248 drawOnChartArea: true,
249 borderDash: [2, 4]
250 },
251 beginAtZero: true,
252 suggestedMax: 5,
253 ticks: {
254 color: document.body.classList.contains('iawp-dark-mode') ? '#ffffff' : '#6D6A73',
255 precision: 0
256 }
257 },
258 x: {
259 stacked: true,
260 grid: {
261 borderColor: '#DEDAE6',
262 tickColor: '#DEDAE6',
263 display: true,
264 drawOnChartArea: false,
265 },
266 ticks: {
267 color: document.body.classList.contains('iawp-dark-mode') ? '#ffffff' : '#6D6A73',
268 beginAtZero: true,
269 callback: function (value, index, values) {
270 return labelsShort[index];
271 }
272 }
273 }
274 },
275 plugins: {
276 mode: String, // 'light' or 'dark'
277 htmlLegend: {
278 container: element.parentNode.querySelector('.legend')
279 },
280 legend: {
281 display: false
282 },
283 corsair: {
284 dash: [2, 4],
285 color: '#777',
286 width: 1
287 },
288 },
289 },
290 plugins: [
291 corsairPlugin,
292 htmlLegendPlugin
293 ],
294 };
295
296 new Chart(element, config);
297 }
298
299 updateChart({
300 element,
301 views
302 }) {
303 const chart = Chart.getChart(element);
304 chart.data.datasets.forEach((dataset, index) => {
305 dataset.data.splice(0, dataset.data.length, ...views)
306 });
307 chart.update('none');
308 }
309 }