1/* globals JSINFO, DOKU_BASE, DokuCookie */ 2 3/** 4 * Modern Statistics Plugin 5 */ 6class StatisticsPlugin { 7 constructor() { 8 this.data = {}; 9 } 10 11 /** 12 * Initialize the statistics plugin 13 */ 14 async init() { 15 try { 16 this.buildTrackingData(); 17 await this.logPageView(); 18 this.attachEventListeners(); 19 } catch (error) { 20 console.error('Statistics plugin initialization failed:', error); 21 } 22 } 23 24 /** 25 * Build tracking data object 26 */ 27 buildTrackingData() { 28 const now = Date.now(); 29 const params = new URLSearchParams(window.location.search); 30 this.data = { 31 p: JSINFO.id, 32 r: document.referrer, 33 sx: screen.width, 34 sy: screen.height, 35 vx: window.innerWidth, 36 vy: window.innerHeight, 37 utm_source: params.get('utm_source') || '', 38 utm_medium: params.get('utm_medium') || '', 39 utm_campaign: params.get('utm_campaign') || '', 40 rnd: now 41 }; 42 } 43 44 /** 45 * Log page view based on action 46 */ 47 async logPageView() { 48 const action = JSINFO.act === 'show' ? 'v' : 's'; 49 await this.logView(action); 50 } 51 52 /** 53 * Attach event listeners for tracking 54 */ 55 attachEventListeners() { 56 // Track external link clicks 57 document.querySelectorAll('a.urlextern').forEach(link => { 58 link.addEventListener('click', this.logExternal.bind(this)); 59 }); 60 61 // Track page unload 62 window.addEventListener('beforeunload', this.logExit.bind(this)); 63 } 64 65 /** 66 * Log a view or session 67 * @param {string} action 'v' = view, 's' = session 68 */ 69 async logView(action) { 70 const params = new URLSearchParams(this.data); 71 const url = `${DOKU_BASE}lib/plugins/statistics/dispatch.php?do=${action}&${params}`; 72 73 try { 74 // Use fetch with keepalive for better reliability 75 await fetch(url, { 76 method: 'GET', 77 keepalive: true, 78 cache: 'no-cache' 79 }); 80 } catch (error) { 81 // Fallback to image beacon for older browsers 82 const img = new Image(); 83 img.src = url; 84 } 85 } 86 87 /** 88 * Log clicks to external URLs 89 * @param {Event} event Click event 90 */ 91 logExternal(event) { 92 const params = new URLSearchParams(this.data); 93 const url = `${DOKU_BASE}lib/plugins/statistics/dispatch.php?do=o&ol=${encodeURIComponent(event.target.href)}&${params}`; 94 95 // Use sendBeacon for reliable tracking 96 if (navigator.sendBeacon) { 97 navigator.sendBeacon(url); 98 } else { 99 // Fallback for older browsers 100 const img = new Image(); 101 img.src = url; 102 } 103 104 return true; 105 } 106 107 /** 108 * Log page exit as session info 109 */ 110 logExit() { 111 const params = new URLSearchParams(this.data); 112 const url = `${DOKU_BASE}lib/plugins/statistics/dispatch.php?do=s&${params}`; 113 114 if (navigator.sendBeacon) { 115 navigator.sendBeacon(url); 116 } 117 } 118} 119 120// Initialize when DOM is ready 121if (document.readyState === 'loading') { 122 document.addEventListener('DOMContentLoaded', () => { 123 new StatisticsPlugin().init(); 124 }); 125} else { 126 // DOM already loaded 127 new StatisticsPlugin().init(); 128} 129 130class ChartComponent extends HTMLElement { 131 connectedCallback() { 132 this.renderChart(); 133 } 134 135 renderChart() { 136 const chartType = this.getAttribute('type'); 137 const data = JSON.parse(this.getAttribute('data')); 138 139 console.log('data', data); 140 141 const canvas = document.createElement("canvas"); 142 canvas.height = this.getAttribute('height') || 300; 143 canvas.width = this.getAttribute('width') || 300; 144 145 this.appendChild(canvas); 146 147 const ctx = canvas.getContext('2d'); 148 149 // basic config 150 const config = { 151 type: chartType, 152 data: data, 153 options: { 154 responsive: false, 155 }, 156 }; 157 158 // percentage labels and tooltips for pie charts 159 if (chartType === "pie") { 160 // chartjs-plugin-datalabels needs to be registered 161 Chart.register(ChartDataLabels); 162 163 config.options.plugins = { 164 datalabels: { 165 formatter: (value, context) => { 166 const total = context.chart.data.datasets[0].data.reduce((a, b) => a + b, 0); 167 return ((value / total) * 100).toFixed(2) + '%'; // percentage 168 }, 169 color: '#fff', 170 } 171 }; 172 } 173 174 new Chart(ctx, config); 175 } 176} 177 178customElements.define('chart-component', ChartComponent); 179