PageSourceSearch

https://www.blaek.de/wp-content/plugins/blaek-elementor-widget/inc…/statistics-highlight.js?ver=1.2.5

js blaek.de collected 2026-10-02 03:44:58 UTC 7,575 bytes, 254 lines download raw bytes

1/**
2 * Blaek Statistics Highlight Widget
3 *
4 * Handles:
5 * 1. Chart.js doughnut chart initialisation
6 * 2. Full-page-width breakout (same pattern as servicelinks-widget)
7 */
8( function ( $ ) {
9	'use strict';
10
11	// Track widgets already initialised by the Elementor element_ready hook so the
12	// window.load fallback does not re-run applyPageWidth a second time.
13	var initializedByHook = {};
14
15	/* ── External tooltip (frosted glass pill) ────────────── */
16
17	function getOrCreateTooltip( chart ) {
18		var tooltipEl = chart.canvas.parentNode.querySelector( '.blaek-statistics-highlight__tooltip' );
19
20		if ( ! tooltipEl ) {
21			tooltipEl = document.createElement( 'div' );
22			tooltipEl.className = 'blaek-statistics-highlight__tooltip';
23
24			var inner = document.createElement( 'div' );
25			inner.className = 'blaek-statistics-highlight__tooltip-inner';
26			tooltipEl.appendChild( inner );
27
28			chart.canvas.parentNode.appendChild( tooltipEl );
29		}
30
31		return tooltipEl;
32	}
33
34	function externalTooltipHandler( context ) {
35		var chart     = context.chart;
36		var tooltip   = context.tooltip;
37		var tooltipEl = getOrCreateTooltip( chart );
38
39		// Hide if no tooltip
40		if ( tooltip.opacity === 0 ) {
41			tooltipEl.style.opacity = '0';
42			tooltipEl.style.pointerEvents = 'none';
43			return;
44		}
45
46		// Build text
47		if ( tooltip.body ) {
48			var bodyLines = tooltip.body.map( function ( b ) { return b.lines; } );
49			var innerEl   = tooltipEl.querySelector( '.blaek-statistics-highlight__tooltip-inner' );
50			innerEl.textContent = '';
51
52			bodyLines.forEach( function ( lines ) {
53				lines.forEach( function ( line ) {
54					var span = document.createElement( 'span' );
55					span.textContent = line;
56					innerEl.appendChild( span );
57				} );
58			} );
59		}
60
61		// Position: place tooltip relative to the chart canvas parent
62		var canvasRect = chart.canvas.getBoundingClientRect();
63		var parentRect = chart.canvas.parentNode.getBoundingClientRect();
64
65		// Tooltip position from Chart.js (relative to canvas)
66		var left = tooltip.caretX;
67		var top  = tooltip.caretY;
68
69		// Offset from canvas to parent
70		var offsetLeft = canvasRect.left - parentRect.left;
71		var offsetTop  = canvasRect.top - parentRect.top;
72
73		tooltipEl.style.opacity = '1';
74		tooltipEl.style.left = ( offsetLeft + left ) + 'px';
75		tooltipEl.style.top  = ( offsetTop + top ) + 'px';
76	}
77
78	/* ── Chart initialisation ─────────────────────────────── */
79
80	function initChart( $chartEl ) {
81		var $canvas = $chartEl.find( '.blaek-statistics-highlight__canvas' );
82		if ( ! $canvas.length ) {
83			return;
84		}
85
86		if ( typeof Chart === 'undefined' ) {
87			return;
88		}
89
90		// Destroy existing instance (Elementor editor re-renders)
91		var existing = Chart.getChart( $canvas[ 0 ] );
92		if ( existing ) {
93			existing.destroy();
94		}
95
96		// Remove old tooltip element if it exists
97		var oldTooltip = $chartEl[ 0 ].querySelector( '.blaek-statistics-highlight__tooltip' );
98		if ( oldTooltip ) {
99			oldTooltip.remove();
100		}
101
102		var chartData = $chartEl.data( 'chart' );
103		if ( ! chartData || ! chartData.labels ) {
104			return;
105		}
106
107		var cutout      = $chartEl.data( 'chart-cutout' ) || 65;
108		var borderWidth = $chartEl.data( 'chart-border-width' );
109		var borderColor = $chartEl.data( 'chart-border-color' ) || 'transparent';
110
111		if ( typeof borderWidth === 'undefined' || borderWidth === null ) {
112			borderWidth = 2;
113		}
114
115		new Chart( $canvas[ 0 ], {
116			type: 'doughnut',
117			data: {
118				labels: chartData.labels,
119				datasets: [ {
120					data: chartData.values,
121					backgroundColor: chartData.colors,
122					borderWidth: borderWidth,
123					borderColor: borderColor,
124					hoverOffset: 8
125				} ]
126			},
127			options: {
128				responsive: true,
129				maintainAspectRatio: true,
130				cutout: cutout + '%',
131				plugins: {
132					legend: {
133						display: false
134					},
135					tooltip: {
136						enabled: false,
137						external: externalTooltipHandler,
138						callbacks: {
139							label: function ( context ) {
140								var total = context.dataset.data.reduce( function ( a, b ) {
141									return a + b;
142								}, 0 );
143								var pct = ( ( context.parsed / total ) * 100 ).toFixed( 2 ).replace( '.', ',' );
144								return context.label + ': ' + pct + ' %';
145							}
146						}
147					}
148				}
149			}
150		} );
151	}
152
153	/* ── Full-page-width breakout ─────────────────────────── */
154
155	function applyPageWidth( $wrapper ) {
156		if ( $wrapper.data( 'page-width' ) !== 'true' && $wrapper.data( 'page-width' ) !== true ) {
157			return;
158		}
159
160		var $target = $wrapper.closest( '.elementor-widget-blaek-statistics-highlight' );
161		if ( ! $target.length ) {
162			$target = $wrapper;
163		}
164
165		var vw = document.documentElement.clientWidth;
166
167		// Use (vw - containerWidth) / 2 to derive margin-left.
168		// offsetWidth is unaffected by CSS transforms (e.g. Elementor entrance
169		// animations) and by any inline styles on $target itself, so it always
170		// reflects the column's natural layout width regardless of timing.
171		var $parent        = $target.parent();
172		var containerWidth = $parent.length ? $parent[ 0 ].offsetWidth : vw;
173		var marginLeft     = -( ( vw - containerWidth ) / 2 );
174
175		$target.css( {
176			width        : vw + 'px',
177			'max-width'  : vw + 'px',
178			'margin-left': marginLeft + 'px'
179		} );
180	}
181
182	/* ── Widget init ──────────────────────────────────────── */
183
184	function initWidget( $scope ) {
185		var $wrappers;
186
187		if ( $scope && $scope.length ) {
188			if ( $scope.hasClass( 'blaek-statistics-highlight' ) ) {
189				$wrappers = $scope;
190			} else if ( $scope.find( '.blaek-statistics-highlight' ).length ) {
191				$wrappers = $scope.find( '.blaek-statistics-highlight' );
192			} else if ( $scope.closest( '.blaek-statistics-highlight' ).length ) {
193				$wrappers = $scope.closest( '.blaek-statistics-highlight' );
194			} else {
195				$wrappers = $();
196			}
197		} else {
198			$wrappers = $( '.blaek-statistics-highlight' );
199		}
200
201		if ( ! $wrappers.length ) {
202			return;
203		}
204
205		$wrappers.each( function () {
206			var $wrapper = $( this );
207			var widgetId = $wrapper.closest( '.elementor-widget' ).attr( 'data-id' );
208
209			if ( widgetId ) {
210				initializedByHook[ widgetId ] = true;
211			}
212
213			// Page width
214			applyPageWidth( $wrapper );
215			$( window ).off( 'resize.blaekShPageWidth-' + widgetId );
216			$( window ).on( 'resize.blaekShPageWidth-' + widgetId, function () {
217				applyPageWidth( $wrapper );
218			} );
219
220			// Chart
221			var $chartEl = $wrapper.find( '.blaek-statistics-highlight__chart' );
222			if ( $chartEl.length ) {
223				initChart( $chartEl );
224			}
225		} );
226	}
227
228	/* ── Elementor frontend hook ──────────────────────────── */
229
230	$( window ).on( 'elementor/frontend/init', function () {
231		if ( typeof elementorFrontend !== 'undefined' && elementorFrontend.hooks ) {
232			elementorFrontend.hooks.addAction(
233				'frontend/element_ready/blaek-statistics-highlight.default',
234				initWidget
235			);
236		}
237	} );
238
239	/* ── Standard page-load fallback ──────────────────────── */
240	// Skip any widget already handled by the element_ready hook above.
241
242	$( window ).on( 'load', function () {
243		setTimeout( function () {
244			$( '.blaek-statistics-highlight' ).each( function () {
245				var $wrapper = $( this );
246				var widgetId = $wrapper.closest( '.elementor-widget' ).attr( 'data-id' );
247				if ( ! initializedByHook[ widgetId ] ) {
248					initWidget( $wrapper );
249				}
250			} );
251		}, 200 );
252	} );
253
254}( jQuery ) );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.